From c90c389a8a8d9d8661e9772ec4144c5cf2039f23 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: [PATCH] Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- AUTHORS | 4 + COPYING | 347 ++ COPYING-DOCS | 397 ++ COPYING.LIB | 486 ++ ChangeLog | 16 + INSTALL | 176 + Mainpage.dox | 8 + Makefile.am.in | 13 + Makefile.cvs | 16 + README | 135 + atlantik/AUTHORS | 1 + atlantik/COPYING | 340 ++ atlantik/COPYING.LIB | 504 ++ atlantik/ChangeLog | 633 +++ atlantik/INSTALL | 33 + atlantik/Makefile.am | 13 + atlantik/README | 94 + atlantik/README.KDE-3.0 | 7 + atlantik/README.packaging | 25 + atlantik/TODO | 48 + atlantik/atlanticd/Makefile.am | 10 + atlantik/atlanticd/README | 6 + atlantik/atlanticd/TODO | 11 + atlantik/atlanticd/atlanticclient.cpp | 49 + atlantik/atlanticd/atlanticclient.h | 36 + atlantik/atlanticd/atlanticdaemon.cpp | 72 + atlantik/atlanticd/atlanticdaemon.h | 48 + atlantik/atlanticd/main.cpp | 27 + atlantik/atlanticd/serversocket.cpp | 32 + atlantik/atlanticd/serversocket.h | 35 + atlantik/atlantik.desktop | 84 + atlantik/atlantikui.rc | 24 + atlantik/client/Makefile.am | 11 + atlantik/client/atlantik.cpp | 853 ++++ atlantik/client/atlantik.h | 268 + atlantik/client/configdlg.cpp | 334 ++ atlantik/client/configdlg.h | 139 + atlantik/client/event.cpp | 42 + atlantik/client/event.h | 40 + atlantik/client/eventlogwidget.cpp | 123 + atlantik/client/eventlogwidget.h | 73 + atlantik/client/main.cpp | 78 + atlantik/client/main.h | 29 + atlantik/client/monopigator.cpp | 164 + atlantik/client/monopigator.h | 79 + .../client/selectconfiguration_widget.cpp | 189 + atlantik/client/selectconfiguration_widget.h | 80 + atlantik/client/selectgame_widget.cpp | 192 + atlantik/client/selectgame_widget.h | 65 + atlantik/client/selectserver_widget.cpp | 178 + atlantik/client/selectserver_widget.h | 73 + atlantik/eventsrc | 475 ++ atlantik/kio_atlantik/Makefile.am | 15 + atlantik/kio_atlantik/atlantik.protocol | 7 + atlantik/kio_atlantik/kio_atlantik.cpp | 70 + atlantik/kio_atlantik/kio_atlantik.h | 22 + atlantik/libatlantic/Makefile.am | 15 + atlantik/libatlantic/atlantic_core.cpp | 369 ++ atlantik/libatlantic/atlantic_core.h | 105 + atlantik/libatlantic/auction.cpp | 56 + atlantik/libatlantic/auction.h | 57 + atlantik/libatlantic/configoption.cpp | 99 + atlantik/libatlantic/configoption.h | 51 + atlantik/libatlantic/estate.cpp | 163 + atlantik/libatlantic/estate.h | 95 + atlantik/libatlantic/estategroup.cpp | 41 + atlantik/libatlantic/estategroup.h | 44 + atlantik/libatlantic/game.cpp | 130 + atlantik/libatlantic/game.h | 62 + atlantik/libatlantic/libatlantic_export.h | 25 + atlantik/libatlantic/player.cpp | 183 + atlantik/libatlantic/player.h | 84 + atlantik/libatlantic/trade.cpp | 200 + atlantik/libatlantic/trade.h | 152 + atlantik/libatlantikclient/Makefile.am | 10 + .../libatlantikclient/atlantik_network.cpp | 928 ++++ atlantik/libatlantikclient/atlantik_network.h | 155 + atlantik/libatlantikclient/monopdprotocol.cpp | 80 + atlantik/libatlantikclient/monopdprotocol.h | 58 + atlantik/libatlantikui/Makefile.am | 15 + atlantik/libatlantikui/auction_widget.cpp | 141 + atlantik/libatlantikui/auction_widget.h | 65 + atlantik/libatlantikui/board.cpp | 601 +++ atlantik/libatlantikui/board.h | 102 + atlantik/libatlantikui/estatedetails.cpp | 327 ++ atlantik/libatlantikui/estatedetails.h | 77 + atlantik/libatlantikui/estateview.cpp | 558 +++ atlantik/libatlantikui/estateview.h | 80 + .../libatlantikui/kwrappedlistviewitem.cpp | 116 + atlantik/libatlantikui/kwrappedlistviewitem.h | 54 + atlantik/libatlantikui/libatlantikui_export.h | 25 + atlantik/libatlantikui/portfolioestate.cpp | 94 + atlantik/libatlantikui/portfolioestate.h | 55 + atlantik/libatlantikui/portfolioview.cpp | 295 ++ atlantik/libatlantikui/portfolioview.h | 73 + atlantik/libatlantikui/token.cpp | 157 + atlantik/libatlantikui/token.h | 62 + atlantik/libatlantikui/trade_widget.cpp | 374 ++ atlantik/libatlantikui/trade_widget.h | 98 + atlantik/pics/Makefile.am | 3 + atlantik/pics/board/Makefile.am | 2 + atlantik/pics/board/arrow.png | Bin 0 -> 674 bytes atlantik/pics/board/qmark-blue.png | Bin 0 -> 411 bytes atlantik/pics/board/qmark-red.png | Bin 0 -> 392 bytes atlantik/pics/board/token.png | Bin 0 -> 1451 bytes atlantik/pics/board/train.png | Bin 0 -> 1099 bytes atlantik/pics/hi16-app-atlantik.png | Bin 0 -> 611 bytes atlantik/pics/hi32-app-atlantik.png | Bin 0 -> 2127 bytes atlantik/pics/hi48-app-atlantik.png | Bin 0 -> 3791 bytes atlantik/pics/misc/Makefile.am | 2 + .../pics/misc/cr32-action-monop_board.png | Bin 0 -> 1257 bytes atlantik/pics/toolbar/Makefile.am | 2 + .../pics/toolbar/cr16-action-jail_pay.png | Bin 0 -> 1025 bytes .../cr22-action-atlantik_buy_estate.png | Bin 0 -> 883 bytes .../pics/toolbar/cr22-action-jail_pay.png | Bin 0 -> 1616 bytes .../cr32-action-atlantik_buy_estate.png | Bin 0 -> 643 bytes atlantik/pics/toolbar/cr32-action-auction.png | Bin 0 -> 2459 bytes .../pics/toolbar/cr32-action-jail_pay.png | Bin 0 -> 2534 bytes .../lo16-action-atlantik_buy_estate.png | Bin 0 -> 233 bytes atlantik/themes/Makefile.am | 1 + atlantik/themes/default/Makefile.am | 1 + atlantik/themes/default/tokens/Makefile.am | 5 + atlantik/themes/default/tokens/badge.png | Bin 0 -> 7365 bytes atlantik/themes/default/tokens/beachball.png | Bin 0 -> 6563 bytes atlantik/themes/default/tokens/bell.png | Bin 0 -> 7221 bytes atlantik/themes/default/tokens/bomb.png | Bin 0 -> 6127 bytes atlantik/themes/default/tokens/cat.png | Bin 0 -> 7819 bytes atlantik/themes/default/tokens/cookie.png | Bin 0 -> 8472 bytes atlantik/themes/default/tokens/cube.png | Bin 0 -> 9082 bytes atlantik/themes/default/tokens/eyeball.png | Bin 0 -> 6877 bytes atlantik/themes/default/tokens/flag.png | Bin 0 -> 7436 bytes atlantik/themes/default/tokens/ghost.png | Bin 0 -> 6041 bytes atlantik/themes/default/tokens/globe.png | Bin 0 -> 10104 bytes atlantik/themes/default/tokens/hamburger.png | Bin 0 -> 9392 bytes atlantik/themes/default/tokens/lips.png | Bin 0 -> 7308 bytes atlantik/themes/default/tokens/puzzle.png | Bin 0 -> 7141 bytes atlantik/themes/default/tokens/pyramid.png | Bin 0 -> 6477 bytes atlantik/themes/default/tokens/skull.png | Bin 0 -> 7975 bytes .../themes/default/tokens/traffic_light.png | Bin 0 -> 5285 bytes atlantik/themes/default/tokens/wizard.png | Bin 0 -> 6988 bytes configure.in.in | 34 + doc/Makefile.am | 5 + doc/api/kcarddialog.png | Bin 0 -> 34451 bytes doc/atlantik/Makefile.am | 4 + doc/atlantik/index.docbook | 323 ++ doc/atlantik/man-atlantik.6.docbook | 86 + doc/kasteroids/Makefile.am | 4 + doc/kasteroids/fuel.png | Bin 0 -> 353 bytes doc/kasteroids/index.docbook | 328 ++ doc/katomic/Makefile.am | 4 + doc/katomic/index.docbook | 358 ++ doc/kbackgammon/Makefile.am | 4 + doc/kbackgammon/board.png | Bin 0 -> 18755 bytes doc/kbackgammon/index.docbook | 622 +++ doc/kbattleship/Makefile.am | 3 + doc/kbattleship/index.docbook | 485 ++ doc/kblackbox/Makefile.am | 4 + doc/kblackbox/index.docbook | 491 ++ doc/kblackbox/kblackboxtbar.png | Bin 0 -> 1901 bytes doc/kbounce/Makefile.am | 4 + doc/kbounce/index.docbook | 383 ++ doc/kbounce/jezball_corridor1.png | Bin 0 -> 14148 bytes doc/kbounce/jezball_corridor2.png | Bin 0 -> 14351 bytes doc/kbounce/jezball_newWall.png | Bin 0 -> 14646 bytes doc/kenolaba/Makefile.am | 4 + doc/kenolaba/index.docbook | 869 ++++ doc/kenolaba/kenolaba-pieces.png | Bin 0 -> 2114 bytes doc/kenolaba/man-kenolaba.6.docbook | 80 + doc/kfouleggs/Makefile.am | 4 + doc/kfouleggs/gamescreen.png | Bin 0 -> 8273 bytes doc/kfouleggs/index.docbook | 802 +++ doc/kgoldrunner/Makefile.am | 3 + doc/kgoldrunner/editbar.png | Bin 0 -> 13120 bytes doc/kgoldrunner/enemy.png | Bin 0 -> 460 bytes doc/kgoldrunner/hero.png | Bin 0 -> 441 bytes doc/kgoldrunner/index.docbook | 1674 +++++++ doc/kgoldrunner/kgoldrunner.png | Bin 0 -> 803 bytes doc/kgoldrunner/level010.png | Bin 0 -> 844 bytes doc/kgoldrunner/select.png | Bin 0 -> 20980 bytes doc/kgoldrunner/tute008.png | Bin 0 -> 41471 bytes doc/kjumpingcube/Makefile.am | 4 + doc/kjumpingcube/index.docbook | 368 ++ doc/klickety/Makefile.am | 4 + doc/klickety/index.docbook | 425 ++ doc/klickety/screenshot.png | Bin 0 -> 18483 bytes doc/klines/Makefile.am | 5 + doc/klines/index.docbook | 328 ++ doc/kmahjongg/Makefile.am | 4 + doc/kmahjongg/background.png | Bin 0 -> 35234 bytes doc/kmahjongg/boardeditor.png | Bin 0 -> 7675 bytes doc/kmahjongg/config.png | Bin 0 -> 6504 bytes doc/kmahjongg/gamescreen.png | Bin 0 -> 67210 bytes doc/kmahjongg/highscore.png | Bin 0 -> 12866 bytes doc/kmahjongg/index.docbook | 1012 ++++ doc/kmahjongg/layout.png | Bin 0 -> 26235 bytes doc/kmahjongg/numbered.png | Bin 0 -> 4675 bytes doc/kmahjongg/shortcuts.png | Bin 0 -> 10421 bytes doc/kmahjongg/showremoved.png | Bin 0 -> 84972 bytes doc/kmahjongg/theme.png | Bin 0 -> 25955 bytes doc/kmahjongg/tileset.png | Bin 0 -> 26066 bytes doc/kmines/Makefile.am | 4 + doc/kmines/index.docbook | 617 +++ doc/kmines/kmines1.png | Bin 0 -> 9222 bytes doc/kmines/kmines2.png | Bin 0 -> 34300 bytes doc/kolf/Makefile.am | 2 + doc/kolf/index.docbook | 833 ++++ doc/konquest/Makefile.am | 4 + doc/konquest/index.docbook | 295 ++ doc/kpat/Makefile.am | 5 + doc/kpat/clubs.png | Bin 0 -> 282 bytes doc/kpat/diamonds.png | Bin 0 -> 1035 bytes doc/kpat/hearts.png | Bin 0 -> 273 bytes doc/kpat/index.docbook | 811 +++ doc/kpat/man-kpat.6.docbook | 340 ++ doc/kpat/playfield.png | Bin 0 -> 117084 bytes doc/kpat/spades.png | Bin 0 -> 1042 bytes doc/kpoker/Makefile.am | 4 + doc/kpoker/index.docbook | 582 +++ doc/kpoker/kpoker1.png | Bin 0 -> 15904 bytes doc/kpoker/kpoker2.png | Bin 0 -> 20181 bytes doc/kreversi/Makefile.am | 4 + doc/kreversi/index.docbook | 533 ++ doc/kreversi/kreversi-configuration.png | Bin 0 -> 30239 bytes doc/kreversi/kreversi1.png | Bin 0 -> 68912 bytes doc/ksame/Makefile.am | 4 + doc/ksame/index.docbook | 295 ++ doc/kshisen/Makefile.am | 4 + doc/kshisen/index.docbook | 423 ++ doc/kshisen/kshisen-configuration.png | Bin 0 -> 25016 bytes doc/kshisen/score-formula.png | Bin 0 -> 9490 bytes doc/kshisen/score-formula.tex | 18 + doc/ksirtet/Makefile.am | 4 + doc/ksirtet/index.docbook | 586 +++ doc/ksmiletris/Makefile.am | 4 + doc/ksmiletris/gamescreen.png | Bin 0 -> 64345 bytes doc/ksmiletris/icon.png | Bin 0 -> 482 bytes doc/ksmiletris/index.docbook | 456 ++ doc/ksmiletris/smiley.png | Bin 0 -> 1458 bytes doc/ksmiletris/symbol.png | Bin 0 -> 369 bytes doc/ksnake/Makefile.am | 4 + doc/ksnake/index.docbook | 346 ++ doc/ksokoban/Makefile.am | 4 + doc/ksokoban/index.docbook | 429 ++ doc/kspaceduel/Makefile.am | 4 + doc/kspaceduel/index.docbook | 659 +++ doc/kspaceduel/kspaceduel3.png | Bin 0 -> 34970 bytes doc/ktron/Makefile.am | 4 + doc/ktron/index.docbook | 575 +++ doc/ktuberling/Makefile.am | 4 + doc/ktuberling/button-new.png | Bin 0 -> 712 bytes doc/ktuberling/button-open.png | Bin 0 -> 939 bytes doc/ktuberling/button-print.png | Bin 0 -> 847 bytes doc/ktuberling/button-redo.png | Bin 0 -> 611 bytes doc/ktuberling/button-save.png | Bin 0 -> 694 bytes doc/ktuberling/button-undo.png | Bin 0 -> 598 bytes doc/ktuberling/gameboard.png | Bin 0 -> 18180 bytes doc/ktuberling/index.docbook | 811 +++ doc/ktuberling/ktuberling.png | Bin 0 -> 5916 bytes doc/ktuberling/menu-edit.png | Bin 0 -> 4579 bytes doc/ktuberling/menu-game.png | Bin 0 -> 9785 bytes doc/ktuberling/menu-help.png | Bin 0 -> 10124 bytes doc/ktuberling/menu-playground.png | Bin 0 -> 3059 bytes doc/ktuberling/menu-raw.png | Bin 0 -> 9022 bytes doc/ktuberling/menu-settings.png | Bin 0 -> 4836 bytes doc/ktuberling/menu-speech.png | Bin 0 -> 12226 bytes doc/ktuberling/technical-reference.docbook | 262 + doc/ktuberling/toolbar.png | Bin 0 -> 3804 bytes doc/kwin4/Makefile.am | 4 + doc/kwin4/index.docbook | 441 ++ doc/lskat/Makefile.am | 4 + doc/lskat/index.docbook | 467 ++ kasteroids/ChangeLog | 35 + kasteroids/Makefile.am | 26 + kasteroids/bg.png | Bin 0 -> 7843 bytes kasteroids/configure.in.in | 5 + kasteroids/hi128-app-kasteroids.png | Bin 0 -> 17919 bytes kasteroids/hi16-app-kasteroids.png | Bin 0 -> 863 bytes kasteroids/hi22-app-kasteroids.png | Bin 0 -> 1394 bytes kasteroids/hi32-app-kasteroids.png | Bin 0 -> 2331 bytes kasteroids/hi48-app-kasteroids.png | Bin 0 -> 4360 bytes kasteroids/hi64-app-kasteroids.png | Bin 0 -> 6545 bytes kasteroids/kasteroids.desktop | 79 + kasteroids/kasteroids.kcfg | 35 + kasteroids/kasteroidsui.rc | 6 + kasteroids/ledmeter.cpp | 118 + kasteroids/ledmeter.h | 54 + kasteroids/main.cpp | 51 + kasteroids/settings.kcfgc | 5 + kasteroids/sounds/Explosion.wav | Bin 0 -> 18427 bytes kasteroids/sounds/Makefile.am | 5 + kasteroids/sprites.h | 129 + kasteroids/sprites/.pbm | 0 kasteroids/sprites/Makefile.am | 36 + kasteroids/sprites/bits/Makefile.am | 17 + kasteroids/sprites/bits/bits.ini | 9 + kasteroids/sprites/bits/bits.pov | 31 + kasteroids/sprites/bits/bits0000.png | Bin 0 -> 257 bytes kasteroids/sprites/bits/bits0001.png | Bin 0 -> 275 bytes kasteroids/sprites/bits/bits0002.png | Bin 0 -> 312 bytes kasteroids/sprites/bits/bits0003.png | Bin 0 -> 342 bytes kasteroids/sprites/bits/bits0004.png | Bin 0 -> 315 bytes kasteroids/sprites/bits/bits0005.png | Bin 0 -> 291 bytes kasteroids/sprites/bits/bits0006.png | Bin 0 -> 240 bytes kasteroids/sprites/bits/bits0007.png | Bin 0 -> 198 bytes kasteroids/sprites/bits/bits0008.png | Bin 0 -> 187 bytes kasteroids/sprites/bits/bits0009.png | Bin 0 -> 268 bytes kasteroids/sprites/bits/bits0010.png | Bin 0 -> 303 bytes kasteroids/sprites/bits/bits0011.png | Bin 0 -> 333 bytes kasteroids/sprites/bits/bits0012.png | Bin 0 -> 329 bytes kasteroids/sprites/bits/bits0013.png | Bin 0 -> 285 bytes kasteroids/sprites/bits/bits0014.png | Bin 0 -> 251 bytes kasteroids/sprites/bits/bits0015.png | Bin 0 -> 222 bytes kasteroids/sprites/exhaust/Makefile.am | 1 + kasteroids/sprites/exhaust/exhaust.png | Bin 0 -> 142 bytes kasteroids/sprites/missile/Makefile.am | 1 + kasteroids/sprites/missile/missile.png | Bin 0 -> 128 bytes kasteroids/sprites/powerups/Makefile.am | 2 + kasteroids/sprites/powerups/brake.png | Bin 0 -> 249 bytes kasteroids/sprites/powerups/energy.png | Bin 0 -> 188 bytes kasteroids/sprites/powerups/shield.png | Bin 0 -> 222 bytes kasteroids/sprites/powerups/shoot.png | Bin 0 -> 345 bytes kasteroids/sprites/powerups/teleport.png | Bin 0 -> 214 bytes kasteroids/sprites/rock1/Makefile.am | 24 + kasteroids/sprites/rock1/rock1.ini | 9 + kasteroids/sprites/rock1/rock1.pov | 26 + kasteroids/sprites/rock1/rock10000.png | Bin 0 -> 3237 bytes kasteroids/sprites/rock1/rock10001.png | Bin 0 -> 3197 bytes kasteroids/sprites/rock1/rock10002.png | Bin 0 -> 3218 bytes kasteroids/sprites/rock1/rock10003.png | Bin 0 -> 3167 bytes kasteroids/sprites/rock1/rock10004.png | Bin 0 -> 3150 bytes kasteroids/sprites/rock1/rock10005.png | Bin 0 -> 3089 bytes kasteroids/sprites/rock1/rock10006.png | Bin 0 -> 3079 bytes kasteroids/sprites/rock1/rock10007.png | Bin 0 -> 3121 bytes kasteroids/sprites/rock1/rock10008.png | Bin 0 -> 2977 bytes kasteroids/sprites/rock1/rock10009.png | Bin 0 -> 2872 bytes kasteroids/sprites/rock1/rock10010.png | Bin 0 -> 2823 bytes kasteroids/sprites/rock1/rock10011.png | Bin 0 -> 2786 bytes kasteroids/sprites/rock1/rock10012.png | Bin 0 -> 2767 bytes kasteroids/sprites/rock1/rock10013.png | Bin 0 -> 2837 bytes kasteroids/sprites/rock1/rock10014.png | Bin 0 -> 2878 bytes kasteroids/sprites/rock1/rock10015.png | Bin 0 -> 3023 bytes kasteroids/sprites/rock1/rock10016.png | Bin 0 -> 3112 bytes kasteroids/sprites/rock1/rock10017.png | Bin 0 -> 3178 bytes kasteroids/sprites/rock1/rock10018.png | Bin 0 -> 3266 bytes kasteroids/sprites/rock1/rock10019.png | Bin 0 -> 3312 bytes kasteroids/sprites/rock1/rock10020.png | Bin 0 -> 3317 bytes kasteroids/sprites/rock1/rock10021.png | Bin 0 -> 3354 bytes kasteroids/sprites/rock1/rock10022.png | Bin 0 -> 3383 bytes kasteroids/sprites/rock1/rock10023.png | Bin 0 -> 3388 bytes kasteroids/sprites/rock1/rock10024.png | Bin 0 -> 3294 bytes kasteroids/sprites/rock1/rock10025.png | Bin 0 -> 3220 bytes kasteroids/sprites/rock1/rock10026.png | Bin 0 -> 3145 bytes kasteroids/sprites/rock1/rock10027.png | Bin 0 -> 3257 bytes kasteroids/sprites/rock1/rock10028.png | Bin 0 -> 3282 bytes kasteroids/sprites/rock1/rock10029.png | Bin 0 -> 3306 bytes kasteroids/sprites/rock1/rock10030.png | Bin 0 -> 3346 bytes kasteroids/sprites/rock1/rock10031.png | Bin 0 -> 3292 bytes kasteroids/sprites/rock2/Makefile.am | 24 + kasteroids/sprites/rock2/rock2.ini | 9 + kasteroids/sprites/rock2/rock2.pov | 26 + kasteroids/sprites/rock2/rock20000.png | Bin 0 -> 1553 bytes kasteroids/sprites/rock2/rock20001.png | Bin 0 -> 1599 bytes kasteroids/sprites/rock2/rock20002.png | Bin 0 -> 1599 bytes kasteroids/sprites/rock2/rock20003.png | Bin 0 -> 1656 bytes kasteroids/sprites/rock2/rock20004.png | Bin 0 -> 1636 bytes kasteroids/sprites/rock2/rock20005.png | Bin 0 -> 1665 bytes kasteroids/sprites/rock2/rock20006.png | Bin 0 -> 1632 bytes kasteroids/sprites/rock2/rock20007.png | Bin 0 -> 1626 bytes kasteroids/sprites/rock2/rock20008.png | Bin 0 -> 1570 bytes kasteroids/sprites/rock2/rock20009.png | Bin 0 -> 1561 bytes kasteroids/sprites/rock2/rock20010.png | Bin 0 -> 1541 bytes kasteroids/sprites/rock2/rock20011.png | Bin 0 -> 1550 bytes kasteroids/sprites/rock2/rock20012.png | Bin 0 -> 1576 bytes kasteroids/sprites/rock2/rock20013.png | Bin 0 -> 1585 bytes kasteroids/sprites/rock2/rock20014.png | Bin 0 -> 1611 bytes kasteroids/sprites/rock2/rock20015.png | Bin 0 -> 1625 bytes kasteroids/sprites/rock2/rock20016.png | Bin 0 -> 1615 bytes kasteroids/sprites/rock2/rock20017.png | Bin 0 -> 1627 bytes kasteroids/sprites/rock2/rock20018.png | Bin 0 -> 1589 bytes kasteroids/sprites/rock2/rock20019.png | Bin 0 -> 1587 bytes kasteroids/sprites/rock2/rock20020.png | Bin 0 -> 1606 bytes kasteroids/sprites/rock2/rock20021.png | Bin 0 -> 1675 bytes kasteroids/sprites/rock2/rock20022.png | Bin 0 -> 1670 bytes kasteroids/sprites/rock2/rock20023.png | Bin 0 -> 1663 bytes kasteroids/sprites/rock2/rock20024.png | Bin 0 -> 1690 bytes kasteroids/sprites/rock2/rock20025.png | Bin 0 -> 1685 bytes kasteroids/sprites/rock2/rock20026.png | Bin 0 -> 1606 bytes kasteroids/sprites/rock2/rock20027.png | Bin 0 -> 1583 bytes kasteroids/sprites/rock2/rock20028.png | Bin 0 -> 1600 bytes kasteroids/sprites/rock2/rock20029.png | Bin 0 -> 1608 bytes kasteroids/sprites/rock2/rock20030.png | Bin 0 -> 1568 bytes kasteroids/sprites/rock2/rock20031.png | Bin 0 -> 1553 bytes kasteroids/sprites/rock3/Makefile.am | 24 + kasteroids/sprites/rock3/rock3.ini | 9 + kasteroids/sprites/rock3/rock3.pov | 26 + kasteroids/sprites/rock3/rock30000.png | Bin 0 -> 825 bytes kasteroids/sprites/rock3/rock30001.png | Bin 0 -> 828 bytes kasteroids/sprites/rock3/rock30002.png | Bin 0 -> 853 bytes kasteroids/sprites/rock3/rock30003.png | Bin 0 -> 870 bytes kasteroids/sprites/rock3/rock30004.png | Bin 0 -> 864 bytes kasteroids/sprites/rock3/rock30005.png | Bin 0 -> 846 bytes kasteroids/sprites/rock3/rock30006.png | Bin 0 -> 821 bytes kasteroids/sprites/rock3/rock30007.png | Bin 0 -> 829 bytes kasteroids/sprites/rock3/rock30008.png | Bin 0 -> 839 bytes kasteroids/sprites/rock3/rock30009.png | Bin 0 -> 830 bytes kasteroids/sprites/rock3/rock30010.png | Bin 0 -> 804 bytes kasteroids/sprites/rock3/rock30011.png | Bin 0 -> 828 bytes kasteroids/sprites/rock3/rock30012.png | Bin 0 -> 810 bytes kasteroids/sprites/rock3/rock30013.png | Bin 0 -> 832 bytes kasteroids/sprites/rock3/rock30014.png | Bin 0 -> 825 bytes kasteroids/sprites/rock3/rock30015.png | Bin 0 -> 839 bytes kasteroids/sprites/rock3/rock30016.png | Bin 0 -> 799 bytes kasteroids/sprites/rock3/rock30017.png | Bin 0 -> 817 bytes kasteroids/sprites/rock3/rock30018.png | Bin 0 -> 830 bytes kasteroids/sprites/rock3/rock30019.png | Bin 0 -> 837 bytes kasteroids/sprites/rock3/rock30020.png | Bin 0 -> 851 bytes kasteroids/sprites/rock3/rock30021.png | Bin 0 -> 835 bytes kasteroids/sprites/rock3/rock30022.png | Bin 0 -> 853 bytes kasteroids/sprites/rock3/rock30023.png | Bin 0 -> 829 bytes kasteroids/sprites/rock3/rock30024.png | Bin 0 -> 840 bytes kasteroids/sprites/rock3/rock30025.png | Bin 0 -> 827 bytes kasteroids/sprites/rock3/rock30026.png | Bin 0 -> 823 bytes kasteroids/sprites/rock3/rock30027.png | Bin 0 -> 827 bytes kasteroids/sprites/rock3/rock30028.png | Bin 0 -> 819 bytes kasteroids/sprites/rock3/rock30029.png | Bin 0 -> 816 bytes kasteroids/sprites/rock3/rock30030.png | Bin 0 -> 809 bytes kasteroids/sprites/rock3/rock30031.png | Bin 0 -> 819 bytes kasteroids/sprites/shield/Makefile.am | 3 + kasteroids/sprites/shield/shield0000.png | Bin 0 -> 3405 bytes kasteroids/sprites/shield/shield0001.png | Bin 0 -> 3192 bytes kasteroids/sprites/shield/shield0002.png | Bin 0 -> 3552 bytes kasteroids/sprites/shield/shield0003.png | Bin 0 -> 3513 bytes kasteroids/sprites/shield/shield0004.png | Bin 0 -> 3150 bytes kasteroids/sprites/shield/shield0005.png | Bin 0 -> 3446 bytes kasteroids/sprites/shield/shield0006.png | Bin 0 -> 3368 bytes kasteroids/sprites/ship/Makefile.am | 22 + kasteroids/sprites/ship/ship.ini | 11 + kasteroids/sprites/ship/ship.pov | 128 + kasteroids/sprites/ship/ship0000.png | Bin 0 -> 1965 bytes kasteroids/sprites/ship/ship0001.png | Bin 0 -> 2053 bytes kasteroids/sprites/ship/ship0002.png | Bin 0 -> 2148 bytes kasteroids/sprites/ship/ship0003.png | Bin 0 -> 2192 bytes kasteroids/sprites/ship/ship0004.png | Bin 0 -> 2181 bytes kasteroids/sprites/ship/ship0005.png | Bin 0 -> 2197 bytes kasteroids/sprites/ship/ship0006.png | Bin 0 -> 2209 bytes kasteroids/sprites/ship/ship0007.png | Bin 0 -> 2226 bytes kasteroids/sprites/ship/ship0008.png | Bin 0 -> 2289 bytes kasteroids/sprites/ship/ship0009.png | Bin 0 -> 2297 bytes kasteroids/sprites/ship/ship0010.png | Bin 0 -> 2311 bytes kasteroids/sprites/ship/ship0011.png | Bin 0 -> 2288 bytes kasteroids/sprites/ship/ship0012.png | Bin 0 -> 2283 bytes kasteroids/sprites/ship/ship0013.png | Bin 0 -> 2209 bytes kasteroids/sprites/ship/ship0014.png | Bin 0 -> 2170 bytes kasteroids/sprites/ship/ship0015.png | Bin 0 -> 2084 bytes kasteroids/sprites/ship/ship0016.png | Bin 0 -> 2025 bytes kasteroids/sprites/ship/ship0017.png | Bin 0 -> 2098 bytes kasteroids/sprites/ship/ship0018.png | Bin 0 -> 2191 bytes kasteroids/sprites/ship/ship0019.png | Bin 0 -> 2201 bytes kasteroids/sprites/ship/ship0020.png | Bin 0 -> 2233 bytes kasteroids/sprites/ship/ship0021.png | Bin 0 -> 2251 bytes kasteroids/sprites/ship/ship0022.png | Bin 0 -> 2255 bytes kasteroids/sprites/ship/ship0023.png | Bin 0 -> 2266 bytes kasteroids/sprites/ship/ship0024.png | Bin 0 -> 2268 bytes kasteroids/sprites/ship/ship0025.png | Bin 0 -> 2243 bytes kasteroids/sprites/ship/ship0026.png | Bin 0 -> 2211 bytes kasteroids/sprites/ship/ship0027.png | Bin 0 -> 2242 bytes kasteroids/sprites/ship/ship0028.png | Bin 0 -> 2174 bytes kasteroids/sprites/ship/ship0029.png | Bin 0 -> 2173 bytes kasteroids/sprites/ship/ship0030.png | Bin 0 -> 2147 bytes kasteroids/sprites/ship/ship0031.png | Bin 0 -> 2085 bytes kasteroids/sprites/ship/ship0032.png | Bin 0 -> 2004 bytes kasteroids/sprites/ship/ship0033.png | Bin 0 -> 2065 bytes kasteroids/sprites/ship/ship0034.png | Bin 0 -> 2110 bytes kasteroids/sprites/ship/ship0035.png | Bin 0 -> 2197 bytes kasteroids/sprites/ship/ship0036.png | Bin 0 -> 2221 bytes kasteroids/sprites/ship/ship0037.png | Bin 0 -> 2264 bytes kasteroids/sprites/ship/ship0038.png | Bin 0 -> 2199 bytes kasteroids/sprites/ship/ship0039.png | Bin 0 -> 2256 bytes kasteroids/sprites/ship/ship0040.png | Bin 0 -> 2262 bytes kasteroids/sprites/ship/ship0041.png | Bin 0 -> 2329 bytes kasteroids/sprites/ship/ship0042.png | Bin 0 -> 2251 bytes kasteroids/sprites/ship/ship0043.png | Bin 0 -> 2281 bytes kasteroids/sprites/ship/ship0044.png | Bin 0 -> 2232 bytes kasteroids/sprites/ship/ship0045.png | Bin 0 -> 2172 bytes kasteroids/sprites/ship/ship0046.png | Bin 0 -> 2076 bytes kasteroids/sprites/ship/ship0047.png | Bin 0 -> 2016 bytes kasteroids/sprites/ship/ship0048.png | Bin 0 -> 1912 bytes kasteroids/sprites/ship/ship0049.png | Bin 0 -> 2056 bytes kasteroids/sprites/ship/ship0050.png | Bin 0 -> 2145 bytes kasteroids/sprites/ship/ship0051.png | Bin 0 -> 2172 bytes kasteroids/sprites/ship/ship0052.png | Bin 0 -> 2264 bytes kasteroids/sprites/ship/ship0053.png | Bin 0 -> 2265 bytes kasteroids/sprites/ship/ship0054.png | Bin 0 -> 2294 bytes kasteroids/sprites/ship/ship0055.png | Bin 0 -> 2353 bytes kasteroids/sprites/ship/ship0056.png | Bin 0 -> 2379 bytes kasteroids/sprites/ship/ship0057.png | Bin 0 -> 2354 bytes kasteroids/sprites/ship/ship0058.png | Bin 0 -> 2367 bytes kasteroids/sprites/ship/ship0059.png | Bin 0 -> 2350 bytes kasteroids/sprites/ship/ship0060.png | Bin 0 -> 2277 bytes kasteroids/sprites/ship/ship0061.png | Bin 0 -> 2189 bytes kasteroids/sprites/ship/ship0062.png | Bin 0 -> 2174 bytes kasteroids/sprites/ship/ship0063.png | Bin 0 -> 2148 bytes kasteroids/toplevel.cpp | 678 +++ kasteroids/toplevel.h | 94 + kasteroids/version.h | 1 + kasteroids/view.cpp | 857 ++++ kasteroids/view.h | 133 + katomic/Makefile.am | 35 + katomic/abilder.png | Bin 0 -> 18617 bytes katomic/abilder.svgz | Bin 0 -> 9890 bytes katomic/atom.h | 35 + katomic/configbox.cpp | 59 + katomic/configbox.h | 35 + katomic/feld.cpp | 644 +++ katomic/feld.h | 116 + katomic/gamewidget.cpp | 230 + katomic/gamewidget.h | 74 + katomic/hi128-app-katomic.png | Bin 0 -> 18820 bytes katomic/hi16-app-katomic.png | Bin 0 -> 798 bytes katomic/hi22-app-katomic.png | Bin 0 -> 4145 bytes katomic/hi32-app-katomic.png | Bin 0 -> 2425 bytes katomic/hi48-app-katomic.png | Bin 0 -> 4944 bytes katomic/hi64-app-katomic.png | Bin 0 -> 6575 bytes katomic/katomic.desktop | 73 + katomic/katomicui.rc | 6 + katomic/levels/level_1 | 21 + katomic/levels/level_10 | 24 + katomic/levels/level_11 | 28 + katomic/levels/level_12 | 29 + katomic/levels/level_13 | 28 + katomic/levels/level_14 | 26 + katomic/levels/level_15 | 27 + katomic/levels/level_16 | 31 + katomic/levels/level_17 | 28 + katomic/levels/level_18 | 23 + katomic/levels/level_19 | 26 + katomic/levels/level_2 | 25 + katomic/levels/level_20 | 24 + katomic/levels/level_21 | 30 + katomic/levels/level_22 | 28 + katomic/levels/level_23 | 24 + katomic/levels/level_24 | 29 + katomic/levels/level_25 | 27 + katomic/levels/level_26 | 27 + katomic/levels/level_27 | 33 + katomic/levels/level_28 | 32 + katomic/levels/level_29 | 31 + katomic/levels/level_3 | 26 + katomic/levels/level_30 | 40 + katomic/levels/level_31 | 29 + katomic/levels/level_32 | 31 + katomic/levels/level_33 | 27 + katomic/levels/level_34 | 33 + katomic/levels/level_35 | 31 + katomic/levels/level_36 | 23 + katomic/levels/level_37 | 41 + katomic/levels/level_38 | 30 + katomic/levels/level_39 | 39 + katomic/levels/level_4 | 26 + katomic/levels/level_40 | 37 + katomic/levels/level_41 | 34 + katomic/levels/level_42 | 26 + katomic/levels/level_43 | 42 + katomic/levels/level_44 | 43 + katomic/levels/level_45 | 36 + katomic/levels/level_46 | 29 + katomic/levels/level_47 | 31 + katomic/levels/level_48 | 35 + katomic/levels/level_49 | 36 + katomic/levels/level_5 | 28 + katomic/levels/level_50 | 38 + katomic/levels/level_51 | 31 + katomic/levels/level_52 | 41 + katomic/levels/level_53 | 30 + katomic/levels/level_54 | 39 + katomic/levels/level_55 | 36 + katomic/levels/level_56 | 37 + katomic/levels/level_57 | 25 + katomic/levels/level_58 | 28 + katomic/levels/level_59 | 39 + katomic/levels/level_6 | 26 + katomic/levels/level_60 | 28 + katomic/levels/level_61 | 30 + katomic/levels/level_62 | 31 + katomic/levels/level_63 | 34 + katomic/levels/level_64 | 34 + katomic/levels/level_65 | 39 + katomic/levels/level_66 | 31 + katomic/levels/level_67 | 31 + katomic/levels/level_68 | 39 + katomic/levels/level_69 | 43 + katomic/levels/level_7 | 27 + katomic/levels/level_70 | 30 + katomic/levels/level_71 | 37 + katomic/levels/level_72 | 38 + katomic/levels/level_73 | 32 + katomic/levels/level_74 | 34 + katomic/levels/level_75 | 30 + katomic/levels/level_76 | 37 + katomic/levels/level_77 | 38 + katomic/levels/level_78 | 41 + katomic/levels/level_79 | 57 + katomic/levels/level_8 | 26 + katomic/levels/level_80 | 29 + katomic/levels/level_81 | 37 + katomic/levels/level_82 | 44 + katomic/levels/level_83 | 46 + katomic/levels/level_9 | 27 + katomic/main.cpp | 68 + katomic/molek.cpp | 158 + katomic/molek.h | 52 + katomic/molek.png | Bin 0 -> 2441 bytes katomic/settings.h | 36 + katomic/toplevel.cpp | 119 + katomic/toplevel.h | 66 + kbackgammon/AUTHORS | 1 + kbackgammon/Makefile.am | 21 + kbackgammon/README | 43 + kbackgammon/TODO | 11 + kbackgammon/engines/Makefile.am | 16 + kbackgammon/engines/fibs/Makefile.am | 9 + kbackgammon/engines/fibs/clip.h | 39 + kbackgammon/engines/fibs/kbgfibs.cpp | 2314 +++++++++ kbackgammon/engines/fibs/kbgfibs.h | 479 ++ kbackgammon/engines/fibs/kbgfibschat.cpp | 828 ++++ kbackgammon/engines/fibs/kbgfibschat.h | 273 + kbackgammon/engines/fibs/kbginvite.cpp | 185 + kbackgammon/engines/fibs/kbginvite.h | 113 + kbackgammon/engines/fibs/kplayerlist.cpp | 902 ++++ kbackgammon/engines/fibs/kplayerlist.h | 298 ++ kbackgammon/engines/generic/Makefile.am | 8 + kbackgammon/engines/generic/kbgengine.cpp | 62 + kbackgammon/engines/generic/kbgengine.h | 298 ++ kbackgammon/engines/gnubg/Makefile.am | 9 + kbackgammon/engines/gnubg/kbggnubg.cpp | 710 +++ kbackgammon/engines/gnubg/kbggnubg.h | 223 + kbackgammon/engines/nextgen/Makefile.am | 9 + kbackgammon/engines/nextgen/kbggame.cpp | 47 + kbackgammon/engines/nextgen/kbggame.h | 57 + kbackgammon/engines/nextgen/kbgng.cpp | 622 +++ kbackgammon/engines/nextgen/kbgng.h | 263 + kbackgammon/engines/nextgen/kbgplayer.cpp | 62 + kbackgammon/engines/nextgen/kbgplayer.h | 58 + kbackgammon/engines/offline/Makefile.am | 9 + kbackgammon/engines/offline/kbgoffline.cpp | 810 +++ kbackgammon/engines/offline/kbgoffline.h | 213 + kbackgammon/eventsrc | 802 +++ kbackgammon/icons/Makefile.am | 3 + kbackgammon/icons/hi128-app-kbackgammon.png | Bin 0 -> 7621 bytes kbackgammon/icons/hi16-app-kbackgammon.png | Bin 0 -> 841 bytes .../icons/hi16-app-kbackgammon_engine.png | Bin 0 -> 1131 bytes kbackgammon/icons/hi22-app-kbackgammon.png | Bin 0 -> 3914 bytes kbackgammon/icons/hi32-app-kbackgammon.png | Bin 0 -> 1875 bytes .../icons/hi32-app-kbackgammon_engine.png | Bin 0 -> 2192 bytes kbackgammon/icons/hi48-app-kbackgammon.png | Bin 0 -> 3359 bytes .../icons/hi48-app-kbackgammon_engine.png | Bin 0 -> 4046 bytes kbackgammon/icons/hi64-app-kbackgammon.png | Bin 0 -> 3704 bytes .../icons/hi64-app-kbackgammon_engine.png | Bin 0 -> 6166 bytes kbackgammon/kbackgammon.desktop | 75 + kbackgammon/kbackgammonui.rc | 51 + kbackgammon/kbg.cpp | 830 ++++ kbackgammon/kbg.h | 228 + kbackgammon/kbgboard.cpp | 2918 +++++++++++ kbackgammon/kbgboard.h | 967 ++++ kbackgammon/kbgstatus.cpp | 544 ++ kbackgammon/kbgstatus.h | 310 ++ kbackgammon/kbgtextview.cpp | 104 + kbackgammon/kbgtextview.h | 81 + kbackgammon/main.cpp | 67 + kbackgammon/pics/Makefile.am | 6 + kbackgammon/pics/kbackgammon-chat.png | Bin 0 -> 292 bytes kbackgammon/pics/kbackgammon-double.xpm | 29 + kbackgammon/sounds/Makefile.am | 8 + kbackgammon/sounds/kbackgammon-lost.wav | Bin 0 -> 134188 bytes kbackgammon/sounds/kbackgammon-move.wav | Bin 0 -> 62364 bytes kbackgammon/sounds/kbackgammon-roll.wav | Bin 0 -> 7522 bytes kbackgammon/sounds/kbackgammon-won.wav | Bin 0 -> 82904 bytes kbackgammon/version.h | 32 + kbattleship/AUTHORS | 4 + kbattleship/CLIENTS | 13 + kbattleship/COPYING | 341 ++ kbattleship/ChangeLog | 1 + kbattleship/INSTALL | 167 + kbattleship/Makefile.am | 1 + kbattleship/NEWS | 45 + kbattleship/README | 23 + kbattleship/TODO | 22 + kbattleship/VERSION | 1 + kbattleship/configure.in.in | 2 + kbattleship/kbattleship/Makefile.am | 39 + kbattleship/kbattleship/_kbattleship._tcp | 4 + kbattleship/kbattleship/dialogs/Makefile.am | 14 + kbattleship/kbattleship/dialogs/chatDlg.ui | 91 + kbattleship/kbattleship/dialogs/connectDlg.ui | 183 + kbattleship/kbattleship/dialogs/infoDlg.ui | 248 + kbattleship/kbattleship/dialogs/serverDlg.ui | 132 + kbattleship/kbattleship/dialogs/statDlg.ui | 465 ++ kbattleship/kbattleship/eventsrc | 475 ++ kbattleship/kbattleship/kbaiplayer.cpp | 107 + kbattleship/kbattleship/kbaiplayer.h | 59 + kbattleship/kbattleship/kbattlefield.cpp | 233 + kbattleship/kbattleship/kbattlefield.h | 82 + kbattleship/kbattleship/kbattleship.cpp | 1352 +++++ kbattleship/kbattleship/kbattleship.desktop | 76 + kbattleship/kbattleship/kbattleship.h | 162 + kbattleship/kbattleship/kbattleshipclient.cpp | 84 + kbattleship/kbattleship/kbattleshipclient.h | 49 + kbattleship/kbattleship/kbattleshipserver.cpp | 124 + kbattleship/kbattleship/kbattleshipserver.h | 60 + kbattleship/kbattleship/kbattleshipui.rc | 17 + kbattleship/kbattleship/kbattleshipview.cpp | 295 ++ kbattleship/kbattleship/kbattleshipview.h | 72 + kbattleship/kbattleship/kbchooserstrategy.cpp | 139 + kbattleship/kbattleship/kbchooserstrategy.h | 46 + .../kbattleship/kbdestroyshipstrategy.cpp | 390 ++ .../kbattleship/kbdestroyshipstrategy.h | 55 + .../kbattleship/kbdiagonalshotstrategy.cpp | 110 + .../kbattleship/kbdiagonalshotstrategy.h | 43 + .../kbattleship/kbdiagonalwrapstrategy.cpp | 320 ++ .../kbattleship/kbdiagonalwrapstrategy.h | 53 + .../kbattleship/kbhorizontalstepstrategy.cpp | 210 + .../kbattleship/kbhorizontalstepstrategy.h | 48 + .../kbattleship/kbrandomshotstrategy.cpp | 102 + .../kbattleship/kbrandomshotstrategy.h | 47 + kbattleship/kbattleship/kbstrategy.cpp | 108 + kbattleship/kbattleship/kbstrategy.h | 52 + .../kbattleship/kbverticalstepstrategy.cpp | 214 + .../kbattleship/kbverticalstepstrategy.h | 49 + kbattleship/kbattleship/kchatwidget.cpp | 83 + kbattleship/kbattleship/kchatwidget.h | 53 + kbattleship/kbattleship/kclientdialog.cpp | 140 + kbattleship/kbattleship/kclientdialog.h | 61 + kbattleship/kbattleship/kgridwidget.cpp | 416 ++ kbattleship/kbattleship/kgridwidget.h | 66 + kbattleship/kbattleship/kmessage.cpp | 101 + kbattleship/kbattleship/kmessage.h | 49 + .../kbattleship/konnectionhandling.cpp | 246 + kbattleship/kbattleship/konnectionhandling.h | 73 + kbattleship/kbattleship/kserverdialog.cpp | 67 + kbattleship/kbattleship/kserverdialog.h | 49 + kbattleship/kbattleship/kship.cpp | 105 + kbattleship/kbattleship/kship.h | 46 + kbattleship/kbattleship/kshiplist.cpp | 175 + kbattleship/kbattleship/kshiplist.h | 58 + kbattleship/kbattleship/kstatdialog.cpp | 92 + kbattleship/kbattleship/kstatdialog.h | 48 + kbattleship/kbattleship/main.cpp | 70 + kbattleship/kbattleship/pictures/Makefile.am | 30 + kbattleship/kbattleship/pictures/border.png | Bin 0 -> 735 bytes kbattleship/kbattleship/pictures/death.png | Bin 0 -> 2690 bytes .../pictures/hi128-app-kbattleship.png | Bin 0 -> 10105 bytes .../pictures/hi16-app-kbattleship.png | Bin 0 -> 515 bytes .../pictures/hi22-app-kbattleship.png | Bin 0 -> 3489 bytes .../pictures/hi32-app-kbattleship.png | Bin 0 -> 1334 bytes .../pictures/hi48-app-kbattleship.png | Bin 0 -> 2591 bytes .../pictures/hi64-app-kbattleship.png | Bin 0 -> 3848 bytes kbattleship/kbattleship/pictures/hit.png | Bin 0 -> 2130 bytes kbattleship/kbattleship/pictures/sea.png | Bin 0 -> 1695 bytes .../kbattleship/pictures/ship1-1-r.png | Bin 0 -> 812 bytes kbattleship/kbattleship/pictures/ship1-1.png | Bin 0 -> 879 bytes .../kbattleship/pictures/ship1-view.png | Bin 0 -> 812 bytes .../kbattleship/pictures/ship2-1-r.png | Bin 0 -> 636 bytes kbattleship/kbattleship/pictures/ship2-1.png | Bin 0 -> 823 bytes .../kbattleship/pictures/ship2-2-r.png | Bin 0 -> 732 bytes kbattleship/kbattleship/pictures/ship2-2.png | Bin 0 -> 938 bytes .../kbattleship/pictures/ship2-view.png | Bin 0 -> 1171 bytes .../kbattleship/pictures/ship3-1-r.png | Bin 0 -> 872 bytes kbattleship/kbattleship/pictures/ship3-1.png | Bin 0 -> 800 bytes .../kbattleship/pictures/ship3-2-r.png | Bin 0 -> 932 bytes kbattleship/kbattleship/pictures/ship3-2.png | Bin 0 -> 963 bytes .../kbattleship/pictures/ship3-3-r.png | Bin 0 -> 923 bytes kbattleship/kbattleship/pictures/ship3-3.png | Bin 0 -> 922 bytes .../kbattleship/pictures/ship3-view.png | Bin 0 -> 879 bytes .../kbattleship/pictures/ship4-1-r.png | Bin 0 -> 627 bytes kbattleship/kbattleship/pictures/ship4-1.png | Bin 0 -> 607 bytes .../kbattleship/pictures/ship4-2-r.png | Bin 0 -> 575 bytes kbattleship/kbattleship/pictures/ship4-2.png | Bin 0 -> 594 bytes .../kbattleship/pictures/ship4-3-r.png | Bin 0 -> 657 bytes kbattleship/kbattleship/pictures/ship4-3.png | Bin 0 -> 645 bytes .../kbattleship/pictures/ship4-4-r.png | Bin 0 -> 971 bytes kbattleship/kbattleship/pictures/ship4-4.png | Bin 0 -> 895 bytes .../kbattleship/pictures/ship4-view.png | Bin 0 -> 969 bytes kbattleship/kbattleship/pictures/water.png | Bin 0 -> 2808 bytes kbattleship/kbattleship/sounds/Makefile.am | 5 + .../sounds/ship-player-shoot-water.ogg | Bin 0 -> 37703 bytes .../kbattleship/sounds/ship-player1-shoot.ogg | Bin 0 -> 55778 bytes .../kbattleship/sounds/ship-player2-shoot.ogg | Bin 0 -> 55778 bytes kbattleship/kbattleship/sounds/ship-sink.ogg | Bin 0 -> 22344 bytes kblackbox/CHANGES | 52 + kblackbox/Makefile.am | 22 + kblackbox/README | 28 + kblackbox/VERSION | 2 + kblackbox/big_kblackbox.xpm | 269 + kblackbox/hi128-app-kblackbox.png | Bin 0 -> 9260 bytes kblackbox/hi16-app-kblackbox.png | Bin 0 -> 735 bytes kblackbox/hi22-app-kblackbox.png | Bin 0 -> 1187 bytes kblackbox/hi32-app-kblackbox.png | Bin 0 -> 1824 bytes kblackbox/hi48-app-kblackbox.png | Bin 0 -> 3146 bytes kblackbox/hi64-app-kblackbox.png | Bin 0 -> 4202 bytes kblackbox/kbbgame.cpp | 740 +++ kblackbox/kbbgame.h | 104 + kblackbox/kbbgfx.cpp | 473 ++ kblackbox/kbbgfx.h | 120 + kblackbox/kblackbox.desktop | 77 + kblackbox/kblackboxui.rc | 24 + kblackbox/main.cpp | 46 + kblackbox/pics/Makefile.am | 10 + kblackbox/pics/blue.xpm | 199 + kblackbox/pics/brown.xpm | 77 + kblackbox/pics/cyan.xpm | 193 + kblackbox/pics/done.xpm | 33 + kblackbox/pics/giveup.xpm | 28 + kblackbox/pics/gray.xpm | 150 + kblackbox/pics/green.xpm | 218 + kblackbox/pics/green2.xpm | 300 ++ kblackbox/pics/red.xpm | 202 + kblackbox/pics/white.xpm | 100 + kblackbox/util.cpp | 68 + kblackbox/util.h | 42 + kblackbox/version.h | 1 + kbounce/Makefile.am | 76 + kbounce/RULES | 25 + kbounce/configure.in.in | 4 + kbounce/game.cpp | 699 +++ kbounce/game.h | 183 + kbounce/highscores.cpp | 18 + kbounce/highscores.h | 17 + kbounce/kbounce.cpp | 491 ++ kbounce/kbounce.desktop | 67 + kbounce/kbounce.h | 104 + kbounce/kbounceui.rc | 17 + kbounce/main.cpp | 75 + kbounce/pics/Makefile.am | 34 + kbounce/pics/ball0000.png | Bin 0 -> 641 bytes kbounce/pics/ball0001.png | Bin 0 -> 622 bytes kbounce/pics/ball0002.png | Bin 0 -> 616 bytes kbounce/pics/ball0003.png | Bin 0 -> 598 bytes kbounce/pics/ball0004.png | Bin 0 -> 593 bytes kbounce/pics/ball0005.png | Bin 0 -> 595 bytes kbounce/pics/ball0006.png | Bin 0 -> 589 bytes kbounce/pics/ball0007.png | Bin 0 -> 591 bytes kbounce/pics/ball0008.png | Bin 0 -> 596 bytes kbounce/pics/ball0009.png | Bin 0 -> 592 bytes kbounce/pics/ball0010.png | Bin 0 -> 603 bytes kbounce/pics/ball0011.png | Bin 0 -> 613 bytes kbounce/pics/ball0012.png | Bin 0 -> 623 bytes kbounce/pics/ball0013.png | Bin 0 -> 627 bytes kbounce/pics/ball0014.png | Bin 0 -> 635 bytes kbounce/pics/ball0015.png | Bin 0 -> 624 bytes kbounce/pics/ball0016.png | Bin 0 -> 608 bytes kbounce/pics/ball0017.png | Bin 0 -> 614 bytes kbounce/pics/ball0018.png | Bin 0 -> 609 bytes kbounce/pics/ball0019.png | Bin 0 -> 610 bytes kbounce/pics/ball0020.png | Bin 0 -> 604 bytes kbounce/pics/ball0021.png | Bin 0 -> 609 bytes kbounce/pics/ball0022.png | Bin 0 -> 629 bytes kbounce/pics/ball0023.png | Bin 0 -> 638 bytes kbounce/pics/ball0024.png | Bin 0 -> 645 bytes kbounce/pics/hi128-app-kbounce.png | Bin 0 -> 12438 bytes kbounce/pics/hi16-app-kbounce.png | Bin 0 -> 822 bytes kbounce/pics/hi22-app-kbounce.png | Bin 0 -> 1298 bytes kbounce/pics/hi32-app-kbounce.png | Bin 0 -> 2175 bytes kbounce/pics/hi48-app-kbounce.png | Bin 0 -> 3697 bytes kbounce/pics/hi64-app-kbounce.png | Bin 0 -> 5157 bytes kbounce/pics/tiles.png | Bin 0 -> 753 bytes kbounce/sounds/Makefile.am | 7 + kbounce/sounds/death.au | Bin 0 -> 1407 bytes kbounce/sounds/reflect.au | Bin 0 -> 716 bytes kbounce/sounds/seconds.au | Bin 0 -> 362 bytes kbounce/sounds/timeout.au | Bin 0 -> 23239 bytes kbounce/sounds/wallend.au | Bin 0 -> 759 bytes kbounce/sounds/wallstart.au | Bin 0 -> 1271 bytes kdegames.lsm | 11 + kenolaba/AbTop.cpp | 988 ++++ kenolaba/AbTop.h | 152 + kenolaba/Ball.cpp | 492 ++ kenolaba/Ball.h | 155 + kenolaba/Board.cpp | 1493 ++++++ kenolaba/Board.h | 198 + kenolaba/BoardWidget.cpp | 1027 ++++ kenolaba/BoardWidget.h | 116 + kenolaba/ChangeLog | 35 + kenolaba/EvalDlg.ui | 1760 +++++++ kenolaba/EvalDlgImpl.cpp | 299 ++ kenolaba/EvalDlgImpl.h | 44 + kenolaba/EvalScheme.cpp | 231 + kenolaba/EvalScheme.h | 61 + kenolaba/Makefile.am | 21 + kenolaba/Move.cpp | 261 + kenolaba/Move.h | 132 + kenolaba/Network.cpp | 193 + kenolaba/Network.h | 60 + kenolaba/README | 23 + kenolaba/Spy.cpp | 155 + kenolaba/Spy.h | 46 + kenolaba/TODO | 8 + kenolaba/bitmaps/Arrow1 | 8 + kenolaba/bitmaps/Arrow1Mask | 6 + kenolaba/bitmaps/Arrow2 | 8 + kenolaba/bitmaps/Arrow2Mask | 6 + kenolaba/bitmaps/Arrow3 | 8 + kenolaba/bitmaps/Arrow3Mask | 6 + kenolaba/bitmaps/Arrow4 | 8 + kenolaba/bitmaps/Arrow4Mask | 6 + kenolaba/bitmaps/Arrow5 | 8 + kenolaba/bitmaps/Arrow5Mask | 6 + kenolaba/bitmaps/Arrow6 | 8 + kenolaba/bitmaps/Arrow6Mask | 6 + kenolaba/bitmaps/Makefile.am | 1 + kenolaba/hi128-app-kenolaba.png | Bin 0 -> 15572 bytes kenolaba/hi16-app-kenolaba.png | Bin 0 -> 879 bytes kenolaba/hi22-app-kenolaba.png | Bin 0 -> 1369 bytes kenolaba/hi32-app-kenolaba.png | Bin 0 -> 2388 bytes kenolaba/hi48-app-kenolaba.png | Bin 0 -> 4346 bytes kenolaba/hi64-app-kenolaba.png | Bin 0 -> 6254 bytes kenolaba/kenolaba.cpp | 71 + kenolaba/kenolaba.desktop | 64 + kenolaba/kenolabaui.rc | 53 + kenolaba/toolbar/Makefile.am | 7 + kenolaba/toolbar/edit.xpm | 38 + kenolaba/toolbar/help.xpm | 29 + kenolaba/toolbar/hint.xpm | 31 + kenolaba/toolbar/network.xpm | 35 + kenolaba/toolbar/new.xpm | 36 + kenolaba/toolbar/noball.xpm | 22 + kenolaba/toolbar/ok.xpm | 25 + kenolaba/toolbar/redball.xpm | 25 + kenolaba/toolbar/spy0.xpm | 39 + kenolaba/toolbar/spy1.xpm | 38 + kenolaba/toolbar/spy2.xpm | 40 + kenolaba/toolbar/spy3.xpm | 38 + kenolaba/toolbar/stop.xpm | 30 + kenolaba/toolbar/undo.xpm | 27 + kenolaba/toolbar/warning.xpm | 26 + kenolaba/toolbar/yellowball.xpm | 25 + kenolaba/version.h | 1 + kfouleggs/CHANGELOG | 1 + kfouleggs/LICENSE | 18 + kfouleggs/Makefile.am | 75 + kfouleggs/README | 14 + kfouleggs/TODO | 3 + kfouleggs/ai.cpp | 40 + kfouleggs/ai.h | 21 + kfouleggs/board.cpp | 161 + kfouleggs/board.h | 44 + kfouleggs/eventsrc | 358 ++ kfouleggs/field.cpp | 61 + kfouleggs/field.h | 19 + kfouleggs/kfouleggs.desktop | 69 + kfouleggs/kfouleggs.kcfg | 60 + kfouleggs/kfouleggsui.rc | 27 + kfouleggs/main.cpp | 92 + kfouleggs/main.h | 34 + kfouleggs/pics/Makefile.am | 3 + kfouleggs/pics/hi128-app-kfouleggs.png | Bin 0 -> 17630 bytes kfouleggs/pics/hi16-app-kfouleggs.png | Bin 0 -> 824 bytes kfouleggs/pics/hi22-app-kfouleggs.png | Bin 0 -> 3971 bytes kfouleggs/pics/hi32-app-kfouleggs.png | Bin 0 -> 2233 bytes kfouleggs/pics/hi48-app-kfouleggs.png | Bin 0 -> 4003 bytes kfouleggs/pics/hi64-app-kfouleggs.png | Bin 0 -> 6222 bytes kfouleggs/piece.cpp | 90 + kfouleggs/piece.h | 50 + kfouleggs/prefs.kcfgc | 9 + kgoldrunner/AUTHORS | 1 + kgoldrunner/BUGS | 38 + kgoldrunner/COPYING | 339 ++ kgoldrunner/ChangeLog | 0 kgoldrunner/Doxyfile | 232 + kgoldrunner/INSTALL | 197 + kgoldrunner/Makefile.am | 1 + kgoldrunner/NEWS | 0 kgoldrunner/README | 36 + kgoldrunner/TODO | 34 + kgoldrunner/VERSION | 1 + kgoldrunner/gamedata/Makefile.am | 13 + kgoldrunner/gamedata/games.dat | 14 + kgoldrunner/gamedata/hi_level.dat | Bin 0 -> 120 bytes kgoldrunner/gamedata/hi_plws.dat | Bin 0 -> 40 bytes kgoldrunner/gamedata/hi_plwv.dat | Bin 0 -> 40 bytes kgoldrunner/gamedata/hi_wad.dat | Bin 0 -> 160 bytes kgoldrunner/gamedata/levels.tar | Bin 0 -> 317440 bytes kgoldrunner/kgoldrunner.kdevelop | 142 + kgoldrunner/kgoldrunner.lsm | 24 + kgoldrunner/src/KGoldrunner.desktop | 122 + kgoldrunner/src/Makefile.am | 42 + kgoldrunner/src/data_messages.cpp | 608 +++ kgoldrunner/src/enemy1.xpm | 25 + kgoldrunner/src/enemy2.xpm | 26 + kgoldrunner/src/hero.xpm | 26 + kgoldrunner/src/hi128-app-kgoldrunner.png | Bin 0 -> 17842 bytes kgoldrunner/src/hi16-app-kgoldrunner.png | Bin 0 -> 744 bytes kgoldrunner/src/hi22-app-kgoldrunner.png | Bin 0 -> 1211 bytes kgoldrunner/src/hi32-app-kgoldrunner.png | Bin 0 -> 2043 bytes kgoldrunner/src/hi48-app-kgoldrunner.png | Bin 0 -> 4077 bytes kgoldrunner/src/hi64-app-kgoldrunner.png | Bin 0 -> 6313 bytes kgoldrunner/src/kgoldrunner.cpp | 1097 ++++ kgoldrunner/src/kgoldrunner.h | 208 + kgoldrunner/src/kgoldrunnerui.rc | 75 + kgoldrunner/src/kgraphics.h | 357 ++ kgoldrunner/src/kgrcanvas.cpp | 561 +++ kgoldrunner/src/kgrcanvas.h | 111 + kgoldrunner/src/kgrconsts.h | 85 + kgoldrunner/src/kgrdialog.cpp | 974 ++++ kgoldrunner/src/kgrdialog.h | 218 + kgoldrunner/src/kgrfigure.cpp | 1803 +++++++ kgoldrunner/src/kgrfigure.h | 231 + kgoldrunner/src/kgrgame.cpp | 2613 ++++++++++ kgoldrunner/src/kgrgame.h | 309 ++ kgoldrunner/src/kgrobject.cpp | 196 + kgoldrunner/src/kgrobject.h | 107 + kgoldrunner/src/main.cpp | 50 + kjumpingcube/AUTHORS | 2 + kjumpingcube/ChangeLog | 27 + kjumpingcube/Makefile.am | 23 + kjumpingcube/README | 39 + kjumpingcube/brain.cpp | 621 +++ kjumpingcube/brain.h | 135 + kjumpingcube/cube.cpp | 99 + kjumpingcube/cube.h | 99 + kjumpingcube/cubebox.cpp | 292 ++ kjumpingcube/cubebox.h | 62 + kjumpingcube/cubeboxbase.h | 246 + kjumpingcube/hi128-app-kjumpingcube.png | Bin 0 -> 9130 bytes kjumpingcube/hi16-app-kjumpingcube.png | Bin 0 -> 688 bytes kjumpingcube/hi22-app-kjumpingcube.png | Bin 0 -> 1225 bytes kjumpingcube/hi32-app-kjumpingcube.png | Bin 0 -> 1723 bytes kjumpingcube/hi48-app-kjumpingcube.png | Bin 0 -> 3034 bytes kjumpingcube/hi64-app-kjumpingcube.png | Bin 0 -> 3813 bytes kjumpingcube/kcubeboxwidget.cpp | 711 +++ kjumpingcube/kcubeboxwidget.h | 188 + kjumpingcube/kcubewidget.cpp | 348 ++ kjumpingcube/kcubewidget.h | 119 + kjumpingcube/kjumpingcube.cpp | 278 ++ kjumpingcube/kjumpingcube.desktop | 102 + kjumpingcube/kjumpingcube.h | 76 + kjumpingcube/kjumpingcube.kcfg | 38 + kjumpingcube/kjumpingcubeui.rc | 20 + kjumpingcube/main.cpp | 58 + kjumpingcube/prefs.kcfgc | 8 + kjumpingcube/settings.ui | 268 + kjumpingcube/version.h | 1 + klickety/CHANGELOG | 22 + klickety/LICENSE | 18 + klickety/Makefile.am | 70 + klickety/README | 12 + klickety/board.cpp | 163 + klickety/board.h | 38 + klickety/eventsrc | 249 + klickety/field.cpp | 80 + klickety/field.h | 33 + klickety/highscores.cpp | 27 + klickety/highscores.h | 18 + klickety/klickety.desktop | 84 + klickety/klicketyui.rc | 17 + klickety/main.cpp | 73 + klickety/main.h | 29 + klickety/pics/Makefile.am | 3 + klickety/pics/hi128-app-klickety.png | Bin 0 -> 9711 bytes klickety/pics/hi16-app-klickety.png | Bin 0 -> 684 bytes klickety/pics/hi22-app-klickety.png | Bin 0 -> 1089 bytes klickety/pics/hi32-app-klickety.png | Bin 0 -> 1716 bytes klickety/pics/hi48-app-klickety.png | Bin 0 -> 3000 bytes klickety/pics/hi64-app-klickety.png | Bin 0 -> 3950 bytes klickety/piece.cpp | 52 + klickety/piece.h | 42 + klines/AUTHORS | 2 + klines/Makefile.am | 27 + klines/ballpainter.cpp | 140 + klines/ballpainter.h | 47 + klines/balls.jpg | Bin 0 -> 73953 bytes klines/cell.cpp | 40 + klines/cell.h | 40 + klines/cfg.h | 48 + klines/field.cpp | 194 + klines/field.h | 65 + klines/field.jpg | Bin 0 -> 338 bytes klines/fire.jpg | Bin 0 -> 2627 bytes klines/hi128-app-klines.png | Bin 0 -> 10828 bytes klines/hi16-app-klines.png | Bin 0 -> 889 bytes klines/hi22-app-klines.png | Bin 0 -> 1281 bytes klines/hi32-app-klines.png | Bin 0 -> 2070 bytes klines/hi48-app-klines.png | Bin 0 -> 3690 bytes klines/hi64-app-klines.png | Bin 0 -> 4483 bytes klines/klines.cpp | 585 +++ klines/klines.desktop | 111 + klines/klines.h | 93 + klines/klines.kcfg | 23 + klines/klinesui.rc | 11 + klines/linesboard.cpp | 754 +++ klines/linesboard.h | 130 + klines/main.cpp | 56 + klines/mwidget.cpp | 77 + klines/mwidget.h | 45 + klines/povray/ball.pov | 289 ++ klines/povray/balls.sh | 13 + klines/povray/clean.sh | 2 + klines/povray/field.pov | 31 + klines/povray/field.sh | 5 + klines/povray/fire.sh | 9 + klines/povray/makeballs.sh | 36 + klines/povray/povray.ini | 59 + klines/prefs.kcfgc | 7 + klines/prompt.cpp | 89 + klines/prompt.h | 53 + klines/templates/cpp_template | 16 + klines/templates/header_template | 16 + kmahjongg/Background.cpp | 115 + kmahjongg/Background.h | 36 + kmahjongg/BoardLayout.cpp | 300 ++ kmahjongg/BoardLayout.h | 62 + kmahjongg/ChangeLog | 97 + kmahjongg/Editor.cpp | 675 +++ kmahjongg/Editor.h | 68 + kmahjongg/GameTimer.cpp | 94 + kmahjongg/GameTimer.h | 53 + kmahjongg/HighScore.cpp | 541 ++ kmahjongg/HighScore.h | 77 + kmahjongg/KmTypes.h | 27 + kmahjongg/Makefile.am | 25 + kmahjongg/Preview.cpp | 513 ++ kmahjongg/Preview.h | 104 + kmahjongg/Tileset.cpp | 281 ++ kmahjongg/Tileset.h | 107 + kmahjongg/boardwidget.cpp | 2023 ++++++++ kmahjongg/boardwidget.h | 244 + kmahjongg/hi128-app-kmahjongg.png | Bin 0 -> 9847 bytes kmahjongg/hi16-app-kmahjongg.png | Bin 0 -> 679 bytes kmahjongg/hi22-app-kmahjongg.png | Bin 0 -> 3688 bytes kmahjongg/hi32-app-kmahjongg.png | Bin 0 -> 1642 bytes kmahjongg/hi48-app-kmahjongg.png | Bin 0 -> 2825 bytes kmahjongg/hi64-app-kmahjongg.png | Bin 0 -> 3952 bytes kmahjongg/kmahjongg.cpp | 562 +++ kmahjongg/kmahjongg.desktop | 72 + kmahjongg/kmahjongg.h | 119 + kmahjongg/kmahjongg.kcfg | 46 + kmahjongg/kmahjonggui.rc | 102 + kmahjongg/main.cpp | 38 + kmahjongg/pics/Makefile.am | 19 + kmahjongg/pics/cross.layout | 81 + kmahjongg/pics/default.bgnd | Bin 0 -> 41434 bytes kmahjongg/pics/default.layout | 86 + kmahjongg/pics/default.theme | 4 + kmahjongg/pics/default.tileset | Bin 0 -> 45748 bytes kmahjongg/pics/haze.bgnd | Bin 0 -> 14708 bytes kmahjongg/pics/kmahjongg.png | Bin 0 -> 9711 bytes kmahjongg/pics/kmahjongg_bgnd.png | Bin 0 -> 27876 bytes kmahjongg/pics/newnum.xpm | 30 + kmahjongg/pics/pirates.bgnd | Bin 0 -> 286354 bytes kmahjongg/pics/pirates.layout | 81 + kmahjongg/pics/pirates.theme | 5 + kmahjongg/pics/pirates.tileset | Bin 0 -> 38860 bytes kmahjongg/pics/pyramid.layout | 86 + kmahjongg/pics/runes.tileset | Bin 0 -> 302454 bytes kmahjongg/pics/slate.bgnd | Bin 0 -> 18270 bytes kmahjongg/pics/splash.png | Bin 0 -> 19311 bytes kmahjongg/pics/stax.layout | 81 + kmahjongg/pics/test.layout | 81 + kmahjongg/pics/test2.layout | 81 + kmahjongg/pics/tower.layout | 86 + kmahjongg/pics/traditional.tileset | Bin 0 -> 47578 bytes kmahjongg/pics/triangle.layout | 86 + kmahjongg/pics/wood.bgnd | Bin 0 -> 18534 bytes kmahjongg/prefs.kcfgc | 7 + kmahjongg/settings.ui | 171 + kmahjongg/version.h | 1 + kmines/CHANGELOG | 325 ++ kmines/LICENSE | 19 + kmines/Makefile.am | 66 + kmines/README | 16 + kmines/TODO | 14 + kmines/bitmaps/Makefile.am | 2 + kmines/bitmaps/README | 1 + kmines/bitmaps/smile | 37 + kmines/bitmaps/smile_happy | 37 + kmines/bitmaps/smile_ohno | 37 + kmines/bitmaps/smile_sleep | 36 + kmines/bitmaps/smile_stress | 36 + kmines/data/Makefile.am | 7 + kmines/data/eventsrc | 988 ++++ kmines/data/hi128-app-kmines.png | Bin 0 -> 12464 bytes kmines/data/hi16-app-kmines.png | Bin 0 -> 742 bytes kmines/data/hi22-app-kmines.png | Bin 0 -> 3926 bytes kmines/data/hi32-app-kmines.png | Bin 0 -> 2061 bytes kmines/data/hi48-app-kmines.png | Bin 0 -> 3877 bytes kmines/data/hi64-app-kmines.png | Bin 0 -> 5325 bytes kmines/data/kmines.desktop | 75 + kmines/defines.cpp | 60 + kmines/defines.h | 72 + kmines/dialogs.cpp | 294 ++ kmines/dialogs.h | 125 + kmines/field.cpp | 462 ++ kmines/field.h | 118 + kmines/frame.cpp | 143 + kmines/frame.h | 56 + kmines/highscores.cpp | 94 + kmines/highscores.h | 41 + kmines/kmines.kcfg | 105 + kmines/kminesui.rc | 102 + kmines/kzoommainwindow.cpp | 115 + kmines/kzoommainwindow.h | 126 + kmines/main.cpp | 260 + kmines/main.h | 73 + kmines/settings.kcfgc | 7 + kmines/settings_addons.h | 5 + kmines/solver/Makefile.am | 18 + kmines/solver/advFastRules.cpp | 482 ++ kmines/solver/adviseFast.cpp | 201 + kmines/solver/adviseFast.h | 70 + kmines/solver/adviseFull.cpp | 655 +++ kmines/solver/adviseFull.h | 93 + kmines/solver/bfield.cpp | 221 + kmines/solver/bfield.h | 83 + kmines/solver/headerP.h | 191 + kmines/solver/solver.cpp | 249 + kmines/solver/solver.h | 84 + kmines/solver/test.cpp | 45 + kmines/solver/testFast.cpp | 30 + kmines/solver/testRate.cpp | 41 + kmines/solver/testSolve.cpp | 33 + kmines/status.cpp | 478 ++ kmines/status.h | 111 + kmines/version.h | 5 + knetwalk/AUTHORS | 6 + knetwalk/Makefile.am | 1 + knetwalk/TODO | 5 + knetwalk/configure.in.in | 2 + knetwalk/src/Makefile.am | 71 + knetwalk/src/SConscript | 23 + knetwalk/src/cell.cpp | 241 + knetwalk/src/cell.h | 61 + knetwalk/src/defines.h | 25 + knetwalk/src/eventsrc | 258 + knetwalk/src/highscores.cpp | 87 + knetwalk/src/highscores.h | 36 + knetwalk/src/knetwalk.desktop | 14 + knetwalk/src/knetwalk.kcfg | 23 + knetwalk/src/knetwalkui.rc | 9 + knetwalk/src/main.cpp | 72 + knetwalk/src/mainwindow.cpp | 421 ++ knetwalk/src/mainwindow.h | 96 + knetwalk/src/pics/Makefile.am | 9 + knetwalk/src/pics/SConscript | 31 + knetwalk/src/pics/background.png | Bin 0 -> 1749 bytes knetwalk/src/pics/background_locked.png | Bin 0 -> 1847 bytes knetwalk/src/pics/cable0001.png | Bin 0 -> 289 bytes knetwalk/src/pics/cable0010.png | Bin 0 -> 291 bytes knetwalk/src/pics/cable0011.png | Bin 0 -> 382 bytes knetwalk/src/pics/cable0100.png | Bin 0 -> 264 bytes knetwalk/src/pics/cable0101.png | Bin 0 -> 223 bytes knetwalk/src/pics/cable0110.png | Bin 0 -> 330 bytes knetwalk/src/pics/cable0111.png | Bin 0 -> 524 bytes knetwalk/src/pics/cable1000.png | Bin 0 -> 302 bytes knetwalk/src/pics/cable1001.png | Bin 0 -> 353 bytes knetwalk/src/pics/cable1010.png | Bin 0 -> 238 bytes knetwalk/src/pics/cable1011.png | Bin 0 -> 556 bytes knetwalk/src/pics/cable1100.png | Bin 0 -> 367 bytes knetwalk/src/pics/cable1101.png | Bin 0 -> 517 bytes knetwalk/src/pics/cable1110.png | Bin 0 -> 549 bytes knetwalk/src/pics/computer1.png | Bin 0 -> 534 bytes knetwalk/src/pics/computer2.png | Bin 0 -> 641 bytes knetwalk/src/pics/hi128-app-knetwalk.png | Bin 0 -> 4050 bytes knetwalk/src/pics/hi22-app-knetwalk.png | Bin 0 -> 704 bytes knetwalk/src/pics/hi32-app-knetwalk.png | Bin 0 -> 980 bytes knetwalk/src/pics/hi64-app-knetwalk.png | Bin 0 -> 1801 bytes knetwalk/src/pics/knetwalk.svgz | Bin 0 -> 1302 bytes knetwalk/src/pics/server.png | Bin 0 -> 540 bytes knetwalk/src/settings.kcfgc | 5 + knetwalk/src/sounds/Makefile.am | 4 + knetwalk/src/sounds/click.wav | Bin 0 -> 1940 bytes knetwalk/src/sounds/connect.wav | Bin 0 -> 3056 bytes knetwalk/src/sounds/start.wav | Bin 0 -> 25880 bytes knetwalk/src/sounds/turn.wav | Bin 0 -> 7804 bytes knetwalk/src/sounds/win.wav | Bin 0 -> 24794 bytes kolf/AUTHORS | 3 + kolf/COPYING | 341 ++ kolf/Makefile.am | 49 + kolf/Makefile.am.fast | 32 + kolf/PLUGINS.docbook | 1 + kolf/README.pool | 7 + kolf/TODO | 1 + kolf/ball.cpp | 466 ++ kolf/ball.h | 114 + kolf/canvasitem.cpp | 48 + kolf/canvasitem.h | 182 + kolf/config.cpp | 48 + kolf/config.h | 42 + kolf/configure.in.in | 3 + kolf/courses.list | 10 + kolf/courses/ADDING_COURSES | 8 + kolf/courses/Classic.kolf | 1986 ++++++++ kolf/courses/Easy.kolf | 1737 +++++++ kolf/courses/Hard.kolf | 4403 +++++++++++++++++ kolf/courses/Impossible | 3573 +++++++++++++ kolf/courses/Makefile.am | 4 + kolf/courses/Medium.kolf | 2557 ++++++++++ kolf/courses/Practice | 376 ++ kolf/courses/ReallyEasy | 2404 +++++++++ kolf/courses/USApro | 1982 ++++++++ kolf/editor.cpp | 60 + kolf/editor.h | 37 + kolf/floater.cpp | 277 ++ kolf/floater.h | 81 + kolf/game.cpp | 4302 ++++++++++++++++ kolf/game.h | 1026 ++++ kolf/graphics/Makefile.am | 4 + kolf/graphics/cup.png | Bin 0 -> 876 bytes kolf/graphics/grass.png | Bin 0 -> 13002 bytes kolf/graphics/puddle.png | Bin 0 -> 622 bytes kolf/graphics/sand.png | Bin 0 -> 968 bytes kolf/intro | 283 ++ kolf/kcomboboxdialog.cpp | 152 + kolf/kcomboboxdialog.h | 114 + kolf/kolf.cpp | 815 +++ kolf/kolf.desktop | 73 + kolf/kolf.h | 147 + kolf/kolf.magic | 2 + kolf/kolfui.rc | 71 + kolf/kvolumecontrol.cpp | 67 + kolf/kvolumecontrol.h | 28 + kolf/main.cpp | 93 + kolf/newgame.cpp | 347 ++ kolf/newgame.h | 106 + kolf/object.cpp | 2 + kolf/object.h | 30 + kolf/objects/Makefile.am | 1 + kolf/objects/poolball/Makefile.am | 13 + kolf/objects/poolball/poolball.cpp | 86 + kolf/objects/poolball/poolball.h | 63 + kolf/objects/poolball/poolball.plugin | 1 + kolf/objects/test/Makefile.am | 13 + kolf/objects/test/test.cpp | 104 + kolf/objects/test/test.h | 56 + kolf/objects/test/test.plugin | 1 + kolf/pics/Makefile.am | 1 + kolf/pics/hi128-app-kolf.png | Bin 0 -> 9779 bytes kolf/pics/hi16-app-kolf.png | Bin 0 -> 665 bytes kolf/pics/hi22-app-kolf.png | Bin 0 -> 926 bytes kolf/pics/hi32-app-kolf.png | Bin 0 -> 1986 bytes kolf/pics/hi48-app-kolf.png | Bin 0 -> 2909 bytes kolf/pics/hi64-app-kolf.png | Bin 0 -> 4216 bytes kolf/pluginloader.cpp | 65 + kolf/pluginloader.h | 13 + kolf/pool.kolf | 303 ++ kolf/printdialogpage.cpp | 35 + kolf/printdialogpage.h | 27 + kolf/rtti.h | 6 + kolf/scoreboard.cpp | 94 + kolf/scoreboard.h | 29 + kolf/slope.cpp | 585 +++ kolf/slope.h | 98 + kolf/sounds/FROM | 11 + kolf/sounds/Makefile.am | 4 + kolf/sounds/blackhole.wav | Bin 0 -> 391724 bytes kolf/sounds/blackholeeject.wav | Bin 0 -> 25708 bytes kolf/sounds/blackholeputin.wav | Bin 0 -> 90668 bytes kolf/sounds/hit.wav | Bin 0 -> 11980 bytes kolf/sounds/holed.wav | Bin 0 -> 10124 bytes kolf/sounds/holeinone.wav | Bin 0 -> 40200 bytes kolf/sounds/puddle.wav | Bin 0 -> 248296 bytes kolf/sounds/wall.wav | Bin 0 -> 471 bytes kolf/statedb.h | 23 + kolf/tutorial.kolf | 1245 +++++ kolf/tutorial.kolfgame | 10 + kolf/vector.cpp | 106 + kolf/vector.h | 92 + kolf/x-kolf.desktop | 60 + kolf/x-kourse.desktop | 53 + konquest/AUTHORS | 12 + konquest/ChangeLog | 26 + konquest/Konquest.cc | 34 + konquest/Makefile.am | 23 + konquest/README | 10 + konquest/TODO | 1 + konquest/fleetdlg.cc | 88 + konquest/fleetdlg.h | 29 + konquest/gameboard.cc | 991 ++++ konquest/gameboard.h | 119 + konquest/gamecore.cc | 646 +++ konquest/gamecore.h | 331 ++ konquest/gameenddlg.cc | 76 + konquest/gameenddlg.h | 32 + konquest/hi128-app-konquest.png | Bin 0 -> 13847 bytes konquest/hi16-app-konquest.png | Bin 0 -> 628 bytes konquest/hi22-app-konquest.png | Bin 0 -> 1049 bytes konquest/hi32-app-konquest.png | Bin 0 -> 1657 bytes konquest/hi48-app-konquest.png | Bin 0 -> 3185 bytes konquest/hi64-app-konquest.png | Bin 0 -> 4893 bytes konquest/images.h | 17 + konquest/int_validator.cc | 53 + konquest/int_validator.h | 31 + konquest/konquest.desktop | 74 + konquest/konquestui.rc | 21 + konquest/mainwin.cc | 77 + konquest/mainwin.h | 33 + konquest/map_widget.cc | 243 + konquest/map_widget.h | 59 + konquest/minimap.cc | 79 + konquest/minimap.h | 44 + konquest/newGameDlg_ui.ui | 440 ++ konquest/newgamedlg.cc | 325 ++ konquest/newgamedlg.h | 51 + konquest/pics/Makefile.am | 7 + konquest/pics/konquest-splash.png | Bin 0 -> 207292 bytes konquest/pics/planet1.xpm | 228 + konquest/pics/planet2.xpm | 190 + konquest/pics/planet3.xpm | 133 + konquest/pics/planet4.xpm | 122 + konquest/pics/planet5.xpm | 143 + konquest/pics/planet6.xpm | 110 + konquest/pics/planet7.xpm | 122 + konquest/pics/planet8.xpm | 189 + konquest/pics/planet9.xpm | 168 + konquest/pics/ruler.xpm | 21 + konquest/planet_info.cc | 161 + konquest/planet_info.h | 53 + konquest/scoredlg.cc | 80 + konquest/scoredlg.h | 32 + konquest/version.h | 4 + kpat/AUTHORS | 6 + kpat/CHANGES | 150 + kpat/Makefile.am | 30 + kpat/README | 41 + kpat/README.tkcTrump | 8 + kpat/TODO | 42 + kpat/card.cpp | 380 ++ kpat/card.h | 131 + kpat/cardmaps.cpp | 248 + kpat/cardmaps.h | 59 + kpat/clock.cpp | 91 + kpat/clock.h | 24 + kpat/computation.cpp | 120 + kpat/computation.h | 53 + kpat/copyright.h | 154 + kpat/dealer.cpp | 1455 ++++++ kpat/dealer.h | 228 + kpat/deck.cpp | 154 + kpat/deck.h | 46 + kpat/fortyeight.cpp | 205 + kpat/fortyeight.h | 46 + kpat/freecell-solver/CREDITS | 57 + kpat/freecell-solver/INSTALL | 70 + kpat/freecell-solver/Makefile.am | 5 + kpat/freecell-solver/Makefile.lite | 94 + kpat/freecell-solver/README | 105 + kpat/freecell-solver/USAGE | 518 ++ kpat/freecell-solver/alloc.c | 127 + kpat/freecell-solver/alloc.h | 86 + kpat/freecell-solver/app_str.c | 74 + kpat/freecell-solver/app_str.h | 39 + kpat/freecell-solver/caas.c | 629 +++ kpat/freecell-solver/caas.h | 28 + kpat/freecell-solver/card.c | 286 ++ kpat/freecell-solver/card.h | 100 + kpat/freecell-solver/cl_chop.c | 245 + kpat/freecell-solver/cl_chop.h | 19 + kpat/freecell-solver/cmd_line.c | 964 ++++ kpat/freecell-solver/fcs.h | 797 +++ kpat/freecell-solver/fcs_cl.h | 65 + kpat/freecell-solver/fcs_config.h | 95 + kpat/freecell-solver/fcs_dm.c | 146 + kpat/freecell-solver/fcs_dm.h | 49 + kpat/freecell-solver/fcs_enums.h | 77 + kpat/freecell-solver/fcs_hash.c | 291 ++ kpat/freecell-solver/fcs_hash.h | 102 + kpat/freecell-solver/fcs_isa.c | 88 + kpat/freecell-solver/fcs_isa.h | 56 + kpat/freecell-solver/fcs_move.h | 122 + kpat/freecell-solver/fcs_user.h | 275 + kpat/freecell-solver/freecell.c | 2433 +++++++++ kpat/freecell-solver/inline.h | 20 + kpat/freecell-solver/intrface.c | 1764 +++++++ kpat/freecell-solver/jhjtypes.h | 25 + kpat/freecell-solver/lib.c | 1244 +++++ kpat/freecell-solver/lookup2.c | 119 + kpat/freecell-solver/lookup2.h | 13 + kpat/freecell-solver/main.c | 859 ++++ kpat/freecell-solver/move.c | 531 ++ kpat/freecell-solver/move.h | 172 + kpat/freecell-solver/ms_ca.h | 33 + kpat/freecell-solver/pqueue.c | 173 + kpat/freecell-solver/pqueue.h | 71 + kpat/freecell-solver/prefix.h | 4 + kpat/freecell-solver/preset.c | 637 +++ kpat/freecell-solver/preset.h | 62 + kpat/freecell-solver/rand.c | 30 + kpat/freecell-solver/rand.h | 49 + kpat/freecell-solver/scans.c | 1170 +++++ kpat/freecell-solver/simpsim.c | 1716 +++++++ kpat/freecell-solver/state.c | 1114 +++++ kpat/freecell-solver/state.h | 660 +++ kpat/freecell-solver/test_arr.h | 136 + kpat/freecell-solver/tests.h | 307 ++ kpat/freecell.cpp | 854 ++++ kpat/freecell.h | 95 + kpat/gamestats.ui | 272 + kpat/gamestatsimpl.cpp | 57 + kpat/gamestatsimpl.h | 16 + kpat/golf.cpp | 169 + kpat/golf.h | 45 + kpat/grandf.cpp | 227 + kpat/grandf.h | 65 + kpat/green.png | Bin 0 -> 144 bytes kpat/gypsy.cpp | 117 + kpat/gypsy.h | 34 + kpat/hint.h | 28 + kpat/icons/Makefile.am | 6 + kpat/icons/hi128-app-kpat.png | Bin 0 -> 10455 bytes kpat/icons/hi16-app-kpat.png | Bin 0 -> 721 bytes kpat/icons/hi22-app-kpat.png | Bin 0 -> 3715 bytes kpat/icons/hi32-app-kpat.png | Bin 0 -> 1858 bytes kpat/icons/hi48-app-kpat.png | Bin 0 -> 3308 bytes kpat/icons/hi64-app-kpat.png | Bin 0 -> 4529 bytes kpat/icons/kpat-lq.png | Bin 0 -> 658 bytes kpat/idiot.cpp | 234 + kpat/idiot.h | 59 + kpat/kings.cpp | 132 + kpat/kings.h | 22 + kpat/klondike.cpp | 495 ++ kpat/klondike.h | 71 + kpat/kpat.desktop | 116 + kpat/kpatui.rc | 40 + kpat/main.cpp | 73 + kpat/mod3.cpp | 312 ++ kpat/mod3.h | 62 + kpat/napoleon.cpp | 204 + kpat/napoleon.h | 56 + kpat/pile.cpp | 463 ++ kpat/pile.h | 154 + kpat/pile_algorithms.cpp | 69 + kpat/pwidget.cpp | 560 +++ kpat/pwidget.h | 90 + kpat/simon.cpp | 156 + kpat/simon.h | 29 + kpat/speeds.h | 26 + kpat/spider.cpp | 484 ++ kpat/spider.h | 71 + kpat/version.h | 6 + kpat/yukon.cpp | 130 + kpat/yukon.h | 23 + kpoker/AUTHORS | 11 + kpoker/ChangeLog | 306 ++ kpoker/DESCRIPTION | 2 + kpoker/Makefile.am | 20 + kpoker/README | 20 + kpoker/TODO | 38 + kpoker/betbox.cpp | 136 + kpoker/betbox.h | 114 + kpoker/defines.h | 75 + kpoker/global.h | 27 + kpoker/hi128-app-kpoker.png | Bin 0 -> 8605 bytes kpoker/hi16-app-kpoker.png | Bin 0 -> 646 bytes kpoker/hi22-app-kpoker.png | Bin 0 -> 3624 bytes kpoker/hi32-app-kpoker.png | Bin 0 -> 1456 bytes kpoker/hi48-app-kpoker.png | Bin 0 -> 2505 bytes kpoker/hi64-app-kpoker.png | Bin 0 -> 3413 bytes kpoker/kpaint.cpp | 209 + kpoker/kpaint.h | 121 + kpoker/kpoker.cpp | 1484 ++++++ kpoker/kpoker.desktop | 75 + kpoker/kpoker.h | 364 ++ kpoker/kpokerui.rc | 30 + kpoker/main.cpp | 54 + kpoker/newgamedlg.cpp | 204 + kpoker/newgamedlg.h | 104 + kpoker/optionsdlg.cpp | 119 + kpoker/optionsdlg.h | 68 + kpoker/player.cpp | 292 ++ kpoker/player.h | 220 + kpoker/playerbox.cpp | 262 + kpoker/playerbox.h | 154 + kpoker/poker.cpp | 531 ++ kpoker/poker.h | 234 + kpoker/sound.cpp | 47 + kpoker/sounds/Makefile.am | 6 + kpoker/sounds/cardflip.wav | Bin 0 -> 1172 bytes kpoker/sounds/hold.wav | Bin 0 -> 1334 bytes kpoker/sounds/lose.wav | Bin 0 -> 5964 bytes kpoker/sounds/win.wav | Bin 0 -> 21831 bytes kpoker/top.cpp | 266 + kpoker/top.h | 70 + kpoker/version.h | 3 + kreversi/AUTHORS | 2 + kreversi/ChangeLog | 553 +++ kreversi/DESIGN | 121 + kreversi/Engine.cpp | 787 +++ kreversi/Engine.h | 245 + kreversi/Game.cpp | 265 + kreversi/Game.h | 143 + kreversi/Makefile.am | 101 + kreversi/Move.cpp | 118 + kreversi/Move.h | 124 + kreversi/NEWS | 20 + kreversi/Position.cpp | 366 ++ kreversi/Position.h | 98 + kreversi/Score.cpp | 70 + kreversi/Score.h | 78 + kreversi/SuperEngine.cpp | 133 + kreversi/SuperEngine.h | 142 + kreversi/TODO | 87 + kreversi/board.cpp | 576 +++ kreversi/board.h | 150 + kreversi/highscores.cpp | 104 + kreversi/highscores.h | 50 + kreversi/icons/Makefile.am | 1 + kreversi/icons/cr16-action-lastmoves.png | Bin 0 -> 593 bytes kreversi/icons/cr16-action-legalmoves.png | Bin 0 -> 573 bytes kreversi/icons/cr22-action-lastmoves.png | Bin 0 -> 780 bytes kreversi/icons/cr22-action-legalmoves.png | Bin 0 -> 772 bytes kreversi/icons/cr32-action-lastmoves.png | Bin 0 -> 1117 bytes kreversi/icons/cr32-action-legalmoves.png | Bin 0 -> 1080 bytes kreversi/icons/cr48-action-lastmoves.png | Bin 0 -> 1592 bytes kreversi/icons/cr48-action-legalmoves.png | Bin 0 -> 1576 bytes kreversi/icons/crsc-action-lastmoves.svgz | Bin 0 -> 2303 bytes kreversi/icons/crsc-action-legalmoves.svgz | Bin 0 -> 2457 bytes kreversi/icons/hi128-app-kreversi.png | Bin 0 -> 12369 bytes kreversi/icons/hi16-app-kreversi.png | Bin 0 -> 865 bytes kreversi/icons/hi22-app-kreversi.png | Bin 0 -> 3931 bytes kreversi/icons/hi32-app-kreversi.png | Bin 0 -> 2265 bytes kreversi/icons/hi48-app-kreversi.png | Bin 0 -> 3484 bytes kreversi/icons/hi64-app-kreversi.png | Bin 0 -> 4754 bytes kreversi/kreversi.cpp | 841 ++++ kreversi/kreversi.desktop | 74 + kreversi/kreversi.h | 178 + kreversi/kreversi.kcfg | 66 + kreversi/kreversiui.rc | 45 + kreversi/kzoommainwindow.cpp | 135 + kreversi/kzoommainwindow.h | 138 + kreversi/main.cpp | 87 + kreversi/pics/Makefile.am | 8 + kreversi/pics/background/Dark_Wood.png | Bin 0 -> 1330 bytes kreversi/pics/background/Earth.png | Bin 0 -> 23427 bytes kreversi/pics/background/Granite.png | Bin 0 -> 11228 bytes kreversi/pics/background/Hexagon.png | Bin 0 -> 334 bytes kreversi/pics/background/Light_Wood.png | Bin 0 -> 2072 bytes kreversi/pics/background/Makefile.am | 8 + kreversi/pics/background/Mystique.png | Bin 0 -> 13323 bytes kreversi/pics/background/Ocean.png | Bin 0 -> 12421 bytes kreversi/pics/background/Pipes.png | Bin 0 -> 14716 bytes kreversi/pics/background/Puzzle.png | Bin 0 -> 11478 bytes kreversi/pics/background/Stones.png | Bin 0 -> 29782 bytes kreversi/pics/chips.png | Bin 0 -> 26778 bytes kreversi/pics/chips_mono.png | Bin 0 -> 24796 bytes kreversi/prefs.kcfgc | 7 + kreversi/qreversigame.cpp | 93 + kreversi/qreversigame.h | 101 + kreversi/qreversigameview.cpp | 299 ++ kreversi/qreversigameview.h | 160 + kreversi/settings.ui | 329 ++ kreversi/sounds/Makefile.am | 7 + kreversi/sounds/eventsrc | 566 +++ kreversi/sounds/reversi-click.wav | Bin 0 -> 2474 bytes kreversi/sounds/reversi-won.wav | Bin 0 -> 40200 bytes kreversi/version.h | 1 + ksame/ChangeLog | 17 + ksame/KSameWidget.cpp | 246 + ksame/KSameWidget.h | 79 + ksame/Makefile.am | 24 + ksame/StoneField.cpp | 399 ++ ksame/StoneField.h | 111 + ksame/StoneWidget.cpp | 345 ++ ksame/StoneWidget.h | 107 + ksame/eventsrc | 391 ++ ksame/hi128-app-ksame.png | Bin 0 -> 16637 bytes ksame/hi16-app-ksame.png | Bin 0 -> 800 bytes ksame/hi22-app-ksame.png | Bin 0 -> 1328 bytes ksame/hi32-app-ksame.png | Bin 0 -> 2155 bytes ksame/hi48-app-ksame.png | Bin 0 -> 3972 bytes ksame/hi64-app-ksame.png | Bin 0 -> 5886 bytes ksame/ksame.desktop | 119 + ksame/ksameui.rc | 17 + ksame/main.cpp | 55 + ksame/stones.png | Bin 0 -> 63981 bytes ksame/version.h | 1 + kshisen/ChangeLog | 113 + kshisen/Makefile.am | 28 + kshisen/app.cpp | 760 +++ kshisen/app.h | 122 + kshisen/board.cpp | 1082 ++++ kshisen/board.h | 189 + kshisen/debug.h | 1 + kshisen/hi128-app-kshisen.png | Bin 0 -> 12289 bytes kshisen/hi16-app-kshisen.png | Bin 0 -> 721 bytes kshisen/hi22-app-kshisen.png | Bin 0 -> 3825 bytes kshisen/hi32-app-kshisen.png | Bin 0 -> 1967 bytes kshisen/hi48-app-kshisen.png | Bin 0 -> 3576 bytes kshisen/hi64-app-kshisen.png | Bin 0 -> 5039 bytes kshisen/kshisen.desktop | 76 + kshisen/kshisen.kcfg | 35 + kshisen/kshisenui.rc | 16 + kshisen/main.cpp | 74 + kshisen/pics/Makefile.am | 5 + kshisen/pics/kshisen_bgnd.png | Bin 0 -> 82905 bytes kshisen/pics/tileset.png | Bin 0 -> 14708 bytes kshisen/prefs.kcfgc | 6 + kshisen/settings.ui | 255 + kshisen/tileset.cpp | 147 + kshisen/tileset.h | 59 + kshisen/version.h | 14 + ksirtet/CHANGELOG | 301 ++ ksirtet/DESIGN | 57 + ksirtet/LICENSE | 19 + ksirtet/Makefile.am | 4 + ksirtet/README | 18 + ksirtet/TODO | 8 + ksirtet/configure.in.in | 2 + ksirtet/ksirtet/Makefile.am | 81 + ksirtet/ksirtet/ai.cpp | 25 + ksirtet/ksirtet/ai.h | 17 + ksirtet/ksirtet/board.cpp | 181 + ksirtet/ksirtet/board.h | 50 + ksirtet/ksirtet/check_score.cpp | 59 + ksirtet/ksirtet/eventsrc | 360 ++ ksirtet/ksirtet/field.cpp | 61 + ksirtet/ksirtet/field.h | 19 + ksirtet/ksirtet/hi128-app-ksirtet.png | Bin 0 -> 15270 bytes ksirtet/ksirtet/hi16-app-ksirtet.png | Bin 0 -> 811 bytes ksirtet/ksirtet/hi22-app-ksirtet.png | Bin 0 -> 1338 bytes ksirtet/ksirtet/hi32-app-ksirtet.png | Bin 0 -> 2282 bytes ksirtet/ksirtet/hi48-app-ksirtet.png | Bin 0 -> 4163 bytes ksirtet/ksirtet/hi64-app-ksirtet.png | Bin 0 -> 6098 bytes ksirtet/ksirtet/ksirtet.desktop | 68 + ksirtet/ksirtet/ksirtet.kcfg | 55 + ksirtet/ksirtet/ksirtetui.rc | 29 + ksirtet/ksirtet/main.cpp | 91 + ksirtet/ksirtet/main.h | 36 + ksirtet/ksirtet/piece.cpp | 82 + ksirtet/ksirtet/piece.h | 63 + ksirtet/ksirtet/prefs.kcfgc | 9 + ksirtet/ksirtet/settings.cpp | 17 + ksirtet/ksirtet/settings.h | 14 + ksmiletris/COPYRIGHT | 22 + ksmiletris/Makefile.am | 21 + ksmiletris/README | 1 + ksmiletris/data/Makefile.am | 16 + ksmiletris/data/bg1.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg10.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg11.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg12.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg13.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg14.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg15.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg16.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg2.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg3.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg4.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg5.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg6.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg7.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg8.bmp | Bin 0 -> 3126 bytes ksmiletris/data/bg9.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a1.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a2.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a3.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a4.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a5.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a6.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a7.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-a8.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b1.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b2.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b3.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b4.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b5.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b6.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b7.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-b8.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c1.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c2.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c3.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c4.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c5.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c6.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c7.bmp | Bin 0 -> 3126 bytes ksmiletris/data/block-c8.bmp | Bin 0 -> 3126 bytes ksmiletris/data/broken-mask.bmp | Bin 0 -> 3126 bytes ksmiletris/data/broken.bmp | Bin 0 -> 3126 bytes ksmiletris/data/cleared-a.bmp | Bin 0 -> 3126 bytes ksmiletris/data/cleared-b.bmp | Bin 0 -> 3126 bytes ksmiletris/data/cleared-c.bmp | Bin 0 -> 3126 bytes ksmiletris/data/pause.bmp | Bin 0 -> 41094 bytes ksmiletris/gamewidget.cpp | 518 ++ ksmiletris/gamewidget.h | 112 + ksmiletris/gamewindow.cpp | 204 + ksmiletris/gamewindow.h | 63 + ksmiletris/hi128-app-ksmiletris.png | Bin 0 -> 13287 bytes ksmiletris/hi16-app-ksmiletris.png | Bin 0 -> 877 bytes ksmiletris/hi22-app-ksmiletris.png | Bin 0 -> 1605 bytes ksmiletris/hi32-app-ksmiletris.png | Bin 0 -> 2224 bytes ksmiletris/hi48-app-ksmiletris.png | Bin 0 -> 4225 bytes ksmiletris/hi64-app-ksmiletris.png | Bin 0 -> 6172 bytes ksmiletris/ksmiletris.desktop | 78 + ksmiletris/ksmiletris.h | 86 + ksmiletris/ksmiletrisui.rc | 12 + ksmiletris/main.cpp | 53 + ksmiletris/mirrorwidget.cpp | 58 + ksmiletris/mirrorwidget.h | 49 + ksmiletris/npiecewidget.cpp | 63 + ksmiletris/npiecewidget.h | 49 + ksmiletris/screenwidget.cpp | 68 + ksmiletris/screenwidget.h | 49 + ksmiletris/wav/Makefile.am | 7 + ksmiletris/wav/break.wav | Bin 0 -> 12250 bytes ksmiletris/wav/clear.wav | Bin 0 -> 18450 bytes ksnake/ChangeLog | 21 + ksnake/Makefile.am | 34 + ksnake/appearance.ui | 87 + ksnake/ball.cpp | 128 + ksnake/ball.h | 62 + ksnake/basket.cpp | 118 + ksnake/basket.h | 70 + ksnake/bitmaps.h | 106 + ksnake/board.cpp | 330 ++ ksnake/board.h | 100 + ksnake/data/Makefile.am | 9 + ksnake/data/backgrounds/Bark.png | Bin 0 -> 29349 bytes ksnake/data/backgrounds/Blue_Carpet.png | Bin 0 -> 67082 bytes ksnake/data/backgrounds/Dark_Wood.png | Bin 0 -> 8630 bytes ksnake/data/backgrounds/Granite.png | Bin 0 -> 5679 bytes ksnake/data/backgrounds/Green_Carpet.png | Bin 0 -> 30829 bytes ksnake/data/backgrounds/Makefile.am | 8 + ksnake/data/backgrounds/Mystique.png | Bin 0 -> 6242 bytes ksnake/data/backgrounds/Rope_Weave.png | Bin 0 -> 9455 bytes ksnake/data/backgrounds/Volcanic.png | Bin 0 -> 8248 bytes ksnake/data/backgrounds/Wood.png | Bin 0 -> 27667 bytes ksnake/data/highScores | Bin 0 -> 130 bytes ksnake/data/levels/Makefile.am | 9 + ksnake/data/levels/room01 | 18 + ksnake/data/levels/room02 | 18 + ksnake/data/levels/room03 | 18 + ksnake/data/levels/room04 | 18 + ksnake/data/levels/room05 | 18 + ksnake/data/levels/room06 | 18 + ksnake/data/levels/room07 | 18 + ksnake/data/levels/room08 | 18 + ksnake/data/levels/room09 | 18 + ksnake/data/levels/room10 | 18 + ksnake/data/levels/room11 | 18 + ksnake/data/levels/room12 | 18 + ksnake/data/levels/room13 | 18 + ksnake/data/levels/room14 | 18 + ksnake/data/levels/room15 | 18 + ksnake/data/levels/room16 | 18 + ksnake/data/levels/room17 | 18 + ksnake/data/levels/room18 | 18 + ksnake/data/levels/room19 | 18 + ksnake/data/levels/room20 | 18 + ksnake/data/levels/room21 | 18 + ksnake/data/levels/room22 | 18 + ksnake/data/levels/room23 | 18 + ksnake/data/levels/room24 | 18 + ksnake/data/levels/room25 | 18 + ksnake/data/pixmaps/Makefile.am | 5 + ksnake/data/pixmaps/apples.png | Bin 0 -> 284 bytes ksnake/data/pixmaps/ball.png | Bin 0 -> 295 bytes ksnake/data/pixmaps/brick.png | Bin 0 -> 25582 bytes ksnake/data/pixmaps/snake1.png | Bin 0 -> 483 bytes ksnake/data/pixmaps/snake2.png | Bin 0 -> 468 bytes ksnake/game.cpp | 180 + ksnake/game.h | 64 + ksnake/general.ui | 202 + ksnake/hi128-app-ksnake.png | Bin 0 -> 17498 bytes ksnake/hi16-app-ksnake.png | Bin 0 -> 695 bytes ksnake/hi22-app-ksnake.png | Bin 0 -> 3755 bytes ksnake/hi32-app-ksnake.png | Bin 0 -> 1862 bytes ksnake/hi48-app-ksnake.png | Bin 0 -> 3453 bytes ksnake/hi64-app-ksnake.png | Bin 0 -> 5522 bytes ksnake/ksnake.desktop | 82 + ksnake/ksnake.kcfg | 50 + ksnake/ksnakeui.rc | 5 + ksnake/level.cpp | 152 + ksnake/level.h | 55 + ksnake/levels.cpp | 58 + ksnake/levels.h | 45 + ksnake/main.cpp | 54 + ksnake/pixServer.cpp | 245 + ksnake/pixServer.h | 76 + ksnake/progress.cpp | 54 + ksnake/progress.h | 47 + ksnake/rattler.cpp | 692 +++ ksnake/rattler.h | 155 + ksnake/settings.kcfgc | 5 + ksnake/snake.cpp | 557 +++ ksnake/snake.h | 140 + ksnake/startroom.cpp | 81 + ksnake/startroom.h | 50 + ksnake/version.h | 1 + ksnake/view.cpp | 59 + ksnake/view.h | 48 + ksokoban/AUTHORS | 36 + ksokoban/Bookmark.cpp | 108 + ksokoban/Bookmark.h | 51 + ksokoban/History.cpp | 131 + ksokoban/History.h | 64 + ksokoban/HtmlPrinter.cpp | 100 + ksokoban/HtmlPrinter.h | 18 + ksokoban/ImageData.cpp | 213 + ksokoban/ImageData.h | 87 + ksokoban/InternalCollections.cpp | 148 + ksokoban/InternalCollections.h | 54 + ksokoban/LevelCollection.cpp | 424 ++ ksokoban/LevelCollection.h | 66 + ksokoban/LevelMap.cpp | 205 + ksokoban/LevelMap.h | 66 + ksokoban/MainWindow.cpp | 364 ++ ksokoban/MainWindow.h | 80 + ksokoban/Makefile.am | 23 + ksokoban/Map.cpp | 204 + ksokoban/Map.h | 122 + ksokoban/MapDelta.cpp | 63 + ksokoban/MapDelta.h | 44 + ksokoban/ModalLabel.cpp | 115 + ksokoban/ModalLabel.h | 39 + ksokoban/Move.cpp | 213 + ksokoban/Move.h | 115 + ksokoban/MoveSequence.cpp | 81 + ksokoban/MoveSequence.h | 45 + ksokoban/NEWS | 73 + ksokoban/PathFinder.cpp | 176 + ksokoban/PathFinder.h | 48 + ksokoban/PlayField.cpp | 1044 ++++ ksokoban/PlayField.h | 149 + ksokoban/Queue.h | 56 + ksokoban/README | 33 + ksokoban/StaticImage.cpp | 86 + ksokoban/StaticImage.h | 33 + ksokoban/TODO | 5 + ksokoban/data/Makefile.am | 5 + ksokoban/data/hi128-app-ksokoban.png | Bin 0 -> 20231 bytes ksokoban/data/hi16-app-ksokoban.png | Bin 0 -> 907 bytes ksokoban/data/hi22-app-ksokoban.png | Bin 0 -> 1412 bytes ksokoban/data/hi32-app-ksokoban.png | Bin 0 -> 2451 bytes ksokoban/data/hi48-app-ksokoban.png | Bin 0 -> 4582 bytes ksokoban/data/hi64-app-ksokoban.png | Bin 0 -> 6908 bytes ksokoban/data/ksokoban.desktop | 70 + ksokoban/images/Makefile.am | 88 + ksokoban/images/bin2c.c | 256 + ksokoban/images/floor_common.inc | 19 + ksokoban/images/goal.png | Bin 0 -> 7467 bytes ksokoban/images/goal.pov | 34 + ksokoban/images/halfstone_1.png | Bin 0 -> 4468 bytes ksokoban/images/halfstone_1.pov | 17 + ksokoban/images/halfstone_2.png | Bin 0 -> 4203 bytes ksokoban/images/halfstone_2.pov | 17 + ksokoban/images/halfstone_3.png | Bin 0 -> 3946 bytes ksokoban/images/halfstone_3.pov | 17 + ksokoban/images/halfstone_4.png | Bin 0 -> 4062 bytes ksokoban/images/halfstone_4.pov | 17 + ksokoban/images/man.png | Bin 0 -> 6298 bytes ksokoban/images/man.pov | 5 + ksokoban/images/man_common.inc | 59 + ksokoban/images/object.png | Bin 0 -> 4955 bytes ksokoban/images/object.pov | 36 + ksokoban/images/saveman.png | Bin 0 -> 8949 bytes ksokoban/images/saveman.pov | 5 + ksokoban/images/starfield.png | Bin 0 -> 9618 bytes ksokoban/images/stone_1.png | Bin 0 -> 7970 bytes ksokoban/images/stone_1.pov | 17 + ksokoban/images/stone_2.png | Bin 0 -> 7964 bytes ksokoban/images/stone_2.pov | 17 + ksokoban/images/stone_3.png | Bin 0 -> 7527 bytes ksokoban/images/stone_3.pov | 17 + ksokoban/images/stone_4.png | Bin 0 -> 7763 bytes ksokoban/images/stone_4.pov | 17 + ksokoban/images/stone_5.png | Bin 0 -> 8082 bytes ksokoban/images/stone_5.pov | 17 + ksokoban/images/stone_6.png | Bin 0 -> 8219 bytes ksokoban/images/stone_6.pov | 17 + ksokoban/images/stone_common.inc | 32 + ksokoban/images/treasure.png | Bin 0 -> 7969 bytes ksokoban/images/treasure.pov | 36 + ksokoban/levels/Makefile.am | 11 + ksokoban/levels/level.data | Bin 0 -> 68359 bytes ksokoban/main.cpp | 86 + kspaceduel/AUTHORS | 2 + kspaceduel/ChangeLog | 11 + kspaceduel/Makefile.am | 36 + kspaceduel/README | 10 + kspaceduel/TODO | 5 + kspaceduel/ai.cpp | 680 +++ kspaceduel/ai.h | 102 + kspaceduel/configure.in.in | 1 + kspaceduel/defines.h | 66 + kspaceduel/dialogs.cpp | 434 ++ kspaceduel/dialogs.h | 102 + kspaceduel/general.ui | 337 ++ kspaceduel/hi128-app-kspaceduel.png | Bin 0 -> 11889 bytes kspaceduel/hi16-app-kspaceduel.png | Bin 0 -> 560 bytes kspaceduel/hi22-app-kspaceduel.png | Bin 0 -> 3667 bytes kspaceduel/hi32-app-kspaceduel.png | Bin 0 -> 1696 bytes kspaceduel/hi48-app-kspaceduel.png | Bin 0 -> 2884 bytes kspaceduel/hi64-app-kspaceduel.png | Bin 0 -> 4290 bytes kspaceduel/kspaceduel.desktop | 76 + kspaceduel/kspaceduel.kcfg | 41 + kspaceduel/kspaceduelui.rc | 16 + kspaceduel/main.cpp | 29 + kspaceduel/mainview.cpp | 1052 ++++ kspaceduel/mainview.h | 104 + kspaceduel/mathroutines.cpp | 41 + kspaceduel/mathroutines.h | 8 + kspaceduel/options.kcfgc | 5 + kspaceduel/pics/Makefile.am | 2 + kspaceduel/pics/cr16-action-spnewgame.png | Bin 0 -> 493 bytes kspaceduel/pics/cr16-action-spnewround.png | Bin 0 -> 493 bytes kspaceduel/pics/cr16-action-sppausegame.png | Bin 0 -> 227 bytes kspaceduel/pics/cr22-action-spnewgame.png | Bin 0 -> 996 bytes kspaceduel/pics/cr22-action-spnewround.png | Bin 0 -> 1031 bytes kspaceduel/pics/cr22-action-sppausegame.png | Bin 0 -> 308 bytes kspaceduel/pics/cr32-action-spnewgame.png | Bin 0 -> 2440 bytes kspaceduel/pics/cr32-action-spnewround.png | Bin 0 -> 2504 bytes kspaceduel/pics/cr32-action-sppausegame.png | Bin 0 -> 323 bytes kspaceduel/pics/lo16-action-spnewgame.png | Bin 0 -> 493 bytes kspaceduel/pics/lo16-action-spnewround.png | Bin 0 -> 493 bytes kspaceduel/pics/lo16-action-sppausegame.png | Bin 0 -> 227 bytes kspaceduel/playerinfo.cpp | 80 + kspaceduel/playerinfo.h | 26 + kspaceduel/sprites.cpp | 340 ++ kspaceduel/sprites.h | 143 + kspaceduel/sprites/Makefile.am | 6 + kspaceduel/sprites/backgr.png | Bin 0 -> 1319 bytes kspaceduel/sprites/explosion/Makefile.am | 26 + kspaceduel/sprites/explosion/explos00.pbm | Bin 0 -> 41 bytes kspaceduel/sprites/explosion/explos00.ppm | Bin 0 -> 603 bytes kspaceduel/sprites/explosion/explos01.pbm | Bin 0 -> 49 bytes kspaceduel/sprites/explosion/explos01.ppm | Bin 0 -> 928 bytes kspaceduel/sprites/explosion/explos02.pbm | Bin 0 -> 78 bytes kspaceduel/sprites/explosion/explos02.ppm | Bin 0 -> 1270 bytes kspaceduel/sprites/explosion/explos03.pbm | Bin 0 -> 87 bytes kspaceduel/sprites/explosion/explos03.ppm | Bin 0 -> 1588 bytes kspaceduel/sprites/explosion/explos04.pbm | Bin 0 -> 99 bytes kspaceduel/sprites/explosion/explos04.ppm | Bin 0 -> 2099 bytes kspaceduel/sprites/explosion/explos05.pbm | Bin 0 -> 141 bytes kspaceduel/sprites/explosion/explos05.ppm | Bin 0 -> 2504 bytes kspaceduel/sprites/explosion/explos06.pbm | Bin 0 -> 145 bytes kspaceduel/sprites/explosion/explos06.ppm | Bin 0 -> 2681 bytes kspaceduel/sprites/explosion/explos07.pbm | Bin 0 -> 153 bytes kspaceduel/sprites/explosion/explos07.ppm | Bin 0 -> 3053 bytes kspaceduel/sprites/explosion/explos08.pbm | Bin 0 -> 153 bytes kspaceduel/sprites/explosion/explos08.ppm | Bin 0 -> 3269 bytes kspaceduel/sprites/explosion/explos09.pbm | Bin 0 -> 153 bytes kspaceduel/sprites/explosion/explos09.ppm | Bin 0 -> 3377 bytes kspaceduel/sprites/explosion/explos10.pbm | Bin 0 -> 161 bytes kspaceduel/sprites/explosion/explos10.ppm | Bin 0 -> 3563 bytes kspaceduel/sprites/explosion/explos11.pbm | Bin 0 -> 204 bytes kspaceduel/sprites/explosion/explos11.ppm | Bin 0 -> 3890 bytes kspaceduel/sprites/explosion/explos12.pbm | Bin 0 -> 209 bytes kspaceduel/sprites/explosion/explos12.ppm | Bin 0 -> 3989 bytes kspaceduel/sprites/explosion/explos13.pbm | Bin 0 -> 214 bytes kspaceduel/sprites/explosion/explos13.ppm | Bin 0 -> 4088 bytes kspaceduel/sprites/explosion/explos14.pbm | Bin 0 -> 214 bytes kspaceduel/sprites/explosion/explos14.ppm | Bin 0 -> 4088 bytes kspaceduel/sprites/explosion/explos15.pbm | Bin 0 -> 214 bytes kspaceduel/sprites/explosion/explos15.ppm | Bin 0 -> 4211 bytes kspaceduel/sprites/explosion/explos16.pbm | Bin 0 -> 224 bytes kspaceduel/sprites/explosion/explos16.ppm | Bin 0 -> 4415 bytes kspaceduel/sprites/explosion/explos17.pbm | Bin 0 -> 224 bytes kspaceduel/sprites/explosion/explos17.ppm | Bin 0 -> 4415 bytes kspaceduel/sprites/explosion/explos18.pbm | Bin 0 -> 224 bytes kspaceduel/sprites/explosion/explos18.ppm | Bin 0 -> 4544 bytes kspaceduel/sprites/explosion/explos19.pbm | Bin 0 -> 224 bytes kspaceduel/sprites/explosion/explos19.ppm | Bin 0 -> 4673 bytes kspaceduel/sprites/explosion/explos20.pbm | Bin 0 -> 229 bytes kspaceduel/sprites/explosion/explos20.ppm | Bin 0 -> 4781 bytes kspaceduel/sprites/explosion/explos21.pbm | Bin 0 -> 229 bytes kspaceduel/sprites/explosion/explos21.ppm | Bin 0 -> 4781 bytes kspaceduel/sprites/explosion/explos22.pbm | Bin 0 -> 229 bytes kspaceduel/sprites/explosion/explos22.ppm | Bin 0 -> 4913 bytes kspaceduel/sprites/explosion/explos23.pbm | Bin 0 -> 229 bytes kspaceduel/sprites/explosion/explos23.ppm | Bin 0 -> 4913 bytes kspaceduel/sprites/explosion/explos24.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos24.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos25.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos25.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos26.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos26.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos27.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos27.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos28.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos28.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos29.pbm | Bin 0 -> 239 bytes kspaceduel/sprites/explosion/explos29.ppm | Bin 0 -> 5135 bytes kspaceduel/sprites/explosion/explos30.pbm | Bin 0 -> 169 bytes kspaceduel/sprites/explosion/explos30.ppm | Bin 0 -> 3629 bytes kspaceduel/sprites/explosion/mineex00.pbm | 13 + kspaceduel/sprites/explosion/mineex00.ppm | Bin 0 -> 290 bytes kspaceduel/sprites/explosion/mineex01.pbm | Bin 0 -> 37 bytes kspaceduel/sprites/explosion/mineex01.ppm | Bin 0 -> 531 bytes kspaceduel/sprites/explosion/mineex02.pbm | Bin 0 -> 39 bytes kspaceduel/sprites/explosion/mineex02.ppm | Bin 0 -> 567 bytes kspaceduel/sprites/explosion/mineex03.pbm | Bin 0 -> 41 bytes kspaceduel/sprites/explosion/mineex03.ppm | Bin 0 -> 651 bytes kspaceduel/sprites/explosion/mineex04.pbm | Bin 0 -> 43 bytes kspaceduel/sprites/explosion/mineex04.ppm | Bin 0 -> 691 bytes kspaceduel/sprites/explosion/mineex05.pbm | Bin 0 -> 45 bytes kspaceduel/sprites/explosion/mineex05.ppm | Bin 0 -> 784 bytes kspaceduel/sprites/explosion/mineex06.pbm | Bin 0 -> 45 bytes kspaceduel/sprites/explosion/mineex06.ppm | Bin 0 -> 784 bytes kspaceduel/sprites/explosion/mineex07.pbm | Bin 0 -> 45 bytes kspaceduel/sprites/explosion/mineex07.ppm | Bin 0 -> 892 bytes kspaceduel/sprites/explosion/mineex08.pbm | Bin 0 -> 63 bytes kspaceduel/sprites/explosion/mineex08.ppm | Bin 0 -> 946 bytes kspaceduel/sprites/explosion/mineex09.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex09.ppm | Bin 0 -> 1048 bytes kspaceduel/sprites/explosion/mineex10.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex10.ppm | Bin 0 -> 1048 bytes kspaceduel/sprites/explosion/mineex11.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex11.ppm | Bin 0 -> 1108 bytes kspaceduel/sprites/explosion/mineex12.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex12.ppm | Bin 0 -> 1108 bytes kspaceduel/sprites/explosion/mineex13.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex13.ppm | Bin 0 -> 1108 bytes kspaceduel/sprites/explosion/mineex14.pbm | Bin 0 -> 72 bytes kspaceduel/sprites/explosion/mineex14.ppm | Bin 0 -> 1288 bytes kspaceduel/sprites/explosion/mineex15.pbm | Bin 0 -> 72 bytes kspaceduel/sprites/explosion/mineex15.ppm | Bin 0 -> 1288 bytes kspaceduel/sprites/explosion/mineex16.pbm | Bin 0 -> 72 bytes kspaceduel/sprites/explosion/mineex16.ppm | Bin 0 -> 1288 bytes kspaceduel/sprites/explosion/mineex17.pbm | Bin 0 -> 69 bytes kspaceduel/sprites/explosion/mineex17.ppm | Bin 0 -> 1168 bytes kspaceduel/sprites/playerinfo/Makefile.am | 6 + kspaceduel/sprites/playerinfo/energy.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/mine.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship10.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship11.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship12.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship13.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship20.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship21.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship22.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/ship23.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/playerinfo/win.pnm | Bin 0 -> 3688 bytes kspaceduel/sprites/powerups/Makefile.am | 5 + kspaceduel/sprites/powerups/pbullet.pbm | 3 + kspaceduel/sprites/powerups/pbullet.ppm | Bin 0 -> 615 bytes kspaceduel/sprites/powerups/penergy.pbm | 3 + kspaceduel/sprites/powerups/penergy.ppm | Bin 0 -> 615 bytes kspaceduel/sprites/powerups/pmine.pbm | 3 + kspaceduel/sprites/powerups/pmine.ppm | Bin 0 -> 615 bytes kspaceduel/sprites/powerups/pshield.pbm | 3 + kspaceduel/sprites/powerups/pshield.ppm | Bin 0 -> 615 bytes kspaceduel/sprites/ship1/Makefile.am | 25 + kspaceduel/sprites/ship1/bullet.pbm | 5 + kspaceduel/sprites/ship1/bullet.ppm | 5 + kspaceduel/sprites/ship1/mine0.pbm | Bin 0 -> 33 bytes kspaceduel/sprites/ship1/mine0.ppm | Bin 0 -> 567 bytes kspaceduel/sprites/ship1/mine1.pbm | Bin 0 -> 57 bytes kspaceduel/sprites/ship1/mine1.ppm | Bin 0 -> 891 bytes kspaceduel/sprites/ship1/ship00.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship00.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship01.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship01.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship1/ship02.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship02.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship1/ship03.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship03.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship1/ship04.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship1/ship04.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship05.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship1/ship05.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship06.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship1/ship06.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship1/ship07.pbm | Bin 0 -> 129 bytes kspaceduel/sprites/ship1/ship07.ppm | Bin 0 -> 2369 bytes kspaceduel/sprites/ship1/ship08.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship1/ship08.ppm | Bin 0 -> 2297 bytes kspaceduel/sprites/ship1/ship09.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship1/ship09.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship1/ship10.pbm | Bin 0 -> 97 bytes kspaceduel/sprites/ship1/ship10.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship1/ship11.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship1/ship11.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship12.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship12.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship13.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship13.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship14.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship14.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship15.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship15.ppm | Bin 0 -> 2234 bytes kspaceduel/sprites/ship1/ship16.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship16.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship17.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship17.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship18.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship18.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship19.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship19.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship20.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship20.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship21.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship1/ship21.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship22.pbm | Bin 0 -> 101 bytes kspaceduel/sprites/ship1/ship22.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship1/ship23.pbm | Bin 0 -> 113 bytes kspaceduel/sprites/ship1/ship23.ppm | Bin 0 -> 2291 bytes kspaceduel/sprites/ship1/ship24.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship1/ship24.ppm | Bin 0 -> 2216 bytes kspaceduel/sprites/ship1/ship25.pbm | Bin 0 -> 125 bytes kspaceduel/sprites/ship1/ship25.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship1/ship26.pbm | Bin 0 -> 99 bytes kspaceduel/sprites/ship1/ship26.ppm | Bin 0 -> 2099 bytes kspaceduel/sprites/ship1/ship27.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship1/ship27.ppm | Bin 0 -> 2044 bytes kspaceduel/sprites/ship1/ship28.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship1/ship28.ppm | Bin 0 -> 2107 bytes kspaceduel/sprites/ship1/ship29.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship29.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship1/ship30.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship30.ppm | Bin 0 -> 2233 bytes kspaceduel/sprites/ship1/ship31.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship31.ppm | Bin 0 -> 2233 bytes kspaceduel/sprites/ship1/ship32.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship32.ppm | Bin 0 -> 2233 bytes kspaceduel/sprites/ship1/ship33.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship33.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship1/ship34.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship34.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship1/ship35.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship35.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship1/ship36.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship1/ship36.ppm | Bin 0 -> 2107 bytes kspaceduel/sprites/ship1/ship37.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship1/ship37.ppm | Bin 0 -> 2044 bytes kspaceduel/sprites/ship1/ship38.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship1/ship38.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship1/ship39.pbm | Bin 0 -> 125 bytes kspaceduel/sprites/ship1/ship39.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship1/ship40.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship1/ship40.ppm | Bin 0 -> 2216 bytes kspaceduel/sprites/ship1/ship41.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship1/ship41.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship1/ship42.pbm | Bin 0 -> 101 bytes kspaceduel/sprites/ship1/ship42.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship1/ship43.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship1/ship43.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship44.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship44.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship45.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship45.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship46.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship46.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship47.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship1/ship47.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship1/ship48.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship1/ship48.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship1/ship49.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship1/ship49.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship1/ship50.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship1/ship50.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship1/ship51.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship51.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship52.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship1/ship52.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship53.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship1/ship53.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship54.pbm | Bin 0 -> 97 bytes kspaceduel/sprites/ship1/ship54.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship1/ship55.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship1/ship55.ppm | Bin 0 -> 2279 bytes kspaceduel/sprites/ship1/ship56.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship1/ship56.ppm | Bin 0 -> 2216 bytes kspaceduel/sprites/ship1/ship57.pbm | Bin 0 -> 125 bytes kspaceduel/sprites/ship1/ship57.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship1/ship58.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship1/ship58.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship1/ship59.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship1/ship59.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship1/ship60.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship1/ship60.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship1/ship61.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship61.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship62.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship62.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship1/ship63.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship63.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship1/ship64.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship1/ship64.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/Makefile.am | 27 + kspaceduel/sprites/ship2/bullet.pbm | 5 + kspaceduel/sprites/ship2/bullet.ppm | 5 + kspaceduel/sprites/ship2/mine0.pbm | Bin 0 -> 35 bytes kspaceduel/sprites/ship2/mine0.ppm | Bin 0 -> 612 bytes kspaceduel/sprites/ship2/mine1.pbm | Bin 0 -> 57 bytes kspaceduel/sprites/ship2/mine1.ppm | Bin 0 -> 843 bytes kspaceduel/sprites/ship2/ship00.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship00.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship01.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship01.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship2/ship02.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship02.ppm | Bin 0 -> 2234 bytes kspaceduel/sprites/ship2/ship03.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship03.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship04.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship2/ship04.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship05.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship2/ship05.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship06.pbm | Bin 0 -> 99 bytes kspaceduel/sprites/ship2/ship06.ppm | Bin 0 -> 2009 bytes kspaceduel/sprites/ship2/ship07.pbm | Bin 0 -> 125 bytes kspaceduel/sprites/ship2/ship07.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship2/ship08.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship2/ship08.ppm | Bin 0 -> 2297 bytes kspaceduel/sprites/ship2/ship09.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship2/ship09.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship2/ship10.pbm | Bin 0 -> 101 bytes kspaceduel/sprites/ship2/ship10.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship2/ship11.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship2/ship11.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship12.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship12.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship13.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship13.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship14.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship14.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship2/ship15.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship15.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship2/ship16.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship16.ppm | Bin 0 -> 2234 bytes kspaceduel/sprites/ship2/ship17.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship17.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship18.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship18.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship19.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship19.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship20.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship20.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship21.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship2/ship21.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship22.pbm | Bin 0 -> 101 bytes kspaceduel/sprites/ship2/ship22.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship2/ship23.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship2/ship23.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship2/ship24.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship2/ship24.ppm | Bin 0 -> 2216 bytes kspaceduel/sprites/ship2/ship25.pbm | Bin 0 -> 125 bytes kspaceduel/sprites/ship2/ship25.ppm | Bin 0 -> 2204 bytes kspaceduel/sprites/ship2/ship26.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship2/ship26.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship2/ship27.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship2/ship27.ppm | Bin 0 -> 2044 bytes kspaceduel/sprites/ship2/ship28.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship2/ship28.ppm | Bin 0 -> 2107 bytes kspaceduel/sprites/ship2/ship29.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship29.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship2/ship30.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship30.ppm | Bin 0 -> 2233 bytes kspaceduel/sprites/ship2/ship31.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship31.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship32.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship32.ppm | Bin 0 -> 2233 bytes kspaceduel/sprites/ship2/ship33.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship33.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship34.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship34.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship2/ship35.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship35.ppm | Bin 0 -> 2170 bytes kspaceduel/sprites/ship2/ship36.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship2/ship36.ppm | Bin 0 -> 2107 bytes kspaceduel/sprites/ship2/ship37.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship2/ship37.ppm | Bin 0 -> 2044 bytes kspaceduel/sprites/ship2/ship38.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship2/ship38.ppm | Bin 0 -> 2168 bytes kspaceduel/sprites/ship2/ship39.pbm | Bin 0 -> 129 bytes kspaceduel/sprites/ship2/ship39.ppm | Bin 0 -> 2369 bytes kspaceduel/sprites/ship2/ship40.pbm | Bin 0 -> 121 bytes kspaceduel/sprites/ship2/ship40.ppm | Bin 0 -> 2381 bytes kspaceduel/sprites/ship2/ship41.pbm | Bin 0 -> 109 bytes kspaceduel/sprites/ship2/ship41.ppm | Bin 0 -> 2279 bytes kspaceduel/sprites/ship2/ship42.pbm | Bin 0 -> 97 bytes kspaceduel/sprites/ship2/ship42.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship2/ship43.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship2/ship43.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship44.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship44.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship45.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship45.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship46.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship46.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship47.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship47.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship48.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship48.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship49.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship49.ppm | Bin 0 -> 2339 bytes kspaceduel/sprites/ship2/ship50.pbm | Bin 0 -> 119 bytes kspaceduel/sprites/ship2/ship50.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship51.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship51.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship52.pbm | Bin 0 -> 114 bytes kspaceduel/sprites/ship2/ship52.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship53.pbm | Bin 0 -> 93 bytes kspaceduel/sprites/ship2/ship53.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship54.pbm | Bin 0 -> 97 bytes kspaceduel/sprites/ship2/ship54.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship2/ship55.pbm | Bin 0 -> 113 bytes kspaceduel/sprites/ship2/ship55.ppm | Bin 0 -> 2291 bytes kspaceduel/sprites/ship2/ship56.pbm | Bin 0 -> 117 bytes kspaceduel/sprites/ship2/ship56.ppm | Bin 0 -> 2216 bytes kspaceduel/sprites/ship2/ship57.pbm | Bin 0 -> 129 bytes kspaceduel/sprites/ship2/ship57.ppm | Bin 0 -> 2369 bytes kspaceduel/sprites/ship2/ship58.pbm | Bin 0 -> 102 bytes kspaceduel/sprites/ship2/ship58.ppm | Bin 0 -> 2075 bytes kspaceduel/sprites/ship2/ship59.pbm | Bin 0 -> 105 bytes kspaceduel/sprites/ship2/ship59.ppm | Bin 0 -> 2045 bytes kspaceduel/sprites/ship2/ship60.pbm | Bin 0 -> 108 bytes kspaceduel/sprites/ship2/ship60.ppm | Bin 0 -> 2108 bytes kspaceduel/sprites/ship2/ship61.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship61.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship62.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship62.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/ship2/ship63.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship63.ppm | Bin 0 -> 2273 bytes kspaceduel/sprites/ship2/ship64.pbm | Bin 0 -> 111 bytes kspaceduel/sprites/ship2/ship64.ppm | Bin 0 -> 2171 bytes kspaceduel/sprites/sun/Makefile.am | 3 + kspaceduel/sprites/sun/sun.pbm | Bin 0 -> 759 bytes kspaceduel/sprites/sun/sun.ppm | Bin 0 -> 16904 bytes kspaceduel/structs.h | 46 + kspaceduel/topwidget.cpp | 144 + kspaceduel/topwidget.h | 33 + kspaceduel/version.h | 1 + ktron/AUTHORS | 2 + ktron/ChangeLog | 21 + ktron/Makefile.am | 22 + ktron/README | 39 + ktron/TODO | 10 + ktron/ai.ui | 141 + ktron/appearance.ui | 302 ++ ktron/general.ui | 219 + ktron/ktron.cpp | 195 + ktron/ktron.desktop | 66 + ktron/ktron.h | 67 + ktron/ktron.kcfg | 84 + ktron/ktronui.rc | 3 + ktron/main.cpp | 59 + ktron/pics/Makefile.am | 15 + ktron/pics/hi128-app-ktron.png | Bin 0 -> 10435 bytes ktron/pics/hi16-app-ktron.png | Bin 0 -> 531 bytes ktron/pics/hi22-app-ktron.png | Bin 0 -> 973 bytes ktron/pics/hi32-app-ktron.png | Bin 0 -> 1490 bytes ktron/pics/hi48-app-ktron.png | Bin 0 -> 2733 bytes ktron/pics/hi64-app-ktron.png | Bin 0 -> 3971 bytes ktron/player.cpp | 57 + ktron/player.h | 56 + ktron/settings.kcfgc | 5 + ktron/tron.cpp | 1654 +++++++ ktron/tron.h | 178 + ktron/version.h | 1 + ktuberling/Makefile.am | 38 + ktuberling/TODO | 40 + ktuberling/action.cpp | 20 + ktuberling/action.h | 28 + ktuberling/hi128-app-ktuberling.png | Bin 0 -> 23202 bytes ktuberling/hi16-app-ktuberling.png | Bin 0 -> 938 bytes ktuberling/hi22-app-ktuberling.png | Bin 0 -> 4254 bytes ktuberling/hi32-app-ktuberling.png | Bin 0 -> 2761 bytes ktuberling/hi48-app-ktuberling.png | Bin 0 -> 5398 bytes ktuberling/hi64-app-ktuberling.png | Bin 0 -> 8366 bytes ktuberling/ktuberling.desktop | 128 + ktuberling/ktuberlingui.rc | 41 + ktuberling/main.cpp | 60 + ktuberling/museum/Makefile.am | 10 + ktuberling/museum/businessman.tuberling | 28 + ktuberling/museum/cool.tuberling | 8 + ktuberling/museum/crazy.tuberling | 9 + ktuberling/museum/cyclop.tuberling | 15 + ktuberling/museum/dali.tuberling | 11 + ktuberling/museum/einstein.tuberling | 10 + ktuberling/museum/fly.tuberling | 8 + ktuberling/museum/grandpa.tuberling | 9 + ktuberling/museum/happy.tuberling | 13 + ktuberling/museum/hippie.tuberling | 8 + ktuberling/museum/idiot.tuberling | 10 + ktuberling/museum/miss.tuberling | 11 + ktuberling/museum/mouse.tuberling | 14 + ktuberling/museum/picasso.tuberling | 10 + ktuberling/museum/sea.tuberling | 18 + ktuberling/museum/serious.tuberling | 13 + ktuberling/pics/Makefile.am | 8 + ktuberling/pics/aquarium-game.png | Bin 0 -> 179746 bytes ktuberling/pics/aquarium-mask.png | Bin 0 -> 6150 bytes ktuberling/pics/layout.i18n | 53 + ktuberling/pics/layout.xml | 924 ++++ ktuberling/pics/penguin-game.png | Bin 0 -> 182537 bytes ktuberling/pics/penguin-mask.png | Bin 0 -> 8016 bytes ktuberling/pics/potato-game.png | Bin 0 -> 118385 bytes ktuberling/pics/potato-mask.png | Bin 0 -> 11159 bytes ktuberling/playground.cpp | 609 +++ ktuberling/playground.h | 86 + ktuberling/soundfactory.cpp | 148 + ktuberling/soundfactory.h | 46 + ktuberling/sounds/Makefile.am | 9 + ktuberling/sounds/NO_TRANSLATIONS_HERE | 0 ktuberling/sounds/badge.wav | Bin 0 -> 8908 bytes ktuberling/sounds/bow.wav | Bin 0 -> 6860 bytes ktuberling/sounds/cigar.wav | Bin 0 -> 10668 bytes ktuberling/sounds/ear.wav | Bin 0 -> 6732 bytes ktuberling/sounds/earring.wav | Bin 0 -> 8940 bytes ktuberling/sounds/eye.wav | Bin 0 -> 6156 bytes ktuberling/sounds/eyebrow.wav | Bin 0 -> 9708 bytes ktuberling/sounds/hat.wav | Bin 0 -> 7116 bytes ktuberling/sounds/moustache.wav | Bin 0 -> 13260 bytes ktuberling/sounds/mouth.wav | Bin 0 -> 7276 bytes ktuberling/sounds/nose.wav | Bin 0 -> 9068 bytes ktuberling/sounds/spectacles.wav | Bin 0 -> 11564 bytes ktuberling/sounds/sunglasses.wav | Bin 0 -> 13612 bytes ktuberling/sounds/tuberling.wav | Bin 0 -> 11212 bytes ktuberling/sounds/watch.wav | Bin 0 -> 8428 bytes ktuberling/todraw.cpp | 90 + ktuberling/todraw.h | 35 + ktuberling/toplevel.cpp | 611 +++ ktuberling/toplevel.h | 105 + ktuberling/x-tuberling.desktop | 61 + kwin4/AUTHORS | 1 + kwin4/COPYING | 341 ++ kwin4/INSTALL | 167 + kwin4/Makefile.am | 7 + kwin4/README | 26 + kwin4/grafix/Makefile.am | 22 + kwin4/grafix/default/aboute.png | Bin 0 -> 1056 bytes kwin4/grafix/default/arrow0.png | Bin 0 -> 1279 bytes kwin4/grafix/default/arrow1.png | Bin 0 -> 1131 bytes kwin4/grafix/default/arrow2.png | Bin 0 -> 1035 bytes kwin4/grafix/default/background.png | Bin 0 -> 184 bytes kwin4/grafix/default/board.png | Bin 0 -> 649 bytes kwin4/grafix/default/crnt.png | Bin 0 -> 1052 bytes kwin4/grafix/default/empty.png | Bin 0 -> 954 bytes kwin4/grafix/default/empty2.png | Bin 0 -> 495 bytes kwin4/grafix/default/game_over.png | Bin 0 -> 15301 bytes kwin4/grafix/default/grafix.rc | 148 + kwin4/grafix/default/introabout.png | Bin 0 -> 2047 bytes kwin4/grafix/default/piece0.png | Bin 0 -> 1171 bytes kwin4/grafix/default/piece1.png | Bin 0 -> 950 bytes kwin4/grafix/default/star0.png | Bin 0 -> 452 bytes kwin4/grafix/default/star1.png | Bin 0 -> 414 bytes kwin4/grafix/default/star2.png | Bin 0 -> 392 bytes kwin4/grafix/default/star3.png | Bin 0 -> 375 bytes kwin4/grafix/default/star4.png | Bin 0 -> 369 bytes kwin4/hi128-app-kwin4.png | Bin 0 -> 10209 bytes kwin4/hi16-app-kwin4.png | Bin 0 -> 871 bytes kwin4/hi22-app-kwin4.png | Bin 0 -> 1281 bytes kwin4/hi32-app-kwin4.png | Bin 0 -> 2037 bytes kwin4/hi48-app-kwin4.png | Bin 0 -> 3574 bytes kwin4/hi64-app-kwin4.png | Bin 0 -> 4236 bytes kwin4/index.html | 213 + kwin4/install-sh | 238 + kwin4/kwin4.desktop | 94 + kwin4/kwin4.kdelnk | 24 + kwin4/kwin4.lsm | 14 + kwin4/kwin4/AboutDlg.kdevdlg | 138 + kwin4/kwin4/Makefile.am | 28 + kwin4/kwin4/kspritecache.cpp | 812 +++ kwin4/kwin4/kspritecache.h | 506 ++ kwin4/kwin4/kwin4.cpp | 626 +++ kwin4/kwin4/kwin4.h | 126 + kwin4/kwin4/kwin4.kcfg | 46 + kwin4/kwin4/kwin4doc.cpp | 1322 +++++ kwin4/kwin4/kwin4doc.h | 194 + kwin4/kwin4/kwin4player.cpp | 160 + kwin4/kwin4/kwin4player.h | 72 + kwin4/kwin4/kwin4proc.cpp | 432 ++ kwin4/kwin4/kwin4proc.h | 83 + kwin4/kwin4/kwin4ui.rc | 28 + kwin4/kwin4/kwin4view.cpp | 729 +++ kwin4/kwin4/kwin4view.h | 82 + kwin4/kwin4/main.cpp | 75 + kwin4/kwin4/prefs.kcfgc | 7 + kwin4/kwin4/scorewidget.cpp | 193 + kwin4/kwin4/scorewidget.h | 45 + kwin4/kwin4/settings.ui | 248 + kwin4/kwin4/statistics.ui | 249 + kwin4/kwin4/statuswidget.ui | 263 + libkdegames/Makefile.am | 23 + libkdegames/README | 25 + libkdegames/TODO | 10 + libkdegames/carddecks/Makefile.am | 24 + libkdegames/carddecks/README | 12 + libkdegames/carddecks/cards-aisleriot/1.png | Bin 0 -> 1261 bytes libkdegames/carddecks/cards-aisleriot/10.png | Bin 0 -> 1933 bytes libkdegames/carddecks/cards-aisleriot/11.png | Bin 0 -> 2422 bytes libkdegames/carddecks/cards-aisleriot/12.png | Bin 0 -> 2387 bytes libkdegames/carddecks/cards-aisleriot/13.png | Bin 0 -> 1966 bytes libkdegames/carddecks/cards-aisleriot/14.png | Bin 0 -> 1794 bytes libkdegames/carddecks/cards-aisleriot/15.png | Bin 0 -> 2368 bytes libkdegames/carddecks/cards-aisleriot/16.png | Bin 0 -> 2343 bytes libkdegames/carddecks/cards-aisleriot/17.png | Bin 0 -> 2790 bytes libkdegames/carddecks/cards-aisleriot/18.png | Bin 0 -> 2092 bytes libkdegames/carddecks/cards-aisleriot/19.png | Bin 0 -> 2597 bytes libkdegames/carddecks/cards-aisleriot/2.png | Bin 0 -> 1125 bytes libkdegames/carddecks/cards-aisleriot/20.png | Bin 0 -> 2465 bytes libkdegames/carddecks/cards-aisleriot/21.png | Bin 0 -> 2503 bytes libkdegames/carddecks/cards-aisleriot/22.png | Bin 0 -> 1888 bytes libkdegames/carddecks/cards-aisleriot/23.png | Bin 0 -> 2470 bytes libkdegames/carddecks/cards-aisleriot/24.png | Bin 0 -> 2258 bytes libkdegames/carddecks/cards-aisleriot/25.png | Bin 0 -> 2063 bytes libkdegames/carddecks/cards-aisleriot/26.png | Bin 0 -> 1845 bytes libkdegames/carddecks/cards-aisleriot/27.png | Bin 0 -> 2226 bytes libkdegames/carddecks/cards-aisleriot/28.png | Bin 0 -> 2144 bytes libkdegames/carddecks/cards-aisleriot/29.png | Bin 0 -> 1980 bytes libkdegames/carddecks/cards-aisleriot/3.png | Bin 0 -> 1505 bytes libkdegames/carddecks/cards-aisleriot/30.png | Bin 0 -> 1788 bytes libkdegames/carddecks/cards-aisleriot/31.png | Bin 0 -> 2091 bytes libkdegames/carddecks/cards-aisleriot/32.png | Bin 0 -> 2097 bytes libkdegames/carddecks/cards-aisleriot/33.png | Bin 0 -> 1810 bytes libkdegames/carddecks/cards-aisleriot/34.png | Bin 0 -> 1685 bytes libkdegames/carddecks/cards-aisleriot/35.png | Bin 0 -> 1917 bytes libkdegames/carddecks/cards-aisleriot/36.png | Bin 0 -> 2006 bytes libkdegames/carddecks/cards-aisleriot/37.png | Bin 0 -> 1654 bytes libkdegames/carddecks/cards-aisleriot/38.png | Bin 0 -> 1737 bytes libkdegames/carddecks/cards-aisleriot/39.png | Bin 0 -> 2014 bytes libkdegames/carddecks/cards-aisleriot/4.png | Bin 0 -> 1559 bytes libkdegames/carddecks/cards-aisleriot/40.png | Bin 0 -> 1885 bytes libkdegames/carddecks/cards-aisleriot/41.png | Bin 0 -> 1449 bytes libkdegames/carddecks/cards-aisleriot/42.png | Bin 0 -> 1577 bytes libkdegames/carddecks/cards-aisleriot/43.png | Bin 0 -> 1798 bytes libkdegames/carddecks/cards-aisleriot/44.png | Bin 0 -> 1763 bytes libkdegames/carddecks/cards-aisleriot/45.png | Bin 0 -> 1619 bytes libkdegames/carddecks/cards-aisleriot/46.png | Bin 0 -> 1334 bytes libkdegames/carddecks/cards-aisleriot/47.png | Bin 0 -> 1768 bytes libkdegames/carddecks/cards-aisleriot/48.png | Bin 0 -> 1738 bytes libkdegames/carddecks/cards-aisleriot/49.png | Bin 0 -> 1312 bytes libkdegames/carddecks/cards-aisleriot/5.png | Bin 0 -> 2129 bytes libkdegames/carddecks/cards-aisleriot/50.png | Bin 0 -> 1126 bytes libkdegames/carddecks/cards-aisleriot/51.png | Bin 0 -> 1570 bytes libkdegames/carddecks/cards-aisleriot/52.png | Bin 0 -> 1584 bytes libkdegames/carddecks/cards-aisleriot/6.png | Bin 0 -> 1877 bytes libkdegames/carddecks/cards-aisleriot/7.png | Bin 0 -> 2403 bytes libkdegames/carddecks/cards-aisleriot/8.png | Bin 0 -> 2359 bytes libkdegames/carddecks/cards-aisleriot/9.png | Bin 0 -> 2143 bytes .../carddecks/cards-aisleriot/COPYRIGHT | 10 + .../carddecks/cards-aisleriot/index.desktop | 20 + libkdegames/carddecks/cards-default/1.png | Bin 0 -> 266 bytes libkdegames/carddecks/cards-default/10.png | Bin 0 -> 1002 bytes libkdegames/carddecks/cards-default/11.png | Bin 0 -> 1048 bytes libkdegames/carddecks/cards-default/12.png | Bin 0 -> 1028 bytes libkdegames/carddecks/cards-default/13.png | Bin 0 -> 952 bytes libkdegames/carddecks/cards-default/14.png | Bin 0 -> 924 bytes libkdegames/carddecks/cards-default/15.png | Bin 0 -> 940 bytes libkdegames/carddecks/cards-default/16.png | Bin 0 -> 896 bytes libkdegames/carddecks/cards-default/17.png | Bin 0 -> 415 bytes libkdegames/carddecks/cards-default/18.png | Bin 0 -> 425 bytes libkdegames/carddecks/cards-default/19.png | Bin 0 -> 451 bytes libkdegames/carddecks/cards-default/2.png | Bin 0 -> 418 bytes libkdegames/carddecks/cards-default/20.png | Bin 0 -> 415 bytes libkdegames/carddecks/cards-default/21.png | Bin 0 -> 403 bytes libkdegames/carddecks/cards-default/22.png | Bin 0 -> 410 bytes libkdegames/carddecks/cards-default/23.png | Bin 0 -> 441 bytes libkdegames/carddecks/cards-default/24.png | Bin 0 -> 400 bytes libkdegames/carddecks/cards-default/25.png | Bin 0 -> 349 bytes libkdegames/carddecks/cards-default/26.png | Bin 0 -> 367 bytes libkdegames/carddecks/cards-default/27.png | Bin 0 -> 395 bytes libkdegames/carddecks/cards-default/28.png | Bin 0 -> 395 bytes libkdegames/carddecks/cards-default/29.png | Bin 0 -> 361 bytes libkdegames/carddecks/cards-default/3.png | Bin 0 -> 303 bytes libkdegames/carddecks/cards-default/30.png | Bin 0 -> 373 bytes libkdegames/carddecks/cards-default/31.png | Bin 0 -> 414 bytes libkdegames/carddecks/cards-default/32.png | Bin 0 -> 373 bytes libkdegames/carddecks/cards-default/33.png | Bin 0 -> 336 bytes libkdegames/carddecks/cards-default/34.png | Bin 0 -> 356 bytes libkdegames/carddecks/cards-default/35.png | Bin 0 -> 388 bytes libkdegames/carddecks/cards-default/36.png | Bin 0 -> 357 bytes libkdegames/carddecks/cards-default/37.png | Bin 0 -> 347 bytes libkdegames/carddecks/cards-default/38.png | Bin 0 -> 367 bytes libkdegames/carddecks/cards-default/39.png | Bin 0 -> 395 bytes libkdegames/carddecks/cards-default/4.png | Bin 0 -> 290 bytes libkdegames/carddecks/cards-default/40.png | Bin 0 -> 368 bytes libkdegames/carddecks/cards-default/41.png | Bin 0 -> 308 bytes libkdegames/carddecks/cards-default/42.png | Bin 0 -> 321 bytes libkdegames/carddecks/cards-default/43.png | Bin 0 -> 346 bytes libkdegames/carddecks/cards-default/44.png | Bin 0 -> 332 bytes libkdegames/carddecks/cards-default/45.png | Bin 0 -> 321 bytes libkdegames/carddecks/cards-default/46.png | Bin 0 -> 326 bytes libkdegames/carddecks/cards-default/47.png | Bin 0 -> 361 bytes libkdegames/carddecks/cards-default/48.png | Bin 0 -> 342 bytes libkdegames/carddecks/cards-default/49.png | Bin 0 -> 300 bytes libkdegames/carddecks/cards-default/5.png | Bin 0 -> 890 bytes libkdegames/carddecks/cards-default/50.png | Bin 0 -> 304 bytes libkdegames/carddecks/cards-default/51.png | Bin 0 -> 333 bytes libkdegames/carddecks/cards-default/52.png | Bin 0 -> 314 bytes libkdegames/carddecks/cards-default/6.png | Bin 0 -> 911 bytes libkdegames/carddecks/cards-default/7.png | Bin 0 -> 1062 bytes libkdegames/carddecks/cards-default/8.png | Bin 0 -> 988 bytes libkdegames/carddecks/cards-default/9.png | Bin 0 -> 1064 bytes .../carddecks/cards-default/index.desktop | 115 + .../carddecks/cards-dondorf-whist-b/1.png | Bin 0 -> 7122 bytes .../carddecks/cards-dondorf-whist-b/10.png | Bin 0 -> 6497 bytes .../carddecks/cards-dondorf-whist-b/11.png | Bin 0 -> 6381 bytes .../carddecks/cards-dondorf-whist-b/12.png | Bin 0 -> 6476 bytes .../carddecks/cards-dondorf-whist-b/13.png | Bin 0 -> 6642 bytes .../carddecks/cards-dondorf-whist-b/14.png | Bin 0 -> 6278 bytes .../carddecks/cards-dondorf-whist-b/15.png | Bin 0 -> 6542 bytes .../carddecks/cards-dondorf-whist-b/16.png | Bin 0 -> 6523 bytes .../carddecks/cards-dondorf-whist-b/17.png | Bin 0 -> 5118 bytes .../carddecks/cards-dondorf-whist-b/18.png | Bin 0 -> 5137 bytes .../carddecks/cards-dondorf-whist-b/19.png | Bin 0 -> 5239 bytes .../carddecks/cards-dondorf-whist-b/2.png | Bin 0 -> 7128 bytes .../carddecks/cards-dondorf-whist-b/20.png | Bin 0 -> 4894 bytes .../carddecks/cards-dondorf-whist-b/21.png | Bin 0 -> 4898 bytes .../carddecks/cards-dondorf-whist-b/22.png | Bin 0 -> 4867 bytes .../carddecks/cards-dondorf-whist-b/23.png | Bin 0 -> 5015 bytes .../carddecks/cards-dondorf-whist-b/24.png | Bin 0 -> 4677 bytes .../carddecks/cards-dondorf-whist-b/25.png | Bin 0 -> 4964 bytes .../carddecks/cards-dondorf-whist-b/26.png | Bin 0 -> 5025 bytes .../carddecks/cards-dondorf-whist-b/27.png | Bin 0 -> 4944 bytes .../carddecks/cards-dondorf-whist-b/28.png | Bin 0 -> 4820 bytes .../carddecks/cards-dondorf-whist-b/29.png | Bin 0 -> 4772 bytes .../carddecks/cards-dondorf-whist-b/3.png | Bin 0 -> 6723 bytes .../carddecks/cards-dondorf-whist-b/30.png | Bin 0 -> 4844 bytes .../carddecks/cards-dondorf-whist-b/31.png | Bin 0 -> 4757 bytes .../carddecks/cards-dondorf-whist-b/32.png | Bin 0 -> 4662 bytes .../carddecks/cards-dondorf-whist-b/33.png | Bin 0 -> 4607 bytes .../carddecks/cards-dondorf-whist-b/34.png | Bin 0 -> 4670 bytes .../carddecks/cards-dondorf-whist-b/35.png | Bin 0 -> 4584 bytes .../carddecks/cards-dondorf-whist-b/36.png | Bin 0 -> 4501 bytes .../carddecks/cards-dondorf-whist-b/37.png | Bin 0 -> 4411 bytes .../carddecks/cards-dondorf-whist-b/38.png | Bin 0 -> 4481 bytes .../carddecks/cards-dondorf-whist-b/39.png | Bin 0 -> 4410 bytes .../carddecks/cards-dondorf-whist-b/4.png | Bin 0 -> 6842 bytes .../carddecks/cards-dondorf-whist-b/40.png | Bin 0 -> 4315 bytes .../carddecks/cards-dondorf-whist-b/41.png | Bin 0 -> 4220 bytes .../carddecks/cards-dondorf-whist-b/42.png | Bin 0 -> 4294 bytes .../carddecks/cards-dondorf-whist-b/43.png | Bin 0 -> 4239 bytes .../carddecks/cards-dondorf-whist-b/44.png | Bin 0 -> 4185 bytes .../carddecks/cards-dondorf-whist-b/45.png | Bin 0 -> 4071 bytes .../carddecks/cards-dondorf-whist-b/46.png | Bin 0 -> 4118 bytes .../carddecks/cards-dondorf-whist-b/47.png | Bin 0 -> 4075 bytes .../carddecks/cards-dondorf-whist-b/48.png | Bin 0 -> 4044 bytes .../carddecks/cards-dondorf-whist-b/49.png | Bin 0 -> 3886 bytes .../carddecks/cards-dondorf-whist-b/5.png | Bin 0 -> 6779 bytes .../carddecks/cards-dondorf-whist-b/50.png | Bin 0 -> 3922 bytes .../carddecks/cards-dondorf-whist-b/51.png | Bin 0 -> 3892 bytes .../carddecks/cards-dondorf-whist-b/52.png | Bin 0 -> 3866 bytes .../carddecks/cards-dondorf-whist-b/6.png | Bin 0 -> 6679 bytes .../carddecks/cards-dondorf-whist-b/7.png | Bin 0 -> 6638 bytes .../carddecks/cards-dondorf-whist-b/8.png | Bin 0 -> 6642 bytes .../carddecks/cards-dondorf-whist-b/9.png | Bin 0 -> 6644 bytes .../carddecks/cards-dondorf-whist-b/COPYRIGHT | 12 + .../cards-dondorf-whist-b/index.desktop | 17 + .../carddecks/cards-gdkcard-bonded/1.png | Bin 0 -> 1430 bytes .../carddecks/cards-gdkcard-bonded/10.png | Bin 0 -> 4774 bytes .../carddecks/cards-gdkcard-bonded/11.png | Bin 0 -> 4670 bytes .../carddecks/cards-gdkcard-bonded/12.png | Bin 0 -> 4746 bytes .../carddecks/cards-gdkcard-bonded/13.png | Bin 0 -> 4573 bytes .../carddecks/cards-gdkcard-bonded/14.png | Bin 0 -> 4580 bytes .../carddecks/cards-gdkcard-bonded/15.png | Bin 0 -> 4546 bytes .../carddecks/cards-gdkcard-bonded/16.png | Bin 0 -> 4636 bytes .../carddecks/cards-gdkcard-bonded/17.png | Bin 0 -> 2402 bytes .../carddecks/cards-gdkcard-bonded/18.png | Bin 0 -> 2276 bytes .../carddecks/cards-gdkcard-bonded/19.png | Bin 0 -> 2614 bytes .../carddecks/cards-gdkcard-bonded/2.png | Bin 0 -> 1408 bytes .../carddecks/cards-gdkcard-bonded/20.png | Bin 0 -> 2599 bytes .../carddecks/cards-gdkcard-bonded/21.png | Bin 0 -> 2397 bytes .../carddecks/cards-gdkcard-bonded/22.png | Bin 0 -> 2293 bytes .../carddecks/cards-gdkcard-bonded/23.png | Bin 0 -> 2626 bytes .../carddecks/cards-gdkcard-bonded/24.png | Bin 0 -> 2489 bytes .../carddecks/cards-gdkcard-bonded/25.png | Bin 0 -> 2227 bytes .../carddecks/cards-gdkcard-bonded/26.png | Bin 0 -> 2126 bytes .../carddecks/cards-gdkcard-bonded/27.png | Bin 0 -> 2434 bytes .../carddecks/cards-gdkcard-bonded/28.png | Bin 0 -> 2253 bytes .../carddecks/cards-gdkcard-bonded/29.png | Bin 0 -> 2352 bytes .../carddecks/cards-gdkcard-bonded/3.png | Bin 0 -> 1765 bytes .../carddecks/cards-gdkcard-bonded/30.png | Bin 0 -> 2204 bytes .../carddecks/cards-gdkcard-bonded/31.png | Bin 0 -> 2571 bytes .../carddecks/cards-gdkcard-bonded/32.png | Bin 0 -> 2385 bytes .../carddecks/cards-gdkcard-bonded/33.png | Bin 0 -> 2152 bytes .../carddecks/cards-gdkcard-bonded/34.png | Bin 0 -> 2049 bytes .../carddecks/cards-gdkcard-bonded/35.png | Bin 0 -> 2410 bytes .../carddecks/cards-gdkcard-bonded/36.png | Bin 0 -> 2251 bytes .../carddecks/cards-gdkcard-bonded/37.png | Bin 0 -> 2076 bytes .../carddecks/cards-gdkcard-bonded/38.png | Bin 0 -> 1994 bytes .../carddecks/cards-gdkcard-bonded/39.png | Bin 0 -> 2367 bytes .../carddecks/cards-gdkcard-bonded/4.png | Bin 0 -> 1679 bytes .../carddecks/cards-gdkcard-bonded/40.png | Bin 0 -> 2256 bytes .../carddecks/cards-gdkcard-bonded/41.png | Bin 0 -> 1870 bytes .../carddecks/cards-gdkcard-bonded/42.png | Bin 0 -> 1811 bytes .../carddecks/cards-gdkcard-bonded/43.png | Bin 0 -> 2160 bytes .../carddecks/cards-gdkcard-bonded/44.png | Bin 0 -> 2131 bytes .../carddecks/cards-gdkcard-bonded/45.png | Bin 0 -> 1891 bytes .../carddecks/cards-gdkcard-bonded/46.png | Bin 0 -> 1792 bytes .../carddecks/cards-gdkcard-bonded/47.png | Bin 0 -> 2191 bytes .../carddecks/cards-gdkcard-bonded/48.png | Bin 0 -> 2122 bytes .../carddecks/cards-gdkcard-bonded/49.png | Bin 0 -> 1701 bytes .../carddecks/cards-gdkcard-bonded/5.png | Bin 0 -> 5611 bytes .../carddecks/cards-gdkcard-bonded/50.png | Bin 0 -> 1642 bytes .../carddecks/cards-gdkcard-bonded/51.png | Bin 0 -> 2007 bytes .../carddecks/cards-gdkcard-bonded/52.png | Bin 0 -> 1989 bytes .../carddecks/cards-gdkcard-bonded/6.png | Bin 0 -> 5516 bytes .../carddecks/cards-gdkcard-bonded/7.png | Bin 0 -> 5609 bytes .../carddecks/cards-gdkcard-bonded/8.png | Bin 0 -> 5592 bytes .../carddecks/cards-gdkcard-bonded/9.png | Bin 0 -> 4594 bytes .../carddecks/cards-gdkcard-bonded/COPYRIGHT | 13 + .../cards-gdkcard-bonded/index.desktop | 37 + libkdegames/carddecks/cards-hard-a-port/1.png | Bin 0 -> 4371 bytes .../carddecks/cards-hard-a-port/10.png | Bin 0 -> 4732 bytes .../carddecks/cards-hard-a-port/11.png | Bin 0 -> 4488 bytes .../carddecks/cards-hard-a-port/12.png | Bin 0 -> 4071 bytes .../carddecks/cards-hard-a-port/13.png | Bin 0 -> 3948 bytes .../carddecks/cards-hard-a-port/14.png | Bin 0 -> 4105 bytes .../carddecks/cards-hard-a-port/15.png | Bin 0 -> 3901 bytes .../carddecks/cards-hard-a-port/16.png | Bin 0 -> 3426 bytes .../carddecks/cards-hard-a-port/17.png | Bin 0 -> 4134 bytes .../carddecks/cards-hard-a-port/18.png | Bin 0 -> 4038 bytes .../carddecks/cards-hard-a-port/19.png | Bin 0 -> 4732 bytes libkdegames/carddecks/cards-hard-a-port/2.png | Bin 0 -> 4013 bytes .../carddecks/cards-hard-a-port/20.png | Bin 0 -> 4147 bytes .../carddecks/cards-hard-a-port/21.png | Bin 0 -> 4124 bytes .../carddecks/cards-hard-a-port/22.png | Bin 0 -> 3459 bytes .../carddecks/cards-hard-a-port/23.png | Bin 0 -> 4192 bytes .../carddecks/cards-hard-a-port/24.png | Bin 0 -> 3610 bytes .../carddecks/cards-hard-a-port/25.png | Bin 0 -> 5800 bytes .../carddecks/cards-hard-a-port/26.png | Bin 0 -> 5042 bytes .../carddecks/cards-hard-a-port/27.png | Bin 0 -> 4679 bytes .../carddecks/cards-hard-a-port/28.png | Bin 0 -> 3785 bytes .../carddecks/cards-hard-a-port/29.png | Bin 0 -> 4283 bytes libkdegames/carddecks/cards-hard-a-port/3.png | Bin 0 -> 3626 bytes .../carddecks/cards-hard-a-port/30.png | Bin 0 -> 4421 bytes .../carddecks/cards-hard-a-port/31.png | Bin 0 -> 5930 bytes .../carddecks/cards-hard-a-port/32.png | Bin 0 -> 5315 bytes .../carddecks/cards-hard-a-port/33.png | Bin 0 -> 4687 bytes .../carddecks/cards-hard-a-port/34.png | Bin 0 -> 4656 bytes .../carddecks/cards-hard-a-port/35.png | Bin 0 -> 4573 bytes .../carddecks/cards-hard-a-port/36.png | Bin 0 -> 4321 bytes .../carddecks/cards-hard-a-port/37.png | Bin 0 -> 4132 bytes .../carddecks/cards-hard-a-port/38.png | Bin 0 -> 3717 bytes .../carddecks/cards-hard-a-port/39.png | Bin 0 -> 4222 bytes libkdegames/carddecks/cards-hard-a-port/4.png | Bin 0 -> 3477 bytes .../carddecks/cards-hard-a-port/40.png | Bin 0 -> 4219 bytes .../carddecks/cards-hard-a-port/41.png | Bin 0 -> 3946 bytes .../carddecks/cards-hard-a-port/42.png | Bin 0 -> 4477 bytes .../carddecks/cards-hard-a-port/43.png | Bin 0 -> 3823 bytes .../carddecks/cards-hard-a-port/44.png | Bin 0 -> 4917 bytes .../carddecks/cards-hard-a-port/45.png | Bin 0 -> 3913 bytes .../carddecks/cards-hard-a-port/46.png | Bin 0 -> 4945 bytes .../carddecks/cards-hard-a-port/47.png | Bin 0 -> 5243 bytes .../carddecks/cards-hard-a-port/48.png | Bin 0 -> 4677 bytes .../carddecks/cards-hard-a-port/49.png | Bin 0 -> 5234 bytes libkdegames/carddecks/cards-hard-a-port/5.png | Bin 0 -> 3864 bytes .../carddecks/cards-hard-a-port/50.png | Bin 0 -> 4871 bytes .../carddecks/cards-hard-a-port/51.png | Bin 0 -> 4339 bytes .../carddecks/cards-hard-a-port/52.png | Bin 0 -> 3983 bytes libkdegames/carddecks/cards-hard-a-port/6.png | Bin 0 -> 4269 bytes libkdegames/carddecks/cards-hard-a-port/7.png | Bin 0 -> 4432 bytes libkdegames/carddecks/cards-hard-a-port/8.png | Bin 0 -> 4201 bytes libkdegames/carddecks/cards-hard-a-port/9.png | Bin 0 -> 5048 bytes .../carddecks/cards-hard-a-port/COPYRIGHT | 14 + .../carddecks/cards-hard-a-port/index.desktop | 29 + .../carddecks/cards-konqi-modern/1.png | Bin 0 -> 4366 bytes .../carddecks/cards-konqi-modern/10.png | Bin 0 -> 4986 bytes .../carddecks/cards-konqi-modern/11.png | Bin 0 -> 4978 bytes .../carddecks/cards-konqi-modern/12.png | Bin 0 -> 4982 bytes .../carddecks/cards-konqi-modern/13.png | Bin 0 -> 4420 bytes .../carddecks/cards-konqi-modern/14.png | Bin 0 -> 4434 bytes .../carddecks/cards-konqi-modern/15.png | Bin 0 -> 4282 bytes .../carddecks/cards-konqi-modern/16.png | Bin 0 -> 4310 bytes .../carddecks/cards-konqi-modern/17.png | Bin 0 -> 4112 bytes .../carddecks/cards-konqi-modern/18.png | Bin 0 -> 4114 bytes .../carddecks/cards-konqi-modern/19.png | Bin 0 -> 4125 bytes .../carddecks/cards-konqi-modern/2.png | Bin 0 -> 4815 bytes .../carddecks/cards-konqi-modern/20.png | Bin 0 -> 4116 bytes .../carddecks/cards-konqi-modern/21.png | Bin 0 -> 4088 bytes .../carddecks/cards-konqi-modern/22.png | Bin 0 -> 4066 bytes .../carddecks/cards-konqi-modern/23.png | Bin 0 -> 4068 bytes .../carddecks/cards-konqi-modern/24.png | Bin 0 -> 4081 bytes .../carddecks/cards-konqi-modern/25.png | Bin 0 -> 4127 bytes .../carddecks/cards-konqi-modern/26.png | Bin 0 -> 4126 bytes .../carddecks/cards-konqi-modern/27.png | Bin 0 -> 4081 bytes .../carddecks/cards-konqi-modern/28.png | Bin 0 -> 4153 bytes .../carddecks/cards-konqi-modern/29.png | Bin 0 -> 4202 bytes .../carddecks/cards-konqi-modern/3.png | Bin 0 -> 4328 bytes .../carddecks/cards-konqi-modern/30.png | Bin 0 -> 4174 bytes .../carddecks/cards-konqi-modern/31.png | Bin 0 -> 4193 bytes .../carddecks/cards-konqi-modern/32.png | Bin 0 -> 4151 bytes .../carddecks/cards-konqi-modern/33.png | Bin 0 -> 4251 bytes .../carddecks/cards-konqi-modern/34.png | Bin 0 -> 4215 bytes .../carddecks/cards-konqi-modern/35.png | Bin 0 -> 4256 bytes .../carddecks/cards-konqi-modern/36.png | Bin 0 -> 4213 bytes .../carddecks/cards-konqi-modern/37.png | Bin 0 -> 4294 bytes .../carddecks/cards-konqi-modern/38.png | Bin 0 -> 4282 bytes .../carddecks/cards-konqi-modern/39.png | Bin 0 -> 4275 bytes .../carddecks/cards-konqi-modern/4.png | Bin 0 -> 4297 bytes .../carddecks/cards-konqi-modern/40.png | Bin 0 -> 4228 bytes .../carddecks/cards-konqi-modern/41.png | Bin 0 -> 4347 bytes .../carddecks/cards-konqi-modern/42.png | Bin 0 -> 4329 bytes .../carddecks/cards-konqi-modern/43.png | Bin 0 -> 4341 bytes .../carddecks/cards-konqi-modern/44.png | Bin 0 -> 4252 bytes .../carddecks/cards-konqi-modern/45.png | Bin 0 -> 4273 bytes .../carddecks/cards-konqi-modern/46.png | Bin 0 -> 4228 bytes .../carddecks/cards-konqi-modern/47.png | Bin 0 -> 4309 bytes .../carddecks/cards-konqi-modern/48.png | Bin 0 -> 4105 bytes .../carddecks/cards-konqi-modern/49.png | Bin 0 -> 4327 bytes .../carddecks/cards-konqi-modern/5.png | Bin 0 -> 4959 bytes .../carddecks/cards-konqi-modern/50.png | Bin 0 -> 4024 bytes .../carddecks/cards-konqi-modern/51.png | Bin 0 -> 4031 bytes .../carddecks/cards-konqi-modern/52.png | Bin 0 -> 3897 bytes .../carddecks/cards-konqi-modern/6.png | Bin 0 -> 4973 bytes .../carddecks/cards-konqi-modern/7.png | Bin 0 -> 4830 bytes .../carddecks/cards-konqi-modern/8.png | Bin 0 -> 4896 bytes .../carddecks/cards-konqi-modern/9.png | Bin 0 -> 4952 bytes .../cards-konqi-modern/index.desktop | 62 + libkdegames/carddecks/cards-penguins/1.png | Bin 0 -> 1869 bytes libkdegames/carddecks/cards-penguins/10.png | Bin 0 -> 2801 bytes libkdegames/carddecks/cards-penguins/11.png | Bin 0 -> 3040 bytes libkdegames/carddecks/cards-penguins/12.png | Bin 0 -> 2981 bytes libkdegames/carddecks/cards-penguins/13.png | Bin 0 -> 2161 bytes libkdegames/carddecks/cards-penguins/14.png | Bin 0 -> 2069 bytes libkdegames/carddecks/cards-penguins/15.png | Bin 0 -> 2367 bytes libkdegames/carddecks/cards-penguins/16.png | Bin 0 -> 2274 bytes libkdegames/carddecks/cards-penguins/17.png | Bin 0 -> 2416 bytes libkdegames/carddecks/cards-penguins/18.png | Bin 0 -> 2134 bytes libkdegames/carddecks/cards-penguins/19.png | Bin 0 -> 2381 bytes libkdegames/carddecks/cards-penguins/2.png | Bin 0 -> 3191 bytes libkdegames/carddecks/cards-penguins/20.png | Bin 0 -> 2212 bytes libkdegames/carddecks/cards-penguins/21.png | Bin 0 -> 2330 bytes libkdegames/carddecks/cards-penguins/22.png | Bin 0 -> 2056 bytes libkdegames/carddecks/cards-penguins/23.png | Bin 0 -> 2254 bytes libkdegames/carddecks/cards-penguins/24.png | Bin 0 -> 2035 bytes libkdegames/carddecks/cards-penguins/25.png | Bin 0 -> 2229 bytes libkdegames/carddecks/cards-penguins/26.png | Bin 0 -> 1989 bytes libkdegames/carddecks/cards-penguins/27.png | Bin 0 -> 2140 bytes libkdegames/carddecks/cards-penguins/28.png | Bin 0 -> 2005 bytes libkdegames/carddecks/cards-penguins/29.png | Bin 0 -> 2213 bytes libkdegames/carddecks/cards-penguins/3.png | Bin 0 -> 1739 bytes libkdegames/carddecks/cards-penguins/30.png | Bin 0 -> 1977 bytes libkdegames/carddecks/cards-penguins/31.png | Bin 0 -> 2111 bytes libkdegames/carddecks/cards-penguins/32.png | Bin 0 -> 1872 bytes libkdegames/carddecks/cards-penguins/33.png | Bin 0 -> 2077 bytes libkdegames/carddecks/cards-penguins/34.png | Bin 0 -> 1889 bytes libkdegames/carddecks/cards-penguins/35.png | Bin 0 -> 1991 bytes libkdegames/carddecks/cards-penguins/36.png | Bin 0 -> 1786 bytes libkdegames/carddecks/cards-penguins/37.png | Bin 0 -> 1918 bytes libkdegames/carddecks/cards-penguins/38.png | Bin 0 -> 1762 bytes libkdegames/carddecks/cards-penguins/39.png | Bin 0 -> 1879 bytes libkdegames/carddecks/cards-penguins/4.png | Bin 0 -> 1523 bytes libkdegames/carddecks/cards-penguins/40.png | Bin 0 -> 1741 bytes libkdegames/carddecks/cards-penguins/41.png | Bin 0 -> 1668 bytes libkdegames/carddecks/cards-penguins/42.png | Bin 0 -> 1536 bytes libkdegames/carddecks/cards-penguins/43.png | Bin 0 -> 1640 bytes libkdegames/carddecks/cards-penguins/44.png | Bin 0 -> 1523 bytes libkdegames/carddecks/cards-penguins/45.png | Bin 0 -> 1672 bytes libkdegames/carddecks/cards-penguins/46.png | Bin 0 -> 1563 bytes libkdegames/carddecks/cards-penguins/47.png | Bin 0 -> 1685 bytes libkdegames/carddecks/cards-penguins/48.png | Bin 0 -> 1571 bytes libkdegames/carddecks/cards-penguins/49.png | Bin 0 -> 1432 bytes libkdegames/carddecks/cards-penguins/5.png | Bin 0 -> 2781 bytes libkdegames/carddecks/cards-penguins/50.png | Bin 0 -> 1329 bytes libkdegames/carddecks/cards-penguins/51.png | Bin 0 -> 1462 bytes libkdegames/carddecks/cards-penguins/52.png | Bin 0 -> 1390 bytes libkdegames/carddecks/cards-penguins/6.png | Bin 0 -> 2699 bytes libkdegames/carddecks/cards-penguins/7.png | Bin 0 -> 2923 bytes libkdegames/carddecks/cards-penguins/8.png | Bin 0 -> 2866 bytes libkdegames/carddecks/cards-penguins/9.png | Bin 0 -> 2894 bytes .../carddecks/cards-penguins/COPYRIGHT | 10 + .../carddecks/cards-penguins/index.desktop | 67 + libkdegames/carddecks/cards-spaced/1.png | Bin 0 -> 5992 bytes libkdegames/carddecks/cards-spaced/10.png | Bin 0 -> 5927 bytes libkdegames/carddecks/cards-spaced/11.png | Bin 0 -> 5976 bytes libkdegames/carddecks/cards-spaced/12.png | Bin 0 -> 6113 bytes libkdegames/carddecks/cards-spaced/13.png | Bin 0 -> 5973 bytes libkdegames/carddecks/cards-spaced/14.png | Bin 0 -> 6036 bytes libkdegames/carddecks/cards-spaced/15.png | Bin 0 -> 5965 bytes libkdegames/carddecks/cards-spaced/16.png | Bin 0 -> 5621 bytes libkdegames/carddecks/cards-spaced/17.png | Bin 0 -> 6906 bytes libkdegames/carddecks/cards-spaced/18.png | Bin 0 -> 6794 bytes libkdegames/carddecks/cards-spaced/19.png | Bin 0 -> 6863 bytes libkdegames/carddecks/cards-spaced/2.png | Bin 0 -> 6041 bytes libkdegames/carddecks/cards-spaced/20.png | Bin 0 -> 6711 bytes libkdegames/carddecks/cards-spaced/21.png | Bin 0 -> 6800 bytes libkdegames/carddecks/cards-spaced/22.png | Bin 0 -> 6749 bytes libkdegames/carddecks/cards-spaced/23.png | Bin 0 -> 6883 bytes libkdegames/carddecks/cards-spaced/24.png | Bin 0 -> 6635 bytes libkdegames/carddecks/cards-spaced/25.png | Bin 0 -> 6736 bytes libkdegames/carddecks/cards-spaced/26.png | Bin 0 -> 6698 bytes libkdegames/carddecks/cards-spaced/27.png | Bin 0 -> 6778 bytes libkdegames/carddecks/cards-spaced/28.png | Bin 0 -> 6515 bytes libkdegames/carddecks/cards-spaced/29.png | Bin 0 -> 6675 bytes libkdegames/carddecks/cards-spaced/3.png | Bin 0 -> 6057 bytes libkdegames/carddecks/cards-spaced/30.png | Bin 0 -> 6594 bytes libkdegames/carddecks/cards-spaced/31.png | Bin 0 -> 6760 bytes libkdegames/carddecks/cards-spaced/32.png | Bin 0 -> 6506 bytes libkdegames/carddecks/cards-spaced/33.png | Bin 0 -> 6554 bytes libkdegames/carddecks/cards-spaced/34.png | Bin 0 -> 6520 bytes libkdegames/carddecks/cards-spaced/35.png | Bin 0 -> 6716 bytes libkdegames/carddecks/cards-spaced/36.png | Bin 0 -> 6444 bytes libkdegames/carddecks/cards-spaced/37.png | Bin 0 -> 6500 bytes libkdegames/carddecks/cards-spaced/38.png | Bin 0 -> 6448 bytes libkdegames/carddecks/cards-spaced/39.png | Bin 0 -> 6636 bytes libkdegames/carddecks/cards-spaced/4.png | Bin 0 -> 5898 bytes libkdegames/carddecks/cards-spaced/40.png | Bin 0 -> 6317 bytes libkdegames/carddecks/cards-spaced/41.png | Bin 0 -> 6381 bytes libkdegames/carddecks/cards-spaced/42.png | Bin 0 -> 6333 bytes libkdegames/carddecks/cards-spaced/43.png | Bin 0 -> 6515 bytes libkdegames/carddecks/cards-spaced/44.png | Bin 0 -> 6247 bytes libkdegames/carddecks/cards-spaced/45.png | Bin 0 -> 6281 bytes libkdegames/carddecks/cards-spaced/46.png | Bin 0 -> 6259 bytes libkdegames/carddecks/cards-spaced/47.png | Bin 0 -> 6378 bytes libkdegames/carddecks/cards-spaced/48.png | Bin 0 -> 6147 bytes libkdegames/carddecks/cards-spaced/49.png | Bin 0 -> 6156 bytes libkdegames/carddecks/cards-spaced/5.png | Bin 0 -> 5995 bytes libkdegames/carddecks/cards-spaced/50.png | Bin 0 -> 6132 bytes libkdegames/carddecks/cards-spaced/51.png | Bin 0 -> 6243 bytes libkdegames/carddecks/cards-spaced/52.png | Bin 0 -> 6043 bytes libkdegames/carddecks/cards-spaced/6.png | Bin 0 -> 6069 bytes libkdegames/carddecks/cards-spaced/7.png | Bin 0 -> 5927 bytes libkdegames/carddecks/cards-spaced/8.png | Bin 0 -> 5866 bytes libkdegames/carddecks/cards-spaced/9.png | Bin 0 -> 6014 bytes libkdegames/carddecks/cards-spaced/COPYRIGHT | 16 + .../carddecks/cards-spaced/index.desktop | 46 + libkdegames/carddecks/cards-warwick/0.png | Bin 0 -> 1606 bytes libkdegames/carddecks/cards-warwick/1.png | Bin 0 -> 500 bytes libkdegames/carddecks/cards-warwick/10.png | Bin 0 -> 1535 bytes libkdegames/carddecks/cards-warwick/105.png | Bin 0 -> 844 bytes libkdegames/carddecks/cards-warwick/106.png | Bin 0 -> 860 bytes libkdegames/carddecks/cards-warwick/107.png | Bin 0 -> 853 bytes libkdegames/carddecks/cards-warwick/108.png | Bin 0 -> 793 bytes libkdegames/carddecks/cards-warwick/109.png | Bin 0 -> 848 bytes libkdegames/carddecks/cards-warwick/11.png | Bin 0 -> 1553 bytes libkdegames/carddecks/cards-warwick/110.png | Bin 0 -> 868 bytes libkdegames/carddecks/cards-warwick/111.png | Bin 0 -> 856 bytes libkdegames/carddecks/cards-warwick/112.png | Bin 0 -> 773 bytes libkdegames/carddecks/cards-warwick/113.png | Bin 0 -> 746 bytes libkdegames/carddecks/cards-warwick/114.png | Bin 0 -> 778 bytes libkdegames/carddecks/cards-warwick/115.png | Bin 0 -> 771 bytes libkdegames/carddecks/cards-warwick/116.png | Bin 0 -> 663 bytes libkdegames/carddecks/cards-warwick/12.png | Bin 0 -> 1500 bytes libkdegames/carddecks/cards-warwick/13.png | Bin 0 -> 1444 bytes libkdegames/carddecks/cards-warwick/14.png | Bin 0 -> 1408 bytes libkdegames/carddecks/cards-warwick/15.png | Bin 0 -> 1452 bytes libkdegames/carddecks/cards-warwick/16.png | Bin 0 -> 1356 bytes libkdegames/carddecks/cards-warwick/17.png | Bin 0 -> 756 bytes libkdegames/carddecks/cards-warwick/18.png | Bin 0 -> 789 bytes libkdegames/carddecks/cards-warwick/19.png | Bin 0 -> 788 bytes libkdegames/carddecks/cards-warwick/2.png | Bin 0 -> 500 bytes libkdegames/carddecks/cards-warwick/20.png | Bin 0 -> 715 bytes libkdegames/carddecks/cards-warwick/21.png | Bin 0 -> 756 bytes libkdegames/carddecks/cards-warwick/22.png | Bin 0 -> 776 bytes libkdegames/carddecks/cards-warwick/23.png | Bin 0 -> 782 bytes libkdegames/carddecks/cards-warwick/24.png | Bin 0 -> 684 bytes libkdegames/carddecks/cards-warwick/25.png | Bin 0 -> 761 bytes libkdegames/carddecks/cards-warwick/26.png | Bin 0 -> 789 bytes libkdegames/carddecks/cards-warwick/27.png | Bin 0 -> 800 bytes libkdegames/carddecks/cards-warwick/28.png | Bin 0 -> 673 bytes libkdegames/carddecks/cards-warwick/29.png | Bin 0 -> 696 bytes libkdegames/carddecks/cards-warwick/3.png | Bin 0 -> 524 bytes libkdegames/carddecks/cards-warwick/30.png | Bin 0 -> 722 bytes libkdegames/carddecks/cards-warwick/31.png | Bin 0 -> 732 bytes libkdegames/carddecks/cards-warwick/32.png | Bin 0 -> 640 bytes libkdegames/carddecks/cards-warwick/33.png | Bin 0 -> 636 bytes libkdegames/carddecks/cards-warwick/34.png | Bin 0 -> 656 bytes libkdegames/carddecks/cards-warwick/35.png | Bin 0 -> 661 bytes libkdegames/carddecks/cards-warwick/36.png | Bin 0 -> 607 bytes libkdegames/carddecks/cards-warwick/37.png | Bin 0 -> 698 bytes libkdegames/carddecks/cards-warwick/38.png | Bin 0 -> 720 bytes libkdegames/carddecks/cards-warwick/39.png | Bin 0 -> 730 bytes libkdegames/carddecks/cards-warwick/4.png | Bin 0 -> 477 bytes libkdegames/carddecks/cards-warwick/40.png | Bin 0 -> 651 bytes libkdegames/carddecks/cards-warwick/41.png | Bin 0 -> 593 bytes libkdegames/carddecks/cards-warwick/42.png | Bin 0 -> 605 bytes libkdegames/carddecks/cards-warwick/43.png | Bin 0 -> 607 bytes libkdegames/carddecks/cards-warwick/44.png | Bin 0 -> 562 bytes libkdegames/carddecks/cards-warwick/45.png | Bin 0 -> 593 bytes libkdegames/carddecks/cards-warwick/46.png | Bin 0 -> 600 bytes libkdegames/carddecks/cards-warwick/47.png | Bin 0 -> 627 bytes libkdegames/carddecks/cards-warwick/48.png | Bin 0 -> 570 bytes libkdegames/carddecks/cards-warwick/49.png | Bin 0 -> 571 bytes libkdegames/carddecks/cards-warwick/5.png | Bin 0 -> 1517 bytes libkdegames/carddecks/cards-warwick/50.png | Bin 0 -> 572 bytes libkdegames/carddecks/cards-warwick/51.png | Bin 0 -> 591 bytes libkdegames/carddecks/cards-warwick/52.png | Bin 0 -> 543 bytes libkdegames/carddecks/cards-warwick/6.png | Bin 0 -> 1477 bytes libkdegames/carddecks/cards-warwick/7.png | Bin 0 -> 1536 bytes libkdegames/carddecks/cards-warwick/8.png | Bin 0 -> 1451 bytes libkdegames/carddecks/cards-warwick/9.png | Bin 0 -> 1617 bytes .../carddecks/cards-warwick/index.desktop | 126 + .../carddecks/cards-xskat-french/1.png | Bin 0 -> 2525 bytes .../carddecks/cards-xskat-french/10.png | Bin 0 -> 11981 bytes .../carddecks/cards-xskat-french/11.png | Bin 0 -> 11495 bytes .../carddecks/cards-xskat-french/12.png | Bin 0 -> 11508 bytes .../carddecks/cards-xskat-french/13.png | Bin 0 -> 14544 bytes .../carddecks/cards-xskat-french/14.png | Bin 0 -> 13192 bytes .../carddecks/cards-xskat-french/15.png | Bin 0 -> 12722 bytes .../carddecks/cards-xskat-french/16.png | Bin 0 -> 12880 bytes .../carddecks/cards-xskat-french/17.png | Bin 0 -> 5891 bytes .../carddecks/cards-xskat-french/18.png | Bin 0 -> 4439 bytes .../carddecks/cards-xskat-french/19.png | Bin 0 -> 3308 bytes .../carddecks/cards-xskat-french/2.png | Bin 0 -> 2244 bytes .../carddecks/cards-xskat-french/20.png | Bin 0 -> 3016 bytes .../carddecks/cards-xskat-french/21.png | Bin 0 -> 5372 bytes .../carddecks/cards-xskat-french/22.png | Bin 0 -> 4037 bytes .../carddecks/cards-xskat-french/23.png | Bin 0 -> 3080 bytes .../carddecks/cards-xskat-french/24.png | Bin 0 -> 2786 bytes .../carddecks/cards-xskat-french/25.png | Bin 0 -> 5005 bytes .../carddecks/cards-xskat-french/26.png | Bin 0 -> 3925 bytes .../carddecks/cards-xskat-french/27.png | Bin 0 -> 2879 bytes .../carddecks/cards-xskat-french/28.png | Bin 0 -> 2777 bytes .../carddecks/cards-xskat-french/29.png | Bin 0 -> 4537 bytes .../carddecks/cards-xskat-french/3.png | Bin 0 -> 1928 bytes .../carddecks/cards-xskat-french/30.png | Bin 0 -> 3505 bytes .../carddecks/cards-xskat-french/31.png | Bin 0 -> 2647 bytes .../carddecks/cards-xskat-french/32.png | Bin 0 -> 2538 bytes .../carddecks/cards-xskat-french/33.png | Bin 0 -> 4139 bytes .../carddecks/cards-xskat-french/34.png | Bin 0 -> 3287 bytes .../carddecks/cards-xskat-french/35.png | Bin 0 -> 2402 bytes .../carddecks/cards-xskat-french/36.png | Bin 0 -> 2227 bytes .../carddecks/cards-xskat-french/37.png | Bin 0 -> 4228 bytes .../carddecks/cards-xskat-french/38.png | Bin 0 -> 3234 bytes .../carddecks/cards-xskat-french/39.png | Bin 0 -> 2528 bytes .../carddecks/cards-xskat-french/4.png | Bin 0 -> 1881 bytes .../carddecks/cards-xskat-french/40.png | Bin 0 -> 2437 bytes .../carddecks/cards-xskat-french/41.png | Bin 0 -> 3653 bytes .../carddecks/cards-xskat-french/42.png | Bin 0 -> 2841 bytes .../carddecks/cards-xskat-french/43.png | Bin 0 -> 2227 bytes .../carddecks/cards-xskat-french/44.png | Bin 0 -> 2123 bytes .../carddecks/cards-xskat-french/45.png | Bin 0 -> 3384 bytes .../carddecks/cards-xskat-french/46.png | Bin 0 -> 2888 bytes .../carddecks/cards-xskat-french/47.png | Bin 0 -> 2382 bytes .../carddecks/cards-xskat-french/48.png | Bin 0 -> 2295 bytes .../carddecks/cards-xskat-french/49.png | Bin 0 -> 2894 bytes .../carddecks/cards-xskat-french/5.png | Bin 0 -> 14410 bytes .../carddecks/cards-xskat-french/50.png | Bin 0 -> 2411 bytes .../carddecks/cards-xskat-french/51.png | Bin 0 -> 2159 bytes .../carddecks/cards-xskat-french/52.png | Bin 0 -> 2043 bytes .../carddecks/cards-xskat-french/6.png | Bin 0 -> 13796 bytes .../carddecks/cards-xskat-french/7.png | Bin 0 -> 13434 bytes .../carddecks/cards-xskat-french/8.png | Bin 0 -> 13344 bytes .../carddecks/cards-xskat-french/9.png | Bin 0 -> 12558 bytes .../carddecks/cards-xskat-french/COPYRIGHT | 8 + .../cards-xskat-french/index.desktop | 55 + .../carddecks/cards-xskat-german/1.png | Bin 0 -> 3915 bytes .../carddecks/cards-xskat-german/10.png | Bin 0 -> 10814 bytes .../carddecks/cards-xskat-german/11.png | Bin 0 -> 10506 bytes .../carddecks/cards-xskat-german/12.png | Bin 0 -> 11304 bytes .../carddecks/cards-xskat-german/13.png | Bin 0 -> 12477 bytes .../carddecks/cards-xskat-german/14.png | Bin 0 -> 12487 bytes .../carddecks/cards-xskat-german/15.png | Bin 0 -> 12450 bytes .../carddecks/cards-xskat-german/16.png | Bin 0 -> 12614 bytes .../carddecks/cards-xskat-german/17.png | Bin 0 -> 7191 bytes .../carddecks/cards-xskat-german/18.png | Bin 0 -> 7804 bytes .../carddecks/cards-xskat-german/19.png | Bin 0 -> 6331 bytes .../carddecks/cards-xskat-german/2.png | Bin 0 -> 3618 bytes .../carddecks/cards-xskat-german/20.png | Bin 0 -> 7794 bytes .../carddecks/cards-xskat-german/21.png | Bin 0 -> 6694 bytes .../carddecks/cards-xskat-german/22.png | Bin 0 -> 7311 bytes .../carddecks/cards-xskat-german/23.png | Bin 0 -> 5828 bytes .../carddecks/cards-xskat-german/24.png | Bin 0 -> 7572 bytes .../carddecks/cards-xskat-german/25.png | Bin 0 -> 6612 bytes .../carddecks/cards-xskat-german/26.png | Bin 0 -> 7207 bytes .../carddecks/cards-xskat-german/27.png | Bin 0 -> 5627 bytes .../carddecks/cards-xskat-german/28.png | Bin 0 -> 6886 bytes .../carddecks/cards-xskat-german/29.png | Bin 0 -> 6141 bytes .../carddecks/cards-xskat-german/3.png | Bin 0 -> 3816 bytes .../carddecks/cards-xskat-german/30.png | Bin 0 -> 6695 bytes .../carddecks/cards-xskat-german/31.png | Bin 0 -> 5139 bytes .../carddecks/cards-xskat-german/32.png | Bin 0 -> 6181 bytes .../carddecks/cards-xskat-german/33.png | Bin 0 -> 5857 bytes .../carddecks/cards-xskat-german/34.png | Bin 0 -> 6473 bytes .../carddecks/cards-xskat-german/35.png | Bin 0 -> 4789 bytes .../carddecks/cards-xskat-german/36.png | Bin 0 -> 5734 bytes .../carddecks/cards-xskat-german/37.png | Bin 0 -> 5485 bytes .../carddecks/cards-xskat-german/38.png | Bin 0 -> 6006 bytes .../carddecks/cards-xskat-german/39.png | Bin 0 -> 4422 bytes .../carddecks/cards-xskat-german/4.png | Bin 0 -> 3891 bytes .../carddecks/cards-xskat-german/40.png | Bin 0 -> 5223 bytes .../carddecks/cards-xskat-german/41.png | Bin 0 -> 5148 bytes .../carddecks/cards-xskat-german/42.png | Bin 0 -> 5651 bytes .../carddecks/cards-xskat-german/43.png | Bin 0 -> 3821 bytes .../carddecks/cards-xskat-german/44.png | Bin 0 -> 4167 bytes .../carddecks/cards-xskat-german/45.png | Bin 0 -> 4642 bytes .../carddecks/cards-xskat-german/46.png | Bin 0 -> 5223 bytes .../carddecks/cards-xskat-german/47.png | Bin 0 -> 3355 bytes .../carddecks/cards-xskat-german/48.png | Bin 0 -> 3761 bytes .../carddecks/cards-xskat-german/49.png | Bin 0 -> 4505 bytes .../carddecks/cards-xskat-german/5.png | Bin 0 -> 13831 bytes .../carddecks/cards-xskat-german/50.png | Bin 0 -> 4802 bytes .../carddecks/cards-xskat-german/51.png | Bin 0 -> 2589 bytes .../carddecks/cards-xskat-german/52.png | Bin 0 -> 2548 bytes .../carddecks/cards-xskat-german/6.png | Bin 0 -> 14126 bytes .../carddecks/cards-xskat-german/7.png | Bin 0 -> 13910 bytes .../carddecks/cards-xskat-german/8.png | Bin 0 -> 14673 bytes .../carddecks/cards-xskat-german/9.png | Bin 0 -> 10522 bytes .../carddecks/cards-xskat-german/COPYRIGHT | 8 + .../cards-xskat-german/index.desktop | 55 + libkdegames/carddecks/convertpysols | 69 + libkdegames/carddecks/decks/deck0.desktop | 113 + libkdegames/carddecks/decks/deck0.png | Bin 0 -> 4918 bytes libkdegames/carddecks/decks/deck1.desktop | 56 + libkdegames/carddecks/decks/deck1.png | Bin 0 -> 4941 bytes libkdegames/carddecks/decks/deck10.desktop | 66 + libkdegames/carddecks/decks/deck10.png | Bin 0 -> 950 bytes libkdegames/carddecks/decks/deck11.desktop | 66 + libkdegames/carddecks/decks/deck11.png | Bin 0 -> 950 bytes libkdegames/carddecks/decks/deck12.desktop | 41 + libkdegames/carddecks/decks/deck12.png | Bin 0 -> 6389 bytes libkdegames/carddecks/decks/deck13.desktop | 63 + libkdegames/carddecks/decks/deck13.png | Bin 0 -> 4296 bytes libkdegames/carddecks/decks/deck14.desktop | 66 + libkdegames/carddecks/decks/deck14.png | Bin 0 -> 2776 bytes libkdegames/carddecks/decks/deck15.desktop | 19 + libkdegames/carddecks/decks/deck15.png | Bin 0 -> 2627 bytes libkdegames/carddecks/decks/deck16.desktop | 60 + libkdegames/carddecks/decks/deck16.png | Bin 0 -> 406 bytes libkdegames/carddecks/decks/deck17.desktop | 65 + libkdegames/carddecks/decks/deck17.png | Bin 0 -> 277 bytes libkdegames/carddecks/decks/deck18.desktop | 19 + libkdegames/carddecks/decks/deck18.png | Bin 0 -> 5217 bytes libkdegames/carddecks/decks/deck19.desktop | 63 + libkdegames/carddecks/decks/deck19.png | Bin 0 -> 5602 bytes libkdegames/carddecks/decks/deck2.desktop | 49 + libkdegames/carddecks/decks/deck2.png | Bin 0 -> 5416 bytes libkdegames/carddecks/decks/deck20.desktop | 71 + libkdegames/carddecks/decks/deck20.png | Bin 0 -> 6011 bytes libkdegames/carddecks/decks/deck21.desktop | 65 + libkdegames/carddecks/decks/deck21.png | Bin 0 -> 6333 bytes libkdegames/carddecks/decks/deck22.desktop | 58 + libkdegames/carddecks/decks/deck22.png | Bin 0 -> 6192 bytes libkdegames/carddecks/decks/deck23.desktop | 55 + libkdegames/carddecks/decks/deck23.png | Bin 0 -> 5964 bytes libkdegames/carddecks/decks/deck24.desktop | 60 + libkdegames/carddecks/decks/deck24.png | Bin 0 -> 4885 bytes libkdegames/carddecks/decks/deck3.desktop | 60 + libkdegames/carddecks/decks/deck3.png | Bin 0 -> 6162 bytes libkdegames/carddecks/decks/deck4.desktop | 93 + libkdegames/carddecks/decks/deck4.png | Bin 0 -> 6355 bytes libkdegames/carddecks/decks/deck5.desktop | 67 + libkdegames/carddecks/decks/deck5.png | Bin 0 -> 4133 bytes libkdegames/carddecks/decks/deck6.desktop | 66 + libkdegames/carddecks/decks/deck6.png | Bin 0 -> 4067 bytes libkdegames/carddecks/decks/deck7.desktop | 67 + libkdegames/carddecks/decks/deck7.png | Bin 0 -> 3464 bytes libkdegames/carddecks/decks/deck8.desktop | 67 + libkdegames/carddecks/decks/deck8.png | Bin 0 -> 3780 bytes libkdegames/carddecks/decks/deck9.desktop | 7 + libkdegames/carddecks/decks/deck9.png | Bin 0 -> 1019 bytes libkdegames/configure.in.in | 49 + libkdegames/highscore/INSTALL | 12 + libkdegames/highscore/Makefile.am | 19 + libkdegames/highscore/kconfigrawbackend.cpp | 62 + libkdegames/highscore/kconfigrawbackend.h | 57 + libkdegames/highscore/kexthighscore.cpp | 289 ++ libkdegames/highscore/kexthighscore.h | 367 ++ libkdegames/highscore/kexthighscore_gui.cpp | 552 +++ libkdegames/highscore/kexthighscore_gui.h | 207 + .../highscore/kexthighscore_internal.cpp | 868 ++++ .../highscore/kexthighscore_internal.h | 277 ++ libkdegames/highscore/kexthighscore_item.cpp | 312 ++ libkdegames/highscore/kexthighscore_item.h | 317 ++ libkdegames/highscore/kexthighscore_tab.cpp | 281 ++ libkdegames/highscore/kexthighscore_tab.h | 117 + libkdegames/highscore/kfilelock.cpp | 88 + libkdegames/highscore/kfilelock.h | 53 + libkdegames/highscore/khighscore.cpp | 262 + libkdegames/highscore/khighscore.h | 311 ++ libkdegames/highscore/kscoredialog.cpp | 411 ++ libkdegames/highscore/kscoredialog.h | 125 + libkdegames/kcanvasrootpixmap.cpp | 39 + libkdegames/kcanvasrootpixmap.h | 61 + libkdegames/kcarddialog.cpp | 808 +++ libkdegames/kcarddialog.h | 345 ++ libkdegames/kcarddialog.png | Bin 0 -> 34451 bytes libkdegames/kchat.cpp | 115 + libkdegames/kchat.h | 147 + libkdegames/kchatbase.cpp | 530 ++ libkdegames/kchatbase.h | 510 ++ libkdegames/kchatdialog.cpp | 253 + libkdegames/kchatdialog.h | 119 + libkdegames/kgame/COMPAT | 55 + libkdegames/kgame/DESIGN | 407 ++ libkdegames/kgame/Makefile.am | 29 + libkdegames/kgame/README.LIB | 12 + libkdegames/kgame/TODO | 41 + libkdegames/kgame/dialogs/Makefile.am | 17 + .../kgame/dialogs/kgameconnectdialog.cpp | 278 ++ .../kgame/dialogs/kgameconnectdialog.h | 169 + .../kgame/dialogs/kgamedebugdialog.cpp | 548 ++ libkdegames/kgame/dialogs/kgamedebugdialog.h | 149 + libkdegames/kgame/dialogs/kgamedialog.cpp | 347 ++ libkdegames/kgame/dialogs/kgamedialog.h | 320 ++ .../kgame/dialogs/kgamedialogconfig.cpp | 773 +++ libkdegames/kgame/dialogs/kgamedialogconfig.h | 362 ++ .../kgame/dialogs/kgameerrordialog.cpp | 129 + libkdegames/kgame/dialogs/kgameerrordialog.h | 113 + libkdegames/kgame/kgame.cpp | 1475 ++++++ libkdegames/kgame/kgame.h | 932 ++++ libkdegames/kgame/kgamechat.cpp | 341 ++ libkdegames/kgame/kgamechat.h | 223 + libkdegames/kgame/kgameerror.cpp | 80 + libkdegames/kgame/kgameerror.h | 59 + libkdegames/kgame/kgameio.cpp | 539 ++ libkdegames/kgame/kgameio.h | 566 +++ libkdegames/kgame/kgamemessage.cpp | 156 + libkdegames/kgame/kgamemessage.h | 173 + libkdegames/kgame/kgamenetwork.cpp | 516 ++ libkdegames/kgame/kgamenetwork.h | 431 ++ libkdegames/kgame/kgameprocess.cpp | 158 + libkdegames/kgame/kgameprocess.h | 242 + libkdegames/kgame/kgameproperty.cpp | 211 + libkdegames/kgame/kgameproperty.h | 848 ++++ libkdegames/kgame/kgamepropertyarray.h | 309 ++ libkdegames/kgame/kgamepropertyhandler.cpp | 407 ++ libkdegames/kgame/kgamepropertyhandler.h | 353 ++ libkdegames/kgame/kgamepropertylist.h | 258 + libkdegames/kgame/kgamesequence.cpp | 125 + libkdegames/kgame/kgamesequence.h | 87 + libkdegames/kgame/kgameversion.h | 54 + libkdegames/kgame/kmessageclient.cpp | 373 ++ libkdegames/kgame/kmessageclient.h | 422 ++ libkdegames/kgame/kmessageio.cpp | 482 ++ libkdegames/kgame/kmessageio.h | 416 ++ libkdegames/kgame/kmessageserver.cpp | 515 ++ libkdegames/kgame/kmessageserver.h | 492 ++ libkdegames/kgame/kmessageserver.png | Bin 0 -> 7791 bytes libkdegames/kgame/kplayer.cpp | 446 ++ libkdegames/kgame/kplayer.h | 471 ++ libkdegames/kgame/libkdegames.html | 187 + libkdegames/kgame/messages.txt | 93 + libkdegames/kgame/scenario0.png | Bin 0 -> 2413 bytes libkdegames/kgame/scenario1.png | Bin 0 -> 8074 bytes libkdegames/kgame/scenario2.png | Bin 0 -> 7398 bytes libkdegames/kgamelcd.cpp | 250 + libkdegames/kgamelcd.h | 249 + libkdegames/kgamemisc.cpp | 62 + libkdegames/kgamemisc.h | 45 + libkdegames/kgameprogress.cpp | 345 ++ libkdegames/kgameprogress.h | 255 + libkdegames/kgrid2d.h | 520 ++ libkdegames/kstdgameaction.cpp | 209 + libkdegames/kstdgameaction.h | 261 + libkdegames/pics/Makefile.am | 4 + libkdegames/pics/cr16-action-endturn.png | Bin 0 -> 364 bytes libkdegames/pics/cr16-action-highscore.png | Bin 0 -> 493 bytes libkdegames/pics/cr16-action-roll.png | Bin 0 -> 988 bytes libkdegames/pics/cr22-action-roll.png | Bin 0 -> 1284 bytes libkdegames/pics/cr32-action-endturn.png | Bin 0 -> 751 bytes libkdegames/pics/cr32-action-highscore.png | Bin 0 -> 2440 bytes libkdegames/pics/cr32-action-roll.png | Bin 0 -> 2235 bytes libkdegames/pics/star.png | Bin 0 -> 1103 bytes libksirtet/CHANGELOG | 1 + libksirtet/DESIGN | 57 + libksirtet/LICENSE | 22 + libksirtet/Makefile.am | 4 + libksirtet/README | 16 + libksirtet/README.gtetris | 21 + libksirtet/TODO | 15 + libksirtet/base/Makefile.am | 16 + libksirtet/base/README | 1 + libksirtet/base/baseprefs.kcfgc | 7 + libksirtet/base/board.cpp | 429 ++ libksirtet/base/board.h | 134 + libksirtet/base/factory.cpp | 52 + libksirtet/base/factory.h | 60 + libksirtet/base/field.cpp | 162 + libksirtet/base/field.h | 66 + libksirtet/base/gtetris.cpp | 241 + libksirtet/base/gtetris.h | 178 + libksirtet/base/highscores.cpp | 19 + libksirtet/base/highscores.h | 13 + libksirtet/base/inter.cpp | 15 + libksirtet/base/inter.h | 23 + libksirtet/base/kzoommainwindow.cpp | 115 + libksirtet/base/kzoommainwindow.h | 128 + libksirtet/base/libksirtet1.kcfg | 38 + libksirtet/base/main.cpp | 131 + libksirtet/base/main.h | 49 + libksirtet/base/piece.cpp | 274 + libksirtet/base/piece.h | 155 + libksirtet/base/settings.cpp | 84 + libksirtet/base/settings.h | 33 + libksirtet/common/Makefile.am | 28 + libksirtet/common/README | 1 + libksirtet/common/ai.cpp | 356 ++ libksirtet/common/ai.h | 122 + libksirtet/common/board.cpp | 286 ++ libksirtet/common/board.h | 75 + libksirtet/common/commonprefs.kcfgc | 9 + libksirtet/common/factory.cpp | 28 + libksirtet/common/factory.h | 36 + libksirtet/common/field.cpp | 243 + libksirtet/common/field.h | 68 + libksirtet/common/highscores.cpp | 60 + libksirtet/common/highscores.h | 20 + libksirtet/common/inter.cpp | 150 + libksirtet/common/inter.h | 62 + libksirtet/common/libksirtet2.kcfg | 36 + libksirtet/common/main.cpp | 60 + libksirtet/common/main.h | 22 + libksirtet/common/misc_ui.cpp | 194 + libksirtet/common/misc_ui.h | 81 + libksirtet/common/settings.cpp | 54 + libksirtet/common/settings.h | 28 + libksirtet/common/types.cpp | 16 + libksirtet/common/types.h | 26 + libksirtet/configure.in.in | 5 + libksirtet/lib/CHANGELOG | 39 + libksirtet/lib/LICENSE | 18 + libksirtet/lib/Makefile.am | 21 + libksirtet/lib/README | 63 + libksirtet/lib/TODO | 18 + libksirtet/lib/defines.cpp | 24 + libksirtet/lib/defines.h | 35 + libksirtet/lib/internal.cpp | 278 ++ libksirtet/lib/internal.h | 152 + libksirtet/lib/keys.cpp | 104 + libksirtet/lib/keys.h | 42 + libksirtet/lib/libksirtet_export.h | 35 + libksirtet/lib/meeting.cpp | 575 +++ libksirtet/lib/meeting.h | 137 + libksirtet/lib/miscui.cpp | 58 + libksirtet/lib/miscui.h | 43 + libksirtet/lib/mp_board.h | 51 + libksirtet/lib/mp_interface.cpp | 274 + libksirtet/lib/mp_interface.h | 246 + libksirtet/lib/mp_option.h | 74 + libksirtet/lib/mp_simple_board.cpp | 84 + libksirtet/lib/mp_simple_board.h | 45 + libksirtet/lib/mp_simple_interface.cpp | 152 + libksirtet/lib/mp_simple_interface.h | 48 + libksirtet/lib/mp_simple_types.cpp | 6 + libksirtet/lib/mp_simple_types.h | 36 + libksirtet/lib/pline.cpp | 147 + libksirtet/lib/pline.h | 112 + libksirtet/lib/smanager.cpp | 115 + libksirtet/lib/smanager.h | 88 + libksirtet/lib/socket.cpp | 80 + libksirtet/lib/socket.h | 65 + libksirtet/lib/types.cpp | 254 + libksirtet/lib/types.h | 197 + libksirtet/lib/version.h | 6 + libksirtet/lib/wizard.cpp | 229 + libksirtet/lib/wizard.h | 57 + lskat/AUTHORS | 1 + lskat/COPYING | 341 ++ lskat/Makefile.am | 11 + lskat/README | 33 + lskat/TODO | 3 + lskat/grafix/Makefile.am | 8 + lskat/grafix/background.png | Bin 0 -> 147 bytes lskat/grafix/deck1.png | Bin 0 -> 4985 bytes lskat/grafix/deck2.png | Bin 0 -> 5456 bytes lskat/grafix/deck3.png | Bin 0 -> 6202 bytes lskat/grafix/deck4.png | Bin 0 -> 5465 bytes lskat/grafix/t1.png | Bin 0 -> 293 bytes lskat/grafix/t2.png | Bin 0 -> 295 bytes lskat/grafix/t3.png | Bin 0 -> 286 bytes lskat/grafix/t4.png | Bin 0 -> 286 bytes lskat/grafix/t5.png | Bin 0 -> 377 bytes lskat/grafix/tback.png | Bin 0 -> 240 bytes lskat/grafix/type1.png | Bin 0 -> 655 bytes lskat/grafix/type2.png | Bin 0 -> 1078 bytes lskat/grafix/type3.png | Bin 0 -> 361 bytes lskat/hi128-app-lskat.png | Bin 0 -> 13259 bytes lskat/hi16-app-lskat.png | Bin 0 -> 750 bytes lskat/hi22-app-lskat.png | Bin 0 -> 1562 bytes lskat/hi32-app-lskat.png | Bin 0 -> 2045 bytes lskat/hi48-app-lskat.png | Bin 0 -> 3821 bytes lskat/hi64-app-lskat.png | Bin 0 -> 5084 bytes lskat/lskat.desktop | 108 + lskat/lskat.kdevprj | 286 ++ lskat/lskat.lsm | 14 + lskat/lskat/KChildConnect.cpp | 124 + lskat/lskat/KChildConnect.h | 53 + lskat/lskat/KConnectEntry.cpp | 165 + lskat/lskat/KConnectEntry.h | 56 + lskat/lskat/KConnectTypes.h | 38 + lskat/lskat/KEInput.cpp | 296 ++ lskat/lskat/KEInput.h | 82 + lskat/lskat/KEMessage.cpp | 326 ++ lskat/lskat/KEMessage.h | 66 + lskat/lskat/KInputChildProcess.cpp | 106 + lskat/lskat/KInputChildProcess.h | 57 + lskat/lskat/KInteractiveConnect.cpp | 80 + lskat/lskat/KInteractiveConnect.h | 41 + lskat/lskat/KMessageEntry.cpp | 81 + lskat/lskat/KMessageEntry.h | 42 + lskat/lskat/KProcessConnect.cpp | 192 + lskat/lskat/KProcessConnect.h | 57 + lskat/lskat/KRSocket.cpp | 374 ++ lskat/lskat/KRSocket.h | 169 + lskat/lskat/KRemoteConnect.cpp | 334 ++ lskat/lskat/KRemoteConnect.h | 74 + lskat/lskat/Makefile.am | 21 + lskat/lskat/lskat.cpp | 1081 ++++ lskat/lskat/lskat.h | 216 + lskat/lskat/lskatdoc.cpp | 817 +++ lskat/lskat/lskatdoc.h | 235 + lskat/lskat/lskatui.rc | 25 + lskat/lskat/lskatview.cpp | 934 ++++ lskat/lskat/lskatview.h | 91 + lskat/lskat/main.cpp | 67 + lskat/lskat/msgdlg.cpp | 74 + lskat/lskat/msgdlg.h | 39 + lskat/lskat/namedlg.cpp | 129 + lskat/lskat/namedlg.h | 49 + lskat/lskat/networkdlg.cpp | 105 + lskat/lskat/networkdlg.h | 48 + lskat/lskat/networkdlgbase.ui | 306 ++ lskat/lskat/templates/cpp_template | 16 + lskat/lskat/templates/header_template | 16 + lskat/lskatproc/KChildConnect.cpp | 124 + lskat/lskatproc/KChildConnect.h | 45 + lskat/lskatproc/KConnectTypes.h | 38 + lskat/lskatproc/KEMessage.cpp | 326 ++ lskat/lskatproc/KEMessage.h | 66 + lskat/lskatproc/KInputChildProcess.cpp | 106 + lskat/lskatproc/KInputChildProcess.h | 57 + lskat/lskatproc/KMessageEntry.cpp | 98 + lskat/lskatproc/KMessageEntry.h | 44 + lskat/lskatproc/Makefile.am | 13 + lskat/lskatproc/docs/Makefile.am | 4 + lskat/lskatproc/docs/en/Makefile.am | 4 + lskat/lskatproc/lskatproc.cpp | 596 +++ lskat/lskatproc/lskatproc.h | 104 + lskat/lskatproc/main.cpp | 28 + lskat/lskatproc/templates/cpp_template | 16 + lskat/lskatproc/templates/header_template | 16 + 3512 files changed, 278389 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 COPYING-DOCS create mode 100644 COPYING.LIB create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Mainpage.dox create mode 100644 Makefile.am.in create mode 100644 Makefile.cvs create mode 100644 README create mode 100644 atlantik/AUTHORS create mode 100644 atlantik/COPYING create mode 100644 atlantik/COPYING.LIB create mode 100644 atlantik/ChangeLog create mode 100644 atlantik/INSTALL create mode 100644 atlantik/Makefile.am create mode 100644 atlantik/README create mode 100644 atlantik/README.KDE-3.0 create mode 100644 atlantik/README.packaging create mode 100644 atlantik/TODO create mode 100644 atlantik/atlanticd/Makefile.am create mode 100644 atlantik/atlanticd/README create mode 100644 atlantik/atlanticd/TODO create mode 100644 atlantik/atlanticd/atlanticclient.cpp create mode 100644 atlantik/atlanticd/atlanticclient.h create mode 100644 atlantik/atlanticd/atlanticdaemon.cpp create mode 100644 atlantik/atlanticd/atlanticdaemon.h create mode 100644 atlantik/atlanticd/main.cpp create mode 100644 atlantik/atlanticd/serversocket.cpp create mode 100644 atlantik/atlanticd/serversocket.h create mode 100644 atlantik/atlantik.desktop create mode 100644 atlantik/atlantikui.rc create mode 100644 atlantik/client/Makefile.am create mode 100644 atlantik/client/atlantik.cpp create mode 100644 atlantik/client/atlantik.h create mode 100644 atlantik/client/configdlg.cpp create mode 100644 atlantik/client/configdlg.h create mode 100644 atlantik/client/event.cpp create mode 100644 atlantik/client/event.h create mode 100644 atlantik/client/eventlogwidget.cpp create mode 100644 atlantik/client/eventlogwidget.h create mode 100644 atlantik/client/main.cpp create mode 100644 atlantik/client/main.h create mode 100644 atlantik/client/monopigator.cpp create mode 100644 atlantik/client/monopigator.h create mode 100644 atlantik/client/selectconfiguration_widget.cpp create mode 100644 atlantik/client/selectconfiguration_widget.h create mode 100644 atlantik/client/selectgame_widget.cpp create mode 100644 atlantik/client/selectgame_widget.h create mode 100644 atlantik/client/selectserver_widget.cpp create mode 100644 atlantik/client/selectserver_widget.h create mode 100644 atlantik/eventsrc create mode 100644 atlantik/kio_atlantik/Makefile.am create mode 100644 atlantik/kio_atlantik/atlantik.protocol create mode 100644 atlantik/kio_atlantik/kio_atlantik.cpp create mode 100644 atlantik/kio_atlantik/kio_atlantik.h create mode 100644 atlantik/libatlantic/Makefile.am create mode 100644 atlantik/libatlantic/atlantic_core.cpp create mode 100644 atlantik/libatlantic/atlantic_core.h create mode 100644 atlantik/libatlantic/auction.cpp create mode 100644 atlantik/libatlantic/auction.h create mode 100644 atlantik/libatlantic/configoption.cpp create mode 100644 atlantik/libatlantic/configoption.h create mode 100644 atlantik/libatlantic/estate.cpp create mode 100644 atlantik/libatlantic/estate.h create mode 100644 atlantik/libatlantic/estategroup.cpp create mode 100644 atlantik/libatlantic/estategroup.h create mode 100644 atlantik/libatlantic/game.cpp create mode 100644 atlantik/libatlantic/game.h create mode 100644 atlantik/libatlantic/libatlantic_export.h create mode 100644 atlantik/libatlantic/player.cpp create mode 100644 atlantik/libatlantic/player.h create mode 100644 atlantik/libatlantic/trade.cpp create mode 100644 atlantik/libatlantic/trade.h create mode 100644 atlantik/libatlantikclient/Makefile.am create mode 100644 atlantik/libatlantikclient/atlantik_network.cpp create mode 100644 atlantik/libatlantikclient/atlantik_network.h create mode 100644 atlantik/libatlantikclient/monopdprotocol.cpp create mode 100644 atlantik/libatlantikclient/monopdprotocol.h create mode 100644 atlantik/libatlantikui/Makefile.am create mode 100644 atlantik/libatlantikui/auction_widget.cpp create mode 100644 atlantik/libatlantikui/auction_widget.h create mode 100644 atlantik/libatlantikui/board.cpp create mode 100644 atlantik/libatlantikui/board.h create mode 100644 atlantik/libatlantikui/estatedetails.cpp create mode 100644 atlantik/libatlantikui/estatedetails.h create mode 100644 atlantik/libatlantikui/estateview.cpp create mode 100644 atlantik/libatlantikui/estateview.h create mode 100644 atlantik/libatlantikui/kwrappedlistviewitem.cpp create mode 100644 atlantik/libatlantikui/kwrappedlistviewitem.h create mode 100644 atlantik/libatlantikui/libatlantikui_export.h create mode 100644 atlantik/libatlantikui/portfolioestate.cpp create mode 100644 atlantik/libatlantikui/portfolioestate.h create mode 100644 atlantik/libatlantikui/portfolioview.cpp create mode 100644 atlantik/libatlantikui/portfolioview.h create mode 100644 atlantik/libatlantikui/token.cpp create mode 100644 atlantik/libatlantikui/token.h create mode 100644 atlantik/libatlantikui/trade_widget.cpp create mode 100644 atlantik/libatlantikui/trade_widget.h create mode 100644 atlantik/pics/Makefile.am create mode 100644 atlantik/pics/board/Makefile.am create mode 100644 atlantik/pics/board/arrow.png create mode 100644 atlantik/pics/board/qmark-blue.png create mode 100644 atlantik/pics/board/qmark-red.png create mode 100644 atlantik/pics/board/token.png create mode 100644 atlantik/pics/board/train.png create mode 100644 atlantik/pics/hi16-app-atlantik.png create mode 100644 atlantik/pics/hi32-app-atlantik.png create mode 100644 atlantik/pics/hi48-app-atlantik.png create mode 100644 atlantik/pics/misc/Makefile.am create mode 100644 atlantik/pics/misc/cr32-action-monop_board.png create mode 100644 atlantik/pics/toolbar/Makefile.am create mode 100644 atlantik/pics/toolbar/cr16-action-jail_pay.png create mode 100644 atlantik/pics/toolbar/cr22-action-atlantik_buy_estate.png create mode 100644 atlantik/pics/toolbar/cr22-action-jail_pay.png create mode 100644 atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png create mode 100644 atlantik/pics/toolbar/cr32-action-auction.png create mode 100644 atlantik/pics/toolbar/cr32-action-jail_pay.png create mode 100644 atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png create mode 100644 atlantik/themes/Makefile.am create mode 100644 atlantik/themes/default/Makefile.am create mode 100644 atlantik/themes/default/tokens/Makefile.am create mode 100644 atlantik/themes/default/tokens/badge.png create mode 100644 atlantik/themes/default/tokens/beachball.png create mode 100644 atlantik/themes/default/tokens/bell.png create mode 100644 atlantik/themes/default/tokens/bomb.png create mode 100644 atlantik/themes/default/tokens/cat.png create mode 100644 atlantik/themes/default/tokens/cookie.png create mode 100644 atlantik/themes/default/tokens/cube.png create mode 100644 atlantik/themes/default/tokens/eyeball.png create mode 100644 atlantik/themes/default/tokens/flag.png create mode 100644 atlantik/themes/default/tokens/ghost.png create mode 100644 atlantik/themes/default/tokens/globe.png create mode 100644 atlantik/themes/default/tokens/hamburger.png create mode 100644 atlantik/themes/default/tokens/lips.png create mode 100644 atlantik/themes/default/tokens/puzzle.png create mode 100644 atlantik/themes/default/tokens/pyramid.png create mode 100644 atlantik/themes/default/tokens/skull.png create mode 100644 atlantik/themes/default/tokens/traffic_light.png create mode 100644 atlantik/themes/default/tokens/wizard.png create mode 100644 configure.in.in create mode 100644 doc/Makefile.am create mode 100644 doc/api/kcarddialog.png create mode 100644 doc/atlantik/Makefile.am create mode 100644 doc/atlantik/index.docbook create mode 100644 doc/atlantik/man-atlantik.6.docbook create mode 100644 doc/kasteroids/Makefile.am create mode 100644 doc/kasteroids/fuel.png create mode 100644 doc/kasteroids/index.docbook create mode 100644 doc/katomic/Makefile.am create mode 100644 doc/katomic/index.docbook create mode 100644 doc/kbackgammon/Makefile.am create mode 100644 doc/kbackgammon/board.png create mode 100644 doc/kbackgammon/index.docbook create mode 100644 doc/kbattleship/Makefile.am create mode 100644 doc/kbattleship/index.docbook create mode 100644 doc/kblackbox/Makefile.am create mode 100644 doc/kblackbox/index.docbook create mode 100644 doc/kblackbox/kblackboxtbar.png create mode 100644 doc/kbounce/Makefile.am create mode 100644 doc/kbounce/index.docbook create mode 100644 doc/kbounce/jezball_corridor1.png create mode 100644 doc/kbounce/jezball_corridor2.png create mode 100644 doc/kbounce/jezball_newWall.png create mode 100644 doc/kenolaba/Makefile.am create mode 100644 doc/kenolaba/index.docbook create mode 100644 doc/kenolaba/kenolaba-pieces.png create mode 100644 doc/kenolaba/man-kenolaba.6.docbook create mode 100644 doc/kfouleggs/Makefile.am create mode 100644 doc/kfouleggs/gamescreen.png create mode 100644 doc/kfouleggs/index.docbook create mode 100644 doc/kgoldrunner/Makefile.am create mode 100644 doc/kgoldrunner/editbar.png create mode 100644 doc/kgoldrunner/enemy.png create mode 100644 doc/kgoldrunner/hero.png create mode 100644 doc/kgoldrunner/index.docbook create mode 100644 doc/kgoldrunner/kgoldrunner.png create mode 100644 doc/kgoldrunner/level010.png create mode 100644 doc/kgoldrunner/select.png create mode 100644 doc/kgoldrunner/tute008.png create mode 100644 doc/kjumpingcube/Makefile.am create mode 100644 doc/kjumpingcube/index.docbook create mode 100644 doc/klickety/Makefile.am create mode 100644 doc/klickety/index.docbook create mode 100644 doc/klickety/screenshot.png create mode 100644 doc/klines/Makefile.am create mode 100644 doc/klines/index.docbook create mode 100644 doc/kmahjongg/Makefile.am create mode 100644 doc/kmahjongg/background.png create mode 100644 doc/kmahjongg/boardeditor.png create mode 100644 doc/kmahjongg/config.png create mode 100644 doc/kmahjongg/gamescreen.png create mode 100644 doc/kmahjongg/highscore.png create mode 100644 doc/kmahjongg/index.docbook create mode 100644 doc/kmahjongg/layout.png create mode 100644 doc/kmahjongg/numbered.png create mode 100644 doc/kmahjongg/shortcuts.png create mode 100644 doc/kmahjongg/showremoved.png create mode 100644 doc/kmahjongg/theme.png create mode 100644 doc/kmahjongg/tileset.png create mode 100644 doc/kmines/Makefile.am create mode 100644 doc/kmines/index.docbook create mode 100644 doc/kmines/kmines1.png create mode 100644 doc/kmines/kmines2.png create mode 100644 doc/kolf/Makefile.am create mode 100644 doc/kolf/index.docbook create mode 100644 doc/konquest/Makefile.am create mode 100644 doc/konquest/index.docbook create mode 100644 doc/kpat/Makefile.am create mode 100644 doc/kpat/clubs.png create mode 100644 doc/kpat/diamonds.png create mode 100644 doc/kpat/hearts.png create mode 100644 doc/kpat/index.docbook create mode 100644 doc/kpat/man-kpat.6.docbook create mode 100644 doc/kpat/playfield.png create mode 100644 doc/kpat/spades.png create mode 100644 doc/kpoker/Makefile.am create mode 100644 doc/kpoker/index.docbook create mode 100644 doc/kpoker/kpoker1.png create mode 100644 doc/kpoker/kpoker2.png create mode 100644 doc/kreversi/Makefile.am create mode 100644 doc/kreversi/index.docbook create mode 100644 doc/kreversi/kreversi-configuration.png create mode 100644 doc/kreversi/kreversi1.png create mode 100644 doc/ksame/Makefile.am create mode 100644 doc/ksame/index.docbook create mode 100644 doc/kshisen/Makefile.am create mode 100644 doc/kshisen/index.docbook create mode 100644 doc/kshisen/kshisen-configuration.png create mode 100644 doc/kshisen/score-formula.png create mode 100644 doc/kshisen/score-formula.tex create mode 100644 doc/ksirtet/Makefile.am create mode 100644 doc/ksirtet/index.docbook create mode 100644 doc/ksmiletris/Makefile.am create mode 100644 doc/ksmiletris/gamescreen.png create mode 100644 doc/ksmiletris/icon.png create mode 100644 doc/ksmiletris/index.docbook create mode 100644 doc/ksmiletris/smiley.png create mode 100644 doc/ksmiletris/symbol.png create mode 100644 doc/ksnake/Makefile.am create mode 100644 doc/ksnake/index.docbook create mode 100644 doc/ksokoban/Makefile.am create mode 100644 doc/ksokoban/index.docbook create mode 100644 doc/kspaceduel/Makefile.am create mode 100644 doc/kspaceduel/index.docbook create mode 100644 doc/kspaceduel/kspaceduel3.png create mode 100644 doc/ktron/Makefile.am create mode 100644 doc/ktron/index.docbook create mode 100644 doc/ktuberling/Makefile.am create mode 100644 doc/ktuberling/button-new.png create mode 100644 doc/ktuberling/button-open.png create mode 100644 doc/ktuberling/button-print.png create mode 100644 doc/ktuberling/button-redo.png create mode 100644 doc/ktuberling/button-save.png create mode 100644 doc/ktuberling/button-undo.png create mode 100644 doc/ktuberling/gameboard.png create mode 100644 doc/ktuberling/index.docbook create mode 100644 doc/ktuberling/ktuberling.png create mode 100644 doc/ktuberling/menu-edit.png create mode 100644 doc/ktuberling/menu-game.png create mode 100644 doc/ktuberling/menu-help.png create mode 100644 doc/ktuberling/menu-playground.png create mode 100644 doc/ktuberling/menu-raw.png create mode 100644 doc/ktuberling/menu-settings.png create mode 100644 doc/ktuberling/menu-speech.png create mode 100644 doc/ktuberling/technical-reference.docbook create mode 100644 doc/ktuberling/toolbar.png create mode 100644 doc/kwin4/Makefile.am create mode 100644 doc/kwin4/index.docbook create mode 100644 doc/lskat/Makefile.am create mode 100644 doc/lskat/index.docbook create mode 100644 kasteroids/ChangeLog create mode 100644 kasteroids/Makefile.am create mode 100644 kasteroids/bg.png create mode 100644 kasteroids/configure.in.in create mode 100644 kasteroids/hi128-app-kasteroids.png create mode 100644 kasteroids/hi16-app-kasteroids.png create mode 100644 kasteroids/hi22-app-kasteroids.png create mode 100644 kasteroids/hi32-app-kasteroids.png create mode 100644 kasteroids/hi48-app-kasteroids.png create mode 100644 kasteroids/hi64-app-kasteroids.png create mode 100644 kasteroids/kasteroids.desktop create mode 100644 kasteroids/kasteroids.kcfg create mode 100644 kasteroids/kasteroidsui.rc create mode 100644 kasteroids/ledmeter.cpp create mode 100644 kasteroids/ledmeter.h create mode 100644 kasteroids/main.cpp create mode 100644 kasteroids/settings.kcfgc create mode 100644 kasteroids/sounds/Explosion.wav create mode 100644 kasteroids/sounds/Makefile.am create mode 100644 kasteroids/sprites.h create mode 100644 kasteroids/sprites/.pbm create mode 100644 kasteroids/sprites/Makefile.am create mode 100644 kasteroids/sprites/bits/Makefile.am create mode 100644 kasteroids/sprites/bits/bits.ini create mode 100644 kasteroids/sprites/bits/bits.pov create mode 100644 kasteroids/sprites/bits/bits0000.png create mode 100644 kasteroids/sprites/bits/bits0001.png create mode 100644 kasteroids/sprites/bits/bits0002.png create mode 100644 kasteroids/sprites/bits/bits0003.png create mode 100644 kasteroids/sprites/bits/bits0004.png create mode 100644 kasteroids/sprites/bits/bits0005.png create mode 100644 kasteroids/sprites/bits/bits0006.png create mode 100644 kasteroids/sprites/bits/bits0007.png create mode 100644 kasteroids/sprites/bits/bits0008.png create mode 100644 kasteroids/sprites/bits/bits0009.png create mode 100644 kasteroids/sprites/bits/bits0010.png create mode 100644 kasteroids/sprites/bits/bits0011.png create mode 100644 kasteroids/sprites/bits/bits0012.png create mode 100644 kasteroids/sprites/bits/bits0013.png create mode 100644 kasteroids/sprites/bits/bits0014.png create mode 100644 kasteroids/sprites/bits/bits0015.png create mode 100644 kasteroids/sprites/exhaust/Makefile.am create mode 100644 kasteroids/sprites/exhaust/exhaust.png create mode 100644 kasteroids/sprites/missile/Makefile.am create mode 100644 kasteroids/sprites/missile/missile.png create mode 100644 kasteroids/sprites/powerups/Makefile.am create mode 100644 kasteroids/sprites/powerups/brake.png create mode 100644 kasteroids/sprites/powerups/energy.png create mode 100644 kasteroids/sprites/powerups/shield.png create mode 100644 kasteroids/sprites/powerups/shoot.png create mode 100644 kasteroids/sprites/powerups/teleport.png create mode 100644 kasteroids/sprites/rock1/Makefile.am create mode 100644 kasteroids/sprites/rock1/rock1.ini create mode 100644 kasteroids/sprites/rock1/rock1.pov create mode 100644 kasteroids/sprites/rock1/rock10000.png create mode 100644 kasteroids/sprites/rock1/rock10001.png create mode 100644 kasteroids/sprites/rock1/rock10002.png create mode 100644 kasteroids/sprites/rock1/rock10003.png create mode 100644 kasteroids/sprites/rock1/rock10004.png create mode 100644 kasteroids/sprites/rock1/rock10005.png create mode 100644 kasteroids/sprites/rock1/rock10006.png create mode 100644 kasteroids/sprites/rock1/rock10007.png create mode 100644 kasteroids/sprites/rock1/rock10008.png create mode 100644 kasteroids/sprites/rock1/rock10009.png create mode 100644 kasteroids/sprites/rock1/rock10010.png create mode 100644 kasteroids/sprites/rock1/rock10011.png create mode 100644 kasteroids/sprites/rock1/rock10012.png create mode 100644 kasteroids/sprites/rock1/rock10013.png create mode 100644 kasteroids/sprites/rock1/rock10014.png create mode 100644 kasteroids/sprites/rock1/rock10015.png create mode 100644 kasteroids/sprites/rock1/rock10016.png create mode 100644 kasteroids/sprites/rock1/rock10017.png create mode 100644 kasteroids/sprites/rock1/rock10018.png create mode 100644 kasteroids/sprites/rock1/rock10019.png create mode 100644 kasteroids/sprites/rock1/rock10020.png create mode 100644 kasteroids/sprites/rock1/rock10021.png create mode 100644 kasteroids/sprites/rock1/rock10022.png create mode 100644 kasteroids/sprites/rock1/rock10023.png create mode 100644 kasteroids/sprites/rock1/rock10024.png create mode 100644 kasteroids/sprites/rock1/rock10025.png create mode 100644 kasteroids/sprites/rock1/rock10026.png create mode 100644 kasteroids/sprites/rock1/rock10027.png create mode 100644 kasteroids/sprites/rock1/rock10028.png create mode 100644 kasteroids/sprites/rock1/rock10029.png create mode 100644 kasteroids/sprites/rock1/rock10030.png create mode 100644 kasteroids/sprites/rock1/rock10031.png create mode 100644 kasteroids/sprites/rock2/Makefile.am create mode 100644 kasteroids/sprites/rock2/rock2.ini create mode 100644 kasteroids/sprites/rock2/rock2.pov create mode 100644 kasteroids/sprites/rock2/rock20000.png create mode 100644 kasteroids/sprites/rock2/rock20001.png create mode 100644 kasteroids/sprites/rock2/rock20002.png create mode 100644 kasteroids/sprites/rock2/rock20003.png create mode 100644 kasteroids/sprites/rock2/rock20004.png create mode 100644 kasteroids/sprites/rock2/rock20005.png create mode 100644 kasteroids/sprites/rock2/rock20006.png create mode 100644 kasteroids/sprites/rock2/rock20007.png create mode 100644 kasteroids/sprites/rock2/rock20008.png create mode 100644 kasteroids/sprites/rock2/rock20009.png create mode 100644 kasteroids/sprites/rock2/rock20010.png create mode 100644 kasteroids/sprites/rock2/rock20011.png create mode 100644 kasteroids/sprites/rock2/rock20012.png create mode 100644 kasteroids/sprites/rock2/rock20013.png create mode 100644 kasteroids/sprites/rock2/rock20014.png create mode 100644 kasteroids/sprites/rock2/rock20015.png create mode 100644 kasteroids/sprites/rock2/rock20016.png create mode 100644 kasteroids/sprites/rock2/rock20017.png create mode 100644 kasteroids/sprites/rock2/rock20018.png create mode 100644 kasteroids/sprites/rock2/rock20019.png create mode 100644 kasteroids/sprites/rock2/rock20020.png create mode 100644 kasteroids/sprites/rock2/rock20021.png create mode 100644 kasteroids/sprites/rock2/rock20022.png create mode 100644 kasteroids/sprites/rock2/rock20023.png create mode 100644 kasteroids/sprites/rock2/rock20024.png create mode 100644 kasteroids/sprites/rock2/rock20025.png create mode 100644 kasteroids/sprites/rock2/rock20026.png create mode 100644 kasteroids/sprites/rock2/rock20027.png create mode 100644 kasteroids/sprites/rock2/rock20028.png create mode 100644 kasteroids/sprites/rock2/rock20029.png create mode 100644 kasteroids/sprites/rock2/rock20030.png create mode 100644 kasteroids/sprites/rock2/rock20031.png create mode 100644 kasteroids/sprites/rock3/Makefile.am create mode 100644 kasteroids/sprites/rock3/rock3.ini create mode 100644 kasteroids/sprites/rock3/rock3.pov create mode 100644 kasteroids/sprites/rock3/rock30000.png create mode 100644 kasteroids/sprites/rock3/rock30001.png create mode 100644 kasteroids/sprites/rock3/rock30002.png create mode 100644 kasteroids/sprites/rock3/rock30003.png create mode 100644 kasteroids/sprites/rock3/rock30004.png create mode 100644 kasteroids/sprites/rock3/rock30005.png create mode 100644 kasteroids/sprites/rock3/rock30006.png create mode 100644 kasteroids/sprites/rock3/rock30007.png create mode 100644 kasteroids/sprites/rock3/rock30008.png create mode 100644 kasteroids/sprites/rock3/rock30009.png create mode 100644 kasteroids/sprites/rock3/rock30010.png create mode 100644 kasteroids/sprites/rock3/rock30011.png create mode 100644 kasteroids/sprites/rock3/rock30012.png create mode 100644 kasteroids/sprites/rock3/rock30013.png create mode 100644 kasteroids/sprites/rock3/rock30014.png create mode 100644 kasteroids/sprites/rock3/rock30015.png create mode 100644 kasteroids/sprites/rock3/rock30016.png create mode 100644 kasteroids/sprites/rock3/rock30017.png create mode 100644 kasteroids/sprites/rock3/rock30018.png create mode 100644 kasteroids/sprites/rock3/rock30019.png create mode 100644 kasteroids/sprites/rock3/rock30020.png create mode 100644 kasteroids/sprites/rock3/rock30021.png create mode 100644 kasteroids/sprites/rock3/rock30022.png create mode 100644 kasteroids/sprites/rock3/rock30023.png create mode 100644 kasteroids/sprites/rock3/rock30024.png create mode 100644 kasteroids/sprites/rock3/rock30025.png create mode 100644 kasteroids/sprites/rock3/rock30026.png create mode 100644 kasteroids/sprites/rock3/rock30027.png create mode 100644 kasteroids/sprites/rock3/rock30028.png create mode 100644 kasteroids/sprites/rock3/rock30029.png create mode 100644 kasteroids/sprites/rock3/rock30030.png create mode 100644 kasteroids/sprites/rock3/rock30031.png create mode 100644 kasteroids/sprites/shield/Makefile.am create mode 100644 kasteroids/sprites/shield/shield0000.png create mode 100644 kasteroids/sprites/shield/shield0001.png create mode 100644 kasteroids/sprites/shield/shield0002.png create mode 100644 kasteroids/sprites/shield/shield0003.png create mode 100644 kasteroids/sprites/shield/shield0004.png create mode 100644 kasteroids/sprites/shield/shield0005.png create mode 100644 kasteroids/sprites/shield/shield0006.png create mode 100644 kasteroids/sprites/ship/Makefile.am create mode 100644 kasteroids/sprites/ship/ship.ini create mode 100644 kasteroids/sprites/ship/ship.pov create mode 100644 kasteroids/sprites/ship/ship0000.png create mode 100644 kasteroids/sprites/ship/ship0001.png create mode 100644 kasteroids/sprites/ship/ship0002.png create mode 100644 kasteroids/sprites/ship/ship0003.png create mode 100644 kasteroids/sprites/ship/ship0004.png create mode 100644 kasteroids/sprites/ship/ship0005.png create mode 100644 kasteroids/sprites/ship/ship0006.png create mode 100644 kasteroids/sprites/ship/ship0007.png create mode 100644 kasteroids/sprites/ship/ship0008.png create mode 100644 kasteroids/sprites/ship/ship0009.png create mode 100644 kasteroids/sprites/ship/ship0010.png create mode 100644 kasteroids/sprites/ship/ship0011.png create mode 100644 kasteroids/sprites/ship/ship0012.png create mode 100644 kasteroids/sprites/ship/ship0013.png create mode 100644 kasteroids/sprites/ship/ship0014.png create mode 100644 kasteroids/sprites/ship/ship0015.png create mode 100644 kasteroids/sprites/ship/ship0016.png create mode 100644 kasteroids/sprites/ship/ship0017.png create mode 100644 kasteroids/sprites/ship/ship0018.png create mode 100644 kasteroids/sprites/ship/ship0019.png create mode 100644 kasteroids/sprites/ship/ship0020.png create mode 100644 kasteroids/sprites/ship/ship0021.png create mode 100644 kasteroids/sprites/ship/ship0022.png create mode 100644 kasteroids/sprites/ship/ship0023.png create mode 100644 kasteroids/sprites/ship/ship0024.png create mode 100644 kasteroids/sprites/ship/ship0025.png create mode 100644 kasteroids/sprites/ship/ship0026.png create mode 100644 kasteroids/sprites/ship/ship0027.png create mode 100644 kasteroids/sprites/ship/ship0028.png create mode 100644 kasteroids/sprites/ship/ship0029.png create mode 100644 kasteroids/sprites/ship/ship0030.png create mode 100644 kasteroids/sprites/ship/ship0031.png create mode 100644 kasteroids/sprites/ship/ship0032.png create mode 100644 kasteroids/sprites/ship/ship0033.png create mode 100644 kasteroids/sprites/ship/ship0034.png create mode 100644 kasteroids/sprites/ship/ship0035.png create mode 100644 kasteroids/sprites/ship/ship0036.png create mode 100644 kasteroids/sprites/ship/ship0037.png create mode 100644 kasteroids/sprites/ship/ship0038.png create mode 100644 kasteroids/sprites/ship/ship0039.png create mode 100644 kasteroids/sprites/ship/ship0040.png create mode 100644 kasteroids/sprites/ship/ship0041.png create mode 100644 kasteroids/sprites/ship/ship0042.png create mode 100644 kasteroids/sprites/ship/ship0043.png create mode 100644 kasteroids/sprites/ship/ship0044.png create mode 100644 kasteroids/sprites/ship/ship0045.png create mode 100644 kasteroids/sprites/ship/ship0046.png create mode 100644 kasteroids/sprites/ship/ship0047.png create mode 100644 kasteroids/sprites/ship/ship0048.png create mode 100644 kasteroids/sprites/ship/ship0049.png create mode 100644 kasteroids/sprites/ship/ship0050.png create mode 100644 kasteroids/sprites/ship/ship0051.png create mode 100644 kasteroids/sprites/ship/ship0052.png create mode 100644 kasteroids/sprites/ship/ship0053.png create mode 100644 kasteroids/sprites/ship/ship0054.png create mode 100644 kasteroids/sprites/ship/ship0055.png create mode 100644 kasteroids/sprites/ship/ship0056.png create mode 100644 kasteroids/sprites/ship/ship0057.png create mode 100644 kasteroids/sprites/ship/ship0058.png create mode 100644 kasteroids/sprites/ship/ship0059.png create mode 100644 kasteroids/sprites/ship/ship0060.png create mode 100644 kasteroids/sprites/ship/ship0061.png create mode 100644 kasteroids/sprites/ship/ship0062.png create mode 100644 kasteroids/sprites/ship/ship0063.png create mode 100644 kasteroids/toplevel.cpp create mode 100644 kasteroids/toplevel.h create mode 100644 kasteroids/version.h create mode 100644 kasteroids/view.cpp create mode 100644 kasteroids/view.h create mode 100644 katomic/Makefile.am create mode 100644 katomic/abilder.png create mode 100644 katomic/abilder.svgz create mode 100644 katomic/atom.h create mode 100644 katomic/configbox.cpp create mode 100644 katomic/configbox.h create mode 100644 katomic/feld.cpp create mode 100644 katomic/feld.h create mode 100644 katomic/gamewidget.cpp create mode 100644 katomic/gamewidget.h create mode 100644 katomic/hi128-app-katomic.png create mode 100644 katomic/hi16-app-katomic.png create mode 100644 katomic/hi22-app-katomic.png create mode 100644 katomic/hi32-app-katomic.png create mode 100644 katomic/hi48-app-katomic.png create mode 100644 katomic/hi64-app-katomic.png create mode 100644 katomic/katomic.desktop create mode 100644 katomic/katomicui.rc create mode 100644 katomic/levels/level_1 create mode 100644 katomic/levels/level_10 create mode 100644 katomic/levels/level_11 create mode 100644 katomic/levels/level_12 create mode 100644 katomic/levels/level_13 create mode 100644 katomic/levels/level_14 create mode 100644 katomic/levels/level_15 create mode 100644 katomic/levels/level_16 create mode 100644 katomic/levels/level_17 create mode 100644 katomic/levels/level_18 create mode 100644 katomic/levels/level_19 create mode 100644 katomic/levels/level_2 create mode 100644 katomic/levels/level_20 create mode 100644 katomic/levels/level_21 create mode 100644 katomic/levels/level_22 create mode 100644 katomic/levels/level_23 create mode 100644 katomic/levels/level_24 create mode 100644 katomic/levels/level_25 create mode 100644 katomic/levels/level_26 create mode 100644 katomic/levels/level_27 create mode 100644 katomic/levels/level_28 create mode 100644 katomic/levels/level_29 create mode 100644 katomic/levels/level_3 create mode 100644 katomic/levels/level_30 create mode 100644 katomic/levels/level_31 create mode 100644 katomic/levels/level_32 create mode 100644 katomic/levels/level_33 create mode 100644 katomic/levels/level_34 create mode 100644 katomic/levels/level_35 create mode 100644 katomic/levels/level_36 create mode 100644 katomic/levels/level_37 create mode 100644 katomic/levels/level_38 create mode 100644 katomic/levels/level_39 create mode 100644 katomic/levels/level_4 create mode 100644 katomic/levels/level_40 create mode 100644 katomic/levels/level_41 create mode 100644 katomic/levels/level_42 create mode 100644 katomic/levels/level_43 create mode 100644 katomic/levels/level_44 create mode 100644 katomic/levels/level_45 create mode 100644 katomic/levels/level_46 create mode 100644 katomic/levels/level_47 create mode 100644 katomic/levels/level_48 create mode 100644 katomic/levels/level_49 create mode 100644 katomic/levels/level_5 create mode 100644 katomic/levels/level_50 create mode 100644 katomic/levels/level_51 create mode 100644 katomic/levels/level_52 create mode 100644 katomic/levels/level_53 create mode 100644 katomic/levels/level_54 create mode 100644 katomic/levels/level_55 create mode 100644 katomic/levels/level_56 create mode 100644 katomic/levels/level_57 create mode 100644 katomic/levels/level_58 create mode 100644 katomic/levels/level_59 create mode 100644 katomic/levels/level_6 create mode 100644 katomic/levels/level_60 create mode 100644 katomic/levels/level_61 create mode 100644 katomic/levels/level_62 create mode 100644 katomic/levels/level_63 create mode 100644 katomic/levels/level_64 create mode 100644 katomic/levels/level_65 create mode 100644 katomic/levels/level_66 create mode 100644 katomic/levels/level_67 create mode 100644 katomic/levels/level_68 create mode 100644 katomic/levels/level_69 create mode 100644 katomic/levels/level_7 create mode 100644 katomic/levels/level_70 create mode 100644 katomic/levels/level_71 create mode 100644 katomic/levels/level_72 create mode 100644 katomic/levels/level_73 create mode 100644 katomic/levels/level_74 create mode 100644 katomic/levels/level_75 create mode 100644 katomic/levels/level_76 create mode 100644 katomic/levels/level_77 create mode 100644 katomic/levels/level_78 create mode 100644 katomic/levels/level_79 create mode 100644 katomic/levels/level_8 create mode 100644 katomic/levels/level_80 create mode 100644 katomic/levels/level_81 create mode 100644 katomic/levels/level_82 create mode 100644 katomic/levels/level_83 create mode 100644 katomic/levels/level_9 create mode 100644 katomic/main.cpp create mode 100644 katomic/molek.cpp create mode 100644 katomic/molek.h create mode 100644 katomic/molek.png create mode 100644 katomic/settings.h create mode 100644 katomic/toplevel.cpp create mode 100644 katomic/toplevel.h create mode 100644 kbackgammon/AUTHORS create mode 100644 kbackgammon/Makefile.am create mode 100644 kbackgammon/README create mode 100644 kbackgammon/TODO create mode 100644 kbackgammon/engines/Makefile.am create mode 100644 kbackgammon/engines/fibs/Makefile.am create mode 100644 kbackgammon/engines/fibs/clip.h create mode 100644 kbackgammon/engines/fibs/kbgfibs.cpp create mode 100644 kbackgammon/engines/fibs/kbgfibs.h create mode 100644 kbackgammon/engines/fibs/kbgfibschat.cpp create mode 100644 kbackgammon/engines/fibs/kbgfibschat.h create mode 100644 kbackgammon/engines/fibs/kbginvite.cpp create mode 100644 kbackgammon/engines/fibs/kbginvite.h create mode 100644 kbackgammon/engines/fibs/kplayerlist.cpp create mode 100644 kbackgammon/engines/fibs/kplayerlist.h create mode 100644 kbackgammon/engines/generic/Makefile.am create mode 100644 kbackgammon/engines/generic/kbgengine.cpp create mode 100644 kbackgammon/engines/generic/kbgengine.h create mode 100644 kbackgammon/engines/gnubg/Makefile.am create mode 100644 kbackgammon/engines/gnubg/kbggnubg.cpp create mode 100644 kbackgammon/engines/gnubg/kbggnubg.h create mode 100644 kbackgammon/engines/nextgen/Makefile.am create mode 100644 kbackgammon/engines/nextgen/kbggame.cpp create mode 100644 kbackgammon/engines/nextgen/kbggame.h create mode 100644 kbackgammon/engines/nextgen/kbgng.cpp create mode 100644 kbackgammon/engines/nextgen/kbgng.h create mode 100644 kbackgammon/engines/nextgen/kbgplayer.cpp create mode 100644 kbackgammon/engines/nextgen/kbgplayer.h create mode 100644 kbackgammon/engines/offline/Makefile.am create mode 100644 kbackgammon/engines/offline/kbgoffline.cpp create mode 100644 kbackgammon/engines/offline/kbgoffline.h create mode 100644 kbackgammon/eventsrc create mode 100644 kbackgammon/icons/Makefile.am create mode 100644 kbackgammon/icons/hi128-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi16-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi16-app-kbackgammon_engine.png create mode 100644 kbackgammon/icons/hi22-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi32-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi32-app-kbackgammon_engine.png create mode 100644 kbackgammon/icons/hi48-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi48-app-kbackgammon_engine.png create mode 100644 kbackgammon/icons/hi64-app-kbackgammon.png create mode 100644 kbackgammon/icons/hi64-app-kbackgammon_engine.png create mode 100644 kbackgammon/kbackgammon.desktop create mode 100644 kbackgammon/kbackgammonui.rc create mode 100644 kbackgammon/kbg.cpp create mode 100644 kbackgammon/kbg.h create mode 100644 kbackgammon/kbgboard.cpp create mode 100644 kbackgammon/kbgboard.h create mode 100644 kbackgammon/kbgstatus.cpp create mode 100644 kbackgammon/kbgstatus.h create mode 100644 kbackgammon/kbgtextview.cpp create mode 100644 kbackgammon/kbgtextview.h create mode 100644 kbackgammon/main.cpp create mode 100644 kbackgammon/pics/Makefile.am create mode 100644 kbackgammon/pics/kbackgammon-chat.png create mode 100644 kbackgammon/pics/kbackgammon-double.xpm create mode 100644 kbackgammon/sounds/Makefile.am create mode 100644 kbackgammon/sounds/kbackgammon-lost.wav create mode 100644 kbackgammon/sounds/kbackgammon-move.wav create mode 100644 kbackgammon/sounds/kbackgammon-roll.wav create mode 100644 kbackgammon/sounds/kbackgammon-won.wav create mode 100644 kbackgammon/version.h create mode 100644 kbattleship/AUTHORS create mode 100644 kbattleship/CLIENTS create mode 100644 kbattleship/COPYING create mode 100644 kbattleship/ChangeLog create mode 100644 kbattleship/INSTALL create mode 100644 kbattleship/Makefile.am create mode 100644 kbattleship/NEWS create mode 100644 kbattleship/README create mode 100644 kbattleship/TODO create mode 100644 kbattleship/VERSION create mode 100644 kbattleship/configure.in.in create mode 100644 kbattleship/kbattleship/Makefile.am create mode 100644 kbattleship/kbattleship/_kbattleship._tcp create mode 100644 kbattleship/kbattleship/dialogs/Makefile.am create mode 100644 kbattleship/kbattleship/dialogs/chatDlg.ui create mode 100644 kbattleship/kbattleship/dialogs/connectDlg.ui create mode 100644 kbattleship/kbattleship/dialogs/infoDlg.ui create mode 100644 kbattleship/kbattleship/dialogs/serverDlg.ui create mode 100644 kbattleship/kbattleship/dialogs/statDlg.ui create mode 100644 kbattleship/kbattleship/eventsrc create mode 100644 kbattleship/kbattleship/kbaiplayer.cpp create mode 100644 kbattleship/kbattleship/kbaiplayer.h create mode 100644 kbattleship/kbattleship/kbattlefield.cpp create mode 100644 kbattleship/kbattleship/kbattlefield.h create mode 100644 kbattleship/kbattleship/kbattleship.cpp create mode 100644 kbattleship/kbattleship/kbattleship.desktop create mode 100644 kbattleship/kbattleship/kbattleship.h create mode 100644 kbattleship/kbattleship/kbattleshipclient.cpp create mode 100644 kbattleship/kbattleship/kbattleshipclient.h create mode 100644 kbattleship/kbattleship/kbattleshipserver.cpp create mode 100644 kbattleship/kbattleship/kbattleshipserver.h create mode 100644 kbattleship/kbattleship/kbattleshipui.rc create mode 100644 kbattleship/kbattleship/kbattleshipview.cpp create mode 100644 kbattleship/kbattleship/kbattleshipview.h create mode 100644 kbattleship/kbattleship/kbchooserstrategy.cpp create mode 100644 kbattleship/kbattleship/kbchooserstrategy.h create mode 100644 kbattleship/kbattleship/kbdestroyshipstrategy.cpp create mode 100644 kbattleship/kbattleship/kbdestroyshipstrategy.h create mode 100644 kbattleship/kbattleship/kbdiagonalshotstrategy.cpp create mode 100644 kbattleship/kbattleship/kbdiagonalshotstrategy.h create mode 100644 kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp create mode 100644 kbattleship/kbattleship/kbdiagonalwrapstrategy.h create mode 100644 kbattleship/kbattleship/kbhorizontalstepstrategy.cpp create mode 100644 kbattleship/kbattleship/kbhorizontalstepstrategy.h create mode 100644 kbattleship/kbattleship/kbrandomshotstrategy.cpp create mode 100644 kbattleship/kbattleship/kbrandomshotstrategy.h create mode 100644 kbattleship/kbattleship/kbstrategy.cpp create mode 100644 kbattleship/kbattleship/kbstrategy.h create mode 100644 kbattleship/kbattleship/kbverticalstepstrategy.cpp create mode 100644 kbattleship/kbattleship/kbverticalstepstrategy.h create mode 100644 kbattleship/kbattleship/kchatwidget.cpp create mode 100644 kbattleship/kbattleship/kchatwidget.h create mode 100644 kbattleship/kbattleship/kclientdialog.cpp create mode 100644 kbattleship/kbattleship/kclientdialog.h create mode 100644 kbattleship/kbattleship/kgridwidget.cpp create mode 100644 kbattleship/kbattleship/kgridwidget.h create mode 100644 kbattleship/kbattleship/kmessage.cpp create mode 100644 kbattleship/kbattleship/kmessage.h create mode 100644 kbattleship/kbattleship/konnectionhandling.cpp create mode 100644 kbattleship/kbattleship/konnectionhandling.h create mode 100644 kbattleship/kbattleship/kserverdialog.cpp create mode 100644 kbattleship/kbattleship/kserverdialog.h create mode 100644 kbattleship/kbattleship/kship.cpp create mode 100644 kbattleship/kbattleship/kship.h create mode 100644 kbattleship/kbattleship/kshiplist.cpp create mode 100644 kbattleship/kbattleship/kshiplist.h create mode 100644 kbattleship/kbattleship/kstatdialog.cpp create mode 100644 kbattleship/kbattleship/kstatdialog.h create mode 100644 kbattleship/kbattleship/main.cpp create mode 100644 kbattleship/kbattleship/pictures/Makefile.am create mode 100644 kbattleship/kbattleship/pictures/border.png create mode 100644 kbattleship/kbattleship/pictures/death.png create mode 100644 kbattleship/kbattleship/pictures/hi128-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hi16-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hi22-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hi32-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hi48-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hi64-app-kbattleship.png create mode 100644 kbattleship/kbattleship/pictures/hit.png create mode 100644 kbattleship/kbattleship/pictures/sea.png create mode 100644 kbattleship/kbattleship/pictures/ship1-1-r.png create mode 100644 kbattleship/kbattleship/pictures/ship1-1.png create mode 100644 kbattleship/kbattleship/pictures/ship1-view.png create mode 100644 kbattleship/kbattleship/pictures/ship2-1-r.png create mode 100644 kbattleship/kbattleship/pictures/ship2-1.png create mode 100644 kbattleship/kbattleship/pictures/ship2-2-r.png create mode 100644 kbattleship/kbattleship/pictures/ship2-2.png create mode 100644 kbattleship/kbattleship/pictures/ship2-view.png create mode 100644 kbattleship/kbattleship/pictures/ship3-1-r.png create mode 100644 kbattleship/kbattleship/pictures/ship3-1.png create mode 100644 kbattleship/kbattleship/pictures/ship3-2-r.png create mode 100644 kbattleship/kbattleship/pictures/ship3-2.png create mode 100644 kbattleship/kbattleship/pictures/ship3-3-r.png create mode 100644 kbattleship/kbattleship/pictures/ship3-3.png create mode 100644 kbattleship/kbattleship/pictures/ship3-view.png create mode 100644 kbattleship/kbattleship/pictures/ship4-1-r.png create mode 100644 kbattleship/kbattleship/pictures/ship4-1.png create mode 100644 kbattleship/kbattleship/pictures/ship4-2-r.png create mode 100644 kbattleship/kbattleship/pictures/ship4-2.png create mode 100644 kbattleship/kbattleship/pictures/ship4-3-r.png create mode 100644 kbattleship/kbattleship/pictures/ship4-3.png create mode 100644 kbattleship/kbattleship/pictures/ship4-4-r.png create mode 100644 kbattleship/kbattleship/pictures/ship4-4.png create mode 100644 kbattleship/kbattleship/pictures/ship4-view.png create mode 100644 kbattleship/kbattleship/pictures/water.png create mode 100644 kbattleship/kbattleship/sounds/Makefile.am create mode 100644 kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg create mode 100644 kbattleship/kbattleship/sounds/ship-player1-shoot.ogg create mode 100644 kbattleship/kbattleship/sounds/ship-player2-shoot.ogg create mode 100644 kbattleship/kbattleship/sounds/ship-sink.ogg create mode 100644 kblackbox/CHANGES create mode 100644 kblackbox/Makefile.am create mode 100644 kblackbox/README create mode 100644 kblackbox/VERSION create mode 100644 kblackbox/big_kblackbox.xpm create mode 100644 kblackbox/hi128-app-kblackbox.png create mode 100644 kblackbox/hi16-app-kblackbox.png create mode 100644 kblackbox/hi22-app-kblackbox.png create mode 100644 kblackbox/hi32-app-kblackbox.png create mode 100644 kblackbox/hi48-app-kblackbox.png create mode 100644 kblackbox/hi64-app-kblackbox.png create mode 100644 kblackbox/kbbgame.cpp create mode 100644 kblackbox/kbbgame.h create mode 100644 kblackbox/kbbgfx.cpp create mode 100644 kblackbox/kbbgfx.h create mode 100644 kblackbox/kblackbox.desktop create mode 100644 kblackbox/kblackboxui.rc create mode 100644 kblackbox/main.cpp create mode 100644 kblackbox/pics/Makefile.am create mode 100644 kblackbox/pics/blue.xpm create mode 100644 kblackbox/pics/brown.xpm create mode 100644 kblackbox/pics/cyan.xpm create mode 100644 kblackbox/pics/done.xpm create mode 100644 kblackbox/pics/giveup.xpm create mode 100644 kblackbox/pics/gray.xpm create mode 100644 kblackbox/pics/green.xpm create mode 100644 kblackbox/pics/green2.xpm create mode 100644 kblackbox/pics/red.xpm create mode 100644 kblackbox/pics/white.xpm create mode 100644 kblackbox/util.cpp create mode 100644 kblackbox/util.h create mode 100644 kblackbox/version.h create mode 100644 kbounce/Makefile.am create mode 100644 kbounce/RULES create mode 100644 kbounce/configure.in.in create mode 100644 kbounce/game.cpp create mode 100644 kbounce/game.h create mode 100644 kbounce/highscores.cpp create mode 100644 kbounce/highscores.h create mode 100644 kbounce/kbounce.cpp create mode 100644 kbounce/kbounce.desktop create mode 100644 kbounce/kbounce.h create mode 100644 kbounce/kbounceui.rc create mode 100644 kbounce/main.cpp create mode 100644 kbounce/pics/Makefile.am create mode 100644 kbounce/pics/ball0000.png create mode 100644 kbounce/pics/ball0001.png create mode 100644 kbounce/pics/ball0002.png create mode 100644 kbounce/pics/ball0003.png create mode 100644 kbounce/pics/ball0004.png create mode 100644 kbounce/pics/ball0005.png create mode 100644 kbounce/pics/ball0006.png create mode 100644 kbounce/pics/ball0007.png create mode 100644 kbounce/pics/ball0008.png create mode 100644 kbounce/pics/ball0009.png create mode 100644 kbounce/pics/ball0010.png create mode 100644 kbounce/pics/ball0011.png create mode 100644 kbounce/pics/ball0012.png create mode 100644 kbounce/pics/ball0013.png create mode 100644 kbounce/pics/ball0014.png create mode 100644 kbounce/pics/ball0015.png create mode 100644 kbounce/pics/ball0016.png create mode 100644 kbounce/pics/ball0017.png create mode 100644 kbounce/pics/ball0018.png create mode 100644 kbounce/pics/ball0019.png create mode 100644 kbounce/pics/ball0020.png create mode 100644 kbounce/pics/ball0021.png create mode 100644 kbounce/pics/ball0022.png create mode 100644 kbounce/pics/ball0023.png create mode 100644 kbounce/pics/ball0024.png create mode 100644 kbounce/pics/hi128-app-kbounce.png create mode 100644 kbounce/pics/hi16-app-kbounce.png create mode 100644 kbounce/pics/hi22-app-kbounce.png create mode 100644 kbounce/pics/hi32-app-kbounce.png create mode 100644 kbounce/pics/hi48-app-kbounce.png create mode 100644 kbounce/pics/hi64-app-kbounce.png create mode 100644 kbounce/pics/tiles.png create mode 100644 kbounce/sounds/Makefile.am create mode 100644 kbounce/sounds/death.au create mode 100644 kbounce/sounds/reflect.au create mode 100644 kbounce/sounds/seconds.au create mode 100644 kbounce/sounds/timeout.au create mode 100644 kbounce/sounds/wallend.au create mode 100644 kbounce/sounds/wallstart.au create mode 100644 kdegames.lsm create mode 100644 kenolaba/AbTop.cpp create mode 100644 kenolaba/AbTop.h create mode 100644 kenolaba/Ball.cpp create mode 100644 kenolaba/Ball.h create mode 100644 kenolaba/Board.cpp create mode 100644 kenolaba/Board.h create mode 100644 kenolaba/BoardWidget.cpp create mode 100644 kenolaba/BoardWidget.h create mode 100644 kenolaba/ChangeLog create mode 100644 kenolaba/EvalDlg.ui create mode 100644 kenolaba/EvalDlgImpl.cpp create mode 100644 kenolaba/EvalDlgImpl.h create mode 100644 kenolaba/EvalScheme.cpp create mode 100644 kenolaba/EvalScheme.h create mode 100644 kenolaba/Makefile.am create mode 100644 kenolaba/Move.cpp create mode 100644 kenolaba/Move.h create mode 100644 kenolaba/Network.cpp create mode 100644 kenolaba/Network.h create mode 100644 kenolaba/README create mode 100644 kenolaba/Spy.cpp create mode 100644 kenolaba/Spy.h create mode 100644 kenolaba/TODO create mode 100644 kenolaba/bitmaps/Arrow1 create mode 100644 kenolaba/bitmaps/Arrow1Mask create mode 100644 kenolaba/bitmaps/Arrow2 create mode 100644 kenolaba/bitmaps/Arrow2Mask create mode 100644 kenolaba/bitmaps/Arrow3 create mode 100644 kenolaba/bitmaps/Arrow3Mask create mode 100644 kenolaba/bitmaps/Arrow4 create mode 100644 kenolaba/bitmaps/Arrow4Mask create mode 100644 kenolaba/bitmaps/Arrow5 create mode 100644 kenolaba/bitmaps/Arrow5Mask create mode 100644 kenolaba/bitmaps/Arrow6 create mode 100644 kenolaba/bitmaps/Arrow6Mask create mode 100644 kenolaba/bitmaps/Makefile.am create mode 100644 kenolaba/hi128-app-kenolaba.png create mode 100644 kenolaba/hi16-app-kenolaba.png create mode 100644 kenolaba/hi22-app-kenolaba.png create mode 100644 kenolaba/hi32-app-kenolaba.png create mode 100644 kenolaba/hi48-app-kenolaba.png create mode 100644 kenolaba/hi64-app-kenolaba.png create mode 100644 kenolaba/kenolaba.cpp create mode 100644 kenolaba/kenolaba.desktop create mode 100644 kenolaba/kenolabaui.rc create mode 100644 kenolaba/toolbar/Makefile.am create mode 100644 kenolaba/toolbar/edit.xpm create mode 100644 kenolaba/toolbar/help.xpm create mode 100644 kenolaba/toolbar/hint.xpm create mode 100644 kenolaba/toolbar/network.xpm create mode 100644 kenolaba/toolbar/new.xpm create mode 100644 kenolaba/toolbar/noball.xpm create mode 100644 kenolaba/toolbar/ok.xpm create mode 100644 kenolaba/toolbar/redball.xpm create mode 100644 kenolaba/toolbar/spy0.xpm create mode 100644 kenolaba/toolbar/spy1.xpm create mode 100644 kenolaba/toolbar/spy2.xpm create mode 100644 kenolaba/toolbar/spy3.xpm create mode 100644 kenolaba/toolbar/stop.xpm create mode 100644 kenolaba/toolbar/undo.xpm create mode 100644 kenolaba/toolbar/warning.xpm create mode 100644 kenolaba/toolbar/yellowball.xpm create mode 100644 kenolaba/version.h create mode 100644 kfouleggs/CHANGELOG create mode 100644 kfouleggs/LICENSE create mode 100644 kfouleggs/Makefile.am create mode 100644 kfouleggs/README create mode 100644 kfouleggs/TODO create mode 100644 kfouleggs/ai.cpp create mode 100644 kfouleggs/ai.h create mode 100644 kfouleggs/board.cpp create mode 100644 kfouleggs/board.h create mode 100644 kfouleggs/eventsrc create mode 100644 kfouleggs/field.cpp create mode 100644 kfouleggs/field.h create mode 100644 kfouleggs/kfouleggs.desktop create mode 100644 kfouleggs/kfouleggs.kcfg create mode 100644 kfouleggs/kfouleggsui.rc create mode 100644 kfouleggs/main.cpp create mode 100644 kfouleggs/main.h create mode 100644 kfouleggs/pics/Makefile.am create mode 100644 kfouleggs/pics/hi128-app-kfouleggs.png create mode 100644 kfouleggs/pics/hi16-app-kfouleggs.png create mode 100644 kfouleggs/pics/hi22-app-kfouleggs.png create mode 100644 kfouleggs/pics/hi32-app-kfouleggs.png create mode 100644 kfouleggs/pics/hi48-app-kfouleggs.png create mode 100644 kfouleggs/pics/hi64-app-kfouleggs.png create mode 100644 kfouleggs/piece.cpp create mode 100644 kfouleggs/piece.h create mode 100644 kfouleggs/prefs.kcfgc create mode 100644 kgoldrunner/AUTHORS create mode 100644 kgoldrunner/BUGS create mode 100644 kgoldrunner/COPYING create mode 100644 kgoldrunner/ChangeLog create mode 100644 kgoldrunner/Doxyfile create mode 100644 kgoldrunner/INSTALL create mode 100644 kgoldrunner/Makefile.am create mode 100644 kgoldrunner/NEWS create mode 100644 kgoldrunner/README create mode 100644 kgoldrunner/TODO create mode 100644 kgoldrunner/VERSION create mode 100644 kgoldrunner/gamedata/Makefile.am create mode 100644 kgoldrunner/gamedata/games.dat create mode 100644 kgoldrunner/gamedata/hi_level.dat create mode 100644 kgoldrunner/gamedata/hi_plws.dat create mode 100644 kgoldrunner/gamedata/hi_plwv.dat create mode 100644 kgoldrunner/gamedata/hi_wad.dat create mode 100644 kgoldrunner/gamedata/levels.tar create mode 100644 kgoldrunner/kgoldrunner.kdevelop create mode 100644 kgoldrunner/kgoldrunner.lsm create mode 100644 kgoldrunner/src/KGoldrunner.desktop create mode 100644 kgoldrunner/src/Makefile.am create mode 100644 kgoldrunner/src/data_messages.cpp create mode 100644 kgoldrunner/src/enemy1.xpm create mode 100644 kgoldrunner/src/enemy2.xpm create mode 100644 kgoldrunner/src/hero.xpm create mode 100644 kgoldrunner/src/hi128-app-kgoldrunner.png create mode 100644 kgoldrunner/src/hi16-app-kgoldrunner.png create mode 100644 kgoldrunner/src/hi22-app-kgoldrunner.png create mode 100644 kgoldrunner/src/hi32-app-kgoldrunner.png create mode 100644 kgoldrunner/src/hi48-app-kgoldrunner.png create mode 100644 kgoldrunner/src/hi64-app-kgoldrunner.png create mode 100644 kgoldrunner/src/kgoldrunner.cpp create mode 100644 kgoldrunner/src/kgoldrunner.h create mode 100644 kgoldrunner/src/kgoldrunnerui.rc create mode 100644 kgoldrunner/src/kgraphics.h create mode 100644 kgoldrunner/src/kgrcanvas.cpp create mode 100644 kgoldrunner/src/kgrcanvas.h create mode 100644 kgoldrunner/src/kgrconsts.h create mode 100644 kgoldrunner/src/kgrdialog.cpp create mode 100644 kgoldrunner/src/kgrdialog.h create mode 100644 kgoldrunner/src/kgrfigure.cpp create mode 100644 kgoldrunner/src/kgrfigure.h create mode 100644 kgoldrunner/src/kgrgame.cpp create mode 100644 kgoldrunner/src/kgrgame.h create mode 100644 kgoldrunner/src/kgrobject.cpp create mode 100644 kgoldrunner/src/kgrobject.h create mode 100644 kgoldrunner/src/main.cpp create mode 100644 kjumpingcube/AUTHORS create mode 100644 kjumpingcube/ChangeLog create mode 100644 kjumpingcube/Makefile.am create mode 100644 kjumpingcube/README create mode 100644 kjumpingcube/brain.cpp create mode 100644 kjumpingcube/brain.h create mode 100644 kjumpingcube/cube.cpp create mode 100644 kjumpingcube/cube.h create mode 100644 kjumpingcube/cubebox.cpp create mode 100644 kjumpingcube/cubebox.h create mode 100644 kjumpingcube/cubeboxbase.h create mode 100644 kjumpingcube/hi128-app-kjumpingcube.png create mode 100644 kjumpingcube/hi16-app-kjumpingcube.png create mode 100644 kjumpingcube/hi22-app-kjumpingcube.png create mode 100644 kjumpingcube/hi32-app-kjumpingcube.png create mode 100644 kjumpingcube/hi48-app-kjumpingcube.png create mode 100644 kjumpingcube/hi64-app-kjumpingcube.png create mode 100644 kjumpingcube/kcubeboxwidget.cpp create mode 100644 kjumpingcube/kcubeboxwidget.h create mode 100644 kjumpingcube/kcubewidget.cpp create mode 100644 kjumpingcube/kcubewidget.h create mode 100644 kjumpingcube/kjumpingcube.cpp create mode 100644 kjumpingcube/kjumpingcube.desktop create mode 100644 kjumpingcube/kjumpingcube.h create mode 100644 kjumpingcube/kjumpingcube.kcfg create mode 100644 kjumpingcube/kjumpingcubeui.rc create mode 100644 kjumpingcube/main.cpp create mode 100644 kjumpingcube/prefs.kcfgc create mode 100644 kjumpingcube/settings.ui create mode 100644 kjumpingcube/version.h create mode 100644 klickety/CHANGELOG create mode 100644 klickety/LICENSE create mode 100644 klickety/Makefile.am create mode 100644 klickety/README create mode 100644 klickety/board.cpp create mode 100644 klickety/board.h create mode 100644 klickety/eventsrc create mode 100644 klickety/field.cpp create mode 100644 klickety/field.h create mode 100644 klickety/highscores.cpp create mode 100644 klickety/highscores.h create mode 100644 klickety/klickety.desktop create mode 100644 klickety/klicketyui.rc create mode 100644 klickety/main.cpp create mode 100644 klickety/main.h create mode 100644 klickety/pics/Makefile.am create mode 100644 klickety/pics/hi128-app-klickety.png create mode 100644 klickety/pics/hi16-app-klickety.png create mode 100644 klickety/pics/hi22-app-klickety.png create mode 100644 klickety/pics/hi32-app-klickety.png create mode 100644 klickety/pics/hi48-app-klickety.png create mode 100644 klickety/pics/hi64-app-klickety.png create mode 100644 klickety/piece.cpp create mode 100644 klickety/piece.h create mode 100644 klines/AUTHORS create mode 100644 klines/Makefile.am create mode 100644 klines/ballpainter.cpp create mode 100644 klines/ballpainter.h create mode 100644 klines/balls.jpg create mode 100644 klines/cell.cpp create mode 100644 klines/cell.h create mode 100644 klines/cfg.h create mode 100644 klines/field.cpp create mode 100644 klines/field.h create mode 100644 klines/field.jpg create mode 100644 klines/fire.jpg create mode 100644 klines/hi128-app-klines.png create mode 100644 klines/hi16-app-klines.png create mode 100644 klines/hi22-app-klines.png create mode 100644 klines/hi32-app-klines.png create mode 100644 klines/hi48-app-klines.png create mode 100644 klines/hi64-app-klines.png create mode 100644 klines/klines.cpp create mode 100644 klines/klines.desktop create mode 100644 klines/klines.h create mode 100644 klines/klines.kcfg create mode 100644 klines/klinesui.rc create mode 100644 klines/linesboard.cpp create mode 100644 klines/linesboard.h create mode 100644 klines/main.cpp create mode 100644 klines/mwidget.cpp create mode 100644 klines/mwidget.h create mode 100644 klines/povray/ball.pov create mode 100755 klines/povray/balls.sh create mode 100755 klines/povray/clean.sh create mode 100644 klines/povray/field.pov create mode 100755 klines/povray/field.sh create mode 100755 klines/povray/fire.sh create mode 100755 klines/povray/makeballs.sh create mode 100644 klines/povray/povray.ini create mode 100644 klines/prefs.kcfgc create mode 100644 klines/prompt.cpp create mode 100644 klines/prompt.h create mode 100644 klines/templates/cpp_template create mode 100644 klines/templates/header_template create mode 100644 kmahjongg/Background.cpp create mode 100644 kmahjongg/Background.h create mode 100644 kmahjongg/BoardLayout.cpp create mode 100644 kmahjongg/BoardLayout.h create mode 100644 kmahjongg/ChangeLog create mode 100644 kmahjongg/Editor.cpp create mode 100644 kmahjongg/Editor.h create mode 100644 kmahjongg/GameTimer.cpp create mode 100644 kmahjongg/GameTimer.h create mode 100644 kmahjongg/HighScore.cpp create mode 100644 kmahjongg/HighScore.h create mode 100644 kmahjongg/KmTypes.h create mode 100644 kmahjongg/Makefile.am create mode 100644 kmahjongg/Preview.cpp create mode 100644 kmahjongg/Preview.h create mode 100644 kmahjongg/Tileset.cpp create mode 100644 kmahjongg/Tileset.h create mode 100644 kmahjongg/boardwidget.cpp create mode 100644 kmahjongg/boardwidget.h create mode 100644 kmahjongg/hi128-app-kmahjongg.png create mode 100644 kmahjongg/hi16-app-kmahjongg.png create mode 100644 kmahjongg/hi22-app-kmahjongg.png create mode 100644 kmahjongg/hi32-app-kmahjongg.png create mode 100644 kmahjongg/hi48-app-kmahjongg.png create mode 100644 kmahjongg/hi64-app-kmahjongg.png create mode 100644 kmahjongg/kmahjongg.cpp create mode 100644 kmahjongg/kmahjongg.desktop create mode 100644 kmahjongg/kmahjongg.h create mode 100644 kmahjongg/kmahjongg.kcfg create mode 100644 kmahjongg/kmahjonggui.rc create mode 100644 kmahjongg/main.cpp create mode 100644 kmahjongg/pics/Makefile.am create mode 100644 kmahjongg/pics/cross.layout create mode 100644 kmahjongg/pics/default.bgnd create mode 100644 kmahjongg/pics/default.layout create mode 100644 kmahjongg/pics/default.theme create mode 100644 kmahjongg/pics/default.tileset create mode 100644 kmahjongg/pics/haze.bgnd create mode 100644 kmahjongg/pics/kmahjongg.png create mode 100644 kmahjongg/pics/kmahjongg_bgnd.png create mode 100644 kmahjongg/pics/newnum.xpm create mode 100644 kmahjongg/pics/pirates.bgnd create mode 100644 kmahjongg/pics/pirates.layout create mode 100644 kmahjongg/pics/pirates.theme create mode 100644 kmahjongg/pics/pirates.tileset create mode 100644 kmahjongg/pics/pyramid.layout create mode 100644 kmahjongg/pics/runes.tileset create mode 100644 kmahjongg/pics/slate.bgnd create mode 100644 kmahjongg/pics/splash.png create mode 100644 kmahjongg/pics/stax.layout create mode 100644 kmahjongg/pics/test.layout create mode 100644 kmahjongg/pics/test2.layout create mode 100644 kmahjongg/pics/tower.layout create mode 100644 kmahjongg/pics/traditional.tileset create mode 100644 kmahjongg/pics/triangle.layout create mode 100644 kmahjongg/pics/wood.bgnd create mode 100644 kmahjongg/prefs.kcfgc create mode 100644 kmahjongg/settings.ui create mode 100644 kmahjongg/version.h create mode 100644 kmines/CHANGELOG create mode 100644 kmines/LICENSE create mode 100644 kmines/Makefile.am create mode 100644 kmines/README create mode 100644 kmines/TODO create mode 100644 kmines/bitmaps/Makefile.am create mode 100644 kmines/bitmaps/README create mode 100644 kmines/bitmaps/smile create mode 100644 kmines/bitmaps/smile_happy create mode 100644 kmines/bitmaps/smile_ohno create mode 100644 kmines/bitmaps/smile_sleep create mode 100644 kmines/bitmaps/smile_stress create mode 100644 kmines/data/Makefile.am create mode 100644 kmines/data/eventsrc create mode 100644 kmines/data/hi128-app-kmines.png create mode 100644 kmines/data/hi16-app-kmines.png create mode 100644 kmines/data/hi22-app-kmines.png create mode 100644 kmines/data/hi32-app-kmines.png create mode 100644 kmines/data/hi48-app-kmines.png create mode 100644 kmines/data/hi64-app-kmines.png create mode 100644 kmines/data/kmines.desktop create mode 100644 kmines/defines.cpp create mode 100644 kmines/defines.h create mode 100644 kmines/dialogs.cpp create mode 100644 kmines/dialogs.h create mode 100644 kmines/field.cpp create mode 100644 kmines/field.h create mode 100644 kmines/frame.cpp create mode 100644 kmines/frame.h create mode 100644 kmines/highscores.cpp create mode 100644 kmines/highscores.h create mode 100644 kmines/kmines.kcfg create mode 100644 kmines/kminesui.rc create mode 100644 kmines/kzoommainwindow.cpp create mode 100644 kmines/kzoommainwindow.h create mode 100644 kmines/main.cpp create mode 100644 kmines/main.h create mode 100644 kmines/settings.kcfgc create mode 100644 kmines/settings_addons.h create mode 100644 kmines/solver/Makefile.am create mode 100644 kmines/solver/advFastRules.cpp create mode 100644 kmines/solver/adviseFast.cpp create mode 100644 kmines/solver/adviseFast.h create mode 100644 kmines/solver/adviseFull.cpp create mode 100644 kmines/solver/adviseFull.h create mode 100644 kmines/solver/bfield.cpp create mode 100644 kmines/solver/bfield.h create mode 100644 kmines/solver/headerP.h create mode 100644 kmines/solver/solver.cpp create mode 100644 kmines/solver/solver.h create mode 100644 kmines/solver/test.cpp create mode 100644 kmines/solver/testFast.cpp create mode 100644 kmines/solver/testRate.cpp create mode 100644 kmines/solver/testSolve.cpp create mode 100644 kmines/status.cpp create mode 100644 kmines/status.h create mode 100644 kmines/version.h create mode 100644 knetwalk/AUTHORS create mode 100644 knetwalk/Makefile.am create mode 100644 knetwalk/TODO create mode 100644 knetwalk/configure.in.in create mode 100644 knetwalk/src/Makefile.am create mode 100644 knetwalk/src/SConscript create mode 100644 knetwalk/src/cell.cpp create mode 100644 knetwalk/src/cell.h create mode 100644 knetwalk/src/defines.h create mode 100644 knetwalk/src/eventsrc create mode 100644 knetwalk/src/highscores.cpp create mode 100644 knetwalk/src/highscores.h create mode 100644 knetwalk/src/knetwalk.desktop create mode 100644 knetwalk/src/knetwalk.kcfg create mode 100644 knetwalk/src/knetwalkui.rc create mode 100644 knetwalk/src/main.cpp create mode 100644 knetwalk/src/mainwindow.cpp create mode 100644 knetwalk/src/mainwindow.h create mode 100644 knetwalk/src/pics/Makefile.am create mode 100644 knetwalk/src/pics/SConscript create mode 100644 knetwalk/src/pics/background.png create mode 100644 knetwalk/src/pics/background_locked.png create mode 100644 knetwalk/src/pics/cable0001.png create mode 100644 knetwalk/src/pics/cable0010.png create mode 100644 knetwalk/src/pics/cable0011.png create mode 100644 knetwalk/src/pics/cable0100.png create mode 100644 knetwalk/src/pics/cable0101.png create mode 100644 knetwalk/src/pics/cable0110.png create mode 100644 knetwalk/src/pics/cable0111.png create mode 100644 knetwalk/src/pics/cable1000.png create mode 100644 knetwalk/src/pics/cable1001.png create mode 100644 knetwalk/src/pics/cable1010.png create mode 100644 knetwalk/src/pics/cable1011.png create mode 100644 knetwalk/src/pics/cable1100.png create mode 100644 knetwalk/src/pics/cable1101.png create mode 100644 knetwalk/src/pics/cable1110.png create mode 100644 knetwalk/src/pics/computer1.png create mode 100644 knetwalk/src/pics/computer2.png create mode 100644 knetwalk/src/pics/hi128-app-knetwalk.png create mode 100644 knetwalk/src/pics/hi22-app-knetwalk.png create mode 100644 knetwalk/src/pics/hi32-app-knetwalk.png create mode 100644 knetwalk/src/pics/hi64-app-knetwalk.png create mode 100644 knetwalk/src/pics/knetwalk.svgz create mode 100644 knetwalk/src/pics/server.png create mode 100644 knetwalk/src/settings.kcfgc create mode 100644 knetwalk/src/sounds/Makefile.am create mode 100644 knetwalk/src/sounds/click.wav create mode 100644 knetwalk/src/sounds/connect.wav create mode 100644 knetwalk/src/sounds/start.wav create mode 100644 knetwalk/src/sounds/turn.wav create mode 100644 knetwalk/src/sounds/win.wav create mode 100644 kolf/AUTHORS create mode 100644 kolf/COPYING create mode 100644 kolf/Makefile.am create mode 100644 kolf/Makefile.am.fast create mode 100644 kolf/PLUGINS.docbook create mode 100644 kolf/README.pool create mode 100644 kolf/TODO create mode 100644 kolf/ball.cpp create mode 100644 kolf/ball.h create mode 100644 kolf/canvasitem.cpp create mode 100644 kolf/canvasitem.h create mode 100644 kolf/config.cpp create mode 100644 kolf/config.h create mode 100644 kolf/configure.in.in create mode 100644 kolf/courses.list create mode 100644 kolf/courses/ADDING_COURSES create mode 100644 kolf/courses/Classic.kolf create mode 100644 kolf/courses/Easy.kolf create mode 100644 kolf/courses/Hard.kolf create mode 100644 kolf/courses/Impossible create mode 100644 kolf/courses/Makefile.am create mode 100644 kolf/courses/Medium.kolf create mode 100644 kolf/courses/Practice create mode 100644 kolf/courses/ReallyEasy create mode 100644 kolf/courses/USApro create mode 100644 kolf/editor.cpp create mode 100644 kolf/editor.h create mode 100644 kolf/floater.cpp create mode 100644 kolf/floater.h create mode 100644 kolf/game.cpp create mode 100644 kolf/game.h create mode 100644 kolf/graphics/Makefile.am create mode 100644 kolf/graphics/cup.png create mode 100644 kolf/graphics/grass.png create mode 100644 kolf/graphics/puddle.png create mode 100644 kolf/graphics/sand.png create mode 100644 kolf/intro create mode 100644 kolf/kcomboboxdialog.cpp create mode 100644 kolf/kcomboboxdialog.h create mode 100644 kolf/kolf.cpp create mode 100644 kolf/kolf.desktop create mode 100644 kolf/kolf.h create mode 100644 kolf/kolf.magic create mode 100644 kolf/kolfui.rc create mode 100644 kolf/kvolumecontrol.cpp create mode 100644 kolf/kvolumecontrol.h create mode 100644 kolf/main.cpp create mode 100644 kolf/newgame.cpp create mode 100644 kolf/newgame.h create mode 100644 kolf/object.cpp create mode 100644 kolf/object.h create mode 100644 kolf/objects/Makefile.am create mode 100644 kolf/objects/poolball/Makefile.am create mode 100644 kolf/objects/poolball/poolball.cpp create mode 100644 kolf/objects/poolball/poolball.h create mode 100644 kolf/objects/poolball/poolball.plugin create mode 100644 kolf/objects/test/Makefile.am create mode 100644 kolf/objects/test/test.cpp create mode 100644 kolf/objects/test/test.h create mode 100644 kolf/objects/test/test.plugin create mode 100644 kolf/pics/Makefile.am create mode 100644 kolf/pics/hi128-app-kolf.png create mode 100644 kolf/pics/hi16-app-kolf.png create mode 100644 kolf/pics/hi22-app-kolf.png create mode 100644 kolf/pics/hi32-app-kolf.png create mode 100644 kolf/pics/hi48-app-kolf.png create mode 100644 kolf/pics/hi64-app-kolf.png create mode 100644 kolf/pluginloader.cpp create mode 100644 kolf/pluginloader.h create mode 100644 kolf/pool.kolf create mode 100644 kolf/printdialogpage.cpp create mode 100644 kolf/printdialogpage.h create mode 100644 kolf/rtti.h create mode 100644 kolf/scoreboard.cpp create mode 100644 kolf/scoreboard.h create mode 100644 kolf/slope.cpp create mode 100644 kolf/slope.h create mode 100644 kolf/sounds/FROM create mode 100644 kolf/sounds/Makefile.am create mode 100644 kolf/sounds/blackhole.wav create mode 100644 kolf/sounds/blackholeeject.wav create mode 100644 kolf/sounds/blackholeputin.wav create mode 100644 kolf/sounds/hit.wav create mode 100644 kolf/sounds/holed.wav create mode 100644 kolf/sounds/holeinone.wav create mode 100644 kolf/sounds/puddle.wav create mode 100644 kolf/sounds/wall.wav create mode 100644 kolf/statedb.h create mode 100644 kolf/tutorial.kolf create mode 100644 kolf/tutorial.kolfgame create mode 100644 kolf/vector.cpp create mode 100644 kolf/vector.h create mode 100644 kolf/x-kolf.desktop create mode 100644 kolf/x-kourse.desktop create mode 100644 konquest/AUTHORS create mode 100644 konquest/ChangeLog create mode 100644 konquest/Konquest.cc create mode 100644 konquest/Makefile.am create mode 100644 konquest/README create mode 100644 konquest/TODO create mode 100644 konquest/fleetdlg.cc create mode 100644 konquest/fleetdlg.h create mode 100644 konquest/gameboard.cc create mode 100644 konquest/gameboard.h create mode 100644 konquest/gamecore.cc create mode 100644 konquest/gamecore.h create mode 100644 konquest/gameenddlg.cc create mode 100644 konquest/gameenddlg.h create mode 100644 konquest/hi128-app-konquest.png create mode 100644 konquest/hi16-app-konquest.png create mode 100644 konquest/hi22-app-konquest.png create mode 100644 konquest/hi32-app-konquest.png create mode 100644 konquest/hi48-app-konquest.png create mode 100644 konquest/hi64-app-konquest.png create mode 100644 konquest/images.h create mode 100644 konquest/int_validator.cc create mode 100644 konquest/int_validator.h create mode 100644 konquest/konquest.desktop create mode 100644 konquest/konquestui.rc create mode 100644 konquest/mainwin.cc create mode 100644 konquest/mainwin.h create mode 100644 konquest/map_widget.cc create mode 100644 konquest/map_widget.h create mode 100644 konquest/minimap.cc create mode 100644 konquest/minimap.h create mode 100644 konquest/newGameDlg_ui.ui create mode 100644 konquest/newgamedlg.cc create mode 100644 konquest/newgamedlg.h create mode 100644 konquest/pics/Makefile.am create mode 100644 konquest/pics/konquest-splash.png create mode 100644 konquest/pics/planet1.xpm create mode 100644 konquest/pics/planet2.xpm create mode 100644 konquest/pics/planet3.xpm create mode 100644 konquest/pics/planet4.xpm create mode 100644 konquest/pics/planet5.xpm create mode 100644 konquest/pics/planet6.xpm create mode 100644 konquest/pics/planet7.xpm create mode 100644 konquest/pics/planet8.xpm create mode 100644 konquest/pics/planet9.xpm create mode 100644 konquest/pics/ruler.xpm create mode 100644 konquest/planet_info.cc create mode 100644 konquest/planet_info.h create mode 100644 konquest/scoredlg.cc create mode 100644 konquest/scoredlg.h create mode 100644 konquest/version.h create mode 100644 kpat/AUTHORS create mode 100644 kpat/CHANGES create mode 100644 kpat/Makefile.am create mode 100644 kpat/README create mode 100644 kpat/README.tkcTrump create mode 100644 kpat/TODO create mode 100644 kpat/card.cpp create mode 100644 kpat/card.h create mode 100644 kpat/cardmaps.cpp create mode 100644 kpat/cardmaps.h create mode 100644 kpat/clock.cpp create mode 100644 kpat/clock.h create mode 100644 kpat/computation.cpp create mode 100644 kpat/computation.h create mode 100644 kpat/copyright.h create mode 100644 kpat/dealer.cpp create mode 100644 kpat/dealer.h create mode 100644 kpat/deck.cpp create mode 100644 kpat/deck.h create mode 100644 kpat/fortyeight.cpp create mode 100644 kpat/fortyeight.h create mode 100644 kpat/freecell-solver/CREDITS create mode 100644 kpat/freecell-solver/INSTALL create mode 100644 kpat/freecell-solver/Makefile.am create mode 100644 kpat/freecell-solver/Makefile.lite create mode 100644 kpat/freecell-solver/README create mode 100644 kpat/freecell-solver/USAGE create mode 100644 kpat/freecell-solver/alloc.c create mode 100644 kpat/freecell-solver/alloc.h create mode 100644 kpat/freecell-solver/app_str.c create mode 100644 kpat/freecell-solver/app_str.h create mode 100644 kpat/freecell-solver/caas.c create mode 100644 kpat/freecell-solver/caas.h create mode 100644 kpat/freecell-solver/card.c create mode 100644 kpat/freecell-solver/card.h create mode 100644 kpat/freecell-solver/cl_chop.c create mode 100644 kpat/freecell-solver/cl_chop.h create mode 100644 kpat/freecell-solver/cmd_line.c create mode 100644 kpat/freecell-solver/fcs.h create mode 100644 kpat/freecell-solver/fcs_cl.h create mode 100644 kpat/freecell-solver/fcs_config.h create mode 100644 kpat/freecell-solver/fcs_dm.c create mode 100644 kpat/freecell-solver/fcs_dm.h create mode 100644 kpat/freecell-solver/fcs_enums.h create mode 100644 kpat/freecell-solver/fcs_hash.c create mode 100644 kpat/freecell-solver/fcs_hash.h create mode 100644 kpat/freecell-solver/fcs_isa.c create mode 100644 kpat/freecell-solver/fcs_isa.h create mode 100644 kpat/freecell-solver/fcs_move.h create mode 100644 kpat/freecell-solver/fcs_user.h create mode 100644 kpat/freecell-solver/freecell.c create mode 100644 kpat/freecell-solver/inline.h create mode 100644 kpat/freecell-solver/intrface.c create mode 100644 kpat/freecell-solver/jhjtypes.h create mode 100644 kpat/freecell-solver/lib.c create mode 100644 kpat/freecell-solver/lookup2.c create mode 100644 kpat/freecell-solver/lookup2.h create mode 100644 kpat/freecell-solver/main.c create mode 100644 kpat/freecell-solver/move.c create mode 100644 kpat/freecell-solver/move.h create mode 100644 kpat/freecell-solver/ms_ca.h create mode 100644 kpat/freecell-solver/pqueue.c create mode 100644 kpat/freecell-solver/pqueue.h create mode 100644 kpat/freecell-solver/prefix.h create mode 100644 kpat/freecell-solver/preset.c create mode 100644 kpat/freecell-solver/preset.h create mode 100644 kpat/freecell-solver/rand.c create mode 100644 kpat/freecell-solver/rand.h create mode 100644 kpat/freecell-solver/scans.c create mode 100644 kpat/freecell-solver/simpsim.c create mode 100644 kpat/freecell-solver/state.c create mode 100644 kpat/freecell-solver/state.h create mode 100644 kpat/freecell-solver/test_arr.h create mode 100644 kpat/freecell-solver/tests.h create mode 100644 kpat/freecell.cpp create mode 100644 kpat/freecell.h create mode 100644 kpat/gamestats.ui create mode 100644 kpat/gamestatsimpl.cpp create mode 100644 kpat/gamestatsimpl.h create mode 100644 kpat/golf.cpp create mode 100644 kpat/golf.h create mode 100644 kpat/grandf.cpp create mode 100644 kpat/grandf.h create mode 100644 kpat/green.png create mode 100644 kpat/gypsy.cpp create mode 100644 kpat/gypsy.h create mode 100644 kpat/hint.h create mode 100644 kpat/icons/Makefile.am create mode 100644 kpat/icons/hi128-app-kpat.png create mode 100644 kpat/icons/hi16-app-kpat.png create mode 100644 kpat/icons/hi22-app-kpat.png create mode 100644 kpat/icons/hi32-app-kpat.png create mode 100644 kpat/icons/hi48-app-kpat.png create mode 100644 kpat/icons/hi64-app-kpat.png create mode 100644 kpat/icons/kpat-lq.png create mode 100644 kpat/idiot.cpp create mode 100644 kpat/idiot.h create mode 100644 kpat/kings.cpp create mode 100644 kpat/kings.h create mode 100644 kpat/klondike.cpp create mode 100644 kpat/klondike.h create mode 100644 kpat/kpat.desktop create mode 100644 kpat/kpatui.rc create mode 100644 kpat/main.cpp create mode 100644 kpat/mod3.cpp create mode 100644 kpat/mod3.h create mode 100644 kpat/napoleon.cpp create mode 100644 kpat/napoleon.h create mode 100644 kpat/pile.cpp create mode 100644 kpat/pile.h create mode 100644 kpat/pile_algorithms.cpp create mode 100644 kpat/pwidget.cpp create mode 100644 kpat/pwidget.h create mode 100644 kpat/simon.cpp create mode 100644 kpat/simon.h create mode 100644 kpat/speeds.h create mode 100644 kpat/spider.cpp create mode 100644 kpat/spider.h create mode 100644 kpat/version.h create mode 100644 kpat/yukon.cpp create mode 100644 kpat/yukon.h create mode 100644 kpoker/AUTHORS create mode 100644 kpoker/ChangeLog create mode 100644 kpoker/DESCRIPTION create mode 100644 kpoker/Makefile.am create mode 100644 kpoker/README create mode 100644 kpoker/TODO create mode 100644 kpoker/betbox.cpp create mode 100644 kpoker/betbox.h create mode 100644 kpoker/defines.h create mode 100644 kpoker/global.h create mode 100644 kpoker/hi128-app-kpoker.png create mode 100644 kpoker/hi16-app-kpoker.png create mode 100644 kpoker/hi22-app-kpoker.png create mode 100644 kpoker/hi32-app-kpoker.png create mode 100644 kpoker/hi48-app-kpoker.png create mode 100644 kpoker/hi64-app-kpoker.png create mode 100644 kpoker/kpaint.cpp create mode 100644 kpoker/kpaint.h create mode 100644 kpoker/kpoker.cpp create mode 100644 kpoker/kpoker.desktop create mode 100644 kpoker/kpoker.h create mode 100644 kpoker/kpokerui.rc create mode 100644 kpoker/main.cpp create mode 100644 kpoker/newgamedlg.cpp create mode 100644 kpoker/newgamedlg.h create mode 100644 kpoker/optionsdlg.cpp create mode 100644 kpoker/optionsdlg.h create mode 100644 kpoker/player.cpp create mode 100644 kpoker/player.h create mode 100644 kpoker/playerbox.cpp create mode 100644 kpoker/playerbox.h create mode 100644 kpoker/poker.cpp create mode 100644 kpoker/poker.h create mode 100644 kpoker/sound.cpp create mode 100644 kpoker/sounds/Makefile.am create mode 100644 kpoker/sounds/cardflip.wav create mode 100644 kpoker/sounds/hold.wav create mode 100644 kpoker/sounds/lose.wav create mode 100644 kpoker/sounds/win.wav create mode 100644 kpoker/top.cpp create mode 100644 kpoker/top.h create mode 100644 kpoker/version.h create mode 100644 kreversi/AUTHORS create mode 100644 kreversi/ChangeLog create mode 100644 kreversi/DESIGN create mode 100644 kreversi/Engine.cpp create mode 100644 kreversi/Engine.h create mode 100644 kreversi/Game.cpp create mode 100644 kreversi/Game.h create mode 100644 kreversi/Makefile.am create mode 100644 kreversi/Move.cpp create mode 100644 kreversi/Move.h create mode 100644 kreversi/NEWS create mode 100644 kreversi/Position.cpp create mode 100644 kreversi/Position.h create mode 100644 kreversi/Score.cpp create mode 100644 kreversi/Score.h create mode 100644 kreversi/SuperEngine.cpp create mode 100644 kreversi/SuperEngine.h create mode 100644 kreversi/TODO create mode 100644 kreversi/board.cpp create mode 100644 kreversi/board.h create mode 100644 kreversi/highscores.cpp create mode 100644 kreversi/highscores.h create mode 100644 kreversi/icons/Makefile.am create mode 100644 kreversi/icons/cr16-action-lastmoves.png create mode 100644 kreversi/icons/cr16-action-legalmoves.png create mode 100644 kreversi/icons/cr22-action-lastmoves.png create mode 100644 kreversi/icons/cr22-action-legalmoves.png create mode 100644 kreversi/icons/cr32-action-lastmoves.png create mode 100644 kreversi/icons/cr32-action-legalmoves.png create mode 100644 kreversi/icons/cr48-action-lastmoves.png create mode 100644 kreversi/icons/cr48-action-legalmoves.png create mode 100644 kreversi/icons/crsc-action-lastmoves.svgz create mode 100644 kreversi/icons/crsc-action-legalmoves.svgz create mode 100644 kreversi/icons/hi128-app-kreversi.png create mode 100644 kreversi/icons/hi16-app-kreversi.png create mode 100644 kreversi/icons/hi22-app-kreversi.png create mode 100644 kreversi/icons/hi32-app-kreversi.png create mode 100644 kreversi/icons/hi48-app-kreversi.png create mode 100644 kreversi/icons/hi64-app-kreversi.png create mode 100644 kreversi/kreversi.cpp create mode 100644 kreversi/kreversi.desktop create mode 100644 kreversi/kreversi.h create mode 100644 kreversi/kreversi.kcfg create mode 100644 kreversi/kreversiui.rc create mode 100644 kreversi/kzoommainwindow.cpp create mode 100644 kreversi/kzoommainwindow.h create mode 100644 kreversi/main.cpp create mode 100644 kreversi/pics/Makefile.am create mode 100644 kreversi/pics/background/Dark_Wood.png create mode 100644 kreversi/pics/background/Earth.png create mode 100644 kreversi/pics/background/Granite.png create mode 100644 kreversi/pics/background/Hexagon.png create mode 100644 kreversi/pics/background/Light_Wood.png create mode 100644 kreversi/pics/background/Makefile.am create mode 100644 kreversi/pics/background/Mystique.png create mode 100644 kreversi/pics/background/Ocean.png create mode 100644 kreversi/pics/background/Pipes.png create mode 100644 kreversi/pics/background/Puzzle.png create mode 100644 kreversi/pics/background/Stones.png create mode 100644 kreversi/pics/chips.png create mode 100644 kreversi/pics/chips_mono.png create mode 100644 kreversi/prefs.kcfgc create mode 100644 kreversi/qreversigame.cpp create mode 100644 kreversi/qreversigame.h create mode 100644 kreversi/qreversigameview.cpp create mode 100644 kreversi/qreversigameview.h create mode 100644 kreversi/settings.ui create mode 100644 kreversi/sounds/Makefile.am create mode 100644 kreversi/sounds/eventsrc create mode 100644 kreversi/sounds/reversi-click.wav create mode 100644 kreversi/sounds/reversi-won.wav create mode 100644 kreversi/version.h create mode 100644 ksame/ChangeLog create mode 100644 ksame/KSameWidget.cpp create mode 100644 ksame/KSameWidget.h create mode 100644 ksame/Makefile.am create mode 100644 ksame/StoneField.cpp create mode 100644 ksame/StoneField.h create mode 100644 ksame/StoneWidget.cpp create mode 100644 ksame/StoneWidget.h create mode 100644 ksame/eventsrc create mode 100644 ksame/hi128-app-ksame.png create mode 100644 ksame/hi16-app-ksame.png create mode 100644 ksame/hi22-app-ksame.png create mode 100644 ksame/hi32-app-ksame.png create mode 100644 ksame/hi48-app-ksame.png create mode 100644 ksame/hi64-app-ksame.png create mode 100644 ksame/ksame.desktop create mode 100644 ksame/ksameui.rc create mode 100644 ksame/main.cpp create mode 100644 ksame/stones.png create mode 100644 ksame/version.h create mode 100644 kshisen/ChangeLog create mode 100644 kshisen/Makefile.am create mode 100644 kshisen/app.cpp create mode 100644 kshisen/app.h create mode 100644 kshisen/board.cpp create mode 100644 kshisen/board.h create mode 100644 kshisen/debug.h create mode 100644 kshisen/hi128-app-kshisen.png create mode 100644 kshisen/hi16-app-kshisen.png create mode 100644 kshisen/hi22-app-kshisen.png create mode 100644 kshisen/hi32-app-kshisen.png create mode 100644 kshisen/hi48-app-kshisen.png create mode 100644 kshisen/hi64-app-kshisen.png create mode 100644 kshisen/kshisen.desktop create mode 100644 kshisen/kshisen.kcfg create mode 100644 kshisen/kshisenui.rc create mode 100644 kshisen/main.cpp create mode 100644 kshisen/pics/Makefile.am create mode 100644 kshisen/pics/kshisen_bgnd.png create mode 100644 kshisen/pics/tileset.png create mode 100644 kshisen/prefs.kcfgc create mode 100644 kshisen/settings.ui create mode 100644 kshisen/tileset.cpp create mode 100644 kshisen/tileset.h create mode 100644 kshisen/version.h create mode 100644 ksirtet/CHANGELOG create mode 100644 ksirtet/DESIGN create mode 100644 ksirtet/LICENSE create mode 100644 ksirtet/Makefile.am create mode 100644 ksirtet/README create mode 100644 ksirtet/TODO create mode 100644 ksirtet/configure.in.in create mode 100644 ksirtet/ksirtet/Makefile.am create mode 100644 ksirtet/ksirtet/ai.cpp create mode 100644 ksirtet/ksirtet/ai.h create mode 100644 ksirtet/ksirtet/board.cpp create mode 100644 ksirtet/ksirtet/board.h create mode 100644 ksirtet/ksirtet/check_score.cpp create mode 100644 ksirtet/ksirtet/eventsrc create mode 100644 ksirtet/ksirtet/field.cpp create mode 100644 ksirtet/ksirtet/field.h create mode 100644 ksirtet/ksirtet/hi128-app-ksirtet.png create mode 100644 ksirtet/ksirtet/hi16-app-ksirtet.png create mode 100644 ksirtet/ksirtet/hi22-app-ksirtet.png create mode 100644 ksirtet/ksirtet/hi32-app-ksirtet.png create mode 100644 ksirtet/ksirtet/hi48-app-ksirtet.png create mode 100644 ksirtet/ksirtet/hi64-app-ksirtet.png create mode 100644 ksirtet/ksirtet/ksirtet.desktop create mode 100644 ksirtet/ksirtet/ksirtet.kcfg create mode 100644 ksirtet/ksirtet/ksirtetui.rc create mode 100644 ksirtet/ksirtet/main.cpp create mode 100644 ksirtet/ksirtet/main.h create mode 100644 ksirtet/ksirtet/piece.cpp create mode 100644 ksirtet/ksirtet/piece.h create mode 100644 ksirtet/ksirtet/prefs.kcfgc create mode 100644 ksirtet/ksirtet/settings.cpp create mode 100644 ksirtet/ksirtet/settings.h create mode 100644 ksmiletris/COPYRIGHT create mode 100644 ksmiletris/Makefile.am create mode 100644 ksmiletris/README create mode 100644 ksmiletris/data/Makefile.am create mode 100644 ksmiletris/data/bg1.bmp create mode 100644 ksmiletris/data/bg10.bmp create mode 100644 ksmiletris/data/bg11.bmp create mode 100644 ksmiletris/data/bg12.bmp create mode 100644 ksmiletris/data/bg13.bmp create mode 100644 ksmiletris/data/bg14.bmp create mode 100644 ksmiletris/data/bg15.bmp create mode 100644 ksmiletris/data/bg16.bmp create mode 100644 ksmiletris/data/bg2.bmp create mode 100644 ksmiletris/data/bg3.bmp create mode 100644 ksmiletris/data/bg4.bmp create mode 100644 ksmiletris/data/bg5.bmp create mode 100644 ksmiletris/data/bg6.bmp create mode 100644 ksmiletris/data/bg7.bmp create mode 100644 ksmiletris/data/bg8.bmp create mode 100644 ksmiletris/data/bg9.bmp create mode 100644 ksmiletris/data/block-a1.bmp create mode 100644 ksmiletris/data/block-a2.bmp create mode 100644 ksmiletris/data/block-a3.bmp create mode 100644 ksmiletris/data/block-a4.bmp create mode 100644 ksmiletris/data/block-a5.bmp create mode 100644 ksmiletris/data/block-a6.bmp create mode 100644 ksmiletris/data/block-a7.bmp create mode 100644 ksmiletris/data/block-a8.bmp create mode 100644 ksmiletris/data/block-b1.bmp create mode 100644 ksmiletris/data/block-b2.bmp create mode 100644 ksmiletris/data/block-b3.bmp create mode 100644 ksmiletris/data/block-b4.bmp create mode 100644 ksmiletris/data/block-b5.bmp create mode 100644 ksmiletris/data/block-b6.bmp create mode 100644 ksmiletris/data/block-b7.bmp create mode 100644 ksmiletris/data/block-b8.bmp create mode 100644 ksmiletris/data/block-c1.bmp create mode 100644 ksmiletris/data/block-c2.bmp create mode 100644 ksmiletris/data/block-c3.bmp create mode 100644 ksmiletris/data/block-c4.bmp create mode 100644 ksmiletris/data/block-c5.bmp create mode 100644 ksmiletris/data/block-c6.bmp create mode 100644 ksmiletris/data/block-c7.bmp create mode 100644 ksmiletris/data/block-c8.bmp create mode 100644 ksmiletris/data/broken-mask.bmp create mode 100644 ksmiletris/data/broken.bmp create mode 100644 ksmiletris/data/cleared-a.bmp create mode 100644 ksmiletris/data/cleared-b.bmp create mode 100644 ksmiletris/data/cleared-c.bmp create mode 100644 ksmiletris/data/pause.bmp create mode 100644 ksmiletris/gamewidget.cpp create mode 100644 ksmiletris/gamewidget.h create mode 100644 ksmiletris/gamewindow.cpp create mode 100644 ksmiletris/gamewindow.h create mode 100644 ksmiletris/hi128-app-ksmiletris.png create mode 100644 ksmiletris/hi16-app-ksmiletris.png create mode 100644 ksmiletris/hi22-app-ksmiletris.png create mode 100644 ksmiletris/hi32-app-ksmiletris.png create mode 100644 ksmiletris/hi48-app-ksmiletris.png create mode 100644 ksmiletris/hi64-app-ksmiletris.png create mode 100644 ksmiletris/ksmiletris.desktop create mode 100644 ksmiletris/ksmiletris.h create mode 100644 ksmiletris/ksmiletrisui.rc create mode 100644 ksmiletris/main.cpp create mode 100644 ksmiletris/mirrorwidget.cpp create mode 100644 ksmiletris/mirrorwidget.h create mode 100644 ksmiletris/npiecewidget.cpp create mode 100644 ksmiletris/npiecewidget.h create mode 100644 ksmiletris/screenwidget.cpp create mode 100644 ksmiletris/screenwidget.h create mode 100644 ksmiletris/wav/Makefile.am create mode 100644 ksmiletris/wav/break.wav create mode 100644 ksmiletris/wav/clear.wav create mode 100644 ksnake/ChangeLog create mode 100644 ksnake/Makefile.am create mode 100644 ksnake/appearance.ui create mode 100644 ksnake/ball.cpp create mode 100644 ksnake/ball.h create mode 100644 ksnake/basket.cpp create mode 100644 ksnake/basket.h create mode 100644 ksnake/bitmaps.h create mode 100644 ksnake/board.cpp create mode 100644 ksnake/board.h create mode 100644 ksnake/data/Makefile.am create mode 100644 ksnake/data/backgrounds/Bark.png create mode 100644 ksnake/data/backgrounds/Blue_Carpet.png create mode 100644 ksnake/data/backgrounds/Dark_Wood.png create mode 100644 ksnake/data/backgrounds/Granite.png create mode 100644 ksnake/data/backgrounds/Green_Carpet.png create mode 100644 ksnake/data/backgrounds/Makefile.am create mode 100644 ksnake/data/backgrounds/Mystique.png create mode 100644 ksnake/data/backgrounds/Rope_Weave.png create mode 100644 ksnake/data/backgrounds/Volcanic.png create mode 100644 ksnake/data/backgrounds/Wood.png create mode 100644 ksnake/data/highScores create mode 100644 ksnake/data/levels/Makefile.am create mode 100644 ksnake/data/levels/room01 create mode 100644 ksnake/data/levels/room02 create mode 100644 ksnake/data/levels/room03 create mode 100644 ksnake/data/levels/room04 create mode 100644 ksnake/data/levels/room05 create mode 100644 ksnake/data/levels/room06 create mode 100644 ksnake/data/levels/room07 create mode 100644 ksnake/data/levels/room08 create mode 100644 ksnake/data/levels/room09 create mode 100644 ksnake/data/levels/room10 create mode 100644 ksnake/data/levels/room11 create mode 100644 ksnake/data/levels/room12 create mode 100644 ksnake/data/levels/room13 create mode 100644 ksnake/data/levels/room14 create mode 100644 ksnake/data/levels/room15 create mode 100644 ksnake/data/levels/room16 create mode 100644 ksnake/data/levels/room17 create mode 100644 ksnake/data/levels/room18 create mode 100644 ksnake/data/levels/room19 create mode 100644 ksnake/data/levels/room20 create mode 100644 ksnake/data/levels/room21 create mode 100644 ksnake/data/levels/room22 create mode 100644 ksnake/data/levels/room23 create mode 100644 ksnake/data/levels/room24 create mode 100644 ksnake/data/levels/room25 create mode 100644 ksnake/data/pixmaps/Makefile.am create mode 100644 ksnake/data/pixmaps/apples.png create mode 100644 ksnake/data/pixmaps/ball.png create mode 100644 ksnake/data/pixmaps/brick.png create mode 100644 ksnake/data/pixmaps/snake1.png create mode 100644 ksnake/data/pixmaps/snake2.png create mode 100644 ksnake/game.cpp create mode 100644 ksnake/game.h create mode 100644 ksnake/general.ui create mode 100644 ksnake/hi128-app-ksnake.png create mode 100644 ksnake/hi16-app-ksnake.png create mode 100644 ksnake/hi22-app-ksnake.png create mode 100644 ksnake/hi32-app-ksnake.png create mode 100644 ksnake/hi48-app-ksnake.png create mode 100644 ksnake/hi64-app-ksnake.png create mode 100644 ksnake/ksnake.desktop create mode 100644 ksnake/ksnake.kcfg create mode 100644 ksnake/ksnakeui.rc create mode 100644 ksnake/level.cpp create mode 100644 ksnake/level.h create mode 100644 ksnake/levels.cpp create mode 100644 ksnake/levels.h create mode 100644 ksnake/main.cpp create mode 100644 ksnake/pixServer.cpp create mode 100644 ksnake/pixServer.h create mode 100644 ksnake/progress.cpp create mode 100644 ksnake/progress.h create mode 100644 ksnake/rattler.cpp create mode 100644 ksnake/rattler.h create mode 100644 ksnake/settings.kcfgc create mode 100644 ksnake/snake.cpp create mode 100644 ksnake/snake.h create mode 100644 ksnake/startroom.cpp create mode 100644 ksnake/startroom.h create mode 100644 ksnake/version.h create mode 100644 ksnake/view.cpp create mode 100644 ksnake/view.h create mode 100644 ksokoban/AUTHORS create mode 100644 ksokoban/Bookmark.cpp create mode 100644 ksokoban/Bookmark.h create mode 100644 ksokoban/History.cpp create mode 100644 ksokoban/History.h create mode 100644 ksokoban/HtmlPrinter.cpp create mode 100644 ksokoban/HtmlPrinter.h create mode 100644 ksokoban/ImageData.cpp create mode 100644 ksokoban/ImageData.h create mode 100644 ksokoban/InternalCollections.cpp create mode 100644 ksokoban/InternalCollections.h create mode 100644 ksokoban/LevelCollection.cpp create mode 100644 ksokoban/LevelCollection.h create mode 100644 ksokoban/LevelMap.cpp create mode 100644 ksokoban/LevelMap.h create mode 100644 ksokoban/MainWindow.cpp create mode 100644 ksokoban/MainWindow.h create mode 100644 ksokoban/Makefile.am create mode 100644 ksokoban/Map.cpp create mode 100644 ksokoban/Map.h create mode 100644 ksokoban/MapDelta.cpp create mode 100644 ksokoban/MapDelta.h create mode 100644 ksokoban/ModalLabel.cpp create mode 100644 ksokoban/ModalLabel.h create mode 100644 ksokoban/Move.cpp create mode 100644 ksokoban/Move.h create mode 100644 ksokoban/MoveSequence.cpp create mode 100644 ksokoban/MoveSequence.h create mode 100644 ksokoban/NEWS create mode 100644 ksokoban/PathFinder.cpp create mode 100644 ksokoban/PathFinder.h create mode 100644 ksokoban/PlayField.cpp create mode 100644 ksokoban/PlayField.h create mode 100644 ksokoban/Queue.h create mode 100644 ksokoban/README create mode 100644 ksokoban/StaticImage.cpp create mode 100644 ksokoban/StaticImage.h create mode 100644 ksokoban/TODO create mode 100644 ksokoban/data/Makefile.am create mode 100644 ksokoban/data/hi128-app-ksokoban.png create mode 100644 ksokoban/data/hi16-app-ksokoban.png create mode 100644 ksokoban/data/hi22-app-ksokoban.png create mode 100644 ksokoban/data/hi32-app-ksokoban.png create mode 100644 ksokoban/data/hi48-app-ksokoban.png create mode 100644 ksokoban/data/hi64-app-ksokoban.png create mode 100644 ksokoban/data/ksokoban.desktop create mode 100644 ksokoban/images/Makefile.am create mode 100644 ksokoban/images/bin2c.c create mode 100644 ksokoban/images/floor_common.inc create mode 100644 ksokoban/images/goal.png create mode 100644 ksokoban/images/goal.pov create mode 100644 ksokoban/images/halfstone_1.png create mode 100644 ksokoban/images/halfstone_1.pov create mode 100644 ksokoban/images/halfstone_2.png create mode 100644 ksokoban/images/halfstone_2.pov create mode 100644 ksokoban/images/halfstone_3.png create mode 100644 ksokoban/images/halfstone_3.pov create mode 100644 ksokoban/images/halfstone_4.png create mode 100644 ksokoban/images/halfstone_4.pov create mode 100644 ksokoban/images/man.png create mode 100644 ksokoban/images/man.pov create mode 100644 ksokoban/images/man_common.inc create mode 100644 ksokoban/images/object.png create mode 100644 ksokoban/images/object.pov create mode 100644 ksokoban/images/saveman.png create mode 100644 ksokoban/images/saveman.pov create mode 100644 ksokoban/images/starfield.png create mode 100644 ksokoban/images/stone_1.png create mode 100644 ksokoban/images/stone_1.pov create mode 100644 ksokoban/images/stone_2.png create mode 100644 ksokoban/images/stone_2.pov create mode 100644 ksokoban/images/stone_3.png create mode 100644 ksokoban/images/stone_3.pov create mode 100644 ksokoban/images/stone_4.png create mode 100644 ksokoban/images/stone_4.pov create mode 100644 ksokoban/images/stone_5.png create mode 100644 ksokoban/images/stone_5.pov create mode 100644 ksokoban/images/stone_6.png create mode 100644 ksokoban/images/stone_6.pov create mode 100644 ksokoban/images/stone_common.inc create mode 100644 ksokoban/images/treasure.png create mode 100644 ksokoban/images/treasure.pov create mode 100644 ksokoban/levels/Makefile.am create mode 100644 ksokoban/levels/level.data create mode 100644 ksokoban/main.cpp create mode 100644 kspaceduel/AUTHORS create mode 100644 kspaceduel/ChangeLog create mode 100644 kspaceduel/Makefile.am create mode 100644 kspaceduel/README create mode 100644 kspaceduel/TODO create mode 100644 kspaceduel/ai.cpp create mode 100644 kspaceduel/ai.h create mode 100644 kspaceduel/configure.in.in create mode 100644 kspaceduel/defines.h create mode 100644 kspaceduel/dialogs.cpp create mode 100644 kspaceduel/dialogs.h create mode 100644 kspaceduel/general.ui create mode 100644 kspaceduel/hi128-app-kspaceduel.png create mode 100644 kspaceduel/hi16-app-kspaceduel.png create mode 100644 kspaceduel/hi22-app-kspaceduel.png create mode 100644 kspaceduel/hi32-app-kspaceduel.png create mode 100644 kspaceduel/hi48-app-kspaceduel.png create mode 100644 kspaceduel/hi64-app-kspaceduel.png create mode 100644 kspaceduel/kspaceduel.desktop create mode 100644 kspaceduel/kspaceduel.kcfg create mode 100644 kspaceduel/kspaceduelui.rc create mode 100644 kspaceduel/main.cpp create mode 100644 kspaceduel/mainview.cpp create mode 100644 kspaceduel/mainview.h create mode 100644 kspaceduel/mathroutines.cpp create mode 100644 kspaceduel/mathroutines.h create mode 100644 kspaceduel/options.kcfgc create mode 100644 kspaceduel/pics/Makefile.am create mode 100644 kspaceduel/pics/cr16-action-spnewgame.png create mode 100644 kspaceduel/pics/cr16-action-spnewround.png create mode 100644 kspaceduel/pics/cr16-action-sppausegame.png create mode 100644 kspaceduel/pics/cr22-action-spnewgame.png create mode 100644 kspaceduel/pics/cr22-action-spnewround.png create mode 100644 kspaceduel/pics/cr22-action-sppausegame.png create mode 100644 kspaceduel/pics/cr32-action-spnewgame.png create mode 100644 kspaceduel/pics/cr32-action-spnewround.png create mode 100644 kspaceduel/pics/cr32-action-sppausegame.png create mode 100644 kspaceduel/pics/lo16-action-spnewgame.png create mode 100644 kspaceduel/pics/lo16-action-spnewround.png create mode 100644 kspaceduel/pics/lo16-action-sppausegame.png create mode 100644 kspaceduel/playerinfo.cpp create mode 100644 kspaceduel/playerinfo.h create mode 100644 kspaceduel/sprites.cpp create mode 100644 kspaceduel/sprites.h create mode 100644 kspaceduel/sprites/Makefile.am create mode 100644 kspaceduel/sprites/backgr.png create mode 100644 kspaceduel/sprites/explosion/Makefile.am create mode 100644 kspaceduel/sprites/explosion/explos00.pbm create mode 100644 kspaceduel/sprites/explosion/explos00.ppm create mode 100644 kspaceduel/sprites/explosion/explos01.pbm create mode 100644 kspaceduel/sprites/explosion/explos01.ppm create mode 100644 kspaceduel/sprites/explosion/explos02.pbm create mode 100644 kspaceduel/sprites/explosion/explos02.ppm create mode 100644 kspaceduel/sprites/explosion/explos03.pbm create mode 100644 kspaceduel/sprites/explosion/explos03.ppm create mode 100644 kspaceduel/sprites/explosion/explos04.pbm create mode 100644 kspaceduel/sprites/explosion/explos04.ppm create mode 100644 kspaceduel/sprites/explosion/explos05.pbm create mode 100644 kspaceduel/sprites/explosion/explos05.ppm create mode 100644 kspaceduel/sprites/explosion/explos06.pbm create mode 100644 kspaceduel/sprites/explosion/explos06.ppm create mode 100644 kspaceduel/sprites/explosion/explos07.pbm create mode 100644 kspaceduel/sprites/explosion/explos07.ppm create mode 100644 kspaceduel/sprites/explosion/explos08.pbm create mode 100644 kspaceduel/sprites/explosion/explos08.ppm create mode 100644 kspaceduel/sprites/explosion/explos09.pbm create mode 100644 kspaceduel/sprites/explosion/explos09.ppm create mode 100644 kspaceduel/sprites/explosion/explos10.pbm create mode 100644 kspaceduel/sprites/explosion/explos10.ppm create mode 100644 kspaceduel/sprites/explosion/explos11.pbm create mode 100644 kspaceduel/sprites/explosion/explos11.ppm create mode 100644 kspaceduel/sprites/explosion/explos12.pbm create mode 100644 kspaceduel/sprites/explosion/explos12.ppm create mode 100644 kspaceduel/sprites/explosion/explos13.pbm create mode 100644 kspaceduel/sprites/explosion/explos13.ppm create mode 100644 kspaceduel/sprites/explosion/explos14.pbm create mode 100644 kspaceduel/sprites/explosion/explos14.ppm create mode 100644 kspaceduel/sprites/explosion/explos15.pbm create mode 100644 kspaceduel/sprites/explosion/explos15.ppm create mode 100644 kspaceduel/sprites/explosion/explos16.pbm create mode 100644 kspaceduel/sprites/explosion/explos16.ppm create mode 100644 kspaceduel/sprites/explosion/explos17.pbm create mode 100644 kspaceduel/sprites/explosion/explos17.ppm create mode 100644 kspaceduel/sprites/explosion/explos18.pbm create mode 100644 kspaceduel/sprites/explosion/explos18.ppm create mode 100644 kspaceduel/sprites/explosion/explos19.pbm create mode 100644 kspaceduel/sprites/explosion/explos19.ppm create mode 100644 kspaceduel/sprites/explosion/explos20.pbm create mode 100644 kspaceduel/sprites/explosion/explos20.ppm create mode 100644 kspaceduel/sprites/explosion/explos21.pbm create mode 100644 kspaceduel/sprites/explosion/explos21.ppm create mode 100644 kspaceduel/sprites/explosion/explos22.pbm create mode 100644 kspaceduel/sprites/explosion/explos22.ppm create mode 100644 kspaceduel/sprites/explosion/explos23.pbm create mode 100644 kspaceduel/sprites/explosion/explos23.ppm create mode 100644 kspaceduel/sprites/explosion/explos24.pbm create mode 100644 kspaceduel/sprites/explosion/explos24.ppm create mode 100644 kspaceduel/sprites/explosion/explos25.pbm create mode 100644 kspaceduel/sprites/explosion/explos25.ppm create mode 100644 kspaceduel/sprites/explosion/explos26.pbm create mode 100644 kspaceduel/sprites/explosion/explos26.ppm create mode 100644 kspaceduel/sprites/explosion/explos27.pbm create mode 100644 kspaceduel/sprites/explosion/explos27.ppm create mode 100644 kspaceduel/sprites/explosion/explos28.pbm create mode 100644 kspaceduel/sprites/explosion/explos28.ppm create mode 100644 kspaceduel/sprites/explosion/explos29.pbm create mode 100644 kspaceduel/sprites/explosion/explos29.ppm create mode 100644 kspaceduel/sprites/explosion/explos30.pbm create mode 100644 kspaceduel/sprites/explosion/explos30.ppm create mode 100644 kspaceduel/sprites/explosion/mineex00.pbm create mode 100644 kspaceduel/sprites/explosion/mineex00.ppm create mode 100644 kspaceduel/sprites/explosion/mineex01.pbm create mode 100644 kspaceduel/sprites/explosion/mineex01.ppm create mode 100644 kspaceduel/sprites/explosion/mineex02.pbm create mode 100644 kspaceduel/sprites/explosion/mineex02.ppm create mode 100644 kspaceduel/sprites/explosion/mineex03.pbm create mode 100644 kspaceduel/sprites/explosion/mineex03.ppm create mode 100644 kspaceduel/sprites/explosion/mineex04.pbm create mode 100644 kspaceduel/sprites/explosion/mineex04.ppm create mode 100644 kspaceduel/sprites/explosion/mineex05.pbm create mode 100644 kspaceduel/sprites/explosion/mineex05.ppm create mode 100644 kspaceduel/sprites/explosion/mineex06.pbm create mode 100644 kspaceduel/sprites/explosion/mineex06.ppm create mode 100644 kspaceduel/sprites/explosion/mineex07.pbm create mode 100644 kspaceduel/sprites/explosion/mineex07.ppm create mode 100644 kspaceduel/sprites/explosion/mineex08.pbm create mode 100644 kspaceduel/sprites/explosion/mineex08.ppm create mode 100644 kspaceduel/sprites/explosion/mineex09.pbm create mode 100644 kspaceduel/sprites/explosion/mineex09.ppm create mode 100644 kspaceduel/sprites/explosion/mineex10.pbm create mode 100644 kspaceduel/sprites/explosion/mineex10.ppm create mode 100644 kspaceduel/sprites/explosion/mineex11.pbm create mode 100644 kspaceduel/sprites/explosion/mineex11.ppm create mode 100644 kspaceduel/sprites/explosion/mineex12.pbm create mode 100644 kspaceduel/sprites/explosion/mineex12.ppm create mode 100644 kspaceduel/sprites/explosion/mineex13.pbm create mode 100644 kspaceduel/sprites/explosion/mineex13.ppm create mode 100644 kspaceduel/sprites/explosion/mineex14.pbm create mode 100644 kspaceduel/sprites/explosion/mineex14.ppm create mode 100644 kspaceduel/sprites/explosion/mineex15.pbm create mode 100644 kspaceduel/sprites/explosion/mineex15.ppm create mode 100644 kspaceduel/sprites/explosion/mineex16.pbm create mode 100644 kspaceduel/sprites/explosion/mineex16.ppm create mode 100644 kspaceduel/sprites/explosion/mineex17.pbm create mode 100644 kspaceduel/sprites/explosion/mineex17.ppm create mode 100644 kspaceduel/sprites/playerinfo/Makefile.am create mode 100644 kspaceduel/sprites/playerinfo/energy.pnm create mode 100644 kspaceduel/sprites/playerinfo/mine.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship10.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship11.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship12.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship13.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship20.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship21.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship22.pnm create mode 100644 kspaceduel/sprites/playerinfo/ship23.pnm create mode 100644 kspaceduel/sprites/playerinfo/win.pnm create mode 100644 kspaceduel/sprites/powerups/Makefile.am create mode 100644 kspaceduel/sprites/powerups/pbullet.pbm create mode 100644 kspaceduel/sprites/powerups/pbullet.ppm create mode 100644 kspaceduel/sprites/powerups/penergy.pbm create mode 100644 kspaceduel/sprites/powerups/penergy.ppm create mode 100644 kspaceduel/sprites/powerups/pmine.pbm create mode 100644 kspaceduel/sprites/powerups/pmine.ppm create mode 100644 kspaceduel/sprites/powerups/pshield.pbm create mode 100644 kspaceduel/sprites/powerups/pshield.ppm create mode 100644 kspaceduel/sprites/ship1/Makefile.am create mode 100644 kspaceduel/sprites/ship1/bullet.pbm create mode 100644 kspaceduel/sprites/ship1/bullet.ppm create mode 100644 kspaceduel/sprites/ship1/mine0.pbm create mode 100644 kspaceduel/sprites/ship1/mine0.ppm create mode 100644 kspaceduel/sprites/ship1/mine1.pbm create mode 100644 kspaceduel/sprites/ship1/mine1.ppm create mode 100644 kspaceduel/sprites/ship1/ship00.pbm create mode 100644 kspaceduel/sprites/ship1/ship00.ppm create mode 100644 kspaceduel/sprites/ship1/ship01.pbm create mode 100644 kspaceduel/sprites/ship1/ship01.ppm create mode 100644 kspaceduel/sprites/ship1/ship02.pbm create mode 100644 kspaceduel/sprites/ship1/ship02.ppm create mode 100644 kspaceduel/sprites/ship1/ship03.pbm create mode 100644 kspaceduel/sprites/ship1/ship03.ppm create mode 100644 kspaceduel/sprites/ship1/ship04.pbm create mode 100644 kspaceduel/sprites/ship1/ship04.ppm create mode 100644 kspaceduel/sprites/ship1/ship05.pbm create mode 100644 kspaceduel/sprites/ship1/ship05.ppm create mode 100644 kspaceduel/sprites/ship1/ship06.pbm create mode 100644 kspaceduel/sprites/ship1/ship06.ppm create mode 100644 kspaceduel/sprites/ship1/ship07.pbm create mode 100644 kspaceduel/sprites/ship1/ship07.ppm create mode 100644 kspaceduel/sprites/ship1/ship08.pbm create mode 100644 kspaceduel/sprites/ship1/ship08.ppm create mode 100644 kspaceduel/sprites/ship1/ship09.pbm create mode 100644 kspaceduel/sprites/ship1/ship09.ppm create mode 100644 kspaceduel/sprites/ship1/ship10.pbm create mode 100644 kspaceduel/sprites/ship1/ship10.ppm create mode 100644 kspaceduel/sprites/ship1/ship11.pbm create mode 100644 kspaceduel/sprites/ship1/ship11.ppm create mode 100644 kspaceduel/sprites/ship1/ship12.pbm create mode 100644 kspaceduel/sprites/ship1/ship12.ppm create mode 100644 kspaceduel/sprites/ship1/ship13.pbm create mode 100644 kspaceduel/sprites/ship1/ship13.ppm create mode 100644 kspaceduel/sprites/ship1/ship14.pbm create mode 100644 kspaceduel/sprites/ship1/ship14.ppm create mode 100644 kspaceduel/sprites/ship1/ship15.pbm create mode 100644 kspaceduel/sprites/ship1/ship15.ppm create mode 100644 kspaceduel/sprites/ship1/ship16.pbm create mode 100644 kspaceduel/sprites/ship1/ship16.ppm create mode 100644 kspaceduel/sprites/ship1/ship17.pbm create mode 100644 kspaceduel/sprites/ship1/ship17.ppm create mode 100644 kspaceduel/sprites/ship1/ship18.pbm create mode 100644 kspaceduel/sprites/ship1/ship18.ppm create mode 100644 kspaceduel/sprites/ship1/ship19.pbm create mode 100644 kspaceduel/sprites/ship1/ship19.ppm create mode 100644 kspaceduel/sprites/ship1/ship20.pbm create mode 100644 kspaceduel/sprites/ship1/ship20.ppm create mode 100644 kspaceduel/sprites/ship1/ship21.pbm create mode 100644 kspaceduel/sprites/ship1/ship21.ppm create mode 100644 kspaceduel/sprites/ship1/ship22.pbm create mode 100644 kspaceduel/sprites/ship1/ship22.ppm create mode 100644 kspaceduel/sprites/ship1/ship23.pbm create mode 100644 kspaceduel/sprites/ship1/ship23.ppm create mode 100644 kspaceduel/sprites/ship1/ship24.pbm create mode 100644 kspaceduel/sprites/ship1/ship24.ppm create mode 100644 kspaceduel/sprites/ship1/ship25.pbm create mode 100644 kspaceduel/sprites/ship1/ship25.ppm create mode 100644 kspaceduel/sprites/ship1/ship26.pbm create mode 100644 kspaceduel/sprites/ship1/ship26.ppm create mode 100644 kspaceduel/sprites/ship1/ship27.pbm create mode 100644 kspaceduel/sprites/ship1/ship27.ppm create mode 100644 kspaceduel/sprites/ship1/ship28.pbm create mode 100644 kspaceduel/sprites/ship1/ship28.ppm create mode 100644 kspaceduel/sprites/ship1/ship29.pbm create mode 100644 kspaceduel/sprites/ship1/ship29.ppm create mode 100644 kspaceduel/sprites/ship1/ship30.pbm create mode 100644 kspaceduel/sprites/ship1/ship30.ppm create mode 100644 kspaceduel/sprites/ship1/ship31.pbm create mode 100644 kspaceduel/sprites/ship1/ship31.ppm create mode 100644 kspaceduel/sprites/ship1/ship32.pbm create mode 100644 kspaceduel/sprites/ship1/ship32.ppm create mode 100644 kspaceduel/sprites/ship1/ship33.pbm create mode 100644 kspaceduel/sprites/ship1/ship33.ppm create mode 100644 kspaceduel/sprites/ship1/ship34.pbm create mode 100644 kspaceduel/sprites/ship1/ship34.ppm create mode 100644 kspaceduel/sprites/ship1/ship35.pbm create mode 100644 kspaceduel/sprites/ship1/ship35.ppm create mode 100644 kspaceduel/sprites/ship1/ship36.pbm create mode 100644 kspaceduel/sprites/ship1/ship36.ppm create mode 100644 kspaceduel/sprites/ship1/ship37.pbm create mode 100644 kspaceduel/sprites/ship1/ship37.ppm create mode 100644 kspaceduel/sprites/ship1/ship38.pbm create mode 100644 kspaceduel/sprites/ship1/ship38.ppm create mode 100644 kspaceduel/sprites/ship1/ship39.pbm create mode 100644 kspaceduel/sprites/ship1/ship39.ppm create mode 100644 kspaceduel/sprites/ship1/ship40.pbm create mode 100644 kspaceduel/sprites/ship1/ship40.ppm create mode 100644 kspaceduel/sprites/ship1/ship41.pbm create mode 100644 kspaceduel/sprites/ship1/ship41.ppm create mode 100644 kspaceduel/sprites/ship1/ship42.pbm create mode 100644 kspaceduel/sprites/ship1/ship42.ppm create mode 100644 kspaceduel/sprites/ship1/ship43.pbm create mode 100644 kspaceduel/sprites/ship1/ship43.ppm create mode 100644 kspaceduel/sprites/ship1/ship44.pbm create mode 100644 kspaceduel/sprites/ship1/ship44.ppm create mode 100644 kspaceduel/sprites/ship1/ship45.pbm create mode 100644 kspaceduel/sprites/ship1/ship45.ppm create mode 100644 kspaceduel/sprites/ship1/ship46.pbm create mode 100644 kspaceduel/sprites/ship1/ship46.ppm create mode 100644 kspaceduel/sprites/ship1/ship47.pbm create mode 100644 kspaceduel/sprites/ship1/ship47.ppm create mode 100644 kspaceduel/sprites/ship1/ship48.pbm create mode 100644 kspaceduel/sprites/ship1/ship48.ppm create mode 100644 kspaceduel/sprites/ship1/ship49.pbm create mode 100644 kspaceduel/sprites/ship1/ship49.ppm create mode 100644 kspaceduel/sprites/ship1/ship50.pbm create mode 100644 kspaceduel/sprites/ship1/ship50.ppm create mode 100644 kspaceduel/sprites/ship1/ship51.pbm create mode 100644 kspaceduel/sprites/ship1/ship51.ppm create mode 100644 kspaceduel/sprites/ship1/ship52.pbm create mode 100644 kspaceduel/sprites/ship1/ship52.ppm create mode 100644 kspaceduel/sprites/ship1/ship53.pbm create mode 100644 kspaceduel/sprites/ship1/ship53.ppm create mode 100644 kspaceduel/sprites/ship1/ship54.pbm create mode 100644 kspaceduel/sprites/ship1/ship54.ppm create mode 100644 kspaceduel/sprites/ship1/ship55.pbm create mode 100644 kspaceduel/sprites/ship1/ship55.ppm create mode 100644 kspaceduel/sprites/ship1/ship56.pbm create mode 100644 kspaceduel/sprites/ship1/ship56.ppm create mode 100644 kspaceduel/sprites/ship1/ship57.pbm create mode 100644 kspaceduel/sprites/ship1/ship57.ppm create mode 100644 kspaceduel/sprites/ship1/ship58.pbm create mode 100644 kspaceduel/sprites/ship1/ship58.ppm create mode 100644 kspaceduel/sprites/ship1/ship59.pbm create mode 100644 kspaceduel/sprites/ship1/ship59.ppm create mode 100644 kspaceduel/sprites/ship1/ship60.pbm create mode 100644 kspaceduel/sprites/ship1/ship60.ppm create mode 100644 kspaceduel/sprites/ship1/ship61.pbm create mode 100644 kspaceduel/sprites/ship1/ship61.ppm create mode 100644 kspaceduel/sprites/ship1/ship62.pbm create mode 100644 kspaceduel/sprites/ship1/ship62.ppm create mode 100644 kspaceduel/sprites/ship1/ship63.pbm create mode 100644 kspaceduel/sprites/ship1/ship63.ppm create mode 100644 kspaceduel/sprites/ship1/ship64.pbm create mode 100644 kspaceduel/sprites/ship1/ship64.ppm create mode 100644 kspaceduel/sprites/ship2/Makefile.am create mode 100644 kspaceduel/sprites/ship2/bullet.pbm create mode 100644 kspaceduel/sprites/ship2/bullet.ppm create mode 100644 kspaceduel/sprites/ship2/mine0.pbm create mode 100644 kspaceduel/sprites/ship2/mine0.ppm create mode 100644 kspaceduel/sprites/ship2/mine1.pbm create mode 100644 kspaceduel/sprites/ship2/mine1.ppm create mode 100644 kspaceduel/sprites/ship2/ship00.pbm create mode 100644 kspaceduel/sprites/ship2/ship00.ppm create mode 100644 kspaceduel/sprites/ship2/ship01.pbm create mode 100644 kspaceduel/sprites/ship2/ship01.ppm create mode 100644 kspaceduel/sprites/ship2/ship02.pbm create mode 100644 kspaceduel/sprites/ship2/ship02.ppm create mode 100644 kspaceduel/sprites/ship2/ship03.pbm create mode 100644 kspaceduel/sprites/ship2/ship03.ppm create mode 100644 kspaceduel/sprites/ship2/ship04.pbm create mode 100644 kspaceduel/sprites/ship2/ship04.ppm create mode 100644 kspaceduel/sprites/ship2/ship05.pbm create mode 100644 kspaceduel/sprites/ship2/ship05.ppm create mode 100644 kspaceduel/sprites/ship2/ship06.pbm create mode 100644 kspaceduel/sprites/ship2/ship06.ppm create mode 100644 kspaceduel/sprites/ship2/ship07.pbm create mode 100644 kspaceduel/sprites/ship2/ship07.ppm create mode 100644 kspaceduel/sprites/ship2/ship08.pbm create mode 100644 kspaceduel/sprites/ship2/ship08.ppm create mode 100644 kspaceduel/sprites/ship2/ship09.pbm create mode 100644 kspaceduel/sprites/ship2/ship09.ppm create mode 100644 kspaceduel/sprites/ship2/ship10.pbm create mode 100644 kspaceduel/sprites/ship2/ship10.ppm create mode 100644 kspaceduel/sprites/ship2/ship11.pbm create mode 100644 kspaceduel/sprites/ship2/ship11.ppm create mode 100644 kspaceduel/sprites/ship2/ship12.pbm create mode 100644 kspaceduel/sprites/ship2/ship12.ppm create mode 100644 kspaceduel/sprites/ship2/ship13.pbm create mode 100644 kspaceduel/sprites/ship2/ship13.ppm create mode 100644 kspaceduel/sprites/ship2/ship14.pbm create mode 100644 kspaceduel/sprites/ship2/ship14.ppm create mode 100644 kspaceduel/sprites/ship2/ship15.pbm create mode 100644 kspaceduel/sprites/ship2/ship15.ppm create mode 100644 kspaceduel/sprites/ship2/ship16.pbm create mode 100644 kspaceduel/sprites/ship2/ship16.ppm create mode 100644 kspaceduel/sprites/ship2/ship17.pbm create mode 100644 kspaceduel/sprites/ship2/ship17.ppm create mode 100644 kspaceduel/sprites/ship2/ship18.pbm create mode 100644 kspaceduel/sprites/ship2/ship18.ppm create mode 100644 kspaceduel/sprites/ship2/ship19.pbm create mode 100644 kspaceduel/sprites/ship2/ship19.ppm create mode 100644 kspaceduel/sprites/ship2/ship20.pbm create mode 100644 kspaceduel/sprites/ship2/ship20.ppm create mode 100644 kspaceduel/sprites/ship2/ship21.pbm create mode 100644 kspaceduel/sprites/ship2/ship21.ppm create mode 100644 kspaceduel/sprites/ship2/ship22.pbm create mode 100644 kspaceduel/sprites/ship2/ship22.ppm create mode 100644 kspaceduel/sprites/ship2/ship23.pbm create mode 100644 kspaceduel/sprites/ship2/ship23.ppm create mode 100644 kspaceduel/sprites/ship2/ship24.pbm create mode 100644 kspaceduel/sprites/ship2/ship24.ppm create mode 100644 kspaceduel/sprites/ship2/ship25.pbm create mode 100644 kspaceduel/sprites/ship2/ship25.ppm create mode 100644 kspaceduel/sprites/ship2/ship26.pbm create mode 100644 kspaceduel/sprites/ship2/ship26.ppm create mode 100644 kspaceduel/sprites/ship2/ship27.pbm create mode 100644 kspaceduel/sprites/ship2/ship27.ppm create mode 100644 kspaceduel/sprites/ship2/ship28.pbm create mode 100644 kspaceduel/sprites/ship2/ship28.ppm create mode 100644 kspaceduel/sprites/ship2/ship29.pbm create mode 100644 kspaceduel/sprites/ship2/ship29.ppm create mode 100644 kspaceduel/sprites/ship2/ship30.pbm create mode 100644 kspaceduel/sprites/ship2/ship30.ppm create mode 100644 kspaceduel/sprites/ship2/ship31.pbm create mode 100644 kspaceduel/sprites/ship2/ship31.ppm create mode 100644 kspaceduel/sprites/ship2/ship32.pbm create mode 100644 kspaceduel/sprites/ship2/ship32.ppm create mode 100644 kspaceduel/sprites/ship2/ship33.pbm create mode 100644 kspaceduel/sprites/ship2/ship33.ppm create mode 100644 kspaceduel/sprites/ship2/ship34.pbm create mode 100644 kspaceduel/sprites/ship2/ship34.ppm create mode 100644 kspaceduel/sprites/ship2/ship35.pbm create mode 100644 kspaceduel/sprites/ship2/ship35.ppm create mode 100644 kspaceduel/sprites/ship2/ship36.pbm create mode 100644 kspaceduel/sprites/ship2/ship36.ppm create mode 100644 kspaceduel/sprites/ship2/ship37.pbm create mode 100644 kspaceduel/sprites/ship2/ship37.ppm create mode 100644 kspaceduel/sprites/ship2/ship38.pbm create mode 100644 kspaceduel/sprites/ship2/ship38.ppm create mode 100644 kspaceduel/sprites/ship2/ship39.pbm create mode 100644 kspaceduel/sprites/ship2/ship39.ppm create mode 100644 kspaceduel/sprites/ship2/ship40.pbm create mode 100644 kspaceduel/sprites/ship2/ship40.ppm create mode 100644 kspaceduel/sprites/ship2/ship41.pbm create mode 100644 kspaceduel/sprites/ship2/ship41.ppm create mode 100644 kspaceduel/sprites/ship2/ship42.pbm create mode 100644 kspaceduel/sprites/ship2/ship42.ppm create mode 100644 kspaceduel/sprites/ship2/ship43.pbm create mode 100644 kspaceduel/sprites/ship2/ship43.ppm create mode 100644 kspaceduel/sprites/ship2/ship44.pbm create mode 100644 kspaceduel/sprites/ship2/ship44.ppm create mode 100644 kspaceduel/sprites/ship2/ship45.pbm create mode 100644 kspaceduel/sprites/ship2/ship45.ppm create mode 100644 kspaceduel/sprites/ship2/ship46.pbm create mode 100644 kspaceduel/sprites/ship2/ship46.ppm create mode 100644 kspaceduel/sprites/ship2/ship47.pbm create mode 100644 kspaceduel/sprites/ship2/ship47.ppm create mode 100644 kspaceduel/sprites/ship2/ship48.pbm create mode 100644 kspaceduel/sprites/ship2/ship48.ppm create mode 100644 kspaceduel/sprites/ship2/ship49.pbm create mode 100644 kspaceduel/sprites/ship2/ship49.ppm create mode 100644 kspaceduel/sprites/ship2/ship50.pbm create mode 100644 kspaceduel/sprites/ship2/ship50.ppm create mode 100644 kspaceduel/sprites/ship2/ship51.pbm create mode 100644 kspaceduel/sprites/ship2/ship51.ppm create mode 100644 kspaceduel/sprites/ship2/ship52.pbm create mode 100644 kspaceduel/sprites/ship2/ship52.ppm create mode 100644 kspaceduel/sprites/ship2/ship53.pbm create mode 100644 kspaceduel/sprites/ship2/ship53.ppm create mode 100644 kspaceduel/sprites/ship2/ship54.pbm create mode 100644 kspaceduel/sprites/ship2/ship54.ppm create mode 100644 kspaceduel/sprites/ship2/ship55.pbm create mode 100644 kspaceduel/sprites/ship2/ship55.ppm create mode 100644 kspaceduel/sprites/ship2/ship56.pbm create mode 100644 kspaceduel/sprites/ship2/ship56.ppm create mode 100644 kspaceduel/sprites/ship2/ship57.pbm create mode 100644 kspaceduel/sprites/ship2/ship57.ppm create mode 100644 kspaceduel/sprites/ship2/ship58.pbm create mode 100644 kspaceduel/sprites/ship2/ship58.ppm create mode 100644 kspaceduel/sprites/ship2/ship59.pbm create mode 100644 kspaceduel/sprites/ship2/ship59.ppm create mode 100644 kspaceduel/sprites/ship2/ship60.pbm create mode 100644 kspaceduel/sprites/ship2/ship60.ppm create mode 100644 kspaceduel/sprites/ship2/ship61.pbm create mode 100644 kspaceduel/sprites/ship2/ship61.ppm create mode 100644 kspaceduel/sprites/ship2/ship62.pbm create mode 100644 kspaceduel/sprites/ship2/ship62.ppm create mode 100644 kspaceduel/sprites/ship2/ship63.pbm create mode 100644 kspaceduel/sprites/ship2/ship63.ppm create mode 100644 kspaceduel/sprites/ship2/ship64.pbm create mode 100644 kspaceduel/sprites/ship2/ship64.ppm create mode 100644 kspaceduel/sprites/sun/Makefile.am create mode 100644 kspaceduel/sprites/sun/sun.pbm create mode 100644 kspaceduel/sprites/sun/sun.ppm create mode 100644 kspaceduel/structs.h create mode 100644 kspaceduel/topwidget.cpp create mode 100644 kspaceduel/topwidget.h create mode 100644 kspaceduel/version.h create mode 100644 ktron/AUTHORS create mode 100644 ktron/ChangeLog create mode 100644 ktron/Makefile.am create mode 100644 ktron/README create mode 100644 ktron/TODO create mode 100644 ktron/ai.ui create mode 100644 ktron/appearance.ui create mode 100644 ktron/general.ui create mode 100644 ktron/ktron.cpp create mode 100644 ktron/ktron.desktop create mode 100644 ktron/ktron.h create mode 100644 ktron/ktron.kcfg create mode 100644 ktron/ktronui.rc create mode 100644 ktron/main.cpp create mode 100644 ktron/pics/Makefile.am create mode 100644 ktron/pics/hi128-app-ktron.png create mode 100644 ktron/pics/hi16-app-ktron.png create mode 100644 ktron/pics/hi22-app-ktron.png create mode 100644 ktron/pics/hi32-app-ktron.png create mode 100644 ktron/pics/hi48-app-ktron.png create mode 100644 ktron/pics/hi64-app-ktron.png create mode 100644 ktron/player.cpp create mode 100644 ktron/player.h create mode 100644 ktron/settings.kcfgc create mode 100644 ktron/tron.cpp create mode 100644 ktron/tron.h create mode 100644 ktron/version.h create mode 100644 ktuberling/Makefile.am create mode 100644 ktuberling/TODO create mode 100644 ktuberling/action.cpp create mode 100644 ktuberling/action.h create mode 100644 ktuberling/hi128-app-ktuberling.png create mode 100644 ktuberling/hi16-app-ktuberling.png create mode 100644 ktuberling/hi22-app-ktuberling.png create mode 100644 ktuberling/hi32-app-ktuberling.png create mode 100644 ktuberling/hi48-app-ktuberling.png create mode 100644 ktuberling/hi64-app-ktuberling.png create mode 100644 ktuberling/ktuberling.desktop create mode 100644 ktuberling/ktuberlingui.rc create mode 100644 ktuberling/main.cpp create mode 100644 ktuberling/museum/Makefile.am create mode 100644 ktuberling/museum/businessman.tuberling create mode 100644 ktuberling/museum/cool.tuberling create mode 100644 ktuberling/museum/crazy.tuberling create mode 100644 ktuberling/museum/cyclop.tuberling create mode 100644 ktuberling/museum/dali.tuberling create mode 100644 ktuberling/museum/einstein.tuberling create mode 100644 ktuberling/museum/fly.tuberling create mode 100644 ktuberling/museum/grandpa.tuberling create mode 100644 ktuberling/museum/happy.tuberling create mode 100644 ktuberling/museum/hippie.tuberling create mode 100644 ktuberling/museum/idiot.tuberling create mode 100644 ktuberling/museum/miss.tuberling create mode 100644 ktuberling/museum/mouse.tuberling create mode 100644 ktuberling/museum/picasso.tuberling create mode 100644 ktuberling/museum/sea.tuberling create mode 100644 ktuberling/museum/serious.tuberling create mode 100644 ktuberling/pics/Makefile.am create mode 100644 ktuberling/pics/aquarium-game.png create mode 100644 ktuberling/pics/aquarium-mask.png create mode 100644 ktuberling/pics/layout.i18n create mode 100644 ktuberling/pics/layout.xml create mode 100644 ktuberling/pics/penguin-game.png create mode 100644 ktuberling/pics/penguin-mask.png create mode 100644 ktuberling/pics/potato-game.png create mode 100644 ktuberling/pics/potato-mask.png create mode 100644 ktuberling/playground.cpp create mode 100644 ktuberling/playground.h create mode 100644 ktuberling/soundfactory.cpp create mode 100644 ktuberling/soundfactory.h create mode 100644 ktuberling/sounds/Makefile.am create mode 100644 ktuberling/sounds/NO_TRANSLATIONS_HERE create mode 100644 ktuberling/sounds/badge.wav create mode 100644 ktuberling/sounds/bow.wav create mode 100644 ktuberling/sounds/cigar.wav create mode 100644 ktuberling/sounds/ear.wav create mode 100644 ktuberling/sounds/earring.wav create mode 100644 ktuberling/sounds/eye.wav create mode 100644 ktuberling/sounds/eyebrow.wav create mode 100644 ktuberling/sounds/hat.wav create mode 100644 ktuberling/sounds/moustache.wav create mode 100644 ktuberling/sounds/mouth.wav create mode 100644 ktuberling/sounds/nose.wav create mode 100644 ktuberling/sounds/spectacles.wav create mode 100644 ktuberling/sounds/sunglasses.wav create mode 100644 ktuberling/sounds/tuberling.wav create mode 100644 ktuberling/sounds/watch.wav create mode 100644 ktuberling/todraw.cpp create mode 100644 ktuberling/todraw.h create mode 100644 ktuberling/toplevel.cpp create mode 100644 ktuberling/toplevel.h create mode 100644 ktuberling/x-tuberling.desktop create mode 100644 kwin4/AUTHORS create mode 100644 kwin4/COPYING create mode 100644 kwin4/INSTALL create mode 100644 kwin4/Makefile.am create mode 100644 kwin4/README create mode 100644 kwin4/grafix/Makefile.am create mode 100644 kwin4/grafix/default/aboute.png create mode 100644 kwin4/grafix/default/arrow0.png create mode 100644 kwin4/grafix/default/arrow1.png create mode 100644 kwin4/grafix/default/arrow2.png create mode 100644 kwin4/grafix/default/background.png create mode 100644 kwin4/grafix/default/board.png create mode 100644 kwin4/grafix/default/crnt.png create mode 100644 kwin4/grafix/default/empty.png create mode 100644 kwin4/grafix/default/empty2.png create mode 100644 kwin4/grafix/default/game_over.png create mode 100644 kwin4/grafix/default/grafix.rc create mode 100644 kwin4/grafix/default/introabout.png create mode 100644 kwin4/grafix/default/piece0.png create mode 100644 kwin4/grafix/default/piece1.png create mode 100644 kwin4/grafix/default/star0.png create mode 100644 kwin4/grafix/default/star1.png create mode 100644 kwin4/grafix/default/star2.png create mode 100644 kwin4/grafix/default/star3.png create mode 100644 kwin4/grafix/default/star4.png create mode 100644 kwin4/hi128-app-kwin4.png create mode 100644 kwin4/hi16-app-kwin4.png create mode 100644 kwin4/hi22-app-kwin4.png create mode 100644 kwin4/hi32-app-kwin4.png create mode 100644 kwin4/hi48-app-kwin4.png create mode 100644 kwin4/hi64-app-kwin4.png create mode 100644 kwin4/index.html create mode 100755 kwin4/install-sh create mode 100644 kwin4/kwin4.desktop create mode 100644 kwin4/kwin4.kdelnk create mode 100644 kwin4/kwin4.lsm create mode 100644 kwin4/kwin4/AboutDlg.kdevdlg create mode 100644 kwin4/kwin4/Makefile.am create mode 100644 kwin4/kwin4/kspritecache.cpp create mode 100644 kwin4/kwin4/kspritecache.h create mode 100644 kwin4/kwin4/kwin4.cpp create mode 100644 kwin4/kwin4/kwin4.h create mode 100644 kwin4/kwin4/kwin4.kcfg create mode 100644 kwin4/kwin4/kwin4doc.cpp create mode 100644 kwin4/kwin4/kwin4doc.h create mode 100644 kwin4/kwin4/kwin4player.cpp create mode 100644 kwin4/kwin4/kwin4player.h create mode 100644 kwin4/kwin4/kwin4proc.cpp create mode 100644 kwin4/kwin4/kwin4proc.h create mode 100644 kwin4/kwin4/kwin4ui.rc create mode 100644 kwin4/kwin4/kwin4view.cpp create mode 100644 kwin4/kwin4/kwin4view.h create mode 100644 kwin4/kwin4/main.cpp create mode 100644 kwin4/kwin4/prefs.kcfgc create mode 100644 kwin4/kwin4/scorewidget.cpp create mode 100644 kwin4/kwin4/scorewidget.h create mode 100644 kwin4/kwin4/settings.ui create mode 100644 kwin4/kwin4/statistics.ui create mode 100644 kwin4/kwin4/statuswidget.ui create mode 100644 libkdegames/Makefile.am create mode 100644 libkdegames/README create mode 100644 libkdegames/TODO create mode 100644 libkdegames/carddecks/Makefile.am create mode 100644 libkdegames/carddecks/README create mode 100644 libkdegames/carddecks/cards-aisleriot/1.png create mode 100644 libkdegames/carddecks/cards-aisleriot/10.png create mode 100644 libkdegames/carddecks/cards-aisleriot/11.png create mode 100644 libkdegames/carddecks/cards-aisleriot/12.png create mode 100644 libkdegames/carddecks/cards-aisleriot/13.png create mode 100644 libkdegames/carddecks/cards-aisleriot/14.png create mode 100644 libkdegames/carddecks/cards-aisleriot/15.png create mode 100644 libkdegames/carddecks/cards-aisleriot/16.png create mode 100644 libkdegames/carddecks/cards-aisleriot/17.png create mode 100644 libkdegames/carddecks/cards-aisleriot/18.png create mode 100644 libkdegames/carddecks/cards-aisleriot/19.png create mode 100644 libkdegames/carddecks/cards-aisleriot/2.png create mode 100644 libkdegames/carddecks/cards-aisleriot/20.png create mode 100644 libkdegames/carddecks/cards-aisleriot/21.png create mode 100644 libkdegames/carddecks/cards-aisleriot/22.png create mode 100644 libkdegames/carddecks/cards-aisleriot/23.png create mode 100644 libkdegames/carddecks/cards-aisleriot/24.png create mode 100644 libkdegames/carddecks/cards-aisleriot/25.png create mode 100644 libkdegames/carddecks/cards-aisleriot/26.png create mode 100644 libkdegames/carddecks/cards-aisleriot/27.png create mode 100644 libkdegames/carddecks/cards-aisleriot/28.png create mode 100644 libkdegames/carddecks/cards-aisleriot/29.png create mode 100644 libkdegames/carddecks/cards-aisleriot/3.png create mode 100644 libkdegames/carddecks/cards-aisleriot/30.png create mode 100644 libkdegames/carddecks/cards-aisleriot/31.png create mode 100644 libkdegames/carddecks/cards-aisleriot/32.png create mode 100644 libkdegames/carddecks/cards-aisleriot/33.png create mode 100644 libkdegames/carddecks/cards-aisleriot/34.png create mode 100644 libkdegames/carddecks/cards-aisleriot/35.png create mode 100644 libkdegames/carddecks/cards-aisleriot/36.png create mode 100644 libkdegames/carddecks/cards-aisleriot/37.png create mode 100644 libkdegames/carddecks/cards-aisleriot/38.png create mode 100644 libkdegames/carddecks/cards-aisleriot/39.png create mode 100644 libkdegames/carddecks/cards-aisleriot/4.png create mode 100644 libkdegames/carddecks/cards-aisleriot/40.png create mode 100644 libkdegames/carddecks/cards-aisleriot/41.png create mode 100644 libkdegames/carddecks/cards-aisleriot/42.png create mode 100644 libkdegames/carddecks/cards-aisleriot/43.png create mode 100644 libkdegames/carddecks/cards-aisleriot/44.png create mode 100644 libkdegames/carddecks/cards-aisleriot/45.png create mode 100644 libkdegames/carddecks/cards-aisleriot/46.png create mode 100644 libkdegames/carddecks/cards-aisleriot/47.png create mode 100644 libkdegames/carddecks/cards-aisleriot/48.png create mode 100644 libkdegames/carddecks/cards-aisleriot/49.png create mode 100644 libkdegames/carddecks/cards-aisleriot/5.png create mode 100644 libkdegames/carddecks/cards-aisleriot/50.png create mode 100644 libkdegames/carddecks/cards-aisleriot/51.png create mode 100644 libkdegames/carddecks/cards-aisleriot/52.png create mode 100644 libkdegames/carddecks/cards-aisleriot/6.png create mode 100644 libkdegames/carddecks/cards-aisleriot/7.png create mode 100644 libkdegames/carddecks/cards-aisleriot/8.png create mode 100644 libkdegames/carddecks/cards-aisleriot/9.png create mode 100644 libkdegames/carddecks/cards-aisleriot/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-aisleriot/index.desktop create mode 100644 libkdegames/carddecks/cards-default/1.png create mode 100644 libkdegames/carddecks/cards-default/10.png create mode 100644 libkdegames/carddecks/cards-default/11.png create mode 100644 libkdegames/carddecks/cards-default/12.png create mode 100644 libkdegames/carddecks/cards-default/13.png create mode 100644 libkdegames/carddecks/cards-default/14.png create mode 100644 libkdegames/carddecks/cards-default/15.png create mode 100644 libkdegames/carddecks/cards-default/16.png create mode 100644 libkdegames/carddecks/cards-default/17.png create mode 100644 libkdegames/carddecks/cards-default/18.png create mode 100644 libkdegames/carddecks/cards-default/19.png create mode 100644 libkdegames/carddecks/cards-default/2.png create mode 100644 libkdegames/carddecks/cards-default/20.png create mode 100644 libkdegames/carddecks/cards-default/21.png create mode 100644 libkdegames/carddecks/cards-default/22.png create mode 100644 libkdegames/carddecks/cards-default/23.png create mode 100644 libkdegames/carddecks/cards-default/24.png create mode 100644 libkdegames/carddecks/cards-default/25.png create mode 100644 libkdegames/carddecks/cards-default/26.png create mode 100644 libkdegames/carddecks/cards-default/27.png create mode 100644 libkdegames/carddecks/cards-default/28.png create mode 100644 libkdegames/carddecks/cards-default/29.png create mode 100644 libkdegames/carddecks/cards-default/3.png create mode 100644 libkdegames/carddecks/cards-default/30.png create mode 100644 libkdegames/carddecks/cards-default/31.png create mode 100644 libkdegames/carddecks/cards-default/32.png create mode 100644 libkdegames/carddecks/cards-default/33.png create mode 100644 libkdegames/carddecks/cards-default/34.png create mode 100644 libkdegames/carddecks/cards-default/35.png create mode 100644 libkdegames/carddecks/cards-default/36.png create mode 100644 libkdegames/carddecks/cards-default/37.png create mode 100644 libkdegames/carddecks/cards-default/38.png create mode 100644 libkdegames/carddecks/cards-default/39.png create mode 100644 libkdegames/carddecks/cards-default/4.png create mode 100644 libkdegames/carddecks/cards-default/40.png create mode 100644 libkdegames/carddecks/cards-default/41.png create mode 100644 libkdegames/carddecks/cards-default/42.png create mode 100644 libkdegames/carddecks/cards-default/43.png create mode 100644 libkdegames/carddecks/cards-default/44.png create mode 100644 libkdegames/carddecks/cards-default/45.png create mode 100644 libkdegames/carddecks/cards-default/46.png create mode 100644 libkdegames/carddecks/cards-default/47.png create mode 100644 libkdegames/carddecks/cards-default/48.png create mode 100644 libkdegames/carddecks/cards-default/49.png create mode 100644 libkdegames/carddecks/cards-default/5.png create mode 100644 libkdegames/carddecks/cards-default/50.png create mode 100644 libkdegames/carddecks/cards-default/51.png create mode 100644 libkdegames/carddecks/cards-default/52.png create mode 100644 libkdegames/carddecks/cards-default/6.png create mode 100644 libkdegames/carddecks/cards-default/7.png create mode 100644 libkdegames/carddecks/cards-default/8.png create mode 100644 libkdegames/carddecks/cards-default/9.png create mode 100644 libkdegames/carddecks/cards-default/index.desktop create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/1.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/10.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/11.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/12.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/13.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/14.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/15.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/16.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/17.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/18.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/19.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/2.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/20.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/21.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/22.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/23.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/24.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/25.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/26.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/27.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/28.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/29.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/3.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/30.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/31.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/32.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/33.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/34.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/35.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/36.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/37.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/38.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/39.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/4.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/40.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/41.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/42.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/43.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/44.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/45.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/46.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/47.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/48.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/49.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/5.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/50.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/51.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/52.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/6.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/7.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/8.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/9.png create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-dondorf-whist-b/index.desktop create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/1.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/10.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/11.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/12.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/13.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/14.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/15.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/16.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/17.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/18.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/19.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/2.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/20.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/21.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/22.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/23.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/24.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/25.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/26.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/27.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/28.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/29.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/3.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/30.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/31.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/32.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/33.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/34.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/35.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/36.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/37.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/38.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/39.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/4.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/40.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/41.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/42.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/43.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/44.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/45.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/46.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/47.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/48.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/49.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/5.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/50.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/51.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/52.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/6.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/7.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/8.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/9.png create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-gdkcard-bonded/index.desktop create mode 100644 libkdegames/carddecks/cards-hard-a-port/1.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/10.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/11.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/12.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/13.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/14.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/15.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/16.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/17.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/18.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/19.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/2.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/20.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/21.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/22.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/23.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/24.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/25.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/26.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/27.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/28.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/29.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/3.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/30.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/31.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/32.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/33.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/34.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/35.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/36.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/37.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/38.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/39.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/4.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/40.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/41.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/42.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/43.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/44.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/45.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/46.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/47.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/48.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/49.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/5.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/50.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/51.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/52.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/6.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/7.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/8.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/9.png create mode 100644 libkdegames/carddecks/cards-hard-a-port/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-hard-a-port/index.desktop create mode 100644 libkdegames/carddecks/cards-konqi-modern/1.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/10.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/11.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/12.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/13.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/14.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/15.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/16.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/17.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/18.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/19.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/2.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/20.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/21.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/22.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/23.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/24.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/25.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/26.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/27.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/28.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/29.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/3.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/30.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/31.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/32.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/33.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/34.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/35.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/36.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/37.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/38.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/39.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/4.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/40.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/41.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/42.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/43.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/44.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/45.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/46.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/47.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/48.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/49.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/5.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/50.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/51.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/52.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/6.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/7.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/8.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/9.png create mode 100644 libkdegames/carddecks/cards-konqi-modern/index.desktop create mode 100644 libkdegames/carddecks/cards-penguins/1.png create mode 100644 libkdegames/carddecks/cards-penguins/10.png create mode 100644 libkdegames/carddecks/cards-penguins/11.png create mode 100644 libkdegames/carddecks/cards-penguins/12.png create mode 100644 libkdegames/carddecks/cards-penguins/13.png create mode 100644 libkdegames/carddecks/cards-penguins/14.png create mode 100644 libkdegames/carddecks/cards-penguins/15.png create mode 100644 libkdegames/carddecks/cards-penguins/16.png create mode 100644 libkdegames/carddecks/cards-penguins/17.png create mode 100644 libkdegames/carddecks/cards-penguins/18.png create mode 100644 libkdegames/carddecks/cards-penguins/19.png create mode 100644 libkdegames/carddecks/cards-penguins/2.png create mode 100644 libkdegames/carddecks/cards-penguins/20.png create mode 100644 libkdegames/carddecks/cards-penguins/21.png create mode 100644 libkdegames/carddecks/cards-penguins/22.png create mode 100644 libkdegames/carddecks/cards-penguins/23.png create mode 100644 libkdegames/carddecks/cards-penguins/24.png create mode 100644 libkdegames/carddecks/cards-penguins/25.png create mode 100644 libkdegames/carddecks/cards-penguins/26.png create mode 100644 libkdegames/carddecks/cards-penguins/27.png create mode 100644 libkdegames/carddecks/cards-penguins/28.png create mode 100644 libkdegames/carddecks/cards-penguins/29.png create mode 100644 libkdegames/carddecks/cards-penguins/3.png create mode 100644 libkdegames/carddecks/cards-penguins/30.png create mode 100644 libkdegames/carddecks/cards-penguins/31.png create mode 100644 libkdegames/carddecks/cards-penguins/32.png create mode 100644 libkdegames/carddecks/cards-penguins/33.png create mode 100644 libkdegames/carddecks/cards-penguins/34.png create mode 100644 libkdegames/carddecks/cards-penguins/35.png create mode 100644 libkdegames/carddecks/cards-penguins/36.png create mode 100644 libkdegames/carddecks/cards-penguins/37.png create mode 100644 libkdegames/carddecks/cards-penguins/38.png create mode 100644 libkdegames/carddecks/cards-penguins/39.png create mode 100644 libkdegames/carddecks/cards-penguins/4.png create mode 100644 libkdegames/carddecks/cards-penguins/40.png create mode 100644 libkdegames/carddecks/cards-penguins/41.png create mode 100644 libkdegames/carddecks/cards-penguins/42.png create mode 100644 libkdegames/carddecks/cards-penguins/43.png create mode 100644 libkdegames/carddecks/cards-penguins/44.png create mode 100644 libkdegames/carddecks/cards-penguins/45.png create mode 100644 libkdegames/carddecks/cards-penguins/46.png create mode 100644 libkdegames/carddecks/cards-penguins/47.png create mode 100644 libkdegames/carddecks/cards-penguins/48.png create mode 100644 libkdegames/carddecks/cards-penguins/49.png create mode 100644 libkdegames/carddecks/cards-penguins/5.png create mode 100644 libkdegames/carddecks/cards-penguins/50.png create mode 100644 libkdegames/carddecks/cards-penguins/51.png create mode 100644 libkdegames/carddecks/cards-penguins/52.png create mode 100644 libkdegames/carddecks/cards-penguins/6.png create mode 100644 libkdegames/carddecks/cards-penguins/7.png create mode 100644 libkdegames/carddecks/cards-penguins/8.png create mode 100644 libkdegames/carddecks/cards-penguins/9.png create mode 100644 libkdegames/carddecks/cards-penguins/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-penguins/index.desktop create mode 100644 libkdegames/carddecks/cards-spaced/1.png create mode 100644 libkdegames/carddecks/cards-spaced/10.png create mode 100644 libkdegames/carddecks/cards-spaced/11.png create mode 100644 libkdegames/carddecks/cards-spaced/12.png create mode 100644 libkdegames/carddecks/cards-spaced/13.png create mode 100644 libkdegames/carddecks/cards-spaced/14.png create mode 100644 libkdegames/carddecks/cards-spaced/15.png create mode 100644 libkdegames/carddecks/cards-spaced/16.png create mode 100644 libkdegames/carddecks/cards-spaced/17.png create mode 100644 libkdegames/carddecks/cards-spaced/18.png create mode 100644 libkdegames/carddecks/cards-spaced/19.png create mode 100644 libkdegames/carddecks/cards-spaced/2.png create mode 100644 libkdegames/carddecks/cards-spaced/20.png create mode 100644 libkdegames/carddecks/cards-spaced/21.png create mode 100644 libkdegames/carddecks/cards-spaced/22.png create mode 100644 libkdegames/carddecks/cards-spaced/23.png create mode 100644 libkdegames/carddecks/cards-spaced/24.png create mode 100644 libkdegames/carddecks/cards-spaced/25.png create mode 100644 libkdegames/carddecks/cards-spaced/26.png create mode 100644 libkdegames/carddecks/cards-spaced/27.png create mode 100644 libkdegames/carddecks/cards-spaced/28.png create mode 100644 libkdegames/carddecks/cards-spaced/29.png create mode 100644 libkdegames/carddecks/cards-spaced/3.png create mode 100644 libkdegames/carddecks/cards-spaced/30.png create mode 100644 libkdegames/carddecks/cards-spaced/31.png create mode 100644 libkdegames/carddecks/cards-spaced/32.png create mode 100644 libkdegames/carddecks/cards-spaced/33.png create mode 100644 libkdegames/carddecks/cards-spaced/34.png create mode 100644 libkdegames/carddecks/cards-spaced/35.png create mode 100644 libkdegames/carddecks/cards-spaced/36.png create mode 100644 libkdegames/carddecks/cards-spaced/37.png create mode 100644 libkdegames/carddecks/cards-spaced/38.png create mode 100644 libkdegames/carddecks/cards-spaced/39.png create mode 100644 libkdegames/carddecks/cards-spaced/4.png create mode 100644 libkdegames/carddecks/cards-spaced/40.png create mode 100644 libkdegames/carddecks/cards-spaced/41.png create mode 100644 libkdegames/carddecks/cards-spaced/42.png create mode 100644 libkdegames/carddecks/cards-spaced/43.png create mode 100644 libkdegames/carddecks/cards-spaced/44.png create mode 100644 libkdegames/carddecks/cards-spaced/45.png create mode 100644 libkdegames/carddecks/cards-spaced/46.png create mode 100644 libkdegames/carddecks/cards-spaced/47.png create mode 100644 libkdegames/carddecks/cards-spaced/48.png create mode 100644 libkdegames/carddecks/cards-spaced/49.png create mode 100644 libkdegames/carddecks/cards-spaced/5.png create mode 100644 libkdegames/carddecks/cards-spaced/50.png create mode 100644 libkdegames/carddecks/cards-spaced/51.png create mode 100644 libkdegames/carddecks/cards-spaced/52.png create mode 100644 libkdegames/carddecks/cards-spaced/6.png create mode 100644 libkdegames/carddecks/cards-spaced/7.png create mode 100644 libkdegames/carddecks/cards-spaced/8.png create mode 100644 libkdegames/carddecks/cards-spaced/9.png create mode 100644 libkdegames/carddecks/cards-spaced/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-spaced/index.desktop create mode 100644 libkdegames/carddecks/cards-warwick/0.png create mode 100644 libkdegames/carddecks/cards-warwick/1.png create mode 100644 libkdegames/carddecks/cards-warwick/10.png create mode 100644 libkdegames/carddecks/cards-warwick/105.png create mode 100644 libkdegames/carddecks/cards-warwick/106.png create mode 100644 libkdegames/carddecks/cards-warwick/107.png create mode 100644 libkdegames/carddecks/cards-warwick/108.png create mode 100644 libkdegames/carddecks/cards-warwick/109.png create mode 100644 libkdegames/carddecks/cards-warwick/11.png create mode 100644 libkdegames/carddecks/cards-warwick/110.png create mode 100644 libkdegames/carddecks/cards-warwick/111.png create mode 100644 libkdegames/carddecks/cards-warwick/112.png create mode 100644 libkdegames/carddecks/cards-warwick/113.png create mode 100644 libkdegames/carddecks/cards-warwick/114.png create mode 100644 libkdegames/carddecks/cards-warwick/115.png create mode 100644 libkdegames/carddecks/cards-warwick/116.png create mode 100644 libkdegames/carddecks/cards-warwick/12.png create mode 100644 libkdegames/carddecks/cards-warwick/13.png create mode 100644 libkdegames/carddecks/cards-warwick/14.png create mode 100644 libkdegames/carddecks/cards-warwick/15.png create mode 100644 libkdegames/carddecks/cards-warwick/16.png create mode 100644 libkdegames/carddecks/cards-warwick/17.png create mode 100644 libkdegames/carddecks/cards-warwick/18.png create mode 100644 libkdegames/carddecks/cards-warwick/19.png create mode 100644 libkdegames/carddecks/cards-warwick/2.png create mode 100644 libkdegames/carddecks/cards-warwick/20.png create mode 100644 libkdegames/carddecks/cards-warwick/21.png create mode 100644 libkdegames/carddecks/cards-warwick/22.png create mode 100644 libkdegames/carddecks/cards-warwick/23.png create mode 100644 libkdegames/carddecks/cards-warwick/24.png create mode 100644 libkdegames/carddecks/cards-warwick/25.png create mode 100644 libkdegames/carddecks/cards-warwick/26.png create mode 100644 libkdegames/carddecks/cards-warwick/27.png create mode 100644 libkdegames/carddecks/cards-warwick/28.png create mode 100644 libkdegames/carddecks/cards-warwick/29.png create mode 100644 libkdegames/carddecks/cards-warwick/3.png create mode 100644 libkdegames/carddecks/cards-warwick/30.png create mode 100644 libkdegames/carddecks/cards-warwick/31.png create mode 100644 libkdegames/carddecks/cards-warwick/32.png create mode 100644 libkdegames/carddecks/cards-warwick/33.png create mode 100644 libkdegames/carddecks/cards-warwick/34.png create mode 100644 libkdegames/carddecks/cards-warwick/35.png create mode 100644 libkdegames/carddecks/cards-warwick/36.png create mode 100644 libkdegames/carddecks/cards-warwick/37.png create mode 100644 libkdegames/carddecks/cards-warwick/38.png create mode 100644 libkdegames/carddecks/cards-warwick/39.png create mode 100644 libkdegames/carddecks/cards-warwick/4.png create mode 100644 libkdegames/carddecks/cards-warwick/40.png create mode 100644 libkdegames/carddecks/cards-warwick/41.png create mode 100644 libkdegames/carddecks/cards-warwick/42.png create mode 100644 libkdegames/carddecks/cards-warwick/43.png create mode 100644 libkdegames/carddecks/cards-warwick/44.png create mode 100644 libkdegames/carddecks/cards-warwick/45.png create mode 100644 libkdegames/carddecks/cards-warwick/46.png create mode 100644 libkdegames/carddecks/cards-warwick/47.png create mode 100644 libkdegames/carddecks/cards-warwick/48.png create mode 100644 libkdegames/carddecks/cards-warwick/49.png create mode 100644 libkdegames/carddecks/cards-warwick/5.png create mode 100644 libkdegames/carddecks/cards-warwick/50.png create mode 100644 libkdegames/carddecks/cards-warwick/51.png create mode 100644 libkdegames/carddecks/cards-warwick/52.png create mode 100644 libkdegames/carddecks/cards-warwick/6.png create mode 100644 libkdegames/carddecks/cards-warwick/7.png create mode 100644 libkdegames/carddecks/cards-warwick/8.png create mode 100644 libkdegames/carddecks/cards-warwick/9.png create mode 100644 libkdegames/carddecks/cards-warwick/index.desktop create mode 100644 libkdegames/carddecks/cards-xskat-french/1.png create mode 100644 libkdegames/carddecks/cards-xskat-french/10.png create mode 100644 libkdegames/carddecks/cards-xskat-french/11.png create mode 100644 libkdegames/carddecks/cards-xskat-french/12.png create mode 100644 libkdegames/carddecks/cards-xskat-french/13.png create mode 100644 libkdegames/carddecks/cards-xskat-french/14.png create mode 100644 libkdegames/carddecks/cards-xskat-french/15.png create mode 100644 libkdegames/carddecks/cards-xskat-french/16.png create mode 100644 libkdegames/carddecks/cards-xskat-french/17.png create mode 100644 libkdegames/carddecks/cards-xskat-french/18.png create mode 100644 libkdegames/carddecks/cards-xskat-french/19.png create mode 100644 libkdegames/carddecks/cards-xskat-french/2.png create mode 100644 libkdegames/carddecks/cards-xskat-french/20.png create mode 100644 libkdegames/carddecks/cards-xskat-french/21.png create mode 100644 libkdegames/carddecks/cards-xskat-french/22.png create mode 100644 libkdegames/carddecks/cards-xskat-french/23.png create mode 100644 libkdegames/carddecks/cards-xskat-french/24.png create mode 100644 libkdegames/carddecks/cards-xskat-french/25.png create mode 100644 libkdegames/carddecks/cards-xskat-french/26.png create mode 100644 libkdegames/carddecks/cards-xskat-french/27.png create mode 100644 libkdegames/carddecks/cards-xskat-french/28.png create mode 100644 libkdegames/carddecks/cards-xskat-french/29.png create mode 100644 libkdegames/carddecks/cards-xskat-french/3.png create mode 100644 libkdegames/carddecks/cards-xskat-french/30.png create mode 100644 libkdegames/carddecks/cards-xskat-french/31.png create mode 100644 libkdegames/carddecks/cards-xskat-french/32.png create mode 100644 libkdegames/carddecks/cards-xskat-french/33.png create mode 100644 libkdegames/carddecks/cards-xskat-french/34.png create mode 100644 libkdegames/carddecks/cards-xskat-french/35.png create mode 100644 libkdegames/carddecks/cards-xskat-french/36.png create mode 100644 libkdegames/carddecks/cards-xskat-french/37.png create mode 100644 libkdegames/carddecks/cards-xskat-french/38.png create mode 100644 libkdegames/carddecks/cards-xskat-french/39.png create mode 100644 libkdegames/carddecks/cards-xskat-french/4.png create mode 100644 libkdegames/carddecks/cards-xskat-french/40.png create mode 100644 libkdegames/carddecks/cards-xskat-french/41.png create mode 100644 libkdegames/carddecks/cards-xskat-french/42.png create mode 100644 libkdegames/carddecks/cards-xskat-french/43.png create mode 100644 libkdegames/carddecks/cards-xskat-french/44.png create mode 100644 libkdegames/carddecks/cards-xskat-french/45.png create mode 100644 libkdegames/carddecks/cards-xskat-french/46.png create mode 100644 libkdegames/carddecks/cards-xskat-french/47.png create mode 100644 libkdegames/carddecks/cards-xskat-french/48.png create mode 100644 libkdegames/carddecks/cards-xskat-french/49.png create mode 100644 libkdegames/carddecks/cards-xskat-french/5.png create mode 100644 libkdegames/carddecks/cards-xskat-french/50.png create mode 100644 libkdegames/carddecks/cards-xskat-french/51.png create mode 100644 libkdegames/carddecks/cards-xskat-french/52.png create mode 100644 libkdegames/carddecks/cards-xskat-french/6.png create mode 100644 libkdegames/carddecks/cards-xskat-french/7.png create mode 100644 libkdegames/carddecks/cards-xskat-french/8.png create mode 100644 libkdegames/carddecks/cards-xskat-french/9.png create mode 100644 libkdegames/carddecks/cards-xskat-french/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-xskat-french/index.desktop create mode 100644 libkdegames/carddecks/cards-xskat-german/1.png create mode 100644 libkdegames/carddecks/cards-xskat-german/10.png create mode 100644 libkdegames/carddecks/cards-xskat-german/11.png create mode 100644 libkdegames/carddecks/cards-xskat-german/12.png create mode 100644 libkdegames/carddecks/cards-xskat-german/13.png create mode 100644 libkdegames/carddecks/cards-xskat-german/14.png create mode 100644 libkdegames/carddecks/cards-xskat-german/15.png create mode 100644 libkdegames/carddecks/cards-xskat-german/16.png create mode 100644 libkdegames/carddecks/cards-xskat-german/17.png create mode 100644 libkdegames/carddecks/cards-xskat-german/18.png create mode 100644 libkdegames/carddecks/cards-xskat-german/19.png create mode 100644 libkdegames/carddecks/cards-xskat-german/2.png create mode 100644 libkdegames/carddecks/cards-xskat-german/20.png create mode 100644 libkdegames/carddecks/cards-xskat-german/21.png create mode 100644 libkdegames/carddecks/cards-xskat-german/22.png create mode 100644 libkdegames/carddecks/cards-xskat-german/23.png create mode 100644 libkdegames/carddecks/cards-xskat-german/24.png create mode 100644 libkdegames/carddecks/cards-xskat-german/25.png create mode 100644 libkdegames/carddecks/cards-xskat-german/26.png create mode 100644 libkdegames/carddecks/cards-xskat-german/27.png create mode 100644 libkdegames/carddecks/cards-xskat-german/28.png create mode 100644 libkdegames/carddecks/cards-xskat-german/29.png create mode 100644 libkdegames/carddecks/cards-xskat-german/3.png create mode 100644 libkdegames/carddecks/cards-xskat-german/30.png create mode 100644 libkdegames/carddecks/cards-xskat-german/31.png create mode 100644 libkdegames/carddecks/cards-xskat-german/32.png create mode 100644 libkdegames/carddecks/cards-xskat-german/33.png create mode 100644 libkdegames/carddecks/cards-xskat-german/34.png create mode 100644 libkdegames/carddecks/cards-xskat-german/35.png create mode 100644 libkdegames/carddecks/cards-xskat-german/36.png create mode 100644 libkdegames/carddecks/cards-xskat-german/37.png create mode 100644 libkdegames/carddecks/cards-xskat-german/38.png create mode 100644 libkdegames/carddecks/cards-xskat-german/39.png create mode 100644 libkdegames/carddecks/cards-xskat-german/4.png create mode 100644 libkdegames/carddecks/cards-xskat-german/40.png create mode 100644 libkdegames/carddecks/cards-xskat-german/41.png create mode 100644 libkdegames/carddecks/cards-xskat-german/42.png create mode 100644 libkdegames/carddecks/cards-xskat-german/43.png create mode 100644 libkdegames/carddecks/cards-xskat-german/44.png create mode 100644 libkdegames/carddecks/cards-xskat-german/45.png create mode 100644 libkdegames/carddecks/cards-xskat-german/46.png create mode 100644 libkdegames/carddecks/cards-xskat-german/47.png create mode 100644 libkdegames/carddecks/cards-xskat-german/48.png create mode 100644 libkdegames/carddecks/cards-xskat-german/49.png create mode 100644 libkdegames/carddecks/cards-xskat-german/5.png create mode 100644 libkdegames/carddecks/cards-xskat-german/50.png create mode 100644 libkdegames/carddecks/cards-xskat-german/51.png create mode 100644 libkdegames/carddecks/cards-xskat-german/52.png create mode 100644 libkdegames/carddecks/cards-xskat-german/6.png create mode 100644 libkdegames/carddecks/cards-xskat-german/7.png create mode 100644 libkdegames/carddecks/cards-xskat-german/8.png create mode 100644 libkdegames/carddecks/cards-xskat-german/9.png create mode 100644 libkdegames/carddecks/cards-xskat-german/COPYRIGHT create mode 100644 libkdegames/carddecks/cards-xskat-german/index.desktop create mode 100755 libkdegames/carddecks/convertpysols create mode 100644 libkdegames/carddecks/decks/deck0.desktop create mode 100644 libkdegames/carddecks/decks/deck0.png create mode 100644 libkdegames/carddecks/decks/deck1.desktop create mode 100644 libkdegames/carddecks/decks/deck1.png create mode 100644 libkdegames/carddecks/decks/deck10.desktop create mode 100644 libkdegames/carddecks/decks/deck10.png create mode 100644 libkdegames/carddecks/decks/deck11.desktop create mode 100644 libkdegames/carddecks/decks/deck11.png create mode 100644 libkdegames/carddecks/decks/deck12.desktop create mode 100644 libkdegames/carddecks/decks/deck12.png create mode 100644 libkdegames/carddecks/decks/deck13.desktop create mode 100644 libkdegames/carddecks/decks/deck13.png create mode 100644 libkdegames/carddecks/decks/deck14.desktop create mode 100644 libkdegames/carddecks/decks/deck14.png create mode 100644 libkdegames/carddecks/decks/deck15.desktop create mode 100644 libkdegames/carddecks/decks/deck15.png create mode 100644 libkdegames/carddecks/decks/deck16.desktop create mode 100644 libkdegames/carddecks/decks/deck16.png create mode 100644 libkdegames/carddecks/decks/deck17.desktop create mode 100644 libkdegames/carddecks/decks/deck17.png create mode 100644 libkdegames/carddecks/decks/deck18.desktop create mode 100644 libkdegames/carddecks/decks/deck18.png create mode 100644 libkdegames/carddecks/decks/deck19.desktop create mode 100644 libkdegames/carddecks/decks/deck19.png create mode 100644 libkdegames/carddecks/decks/deck2.desktop create mode 100644 libkdegames/carddecks/decks/deck2.png create mode 100644 libkdegames/carddecks/decks/deck20.desktop create mode 100644 libkdegames/carddecks/decks/deck20.png create mode 100644 libkdegames/carddecks/decks/deck21.desktop create mode 100644 libkdegames/carddecks/decks/deck21.png create mode 100644 libkdegames/carddecks/decks/deck22.desktop create mode 100644 libkdegames/carddecks/decks/deck22.png create mode 100644 libkdegames/carddecks/decks/deck23.desktop create mode 100644 libkdegames/carddecks/decks/deck23.png create mode 100644 libkdegames/carddecks/decks/deck24.desktop create mode 100644 libkdegames/carddecks/decks/deck24.png create mode 100644 libkdegames/carddecks/decks/deck3.desktop create mode 100644 libkdegames/carddecks/decks/deck3.png create mode 100644 libkdegames/carddecks/decks/deck4.desktop create mode 100644 libkdegames/carddecks/decks/deck4.png create mode 100644 libkdegames/carddecks/decks/deck5.desktop create mode 100644 libkdegames/carddecks/decks/deck5.png create mode 100644 libkdegames/carddecks/decks/deck6.desktop create mode 100644 libkdegames/carddecks/decks/deck6.png create mode 100644 libkdegames/carddecks/decks/deck7.desktop create mode 100644 libkdegames/carddecks/decks/deck7.png create mode 100644 libkdegames/carddecks/decks/deck8.desktop create mode 100644 libkdegames/carddecks/decks/deck8.png create mode 100644 libkdegames/carddecks/decks/deck9.desktop create mode 100644 libkdegames/carddecks/decks/deck9.png create mode 100644 libkdegames/configure.in.in create mode 100644 libkdegames/highscore/INSTALL create mode 100644 libkdegames/highscore/Makefile.am create mode 100644 libkdegames/highscore/kconfigrawbackend.cpp create mode 100644 libkdegames/highscore/kconfigrawbackend.h create mode 100644 libkdegames/highscore/kexthighscore.cpp create mode 100644 libkdegames/highscore/kexthighscore.h create mode 100644 libkdegames/highscore/kexthighscore_gui.cpp create mode 100644 libkdegames/highscore/kexthighscore_gui.h create mode 100644 libkdegames/highscore/kexthighscore_internal.cpp create mode 100644 libkdegames/highscore/kexthighscore_internal.h create mode 100644 libkdegames/highscore/kexthighscore_item.cpp create mode 100644 libkdegames/highscore/kexthighscore_item.h create mode 100644 libkdegames/highscore/kexthighscore_tab.cpp create mode 100644 libkdegames/highscore/kexthighscore_tab.h create mode 100644 libkdegames/highscore/kfilelock.cpp create mode 100644 libkdegames/highscore/kfilelock.h create mode 100644 libkdegames/highscore/khighscore.cpp create mode 100644 libkdegames/highscore/khighscore.h create mode 100644 libkdegames/highscore/kscoredialog.cpp create mode 100644 libkdegames/highscore/kscoredialog.h create mode 100644 libkdegames/kcanvasrootpixmap.cpp create mode 100644 libkdegames/kcanvasrootpixmap.h create mode 100644 libkdegames/kcarddialog.cpp create mode 100644 libkdegames/kcarddialog.h create mode 100644 libkdegames/kcarddialog.png create mode 100644 libkdegames/kchat.cpp create mode 100644 libkdegames/kchat.h create mode 100644 libkdegames/kchatbase.cpp create mode 100644 libkdegames/kchatbase.h create mode 100644 libkdegames/kchatdialog.cpp create mode 100644 libkdegames/kchatdialog.h create mode 100644 libkdegames/kgame/COMPAT create mode 100644 libkdegames/kgame/DESIGN create mode 100644 libkdegames/kgame/Makefile.am create mode 100644 libkdegames/kgame/README.LIB create mode 100644 libkdegames/kgame/TODO create mode 100644 libkdegames/kgame/dialogs/Makefile.am create mode 100644 libkdegames/kgame/dialogs/kgameconnectdialog.cpp create mode 100644 libkdegames/kgame/dialogs/kgameconnectdialog.h create mode 100644 libkdegames/kgame/dialogs/kgamedebugdialog.cpp create mode 100644 libkdegames/kgame/dialogs/kgamedebugdialog.h create mode 100644 libkdegames/kgame/dialogs/kgamedialog.cpp create mode 100644 libkdegames/kgame/dialogs/kgamedialog.h create mode 100644 libkdegames/kgame/dialogs/kgamedialogconfig.cpp create mode 100644 libkdegames/kgame/dialogs/kgamedialogconfig.h create mode 100644 libkdegames/kgame/dialogs/kgameerrordialog.cpp create mode 100644 libkdegames/kgame/dialogs/kgameerrordialog.h create mode 100644 libkdegames/kgame/kgame.cpp create mode 100644 libkdegames/kgame/kgame.h create mode 100644 libkdegames/kgame/kgamechat.cpp create mode 100644 libkdegames/kgame/kgamechat.h create mode 100644 libkdegames/kgame/kgameerror.cpp create mode 100644 libkdegames/kgame/kgameerror.h create mode 100644 libkdegames/kgame/kgameio.cpp create mode 100644 libkdegames/kgame/kgameio.h create mode 100644 libkdegames/kgame/kgamemessage.cpp create mode 100644 libkdegames/kgame/kgamemessage.h create mode 100644 libkdegames/kgame/kgamenetwork.cpp create mode 100644 libkdegames/kgame/kgamenetwork.h create mode 100644 libkdegames/kgame/kgameprocess.cpp create mode 100644 libkdegames/kgame/kgameprocess.h create mode 100644 libkdegames/kgame/kgameproperty.cpp create mode 100644 libkdegames/kgame/kgameproperty.h create mode 100644 libkdegames/kgame/kgamepropertyarray.h create mode 100644 libkdegames/kgame/kgamepropertyhandler.cpp create mode 100644 libkdegames/kgame/kgamepropertyhandler.h create mode 100644 libkdegames/kgame/kgamepropertylist.h create mode 100644 libkdegames/kgame/kgamesequence.cpp create mode 100644 libkdegames/kgame/kgamesequence.h create mode 100644 libkdegames/kgame/kgameversion.h create mode 100644 libkdegames/kgame/kmessageclient.cpp create mode 100644 libkdegames/kgame/kmessageclient.h create mode 100644 libkdegames/kgame/kmessageio.cpp create mode 100644 libkdegames/kgame/kmessageio.h create mode 100644 libkdegames/kgame/kmessageserver.cpp create mode 100644 libkdegames/kgame/kmessageserver.h create mode 100644 libkdegames/kgame/kmessageserver.png create mode 100644 libkdegames/kgame/kplayer.cpp create mode 100644 libkdegames/kgame/kplayer.h create mode 100644 libkdegames/kgame/libkdegames.html create mode 100644 libkdegames/kgame/messages.txt create mode 100644 libkdegames/kgame/scenario0.png create mode 100644 libkdegames/kgame/scenario1.png create mode 100644 libkdegames/kgame/scenario2.png create mode 100644 libkdegames/kgamelcd.cpp create mode 100644 libkdegames/kgamelcd.h create mode 100644 libkdegames/kgamemisc.cpp create mode 100644 libkdegames/kgamemisc.h create mode 100644 libkdegames/kgameprogress.cpp create mode 100644 libkdegames/kgameprogress.h create mode 100644 libkdegames/kgrid2d.h create mode 100644 libkdegames/kstdgameaction.cpp create mode 100644 libkdegames/kstdgameaction.h create mode 100644 libkdegames/pics/Makefile.am create mode 100644 libkdegames/pics/cr16-action-endturn.png create mode 100644 libkdegames/pics/cr16-action-highscore.png create mode 100644 libkdegames/pics/cr16-action-roll.png create mode 100644 libkdegames/pics/cr22-action-roll.png create mode 100644 libkdegames/pics/cr32-action-endturn.png create mode 100644 libkdegames/pics/cr32-action-highscore.png create mode 100644 libkdegames/pics/cr32-action-roll.png create mode 100644 libkdegames/pics/star.png create mode 100644 libksirtet/CHANGELOG create mode 100644 libksirtet/DESIGN create mode 100644 libksirtet/LICENSE create mode 100644 libksirtet/Makefile.am create mode 100644 libksirtet/README create mode 100644 libksirtet/README.gtetris create mode 100644 libksirtet/TODO create mode 100644 libksirtet/base/Makefile.am create mode 100644 libksirtet/base/README create mode 100644 libksirtet/base/baseprefs.kcfgc create mode 100644 libksirtet/base/board.cpp create mode 100644 libksirtet/base/board.h create mode 100644 libksirtet/base/factory.cpp create mode 100644 libksirtet/base/factory.h create mode 100644 libksirtet/base/field.cpp create mode 100644 libksirtet/base/field.h create mode 100644 libksirtet/base/gtetris.cpp create mode 100644 libksirtet/base/gtetris.h create mode 100644 libksirtet/base/highscores.cpp create mode 100644 libksirtet/base/highscores.h create mode 100644 libksirtet/base/inter.cpp create mode 100644 libksirtet/base/inter.h create mode 100644 libksirtet/base/kzoommainwindow.cpp create mode 100644 libksirtet/base/kzoommainwindow.h create mode 100644 libksirtet/base/libksirtet1.kcfg create mode 100644 libksirtet/base/main.cpp create mode 100644 libksirtet/base/main.h create mode 100644 libksirtet/base/piece.cpp create mode 100644 libksirtet/base/piece.h create mode 100644 libksirtet/base/settings.cpp create mode 100644 libksirtet/base/settings.h create mode 100644 libksirtet/common/Makefile.am create mode 100644 libksirtet/common/README create mode 100644 libksirtet/common/ai.cpp create mode 100644 libksirtet/common/ai.h create mode 100644 libksirtet/common/board.cpp create mode 100644 libksirtet/common/board.h create mode 100644 libksirtet/common/commonprefs.kcfgc create mode 100644 libksirtet/common/factory.cpp create mode 100644 libksirtet/common/factory.h create mode 100644 libksirtet/common/field.cpp create mode 100644 libksirtet/common/field.h create mode 100644 libksirtet/common/highscores.cpp create mode 100644 libksirtet/common/highscores.h create mode 100644 libksirtet/common/inter.cpp create mode 100644 libksirtet/common/inter.h create mode 100644 libksirtet/common/libksirtet2.kcfg create mode 100644 libksirtet/common/main.cpp create mode 100644 libksirtet/common/main.h create mode 100644 libksirtet/common/misc_ui.cpp create mode 100644 libksirtet/common/misc_ui.h create mode 100644 libksirtet/common/settings.cpp create mode 100644 libksirtet/common/settings.h create mode 100644 libksirtet/common/types.cpp create mode 100644 libksirtet/common/types.h create mode 100644 libksirtet/configure.in.in create mode 100644 libksirtet/lib/CHANGELOG create mode 100644 libksirtet/lib/LICENSE create mode 100644 libksirtet/lib/Makefile.am create mode 100644 libksirtet/lib/README create mode 100644 libksirtet/lib/TODO create mode 100644 libksirtet/lib/defines.cpp create mode 100644 libksirtet/lib/defines.h create mode 100644 libksirtet/lib/internal.cpp create mode 100644 libksirtet/lib/internal.h create mode 100644 libksirtet/lib/keys.cpp create mode 100644 libksirtet/lib/keys.h create mode 100644 libksirtet/lib/libksirtet_export.h create mode 100644 libksirtet/lib/meeting.cpp create mode 100644 libksirtet/lib/meeting.h create mode 100644 libksirtet/lib/miscui.cpp create mode 100644 libksirtet/lib/miscui.h create mode 100644 libksirtet/lib/mp_board.h create mode 100644 libksirtet/lib/mp_interface.cpp create mode 100644 libksirtet/lib/mp_interface.h create mode 100644 libksirtet/lib/mp_option.h create mode 100644 libksirtet/lib/mp_simple_board.cpp create mode 100644 libksirtet/lib/mp_simple_board.h create mode 100644 libksirtet/lib/mp_simple_interface.cpp create mode 100644 libksirtet/lib/mp_simple_interface.h create mode 100644 libksirtet/lib/mp_simple_types.cpp create mode 100644 libksirtet/lib/mp_simple_types.h create mode 100644 libksirtet/lib/pline.cpp create mode 100644 libksirtet/lib/pline.h create mode 100644 libksirtet/lib/smanager.cpp create mode 100644 libksirtet/lib/smanager.h create mode 100644 libksirtet/lib/socket.cpp create mode 100644 libksirtet/lib/socket.h create mode 100644 libksirtet/lib/types.cpp create mode 100644 libksirtet/lib/types.h create mode 100644 libksirtet/lib/version.h create mode 100644 libksirtet/lib/wizard.cpp create mode 100644 libksirtet/lib/wizard.h create mode 100644 lskat/AUTHORS create mode 100644 lskat/COPYING create mode 100644 lskat/Makefile.am create mode 100644 lskat/README create mode 100644 lskat/TODO create mode 100644 lskat/grafix/Makefile.am create mode 100644 lskat/grafix/background.png create mode 100644 lskat/grafix/deck1.png create mode 100644 lskat/grafix/deck2.png create mode 100644 lskat/grafix/deck3.png create mode 100644 lskat/grafix/deck4.png create mode 100644 lskat/grafix/t1.png create mode 100644 lskat/grafix/t2.png create mode 100644 lskat/grafix/t3.png create mode 100644 lskat/grafix/t4.png create mode 100644 lskat/grafix/t5.png create mode 100644 lskat/grafix/tback.png create mode 100644 lskat/grafix/type1.png create mode 100644 lskat/grafix/type2.png create mode 100644 lskat/grafix/type3.png create mode 100644 lskat/hi128-app-lskat.png create mode 100644 lskat/hi16-app-lskat.png create mode 100644 lskat/hi22-app-lskat.png create mode 100644 lskat/hi32-app-lskat.png create mode 100644 lskat/hi48-app-lskat.png create mode 100644 lskat/hi64-app-lskat.png create mode 100644 lskat/lskat.desktop create mode 100644 lskat/lskat.kdevprj create mode 100644 lskat/lskat.lsm create mode 100644 lskat/lskat/KChildConnect.cpp create mode 100644 lskat/lskat/KChildConnect.h create mode 100644 lskat/lskat/KConnectEntry.cpp create mode 100644 lskat/lskat/KConnectEntry.h create mode 100644 lskat/lskat/KConnectTypes.h create mode 100644 lskat/lskat/KEInput.cpp create mode 100644 lskat/lskat/KEInput.h create mode 100644 lskat/lskat/KEMessage.cpp create mode 100644 lskat/lskat/KEMessage.h create mode 100644 lskat/lskat/KInputChildProcess.cpp create mode 100644 lskat/lskat/KInputChildProcess.h create mode 100644 lskat/lskat/KInteractiveConnect.cpp create mode 100644 lskat/lskat/KInteractiveConnect.h create mode 100644 lskat/lskat/KMessageEntry.cpp create mode 100644 lskat/lskat/KMessageEntry.h create mode 100644 lskat/lskat/KProcessConnect.cpp create mode 100644 lskat/lskat/KProcessConnect.h create mode 100644 lskat/lskat/KRSocket.cpp create mode 100644 lskat/lskat/KRSocket.h create mode 100644 lskat/lskat/KRemoteConnect.cpp create mode 100644 lskat/lskat/KRemoteConnect.h create mode 100644 lskat/lskat/Makefile.am create mode 100644 lskat/lskat/lskat.cpp create mode 100644 lskat/lskat/lskat.h create mode 100644 lskat/lskat/lskatdoc.cpp create mode 100644 lskat/lskat/lskatdoc.h create mode 100644 lskat/lskat/lskatui.rc create mode 100644 lskat/lskat/lskatview.cpp create mode 100644 lskat/lskat/lskatview.h create mode 100644 lskat/lskat/main.cpp create mode 100644 lskat/lskat/msgdlg.cpp create mode 100644 lskat/lskat/msgdlg.h create mode 100644 lskat/lskat/namedlg.cpp create mode 100644 lskat/lskat/namedlg.h create mode 100644 lskat/lskat/networkdlg.cpp create mode 100644 lskat/lskat/networkdlg.h create mode 100644 lskat/lskat/networkdlgbase.ui create mode 100644 lskat/lskat/templates/cpp_template create mode 100644 lskat/lskat/templates/header_template create mode 100644 lskat/lskatproc/KChildConnect.cpp create mode 100644 lskat/lskatproc/KChildConnect.h create mode 100644 lskat/lskatproc/KConnectTypes.h create mode 100644 lskat/lskatproc/KEMessage.cpp create mode 100644 lskat/lskatproc/KEMessage.h create mode 100644 lskat/lskatproc/KInputChildProcess.cpp create mode 100644 lskat/lskatproc/KInputChildProcess.h create mode 100644 lskat/lskatproc/KMessageEntry.cpp create mode 100644 lskat/lskatproc/KMessageEntry.h create mode 100644 lskat/lskatproc/Makefile.am create mode 100644 lskat/lskatproc/docs/Makefile.am create mode 100644 lskat/lskatproc/docs/en/Makefile.am create mode 100644 lskat/lskatproc/lskatproc.cpp create mode 100644 lskat/lskatproc/lskatproc.h create mode 100644 lskat/lskatproc/main.cpp create mode 100644 lskat/lskatproc/templates/cpp_template create mode 100644 lskat/lskatproc/templates/header_template diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..a3b0c210 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ + +Look in the subdirs to get info about the authors. + +The package is maintained by Stephan Kulow diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..8900e10b --- /dev/null +++ b/COPYING @@ -0,0 +1,347 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/COPYING-DOCS b/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..01148ab6 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,486 @@ +NOTE! The LGPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde libraries) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1301, USA. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..59959f64 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,16 @@ +1999-03-11 Mario Weilguni + + * fixed (hopefully) all problems with Qt-2.0 + +1999-02-17 David Faure + + * Added call to AC_CHECK_RANDOM, and converted all apps to use + random() and srandom() (and to avoid RAND_MAX). + If random() is not defined, acconfig.h defines the header for the + fake functions in kdecore. + +1998-12-05 Alex Zepeda + + * autmoc: Moved to admin/ + * Makefile.cvs: Copied over from kdenetwork verbatim. + * README: Updated to "require" Qt 1.41. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..f8bad0c1 --- /dev/null +++ b/INSTALL @@ -0,0 +1,176 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an +installation prefix other than `/usr/local/kde' by giving `configure' +the option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 00000000..061abb9a --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,8 @@ +/** \mainpage The KDE Games API Reference + +This section contains the KDE online class reference for the current development version of KDE Games. + +- libkdegames (classes) + + +*/ diff --git a/Makefile.am.in b/Makefile.am.in new file mode 100644 index 00000000..2aa4523a --- /dev/null +++ b/Makefile.am.in @@ -0,0 +1,13 @@ +AUTOMAKE_OPTIONS = foreign 1.6.1 +COMPILE_FIRST = libkdegames +COMPILE_AFTER_libksirtet = ksirtet kfouleggs klickety +DISTCLEANFILES = inst-apps + +MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS +include admin/deps.am + +include admin/Doxyfile.am + + + + diff --git a/Makefile.cvs b/Makefile.cvs new file mode 100644 index 00000000..b4752bd8 --- /dev/null +++ b/Makefile.cvs @@ -0,0 +1,16 @@ + +all: + @echo "This Makefile is only for the CVS repository" + @echo "This will be deleted before making the distribution" + @echo "" + @if test ! -d admin; then \ + echo "Please recheckout this module!" ;\ + echo "for cvs: use checkout once and after that update again" ;\ + echo "for cvsup: checkout kde-common from cvsup and" ;\ + echo " link kde-common/admin to ./admin" ;\ + exit 1 ;\ + fi + $(MAKE) -f admin/Makefile.common cvs + +.SILENT: + diff --git a/README b/README new file mode 100644 index 00000000..76d4581b --- /dev/null +++ b/README @@ -0,0 +1,135 @@ +In this file: + +* About kdegames +* Common Mistakes +* Debugging +* More Info + + +About kdegames +-------------- +This is a compilation of various games + +* atlantik + Monopoly-like board games + +* debian + Files necessary to create Debian packages. + +* doc + XML based documentation for the programs. + +* kasteroids + Shoot at those nasty asteroids. + +* katomic + Build complex atoms with a minimal amount of moves. + +* kbackgammon + Play backgammon against a local human player, via a game server or + against GNU Backgammon (not included) + +* kbattleship + Sink battleship of your opponents, with built-in game server. + +* kblackbox + Find atoms in a grid by shooting electrons. + +* kbounce + Claim areas and don't get disturbed. + +* keneloba + Push pieces of your opponent out of the board (an Abalone type game). + +* kfouleggs + A famous japanese game known as puyo-puyo. + +* kjumpingcube + A tactical game for number-crunchers. + +* klickety + A tetris like game. + +* klines + Place 5 equal pieces together, but wait, there are 3 new ones. + +* mahjongg + A tile laying patience. + +* kmines + The classical mine sweeper. + +* kolf + A mini golf game. + +* konquest + Conquer the planets of your enemy. + +* kpat + Several patience card games. + +* kpoker + The game of poker. + +* kreversi + The old reversi board game, also known as Othello. + +* ksame + Collect pieces of the same color. + +* kshisen + Patience game where you take away all pieces. + +* ksirtet + Very known if spelt this backwards. + +* ksmiletris + Another Tetris-like game. + +* ksnake + Don't bite yourself, eat apples! + +* ksokoban + Move all storage boxes into the cabinet. + +* kspaceduel + Two player game with shooting spaceships flying around a sun. + +* ktron + Like ksnake, but without fruits. + +* ktuberling + Kids game: make your own potato (NO french fries!) + +* kwin4 + Place 4 pieces in a row. + +* libkdegames + KDE game library used by many of these programs, contains also images + of card decks. + +* lskat + Lieutnant skat. + + +Common Mistakes +--------------- +If configure claims Qt cannot be found, have a look at http://www.trolltech.com +to get a copy of latest Qt 3.3.x version. + + +Debugging +--------- +You can use --enable-debug with the configure script, if you want to have +debug code in your KDE apps and libs. I recommend to do this, since this is +alpha software and this makes debugging things a whole lot easier. + + +More Info +--------- +Please direct any bug reports to our bug list by visiting +http://bugs.kde.org. + +General KDE discussions should go to the KDE mailing list (kde@kde.org). + + diff --git a/atlantik/AUTHORS b/atlantik/AUTHORS new file mode 100644 index 00000000..5ef9d50f --- /dev/null +++ b/atlantik/AUTHORS @@ -0,0 +1 @@ +Rob Kaper diff --git a/atlantik/COPYING b/atlantik/COPYING new file mode 100644 index 00000000..c13faf0d --- /dev/null +++ b/atlantik/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/atlantik/COPYING.LIB b/atlantik/COPYING.LIB new file mode 100644 index 00000000..ae23fcfd --- /dev/null +++ b/atlantik/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/atlantik/ChangeLog b/atlantik/ChangeLog new file mode 100644 index 00000000..8f889261 --- /dev/null +++ b/atlantik/ChangeLog @@ -0,0 +1,633 @@ +0.7.5 (KDE 3.4) +----- + +- Show mortgage and house prices/values on estate views. +- Make token image a saved configuration setting, use KIconDialog. +- Avoid scrollbar by using KWrappedListViewItem. + +0.7.2 (KDE 3.3.2) +----- + +- Proper use of host and port in kio_atlantik: fixes problems with + invitations sent by newer clients. +- bugfix: scroll player views when they get too big. (#69043) +- bugfix: show correct amount of players in trade widget. + +0.7.1 (KDE 3.3.1) +----- + +- Don't show warning dialog when exiting a game that ended. (#88617) + +0.7.0 (KDE 3.3) +----- + +- Event log +- Use of KStatusBar +- Game master can boot other players to lounge during game setup (#52631) +- Support gameupdate tag, deprecate updategamelist (requires monopd >= 0.9.0) +- Support individual configupdate tags +- Show estate names on board (#61858) +- Properly withdraw from a game prior to intended client exits, to avoid + triggering monopd's reconnection timeout. + +0.6.3 +----- + +- bugfix: avoid crash when unresolvable hosts are in the meta server list +- bugfix: fix crash when network core is reset while readbuffer isn't empty +- bugfix: chat view can be cleared (#69044) + +0.6.1 +----- + +- hide development servers identifying themselves with "-dev" + +0.6.0 +----- + +- support for custom tokens +- don't connect to meta server unless user has configured to do so +- support for KNotify events +- option to hide development servers (on by default) +- gui to connect to custom server +- klatencytimer: support for server ping times +- option to show timestamp in chat messages + +0.5.5 +----- + +- bugfix: fix wild pointer when removing trade items (#68589) +- bugfix: set proper palette color for EstateDetails so Keramik buttons + don't look out of place + +0.5.4 +----- + +- bugfix: don't interrupt token movement when player leaves jail on doubles +- bugfix: add m_atlanticCore checks in Board, fixes some Atlantik Designer + crashes +- bugfix: don't show players from other games in auction and trade widgets + on monopd 0.9.0 servers +- bugfix: valgrind found two crash conditions when leaving game + configuration widget or auction widget (#66498) + +0.5.3 +----- + +- fix problem where sometimes the select game widget appears + after starting a game +- removed buggy trademap in network code for decent ptrlist in core +- memory management: reset complete core when going to select server page +- store reconnection cookie +- hide token when player goes bankrupt +- prevent player from building/unmortgaging when in debt +- disable auction/usecard menu item when not available (requires monopd >= 0.8.1) +- make use of trade revisions (monopd >= 0.8.2 recommended) +- don't tell game has started when it has not +- sort user column in server list by number, not alphabetically +- show non-game users when not in a game +- prevent double close buttons on estate details + +0.5.2 +----- + +- correctly handle utf-8 over the network +- only show localhost server when it is available +- leaving a game and starting a new game correctly reinits atlanticCore +- use disability to join game when size == MAX in gameupdate (games in + updategamelist now have canbejoined boolean attribute) +- using playerupdate for player list in SelectConfiguration +- use portfolioview instead of klistview for players during config +- better handling of display messages from server +- fixed token animation + +0.5.1 +----- + +- fixed potential double initialization of gameboard +- don't crash when removing money from a trade +- properly quote arguments given to KProcess in kio_atlantik +- valgrind leak and unitialised memory access fixes + +0.5.0 +----- + +- fixed dangling pointer for removeGUI(Trade *) in AtlanticCore +- player name change doesn't make it to trade money items +- fixed token positioning on game start +- fixes to work with monopd 0.6.0 API + +0.4.0 +----- + +- token geometry is now aware of estate colour captions +- queue display widgets + - fix buttons, they have specific targets, not just the current widget + - delete buttons in EstateDetails::newUpdate() +- commandline parameters host, port and game supported (for auto-join) +- kio_atlantik allows for easy connecting from other applications + (kopete/kmail) +- leave game and leave server options +- pre-game configuration + +2002-07-08 (kaper) +---------- + +- support for auto-connect and auto-join + +2002-07-07 (kaper) +---------- + +- request full update when getting invalid data from the network +- gui bugfixes + +2002-07-04 (kaper) (atlantik-0.3.0) +---------- + +- clear display on + +2002-07-03 (kaper) +---------- + +- removing trade money works again, fixed possible crashes in trade and + auction deletion within network API + +2002-06-30 (kaper) +---------- + +- monopd no longer sends gamelist on connect, so request it +- more intelligent EstateDetails, shows no useless info anymore and added + groupname, price and isMortgaged +- 10% or $200 taxes finally implemented! (requires monopd CVS >= 20020701) +- removed
from messagewindow, apparently Qt autowraps (again??) + +2002-06-27 (kaper) +---------- + +- recently committed some changes to be up-to-date with some API monopd + changes +- generalised BoardDisplay::displayCard() into displayText() so I can fix + the missing jail notification bug + +2002-04-29 (kaper) +---------- + +- dropped QSocket for KExtendedSocket + +2002-03-30 (kaper) +---------- + +- monopd API: support for estategroupupdate +- build portfolioview upon game start for better layouting + +2002-02-19 (kaper) +---------- + +- Trading done except for small esthetic TODOs. + +2002-02-13 (kaper) +---------- + +- Nicer button layouts (QSpacerItems, KIcon::SizeSmall, margin/spacingHints) + +2002-02-11 (kaper) +---------- + +- Gametypes no longer hardcoded but fetched from monopd server +- Trading money, rejecting trades! + +2002-01-31 (kaper) +---------- + +- Bugfixes +- Trades! (ok, just the estates and no way to accept, but still ;) + +2002-01-08 (kaper) +---------- + +- Auctions! +- Board resize updates tokens again + +2001-12-22 (katz) +--------- + +- Add support for setting AtlantikBoard's size in its ctor; + simply tell it maxEstates. Right now for Atlantik's board + it simply uses a hard-coded 40 +- in Designer, implement 'smaller' nad 'larger' menu entries, + now able to save and load boards with any size!!! +- add boardinfo.[h/cpp] that has a BoardInfo class that has board information, + and a class to View/Edit it +- designer uses it, doesn't read/write the info yet tho + +2001-12-20 (katz) +--------- + +- fix using user's colors for openNew in designer + +2001-12-20 (katz) +--------- + +Designer: +- gets its first entry in ChangeLog, because designer has been + on the whole useless before this +- supports chance and cc cards; loads and saves properly +- doesn't crash in random places +- loads and saves in Cap's new format +- fix probs with the swallowed dialogs +- don't use defaultcity.conf in openNew(); make board with + user's KDE colors! pretty :-) doesn'twork tho, neil will fix +- use KComboBox now QComboBox +- maybe some other things? + +2001-12-20 (kaper) +---------- + +- Moved network stuff to libatlantiknetwork + +2001-12-19 (kaper) +---------- + +- Better toolbar disable/enable code (API changes in monopd) +- Bugfixes for m_playerSelf +- preparations to use KExtendedSocket instead of QSocket +- using KPushButton instead of QPushButton + +2001-12-18 (kaper) +---------- + +- moved Trade to libatlantic +- libatlantic now includes AtlanticCore wrapper + +2001-12-17 (kaper) +---------- + +- moved object management to Network class +- moved Estate and Player to libatlantic + +2001-12-16 (kaper) +---------- + +- monopd no longer 'includes' estates in trades, but has a proper + targetplayer for them. added support for this API update, as well as the + update regarding money trades + +2001-12-14 (kaper) +---------- + +- better looking portfolios +- support for displaying game types in game list +- ability to choose between city or atlantic gametype +- GUI to create a trade + +2001-11-29 (kaper) +---------- + +- Portfolios are _truly_ dynamic now. :-) + +2001-11-27 (kaper) +---------- + +- Set version to 0.1.2 CVS. + +2001-11-27 (kaper) (0.1.1 release) +---------- + +- PortfolioViews and PortfolioEstates work again! +- Tagged 0.1.1 release. + +2001-11-23 (kaper) +---------- + +- Trading skeleton +- Better implementation of connection between Player and PortfolioView +- Various code cleanups + +2001-11-21 (kaper) +---------- + +- Very basic skeleton for atlanticd (monopd-compatible server) + +2001-11-15 (kaper) +---------- + +- Even more dynamic thinking: estateview actions now completely depend on + server data instead of own checks. + +2001-11-14 (kaper) +---------- + +- Tokens are correctly positioned at startup and token animation is working + again. +- Chance/community cards are displayed in board center. + +2001-11-12 (kaper) +---------- + +- EstateViews have correct orientation again. +- Fixed weird Quartz behavior on large resolutions. + +2001-11-11 (kaper) +---------- + +- First changes to replace KMessageBox with in-window widget. +- Starting games is possible again. +- Connected Estate::changed to EstateView::estateChanged. +- Added informational message at game startup regarding current Atlantik + state (buggy, at least). +- No longer using fixed geometry. +- Dynamic (server guided) colour support for estates. +- Board configuration settings update properly again. +- Starting Player / Token relationship as we did for Estate / EstateView +- Tokens move again (animation not in operation yet). +- Right Mouse Button actions on estates are properly working again. + +2001-11-05 (kaper) +---------- + +- SelectGame and SelectConfiguration KWizard replacement widgets done. +- Better icon loading. +- Better error checking when connecting to a server. + +2001-10-19 (kaper) +---------- + +- Rewriting new game wizard into regular widgets, SelectServer done. +- Internal changes. + +2001-10-10 (kaper) +---------- + +- Estates are created dynamically now! +- Lots of internal changes regarding player and estateupdates. +- monopd API change: mortgages are now a toggle. + +2001-10-09 (kaper) +---------- + +- Portfolioviews are now generated dynamically. The Atlantik class manages + the creation and updates of the content are done by the (new) Player + class. Atlantik does act as intermediate here, though, eventually being + responsible for both player and widget management. +- Player objects/views are only created when playerupdate contains init=1 +- Estate class created, simplified grid layout code for board. +- Temporarily removed gameboard spacer code. + +2001-10-05 (kaper) +---------- + +- Qt3 updates. +- Small monopd API updates. + +2001-09-04 (kaper) +---------- + +- Been a while since the last update, due to the renaming to Atlantik (now + mostly taken care of, thus this entry), new game concept (in progress) and + my vacation to San Francisco (unfortunately no longer in progress). + +2001-08-06 (kaper) +---------- + +- Monopigator works! :-) + +2001-07-30 (kaper) +---------- + +- Estateupdate visual update fixes. +- Using can_be_mortgaged and can_be_unmortgaged attributes of monopd's + estateupdate. +- New app icons by Bart Szyszka :-) +- Token confirmation disabled for jumpToken when resizing gameboard or after + directmove instrution from server. + +2001-07-19 (kaper) +---------- + +- Network interface for trades completed, all commands and signals are in + place. + +2001-07-17 (kaper) +---------- + +- Encapsulated actual monopd API commands in gameNetwork +- Extended gameNetwork to support trading API commands +- RMB actions on estates only available when owned by player respresented by + this client + +2001-07-16 (kaper) +---------- + +- Small bugfix connecting standard roll action to correct slot. +- Code documentation! (at least for the KMonop class) +- Quartz effects! (configurable) + +2001-07-13 (neil) +---------- + +- UI: make the SelectGame widgets respond as the user may expect them to + +2001-07-01 (kaper) +---------- + +- Using KStdGameAction more and more (requires kdenonbeta version for roll + action) +- Updated to be compatible with recent monopd API changes + +2001-06-29 (kaper) +---------- + +- Bugfix: when owner=-1 in estateupdate, KMonop now clears the + portfolio/board estateviews. +- Added icon for Go. +- New config option to highlite unowned properties. +- Seperated server messages from chat. + +2001-06-27 (kaper) +---------- + +- Updated TODO (some wishlist items, changed version roadmap) +- Added a lot of i18n strings! + +2001-06-26 (kaper) +---------- + +- New config option: mortgaged properties can be grayed out on the gameboard +- Slightly increased size of portfolio estates +- Added RMB popup to estates with mortgage/unmortgage and build/sell house + actions +- Added luxury tax and community chest icons. + +2001-06-24 (kaper) +---------- + +- Server port no longer hardcoded, added extra default server (running + monopd CVS). +- Game board visualization of houses and hotels! + +2001-06-21 (kaper) +---------- + +- Network code parses attribs houses and mortgaged. + +2001-06-19 (kaper) +---------- + +- Added "end turn" button. +- Added "pay to leave jail" button. +- Added playername to config dialog. +- Updated parsing of changes in monopd. + +2001-06-17 (kaper) +---------- + +- Upgraded version to pre-0.2.0 + +2001-06-17 (kaper) (0.1.0 release) +---------- + +- Happy birthday Katy. Love, Rob. +- Integrated recent monopd updates. +- Some internal code changes. +- Added support for +- Added support for which replaced +- Tagged 0.1.0 release + +2001-06-10 +---------- + +- Portfolioestate/board recognize utilities as ownable estates. +- Prepared utilities for icons. + +2001-06-09 +---------- + +- Configuration works! +- Location confirmation upon jumpToken. +- Unowned indication also for railroads. + +2001-06-05 +---------- + +- More PlayerUpdate* changes. + +2001-06-05 +---------- + +- Added parsing of message which is new in monopd. + Chatting can thus be done through the console (lineedit field) now. +- Some changes to configure dialog, none that influence behavior though. +- Got tired of passing netw through all classes, it's pretty general anyway + so I made it a general variable. +- PlayerUpdate* changes (movetoken was removed from monopd API) + +2001-06-01 +---------- + +- Added parsing of command which is new in monopd +- Sending .t# command to monopd which is now required during token movement + +2001-05-30 +---------- + +- Token is now a nice icon (not yet transparent though, unfortunately I + couldn't get that to work right) + +2001-05-28 +---------- + +- Added board icons for train and chance estates +- Token which has turn is raised to make sure it's on top + +2001-05-27 +---------- + +- Moved some of the XML parsing code back to GameNetwork::processNode +- Changed version (back.. ssht!) to pre-0.0.2 +- Internal improvements to network/newgamewizard code (more accurate slots + and button validation) + +2001-05-27 (0.0.1 release) +---------- + +- Visual feedback showing who's turn it is +- Roll/buy buttons only enabled during turn +- Tagged 0.0.1 release + +2001-05-20 +---------- + +- Small cosmetic changes. +- New application icons, icons installdir changed. +- Configure dialog (looks nice, doesn't load/save yet) + +2001-05-16 +---------- + +- Message view autoscrolls. +- Small (cosmetic) portfolioview updates. +- Visual display on board showing which properties are still for sale + +2001-05-15 +---------- + +- New game dialog improvements (select game page checks network status and + gamelist availability +- Tokens are actually moving over the board! (instead of jumping) + +2001-05-08 +---------- + +- Portfolios built upon "playerupdate" message instead of final playerlist, + since we'll get plenty more updates anyway. +- Token placed on location hinted by playerupdate. +- Input box at bottom left corner can be used to send messages to the server + to compensate for any commands not yet implemented. Such as .n to set your + name and .r to roll. +- Cash and estates in portfolio get updated after purchase/rent. + +2001-05-07 +---------- + +- Wizard notifies game server we're starting the game upon finish. Bugfix in + KMonop::slotStartNewGame which checks whether wizard still exists before + trying to hide it. +- Portfolio overviews are built upon game start, requires monopd which sends + final attribute along with playerlist. +- Framework for token class. +- Informational messages from server are shown in output textbox. +- Only sending start game command when wizard is finished, not when it is + cancelled. + +2001-05-03 +---------- + +- Various code cleanups, keeping things neat. +- Playerlist and gamelist are automatically sent by server, manual request + no longer required. + +2001-05-02 +---------- + +- Playerlist is fetched from server. +- Playerlist is interpreted and updated when someone enters. +- Option to finish setup and launch game, closes all wizards. +- Network object moved to KMonop, where it belongs. Wizard and its pages use + pointers. + +2001-04-30 +---------- + +- NewGameDialog is now a wizard. A connection to the server is made and a + list of available games to join fetched, using Qt's XML parsing + capabilities. + +2001-04-27 +---------- + +- Turned NewGameDialog into modal dialog. + +2001-04-26 +---------- + +- Initial ChangeLog entry. diff --git a/atlantik/INSTALL b/atlantik/INSTALL new file mode 100644 index 00000000..34523708 --- /dev/null +++ b/atlantik/INSTALL @@ -0,0 +1,33 @@ +Contents +-------- + + 1. Compiling the CVS version + 2. Compiling a release tarball + 3. Installing a finished build + 4. Starting the application + +1. Compiling the CVS version +---------------------------- + +cd kdegames && make -f Makefile.cvs && ./configure && cd atlantik && make + +2. Compiling a release tarball +------------------------------ + +cd atlantik-x.y.z && ./configure && make + +3. Installing a finished build +------------------------------ + +As root; + +make install + +4. Starting the application +--------------------------- + +Click on K->Games->Board->Atlantik or run atlantik from konsole or minicli. + +Please note that Atlantik is only a client! You will need to connect to a +monopd server on the Internet or run one locally! See the README file for +more details on the Atlantik/monopd client/server concept. diff --git a/atlantik/Makefile.am b/atlantik/Makefile.am new file mode 100644 index 00000000..d4389577 --- /dev/null +++ b/atlantik/Makefile.am @@ -0,0 +1,13 @@ +SUBDIRS = libatlantic libatlantikclient libatlantikui client \ + kio_atlantik pics themes + +EXTRA_DIST = atlantik.desktop + +xdg_apps_DATA = atlantik.desktop + +rcdir = $(kde_datadir)/atlantik +rc_DATA = atlantikui.rc eventsrc + +messages: rc.cpp + $(XGETTEXT) `find . -name '*.cpp'` -o $(podir)/atlantik.pot + diff --git a/atlantik/README b/atlantik/README new file mode 100644 index 00000000..c51d2859 --- /dev/null +++ b/atlantik/README @@ -0,0 +1,94 @@ +Contents +-------- + +1. Introduction +2. Download +3. Mailinglists +4. Roadmap +5. Designer +6. Legal issues + +1. Introduction +--------------- + +Atlantik is a KDE client for playing Monopoly-like games on the monopd +network. + +Purpose of the Atlantic board game is to acquire land in major cities in +North America and Europe while being a transatlantic traveller. One of the +game modes plays like the popular real estate board game based on Atlantic +City street names. + +Atlantik was previously known as KMonop and might still be referred to as +such in some documentation. + +2. Download +----------- + +Atlantik can be downloaded from + +http://unixcode.org/atlantik/ + +and monopd can be downloaded from + +http://unixcode.org/monopd/ + +However, the latest versions of both projects are found in CVS. + +Atlantik is located in the CVS repository of the KDE project in the module +kdegames. See http://www.kde.org/anoncvs.html for instructions how to +download KDE modules from CVS. Atlantik is included in the kdegames package +since the KDE 3.1 release. + +For more information on monopd CVS see the monopd pages on +http://sourceforge.net/projects/monopd/ + +Both monopd and Atlantik are in heavy development and it is important to match +versions when connecting to a game server. One can choose a suitable server +through the Monopigator interface. + +3. Mailinglists +--------------= + +There are mailinglists available for discussion of Atlantik development. The +atlantik-devel list is for general discussion and development of the +codebase, the atlantik-cvs list has all the CVS commits in it and the +atlantik-artists list is for the development of GFX/SFX for Atlantik. + +See http://mail.kde.org/mailman/listinfo/atlantik-devel +See http://mail.kde.org/mailman/listinfo/atlantik-cvs +and http://mail.kde.org/mailman/listinfo/atlantik-artists + +on information on subscribing, posting and list archives. + +There is a seperate mailinglist for monopd related discussion: + +To subscribe, send e-mail to: monopd-devel-subscribe@lists.capsi.com +To post, send e-mail to: monopd-devel@lists.capsi.com + +4. Roadmap +---------- + +The TODO file gives a nice approximation of desired functionality and +priorities. It can be used as roadmap or checklist. + +5. Designer +----------- + +Atlantik Designer is a game board designer that will create game config +files for monopd. You can find it in KDE CVS, module kdeaddons. + +6. Legal issues +--------------- + +Many people have expressed their concerns about possible copyrights, +trademarks and patents applicable to Monopoly® and the possible implications +for Atlantik (and monopd) development and distribution. + +I believe that Atlantik and monopd are completely clear of violating any +copyrights, trademarks or patents and that there are no legal issues that +might affect development or distibution of either application. + +For more information, please read + +http://unixcode.org/atlantik/legal.html diff --git a/atlantik/README.KDE-3.0 b/atlantik/README.KDE-3.0 new file mode 100644 index 00000000..c941aaa9 --- /dev/null +++ b/atlantik/README.KDE-3.0 @@ -0,0 +1,7 @@ +Atlantik uses KExtendedSocket to connect to monopd servers. Due to some bugs +in KBufferedIO and KExtendedSocket in kdelibs, you might experience +unexplained crashes when using Atlantik with KDE 3.0, 3.0.1 or 3.0.2. + +It is recommended to run Atlantik with at least KDE 3.0.3 or 3.1 Beta1, or a +KDE CVS checkout of the HEAD branch recent enough to contain revision 1.6 of +kdelibs/kdecore/kbufferedio.cpp and revision 1.39 of kextsock.cpp. diff --git a/atlantik/README.packaging b/atlantik/README.packaging new file mode 100644 index 00000000..90608a7c --- /dev/null +++ b/atlantik/README.packaging @@ -0,0 +1,25 @@ +Notes for packaging stand-alone releases that will work with all of KDE 3.x: + +kdegames/configure.in.in +------------------------ + +#MIN_CONFIG(3.0) + +doc/ +---- + +Remove da/ and "da" SUBDIRS entry from Makefile.in, it has a non-backward +compatible entity. + +Ensure + + +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include "atlanticclient.h" +#include "atlanticclient.moc" + +AtlanticClient::AtlanticClient(QObject *parent, const char *name) : QSocket(parent, name) +{ + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); +} + +void AtlanticClient::sendData(const QString &data) +{ + writeBlock(data.latin1(), data.length()); +} + +void AtlanticClient::readData() +{ + if (canReadLine()) + { + emit clientInput(this, readLine()); + + // There might be more data + QTimer::singleShot(0, this, SLOT(readData())); + } + else + { + // Maximum message size. Messages won't get bigger than 32k anyway, so + // if we didn't receive a newline by now, we probably won't anyway. + if (bytesAvailable() > (1024 * 32)) + flush(); + } +} diff --git a/atlantik/atlanticd/atlanticclient.h b/atlantik/atlanticd/atlanticclient.h new file mode 100644 index 00000000..7b47b0f3 --- /dev/null +++ b/atlantik/atlanticd/atlanticclient.h @@ -0,0 +1,36 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef CLIENT_H +#define CLIENT_H + +#include + +class AtlanticClient : public QSocket +{ +Q_OBJECT + +public: + AtlanticClient(QObject *parent = 0, const char *name = 0); + void sendData(const QString &data); + +private slots: + void readData(); + +signals: + void clientInput(AtlanticClient *client, const QString &data); +}; +#endif diff --git a/atlantik/atlanticd/atlanticdaemon.cpp b/atlantik/atlanticd/atlanticdaemon.cpp new file mode 100644 index 00000000..3fb80cf0 --- /dev/null +++ b/atlantik/atlanticd/atlanticdaemon.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include + +#include "atlanticclient.h" +#include "atlanticdaemon.h" +#include "atlanticdaemon.moc" +#include "serversocket.h" + +AtlanticDaemon::AtlanticDaemon() +{ + m_serverSocket = new ServerSocket(1234, 100); + connect(m_serverSocket, SIGNAL(newClient(AtlanticClient *)), this, SLOT(newClient(AtlanticClient *))); + + m_atlanticCore = new AtlanticCore(this, "atlanticCore"); + + // Create socket for Monopigator + m_monopigatorSocket = new QSocket(); + connect(m_monopigatorSocket, SIGNAL(connected()), this, SLOT(monopigatorConnected())); + + // Register server + monopigatorRegister(); +} + +AtlanticDaemon::~AtlanticDaemon() +{ + delete m_monopigatorSocket; +} + +void AtlanticDaemon::monopigatorRegister() +{ + m_monopigatorSocket->connectToHost("gator.monopd.net", 80); +} + +void AtlanticDaemon::monopigatorConnected() +{ + QString get = "GET /register.php?host=capsi.com&port=1234&version=atlanticd-prototype HTTP/1.1\nHost: gator.monopd.net\n\n"; + m_monopigatorSocket->writeBlock(get.latin1(), get.length()); + m_monopigatorSocket->close(); + + // Monopigator clears old entries, so keep registering every 180s + QTimer::singleShot(180000, this, SLOT(monopigatorRegister())); +} + +void AtlanticDaemon::newClient(AtlanticClient *client) +{ + m_clients.append(client); + + connect(client, SIGNAL(clientInput(AtlanticClient *, const QString &)), this, SLOT(clientInput(AtlanticClient *, const QString &))); +} + +void AtlanticDaemon::clientInput(AtlanticClient *client, const QString &data) +{ +} diff --git a/atlantik/atlanticd/atlanticdaemon.h b/atlantik/atlanticd/atlanticdaemon.h new file mode 100644 index 00000000..729a960e --- /dev/null +++ b/atlantik/atlanticd/atlanticdaemon.h @@ -0,0 +1,48 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIC_ATLANTICDAEMON_H +#define ATLANTIC_ATLANTICDAEMON_H + +#include + +class QSocket; + +class AtlanticCore; +class AtlanticClient; +class ServerSocket; + +class AtlanticDaemon : public QObject +{ +Q_OBJECT + +public: + AtlanticDaemon(); + ~AtlanticDaemon(); +private slots: + void monopigatorRegister(); + void monopigatorConnected(); + void newClient(AtlanticClient *client); + void clientInput(AtlanticClient *client, const QString &data); + +private: + QSocket *m_monopigatorSocket; + ServerSocket *m_serverSocket; + AtlanticCore *m_atlanticCore; + QPtrList m_clients; +}; + +#endif diff --git a/atlantik/atlanticd/main.cpp b/atlantik/atlanticd/main.cpp new file mode 100644 index 00000000..235dcd00 --- /dev/null +++ b/atlantik/atlanticd/main.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "atlanticdaemon.h" + +int main(int argc, char *argv[]) +{ + new AtlanticDaemon(); + + QApplication qapplication(argc, argv); + qapplication.exec(); +} diff --git a/atlantik/atlanticd/serversocket.cpp b/atlantik/atlanticd/serversocket.cpp new file mode 100644 index 00000000..2056a754 --- /dev/null +++ b/atlantik/atlanticd/serversocket.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "atlanticclient.h" +#include "serversocket.h" + +ServerSocket::ServerSocket(int port, int backlog) : QServerSocket(port, backlog) +{ +} + +void ServerSocket::newConnection(int socket) +{ + AtlanticClient *client = new AtlanticClient(this, "socket"); + client->setSocket(socket); + + emit newClient(client); +} + +#include "serversocket.moc" diff --git a/atlantik/atlanticd/serversocket.h b/atlantik/atlanticd/serversocket.h new file mode 100644 index 00000000..fce347e9 --- /dev/null +++ b/atlantik/atlanticd/serversocket.h @@ -0,0 +1,35 @@ +// Copyright (c) 2002 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef SERVERSOCKET_H +#define SERVERSOCKET_H + +#include + +class AtlanticClient; + +class ServerSocket : public QServerSocket +{ +Q_OBJECT + +public: + ServerSocket(int port, int backlog); + void newConnection(int socket); + +signals: + void newClient(AtlanticClient *client); +}; +#endif diff --git a/atlantik/atlantik.desktop b/atlantik/atlantik.desktop new file mode 100644 index 00000000..d5191956 --- /dev/null +++ b/atlantik/atlantik.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Exec=atlantik -caption "%c" %i %m +Name=Atlantik +Name[ar]=لعبة الرقعة (Atlantik) +Name[be]=Ðтлантыка +Name[bn]=আটলানà§à¦Ÿà¦¿à¦• +Name[eo]=Atlantiko +Name[fi]=Monopoli +Name[hi]=अटलांटिक +Name[lv]=Atlantija +Name[ne]=à¤à¤Ÿà¤²à¤¾à¤¨à¥à¤Ÿà¤¿à¤• +Name[pa]=à¨à¨Ÿà¨²à¨¾à¨‚ਟਿਕ +Name[ta]=அடà¯à®²à®¾à®©à¯à®Ÿà®¿à®•à¯ +Name[tg]=Ðтлантик +Name[th]=à¹à¸­à¸•à¹à¸¥à¸™à¸•à¸´à¸„ +Name[wa]=Atlantike +Name[zh_TW]=Atlantik å¤§å¯Œç¿ +Name[zu]=I-Atlantik +GenericName=Monopoly®-like Board Games +GenericName[ar]=ألعاب الرقعة الشبيهة بمونوبولي +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ ÐœÐ°Ð½Ð°Ð¿Ð¾Ð»Ñ–Ñ +GenericName[bg]=Монополи® +GenericName[bn]=মনোপলি®-জাতীয় ছকভিতà§à¦¤à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari a seurt gant Monopoly® +GenericName[bs]=Igre nalik na Monopol® +GenericName[ca]=Jocs de taula semblants al Monopoly® +GenericName[cs]=Deskové hry podobné Monopoly® +GenericName[cy]=Gêmau Bwrdd tebyg i Monopoly® +GenericName[da]=Matador®-lignende brætspil +GenericName[de]=Monopoly®-ähnliche Brettspiele +GenericName[el]=ΕπιτÏαπέζια παιχνίδια παÏόμοια με το Monopoly® +GenericName[eo]=Monopoly-similaj bretludoj +GenericName[es]=Juegos de tablero estilo Monopoly® +GenericName[et]=Monopoli stiilis mängud +GenericName[eu]=Monopoly® bezalako mahai jokuak +GenericName[fa]=بازیهای شبیه تک قطبی تخته +GenericName[fi]=Monopoli®-tyyliset lautapelit +GenericName[fr]=Jeux de plateau dans le style du Monopoly® +GenericName[he]=משחקי לוח נוסח Monopoly® +GenericName[hi]=मोनोपॉली-®-जैसे बिसात के खेल +GenericName[hr]=Igre poput Monopola® +GenericName[hu]=Monopoly-szerű játék +GenericName[is]=Monopoly®- Borðleikir +GenericName[it]=Gioco da tavolo simile a Monopoly® +GenericName[ja]=モノãƒãƒªãƒ¼ã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ដារ​ដូច Monopoly® +GenericName[lt]=Monopolio tipo stalo žaidimas +GenericName[lv]=Monopoly® lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игри на табла во Ñтилот на Монопол® +GenericName[nb]=Monopol-aktige brettspill +GenericName[nds]=Monopoly-liek Spelen +GenericName[ne]=à¤à¤•à¤¾à¤§à¤¿à¤•à¤¾à¤°Â® जसà¥à¤¤à¥‹ बोरà¥à¤¡ खेल +GenericName[nl]=Monopoly®-achtige bordspellen +GenericName[nn]=Monopol®-liknande brettspel +GenericName[pa]=Monopoly®-ਵਰਗੀ ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa podobna do Monopoly® +GenericName[pt]=Jogo de Tabuleiro tipo Monopoly® +GenericName[pt_BR]=Jogo parecido com Banco Imobiliário® +GenericName[ro]=Un joc de tip Monopoly® +GenericName[ru]=Ðтлантик +GenericName[se]=Monopol®-lágan duolbbášspeallu +GenericName[sk]=Hry typu Monopoly® +GenericName[sl]=PloÅ¡Äadna igra, podobna Monopoliju® +GenericName[sr]=Игре на табли налик на Монопол +GenericName[sr@Latn]=Igre na tabli nalik na Monopol +GenericName[sv]=Monopol®-liknande brädspel +GenericName[ta]=Monopoly போல போரà¯à®Ÿà¯ விளையாடà¯à®Ÿà¯à®•à®³à¯ +GenericName[tg]=Бозии Рӯимизӣ ба монанди МонополиÑ® +GenericName[tr]=Monopoly®-benzeri tahta oyunları +GenericName[uk]=Ігри на дошці Ñхожі на гру МонополіÑ® +GenericName[uz]=Monopoly® oÊ»yiniga oÊ»xshagan stol oÊ»yini +GenericName[uz@cyrillic]=Monopoly® ўйинига ўхшаган Ñтол ўйини +GenericName[ven]=Mutambo wa kha Bodo unanga Monopoly® +GenericName[wa]=Djeus di platea djinre monopoly +GenericName[zh_CN]=类似 Monopoly® çš„æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=é¡žä¼¼Monopoly®大富ç¿æ£‹ç›¤éŠæˆ² +GenericName[zu]=Imidlalo efana ne-Monopoly yebhodi +Type=Application +DocPath=atlantik/index.html +Terminal=false +Icon=atlantik +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/atlantik/atlantikui.rc b/atlantik/atlantikui.rc new file mode 100644 index 00000000..1f37d2f9 --- /dev/null +++ b/atlantik/atlantikui.rc @@ -0,0 +1,24 @@ + + + + &Game + + + &Move + + + + + + + + + + + + + + + + + diff --git a/atlantik/client/Makefile.am b/atlantik/client/Makefile.am new file mode 100644 index 00000000..25eb24e1 --- /dev/null +++ b/atlantik/client/Makefile.am @@ -0,0 +1,11 @@ +bin_PROGRAMS = atlantik +INCLUDES = -I$(top_srcdir)/libkdegames -I$(srcdir)/../libatlantic -I$(srcdir)/../libatlantikclient -I$(srcdir)/../libatlantikui $(all_includes) +atlantik_LDFLAGS = $(all_libraries) $(KDE_RPATH) +atlantik_LDADD = ../libatlantikui/libatlantikui.la ../libatlantikclient/libatlantikclient.la $(LIB_KDEGAMES) $(LIB_KIO) +atlantik_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +atlantik_SOURCES = atlantik.cpp configdlg.cpp event.cpp eventlogwidget.cpp \ + main.cpp monopigator.cpp selectconfiguration_widget.cpp \ + selectgame_widget.cpp selectserver_widget.cpp + +METASOURCES = AUTO diff --git a/atlantik/client/atlantik.cpp b/atlantik/client/atlantik.cpp new file mode 100644 index 00000000..56bae64b --- /dev/null +++ b/atlantik/client/atlantik.cpp @@ -0,0 +1,853 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#undef KDE_3_2_FEATURES +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0) + #define KDE_3_2_FEATURES +#endif +#endif + +#include + +#include +#include +#include +#include +#include +#include "atlantik.moc" + +#include + +#include +#include + +#include "eventlogwidget.h" +#include "main.h" +#include "selectserver_widget.h" +#include "selectgame_widget.h" +#include "selectconfiguration_widget.h" + +LogTextEdit::LogTextEdit( QWidget *parent, const char *name ) : QTextEdit( parent, name ) +{ +#ifdef KDE_3_2_FEATURES + m_clear = KStdAction::clear( this, SLOT( clear() ), 0 ); +#else + m_clear = new KAction( i18n("Clear"), "clear", NULL, this, SLOT( clear() ), static_cast(0), "clear" ); +#endif + m_selectAll = KStdAction::selectAll( this, SLOT( selectAll() ), 0 ); + m_copy = KStdAction::copy( this, SLOT( copy() ), 0 ); +} + +LogTextEdit::~LogTextEdit() +{ + delete m_clear; + delete m_selectAll; + delete m_copy; +} + +QPopupMenu *LogTextEdit::createPopupMenu( const QPoint & ) +{ + QPopupMenu *rmbMenu = new QPopupMenu( this ); + m_clear->plug( rmbMenu ); + rmbMenu->insertSeparator(); + m_copy->setEnabled( hasSelectedText() ); + m_copy->plug( rmbMenu ); + m_selectAll->plug( rmbMenu ); + + return rmbMenu; +} + +Atlantik::Atlantik () + : KMainWindow (), + m_runningGame( false ) +{ + // Read application configuration + readConfig(); + + // Toolbar: Game +// KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection(), "game_new"); + m_showEventLog = new KAction(i18n("Show Event &Log")/*, "atlantik_showeventlog"*/, CTRL+Key_L, this, SLOT(showEventLog()), actionCollection(), "showeventlog"); + KStdGameAction::quit(kapp, SLOT(closeAllWindows()), actionCollection(), "game_quit"); + + // Toolbar: Settings + KStdAction::preferences(this, SLOT(slotConfigure()), actionCollection()); + KStdAction::configureNotifications(this, SLOT(configureNotifications()), actionCollection()); + + // Initialize pointers to 0L + m_configDialog = 0; + m_board = 0; + m_eventLogWidget = 0; + m_selectServer = 0; + m_selectGame = 0; + m_selectConfiguration = 0; + m_atlantikNetwork = 0; + + // Game and network core + m_atlanticCore = new AtlanticCore(this, "atlanticCore"); + connect(m_atlanticCore, SIGNAL(createGUI(Player *)), this, SLOT(newPlayer(Player *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Player *)), this, SLOT(removeGUI(Player *))); + connect(m_atlanticCore, SIGNAL(createGUI(Trade *)), this, SLOT(newTrade(Trade *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Trade *)), this, SLOT(removeGUI(Trade *))); + + initEventLog(); + initNetworkObject(); + + // Menu,toolbar: Move + m_roll = KStdGameAction::roll(this, SIGNAL(rollDice()), actionCollection()); + m_roll->setEnabled(false); + m_buyEstate = new KAction(i18n("&Buy"), "atlantik_buy_estate", CTRL+Key_B, this, SIGNAL(buyEstate()), actionCollection(), "buy_estate"); + m_buyEstate->setEnabled(false); + m_auctionEstate = new KAction(i18n("&Auction"), "auction", CTRL+Key_A, this, SIGNAL(auctionEstate()), actionCollection(), "auction"); + m_auctionEstate->setEnabled(false); + m_endTurn = KStdGameAction::endTurn(this, SIGNAL(endTurn()), actionCollection()); + m_endTurn->setEnabled(false); + m_jailCard = new KAction(i18n("Use Card to Leave Jail")/*, "atlantik_move_jail_card"*/, 0, this, SIGNAL(jailCard()), actionCollection(), "move_jailcard"); + m_jailCard->setEnabled(false); + m_jailPay = new KAction(i18n("&Pay to Leave Jail"), "jail_pay", CTRL+Key_P, this, SIGNAL(jailPay()), actionCollection(), "move_jailpay"); + m_jailPay->setEnabled(false); + m_jailRoll = new KAction(i18n("Roll to Leave &Jail")/*, "atlantik_move_jail_roll"*/, CTRL+Key_J, this, SIGNAL(jailRoll()), actionCollection(), "move_jailroll"); + m_jailRoll->setEnabled(false); + + // Mix code and XML into GUI + KMainWindow::createGUI(); + applyMainWindowSettings( KGlobal::config(), "AtlantikMainWindow" ); + KMainWindow::statusBar()->insertItem("Atlantik " ATLANTIK_VERSION_STRING, 0); + KMainWindow::statusBar()->insertItem(QString::null, 1); + connect(statusBar(), SIGNAL(released(int)), this, SLOT(statusBarClick(int))); + + // Main widget, containing all others + m_mainWidget = new QWidget(this, "main"); + m_mainWidget->show(); + m_mainLayout = new QGridLayout(m_mainWidget, 3, 2); + setCentralWidget(m_mainWidget); + + // Vertical view area for portfolios. + m_portfolioScroll = new QScrollView(m_mainWidget, "pfScroll"); + m_mainLayout->addWidget( m_portfolioScroll, 0, 0 ); + m_portfolioScroll->setHScrollBarMode( QScrollView::AlwaysOff ); + m_portfolioScroll->setResizePolicy( QScrollView::AutoOneFit ); + m_portfolioScroll->setFixedHeight( 200 ); + m_portfolioScroll->hide(); + + m_portfolioWidget = new QWidget( m_portfolioScroll->viewport(), "pfWidget" ); + m_portfolioScroll->addChild( m_portfolioWidget ); + m_portfolioWidget->show(); + + m_portfolioLayout = new QVBoxLayout(m_portfolioWidget); + m_portfolioViews.setAutoDelete(true); + + // Nice label +// m_portfolioLabel = new QLabel(i18n("Players"), m_portfolioWidget, "pfLabel"); +// m_portfolioLayout->addWidget(m_portfolioLabel); +// m_portfolioLabel->show(); + + // Text view for chat and status messages from server. + m_serverMsgs = new LogTextEdit(m_mainWidget, "serverMsgs"); + m_serverMsgs->setTextFormat(QTextEdit::PlainText); + m_serverMsgs->setReadOnly(true); + m_serverMsgs->setHScrollBarMode(QScrollView::AlwaysOff); + m_serverMsgs->setMinimumWidth(200); + m_mainLayout->addWidget(m_serverMsgs, 1, 0); + + // LineEdit to enter commands and chat messages. + m_input = new QLineEdit(m_mainWidget, "input"); + m_mainLayout->addWidget(m_input, 2, 0); + + m_serverMsgs->setFocusProxy(m_input); + + connect(m_input, SIGNAL(returnPressed()), this, SLOT(slotSendMsg())); + + // Set stretching where we want it. + m_mainLayout->setRowStretch(1, 1); // make m_board+m_serverMsgs stretch vertically, not the rest + m_mainLayout->setColStretch(1, 1); // make m_board stretch horizontally, not the rest + + // Check command-line args to see if we need to connect or show Monopigator window + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + QCString host = args->getOption("host"); + QCString port = args->getOption("port"); + if (!host.isNull() && !port.isNull()) + m_atlantikNetwork->serverConnect(host, port.toInt()); + else + showSelectServer(); +} + +void Atlantik::readConfig() +{ + // Read configuration settings + KConfig *config = kapp->config(); + + // General configuration + config->setGroup("General"); + m_config.chatTimestamps = config->readBoolEntry("ChatTimeStamps", false); + + // Personalization configuration + config->setGroup("Personalization"); + m_config.playerName = config->readEntry("PlayerName", "Atlantik"); + m_config.playerImage = config->readEntry("PlayerImage", "cube.png"); + + // Board configuration + config->setGroup("Board"); + m_config.indicateUnowned = config->readBoolEntry("IndicateUnowned", true); + m_config.highliteUnowned = config->readBoolEntry("HighliteUnowned", false); + m_config.darkenMortgaged = config->readBoolEntry("DarkenMortgaged", true); + m_config.animateTokens = config->readBoolEntry("AnimateToken", false); + m_config.quartzEffects = config->readBoolEntry("QuartzEffects", true); + + // Meta server configuation + config->setGroup("Monopigator"); + m_config.connectOnStart = config->readBoolEntry("ConnectOnStart", false); + m_config.hideDevelopmentServers = config->readBoolEntry("HideDevelopmentServers", true); + + // Portfolio colors + config->setGroup("WM"); + QColor activeDefault(204, 204, 204), inactiveDefault(153, 153, 153); + m_config.activeColor = config->readColorEntry("activeBackground", &activeDefault); + m_config.inactiveColor = config->readColorEntry("inactiveBlend", &inactiveDefault); +} + +void Atlantik::newPlayer(Player *player) +{ + initBoard(); + m_board->addToken(player); + addPortfolioView(player); + + // Player::changed() is not connected until later this method, so + // we'd better force an update. + playerChanged(player); + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + connect(player, SIGNAL(gainedTurn()), this, SLOT(gainedTurn())); + connect(player, SIGNAL(changed(Player *)), m_board, SLOT(playerChanged(Player *))); + + KNotifyClient::event(winId(), "newplayer"); +} + +void Atlantik::newEstate(Estate *estate) +{ + initBoard(); + m_board->addEstateView(estate, m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects); +} + +void Atlantik::newTrade(Trade *trade) +{ + TradeDisplay *tradeDisplay = new TradeDisplay(trade, m_atlanticCore, 0, "tradeDisplay"); + m_tradeGUIMap[trade] = tradeDisplay; + tradeDisplay->show(); +} + +void Atlantik::newAuction(Auction *auction) +{ + initBoard(); + m_board->addAuctionWidget(auction); +} + +void Atlantik::removeGUI(Player *player) +{ + // Find and remove portfolioview + PortfolioView *portfolioView = findPortfolioView(player); + if (portfolioView) + m_portfolioViews.remove(portfolioView); + + if (m_board) + m_board->removeToken(player); +} + +void Atlantik::removeGUI(Trade *trade) +{ + if (TradeDisplay *tradeDisplay = m_tradeGUIMap[trade]) + delete tradeDisplay; +} + +void Atlantik::showSelectServer() +{ + if (m_selectServer) + return; + + m_selectServer = new SelectServer(m_config.connectOnStart, m_config.hideDevelopmentServers, m_mainWidget, "selectServer"); + m_mainLayout->addMultiCellWidget(m_selectServer, 0, 2, 1, 1); + m_selectServer->show(); + + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + m_atlanticCore->reset(true); + initNetworkObject(); + + connect(m_selectServer, SIGNAL(serverConnect(const QString, int)), m_atlantikNetwork, SLOT(serverConnect(const QString, int))); + connect(m_selectServer, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &))); + + m_selectServer->slotRefresh( m_config.connectOnStart ); +} + +void Atlantik::showSelectGame() +{ + if (m_selectGame) + return; + + m_selectGame = new SelectGame(m_atlanticCore, m_mainWidget, "selectGame"); + m_atlanticCore->emitGames(); + + m_mainLayout->addMultiCellWidget(m_selectGame, 0, 2, 1, 1); + m_selectGame->show(); + + // Reset core and GUI + if (m_board) + { + m_board->hide(); + m_board->reset(); +// delete m_board; +// m_board = 0; + + // m_portfolioViews.clear(); + m_atlanticCore->reset(); + } + + if (m_selectServer) + { + delete m_selectServer; + m_selectServer = 0; + } + if (m_selectConfiguration) + { + delete m_selectConfiguration; + m_selectConfiguration = 0; + } + + connect(m_selectGame, SIGNAL(joinGame(int)), m_atlantikNetwork, SLOT(joinGame(int))); + connect(m_selectGame, SIGNAL(newGame(const QString &)), m_atlantikNetwork, SLOT(newGame(const QString &))); + connect(m_selectGame, SIGNAL(leaveServer()), this, SLOT(showSelectServer())); + connect(m_selectGame, SIGNAL(msgStatus(const QString &)), this, SLOT(slotMsgStatus(const QString &))); +} + +void Atlantik::showSelectConfiguration() +{ + if (m_selectConfiguration) + return; + + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + m_selectConfiguration = new SelectConfiguration(m_atlanticCore, m_mainWidget, "selectConfiguration"); + m_mainLayout->addMultiCellWidget(m_selectConfiguration, 0, 2, 1, 1); + m_selectConfiguration->show(); + + connect(m_atlanticCore, SIGNAL(createGUI(ConfigOption *)), m_selectConfiguration, SLOT(addConfigOption(ConfigOption *))); + connect(m_atlantikNetwork, SIGNAL(gameOption(QString, QString, QString, QString, QString)), m_selectConfiguration, SLOT(gameOption(QString, QString, QString, QString, QString))); + connect(m_atlantikNetwork, SIGNAL(gameInit()), m_selectConfiguration, SLOT(initGame())); + connect(m_selectConfiguration, SIGNAL(startGame()), m_atlantikNetwork, SLOT(startGame())); + connect(m_selectConfiguration, SIGNAL(leaveGame()), m_atlantikNetwork, SLOT(leaveGame())); + connect(m_selectConfiguration, SIGNAL(changeOption(int, const QString &)), m_atlantikNetwork, SLOT(changeOption(int, const QString &))); + connect(m_selectConfiguration, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString))); + connect(m_selectConfiguration, SIGNAL(iconSelected(const QString &)), m_atlantikNetwork, SLOT(setImage(const QString &))); + connect(m_selectConfiguration, SIGNAL(statusMessage(const QString &)), this, SLOT(slotMsgStatus(const QString &))); +} + +void Atlantik::initBoard() +{ + if (m_board) + return; + + m_board = new AtlantikBoard(m_atlanticCore, 40, AtlantikBoard::Play, m_mainWidget, "board"); + m_board->setViewProperties(m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens); + + connect(m_atlantikNetwork, SIGNAL(displayDetails(QString, bool, bool, Estate *)), m_board, SLOT(insertDetails(QString, bool, bool, Estate *))); + connect(m_atlantikNetwork, SIGNAL(addCommandButton(QString, QString, bool)), m_board, SLOT(displayButton(QString, QString, bool))); + connect(m_atlantikNetwork, SIGNAL(addCloseButton()), m_board, SLOT(addCloseButton())); + connect(m_board, SIGNAL(tokenConfirmation(Estate *)), m_atlantikNetwork, SLOT(tokenConfirmation(Estate *))); + connect(m_board, SIGNAL(buttonCommand(QString)), m_atlantikNetwork, SLOT(writeData(QString))); +} + +void Atlantik::showBoard() +{ + if (m_selectGame) + { + delete m_selectGame; + m_selectGame = 0; + } + + if (m_selectConfiguration) + { + delete m_selectConfiguration; + m_selectConfiguration = 0; + } + + if (!m_board) + initBoard(); + + m_runningGame = true; + + m_mainLayout->addMultiCellWidget(m_board, 0, 2, 1, 1); + m_board->displayDefault(); + m_board->show(); + + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); *it; ++it) + if ((portfolioView = dynamic_cast(*it))) + portfolioView->buildPortfolio(); +} + +void Atlantik::freezeBoard() +{ + if (!m_board) + showBoard(); + + m_runningGame = false; + // TODO: m_board->freeze(); +} + +void Atlantik::slotNetworkConnected() +{ +} + +void Atlantik::slotNetworkError(int errnum) +{ + QString errMsg(i18n("Error connecting: ")); + + switch (m_atlantikNetwork->status()) + { + case IO_ConnectError: + if (errnum == ECONNREFUSED) + errMsg.append(i18n("connection refused by host.")); + else + errMsg.append(i18n("could not connect to host.")); + break; + + case IO_LookupError: + errMsg.append(i18n("host not found.")); + break; + + default: + errMsg.append(i18n("unknown error.")); + } + + serverMsgsAppend(errMsg); + + // Re-init network object + initNetworkObject(); +} + +void Atlantik::networkClosed(int status) +{ + switch( status ) + { + case KBufferedIO::involuntary: + slotMsgStatus( i18n("Connection with server %1:%2 lost.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") ); + showSelectServer(); + break; + default: + if ( !m_atlantikNetwork->host().isEmpty() ) + slotMsgStatus( i18n("Disconnected from %1:%2.").arg(m_atlantikNetwork->host()).arg(m_atlantikNetwork->port()), QString("connect_no") ); + break; + } +} + +void Atlantik::slotConfigure() +{ + if (m_configDialog == 0) + m_configDialog = new ConfigDialog(this); + m_configDialog->show(); + + connect(m_configDialog, SIGNAL(okClicked()), this, SLOT(slotUpdateConfig())); +} + +void Atlantik::showEventLog() +{ + if (!m_eventLogWidget) + m_eventLogWidget = new EventLogWidget(m_eventLog, 0); + m_eventLogWidget->show(); +} + +void Atlantik::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void Atlantik::slotUpdateConfig() +{ + KConfig *config=kapp->config(); + bool optBool, configChanged = false; + QString optStr; + + optBool = m_configDialog->chatTimestamps(); + if (m_config.chatTimestamps != optBool) + { + m_config.chatTimestamps = optBool; + configChanged = true; + } + + optStr = m_configDialog->playerName(); + if (m_config.playerName != optStr) + { + m_config.playerName = optStr; + m_atlantikNetwork->setName(optStr); + } + + optStr = m_configDialog->playerImage(); + if (m_config.playerImage != optStr) + { + m_config.playerImage = optStr; + m_atlantikNetwork->setImage(optStr); + } + + optBool = m_configDialog->indicateUnowned(); + if (m_config.indicateUnowned != optBool) + { + m_config.indicateUnowned = optBool; + configChanged = true; + } + + optBool = m_configDialog->highliteUnowned(); + if (m_config.highliteUnowned != optBool) + { + m_config.highliteUnowned = optBool; + configChanged = true; + } + + optBool = m_configDialog->darkenMortgaged(); + if (m_config.darkenMortgaged != optBool) + { + m_config.darkenMortgaged = optBool; + configChanged = true; + } + + optBool = m_configDialog->animateToken(); + if (m_config.animateTokens != optBool) + { + m_config.animateTokens = optBool; + configChanged = true; + } + + optBool = m_configDialog->quartzEffects(); + if (m_config.quartzEffects != optBool) + { + m_config.quartzEffects = optBool; + configChanged = true; + } + + optBool = m_configDialog->connectOnStart(); + if (m_config.connectOnStart != optBool) + { + m_config.connectOnStart = optBool; + configChanged = true; + } + + optBool = m_configDialog->hideDevelopmentServers(); + if (m_config.hideDevelopmentServers != optBool) + { + m_config.hideDevelopmentServers = optBool; + if (m_selectServer) + m_selectServer->setHideDevelopmentServers(optBool); + + configChanged = true; + } + + config->setGroup("General"); + config->writeEntry("ChatTimeStamps", m_config.chatTimestamps); + + config->setGroup("Personalization"); + config->writeEntry("PlayerName", m_config.playerName); + config->writeEntry("PlayerImage", m_config.playerImage); + + config->setGroup("Board"); + config->writeEntry("IndicateUnowned", m_config.indicateUnowned); + config->writeEntry("HighliteUnowned", m_config.highliteUnowned); + config->writeEntry("DarkenMortgaged", m_config.darkenMortgaged); + config->writeEntry("AnimateToken", m_config.animateTokens); + config->writeEntry("QuartzEffects", m_config.quartzEffects); + + config->setGroup("Monopigator"); + config->writeEntry("ConnectOnStart", m_config.connectOnStart); + config->writeEntry("HideDevelopmentServers", m_config.hideDevelopmentServers); + + config->sync(); + + if (configChanged && m_board) + m_board->setViewProperties(m_config.indicateUnowned, m_config.highliteUnowned, m_config.darkenMortgaged, m_config.quartzEffects, m_config.animateTokens); +} + +void Atlantik::slotSendMsg() +{ + m_atlantikNetwork->cmdChat(m_input->text()); + m_input->setText(QString::null); +} + +void Atlantik::slotMsgInfo(QString msg) +{ + serverMsgsAppend(msg); +} + +void Atlantik::slotMsgError(QString msg) +{ + serverMsgsAppend("Error: " + msg); +} + +void Atlantik::slotMsgStatus(const QString &message, const QString &icon) +{ + KMainWindow::statusBar()->changeItem(message, 1); + m_eventLog->addEvent(message, icon); +} + +void Atlantik::slotMsgChat(QString player, QString msg) +{ + if (m_config.chatTimestamps) + { + QTime time = QTime::currentTime(); + serverMsgsAppend(QString("[%1] %2: %3").arg(time.toString("hh:mm")).arg(player).arg(msg)); + } + else + serverMsgsAppend(player + ": " + msg); + KNotifyClient::event(winId(), "chat"); +} + +void Atlantik::serverMsgsAppend(QString msg) +{ + // Use append, not setText(old+new) because that one doesn't wrap + m_serverMsgs->append(msg); + m_serverMsgs->ensureVisible(0, m_serverMsgs->contentsHeight()); +} + +void Atlantik::playerChanged(Player *player) +{ + PortfolioView *portfolioView = findPortfolioView(player); + if (!portfolioView) + portfolioView = addPortfolioView(player); + + Player *playerSelf = m_atlanticCore->playerSelf(); + if (player == playerSelf) + { + // We changed ourselves.. + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); *it; ++it) + if ((portfolioView = dynamic_cast(*it))) + { + // Clear all portfolios if we're not in game + if ( !player->game() ) + portfolioView->clearPortfolio(); + + // Show players in our game, hide the rest + Player *pTmp = portfolioView->player(); + if (pTmp->game() == playerSelf->game()) + portfolioView->show(); + else + portfolioView->hide(); + } + if (!player->game()) + showSelectGame(); + else + { + if ( !m_board || m_board->isHidden() ) + showSelectConfiguration(); + } + + m_roll->setEnabled(player->canRoll()); + m_buyEstate->setEnabled(player->canBuy()); + m_auctionEstate->setEnabled(player->canAuction()); + + // TODO: Should be more finetuned, but monopd doesn't send can_endturn can_payjail can_jailroll yet + m_endTurn->setEnabled(player->hasTurn() && !(player->canRoll() || player->canBuy() || player->inJail())); + m_jailCard->setEnabled(player->canUseCard()); + m_jailPay->setEnabled(player->hasTurn() && player->inJail()); + m_jailRoll->setEnabled(player->hasTurn() && player->inJail()); + } + else + { + // Another player changed, check if we need to show or hide + // his/her portfolioView. + if (playerSelf) + { + if (player->game() == playerSelf->game()) + portfolioView->show(); + else + portfolioView->hide(); + } + else if ( !player->game() ) + portfolioView->hide(); + } +} + +void Atlantik::gainedTurn() +{ + KNotifyClient::event(winId(), "gainedturn", i18n("It is your turn now.") ); +} + +void Atlantik::initEventLog() +{ + m_eventLog = new EventLog(); +} + +void Atlantik::initNetworkObject() +{ + if (m_atlantikNetwork) + { + m_atlantikNetwork->reset(); + return; + } + + m_atlantikNetwork = new AtlantikNetwork(m_atlanticCore); + connect(m_atlantikNetwork, SIGNAL(msgInfo(QString)), this, SLOT(slotMsgInfo(QString))); + connect(m_atlantikNetwork, SIGNAL(msgError(QString)), this, SLOT(slotMsgError(QString))); + connect(m_atlantikNetwork, SIGNAL(msgStatus(const QString &, const QString &)), this, SLOT(slotMsgStatus(const QString &, const QString &))); + connect(m_atlantikNetwork, SIGNAL(msgChat(QString, QString)), this, SLOT(slotMsgChat(QString, QString))); + + connect(m_atlantikNetwork, SIGNAL(connectionSuccess()), this, SLOT(slotNetworkConnected())); + connect(m_atlantikNetwork, SIGNAL(connectionFailed(int)), this, SLOT(slotNetworkError(int))); + connect(m_atlantikNetwork, SIGNAL(closed(int)), this, SLOT(networkClosed(int))); + + connect(m_atlantikNetwork, SIGNAL(receivedHandshake()), this, SLOT(sendHandshake())); + + connect(m_atlantikNetwork, SIGNAL(gameConfig()), this, SLOT(showSelectConfiguration())); + connect(m_atlantikNetwork, SIGNAL(gameInit()), this, SLOT(initBoard())); + connect(m_atlantikNetwork, SIGNAL(gameRun()), this, SLOT(showBoard())); + connect(m_atlantikNetwork, SIGNAL(gameEnd()), this, SLOT(freezeBoard())); + + connect(m_atlantikNetwork, SIGNAL(newEstate(Estate *)), this, SLOT(newEstate(Estate *))); + connect(m_atlantikNetwork, SIGNAL(newAuction(Auction *)), this, SLOT(newAuction(Auction *))); + + connect(m_atlantikNetwork, SIGNAL(clientCookie(QString)), this, SLOT(clientCookie(QString))); + connect(m_atlantikNetwork, SIGNAL(networkEvent(const QString &, const QString &)), m_eventLog, SLOT(addEvent(const QString &, const QString &))); + + connect(this, SIGNAL(rollDice()), m_atlantikNetwork, SLOT(rollDice())); + connect(this, SIGNAL(buyEstate()), m_atlantikNetwork, SLOT(buyEstate())); + connect(this, SIGNAL(auctionEstate()), m_atlantikNetwork, SLOT(auctionEstate())); + connect(this, SIGNAL(endTurn()), m_atlantikNetwork, SLOT(endTurn())); + connect(this, SIGNAL(jailCard()), m_atlantikNetwork, SLOT(jailCard())); + connect(this, SIGNAL(jailPay()), m_atlantikNetwork, SLOT(jailPay())); + connect(this, SIGNAL(jailRoll()), m_atlantikNetwork, SLOT(jailRoll())); +} + +void Atlantik::clientCookie(QString cookie) +{ + KConfig *config = kapp->config(); + + if (cookie.isNull()) + { + if (config->hasGroup("Reconnection")) + config->deleteGroup("Reconnection", true); + } + else if (m_atlantikNetwork) + { + config->setGroup("Reconnection"); + config->writeEntry("Host", m_atlantikNetwork->host()); + config->writeEntry("Port", m_atlantikNetwork->port()); + config->writeEntry("Cookie", cookie); + } + else + return; + + config->sync(); +} + +void Atlantik::sendHandshake() +{ + m_atlantikNetwork->setName(m_config.playerName); + m_atlantikNetwork->setImage(m_config.playerImage); + + // Check command-line args to see if we need to auto-join + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + QCString game = args->getOption("game"); + if (!game.isNull()) + m_atlantikNetwork->joinGame(game.toInt()); +} + +void Atlantik::statusBarClick(int item) +{ + if ( item == 0 ) + { + KAboutApplication dialog(kapp->aboutData(), this); + dialog.exec(); + } + else if ( item == 1) + showEventLog(); +} + +PortfolioView *Atlantik::addPortfolioView(Player *player) +{ + PortfolioView *portfolioView = new PortfolioView(m_atlanticCore, player, m_config.activeColor, m_config.inactiveColor, m_portfolioWidget); + m_portfolioViews.append(portfolioView); + if ( m_portfolioViews.count() > 0 && m_portfolioScroll->isHidden() ) + m_portfolioScroll->show(); + + connect(player, SIGNAL(changed(Player *)), portfolioView, SLOT(playerChanged())); + connect(portfolioView, SIGNAL(newTrade(Player *)), m_atlantikNetwork, SLOT(newTrade(Player *))); + connect(portfolioView, SIGNAL(kickPlayer(Player *)), m_atlantikNetwork, SLOT(kickPlayer(Player *))); + connect(portfolioView, SIGNAL(estateClicked(Estate *)), m_board, SLOT(prependEstateDetails(Estate *))); + + m_portfolioLayout->addWidget(portfolioView); + portfolioView->show(); + + return portfolioView; +} + +PortfolioView *Atlantik::findPortfolioView(Player *player) +{ + PortfolioView *portfolioView = 0; + for (QPtrListIterator it(m_portfolioViews); (portfolioView = *it) ; ++it) + if (player == portfolioView->player()) + return portfolioView; + + return 0; +} + +void Atlantik::closeEvent(QCloseEvent *e) +{ + Game *gameSelf = m_atlanticCore->gameSelf(); + Player *playerSelf = m_atlanticCore->playerSelf(); + + int result = KMessageBox::Continue; + if ( gameSelf && !playerSelf->isBankrupt() && m_runningGame ) + result = KMessageBox::warningContinueCancel( this, i18n("You are currently part of an active game. Are you sure you want to close Atlantik? If you do, you forfeit the game."), i18n("Close & Forfeit?"), i18n("Close && Forfeit") ); + + if ( result == KMessageBox::Continue ) + { + if ( m_atlantikNetwork ) + m_atlantikNetwork->leaveGame(); + + saveMainWindowSettings(kapp->config(), "AtlantikMainWindow"); + KMainWindow::closeEvent(e); + } +} diff --git a/atlantik/client/atlantik.h b/atlantik/client/atlantik.h new file mode 100644 index 00000000..94f2ca7c --- /dev/null +++ b/atlantik/client/atlantik.h @@ -0,0 +1,268 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ATLANTIK_H +#define ATLANTIK_ATLANTIK_H + +#include +#include +#include +#include +#include + +#include + +#include "configdlg.h" +#include "portfolioview.h" +#include "board.h" + +class QScrollView; + +class AtlanticCore; +class AtlantikNetwork; + +struct AtlantikConfig +{ + // General options; + bool chatTimestamps; + + // Personalization options + QString playerName, playerImage; + + // Board options + bool indicateUnowned; + bool highliteUnowned; + bool darkenMortgaged; + bool quartzEffects; + bool animateTokens; + + // Meta server options + bool connectOnStart; + bool hideDevelopmentServers; + + // Portfolio colors + QColor activeColor, inactiveColor; +}; + +class EventLog; +class EventLogWidget; +class SelectServer; +class SelectGame; +class SelectConfiguration; +class TradeDisplay; + +class Player; +class Estate; +class Trade; + +class LogTextEdit : public QTextEdit +{ +Q_OBJECT + +public: + LogTextEdit( QWidget *parent = 0, const char *name = 0 ); + virtual ~LogTextEdit(); + + QPopupMenu *createPopupMenu( const QPoint & pos ); + +private: + KAction *m_clear, *m_selectAll, *m_copy; +}; + +/** + * Main Atlantik window. + * Manages gameboard, portfolios and pretty much everything else. + * + * @author Rob Kaper + */ +class Atlantik : public KMainWindow +{ +Q_OBJECT + +public: + /** + * Create an Atlantik window. + * + */ + Atlantik(); + + /** + * Read the configuration settings using KConfig. + * + */ + void readConfig(); + + /** + * Appends a message the text view. + * + * @param msg Message to be appended. + */ + void serverMsgsAppend(QString msg); + + AtlantikConfig config() { return m_config; } + +private slots: + void showSelectServer(); + void showSelectGame(); + void showSelectConfiguration(); + void initBoard(); + void showBoard(); + void freezeBoard(); + void clientCookie(QString cookie); + void sendHandshake(); + void statusBarClick(int); + +public slots: + + /** + * A network connection has been established, so we can show the game + * list instead of the server list. + * + */ + void slotNetworkConnected(); + + /** + * An error occurred while setting up the network connection. Inform the + * user. + * + * @param errno See http://doc.trolltech.com/3.0/qsocket.html#Error-enum + */ + void slotNetworkError(int errnum); + + void networkClosed(int status); + + /** + * Creates a new modeless configure dialog or raises it when it already exists. + * + */ + void slotConfigure(); + + /** + * Opens the event log widget. + * + */ + void showEventLog(); + + /** + * Opens the KNotify dialog for configuration events. + * + */ + void configureNotifications(); + + /** + * Reads values from configuration dialog and stores them into + * global configuration struct. If values have changed, appropriate + * methods within the application are called. Configuration is saved + * to file in any case. + * + */ + void slotUpdateConfig(); + + /** + * Writes the contents of the text input field to the network + * interface and clears the text input field. + * + */ + void slotSendMsg(); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as informational message. + * + * @param msg The message to be appended. + */ + void slotMsgInfo(QString msg); + + void slotMsgStatus(const QString &message, const QString &icon = QString::null); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as error message. + * + * @param msg The error message to be appended. + */ + void slotMsgError(QString msg); + + /** + * Informs serverMsgs() to append an incoming message from the + * server to the text view as chat message. + * + * @param player The name of the player chatting. + * @param msg The chat message to be appended. + */ + void slotMsgChat(QString player, QString msg); + + void newPlayer(Player *player); + void newEstate(Estate *estate); + void newTrade(Trade *trade); + void newAuction(Auction *auction); + + void removeGUI(Player *player); + void removeGUI(Trade *trade); + + void playerChanged(Player *player); + void gainedTurn(); + +signals: + void rollDice(); + void buyEstate(); + void auctionEstate(); + void endTurn(); + void jailCard(); + void jailPay(); + void jailRoll(); + +protected: + void closeEvent(QCloseEvent *); + +private: + void initEventLog(); + void initNetworkObject(); + PortfolioView *addPortfolioView(Player *player); + PortfolioView *findPortfolioView(Player *player); + + QScrollView *m_portfolioScroll; + QWidget *m_mainWidget, *m_portfolioWidget; + QGridLayout *m_mainLayout; + QVBoxLayout *m_portfolioLayout; + + QLabel *m_portfolioLabel; + QLineEdit *m_input; + QTextEdit *m_serverMsgs; + + KAction *m_roll, *m_buyEstate, *m_auctionEstate, *m_endTurn, + *m_jailCard, *m_jailPay, *m_jailRoll, *m_configure, + *m_showEventLog; + + AtlanticCore *m_atlanticCore; + AtlantikNetwork *m_atlantikNetwork; + AtlantikConfig m_config; + + ConfigDialog *m_configDialog; + AtlantikBoard *m_board; + SelectServer *m_selectServer; + SelectGame *m_selectGame; + SelectConfiguration *m_selectConfiguration; + EventLog *m_eventLog; + EventLogWidget *m_eventLogWidget; + + QPtrList m_portfolioViews; + QMap m_tradeGUIMap; + + bool m_runningGame; +}; + +#endif diff --git a/atlantik/client/configdlg.cpp b/atlantik/client/configdlg.cpp new file mode 100644 index 00000000..a8e152b5 --- /dev/null +++ b/atlantik/client/configdlg.cpp @@ -0,0 +1,334 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include + +#include +#undef KDE_3_1_FEATURES +#undef KDE_3_3_FEATURES +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) +#define KDE_3_1_FEATURES +#endif +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,90) +#define KDE_3_3_FEATURES +#endif +#endif + +#include +#include +#include +#include +#include + +#include "atlantik.h" +#include "configdlg.moc" + +ConfigDialog::ConfigDialog(Atlantik* parent, const char *name) : KDialogBase(IconList, i18n("Configure Atlantik"), Ok|Cancel, Ok, parent, "config_atlantik", false, name) +{ + m_parent = parent; + p_general = addPage(i18n("General"), i18n("General"), BarIcon("configure", KIcon::SizeMedium)); + p_p13n = addPage(i18n("Personalization"), i18n("Personalization"), BarIcon("personal", KIcon::SizeMedium)); + p_board = addPage(i18n("Board"), i18n("Board"), BarIcon("monop_board", KIcon::SizeMedium)); + p_monopigator = addPage(i18n("Meta Server"), i18n("Meta Server"), BarIcon("network", KIcon::SizeMedium)); + + configGeneral = new ConfigGeneral(this, p_general, "configGeneral"); + configPlayer = new ConfigPlayer(this, p_p13n, "configPlayer"); + configBoard = new ConfigBoard(this, p_board, "configBoard"); + configMonopigator = new ConfigMonopigator(this, p_monopigator, "configMonopigator"); + + setMinimumSize(sizeHint()); +} + +bool ConfigDialog::chatTimestamps() +{ + return configGeneral->chatTimestamps(); +} + +bool ConfigDialog::indicateUnowned() +{ + return configBoard->indicateUnowned(); +} + +bool ConfigDialog::highliteUnowned() +{ + return configBoard->highliteUnowned(); +} + +bool ConfigDialog::darkenMortgaged() +{ + return configBoard->darkenMortgaged(); +} + +bool ConfigDialog::animateToken() +{ + return configBoard->animateToken(); +} + +bool ConfigDialog::quartzEffects() +{ + return configBoard->quartzEffects(); +} + +QString ConfigDialog::playerName() +{ + return configPlayer->playerName(); +} + +QString ConfigDialog::playerImage() +{ + return configPlayer->playerImage(); +} + +bool ConfigDialog::connectOnStart() +{ + return configMonopigator->connectOnStart(); +} + +bool ConfigDialog::hideDevelopmentServers() +{ + return configMonopigator->hideDevelopmentServers(); +} + +AtlantikConfig ConfigDialog::config() +{ + return m_parent->config(); +} + +ConfigPlayer::ConfigPlayer(ConfigDialog* configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + QLabel *label = new QLabel(i18n("Player name:"), parent); + layout->addWidget(label); + + m_playerName = new QLineEdit(parent); + layout->addWidget(m_playerName); + + QLabel *label2 = new QLabel(i18n("Player image:"), parent); + layout->addWidget(label2); + + m_playerIcon = new KPushButton(parent, "playerIcon"); + layout->addWidget(m_playerIcon); + + connect( m_playerIcon, SIGNAL(clicked()), this, SLOT(chooseImage()) ); + + layout->addStretch(1); + + reset(); +} + +QString ConfigPlayer::playerName() +{ + return m_playerName->text(); +} + +QString ConfigPlayer::playerImage() +{ + return m_playerImage; +} +void ConfigPlayer::chooseImage() +{ + KIconDialog iconDialog( this, "iconDialog" ); +#ifdef KDE_3_1_FEATURES + iconDialog.setCustomLocation( locate("appdata", "themes/default/tokens/") ); +#endif + +#ifdef KDE_3_3_FEATURES + iconDialog.setup( KIcon::Desktop, KIcon::Application, false, 0, true, true, true ); // begin with user icons, lock editing +#else + iconDialog.setup( KIcon::Desktop, KIcon::Application, false, 0, true ); // begin with user icons +#endif + + QString image = iconDialog.openDialog(); + + if ( image.isEmpty() ) + return; + + QStringList splitPath = QStringList::split( '/', image ); + m_playerImage = splitPath[ splitPath.count()-1 ]; + + setImage(); +} + +void ConfigPlayer::setImage() +{ + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_playerImage); + if (KStandardDirs::exists(filename)) + m_playerIcon->setPixmap( QPixmap(filename) ); +} + +void ConfigPlayer::reset() +{ + m_playerName->setText(m_configDialog->config().playerName); + m_playerImage = m_configDialog->config().playerImage; + setImage(); +} + +ConfigMonopigator::ConfigMonopigator(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + m_connectOnStart = new QCheckBox(i18n("Request list of Internet servers on start-up"), parent); + layout->addWidget(m_connectOnStart); + + QString message=i18n( + "If checked, Atlantik connects to a meta server on start-up to\n" + "request a list of Internet servers.\n"); + QWhatsThis::add(m_connectOnStart, message); + + m_hideDevelopmentServers = new QCheckBox(i18n("Hide development servers"), parent); + layout->addWidget(m_hideDevelopmentServers); + + message=i18n( + "Some of the Internet servers might be running development\n" + "versions of the server software. If checked, Atlantik will not\n" + "display these servers.\n"); + QWhatsThis::add(m_hideDevelopmentServers, message); + + layout->addStretch(1); + + reset(); +} + +bool ConfigMonopigator::connectOnStart() +{ + return m_connectOnStart->isChecked(); +} + +bool ConfigMonopigator::hideDevelopmentServers() +{ + return m_hideDevelopmentServers->isChecked(); +} + +void ConfigMonopigator::reset() +{ + m_connectOnStart->setChecked(m_configDialog->config().connectOnStart); + m_hideDevelopmentServers->setChecked(m_configDialog->config().hideDevelopmentServers); +} + +ConfigGeneral::ConfigGeneral(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + m_chatTimestamps = new QCheckBox(i18n("Show timestamps in chat messages"), parent); + layout->addWidget(m_chatTimestamps); + + QString message=i18n( + "If checked, Atlantik will add timestamps in front of chat\n" + "messages.\n"); + QWhatsThis::add(m_chatTimestamps, message); + + layout->addStretch(1); + + reset(); +} + +bool ConfigGeneral::chatTimestamps() +{ + return m_chatTimestamps->isChecked(); +} + +void ConfigGeneral::reset() +{ + m_chatTimestamps->setChecked(m_configDialog->config().chatTimestamps); +} + +ConfigBoard::ConfigBoard(ConfigDialog *configDialog, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_configDialog = configDialog; + QVBoxLayout *layout = new QVBoxLayout(parent, KDialog::marginHint(), KDialog::spacingHint()); + + QGroupBox *box = new QGroupBox(1, Qt::Horizontal, i18n("Game Status Feedback"), parent); + layout->addWidget(box); + + m_indicateUnowned = new QCheckBox(i18n("Display title deed card on unowned properties"), box); + QString message=i18n( + "If checked, unowned properties on the board display an estate\n" + "card to indicate the property is for sale.\n"); + QWhatsThis::add(m_indicateUnowned, message); + + m_highliteUnowned = new QCheckBox(i18n("Highlight unowned properties"), box); + message=i18n( + "If checked, unowned properties on the board are highlighted to\n" + "indicate the property is for sale.\n"); + QWhatsThis::add(m_highliteUnowned, message); + + m_darkenMortgaged = new QCheckBox(i18n("Darken mortgaged properties"), box); + message=i18n( + "If checked, mortgaged properties on the board will be colored\n" + "darker than of the default color.\n"); + QWhatsThis::add(m_darkenMortgaged, message); + + m_animateToken = new QCheckBox(i18n("Animate token movement"), box); + message=i18n( + "If checked, tokens will move across the board\n" + "instead of jumping directly to their new location.\n"); + QWhatsThis::add(m_animateToken, message); + + m_quartzEffects = new QCheckBox(i18n("Quartz effects"), box); + message=i18n( + "If checked, the colored headers of street estates on the board " + "will have a Quartz effect similar to the Quartz KWin style.\n"); + QWhatsThis::add(m_quartzEffects, message); + +// box = new QGroupBox(1, Qt::Horizontal, i18n("Size"), parent); +// layout->addWidget(box); + + layout->addStretch(1); + + reset(); +} + +bool ConfigBoard::indicateUnowned() +{ + return m_indicateUnowned->isChecked(); +} + +bool ConfigBoard::highliteUnowned() +{ + return m_highliteUnowned->isChecked(); +} + +bool ConfigBoard::darkenMortgaged() +{ + return m_darkenMortgaged->isChecked(); +} + +bool ConfigBoard::animateToken() +{ + return m_animateToken->isChecked(); +} + +bool ConfigBoard::quartzEffects() +{ + return m_quartzEffects->isChecked(); +} + +void ConfigBoard::reset() +{ + m_indicateUnowned->setChecked(m_configDialog->config().indicateUnowned); + m_highliteUnowned->setChecked(m_configDialog->config().highliteUnowned); + m_darkenMortgaged->setChecked(m_configDialog->config().darkenMortgaged); + m_animateToken->setChecked(m_configDialog->config().animateTokens); + m_quartzEffects->setChecked(m_configDialog->config().quartzEffects); +} diff --git a/atlantik/client/configdlg.h b/atlantik/client/configdlg.h new file mode 100644 index 00000000..c1f74294 --- /dev/null +++ b/atlantik/client/configdlg.h @@ -0,0 +1,139 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_CONFIGDLG_H +#define ATLANTIK_CONFIGDLG_H + +#include +#include +#include + +#include + +class QString; + +class KPushButton; + +class Atlantik; +class ConfigDialog; + +struct AtlantikConfig; + +class ConfigPlayer : public QWidget +{ +Q_OBJECT + +public: + ConfigPlayer(ConfigDialog *configDialog, QWidget *parent, const char *name=0); + + QString playerName(); + QString playerImage(); + +private slots: + void chooseImage(); + +private: + void setImage(); + void reset(); + + ConfigDialog *m_configDialog; + QLineEdit *m_playerName; + QString m_playerImage; + KPushButton *m_playerIcon; +}; + +class ConfigBoard : public QWidget +{ +Q_OBJECT + +public: + ConfigBoard(ConfigDialog *configDialog, QWidget *parent, const char *name=0); + + bool indicateUnowned(); + bool highliteUnowned(); + bool darkenMortgaged(); + bool animateToken(); + bool quartzEffects(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_indicateUnowned, *m_highliteUnowned, *m_darkenMortgaged, *m_animateToken, *m_quartzEffects; +}; + +class ConfigMonopigator : public QWidget +{ +Q_OBJECT + +public: + ConfigMonopigator(ConfigDialog *dialog, QWidget *parent, const char *name = 0); + + bool connectOnStart(); + bool hideDevelopmentServers(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_connectOnStart, *m_hideDevelopmentServers; +}; + +class ConfigGeneral : public QWidget +{ +Q_OBJECT + +public: + ConfigGeneral(ConfigDialog *dialog, QWidget *parent, const char *name = 0); + + bool chatTimestamps(); + +private: + void reset(); + + ConfigDialog *m_configDialog; + QCheckBox *m_chatTimestamps; +}; + +class ConfigDialog : public KDialogBase +{ +Q_OBJECT + +public: + ConfigDialog(Atlantik *parent, const char *name=0); + + bool chatTimestamps(); + bool indicateUnowned(); + bool highliteUnowned(); + bool darkenMortgaged(); + bool animateToken(); + bool quartzEffects(); + AtlantikConfig config(); + QString playerName(); + QString playerImage(); + bool connectOnStart(); + bool hideDevelopmentServers(); + +private: + Atlantik *m_parent; + QFrame *p_general, *p_p13n, *p_board, *p_monopigator; + ConfigPlayer *configPlayer; + ConfigBoard *configBoard; + ConfigMonopigator *configMonopigator; + ConfigGeneral *configGeneral; +}; + +#endif diff --git a/atlantik/client/event.cpp b/atlantik/client/event.cpp new file mode 100644 index 00000000..218f6b87 --- /dev/null +++ b/atlantik/client/event.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include "event.moc" + +Event::Event(const QDateTime &dateTime, const QString &description, const QString &icon) +{ + m_dateTime = dateTime; + m_description = description; + m_icon = icon; +} + +QDateTime Event::dateTime() const +{ + return m_dateTime; +} + +QString Event::description() const +{ + return m_description; +} + +QString Event::icon() const +{ + return m_icon; +} diff --git a/atlantik/client/event.h b/atlantik/client/event.h new file mode 100644 index 00000000..f2f56444 --- /dev/null +++ b/atlantik/client/event.h @@ -0,0 +1,40 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_EVENT_H +#define ATLANTIK_EVENT_H + +#include + +class QDateTime; +class QString; + +class Event : public QObject +{ +Q_OBJECT + +public: + Event(const QDateTime &dateTime, const QString &description, const QString &icon = QString::null); + QDateTime dateTime() const; + QString description() const; + QString icon() const; + +private: + QDateTime m_dateTime; + QString m_description, m_icon; +}; + +#endif diff --git a/atlantik/client/eventlogwidget.cpp b/atlantik/client/eventlogwidget.cpp new file mode 100644 index 00000000..b0f77ab8 --- /dev/null +++ b/atlantik/client/eventlogwidget.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2003-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "event.h" +#include "eventlogwidget.moc" + +EventLog::EventLog() +{ +} + +void EventLog::addEvent(const QString &description, const QString &icon) +{ + Event *event = new Event(QDateTime::currentDateTime(), description, icon); + m_events.append(event); + emit newEvent(event); +} + +QPtrList EventLog::events() +{ + return m_events; +} + +EventLogWidget::EventLogWidget(EventLog *eventLog, QWidget *parent, const char *name) + : QWidget(parent, name, + WType_Dialog | WStyle_Customize | WStyle_DialogBorder | WStyle_Title | + WStyle_Minimize | WStyle_ContextHelp ) +{ + m_eventLog = eventLog; + + connect(m_eventLog, SIGNAL(newEvent(Event *)), this, SLOT(addEvent(Event *))); + + setCaption(i18n("Event Log")); + + QVBoxLayout *listCompBox = new QVBoxLayout(this, KDialog::marginHint()); + + m_eventList = new KListView(this, "eventList"); + listCompBox->addWidget(m_eventList); + + m_eventList->addColumn(i18n("Date/Time")); + m_eventList->addColumn(i18n("Description")); + m_eventList->header()->setClickEnabled( false ); + + QHBoxLayout *actionBox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + listCompBox->addItem(actionBox); + + actionBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_saveButton = new KPushButton(BarIcon("filesave", KIcon::SizeSmall), i18n("&Save As..."), this); + actionBox->addWidget(m_saveButton); + + connect(m_saveButton, SIGNAL(clicked()), this, SLOT(save())); + + // Populate + QPtrList events = m_eventLog->events(); + for (QPtrListIterator it( events ); (*it) ; ++it) + addEvent( (*it) ); +} + +void EventLogWidget::addEvent(Event *event) +{ + // FIXME: allow a way to view non-squeezed message + // FIXME: allow a way to show older messages + + if ( m_eventList->childCount() >= 25 ) + delete m_eventList->firstChild(); + + QString description = KStringHandler::rsqueeze( event->description(), 200 ); + KListViewItem *item = new KListViewItem(m_eventList, event->dateTime().toString("yyyy-MM-dd hh:mm:ss zzz"), description); + if (event->icon().isEmpty()) + item->setPixmap(1, QPixmap(SmallIcon("atlantik"))); + else + item->setPixmap(1, QPixmap(SmallIcon(event->icon()))); + + m_eventList->ensureItemVisible(item); +} + +void EventLogWidget::closeEvent(QCloseEvent *e) +{ + e->accept(); +} + +void EventLogWidget::save() +{ + QFile file( KFileDialog::getSaveFileName() ); + if ( file.open( IO_WriteOnly ) ) + { + QTextStream stream(&file); + + stream << i18n( "Atlantik log file, saved at %1." ).arg( QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") ) << endl; + + QPtrList events = m_eventLog->events(); + for (QPtrListIterator it( events ); (*it) ; ++it) + stream << (*it)->dateTime().toString("yyyy-MM-dd hh:mm:ss") << " " << (*it)->description() << endl; + file.close(); + } +} diff --git a/atlantik/client/eventlogwidget.h b/atlantik/client/eventlogwidget.h new file mode 100644 index 00000000..4b925d47 --- /dev/null +++ b/atlantik/client/eventlogwidget.h @@ -0,0 +1,73 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_EVENTLOGWIDGET_H +#define ATLANTIK_EVENTLOGWIDGET_H + +#include +#include + +class QString; + +class Event; + +class KListView; +class KListViewItem; +class KPushButton; + +class EventLog : public QObject +{ +Q_OBJECT + +public: + EventLog(); + QPtrList events(); + +public slots: + void addEvent(const QString &description, const QString &icon = QString::null); + +signals: + void newEvent(Event *event); + +private: + QPtrList m_events; +}; + +class EventLogWidget : public QWidget +{ +Q_OBJECT + +public: + enum EventLogType { Default, Net_In, Net_Out }; + + EventLogWidget(EventLog *eventLog, QWidget *parent=0, const char *name = 0); + +public slots: + void addEvent(Event *event); + +protected: + void closeEvent(QCloseEvent *e); + +private slots: + void save(); + +private: + EventLog *m_eventLog; + KListView *m_eventList; + KPushButton *m_saveButton; +}; + +#endif diff --git a/atlantik/client/main.cpp b/atlantik/client/main.cpp new file mode 100644 index 00000000..5f0e2fc6 --- /dev/null +++ b/atlantik/client/main.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include + +#include "main.h" +#include "atlantik.h" + +static KCmdLineOptions options[] = +{ + { "h", 0, 0 }, + { "host ", I18N_NOOP("Connect to this host"), 0 }, + { "p", 0, 0 }, + { "port ", I18N_NOOP("Connect at this port"), "1234" }, + { "g", 0, 0 }, + { "game ", I18N_NOOP("Join this game"), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( + "atlantik", + I18N_NOOP("Atlantik"), ATLANTIK_VERSION_STRING, + I18N_NOOP("The Atlantic board game"), + KAboutData::License_GPL, + I18N_NOOP("(c) 1998-2004 Rob Kaper"), + I18N_NOOP("KDE client for playing Monopoly-like games on the monopd network."), + "http://unixcode.org/atlantik/" + ); + + aboutData.addAuthor("Rob Kaper", I18N_NOOP("main author"), "cap@capsi.com", "http://capsi.com/"); + + // Patches and artists + aboutData.addCredit("Thiago Macieira", I18N_NOOP("KExtendedSocket support"), "thiagom@wanadoo.fr"); + aboutData.addCredit("Albert Astals Cid", I18N_NOOP("various patches"), "tsdgeos@terra.es"); + + aboutData.addCredit("Bart Szyszka", I18N_NOOP("application icon"), "bart@gigabee.com", "http://www.gigabee.com/"); + aboutData.addCredit("Rob Malda", I18N_NOOP("token icons"), "", "http://cmdrtaco.net/"); + aboutData.addCredit("Elhay Achiam", I18N_NOOP("icons"), "elhay_a@bezeqint.net"); + aboutData.addCredit("Carlo Caneva", I18N_NOOP("icons"), "webmaster@molecola.com", "http://www.molecola.com/"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions (options); + + KApplication::addCmdLineOptions(); + KApplication kapplication; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (kapplication.isRestored()) + RESTORE(Atlantik) + else + { + Atlantik *atlantik = new Atlantik; + atlantik->setMinimumSize(640, 480); + atlantik->setCaption(i18n("The Atlantic Board Game")); + atlantik->show(); + } + + return kapplication.exec(); +} diff --git a/atlantik/client/main.h b/atlantik/client/main.h new file mode 100644 index 00000000..4c2f00c6 --- /dev/null +++ b/atlantik/client/main.h @@ -0,0 +1,29 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_MAIN_H +#define ATLANTIK_MAIN_H + +#define ATLANTIK_VERSION 075 +#define ATLANTIK_VERSION_STRING "0.7.5" + +#define ATLANTIK_VERSION_MAJOR 0 +#define ATLANTIK_VERSION_MINOR 7 +#define ATLANTIK_VERSION_RELEASE 5 + +int main(int, char *[]); + +#endif diff --git a/atlantik/client/monopigator.cpp b/atlantik/client/monopigator.cpp new file mode 100644 index 00000000..83ef0d42 --- /dev/null +++ b/atlantik/client/monopigator.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include + +#include "monopigator.moc" +#include "main.h" + +Monopigator::Monopigator() +{ + m_downloadData = 0; + m_job = 0; + m_timer = 0; +} + +Monopigator::~Monopigator() +{ + if (m_job) + m_job -> kill(); + delete m_downloadData; + m_downloadData=0L; +} + +void Monopigator::loadData(const KURL &url) +{ + delete m_downloadData; + m_downloadData = new QBuffer(); + m_downloadData->open(IO_WriteOnly); + m_downloadData->reset(); + + m_job = KIO::get(url, true, false); + m_job->addMetaData(QString::fromLatin1("UserAgent"), QString::fromLatin1("Atlantik/" ATLANTIK_VERSION_STRING)); + + if (!m_timer) + { + m_timer = new QTimer(this); + m_timer->start(10000, true); + } + + connect(m_job, SIGNAL(data(KIO::Job *, const QByteArray &)), SLOT(slotData(KIO::Job *, const QByteArray &))); + connect(m_job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *))); + connect(m_timer, SIGNAL(timeout()), SLOT(slotTimeout())); +} + +void Monopigator::slotData(KIO::Job *, const QByteArray &data) +{ + m_timer->stop(); + m_downloadData->writeBlock(data.data(), data.size()); +} + +void Monopigator::slotResult(KIO::Job *job) +{ + processData(m_downloadData->buffer(), !job->error()); + m_job = 0; +} + +void Monopigator::slotTimeout() +{ + if (m_job) + m_job -> kill(); + m_job = 0; + + emit timeout(); +} + +void Monopigator::processData(const QByteArray &data, bool okSoFar) +{ + if (okSoFar) + { + QString xmlData(data); + QDomDocument domDoc; + if (domDoc.setContent(xmlData)) + { + QDomElement eTop = domDoc.documentElement(); + if (eTop.tagName() != "monopigator") + return; + + QDomNode n = eTop.firstChild(); + while(!n.isNull()) + { + QDomElement e = n.toElement(); + if(!e.isNull()) + { + if (e.tagName() == "server") + emit monopigatorAdd(e.attributeNode(QString("ip")).value(), e.attributeNode(QString("host")).value(), e.attributeNode(QString("port")).value(), e.attributeNode(QString("version")).value(), e.attributeNode(QString("users")).value().toInt()); + } + n = n.nextSibling(); + } + emit finished(); + } + } +} + +MonopigatorEntry::MonopigatorEntry(QListView *parent, QString host, QString latency, QString version, QString users, QString port, QString ip) : QObject(), QListViewItem(parent, host, latency, version, users, port) +{ + m_isDev = ( version.find( QRegExp("(CVS|-dev)") ) != -1 ) ? true : false; + + setEnabled(false); + parent->sort(); + + if ( !ip.isEmpty() ) + host = ip; + m_latencySocket = new KExtendedSocket( host, port.toInt(), KExtendedSocket::inputBufferedSocket | KExtendedSocket::noResolve ); + connect(m_latencySocket, SIGNAL(lookupFinished(int)), this, SLOT(resolved())); + connect(m_latencySocket, SIGNAL(connectionSuccess()), this, SLOT(connected())); + m_latencySocket->startAsyncConnect(); +} + +void MonopigatorEntry::resolved() +{ + time.start(); +} + +void MonopigatorEntry::connected() +{ + setText( 1, QString::number(time.elapsed()) ); + setEnabled(true); + listView()->sort(); + delete m_latencySocket; +} + +int MonopigatorEntry::compare(QListViewItem *i, int col, bool ascending) const +{ + // Colums 1 and 3 are integers (latency and users) + if (col == 1 || col == 3) + { + int myVal = text(col).toInt(), iVal = i->text(col).toInt(); + if (myVal == iVal) + return 0; + else if (myVal > iVal) + return 1; + else + return -1; + } + return key( col, ascending ).compare( i->key( col, ascending) ); +} + +bool MonopigatorEntry::isDev() const +{ + return m_isDev; +} + +void MonopigatorEntry::showDevelopmentServers(bool show) +{ + if ( isVisible() != show ) + setVisible(show); +} diff --git a/atlantik/client/monopigator.h b/atlantik/client/monopigator.h new file mode 100644 index 00000000..1fd57b1b --- /dev/null +++ b/atlantik/client/monopigator.h @@ -0,0 +1,79 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_MONOPIGATOR_H +#define ATLANTIK_MONOPIGATOR_H + +#include +#include +#include +#include + +#include +#include + +class KExtendedSocket; +class QTime; + +class Monopigator : public QObject +{ +Q_OBJECT + +public: + Monopigator(); + ~Monopigator(); + void loadData(const KURL &); + +signals: + void monopigatorAdd(QString ip, QString host, QString port, QString version, int users); + void finished(); + void timeout(); + +private slots: + void slotData(KIO::Job *, const QByteArray &); + void slotResult(KIO::Job *); + void slotTimeout(); + +private: + void processData(const QByteArray &, bool = true); + + QBuffer *m_downloadData; + QTimer *m_timer; + KIO::Job *m_job; +}; + +class MonopigatorEntry : public QObject, public QListViewItem +{ +Q_OBJECT + +public: + MonopigatorEntry(QListView *parent, QString host, QString latency, QString version, QString users, QString port, QString ip); + int compare(QListViewItem *i, int col, bool ascending) const; + bool isDev() const; + +private slots: + void resolved(); + void connected(); + void showDevelopmentServers(bool show); + +private: + KExtendedSocket *m_latencySocket; + QTime time; + QListView *m_parent; + bool m_isDev; +}; + +#endif diff --git a/atlantik/client/selectconfiguration_widget.cpp b/atlantik/client/selectconfiguration_widget.cpp new file mode 100644 index 00000000..0e7d5cdb --- /dev/null +++ b/atlantik/client/selectconfiguration_widget.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "selectconfiguration_widget.moc" + +SelectConfiguration::SelectConfiguration(AtlanticCore *atlanticCore, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + m_game = 0; + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Game configuration. + m_configBox = new QVGroupBox(i18n("Game Configuration"), this, "configBox"); + m_mainLayout->addWidget(m_configBox); + + // Player buttons. + QHBoxLayout *playerButtons = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + playerButtons->setMargin(0); + + playerButtons->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + // Vertical spacer. + m_mainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + // Server buttons. + QHBoxLayout *serverButtons = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + serverButtons->setMargin(0); + + m_backButton = new KPushButton(SmallIcon("back"), i18n("Leave Game"), this); + serverButtons->addWidget(m_backButton); + + connect(m_backButton, SIGNAL(clicked()), this, SIGNAL(leaveGame())); + + serverButtons->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_startButton = new KPushButton(SmallIconSet("forward"), i18n("Start Game"), this); + serverButtons->addWidget(m_startButton); + m_startButton->setEnabled(false); + + connect(m_startButton, SIGNAL(clicked()), this, SIGNAL(startGame())); + + Player *playerSelf = m_atlanticCore->playerSelf(); + playerChanged(playerSelf); + connect(playerSelf, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + + emit statusMessage(i18n("Retrieving configuration list...")); +} + +void SelectConfiguration::initGame() +{ + emit statusMessage(i18n("Game started. Retrieving full game data...")); +} + +void SelectConfiguration::addConfigOption(ConfigOption *configOption) +{ + // FIXME: only bool types supported! + QCheckBox *checkBox = new QCheckBox(configOption->description(), m_configBox, "checkbox"); + m_configMap[(QObject *)checkBox] = configOption; + m_configBoxMap[configOption] = checkBox; + + checkBox->setChecked( configOption->value().toInt() ); + checkBox->setEnabled( configOption->edit() && m_atlanticCore->selfIsMaster() ); + checkBox->show(); + + connect(checkBox, SIGNAL(clicked()), this, SLOT(changeOption())); + connect(configOption, SIGNAL(changed(ConfigOption *)), this, SLOT(optionChanged(ConfigOption *))); +} + +void SelectConfiguration::gameOption(QString title, QString type, QString value, QString edit, QString command) +{ + // Find if option exists in GUI yet + if (QCheckBox *checkBox = dynamic_cast(m_checkBoxMap[command])) + { + checkBox->setChecked(value.toInt()); + checkBox->setEnabled(edit.toInt()); + return; + } + + // Create option + if (type == "bool") + { + QCheckBox *checkBox = new QCheckBox(title, m_configBox, "checkbox"); + m_optionCommandMap[(QObject *)checkBox] = command; + m_checkBoxMap[command] = checkBox; + checkBox->setChecked(value.toInt()); + checkBox->setEnabled(edit.toInt()); + checkBox->show(); + + connect(checkBox, SIGNAL(clicked()), this, SLOT(optionChanged())); + } + // TODO: create options other than type=bool + + // TODO: Enable edit for master only +} + +void SelectConfiguration::changeOption() +{ + ConfigOption *configOption = m_configMap[(QObject *)QObject::sender()]; + if (configOption) + { + kdDebug() << "checked " << ((QCheckBox *)QObject::sender())->isChecked() << endl; + emit changeOption( configOption->id(), QString::number( ((QCheckBox *)QObject::sender())->isChecked() ) ); + } +} + +void SelectConfiguration::optionChanged(ConfigOption *configOption) +{ + QCheckBox *checkBox = m_configBoxMap[configOption]; + if (checkBox) + { + checkBox->setText( configOption->description() ); + checkBox->setChecked( configOption->value().toInt() ); + checkBox->setEnabled( configOption->edit() && m_atlanticCore->selfIsMaster() ); + } +} + +void SelectConfiguration::optionChanged() +{ + QString command = m_optionCommandMap[(QObject *)QObject::sender()]; + + if (QCheckBox *checkBox = m_checkBoxMap[command]) + { + command.append(QString::number(checkBox->isChecked())); + emit buttonCommand(command); + } +} + +void SelectConfiguration::slotEndUpdate() +{ + emit statusMessage(i18n("Retrieved configuration list.")); +} + +void SelectConfiguration::playerChanged(Player *player) +{ + kdDebug() << "playerChanged" << endl; + + if (player->game() != m_game) + { + kdDebug() << "playerChanged::change" << endl; + + if (m_game) + disconnect(m_game, SIGNAL(changed(Game *)), this, SLOT(gameChanged(Game *))); + + m_game = player->game(); + + if (m_game) + connect(m_game, SIGNAL(changed(Game *)), this, SLOT(gameChanged(Game *))); + } +} + +void SelectConfiguration::gameChanged(Game *game) +{ + m_startButton->setEnabled( game->master() == m_atlanticCore->playerSelf() ); + + for (QMapIterator it = m_configBoxMap.begin() ; it != m_configBoxMap.end() ; ++it) + (*it)->setEnabled( it.key()->edit() && m_atlanticCore->selfIsMaster() ); +} diff --git a/atlantik/client/selectconfiguration_widget.h b/atlantik/client/selectconfiguration_widget.h new file mode 100644 index 00000000..033a0eb0 --- /dev/null +++ b/atlantik/client/selectconfiguration_widget.h @@ -0,0 +1,80 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTCONFIGURATION_WIDGET_H +#define ATLANTIK_SELECTCONFIGURATION_WIDGET_H + +#include +#include +#include + +#include +#include + +class QCheckBox; +class QListViewItem; + +class AtlanticCore; +class ConfigOption; +class Game; +class Player; + +class SelectConfiguration : public QWidget +{ +Q_OBJECT + +public: + SelectConfiguration(AtlanticCore *atlanticCore, QWidget *parent, const char *name=0); + + void setCanStart(const bool &canStart); + QString hostToConnect() const; + int portToConnect(); + +private slots: + void addConfigOption(ConfigOption *configOption); + void changeOption(); + void gameOption(QString title, QString type, QString value, QString edit, QString command); + void optionChanged(ConfigOption *configOption); + void optionChanged(); + void slotEndUpdate(); + void initGame(); + void playerChanged(Player *player); + void gameChanged(Game *game); + +signals: + void startGame(); + void leaveGame(); + void joinConfiguration(int configurationId); + void newConfiguration(); + void changeOption(int configId, const QString &value); + void buttonCommand(QString); + void iconSelected(const QString &); + void statusMessage(const QString &message); + +private: + QVBoxLayout *m_mainLayout; + QVGroupBox *m_configBox, *m_messageBox; + KPushButton *m_backButton, *m_startButton; + QMap m_optionCommandMap; + QMap m_configMap; + QMap m_configBoxMap; + QMap m_checkBoxMap; + QMap m_items; + Game *m_game; + AtlanticCore *m_atlanticCore; +}; + +#endif diff --git a/atlantik/client/selectgame_widget.cpp b/atlantik/client/selectgame_widget.cpp new file mode 100644 index 00000000..85d4f886 --- /dev/null +++ b/atlantik/client/selectgame_widget.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "selectgame_widget.h" + +SelectGame::SelectGame(AtlanticCore *atlanticCore, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + + connect(m_atlanticCore, SIGNAL(createGUI(Game *)), this, SLOT(addGame(Game *))); + connect(m_atlanticCore, SIGNAL(removeGUI(Game *)), this, SLOT(delGame(Game *))); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + QVGroupBox *groupBox; + groupBox = new QVGroupBox(i18n("Create or Select monopd Game"), this, "groupBox"); + m_mainLayout->addWidget(groupBox); + + // List of games + m_gameList = new KListView(groupBox, "m_gameList"); + m_gameList->addColumn(i18n("Game")); + m_gameList->addColumn(i18n("Description")); + m_gameList->addColumn(i18n("Id")); + m_gameList->addColumn(i18n("Players")); + m_gameList->setAllColumnsShowFocus(true); +// m_mainLayout->addWidget(m_gameList); + + connect(m_gameList, SIGNAL(clicked(QListViewItem *)), this, SLOT(validateConnectButton())); + connect(m_gameList, SIGNAL(doubleClicked(QListViewItem *)), this, SLOT(connectClicked())); + connect(m_gameList, SIGNAL(rightButtonClicked(QListViewItem *, const QPoint &, int)), this, SLOT(validateConnectButton())); + connect(m_gameList, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(validateConnectButton())); + + QHBoxLayout *buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + + KPushButton *backButton = new KPushButton(SmallIcon("back"), i18n("Server List"), this); + buttonBox->addWidget(backButton); + + connect(backButton, SIGNAL(clicked()), this, SIGNAL(leaveServer())); + + buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_connectButton = new KPushButton(SmallIconSet("forward"), i18n("Create Game"), this); + m_connectButton->setEnabled(false); + buttonBox->addWidget(m_connectButton); + + connect(m_connectButton, SIGNAL(clicked()), this, SLOT(connectClicked())); + +} + +void SelectGame::addGame(Game *game) +{ + connect(game, SIGNAL(changed(Game *)), this, SLOT(updateGame(Game *))); + + if (game->id() == -1) + { + QListViewItem *item = new QListViewItem( m_gameList, i18n("Create a new %1 Game").arg(game->name()), game->description(), QString::null, QString::null, game->type() ); + item->setPixmap(0, QPixmap(SmallIcon("filenew"))); + } + else + { + Player *master = game->master(); + QListViewItem *item = new QListViewItem( m_gameList, i18n("Join %1's %2 Game").arg( (master ? master->name() : QString::null), game->name() ), game->description(), QString::number(game->id()), QString::number(game->players()), game->type() ); + item->setPixmap( 0, QPixmap(SmallIcon("atlantik")) ); + item->setEnabled(game->canBeJoined()); + + KNotifyClient::event(winId(), "newgame"); + + connect(master, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + +// validateConnectButton(); +} + +void SelectGame::delGame(Game *game) +{ + QListViewItem *item = findItem(game); + if (!item) + return; + + delete item; + + validateConnectButton(); +} + +void SelectGame::updateGame(Game *game) +{ + QListViewItem *item = findItem(game); + if (!item) + return; + + item->setText( 1, game->description() ); + + if (game->id() == -1) + item->setText(0, i18n("Create a new %1 Game").arg(game->name())); + else + { + Player *master = game->master(); + item->setText( 0, i18n("Join %1's %2 Game").arg( (master ? master->name() : QString::null), game->name() ) ); + item->setText( 3, QString::number( game->players() ) ); + item->setEnabled( game->canBeJoined() ); + + connect(master, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + m_gameList->triggerUpdate(); + + validateConnectButton(); +} + +void SelectGame::playerChanged(Player *player) +{ + QListViewItem *item = m_gameList->firstChild(); + Game *game = 0; + + while (item) + { + game = m_atlanticCore->findGame( item->text(2).toInt() ); + if ( game && game->master() == player ) + { + item->setText( 0, i18n("Join %1's %2 Game").arg( player->name(), game->name() ) ); + return; + } + item = item->nextSibling(); + } +} + +QListViewItem *SelectGame::findItem(Game *game) +{ + QListViewItem *item = m_gameList->firstChild(); + while (item) + { + if ( (game->id() == -1 || item->text(2) == QString::number(game->id())) && item->text(4) == game->type() ) + return item; + + item = item->nextSibling(); + } + return 0; +} + +void SelectGame::validateConnectButton() +{ + if (QListViewItem *item = m_gameList->selectedItem()) + { + if (item->text(2).toInt() > 0) + m_connectButton->setText(i18n("Join Game")); + else + m_connectButton->setText(i18n("Create Game")); + + m_connectButton->setEnabled(true); + } + else + m_connectButton->setEnabled(false); +} + +void SelectGame::connectClicked() +{ + if (QListViewItem *item = m_gameList->selectedItem()) + { + if (int gameId = item->text(2).toInt()) + emit joinGame(gameId); + else + emit newGame(item->text(4)); + } +} + +#include "selectgame_widget.moc" diff --git a/atlantik/client/selectgame_widget.h b/atlantik/client/selectgame_widget.h new file mode 100644 index 00000000..d47e905e --- /dev/null +++ b/atlantik/client/selectgame_widget.h @@ -0,0 +1,65 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTGAME_WIDGET_H +#define ATLANTIK_SELECTGAME_WIDGET_H + +#include +#include + +#include +#include + +class AtlanticCore; +class Game; +class Player; + +class SelectGame : public QWidget +{ +Q_OBJECT + +public: + SelectGame(AtlanticCore *atlanticCore, QWidget *parent, const char *name=0); + + void initPage(); + bool validateNext(); + QString hostToConnect() const; + int portToConnect(); + +private slots: + void connectClicked(); + void addGame(Game *game); + void delGame(Game *game); + void updateGame(Game *game); + void playerChanged(Player *player); + void validateConnectButton(); + +signals: + void joinGame(int gameId); + void newGame(const QString &gameType); + void leaveServer(); + void msgStatus(const QString &status); + +private: + QListViewItem *findItem(Game *game); + + AtlanticCore *m_atlanticCore; + QVBoxLayout *m_mainLayout; + KListView *m_gameList; + KPushButton *m_connectButton; +}; + +#endif diff --git a/atlantik/client/selectserver_widget.cpp b/atlantik/client/selectserver_widget.cpp new file mode 100644 index 00000000..39c07b50 --- /dev/null +++ b/atlantik/client/selectserver_widget.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "selectserver_widget.moc" + +SelectServer::SelectServer(bool useMonopigatorOnStart, bool hideDevelopmentServers, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_hideDevelopmentServers = hideDevelopmentServers; + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Custom server group + QHGroupBox *customGroup = new QHGroupBox(i18n("Enter Custom monopd Server"), this, "customGroup"); + m_mainLayout->addWidget(customGroup); + + QLabel *hostLabel = new QLabel(i18n("Hostname:"), customGroup); + + m_hostEdit = new KLineEdit(customGroup); + m_hostEdit->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); + + QLabel *portLabel = new QLabel(i18n("Port:"), customGroup); + + m_portEdit = new KLineEdit(QString::number(1234), customGroup); + m_portEdit->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum)); + + KPushButton *connectButton = new KPushButton( KGuiItem(i18n("Connect"), "network"), customGroup); + connect(connectButton, SIGNAL(clicked()), this, SLOT(customConnect())); + + // Server list group + QVButtonGroup *bgroup = new QVButtonGroup(i18n("Select monopd Server"), this, "bgroup"); + bgroup->setExclusive(true); + m_mainLayout->addWidget(bgroup); + + // List of servers + m_serverList = new KListView(bgroup, "m_serverList"); + m_serverList->addColumn(i18n("Host")); + m_serverList->addColumn(i18n("Latency")); + m_serverList->addColumn(i18n("Version")); + m_serverList->addColumn(i18n("Users")); + m_serverList->setAllColumnsShowFocus(true); + m_serverList->setSorting(1); +// m_mainLayout->addWidget(m_serverList); + + connect(m_serverList, SIGNAL(clicked(QListViewItem *)), this, SLOT(validateConnectButton())); + connect(m_serverList, SIGNAL(doubleClicked(QListViewItem *)), this, SLOT(slotConnect())); + connect(m_serverList, SIGNAL(rightButtonClicked(QListViewItem *, const QPoint &, int)), this, SLOT(validateConnectButton())); + connect(m_serverList, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(validateConnectButton())); + + QHBoxLayout *buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + // Server List / Refresh + m_refreshButton = new KPushButton( KGuiItem(useMonopigatorOnStart ? i18n("Reload Server List") : i18n("Get Server List"), useMonopigatorOnStart ? "reload" : "network"), this); + buttonBox->addWidget(m_refreshButton); + + connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(slotRefresh())); + + // Connect + m_connectButton = new KPushButton(BarIconSet("forward", KIcon::SizeSmall), i18n("Connect"), this); + m_connectButton->setEnabled(false); + buttonBox->addWidget(m_connectButton); + + connect(m_connectButton, SIGNAL(clicked()), this, SLOT(slotConnect())); + + // Monopigator + m_monopigator = new Monopigator(); + + connect(m_monopigator, SIGNAL(monopigatorAdd(QString, QString, QString, QString, int)), this, SLOT(slotMonopigatorAdd(QString, QString, QString, QString, int))); + connect(m_monopigator, SIGNAL(finished()), SLOT(monopigatorFinished())); + connect(m_monopigator, SIGNAL(timeout()), SLOT(monopigatorTimeout())); +} + +SelectServer::~SelectServer() +{ + delete m_monopigator; +} + +void SelectServer::setHideDevelopmentServers(bool hideDevelopmentServers) +{ + if ( m_hideDevelopmentServers != hideDevelopmentServers ) + { + m_hideDevelopmentServers = hideDevelopmentServers; + emit showDevelopmentServers( !m_hideDevelopmentServers ); + } +} + +void SelectServer::initMonopigator() +{ + // Hardcoded, but there aren't any other Monopigator root servers at the moment + emit msgStatus(i18n("Retrieving server list...")); + + m_refreshButton->setGuiItem(KGuiItem(i18n("Reload Server List"), "reload")); + m_monopigator->loadData(KURL( "http://monopd-gator.kde.org/")); +} + +void SelectServer::slotMonopigatorAdd(QString ip, QString host, QString port, QString version, int users) +{ + MonopigatorEntry *item = new MonopigatorEntry(m_serverList, host, QString::number(9999), version, (users == -1) ? i18n("unknown") : QString::number(users), port, ip); + item->setPixmap(0, BarIcon("atlantik", KIcon::SizeSmall)); + + if ( item->isDev() ) + { + item->setVisible( !m_hideDevelopmentServers ); + connect(this, SIGNAL(showDevelopmentServers(bool)), item, SLOT(showDevelopmentServers(bool))); + } + + validateConnectButton(); +} + +void SelectServer::monopigatorFinished() +{ + emit msgStatus(i18n("Retrieved server list.")); + m_refreshButton->setEnabled(true); +} + +void SelectServer::monopigatorTimeout() +{ + emit msgStatus(i18n("Error while retrieving the server list.")); + m_refreshButton->setEnabled(true); +} + +void SelectServer::validateConnectButton() +{ + if (m_serverList->selectedItem()) + m_connectButton->setEnabled(true); + else + m_connectButton->setEnabled(false); +} + +void SelectServer::slotRefresh(bool useMonopigator) +{ + m_serverList->clear(); + validateConnectButton(); + + if (useMonopigator) + { + m_refreshButton->setEnabled(false); + initMonopigator(); + } +} + +void SelectServer::slotConnect() +{ + if (QListViewItem *item = m_serverList->selectedItem()) + emit serverConnect(item->text(0), item->text(4).toInt()); +} + +void SelectServer::customConnect() +{ + if (!m_hostEdit->text().isEmpty() && !m_portEdit->text().isEmpty()) + emit serverConnect(m_hostEdit->text(), m_portEdit->text().toInt()); +} diff --git a/atlantik/client/selectserver_widget.h b/atlantik/client/selectserver_widget.h new file mode 100644 index 00000000..c5d1b586 --- /dev/null +++ b/atlantik/client/selectserver_widget.h @@ -0,0 +1,73 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_SELECTSERVER_WIDGET_H +#define ATLANTIK_SELECTSERVER_WIDGET_H + +#include +#include +#include + +#include +#include +#include + +#include "monopigator.h" + +class KExtendedSocket; + +class SelectServer : public QWidget +{ +Q_OBJECT + +public: + SelectServer(bool useMonopigatorOnStart, bool hideDevelopmentServers, QWidget *parent, const char *name=0); + virtual ~SelectServer(); + + void initPage(); + void setHideDevelopmentServers(bool hideDevelopmentServers); + bool validateNext(); + QString hostToConnect() const; + int portToConnect(); + +public slots: + void validateConnectButton(); + void slotRefresh(bool useMonopigator = true); + void slotMonopigatorAdd(QString ip, QString host, QString port, QString version, int users); + +private slots: + void slotConnect(); + void customConnect(); + void monopigatorFinished(); + void monopigatorTimeout(); + +signals: + void serverConnect(const QString host, int port); + void msgStatus(const QString &message); + void showDevelopmentServers(bool show); + +private: + void initMonopigator(); + + QVBoxLayout *m_mainLayout; + KListView *m_serverList; + KLineEdit *m_hostEdit, *m_portEdit; + KPushButton *m_addServerButton, *m_refreshButton, *m_customConnect, *m_connectButton; + Monopigator *m_monopigator; + bool m_hideDevelopmentServers; +}; + +#endif diff --git a/atlantik/eventsrc b/atlantik/eventsrc new file mode 100644 index 00000000..a6d0327c --- /dev/null +++ b/atlantik/eventsrc @@ -0,0 +1,475 @@ +[!Global!] +IconName=atlantik +Comment=Atlantik +Comment[be]=Ðтлантыка +Comment[bn]=আটলানà§à¦Ÿà¦¿à¦• +Comment[eo]=Atlantiko +Comment[hi]=अटलांटिक +Comment[lv]=Atlantija +Comment[mk]=Ðтлантик +Comment[ne]=à¤à¤Ÿà¤²à¤¾à¤¨à¥à¤Ÿà¤¿à¤• +Comment[pa]=à¨à¨Ÿà¨²à¨¾à¨Ÿà¨¿à¨• +Comment[ta]=அடà¯à®²à®¾à®£à¯à®Ÿà®¿à®•à¯ +Comment[tg]=Ðтлантик +Comment[wa]=Atlantike + +[gainedturn] +Name=Gained Turn +Name[ar]=ربحت دورا +Name[be]=Канец ходу +Name[bg]=Придобит ред +Name[bn]=চাল দিন +Name[bs]=Potez dobiven +Name[ca]=Et toca jugar +Name[cs]=Získán tah +Name[cy]=Cael Tro +Name[da]=Vundet tur +Name[de]=Gewonnene Runde +Name[el]=ΚεÏδισμένη Ï€Ïοσπάθεια +Name[eo]=Gajnita vico +Name[es]=Turno ganado +Name[et]=Omandatud käik +Name[eu]=Irabazitako txanda +Name[fa]=نوبت به دست آمده +Name[fi]=Voitettu kierros +Name[fr]=Tour gagné +Name[gl]=Quenda Gañada +Name[he]=הרווחת תור +Name[hi]=लाभ वाली बारी +Name[hr]=Dobiven potez +Name[hu]=Nyert forduló +Name[is]=Græddi leik +Name[it]=Turno guadagnato +Name[ja]=順番ãŒå›žã£ã¦ãã¾ã—㟠+Name[km]=បាន​យក​វáŸáž“ +Name[lt]=Gautas Ä—jimas +Name[lv]=IegÅ«ts gÄjiens +Name[mk]=Добиен потег +Name[nb]=Ekstratur +Name[nds]=Wunnen Törn +Name[ne]=पà¥à¤°à¤¾à¤ªà¥à¤¤ मौका +Name[nl]=Gewonnen ronde +Name[nn]=Ekstratur +Name[pl]=Zyskany ruch +Name[pt]=Jogada Ganha +Name[pt_BR]=Rodada vencida +Name[ru]=Конец хода +Name[se]=Liigemátki +Name[sk]=Získaní Å¥ah +Name[sl]=Dobljena poteza +Name[sr]=Добијен потез +Name[sr@Latn]=Dobijen potez +Name[sv]=Du vann omgÃ¥ngen +Name[ta]=திரà¯à®ªà¯à®ªà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Gained Чархиш +Name[tr]=Karlı Sıra +Name[uk]=Кінець ходу +Name[wa]=Djeu wangnî +Name[zh_CN]=获得一次机会 +Name[zh_TW]=å¢žåŠ çš„å›žåˆ +Comment=It is your turn now +Comment[ar]=دورك الآن +Comment[be]=Ваш ход +Comment[bg]=Придобит ред +Comment[bn]=à¦à¦–ন আপনার চাল +Comment[br]=Din eo bremañ +Comment[bs]=Na vas je red +Comment[ca]=És el teu torn +Comment[cs]=Jste na tahu +Comment[cy]=Eich tro chi ydy o rwan +Comment[da]=Det er din tur nu +Comment[de]=Sie sind am Zug +Comment[el]=Είναι η σειÏά σας Ï„ÏŽÏα +Comment[eo]=Estas nun via vico +Comment[es]=Es su turno +Comment[et]=Sinu kord täringut veeretada +Comment[eu]=Zure txanda da +Comment[fa]=اکنون نوبت شماست +Comment[fi]=Sinun vuoro +Comment[fr]=C'est à votre tour de jeter les dés +Comment[gl]=É a súa quenda +Comment[he]=תורך עכשיו +Comment[hi]=यह अब आपकी बारी है +Comment[hr]=Vi ste na potezu +Comment[hu]=Most Ön következik +Comment[is]=Þú átt að gera +Comment[it]=Ora tocca a te +Comment[ja]=ã‚ãªãŸã®ç•ªã§ã™ +Comment[km]=ឥឡូវ​វា​ជា​វáŸáž“​របស់​អ្នក +Comment[lt]=Dabar JÅ«sų Ä—jimas +Comment[lv]=Å is ir jÅ«su gÄjiens +Comment[mk]=Вие Ñте на потег +Comment[nb]=Det er din tur nÃ¥ +Comment[nds]=Nu büst Du an de Reeg +Comment[ne]=अब तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt +Comment[nn]=Det er din tur no +Comment[pa]=ਹà©à¨£ ਤà©à¨¹à¨¾à¨¡à©€ ਵਾਰੀ ਠ+Comment[pl]=Twój ruch +Comment[pt]=É a sua vez de jogar +Comment[pt_BR]=É a sua vez de jogar agora +Comment[ro]=Este rîndul dumneavoastră +Comment[ru]=Ваша очередь +Comment[se]=Du vuorru dál +Comment[sk]=Ste na Å¥ahu +Comment[sl]=Sedaj je vaÅ¡a poteza +Comment[sr]=Сада је ваш потез +Comment[sr@Latn]=Sada je vaÅ¡ potez +Comment[sv]=Det är din tur nu +Comment[ta]=இத௠உஙà¯à®•à®³à¯à®Ÿà¯ˆà®¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ҳоло навбати шумо аÑÑ‚ +Comment[tr]=Sıra ÅŸimdi sizde +Comment[uk]=Тепер ваша черга +Comment[uz]=Endi siz yuriysiz +Comment[uz@cyrillic]=Энди Ñиз юрийÑиз +Comment[wa]=C' est a vos asteure +Comment[zh_CN]=轮到您了 +Comment[zh_TW]=該您擲骰å­äº† +default_presentation=80 + +[chat] +Name=Chat +Name[ar]=دردشة +Name[be]=Размова +Name[bg]=Изпратено Ñъобщение +Name[bn]=আডà§à¦¡à¦¾ +Name[br]=Flapañ +Name[bs]=Razgovor +Name[ca]=Xat +Name[cs]=Rozhovor +Name[cy]=Sgwrs +Name[el]=Κουβέντα +Name[eo]=Babilu +Name[et]=Vestlus +Name[eu]=Berriketa +Name[fa]=Ú¯Ù¾ +Name[fi]=Keskustelu +Name[fr]=Discussion +Name[he]=צ'ט +Name[hi]=गपशप +Name[hr]=Brbljanje +Name[hu]=Csevegés +Name[is]=Spjall +Name[ja]=ãƒãƒ£ãƒƒãƒˆ +Name[km]=សន្ទនា +Name[lt]=Pokalbiai +Name[lv]=ÄŒats +Name[mk]=Разговор +Name[nb]=Prat +Name[nds]=Klönen +Name[ne]=कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ +Name[nl]=Gesprek +Name[nn]=Prat +Name[pa]=ਗੱਲਬਾਤ +Name[pl]=Rozmowa +Name[pt]=Conversar +Name[pt_BR]=Bate-papo +Name[ru]=Чат +Name[se]=Buillar +Name[sk]=Rozhovor +Name[sl]=Pogovor +Name[sr]=ЋаÑкање +Name[sr@Latn]=Ćaskanje +Name[sv]=Chatt +Name[ta]=அரடà¯à®Ÿà¯ˆ +Name[tg]=Чат +Name[tr]=Muhabbet +Name[uk]=Розмова +Name[uz@cyrillic]=Чат +Name[wa]=Berdelaedje +Name[zh_CN]=èŠå¤© +Name[zh_TW]=èŠå¤© +Comment=A player sends a chat message +Comment[ar]=لقد أرسل لاعب رسالة دردشة +Comment[be]=Гульнёўца Ñказаў +Comment[bg]=Изпратено е Ñъобщение от играч +Comment[bn]=à¦à¦•à¦œà¦¨ খেলোয়াড় à¦à¦•à¦Ÿà¦¿ বারà§à¦¤à¦¾ পাঠালেন +Comment[bs]=IgraÄ je poslao poruku +Comment[ca]=Un jugador envia un missatge de xat +Comment[cs]=HrÃ¡Ä odesílá zprávu +Comment[cy]=Mae chwaraewr yn danfon neges sgwrs +Comment[da]=En spiller sender en chat-besked +Comment[de]=Ein Spieler sendet eine Chat-Nachricht +Comment[el]=Ένας παίκτης στέλνει ένα μήνυμα κουβέντας +Comment[eo]=Ludanto sendas babilmesaÄon al Vi +Comment[es]=Un jugador ha enviado un mensaje +Comment[et]=Mängija saatis vestlusteate +Comment[eu]=Jokalari batek berriketarako mezu bat bidali du +Comment[fa]=یک بازیکن یک پیام Ú¯Ù¾ را ارسال می‌‌کند +Comment[fi]=Pelaaja lähettää viestin +Comment[fr]=Un joueur envoie un message +Comment[gl]=Un xogador enviou unha mensaxe +Comment[he]=שחקן שולח הודעת צ'ט +Comment[hi]=à¤à¤• खिलाड़ी ने गपशप संदेश भेजा +Comment[hr]=IgraÄ je poslao poruku brbljanja +Comment[hu]=Egy játékos szöveges üzenetet küld +Comment[is]=Leikmaður sendir skilaboð +Comment[it]=Un giocatore ha inviato un messaggio +Comment[ja]=プレイヤーãŒãƒãƒ£ãƒƒãƒˆãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ä¿¡ã—ã¾ã—㟠+Comment[km]=អ្នក​លáŸáž„​ផ្ញើ​សារ​សន្ទនា +Comment[lt]=ŽaidÄ—jas siunÄia žinutÄ™ +Comment[lv]=SpÄ“lÄ“tÄjs nosÅ«ta Äata ziņu +Comment[mk]=Играчот иÑпраќа порака за разговор +Comment[nb]=En spiller har sendt en pratemelding +Comment[nds]=En Speler sendt en Klöön-Naricht +Comment[ne]=खेलाडिले कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ सनà¥à¤¦à¥‡à¤¶ पठाउदछ +Comment[nl]=Een speler stuurt een bericht +Comment[nn]=Ein spelar har sendt ei pratemelding +Comment[pa]=ਇੱਕ ਖਿਡਾਰੀ ਇੱਕ ਹੀ ਸà©à¨¨à©‡à¨¹à¨¾ ਭੇਜ ਸਕਦਾ ਹੈ +Comment[pl]=Gracz wysyÅ‚a wiadomość w oknie rozmowy +Comment[pt]=Um jogador envia uma mensagem +Comment[pt_BR]=Um jogador enviou uma mensagem +Comment[ru]=Игрок Ñказал +Comment[se]=Speallár lea sádden buillardandieÄ‘u +Comment[sk]=HrÃ¡Ä poslal správu +Comment[sl]=Igralec poÅ¡ilja pogovorno sporoÄilo +Comment[sr]=Играч је поÑлао ћаÑкајућу поруку +Comment[sr@Latn]=IgraÄ je poslao ćaskajuću poruku +Comment[sv]=En spelare skickar ett chattmeddelande +Comment[ta]= ஒர௠விளையாடà¯à®Ÿà®¾à®³à®°à¯ அரடà¯à®Ÿà¯ˆ செயà¯à®¤à®¿à®¯à¯ˆ அனà¯à®ªà¯à®ªà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингар паёми чат фириÑтод +Comment[tr]=Oyuncu bir mesaj gönderdi +Comment[uk]=Гравець відÑилає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ +Comment[uz]=OÊ»yinchi xabar joÊ»natayapti +Comment[uz@cyrillic]=Ўйинчи хабар жўнатаÑпти +Comment[wa]=On djouweu evoye on messaedje di berdelaedje +Comment[zh_CN]=一个玩家å‘é€äº†ä¸€æ¡èŠå¤©ä¿¡æ¯ +Comment[zh_TW]=一個玩家é€å‡ºäº†ä¸€æ¢èŠå¤©è¨Šæ¯ +default_presentation=64 + +[newplayer] +Name=New player +Name[ar]=لاعب جديد +Name[be]=Ðовы гульнёўца +Name[bg]=Ðов играч +Name[bn]=নতà§à¦¨ খেলোয়াড় +Name[br]=C'hoarier nevez +Name[bs]=Novi igraÄ +Name[ca]=Nou jugador +Name[cs]=Nový hrÃ¡Ä +Name[cy]=Chwaraewr Newydd +Name[da]=Ny spiller +Name[de]=Neuer Spieler +Name[el]=Îέος παίκτης +Name[eo]=Nova Ludanto +Name[es]=Nuevo jugador +Name[et]=Uus mängija +Name[eu]=Jokalari berria +Name[fa]=بازیکن جدید +Name[fi]=Uusi pelaaja +Name[fr]=Nouveau joueur +Name[ga]=Imreoir nua +Name[gl]=Novo xogador +Name[he]=שחקן חדש +Name[hi]=नया खिलाड़ी +Name[hr]=Novi igraÄ +Name[hu]=Új játékos +Name[is]=Nýr leikmaður +Name[it]=Nuovo giocatore +Name[ja]=æ–°è¦ãƒ—レイヤー +Name[km]=អ្នក​លáŸáž„​ážáŸ’មី +Name[lt]=Naujas žaidÄ—jas +Name[lv]=Jauns spÄ“lÄ“tÄjs +Name[mk]=Ðов играч +Name[nb]=Ny spiller +Name[nds]=Nieg Speler +Name[ne]=नयाठखेलाडी +Name[nl]=Nieuwe speler +Name[nn]=Ny spelar +Name[pa]=ਨਵਾਂ ਖਿਡਾਰੀ +Name[pl]=Nowy gracz +Name[pt]=Novo jogador +Name[pt_BR]=Novo jogador +Name[ro]=Jucător nou +Name[ru]=Ðовый игрок +Name[se]=OÄ‘Ä‘a speallár +Name[sk]=Nový hrÃ¡Ä +Name[sl]=Nov igralec +Name[sr]=Ðови играч +Name[sr@Latn]=Novi igraÄ +Name[sv]=Ny spelare +Name[ta]=பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà¯ வீரர௠+Name[tg]=Бозингари нав +Name[tr]=Yeni oynatıcı +Name[uk]=Ðовий гравець +Name[uz]=Yangi oÊ»yinchi +Name[uz@cyrillic]=Янги ўйинчи +Name[wa]=Novea djouweu +Name[zh_CN]=新玩家 +Name[zh_TW]=新玩家 +Comment=A new player joins the game +Comment[ar]=لقد اشترك لاعب جديد ÙÙŠ اللعبة +Comment[be]=Ðовы гульнёўца далучыўÑÑ Ð´Ð° гульні +Comment[bg]=Ðов играч Ñе приÑъедини към играта +Comment[bn]=à¦à¦•à¦œà¦¨ নতà§à¦¨ খেলোয়াড় খেলায় যোগ দিয়েছেন +Comment[bs]=Novi igraÄ se ukljuÄuje u igru +Comment[ca]=Un nou jugador s'uneix a la partida +Comment[cs]=Nový hrÃ¡Ä se pÅ™ipojuje ke hÅ™e +Comment[cy]=Mae chwaraewr newydd yn ymuno â'r gêm +Comment[da]=En ny spiller gÃ¥r med i spillet +Comment[de]=Ein neuer Spieler ist hinzugekommen +Comment[el]=Ένας νέος παίκτης μπαίνει στο παιχνίδι +Comment[eo]=Nova ludanto ekpartoprenas +Comment[es]=Un nuevo jugador se une a la partida +Comment[et]=Mänguga liitus uus mängija +Comment[eu]=Jokalari berri batek jokoarekin bat egin du +Comment[fa]=یک بازیکن جدید به بازی می‌پیوندد +Comment[fi]=Uusi pelaaja liittyi peliin +Comment[fr]=Un nouveau joueur vient de se joindre à la partie +Comment[gl]=Un novo xogador entra na partida +Comment[he]=שחקן חדש מצטרף למשחק +Comment[hi]=à¤à¤• नया खिलाड़ी खेल में शामिल हà¥à¤† +Comment[hr]=Igri se pridružio novi igraÄ +Comment[hu]=Egy új játékos csatlakozik a játékhoz +Comment[is]=Nýr leikmaður tengist leiknum +Comment[it]=C'è un nuovo giocatore in gara +Comment[ja]=æ–°ã—ã„プレイヤーãŒã‚²ãƒ¼ãƒ ã«å‚加ã—ã¾ã—㟠+Comment[km]=អ្នក​លáŸáž„​ážáŸ’មី​ចូល​រួម​ល្បែង +Comment[lt]=Prisijungia naujas žaidÄ—jas +Comment[lv]=Jauns spÄ“lÄ“tÄjs pievienojas spÄ“lei +Comment[mk]=Ðов играч Ñе приклучува на играта +Comment[nb]=En ny spiller bilr med i spillet +Comment[nds]=En nieg Speler maakt mit +Comment[ne]=नयाठखेलाडीले खेलमा भाग लिनà¥à¤› +Comment[nl]=Een nieuwe speler neemt deel aan het spel +Comment[nn]=Ein ny spelar vert med i spelet +Comment[pa]=ਖੇਡ ਵਿੱਚ ਨਵਾਂ ਖਿਡਾਰੀ ਆਇਆ +Comment[pl]=Nowy gracz doÅ‚Ä…czyÅ‚ do gry +Comment[pt]=Um novo jogador junta-se ao jogo +Comment[pt_BR]=Um novo jogador entrou no jogo +Comment[ro]=Un jucător nou se alătură jocului +Comment[ru]=Ðовый игрок приÑоединилÑÑ Ðº игре +Comment[se]=OÄ‘Ä‘a speallár searvvai speallui +Comment[sk]=Nový hrÃ¡Ä sa pridal k hre +Comment[sl]=Nov igralec se je pridružil igri +Comment[sr]=Ðови играч Ñе придружио игри +Comment[sr@Latn]=Novi igraÄ se pridružio igri +Comment[sv]=En ny spelare gÃ¥r med i spelet +Comment[ta]=ஒர௠பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà®¾à®³à®°à¯ சேரà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари нав ба бозӣ ҳамроҳ шуд +Comment[tr]=Yeni bir oyuncu oyuna girdi +Comment[uk]=Ðовий гравець приєднуєтьÑÑ Ð´Ð¾ гри +Comment[uz]=OÊ»yinga yangi oÊ»yinchi qoÊ»shilayapti +Comment[uz@cyrillic]=Ўйинга Ñнги ўйинчи қўшилаÑпти +Comment[wa]=On novea djouweu s' a raloyî Ã¥ djeu +Comment[zh_CN]=ä¸€ä¸ªæ–°çŽ©å®¶åŠ å…¥äº†æ¸¸æˆ +Comment[zh_TW]=一個新玩家加入此éŠæˆ² + +[newgame] +Name=New game +Name[ar]=لعبة جديدة +Name[be]=ÐÐ¾Ð²Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +Name[bg]=Ðова игра +Name[bn]=নতà§à¦¨ খেলা +Name[br]=C'hoari nevez +Name[bs]=Nova igra +Name[ca]=Nova partida +Name[cs]=Nová hra +Name[cy]=Gêm Newydd +Name[da]=Nyt spil +Name[de]=Neues Spiel +Name[el]=Îέο παιχνίδι +Name[eo]=Nova Ludo +Name[es]=Partida nueva +Name[et]=Uus mäng +Name[eu]=Jokoi berria +Name[fa]=بازی جدید +Name[fi]=Uusi peli +Name[fr]=Nouveau jeu +Name[ga]=Cluiche Nua +Name[gl]=Novo xogo +Name[he]=משחק חדש +Name[hi]=नया खेल +Name[hr]=Nova igra +Name[hu]=Új játék +Name[is]=Nýr leikur +Name[it]=Nuova partita +Name[ja]=æ–°è¦ã‚²ãƒ¼ãƒ  +Name[km]=ល្បែង​ážáŸ’មី +Name[lt]=Naujas žaidimas +Name[lv]=Jauna spÄ“le +Name[mk]=Ðова игра +Name[nb]=Nytt spill +Name[nds]=Nieg Speel +Name[ne]=नयाठखेल +Name[nl]=Nieuw spel +Name[nn]=Nytt spel +Name[pa]=ਨਵੀਂ ਖੇਡ +Name[pl]=Nowa gra +Name[pt]=Novo jogo +Name[pt_BR]=Novo jogo +Name[ro]=Joc nou +Name[ru]=ÐÐ¾Ð²Ð°Ñ Ð¸Ð³Ñ€Ð° +Name[se]=OÄ‘Ä‘a speallu +Name[sk]=Nová hra +Name[sl]=Nova igra +Name[sr]=Ðова игра +Name[sr@Latn]=Nova igra +Name[sv]=Nytt spel +Name[ta]=பà¯à®¤à®¿à®¯ விளையாடà¯à®Ÿà¯ +Name[tg]=Бозии нав +Name[tr]=Yeni oyun +Name[uk]=Ðова гра +Name[uz]=Yangi oÊ»yin +Name[uz@cyrillic]=Янги ўйин +Name[wa]=Novea djeu +Name[zh_CN]=æ–°æ¸¸æˆ +Name[zh_TW]=æ–°éŠæˆ² +Comment=A new game is created +Comment[ar]=لقد انشئت لعبة جديدة +Comment[be]=Ð¡Ñ‚Ð²Ð¾Ñ€Ð°Ð½Ð°Ñ Ð½Ð¾Ð²Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +Comment[bg]=Създадена е нова игра +Comment[bn]=à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ খেলা আরমà§à¦­ হয়েছে +Comment[br]=Krouet eo ur c'hoari nevez +Comment[bs]=Napravljena je nova igra +Comment[ca]=Es crea una nova partida +Comment[cs]=Je vytvoÅ™ena nová hra +Comment[cy]=Mae gêm newydd wedi ei greu +Comment[da]=Et nyt spil bliver oprettet +Comment[de]=Es beginnt ein neues Spiel +Comment[el]=Ένα νέο παιχνίδι δημιουÏγείται +Comment[eo]=Nova ludo kreiÄis +Comment[es]=Una nueva partida ha sido creada +Comment[et]=Loodi uus mäng +Comment[eu]=Joko berri bat sortu da +Comment[fa]=یک بازی جدید ایجاد می‌‌‌شود +Comment[fi]=Uusi peli luotiin +Comment[fr]=Une nouvelle partie vient d'être créée +Comment[gl]=Creouse unha nova partida +Comment[he]=נוצר משחק חדש +Comment[hi]=à¤à¤• नया खेल बनाया गया +Comment[hr]=Pokrenuta je nova igra +Comment[hu]=Új játék jön létre +Comment[is]=Nýr leikur er búinn til +Comment[it]=Viene avviata una nuova partita +Comment[ja]=æ–°ã—ã„ゲームを作æˆã—ã¾ã—㟠+Comment[km]=ល្បែង​ážáŸ’មី​ážáŸ’រូវ​បាន​បង្កើហ+Comment[lt]=Sukuriamas naujas žaidimas +Comment[lv]=Jauna spÄ“le ir izveidota +Comment[mk]=Создадена е нова игра +Comment[nb]=Et nytt spill startes +Comment[nds]=En nieg Speel warrt opstellt +Comment[ne]=नयाठखेल सिरà¥à¤œà¤¨à¤¾ गरियो +Comment[nl]=Er is een nieuw spel aangemaakt +Comment[nn]=Eit nytt spel vert starta +Comment[pa]=ਇੱਕ ਨਵੀਂ ਖੇਡ ਬਣਾਈ ਗਈ ਹੈ +Comment[pl]=Utworzenie nowej gry +Comment[pt]=Um novo jogo é criado +Comment[pt_BR]=Um novo jogo foi criado +Comment[ro]=Este creat un joc nou +Comment[ru]=Ðачата Ð½Ð¾Ð²Ð°Ñ Ð¸Ð³Ñ€Ð° +Comment[se]=OÄ‘Ä‘a speallu ráhkaduvvo +Comment[sk]=Vytvorená nová hra +Comment[sl]=Ustvarjena je bila nova igra +Comment[sr]=Ðаправљена је нова игра +Comment[sr@Latn]=Napravljena je nova igra +Comment[sv]=Ett nytt spel har skapats +Comment[ta]=ஒர௠பà¯à®¤à¯ விளையாடà¯à®Ÿà¯ உரà¯à®µà®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®•à®¿à®±à®¤à¯ +Comment[tg]=Бозии нав офарида шуд +Comment[tr]=Yeni bir oyun baÅŸlatıldı +Comment[uk]=Ðову гру Ñтворено +Comment[uz]=Yangi oÊ»yin yaratilmoqda +Comment[uz@cyrillic]=Янги ўйин Ñратилмоқда +Comment[wa]=On novea djeu a stî enondé +Comment[zh_CN]=åˆ›å»ºäº†ä¸€ä¸ªæ–°æ¸¸æˆ +Comment[zh_TW]=一個新éŠæˆ²é–‹å•Ÿå®Œæˆ diff --git a/atlantik/kio_atlantik/Makefile.am b/atlantik/kio_atlantik/Makefile.am new file mode 100644 index 00000000..69279666 --- /dev/null +++ b/atlantik/kio_atlantik/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = kio_atlantik.la + +kio_atlantik_la_SOURCES = kio_atlantik.cpp +kio_atlantik_la_LIBADD = $(LIB_KIO) +kio_atlantik_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +noinst_HEADERS = kio_atlantik.h + +kdelnk_DATA = atlantik.protocol +kdelnkdir = $(kde_servicesdir) + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kio_atlantik.pot diff --git a/atlantik/kio_atlantik/atlantik.protocol b/atlantik/kio_atlantik/atlantik.protocol new file mode 100644 index 00000000..6839c736 --- /dev/null +++ b/atlantik/kio_atlantik/atlantik.protocol @@ -0,0 +1,7 @@ +[Protocol] +exec=kio_atlantik +protocol=atlantik +input=none +output=none +reading=true +Icon=atlantik diff --git a/atlantik/kio_atlantik/kio_atlantik.cpp b/atlantik/kio_atlantik/kio_atlantik.cpp new file mode 100644 index 00000000..3707d41b --- /dev/null +++ b/atlantik/kio_atlantik/kio_atlantik.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include + +#include +#undef KDE_3_1_FEATURES +#ifdef KDE_MAKE_VERSION +#if KDE_VERSION > KDE_MAKE_VERSION (3, 1, 0) +#define KDE_3_1_FEATURES +#endif +#endif +#include +#include +#include + +#include "kio_atlantik.h" +#include "libatlantic_export.h" + +extern "C" +{ + int LIBATLANTIC_EXPORT kdemain( int, char **argv ) + { + KInstance instance( "kio_atlantik" ); + AtlantikProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + return 0; + } +} + +void AtlantikProtocol::get( const KURL& url ) +{ + KProcess *proc = new KProcess; + *proc << "atlantik"; + +#ifdef KDE_3_1_FEATURES + QString host = url.hasHost() ? url.host() : KProcess::quote( url.queryItem("host") ); +#else + QString host = url.hasHost() ? url.host() : url.queryItem("host"); +#endif + QString port = QString::number( url.port() ? url.port() : 1234 ); + int game = url.queryItem("game").toInt(); + QString gameString = game ? QString::number( game ) : QString::null; + + if (!host.isNull() && !port.isNull()) + { + *proc << "--host" << host << "--port" << port; + if (!gameString.isNull()) + *proc << "--game" << gameString; + } + + proc->start(KProcess::DontCare); + proc->detach(); + finished(); +} diff --git a/atlantik/kio_atlantik/kio_atlantik.h b/atlantik/kio_atlantik/kio_atlantik.h new file mode 100644 index 00000000..ac794ca9 --- /dev/null +++ b/atlantik/kio_atlantik/kio_atlantik.h @@ -0,0 +1,22 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// version 2 as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; see the file COPYING. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +class AtlantikProtocol : public KIO::SlaveBase +{ +public: + AtlantikProtocol( const QCString &pool, const QCString &app) : SlaveBase( "atlantik", pool, app ) {} + virtual void get( const KURL& url ); +}; diff --git a/atlantik/libatlantic/Makefile.am b/atlantik/libatlantic/Makefile.am new file mode 100644 index 00000000..314a0215 --- /dev/null +++ b/atlantik/libatlantic/Makefile.am @@ -0,0 +1,15 @@ +KDE_OPTIONS = qtonly + +INCLUDES = $(all_includes) +lib_LTLIBRARIES = libatlantic.la +libatlantic_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantic_la_LIBADD = $(LIB_QT) + +libatlantic_la_SOURCES = atlantic_core.cpp auction.cpp configoption.cpp estate.cpp \ + estategroup.cpp game.cpp player.cpp trade.cpp + +libatlanticincludedir = $(includedir)/atlantic +libatlanticinclude_HEADERS = atlantic_core.h auction.h configoption.h estate.h \ + estategroup.h game.h player.h trade.h libatlantic_export.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantic/atlantic_core.cpp b/atlantik/libatlantic/atlantic_core.cpp new file mode 100644 index 00000000..39e1200e --- /dev/null +++ b/atlantik/libatlantic/atlantic_core.cpp @@ -0,0 +1,369 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "atlantic_core.h" + +#include "auction.h" +#include "configoption.h" +#include "estate.h" +#include "estategroup.h" +#include "game.h" +#include "player.h" +#include "trade.h" + +AtlanticCore::AtlanticCore(QObject *parent, const char *name) : QObject(parent, name) +{ + m_playerSelf = 0; +} + +void AtlanticCore::reset(bool deletePermanents) +{ + m_auctions.setAutoDelete(true); + m_auctions.clear(); + m_auctions.setAutoDelete(false); + m_estates.setAutoDelete(true); + m_estates.clear(); + m_estates.setAutoDelete(false); + m_estateGroups.setAutoDelete(true); + m_estateGroups.clear(); + m_estateGroups.setAutoDelete(false); + m_configOptions.setAutoDelete(true); + m_configOptions.clear(); + m_configOptions.setAutoDelete(false); + + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + { + emit removeGUI(trade); + trade->deleteLater(); + } + m_trades.clear(); + + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + { + if (deletePermanents) + { + emit removeGUI(player); + player->deleteLater(); + } + else + { + player->setLocation(0); + player->setDestination(0); + } + } + if (deletePermanents) + { + m_players.clear(); + m_playerSelf = 0; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + { + emit removeGUI(game); + game->deleteLater(); + } + m_games.clear(); + } +} + +bool AtlanticCore::selfIsMaster() const +{ + return (m_playerSelf && m_playerSelf->game() && m_playerSelf->game()->master() == m_playerSelf); +} + +void AtlanticCore::setPlayerSelf(Player *player) +{ + m_playerSelf = player; +} + +Player *AtlanticCore::playerSelf() +{ + return m_playerSelf; +} + +QPtrList AtlanticCore::players() +{ + return m_players; +} + +Player *AtlanticCore::newPlayer(int playerId, const bool &playerSelf) +{ + Player *player = new Player(playerId); + m_players.append(player); + + if (playerSelf) + { + player->setIsSelf(playerSelf); + m_playerSelf = player; + } + + emit createGUI(player); + + return player; +} + +Player *AtlanticCore::findPlayer(int playerId) +{ + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + if (player->id() == playerId) + return player; + + return 0; +} + +void AtlanticCore::removePlayer(Player *player) +{ + m_players.remove(player); + emit removeGUI(player); + player->deleteLater(); +} + +QPtrList AtlanticCore::games() +{ + return m_games; +} + +Game *AtlanticCore::newGame(int gameId, const QString &type) +{ + Game *game = new Game(gameId); + m_games.append(game); + + if ( !type.isNull() ) + game->setType(type); + + emit createGUI(game); + + return game; +} + +Game *AtlanticCore::findGame(const QString &type) +{ + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + if (game->id() == -1 && game->type() == type) + return game; + + return 0; +} + +Game *AtlanticCore::findGame(int gameId) +{ + if (gameId == -1) + return 0; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + if (game->id() == gameId) + return game; + + return 0; +} + +Game *AtlanticCore::gameSelf() +{ + return( m_playerSelf ? m_playerSelf->game() : 0 ); +} + +void AtlanticCore::removeGame(Game *game) +{ + m_games.remove(game); + emit removeGUI(game); + game->deleteLater(); +} + +void AtlanticCore::emitGames() +{ + for (QPtrListIterator it(m_games); (*it) ; ++it) + emit createGUI( (*it) ); +} + +QPtrList AtlanticCore::estates() +{ + return m_estates; +} + +Estate *AtlanticCore::newEstate(int estateId) +{ + Estate *estate = new Estate(estateId); + m_estates.append(estate); + return estate; +} + +Estate *AtlanticCore::findEstate(int estateId) +{ + Estate *estate = 0; + for (QPtrListIterator it(m_estates); (estate = *it) ; ++it) + if (estate->id() == estateId) + return estate; + + return 0; +} + +Estate *AtlanticCore::estateAfter(Estate *estate) +{ + Estate *eFirst = 0, *eTmp = 0; + bool useNext = false; + for (QPtrListIterator it(m_estates); (eTmp = *it) ; ++it) + { + if (!eFirst) + eFirst = eTmp; + if (eTmp == estate) + useNext = true; + else if (useNext) + return eTmp; + } + return eFirst; +} + +QPtrList AtlanticCore::estateGroups() +{ + return m_estateGroups; +} + +EstateGroup *AtlanticCore::newEstateGroup(int groupId) +{ + EstateGroup *estateGroup = new EstateGroup(groupId); + m_estateGroups.append(estateGroup); + return estateGroup; +} + +EstateGroup *AtlanticCore::findEstateGroup(int groupId) +{ + EstateGroup *estateGroup = 0; + for (QPtrListIterator it(m_estateGroups); (estateGroup = *it) ; ++it) + if (estateGroup->id() == groupId) + return estateGroup; + + return 0; +} + +QPtrList AtlanticCore::trades() +{ + return m_trades; +} + +Trade *AtlanticCore::newTrade(int tradeId) +{ + Trade *trade = new Trade(tradeId); + m_trades.append(trade); + + emit createGUI(trade); + + return trade; +} + +Trade *AtlanticCore::findTrade(int tradeId) +{ + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + if (trade->tradeId() == tradeId) + return trade; + + return 0; +} + +void AtlanticCore::removeTrade(Trade *trade) +{ + m_trades.remove(trade); + emit removeGUI(trade); + trade->deleteLater(); +} + +QPtrList AtlanticCore::auctions() +{ + return m_auctions; +} + +Auction *AtlanticCore::newAuction(int auctionId, Estate *estate) +{ + Auction *auction = new Auction(auctionId, estate); + m_auctions.append(auction); + return auction; +} + +void AtlanticCore::delAuction(Auction *auction) +{ + m_auctions.remove(auction); + delete auction; +} + +ConfigOption *AtlanticCore::newConfigOption(int configId) +{ + ConfigOption *configOption = new ConfigOption(configId); + m_configOptions.append(configOption); + + emit createGUI(configOption); + + return configOption; +} + +void AtlanticCore::removeConfigOption(ConfigOption *configOption) +{ + m_configOptions.remove(configOption); + emit removeGUI(configOption); + configOption->deleteLater(); +} + +ConfigOption *AtlanticCore::findConfigOption(int configId) +{ + ConfigOption *configOption = 0; + for (QPtrListIterator it(m_configOptions); (configOption = *it) ; ++it) + if (configOption->id() == configId) + return configOption; + + return 0; +} + +void AtlanticCore::printDebug() +{ + Player *player = 0; + for (QPtrListIterator it(m_players); (player = *it) ; ++it) + if (player == m_playerSelf) + std::cout << "PS: " << player->name().latin1() << ", game " << QString::number(player->game() ? player->game()->id() : -1).latin1() << std::endl; + else + std::cout << " P: " << player->name().latin1() << ", game " << QString::number(player->game() ? player->game()->id() : -1).latin1() << std::endl; + + Game *game = 0; + for (QPtrListIterator it(m_games); (game = *it) ; ++it) + std::cout << " G: " << QString::number(game->id()).latin1() << ", master: " << QString::number(game->master() ? game->master()->id() : -1 ).latin1() << std::endl; + + Estate *estate = 0; + for (QPtrListIterator it(m_estates); (estate = *it) ; ++it) + std::cout << " E: " << estate->name().latin1() << std::endl; + + EstateGroup *estateGroup = 0; + for (QPtrListIterator it(m_estateGroups); (estateGroup = *it) ; ++it) + std::cout << "EG: " << estateGroup->name().latin1() << std::endl; + + Auction *auction = 0; + for (QPtrListIterator it(m_auctions); (auction = *it) ; ++it) + std::cout << " A: " << QString::number(auction->auctionId()).latin1() << std::endl; + + Trade *trade = 0; + for (QPtrListIterator it(m_trades); (trade = *it) ; ++it) + std::cout << " T: " << QString::number(trade->tradeId()).latin1() << std::endl; + + ConfigOption *configOption = 0; + for (QPtrListIterator it(m_configOptions); (configOption = *it) ; ++it) + std::cout << "CO:" << QString::number(configOption->id()).latin1() << " " << configOption->name().latin1() << " " << configOption->value().latin1() << std::endl; +} + +#include "atlantic_core.moc" diff --git a/atlantik/libatlantic/atlantic_core.h b/atlantik/libatlantic/atlantic_core.h new file mode 100644 index 00000000..bca5b783 --- /dev/null +++ b/atlantik/libatlantic/atlantic_core.h @@ -0,0 +1,105 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_CORE_H +#define LIBATLANTIC_CORE_H + +#include +#include + +#include "libatlantic_export.h" + +class Player; +class ConfigOption; +class Estate; +class EstateGroup; +class Game; +class Trade; +class Auction; + +class LIBATLANTIC_EXPORT AtlanticCore : public QObject +{ +Q_OBJECT + +public: + AtlanticCore(QObject *parent, const char *name); + + void reset(bool deletePermanents = false); + + bool selfIsMaster() const; + + void setPlayerSelf(Player *player); + Player *playerSelf(); + + QPtrList players(); + Player *newPlayer(int playerId, const bool &playerSelf = false); + Player *findPlayer(int playerId); + void removePlayer(Player *player); + + QPtrList games(); + Game *newGame(int gameId, const QString &type = QString::null); + Game *findGame(const QString &type); // finds game types + Game *findGame(int gameId); // finds actual games + Game *gameSelf(); + void removeGame(Game *game); + void emitGames(); + + QPtrList estates(); + Estate *newEstate(int estateId); + Estate *findEstate(int estateId); + Estate *estateAfter(Estate *estate); + + QPtrList estateGroups(); + EstateGroup *newEstateGroup(int groupId); + EstateGroup *findEstateGroup(int groupId); + + QPtrList trades(); + Trade *newTrade(int tradeId); + Trade *findTrade(int tradeId); + void removeTrade(Trade *trade); + + QPtrList auctions(); + Auction *newAuction(int auctionId, Estate *estate); + void delAuction(Auction *auction); + + ConfigOption *newConfigOption(int configId); + void removeConfigOption(ConfigOption *configOption); + ConfigOption *findConfigOption(int configId); + + void printDebug(); + +signals: + void createGUI(Player *player); + void removeGUI(Player *player); + void createGUI(Game *game); + void removeGUI(Game *game); + void createGUI(Trade *trade); + void removeGUI(Trade *trade); + void createGUI(ConfigOption *configOption); + void removeGUI(ConfigOption *configOption); + +private: + Player *m_playerSelf; + QPtrList m_players; + QPtrList m_games; + QPtrList m_estates; + QPtrList m_estateGroups; + QPtrList m_trades; + QPtrList m_auctions; + QPtrList m_configOptions; +}; + +#endif diff --git a/atlantik/libatlantic/auction.cpp b/atlantik/libatlantic/auction.cpp new file mode 100644 index 00000000..70734c4e --- /dev/null +++ b/atlantik/libatlantic/auction.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "auction.h" +#include "auction.moc" +#include "player.h" +#include "estate.h" + +Auction::Auction(int auctionId, Estate *estate) : QObject() +{ + m_auctionId = auctionId; + m_estate = estate; + m_status = 0; + m_changed = false; +} + +Auction::~Auction() +{ + emit completed(); +} + +void Auction::setStatus(int status) +{ + if (m_status != status) + { + m_status = status; + m_changed = true; + } +} + +void Auction::newBid(Player *player, int amount) +{ + emit updateBid(player, amount); +} + +void Auction::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/auction.h b/atlantik/libatlantic/auction.h new file mode 100644 index 00000000..cc44cce5 --- /dev/null +++ b/atlantik/libatlantic/auction.h @@ -0,0 +1,57 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_AUCTION_H +#define LIBATLANTIC_AUCTION_H + +#include + +#include "libatlantic_export.h" + +class Player; +class Estate; + +class LIBATLANTIC_EXPORT Auction : public QObject +{ +Q_OBJECT + +public: + Auction(int auctionId, Estate *estate); + virtual ~Auction(); + + int auctionId() { return m_auctionId; } + Estate *estate() { return m_estate; } + + void setStatus(int status); + int status() { return m_status; } + + void newBid(Player *player, int bid); + + void update(bool force = false); + +signals: + void changed(); + void completed(); + void bid(Auction *auction, int amount); + void updateBid(Player *player, int amount); + +private: + bool m_changed; + int m_auctionId, m_status; + Estate *m_estate; +}; + +#endif diff --git a/atlantik/libatlantic/configoption.cpp b/atlantik/libatlantic/configoption.cpp new file mode 100644 index 00000000..00a8eb12 --- /dev/null +++ b/atlantik/libatlantic/configoption.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "configoption.h" + +ConfigOption::ConfigOption(int configId) : QObject() +{ + m_id = configId; + m_name = ""; + m_description = ""; + m_edit = false; + m_value = ""; + m_changed = false; +} + +int ConfigOption::id() +{ + return m_id; +} + +void ConfigOption::setName(const QString &name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString ConfigOption::name() const +{ + return m_name; +} + +void ConfigOption::setDescription(const QString &description) +{ + if (m_description != description) + { + m_description = description; + m_changed = true; + } +} + +QString ConfigOption::description() const +{ + return m_description; +} + +void ConfigOption::setEdit(bool edit) +{ + if (m_edit != edit) + { + m_edit = edit; + m_changed = true; + } +} + +bool ConfigOption::edit() +{ + return m_edit; +} + +void ConfigOption::setValue(const QString &value) +{ + if (m_value != value) + { + m_value = value; + m_changed = true; + } +} + +QString ConfigOption::value() const +{ + return m_value; +} + +void ConfigOption::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +#include "configoption.moc" diff --git a/atlantik/libatlantic/configoption.h b/atlantik/libatlantic/configoption.h new file mode 100644 index 00000000..a29d6b45 --- /dev/null +++ b/atlantik/libatlantic/configoption.h @@ -0,0 +1,51 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_CONFIGOPTION_H +#define LIBATLANTIC_CONFIGOPTION_H + +#include +#include + +#include "libatlantic_export.h" + +class LIBATLANTIC_EXPORT ConfigOption : public QObject +{ +Q_OBJECT + +public: + ConfigOption(int configId); + int id(); + void setName(const QString &name); + QString name() const; + void setDescription(const QString &description); + QString description() const; + void setEdit(bool edit); + bool edit(); + void setValue(const QString &value); + QString value() const; + void update(bool force = false); + +signals: + void changed(ConfigOption *configOption); + +private: + int m_id; + bool m_changed, m_edit; + QString m_name, m_description, m_value; +}; + +#endif diff --git a/atlantik/libatlantic/estate.cpp b/atlantik/libatlantic/estate.cpp new file mode 100644 index 00000000..eef69280 --- /dev/null +++ b/atlantik/libatlantic/estate.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +//#include + +#include "estate.h" +#include "estate.moc" +#include "player.h" + +Estate::Estate(int estateId) : QObject() +{ + m_id = estateId; + m_name = QString::null; + m_owner = 0; + m_houses = 0; + m_price = 0; + m_money = 0; + m_estateGroup = 0; + m_changed = m_canBeOwned = m_canBuyHouses = m_canSellHouses = m_isMortgaged = m_canToggleMortgage = false; + m_bgColor = QColor(); + m_color = QColor(); +} + +void Estate::setEstateGroup(EstateGroup *estateGroup) +{ + if (m_estateGroup != estateGroup) + m_estateGroup = estateGroup; +} + +void Estate::setOwner(Player *player) +{ + if (m_owner != player) + { + m_owner = player; + m_changed = true; + } +} +bool Estate::isOwned() const +{ + if (m_owner) + return true; + else + return false; +} + +bool Estate::isOwnedBySelf() const +{ + if (m_owner && m_owner->isSelf()) + return true; + else + return false; +} + +void Estate::setHouses(unsigned int houses) +{ + if (m_houses != houses) + m_houses = houses; + m_changed = true; +} + +void Estate::setName(QString name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString Estate::name() const +{ + return m_name; +} + +void Estate::setColor(QColor color) +{ + if (m_color != color) + { + m_color = color; + m_changed = true; + } +} + +void Estate::setBgColor(QColor color) +{ + if (m_bgColor != color) + { + m_bgColor = color; + m_changed = true; + } +} + +void Estate::setCanBeOwned(const bool canBeOwned) +{ + if (m_canBeOwned != canBeOwned) + m_canBeOwned = canBeOwned; +} + +void Estate::setCanBuyHouses(const bool canBuyHouses) +{ + if (m_canBuyHouses != canBuyHouses) + m_canBuyHouses = canBuyHouses; +} + +void Estate::setCanSellHouses(const bool canSellHouses) +{ + if (m_canSellHouses != canSellHouses) + m_canSellHouses = canSellHouses; +} + +void Estate::setIsMortgaged(const bool isMortgaged) +{ + if (m_isMortgaged != isMortgaged) + { + m_isMortgaged = isMortgaged; + m_changed = true; + } +} + +void Estate::setCanToggleMortgage(const bool canToggleMortgage) +{ + if (m_canToggleMortgage != canToggleMortgage) + { + m_canToggleMortgage = canToggleMortgage; + m_changed = true; + } +} + +void Estate::setMoney(int money) +{ + if (m_money != money) + { + m_money = money; + m_changed = true; + } +} + +int Estate::money() +{ + return m_money; +} + +void Estate::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/estate.h b/atlantik/libatlantic/estate.h new file mode 100644 index 00000000..b6b768a5 --- /dev/null +++ b/atlantik/libatlantic/estate.h @@ -0,0 +1,95 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_ESTATE_H +#define LIBATLANTIC_ESTATE_H + +#include +#include + +#include "libatlantic_export.h" + +class EstateGroup; +class Player; + +class LIBATLANTIC_EXPORT Estate : public QObject +{ +Q_OBJECT + +public: + Estate(int estateId); + int id() { return m_id; } + void setName(QString name); + QString name() const; + void setEstateGroup(EstateGroup *estateGroup); + EstateGroup *estateGroup() { return m_estateGroup; } + void setOwner(Player *player); + bool isOwned() const; + bool isOwnedBySelf() const; + Player *owner() { return m_owner; } + void setHouses(unsigned int houses); + unsigned int houses() { return m_houses; } + void setCanBeOwned(const bool canBeOwned); + bool canBeOwned() const { return m_canBeOwned; } + void setCanBuyHouses(const bool canBuyHouses); + bool canBuyHouses() const { return m_canBuyHouses; } + void setCanSellHouses(const bool canSellHouses); + bool canSellHouses() const { return m_canSellHouses; } + void setHousePrice(const unsigned int housePrice) { m_housePrice = housePrice; } + unsigned int housePrice() const { return m_housePrice; } + void setHouseSellPrice(const unsigned int houseSellPrice) { m_houseSellPrice = houseSellPrice; } + unsigned int houseSellPrice() const { return m_houseSellPrice; } + void setIsMortgaged(const bool isMortgaged); + bool isMortgaged() const { return m_isMortgaged; } + void setCanToggleMortgage(const bool canToggleMortgage); + bool canToggleMortgage() const { return m_canToggleMortgage; } + void setMortgagePrice(const unsigned int mortgagePrice) { m_mortgagePrice = mortgagePrice; } + unsigned int mortgagePrice() const { return m_mortgagePrice; } + void setUnmortgagePrice(const unsigned int unmortgagePrice) { m_unmortgagePrice = unmortgagePrice; } + unsigned int unmortgagePrice() const { return m_unmortgagePrice; } + void setColor(QColor color); + QColor color() const { return m_color; } + void setBgColor(QColor color); + QColor bgColor() const { return m_bgColor; } + void setPrice(const unsigned int price) { m_price = price; } + unsigned int price() const { return m_price; } + void setMoney(int money); + int money(); + void update(bool force = false); + +signals: + void changed(); + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void newTrade(Player *player); + void LMBClicked(Estate *estate); + +protected: + bool m_changed; + int m_id; + +private: + QString m_name; + Player *m_owner; + EstateGroup *m_estateGroup; + unsigned int m_houses, m_price, m_housePrice, m_houseSellPrice, m_mortgagePrice, m_unmortgagePrice; + int m_money; + bool m_canBeOwned, m_canBuyHouses, m_canSellHouses, m_isMortgaged, m_canToggleMortgage; + QColor m_bgColor, m_color; +}; + +#endif diff --git a/atlantik/libatlantic/estategroup.cpp b/atlantik/libatlantic/estategroup.cpp new file mode 100644 index 00000000..e0148afc --- /dev/null +++ b/atlantik/libatlantic/estategroup.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "estategroup.h" +#include "estategroup.moc" + +EstateGroup::EstateGroup(const int id) : QObject() +{ + m_id = id; +} + +void EstateGroup::setName(const QString name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +void EstateGroup::update(bool force) +{ + if (m_changed || force) + { + emit changed(); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/estategroup.h b/atlantik/libatlantic/estategroup.h new file mode 100644 index 00000000..3e08a9ce --- /dev/null +++ b/atlantik/libatlantic/estategroup.h @@ -0,0 +1,44 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_ESTATEGROUP_H +#define LIBATLANTIC_ESTATEGROUP_H + +#include + +#include "libatlantic_export.h" + +class LIBATLANTIC_EXPORT EstateGroup : public QObject +{ +Q_OBJECT + +public: + EstateGroup(const int id); + int id() { return m_id; } + void setName(const QString name); + QString name() const { return m_name; } + void update(bool force = false); + +signals: + void changed(); + +private: + int m_id; + bool m_changed; + QString m_name; +}; + +#endif diff --git a/atlantik/libatlantic/game.cpp b/atlantik/libatlantic/game.cpp new file mode 100644 index 00000000..1f4eb244 --- /dev/null +++ b/atlantik/libatlantic/game.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include "game.h" + +Game::Game(int gameId) : QObject() +{ + m_id = gameId; + m_description = QString::null; + m_type = QString::null; + m_players = 0; + m_master = 0; + + m_changed = false; +} + +int Game::id() const +{ + return m_id; +} + +void Game::setCanBeJoined(const bool &canBeJoined) +{ + if (m_canBeJoined != canBeJoined) + { + m_canBeJoined = canBeJoined; + m_changed = true; + } +} + +bool Game::canBeJoined() const +{ + return m_canBeJoined; +} + +void Game::setDescription(const QString &description) +{ + if (m_description != description) + { + m_description = description; + m_changed = true; + } +} + +QString Game::description() const +{ + return m_description; +} + +void Game::setName(const QString &name) +{ + if (m_name != name) + { + m_name = name; + m_changed = true; + } +} + +QString Game::name() const +{ + return m_name; +} + +void Game::setType(const QString &type) +{ + if (m_type != type) + { + m_type = type; + m_changed = true; + } +} + +QString Game::type() const +{ + return m_type; +} + +void Game::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +int Game::players() +{ + return m_players; +} + +void Game::setPlayers(int players) +{ + if (m_players != players) + { + m_players = players; + m_changed = true; + } +} + +Player *Game::master() +{ + return m_master; +} + +void Game::setMaster(Player *master) +{ + if (m_master != master) + { + m_master = master; + m_changed = true; + } +} + +#include "game.moc" diff --git a/atlantik/libatlantic/game.h b/atlantik/libatlantic/game.h new file mode 100644 index 00000000..8eaa85f6 --- /dev/null +++ b/atlantik/libatlantic/game.h @@ -0,0 +1,62 @@ +// Copyright (c) 2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_GAME_H +#define LIBATLANTIC_GAME_H + +#include + +#include "libatlantic_export.h" + +class QString; + +class Player; + +class LIBATLANTIC_EXPORT Game : public QObject +{ +Q_OBJECT + +public: + Game(int gameId); + + int id() const; + void setCanBeJoined(const bool &canBeJoined); + bool canBeJoined() const; + void setDescription(const QString &description); + QString description() const; + void setName(const QString &name); + QString name() const; + void setType(const QString &type); + QString type() const; + int players(); + void setPlayers(int players); + Player *master(); + void setMaster(Player *master); + + void update(bool force = false); + +signals: + void changed(Game *game); + +private: + bool m_changed; + bool m_canBeJoined; + QString m_description, m_name, m_type; + int m_id, m_players; + Player *m_master; +}; + +#endif diff --git a/atlantik/libatlantic/libatlantic_export.h b/atlantik/libatlantic/libatlantic_export.h new file mode 100644 index 00000000..6ea0423a --- /dev/null +++ b/atlantik/libatlantic/libatlantic_export.h @@ -0,0 +1,25 @@ +// Copyright (c) 2004 Dirk Mueller + +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_EXPORT_H +#define LIBATLANTIC_EXPORT_H + +#include + +#define LIBATLANTIC_EXPORT KDE_EXPORT + +#endif diff --git a/atlantik/libatlantic/player.cpp b/atlantik/libatlantic/player.cpp new file mode 100644 index 00000000..ab5e9268 --- /dev/null +++ b/atlantik/libatlantic/player.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "player.h" +#include "player.moc" +#include "estate.h" + +Player::Player(int playerId) : QObject() +{ + m_id = playerId; + m_game = 0; + m_name = ""; + m_host = ""; + m_image = ""; + m_location = m_destination = 0; + m_money = 0; + m_changed = m_isSelf = false; + m_bankrupt = m_hasDebt = m_hasTurn = m_canRoll = m_canBuy = m_canAuction = m_canUseCard = m_inJail = false; +} + +void Player::setGame(Game *game) +{ + if (m_game != game) + { + m_game = game; + m_changed = true; + } +} + +Game *Player::game() +{ + return m_game; +} + +void Player::setLocation(Estate *location) +{ + if (m_location != location) + { + m_location = location; + m_changed = true; + } +} + +void Player::setDestination(Estate *destination) +{ + if (m_destination != destination) + { + m_destination = destination; + m_changed = true; + } +} + +void Player::setBankrupt(bool bankrupt) +{ + if (m_bankrupt != bankrupt) + { + m_bankrupt = bankrupt; + m_changed = true; + } +} + +void Player::setHasDebt(bool hasDebt) +{ + if (m_hasDebt != hasDebt) + { + m_hasDebt = hasDebt; + m_changed = true; + } +} + +void Player::setHasTurn(const bool hasTurn) +{ + if (m_hasTurn != hasTurn) + { + m_hasTurn = hasTurn; + m_changed = true; + if (m_hasTurn && m_isSelf) + emit gainedTurn(); + } +} + +void Player::setCanRoll(bool canRoll) +{ + if (m_canRoll != canRoll) + { + m_canRoll = canRoll; + m_changed = true; + } +} + +void Player::setCanBuy(bool canBuy) +{ + if (m_canBuy != canBuy) + { + m_canBuy = canBuy; + m_changed = true; + } +} + +void Player::setCanAuction(bool canAuction) +{ + if (m_canAuction != canAuction) + { + m_canAuction = canAuction; + m_changed = true; + } +} + +void Player::setCanUseCard(bool canUseCard) +{ + if (m_canUseCard != canUseCard) + { + m_canUseCard = canUseCard; + m_changed = true; + } +} + +void Player::setInJail(const bool inJail) +{ + if (m_inJail != inJail) + { + m_inJail = inJail; + m_changed = true; + } +} + +void Player::setName(const QString _n) +{ + if (m_name != _n) + { + m_name = _n; + m_changed = true; + } +} + +void Player::setHost(const QString &host) +{ + if (m_host != host) + { + m_host = host; + m_changed = true; + } +} + +void Player::setImage(const QString &image) +{ + if (m_image != image) + { + m_image = image; + m_changed = true; + } +} + +void Player::setMoney(unsigned int money) +{ + if (m_money != money) + { + m_money = money; + m_changed = true; + } +} + +void Player::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} diff --git a/atlantik/libatlantic/player.h b/atlantik/libatlantic/player.h new file mode 100644 index 00000000..571276ef --- /dev/null +++ b/atlantik/libatlantic/player.h @@ -0,0 +1,84 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_PLAYER_H +#define LIBATLANTIC_PLAYER_H + +#include +#include + +#include "libatlantic_export.h" + +class Estate; +class Game; + +class LIBATLANTIC_EXPORT Player : public QObject +{ +Q_OBJECT + +public: + Player(int playerId); + + int id() { return m_id; } + void setGame(Game *game); + Game *game(); + void setLocation(Estate *location); + Estate *location() { return m_location; } + void setDestination(Estate *destination); + Estate *destination() { return m_destination; } + void setIsSelf(const bool isSelf) { m_isSelf = isSelf; } + bool isSelf() const { return m_isSelf; } + void setBankrupt(bool bankrupt); + bool isBankrupt() { return m_bankrupt; } + void setHasDebt(bool hasDebt); + bool hasDebt() { return m_hasDebt; } + void setHasTurn(const bool hasTurn); + bool hasTurn() const { return m_hasTurn; } + void setCanRoll(bool canRoll); + bool canRoll() const { return m_canRoll; } + void setCanBuy(bool canBuy); + bool canBuy() const { return m_canBuy; } + void setCanAuction(bool canAuction); + bool canAuction() const { return m_canAuction; } + void setCanUseCard(bool canUseCard); + bool canUseCard() const { return m_canUseCard; } + void setInJail(const bool inJail); + bool inJail() const { return m_inJail; } + void setName(const QString _n); + QString name() const { return m_name; } + void setHost(const QString &host); + QString host() const { return m_host; } + void setImage(const QString &image); + const QString image() const { return m_image; } + void setMoney(unsigned int _m); + unsigned int money() const { return m_money; } + void update(bool force = false); + +signals: + void changed(Player *player); + void gainedTurn(); + +private: + int m_id; + bool m_changed, m_isSelf; + bool m_bankrupt, m_hasDebt, m_hasTurn, m_canRoll, m_canBuy, m_canAuction, m_canUseCard, m_inJail; + unsigned int m_money; + QString m_name, m_host, m_image; + Game *m_game; + Estate *m_location, *m_destination; +}; + +#endif diff --git a/atlantik/libatlantic/trade.cpp b/atlantik/libatlantic/trade.cpp new file mode 100644 index 00000000..b516dc70 --- /dev/null +++ b/atlantik/libatlantic/trade.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include "trade.h" +#include "trade.moc" +#include "player.h" +#include "estate.h" + +Trade::Trade(int tradeId) +{ + m_tradeId = tradeId; + m_revision = 0; + m_changed = m_rejected = false; +} + +void Trade::setRevision(int revision) +{ + m_revision = revision; +} + +int Trade::revision() const +{ + return m_revision; +} + +void Trade::addPlayer(Player *player) +{ + m_playerAcceptMap[player] = false; +} + +void Trade::removePlayer(Player *player) +{ + m_playerAcceptMap[player] = false; +} + +unsigned int Trade::count( bool acceptOnly ) +{ + unsigned int count = 0; + for (QMapIterator it = m_playerAcceptMap.begin() ; it != m_playerAcceptMap.end() ; ++it) + if ( !acceptOnly || it.data() ) + count++; + return count; +} + +void Trade::updateEstate(Estate *estate, Player *to) +{ + TradeEstate *t=0; + + for (QPtrListIterator i(mTradeItems); *i; ++i) + { + t=dynamic_cast(*i); + + if (!t) + continue; + + if (t->estate()==estate) + break; + + t=0; + } + if (t) + { + if (to) + { + if (t->to() == to) + return; + t->setTo(to); + } + else + { + mTradeItems.removeRef(t); + emit itemRemoved(t); + t->deleteLater(); + } + } + else if (estate && to) + { + // new trade + t = new TradeEstate(estate, this, to); + + mTradeItems.append(t); + emit itemAdded(t); + } +} + +void Trade::updateMoney(unsigned int money, Player *from, Player *to) +{ + TradeMoney *t=0; + + for (QPtrListIterator i(mTradeItems); *i; ++i) + { + t=dynamic_cast(*i); + + if (!t) + continue; + + if (t->from() == from && t->to() == to && t->money()) + break; + + t=0; + } + if (t) + { + if (from && to && money) + { + if (t->money() == money) + return; + t->setMoney(money); + } + else + { + mTradeItems.removeRef(t); + emit itemRemoved(t); + t->deleteLater(); + } + } + else if (from && to && money) + { + // new trade + t = new TradeMoney(money, this, from, to); + + mTradeItems.append(t); + emit itemAdded(t); + } +} + +void Trade::updateAccept(Player *player, bool accept) +{ + if (m_playerAcceptMap[player] != accept) + { + m_playerAcceptMap[player] = accept; + m_changed = true; + } +} + +void Trade::reject(Player *player) +{ + m_rejected = true; + emit rejected(player); +} + +void Trade::update(bool force) +{ + if (m_changed || force) + { + emit changed(this); + m_changed = false; + } +} + +TradeItem::TradeItem(Trade *trade, Player *from, Player *to) : mFrom(from), mTo(to), mTrade(trade) +{ + connect(from, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); + connect(to, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); +} + +void TradeItem::playerChanged() +{ + emit changed(this); +} + +TradeEstate::TradeEstate(Estate *estate, Trade *trade, Player *to) : TradeItem(trade, estate->owner(), to), mEstate(estate) +{ +} + +QString TradeEstate::text() const +{ + return mEstate->name(); +} + +TradeMoney::TradeMoney(unsigned int money, Trade *trade, Player *from, Player *to) : TradeItem(trade, from, to), m_money(money) +{ +} + +void TradeMoney::setMoney(unsigned int money) +{ + if (m_money != money) + { + m_money = money; + emit changed(this); + } +} + +QString TradeMoney::text() const +{ + return QString("$%1").arg(m_money); +} diff --git a/atlantik/libatlantic/trade.h b/atlantik/libatlantic/trade.h new file mode 100644 index 00000000..5d8f3c01 --- /dev/null +++ b/atlantik/libatlantic/trade.h @@ -0,0 +1,152 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIC_TRADE_H +#define LIBATLANTIC_TRADE_H + +#include +#include +#include + +#include "libatlantic_export.h" +#include "player.h" + +class Player; +class Trade; +class Estate; + +class LIBATLANTIC_EXPORT TradeItem : public QObject +{ +Q_OBJECT + +public: + TradeItem(Trade *trade, Player *from, Player *to); + virtual ~TradeItem() { } + + Player *from() { return mFrom; } + Player *to() { return mTo; } + void setTo(Player *p) { mTo=p; } + Trade *trade() { return mTrade; } + + /** + * how to visualize this + **/ + virtual QString text() const=0; + +signals: + void changed(TradeItem *); + +private slots: + void playerChanged(); + +private: + Player *mFrom, *mTo; + Trade *mTrade; +}; + +class LIBATLANTIC_EXPORT TradeEstate : public TradeItem +{ +Q_OBJECT + +public: + TradeEstate(Estate *estate, Trade *trade, Player *to); + + Estate *estate() { return mEstate; } + + virtual QString text() const; + +signals: + void updateEstate(Trade *trade, Estate *estate, Player *player); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + +private: + Estate *mEstate; +}; + +class LIBATLANTIC_EXPORT TradeMoney : public TradeItem +{ +Q_OBJECT + +public: + TradeMoney(unsigned int money, Trade *trade, Player *from, Player *to); + + unsigned int money() const { return m_money; } + void setMoney(unsigned int money); + + virtual QString text() const; + +signals: + void changed(TradeItem *tradeItem); + +private: + unsigned int m_money; +}; + + +class LIBATLANTIC_EXPORT Trade : public QObject +{ +Q_OBJECT + +public: + Trade(int tradeId); + int tradeId() { return m_tradeId; } + + void setRevision(int revision); + int revision() const; + + void addPlayer(Player *player); + void removePlayer(Player *player); + + unsigned int count( bool acceptOnly ); + + bool isRejected() { return m_rejected; } + +private slots: + /** + * tell someone that this changed + **/ +// void changed(TradeItem *i) { emit itemChanged(i); } + +public: + void update(bool force = false); + void updateEstate(Estate *estate, Player *player); + void updateMoney(unsigned int money, Player *from, Player *to); + void updateAccept(Player *player, bool accept); + void reject(Player *player); + +signals: + void changed(Trade *); + void rejected(Player *player); + + void itemAdded(TradeItem *); + void itemRemoved(TradeItem *); + + void updateEstate(Trade *trade, Estate *estate, Player *to); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + void reject(Trade *trade); + void accept(Trade *trade); + +private: + bool m_changed, m_rejected; + int m_tradeId, m_revision; + + QPtrList mPlayers; + QMap m_playerAcceptMap; + + QPtrList mTradeItems; +}; + +#endif diff --git a/atlantik/libatlantikclient/Makefile.am b/atlantik/libatlantikclient/Makefile.am new file mode 100644 index 00000000..92c79eb2 --- /dev/null +++ b/atlantik/libatlantikclient/Makefile.am @@ -0,0 +1,10 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +lib_LTLIBRARIES = libatlantikclient.la +libatlantikclient_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantikclient_la_LIBADD = ../libatlantic/libatlantic.la $(LIB_KIO) + +libatlantikclient_la_SOURCES = atlantik_network.cpp monopdprotocol.cpp + +noinst_HEADERS = atlantik_network.h monopdprotocol.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantikclient/atlantik_network.cpp b/atlantik/libatlantikclient/atlantik_network.cpp new file mode 100644 index 00000000..7b1926d3 --- /dev/null +++ b/atlantik/libatlantikclient/atlantik_network.cpp @@ -0,0 +1,928 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "atlantik_network.h" + +AtlantikNetwork::AtlantikNetwork(AtlanticCore *atlanticCore) : KExtendedSocket(0, 0, KExtendedSocket::inputBufferedSocket) +{ + m_atlanticCore = atlanticCore; + m_textStream = new QTextStream(this); + m_textStream->setCodec(QTextCodec::codecForName("utf8")); + m_playerId = -1; + m_serverVersion = ""; + + QObject::connect(this, SIGNAL(readyRead()), this, SLOT(slotRead())); + QObject::connect(this, SIGNAL(lookupFinished(int)), + this, SLOT(slotLookupFinished(int))); + QObject::connect(this, SIGNAL(connectionSuccess()), + this, SLOT(slotConnectionSuccess())); + QObject::connect(this, SIGNAL(connectionFailed(int)), + this, SLOT(slotConnectionFailed(int))); +} + +AtlantikNetwork::~AtlantikNetwork(void) +{ + delete m_textStream; +} + +void AtlantikNetwork::rollDice() +{ + writeData(".r"); +} + +void AtlantikNetwork::buyEstate() +{ + writeData(".eb"); +} + +void AtlantikNetwork::auctionEstate() +{ + writeData(".ea"); +} + +void AtlantikNetwork::startGame() +{ + writeData(".gs"); +} + +void AtlantikNetwork::reconnect(const QString &cookie) +{ + writeData(".R" + cookie); +} + +void AtlantikNetwork::leaveGame() +{ + writeData(".gx"); +} + +void AtlantikNetwork::endTurn() +{ + writeData(".E"); +} + +void AtlantikNetwork::setName(QString name) +{ + // Almost deprecated, will be replaced by libmonopdprotocol + writeData(QString(".n%1").arg(name)); +} + +void AtlantikNetwork::tokenConfirmation(Estate *estate) +{ + writeData(QString(".t%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateToggleMortgage(Estate *estate) +{ + writeData(QString(".em%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateHouseBuy(Estate *estate) +{ + writeData(QString(".hb%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::estateHouseSell(Estate *estate) +{ + writeData(QString(".hs%1").arg(estate ? estate->id() : -1)); +} + +void AtlantikNetwork::newGame(const QString &gameType) +{ + writeData(QString(".gn%1").arg(gameType)); +} + +void AtlantikNetwork::joinGame(int gameId) +{ + writeData(QString(".gj%1").arg(gameId)); +} + +void AtlantikNetwork::cmdChat(QString msg) +{ + writeData(msg); +} + +void AtlantikNetwork::newTrade(Player *player) +{ + writeData(QString(".Tn%1").arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::kickPlayer(Player *player) +{ + writeData(QString(".gk%1").arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::tradeUpdateEstate(Trade *trade, Estate *estate, Player *player) +{ + writeData(QString(".Te%1:%2:%3").arg(trade ? trade->tradeId() : -1).arg(estate ? estate->id() : -1).arg(player ? player->id() : -1)); +} + +void AtlantikNetwork::tradeUpdateMoney(Trade *trade, unsigned int money, Player *pFrom, Player *pTo) +{ + writeData(QString(".Tm%1:%2:%3:%4").arg(trade ? trade->tradeId() : -1).arg(pFrom ? pFrom->id() : -1).arg(pTo ? pTo->id() : -1).arg(money)); +} + +void AtlantikNetwork::tradeReject(Trade *trade) +{ + writeData(QString(".Tr%1").arg(trade ? trade->tradeId() : -1)); +} + +void AtlantikNetwork::tradeAccept(Trade *trade) +{ + writeData(QString(".Ta%1:%2").arg(trade ? trade->tradeId() : -1).arg(trade ? trade->revision() : -1)); +} + +void AtlantikNetwork::auctionBid(Auction *auction, int amount) +{ + writeData(QString(".ab%1:%2").arg(auction ? auction->auctionId() : -1).arg(amount)); +} + +void AtlantikNetwork::setImage(const QString &name) +{ + writeData(QString(".pi%1").arg(name)); +} + +void AtlantikNetwork::jailPay() +{ + writeData(".jp"); +} + +void AtlantikNetwork::jailRoll() +{ + writeData(".jr"); +} + +void AtlantikNetwork::jailCard() +{ + writeData(".jc"); +} + +void AtlantikNetwork::changeOption(int configId, const QString &value) +{ + writeData( QString(".gc%1:%2").arg(configId).arg(value) ); +} + +void AtlantikNetwork::writeData(QString msg) +{ + emit networkEvent(msg, "1rightarrow"); + msg.append("\n"); + if (socketStatus() == KExtendedSocket::connected) + *m_textStream << msg; + else + kdDebug() << "warning: socket not connected!" << endl; +} + +void AtlantikNetwork::slotRead() +{ + if ( socketStatus() != KExtendedSocket::connected ) + return; + + if (canReadLine()) + { + processMsg(m_textStream->readLine()); + // There might be more data + QTimer::singleShot(0, this, SLOT(slotRead())); + } + else + { + // Maximum message size. Messages won't get bigger than 32k anyway, so + // if we didn't receive a newline by now, we probably won't anyway. + if (bytesAvailable() > (1024 * 32)) + flush(); + } +} + +void AtlantikNetwork::processMsg(const QString &msg) +{ + emit networkEvent(msg, "1leftarrow"); + + QDomDocument dom; + dom.setContent(msg); + QDomElement e = dom.documentElement(); + if (e.tagName() != "monopd") + { + // Invalid data, request full update from server + writeData(".f"); + return; + } + QDomNode n = e.firstChild(); + processNode(n); + m_atlanticCore->printDebug(); +} + +void AtlantikNetwork::processNode(QDomNode n) +{ + QDomAttr a; + + for ( ; !n.isNull() ; n = n.nextSibling() ) + { + QDomElement e = n.toElement(); + if(!e.isNull()) + { + if (e.tagName() == "server") + { + a = e.attributeNode( QString("version") ); + if ( !a.isNull() ) + m_serverVersion = a.value(); + + emit receivedHandshake(); + } + else if (e.tagName() == "msg") + { + a = e.attributeNode(QString("type")); + if (!a.isNull()) + { + if (a.value() == "error") + emit msgError(e.attributeNode(QString("value")).value()); + else if (a.value() == "info") + emit msgInfo(e.attributeNode(QString("value")).value()); + else if (a.value() == "chat") + emit msgChat(e.attributeNode(QString("author")).value(), e.attributeNode(QString("value")).value()); + } + } + else if (e.tagName() == "display") + { + int estateId = -1; + + a = e.attributeNode(QString("estateid")); + if (!a.isNull()) + { + estateId = a.value().toInt(); + Estate *estate; + estate = m_atlanticCore->findEstate(a.value().toInt()); + + emit displayDetails(e.attributeNode(QString("text")).value(), e.attributeNode(QString("cleartext")).value().toInt(), e.attributeNode(QString("clearbuttons")).value().toInt(), estate); + + bool hasButtons = false; + for( QDomNode nButtons = n.firstChild() ; !nButtons.isNull() ; nButtons = nButtons.nextSibling() ) + { + QDomElement eButton = nButtons.toElement(); + if (!eButton.isNull() && eButton.tagName() == "button") + { + emit addCommandButton(eButton.attributeNode(QString("command")).value(), eButton.attributeNode(QString("caption")).value(), eButton.attributeNode(QString("enabled")).value().toInt()); + hasButtons = true; + } + } + + if (!hasButtons) + emit addCloseButton(); + } + } + else if (e.tagName() == "client") + { + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + m_playerId = a.value().toInt(); + + a = e.attributeNode(QString("cookie")); + if (!a.isNull()) + emit clientCookie(a.value()); + } + else if (e.tagName() == "configupdate") + { + int configId = -1; + a = e.attributeNode(QString("configid")); + if (!a.isNull()) + { + configId = a.value().toInt(); + ConfigOption *configOption; + if (!(configOption = m_atlanticCore->findConfigOption(configId))) + configOption = m_atlanticCore->newConfigOption( configId ); + + a = e.attributeNode(QString("name")); + if (configOption && !a.isNull()) + configOption->setName(a.value()); + + a = e.attributeNode(QString("description")); + if (configOption && !a.isNull()) + configOption->setDescription(a.value()); + + a = e.attributeNode(QString("edit")); + if (configOption && !a.isNull()) + configOption->setEdit(a.value().toInt()); + + a = e.attributeNode(QString("value")); + if (configOption && !a.isNull()) + configOption->setValue(a.value()); + + if (configOption) + configOption->update(); + } + + int gameId = -1; + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + gameId = a.value().toInt(); + for( QDomNode nOptions = n.firstChild() ; !nOptions.isNull() ; nOptions = nOptions.nextSibling() ) + { + QDomElement eOption = nOptions.toElement(); + if (!eOption.isNull() && eOption.tagName() == "option") + emit gameOption(eOption.attributeNode(QString("title")).value(), eOption.attributeNode(QString("type")).value(), eOption.attributeNode(QString("value")).value(), eOption.attributeNode(QString("edit")).value(), eOption.attributeNode(QString("command")).value()); + } + emit endConfigUpdate(); + } + } + else if (e.tagName() == "deletegame") + { + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + int gameId = a.value().toInt(); + + Game *game = m_atlanticCore->findGame(gameId); + if (game) + m_atlanticCore->removeGame(game); + } + } + else if (e.tagName() == "gameupdate") + { + int gameId = -1; + + a = e.attributeNode(QString("gameid")); + if (!a.isNull()) + { + gameId = a.value().toInt(); + + Player *playerSelf = m_atlanticCore->playerSelf(); + if ( playerSelf && playerSelf->game() ) + kdDebug() << "gameupdate for " << QString::number(gameId) << " with playerSelf in game " << QString::number(playerSelf->game()->id()) << endl; + else + kdDebug() << "gameupdate for " << QString::number(gameId) << endl; + + + Game *game = 0; + if (gameId == -1) + { + a = e.attributeNode(QString("gametype")); + if ( !a.isNull() && !(game = m_atlanticCore->findGame(a.value())) ) + game = m_atlanticCore->newGame(gameId, a.value()); + } + else if (!(game = m_atlanticCore->findGame(gameId))) + game = m_atlanticCore->newGame(gameId); + + a = e.attributeNode(QString("canbejoined")); + if (game && !a.isNull()) + game->setCanBeJoined(a.value().toInt()); + + a = e.attributeNode(QString("description")); + if (game && !a.isNull()) + game->setDescription(a.value()); + + a = e.attributeNode(QString("name")); + if (game && !a.isNull()) + game->setName(a.value()); + + a = e.attributeNode(QString("players")); + if (game && !a.isNull()) + game->setPlayers(a.value().toInt()); + + a = e.attributeNode(QString("master")); + if (game && !a.isNull()) + { + // Ensure setMaster succeeds by creating player if necessary + Player *player = m_atlanticCore->findPlayer( a.value().toInt() ); + if ( !player ) + player = m_atlanticCore->newPlayer( a.value().toInt() ); + game->setMaster( player ); + } + + QString status = e.attributeNode(QString("status")).value(); + if ( m_serverVersion.left(4) == "0.9." || (playerSelf && playerSelf->game() == game) ) + { + if (status == "config") + emit gameConfig(); + else if (status == "init") + emit gameInit(); + else if (status == "run") + emit gameRun(); + else if (status == "end") + emit gameEnd(); + } + + if (game) + game->update(); + } + } + else if (e.tagName() == "deleteplayer") + { + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + { + int playerId = a.value().toInt(); + + Player *player = m_atlanticCore->findPlayer(playerId); + if (player) + m_atlanticCore->removePlayer(player); + } + } + else if (e.tagName() == "playerupdate") + { + int playerId = -1; + + a = e.attributeNode(QString("playerid")); + if (!a.isNull()) + { + playerId = a.value().toInt(); + + Player *player; + if (!(player = m_atlanticCore->findPlayer(playerId))) + player = m_atlanticCore->newPlayer( playerId, (m_playerId == playerId) ); + + // Update player name + a = e.attributeNode(QString("name")); + if (player && !a.isNull()) + player->setName(a.value()); + + // Update player game + a = e.attributeNode(QString("game")); + if (player && !a.isNull()) + { + int gameId = a.value().toInt(); + if (gameId == -1) + player->setGame( 0 ); + else + { + // Ensure setGame succeeds by creating game if necessary + Game *game = m_atlanticCore->findGame(a.value().toInt()); + if (!game) + game = m_atlanticCore->newGame(a.value().toInt()); // + player->setGame( game ); + } + } + + // Update player host + a = e.attributeNode(QString("host")); + if (player && !a.isNull()) + player->setHost(a.value()); + + // Update player image/token + a = e.attributeNode(QString("image")); + if (player && !a.isNull()) + player->setImage(a.value()); + + // Update player money + a = e.attributeNode(QString("money")); + if (player && !a.isNull()) + player->setMoney(a.value().toInt()); + + a = e.attributeNode(QString("bankrupt")); + if (player && !a.isNull()) + player->setBankrupt(a.value().toInt()); + + a = e.attributeNode(QString("hasdebt")); + if (player && !a.isNull()) + player->setHasDebt(a.value().toInt()); + + a = e.attributeNode(QString("hasturn")); + if (player && !a.isNull()) + player->setHasTurn(a.value().toInt()); + + // Update whether player can roll + a = e.attributeNode(QString("can_roll")); + if (player && !a.isNull()) + player->setCanRoll(a.value().toInt()); + + // Update whether player can buy + a = e.attributeNode(QString("can_buyestate")); + if (player && !a.isNull()) + player->setCanBuy(a.value().toInt()); + + // Update whether player can auction + a = e.attributeNode(QString("canauction")); + if (player && !a.isNull()) + player->setCanAuction(a.value().toInt()); + + // Update whether player can use a card + a = e.attributeNode(QString("canusecard")); + if (player && !a.isNull()) + player->setCanUseCard(a.value().toInt()); + + // Update whether player is jailed + a = e.attributeNode(QString("jailed")); + if (player && !a.isNull()) + { + player->setInJail(a.value().toInt()); + // TODO: emit signal with player ptr so board can setText and display something + } + + // Update player location + a = e.attributeNode(QString("location")); + if (!a.isNull()) + { + m_playerLocationMap[player] = a.value().toInt(); + + bool directMove = false; + + Estate *estate = m_atlanticCore->findEstate(a.value().toInt()); + + a = e.attributeNode(QString("directmove")); + if (!a.isNull()) + directMove = a.value().toInt(); + + if (player && estate) + { + if (directMove) + player->setLocation(estate); + else + player->setDestination(estate); + } + } + + if (player) + player->update(); + } + } + else if (e.tagName() == "estategroupupdate") + { + a = e.attributeNode(QString("groupid")); + if (!a.isNull()) + { + int groupId = a.value().toInt(); + + EstateGroup *estateGroup = 0; + bool b_newEstateGroup = false; + + if (!(estateGroup = m_atlanticCore->findEstateGroup(groupId))) + { + // Create EstateGroup object + estateGroup = m_atlanticCore->newEstateGroup(a.value().toInt()); + b_newEstateGroup = true; + } + + a = e.attributeNode(QString("name")); + if (estateGroup && !a.isNull()) + estateGroup->setName(a.value()); + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (estateGroup) + { + if (b_newEstateGroup) + emit newEstateGroup(estateGroup); + estateGroup->update(); + } + } + } + else if (e.tagName() == "estateupdate") + { + int estateId = -1; + + a = e.attributeNode(QString("estateid")); + if (!a.isNull()) + { + estateId = a.value().toInt(); + + Estate *estate = 0; + bool b_newEstate = false; + + // FIXME: allow any estateId, GUI should not use it to determin its geometry + if (estateId >= 0 && estateId < 100 && !(estate = m_atlanticCore->findEstate(a.value().toInt()))) + { + // Create estate object + estate = m_atlanticCore->newEstate(estateId); + b_newEstate = true; + + QObject::connect(estate, SIGNAL(estateToggleMortgage(Estate *)), this, SLOT(estateToggleMortgage(Estate *))); + QObject::connect(estate, SIGNAL(estateHouseBuy(Estate *)), this, SLOT(estateHouseBuy(Estate *))); + QObject::connect(estate, SIGNAL(estateHouseSell(Estate *)), this, SLOT(estateHouseSell(Estate *))); + QObject::connect(estate, SIGNAL(newTrade(Player *)), this, SLOT(newTrade(Player *))); + + // Players without estate should get one + Player *player = 0; + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); (player = *it) ; ++it) + if (m_playerLocationMap[player] == estate->id()) + player->setLocation(estate); + } + + a = e.attributeNode(QString("name")); + if (estate && !a.isNull()) + estate->setName(a.value()); + + a = e.attributeNode(QString("color")); + if (estate && !a.isNull() && !a.value().isEmpty()) + estate->setColor(a.value()); + + a = e.attributeNode(QString("bgcolor")); + if (estate && !a.isNull()) + estate->setBgColor(a.value()); + + a = e.attributeNode(QString("owner")); + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + if (estate && !a.isNull()) + estate->setOwner(player); + + a = e.attributeNode(QString("houses")); + if (estate && !a.isNull()) + estate->setHouses(a.value().toInt()); + + a = e.attributeNode(QString("mortgaged")); + if (estate && !a.isNull()) + estate->setIsMortgaged(a.value().toInt()); + + a = e.attributeNode(QString("group")); + if (!a.isNull()) + { + EstateGroup *estateGroup = m_atlanticCore->findEstateGroup(a.value().toInt()); + if (estate) + estate->setEstateGroup(estateGroup); + } + + a = e.attributeNode(QString("can_toggle_mortgage")); + if (estate && !a.isNull()) + estate->setCanToggleMortgage(a.value().toInt()); + + a = e.attributeNode(QString("can_be_owned")); + if (estate && !a.isNull()) + estate->setCanBeOwned(a.value().toInt()); + + a = e.attributeNode(QString("can_buy_houses")); + if (estate && !a.isNull()) + estate->setCanBuyHouses(a.value().toInt()); + + a = e.attributeNode(QString("can_sell_houses")); + if (estate && !a.isNull()) + estate->setCanSellHouses(a.value().toInt()); + + a = e.attributeNode(QString("price")); + if (estate && !a.isNull()) + estate->setPrice(a.value().toInt()); + + a = e.attributeNode(QString("houseprice")); + if (estate && !a.isNull()) + estate->setHousePrice(a.value().toInt()); + + a = e.attributeNode(QString("sellhouseprice")); + if (estate && !a.isNull()) + estate->setHouseSellPrice(a.value().toInt()); + + a = e.attributeNode(QString("mortgageprice")); + if (estate && !a.isNull()) + estate->setMortgagePrice(a.value().toInt()); + + a = e.attributeNode(QString("unmortgageprice")); + if (estate && !a.isNull()) + estate->setUnmortgagePrice(a.value().toInt()); + + a = e.attributeNode(QString("money")); + if (estate && !a.isNull()) + estate->setMoney(a.value().toInt()); + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (estate) + { + if (b_newEstate) + emit newEstate(estate); + estate->update(); + } + } + } + else if (e.tagName() == "tradeupdate") + { + a = e.attributeNode(QString("tradeid")); + if (!a.isNull()) + { + int tradeId = a.value().toInt(); + + Trade *trade = m_atlanticCore->findTrade(tradeId); + if (!trade) + { + // Create trade object + trade = m_atlanticCore->newTrade(tradeId); + + QObject::connect(trade, SIGNAL(updateEstate(Trade *, Estate *, Player *)), this, SLOT(tradeUpdateEstate(Trade *, Estate *, Player *))); + QObject::connect(trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)), this, SLOT(tradeUpdateMoney(Trade *, unsigned int, Player *, Player *))); + QObject::connect(trade, SIGNAL(reject(Trade *)), this, SLOT(tradeReject(Trade *))); + QObject::connect(trade, SIGNAL(accept(Trade *)), this, SLOT(tradeAccept(Trade *))); + } + + a = e.attributeNode(QString("revision")); + if (trade && !a.isNull()) + trade->setRevision(a.value().toInt()); + + QString type = e.attributeNode(QString("type")).value(); + if (type=="new") + { + // TODO: trade->setActor + // Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("actor")).value().toInt()); + // if (trade && player) + // trade->setActor(player); + + QDomNode n_player = n.firstChild(); + while(!n_player.isNull()) + { + QDomElement e_player = n_player.toElement(); + if (!e_player.isNull() && e_player.tagName() == "tradeplayer") + { + Player *player = m_atlanticCore->findPlayer(e_player.attributeNode(QString("playerid")).value().toInt()); + if (trade && player) + { + trade->addPlayer(player); + QObject::connect(m_atlanticCore, SIGNAL(removePlayer(Player *)), trade, SLOT(removePlayer(Player *))); + } + } + n_player = n_player.nextSibling(); + } + } + else if (type=="accepted" && trade) + emit msgTradeUpdateAccepted(trade); + else if (type=="completed" && trade) + { + m_atlanticCore->removeTrade(trade); + trade = 0; + } + else if (type=="rejected") + { + Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("actor")).value().toInt()); + if (trade) + trade->reject(player); + if ( player && player == m_atlanticCore->playerSelf() ) + { + m_atlanticCore->removeTrade(trade); + trade = 0; + } + } + else + { + // No type specified, edit is implied. + + QDomNode n_child = n.firstChild(); + while(!n_child.isNull()) + { + QDomElement e_child = n_child.toElement(); + if (!e_child.isNull()) + { + if (e_child.tagName() == "tradeplayer") + { + a = e_child.attributeNode(QString("playerid")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("accept")); + if (trade && player && !a.isNull()) + trade->updateAccept(player, (bool)(a.value().toInt())); + } + } + else if (e_child.tagName() == "tradeestate") + { + a = e_child.attributeNode(QString("estateid")); + if (!a.isNull()) + { + Estate *estate = m_atlanticCore->findEstate(a.value().toInt()); + a = e_child.attributeNode(QString("targetplayer")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(a.value().toInt()); + // Allow NULL player, it will remove the component + if (trade && estate) + trade->updateEstate(estate, player); + } + } + } + else if (e_child.tagName() == "trademoney") + { + Player *pFrom = 0, *pTo = 0; + + a = e_child.attributeNode(QString("playerfrom")); + if (!a.isNull()) + pFrom = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("playerto")); + if (!a.isNull()) + pTo = m_atlanticCore->findPlayer(a.value().toInt()); + + a = e_child.attributeNode(QString("money")); + kdDebug() << "tradeupdatemoney" << (pFrom ? "1" : "0") << (pTo ? "1" : "0") << (a.isNull() ? "0" : "1") << endl; + if (trade && pFrom && pTo && !a.isNull()) + trade->updateMoney(a.value().toInt(), pFrom, pTo); + } + } + n_child = n_child.nextSibling(); + } + } + + if (trade) + trade->update(); + } + } + else if (e.tagName() == "auctionupdate") + { + a = e.attributeNode(QString("auctionid")); + if (!a.isNull()) + { + int auctionId = a.value().toInt(); + + Auction *auction; + bool b_newAuction = false; + if (!(auction = m_auctions[auctionId])) + { + // Create auction object + auction = m_atlanticCore->newAuction(auctionId, m_atlanticCore->findEstate(e.attributeNode(QString("estateid")).value().toInt())); + m_auctions[auctionId] = auction; + + QObject::connect(auction, SIGNAL(bid(Auction *, int)), this, SLOT(auctionBid(Auction *, int))); + + b_newAuction = true; + } + + a = e.attributeNode(QString("highbidder")); + if (!a.isNull()) + { + Player *player = m_atlanticCore->findPlayer(e.attributeNode(QString("highbidder")).value().toInt()); + a = e.attributeNode(QString("highbid")); + if (auction && !a.isNull()) + auction->newBid(player, a.value().toInt()); + } + + a = e.attributeNode(QString("status")); + if (auction && !a.isNull()) + { + int status = a.value().toInt(); + auction->setStatus(status); + + // TODO: find a good way to visualise "sold!" + if (status == 3) + { + m_atlanticCore->delAuction(auction); + m_auctions[auctionId] = 0; + auction = 0; + } + } + + // Emit signal so GUI implementations can create view(s) + // TODO: port to atlanticcore and create view there + if (auction) + { + if (b_newAuction) + emit newAuction(auction); + auction->update(); + } + } + } + else + kdDebug() << "ignored TAG: " << e.tagName() << endl; + } + // TODO: remove permanently? + // QDomNode node = n.firstChild(); + // processNode(node); + } +} + +void AtlantikNetwork::serverConnect(const QString host, int port) +{ + setAddress(host, port); + enableRead(true); + emit msgStatus(i18n("Connecting to %1:%2...").arg(host).arg(QString::number(port)), "connect_creating"); + startAsyncConnect(); +} + +void AtlantikNetwork::slotLookupFinished(int count) +{ + emit msgStatus(i18n("Server host name lookup finished...")); +} + +void AtlantikNetwork::slotConnectionSuccess() +{ + emit msgStatus(i18n("Connected to %1:%2.").arg(host()).arg(port()), "connect_established"); +} + +void AtlantikNetwork::slotConnectionFailed(int error) +{ + emit msgStatus(i18n("Connection failed! Error code: %1").arg(error), "connect_no"); +} + +#include "atlantik_network.moc" diff --git a/atlantik/libatlantikclient/atlantik_network.h b/atlantik/libatlantikclient/atlantik_network.h new file mode 100644 index 00000000..087a01be --- /dev/null +++ b/atlantik/libatlantikclient/atlantik_network.h @@ -0,0 +1,155 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIK_NETWORK_H +#define LIBATLANTIK_NETWORK_H + +#include + +#include +#include "libatlantic_export.h" +class QDomNode; +class QTextStream; + +class AtlanticCore; + +class Player; +class Estate; +class EstateGroup; +class Trade; +class Auction; + +class LIBATLANTIC_EXPORT AtlantikNetwork : public KExtendedSocket +{ +Q_OBJECT + +public: + AtlantikNetwork(AtlanticCore *atlanticCore); + virtual ~AtlantikNetwork(void); + void setName(QString name); + void cmdChat(QString msg); + +private slots: + void writeData(QString msg); + void rollDice(); + void endTurn(); + void newGame(const QString &gameType); + void reconnect(const QString &cookie); + void startGame(); + void buyEstate(); + void auctionEstate(); + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void jailCard(); + void jailPay(); + void jailRoll(); + void newTrade(Player *player); + void kickPlayer(Player *player); + void tokenConfirmation(Estate *); + void tradeUpdateEstate(Trade *trade, Estate *estate, Player *player); + void tradeUpdateMoney(Trade *trade, unsigned int money, Player *pFrom, Player *pTo); + void tradeReject(Trade *trade); + void tradeAccept(Trade *trade); + void auctionBid(Auction *auction, int amount); + void changeOption(int, const QString &value); + void slotLookupFinished(int count); + void slotConnectionSuccess(); + void slotConnectionFailed(int error); + +public slots: + void serverConnect(const QString host, int port); + void joinGame(int gameId); + void leaveGame(); + void slotRead(); + void setImage(const QString &name); + +signals: + /** + * A new estate was created. This signal might be replaced with one in + * the AtlanticCore class in the future, but it is here now because we + * do not want GUI implementations to create a view until the + * estateupdate message has been fully parsed. + * + * @param estate Created Estate object. + */ + void newEstate(Estate *estate); + + /** + * A new estate group was created. This signal might be replaced with + * one in the AtlanticCore class in the future, but it is here now + * because we do not want GUI implementations to create a view until the + * estategroupupdate message has been fully parsed. + * + * @param estateGroup Created EstateGroup object. + */ + void newEstateGroup(EstateGroup *estateGroup); + + void msgInfo(QString); + void msgError(QString); + void msgChat(QString, QString); + void msgStatus(const QString &data, const QString &icon = QString::null); + void networkEvent(const QString &data, const QString &icon); + + void displayDetails(QString text, bool clearText, bool clearButtons, Estate *estate = 0); + void addCommandButton(QString command, QString caption, bool enabled); + void addCloseButton(); + + void gameOption(QString title, QString type, QString value, QString edit, QString command); + void endConfigUpdate(); + + void gameConfig(); + void gameInit(); + void gameRun(); + void gameEnd(); + + /** + * The trade has been completed. Emitted after all necessary estate and + * player updates are processed. + * + * @param trade Trade + */ + void msgTradeUpdateAccepted(Trade *trade); + + /** + * One of the players rejected the trade and the trade object has been + * deleted from the server. + * + * @param trade Trade + * @param playerId Unique player identifier of rejecting player + */ + void msgTradeUpdateRejected(Trade *trade, int playerId); + + void newAuction(Auction *auction); + void auctionCompleted(Auction *auction); + void receivedHandshake(); + void clientCookie(QString cookie); + +private: + void processMsg(const QString &msg); + void processNode(QDomNode); + + AtlanticCore *m_atlanticCore; + QTextStream *m_textStream; + + int m_playerId; + QString m_serverVersion; + + QMap m_playerLocationMap; + QMap m_auctions; +}; + +#endif diff --git a/atlantik/libatlantikclient/monopdprotocol.cpp b/atlantik/libatlantikclient/monopdprotocol.cpp new file mode 100644 index 00000000..5f6c401b --- /dev/null +++ b/atlantik/libatlantikclient/monopdprotocol.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +/* +#include +#include +#include +#include +#include +#include +*/ + +#include + +#include "monopdprotocol.h" +#include "monopdprotocol.moc" + +MonopdProtocol::MonopdProtocol() : QObject() +{ +} + +void MonopdProtocol::auctionEstate() +{ + sendData(QString::fromLatin1(".ea")); +} + +void MonopdProtocol::buyEstate() +{ + sendData(QString::fromLatin1(".eb")); +} + +void MonopdProtocol::confirmTokenLocation(Estate *estate) +{ + QString data(".t"); + data.append(QString::number(estate ? estate->id() : -1)); + sendData(data); +} + +void MonopdProtocol::endTurn() +{ + sendData(QString::fromLatin1(".E")); +} + +void MonopdProtocol::rollDice() +{ + sendData(QString::fromLatin1(".r")); +} + +void MonopdProtocol::setName(QString name) +{ + QString data(".n"); + data.append(name); + sendData(data); +} + +void MonopdProtocol::startGame() +{ + sendData(QString::fromLatin1(".gs")); +} + +void MonopdProtocol::sendData(QString) +{ + // Your reimplementation of this method should send send data over the + // network. +} diff --git a/atlantik/libatlantikclient/monopdprotocol.h b/atlantik/libatlantikclient/monopdprotocol.h new file mode 100644 index 00000000..0fc16ad8 --- /dev/null +++ b/atlantik/libatlantikclient/monopdprotocol.h @@ -0,0 +1,58 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +// WARNING: this codebase is not being used yet. Please use AtlantikNetwork +// until the protocol seperation has been completed. + +#ifndef MONOPDPROTOCOL_H_H +#define MONOPDPROTOCOL_H_H + +#include + +class QString; + +/* +class AtlanticCore; + +class Player; +class EstateGroup; +class Trade; +class Auction; +*/ + +class Estate; + +class MonopdProtocol : public QObject +{ +Q_OBJECT + +public: + MonopdProtocol(); + +private slots: + void auctionEstate(); + void buyEstate(); + void confirmTokenLocation(Estate *estate); + void endTurn(); + void rollDice(); + void setName(QString name); + void startGame(); + +private: + virtual void sendData(QString data); +}; + +#endif diff --git a/atlantik/libatlantikui/Makefile.am b/atlantik/libatlantikui/Makefile.am new file mode 100644 index 00000000..2e3bbbed --- /dev/null +++ b/atlantik/libatlantikui/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_srcdir)/atlantik/libatlantic $(all_includes) +lib_LTLIBRARIES = libatlantikui.la +libatlantikui_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libatlantikui_la_LIBADD = ../libatlantic/libatlantic.la $(LIB_KIO) + +libatlantikui_la_SOURCES = auction_widget.cpp board.cpp estatedetails.cpp \ + estateview.cpp kwrappedlistviewitem.cpp portfolioestate.cpp \ + portfolioview.cpp token.cpp trade_widget.cpp + +libatlantikuiincludedir = $(includedir)/atlantik/ui +libatlantikuiinclude_HEADERS = auction_widget.h board.h estatedetails.h \ + estateview.h kwrappedlistviewitem.h portfolioestate.h \ + portfolioview.h token.h trade_widget.h libatlantikui_export.h + +METASOURCES = AUTO diff --git a/atlantik/libatlantikui/auction_widget.cpp b/atlantik/libatlantikui/auction_widget.cpp new file mode 100644 index 00000000..e7dc7fd8 --- /dev/null +++ b/atlantik/libatlantikui/auction_widget.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "auction_widget.moc" + +AuctionWidget::AuctionWidget(AtlanticCore *atlanticCore, Auction *auction, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + + m_auction = auction; + connect(m_auction, SIGNAL(changed()), this, SLOT(auctionChanged())); + connect(m_auction, SIGNAL(updateBid(Player *, int)), this, SLOT(updateBid(Player *, int))); + connect(this, SIGNAL(bid(Auction *, int)), m_auction, SIGNAL(bid(Auction *, int))); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint()); + Q_CHECK_PTR(m_mainLayout); + + // Player list + Estate *estate = auction->estate(); + m_playerGroupBox = new QVGroupBox(estate ? i18n("Auction: %1").arg(estate->name()) : i18n("Auction"), this, "groupBox"); + m_mainLayout->addWidget(m_playerGroupBox); + + m_playerList = new KListView(m_playerGroupBox); + m_playerList->addColumn(i18n("Player")); + m_playerList->addColumn(i18n("Bid")); + m_playerList->setSorting(1, false); + + KListViewItem *item; + Player *player, *pSelf = m_atlanticCore->playerSelf(); + + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); *it; ++it) + { + if ( (player = *it) && player->game() == pSelf->game() ) + { + item = new KListViewItem(m_playerList, player->name(), QString("0")); + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + m_playerItems[player] = item; + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + } + + // Bid spinbox and button + QHBox *bidBox = new QHBox(this); + m_mainLayout->addWidget(bidBox); + + m_bidSpinBox = new QSpinBox(1, 10000, 1, bidBox); + + KPushButton *bidButton = new KPushButton(i18n("Make Bid"), bidBox, "bidButton"); + connect(bidButton, SIGNAL(clicked()), this, SLOT(slotBidButtonClicked())); + + // Status label + m_statusLabel = new QLabel(this, "statusLabel"); + m_mainLayout->addWidget(m_statusLabel); +} + +void AuctionWidget::auctionChanged() +{ + QString status; + switch (m_auction->status()) + { + case 1: + status = i18n("Going once..."); + break; + + case 2: + status = i18n("Going twice..."); + break; + + case 3: + status = i18n("Sold!"); + break; + + default: + status = QString::null; + } + m_statusLabel->setText(status); +} + +void AuctionWidget::playerChanged(Player *player) +{ + if (!player) + return; + + QListViewItem *item; + if (!(item = m_playerItems[player])) + return; + + item->setText(0, player->name()); + m_playerList->triggerUpdate(); +} + +void AuctionWidget::updateBid(Player *player, int amount) +{ + if (!player) + return; + + QListViewItem *item; + if (!(item = m_playerItems[player])) + return; + + item->setText(1, QString::number(amount)); + m_bidSpinBox->setMinValue(amount+1); + m_playerList->triggerUpdate(); +} + +void AuctionWidget::slotBidButtonClicked() +{ + emit bid(m_auction, m_bidSpinBox->value()); +} diff --git a/atlantik/libatlantikui/auction_widget.h b/atlantik/libatlantikui/auction_widget.h new file mode 100644 index 00000000..a87b8fc4 --- /dev/null +++ b/atlantik/libatlantikui/auction_widget.h @@ -0,0 +1,65 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_AUCTION_WIDGET_H +#define ATLANTIK_AUCTION_WIDGET_H + +#include +#include +#include + +#include + +class QVGroupBox; +class QSpinBox; +class QLabel; + +class KListViewItem; + +class AtlanticCore; +class Player; +class Auction; + +class AuctionWidget : public QWidget +{ +Q_OBJECT + +public: + AuctionWidget(AtlanticCore *atlanticCore, Auction *auction, QWidget *parent, const char *name=0); + +private slots: + void auctionChanged(); + void playerChanged(Player *player); + void updateBid(Player *player, int amount); + void slotBidButtonClicked(); + +signals: + void bid(Auction *auction, int amount); + +private: + QVBoxLayout *m_mainLayout; + QVGroupBox *m_playerGroupBox; + QSpinBox *m_bidSpinBox; + QMap m_playerItems; + QLabel *m_statusLabel; + + KListView *m_playerList; + + AtlanticCore *m_atlanticCore; + Auction *m_auction; +}; + +#endif diff --git a/atlantik/libatlantikui/board.cpp b/atlantik/libatlantikui/board.cpp new file mode 100644 index 00000000..a4fdf3ce --- /dev/null +++ b/atlantik/libatlantikui/board.cpp @@ -0,0 +1,601 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "auction_widget.h" +#include "estatedetails.h" +#include "estateview.h" +#include "token.h" + +#include "board.h" +#include "board.moc" + +AtlantikBoard::AtlantikBoard(AtlanticCore *atlanticCore, int maxEstates, DisplayMode mode, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = atlanticCore; + m_maxEstates = maxEstates; + m_mode = mode; + m_animateTokens = false; + m_lastServerDisplay = 0; + + setMinimumSize(QSize(500, 500)); + + int sideLen = maxEstates/4; + + // Animated token movement + m_movingToken = 0; + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), this, SLOT(slotMoveToken())); + m_resumeTimer = false; + + m_gridLayout = new QGridLayout(this, sideLen+1, sideLen+1); + for(int i=0;i<=sideLen;i++) + { + if (i==0 || i==sideLen) + { + m_gridLayout->setRowStretch(i, 3); + m_gridLayout->setColStretch(i, 3); + } + else + { + m_gridLayout->setRowStretch(i, 2); + m_gridLayout->setColStretch(i, 2); + } + } + +// spacer = new QWidget(this); +// m_gridLayout->addWidget(spacer, sideLen, sideLen); // SE + + m_displayQueue.setAutoDelete(true); + m_estateViews.setAutoDelete(true); + m_tokens.setAutoDelete(true); + + displayDefault(); +} + +AtlantikBoard::~AtlantikBoard() +{ + reset(); +} + +void AtlantikBoard::reset() +{ + kdDebug() << "AtlantikBoard::reset" << endl; + + m_tokens.clear(); + m_estateViews.clear(); + m_displayQueue.clear(); + m_lastServerDisplay = 0; + m_movingToken = 0; +} + +void AtlantikBoard::setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, bool animateTokens) +{ + if (m_animateTokens != animateTokens) + m_animateTokens = animateTokens; + + // Update EstateViews + EstateView *estateView; + for (QPtrListIterator it(m_estateViews); *it; ++it) + if ((estateView = dynamic_cast(*it))) + estateView->setViewProperties(indicateUnowned, highliteUnowned, darkenMortgaged, quartzEffects); +} + +int AtlantikBoard::heightForWidth(int width) +{ + return width; +} + +EstateView *AtlantikBoard::findEstateView(Estate *estate) +{ + EstateView *estateView; + for (QPtrListIterator i(m_estateViews); *i; ++i) + { + estateView = dynamic_cast(*i); + if (estateView && estateView->estate() == estate) + return estateView; + } + return 0; +} + +void AtlantikBoard::addEstateView(Estate *estate, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects) +{ + QString icon = QString(); + int estateId = estate->id(); + EstateOrientation orientation = North; + int sideLen = m_gridLayout->numRows() - 1; + + if (estateId < sideLen) + orientation = North; + else if (estateId < 2*sideLen) + orientation = East; + else if (estateId < 3*sideLen) + orientation = South; + else //if (estateId < 4*sideLen) + orientation = West; + + EstateView *estateView = new EstateView(estate, orientation, icon, indicateUnowned, highliteUnowned, darkenMortgaged, quartzEffects, this, "estateview"); + m_estateViews.append(estateView); + + connect(estate, SIGNAL(changed()), estateView, SLOT(estateChanged())); + connect(estateView, SIGNAL(estateToggleMortgage(Estate *)), estate, SIGNAL(estateToggleMortgage(Estate *))); + connect(estateView, SIGNAL(LMBClicked(Estate *)), estate, SIGNAL(LMBClicked(Estate *))); + connect(estateView, SIGNAL(estateHouseBuy(Estate *)), estate, SIGNAL(estateHouseBuy(Estate *))); + connect(estateView, SIGNAL(estateHouseSell(Estate *)), estate, SIGNAL(estateHouseSell(Estate *))); + connect(estateView, SIGNAL(newTrade(Player *)), estate, SIGNAL(newTrade(Player *))); + + // Designer has its own LMBClicked slot + if (m_mode == Play) + connect(estateView, SIGNAL(LMBClicked(Estate *)), this, SLOT(prependEstateDetails(Estate *))); + + if (estateIdaddWidget(estateView, sideLen, sideLen-estateId); + else if (estateId<2*sideLen) + m_gridLayout->addWidget(estateView, 2*sideLen-estateId, 0); + else if (estateId<3*sideLen) + m_gridLayout->addWidget(estateView, 0, estateId-2*sideLen); + else + m_gridLayout->addWidget(estateView, estateId-3*sideLen, sideLen); + + estateView->show(); + + if (m_atlanticCore) + { + Player *player = 0; + QPtrList playerList = m_atlanticCore->players(); + for (QPtrListIterator it(playerList); (player = *it) ; ++it) + if (player->location() == estate) + addToken(player); + } +} + +void AtlantikBoard::addAuctionWidget(Auction *auction) +{ + AuctionWidget *auctionW = new AuctionWidget(m_atlanticCore, auction, this); + m_lastServerDisplay = auctionW; + m_displayQueue.prepend(auctionW); + updateCenter(); + + connect(auction, SIGNAL(completed()), this, SLOT(displayDefault())); +} + +Token *AtlantikBoard::findToken(Player *player) +{ + Token *token = 0; + for (QPtrListIterator it(m_tokens); (token = *it) ; ++it) + if (token->player() == player) + return token; + return 0; +} + +void AtlantikBoard::addToken(Player *player) +{ + if (!player->location()) + { + kdDebug() << "addToken ignored - estateView null" << endl; + return; + } + + Player *playerSelf = 0; + if (m_atlanticCore) + playerSelf = m_atlanticCore->playerSelf(); + + if (playerSelf && playerSelf->game() != player->game() ) + { + kdDebug() << "addToken ignored - not in same game as playerSelf" << endl; + return; + } + + kdDebug() << "addToken" << endl; + + Token *token = new Token(player, this, "token"); + m_tokens.append(token); + connect(player, SIGNAL(changed(Player *)), token, SLOT(playerChanged())); + + jumpToken(token); + + // Timer to reinit the gameboard _after_ event loop + QTimer::singleShot(100, this, SLOT(slotResizeAftermath())); +} + +void AtlantikBoard::playerChanged(Player *player) +{ + kdDebug() << "playerChanged: playerLoc " << (player->location() ? player->location()->name() : "none") << endl; + + Player *playerSelf = 0; + if (m_atlanticCore) + playerSelf = m_atlanticCore->playerSelf(); + + // Update token + Token *token = findToken(player); + if (token) + { + kdDebug() << "playerChanged: tokenLoc " << (token->location() ? token->location()->name() : "none") << endl; + if (player->isBankrupt() || (playerSelf && playerSelf->game() != player->game()) ) + token->hide(); + if (player->hasTurn()) + token->raise(); + + bool jump = false, move = false; + + if (token->inJail() != player->inJail()) + { + token->setInJail(player->inJail()); + + // If any status such as jail is ever allowed to + // change in the future, during movement, this needs + // to be addressed in moveToken and subsequent steps. + if (token != m_movingToken) + jump = true; + } + + if (token->location() != player->location()) + { + token->setLocation(player->location()); + jump = true; + } + + if (player->destination() && token->destination() != player->destination()) + { + if (m_animateTokens) + { + token->setDestination(player->destination()); + move = true; + } + else + { + token->setLocation(player->destination()); + jump = true; + } + } + + if (move) + moveToken(token); + else if (jump) + jumpToken(token); + } + else + addToken(player); +} + +void AtlantikBoard::removeToken(Player *player) +{ + Token *token = findToken(player); + if (!token) + return; + + if (token == m_movingToken) + { + m_timer->stop(); + m_movingToken = 0; + } + + m_tokens.remove(token); +} + +void AtlantikBoard::jumpToken(Token *token) +{ + if (!token || !token->location()) + return; + + kdDebug() << "jumpToken to " << token->location()->name() << endl; + + QPoint tGeom = calculateTokenDestination(token); + token->setGeometry(tGeom.x(), tGeom.y(), token->width(), token->height()); + + Player *player = token->player(); + if (player) + { + player->setLocation(token->location()); + player->setDestination(0); + + if (token->isHidden() && !player->isBankrupt()) + token->show(); + } + + if (token == m_movingToken) + { + m_timer->stop(); + + if (!m_resumeTimer) + m_movingToken = 0; + } + + emit tokenConfirmation(token->location()); +} + +void AtlantikBoard::moveToken(Token *token) +{ + kdDebug() << "moveToken to " << token->destination()->name() << endl; + + m_movingToken = token; + + // Start timer + m_timer->start(15); +} + +QPoint AtlantikBoard::calculateTokenDestination(Token *token, Estate *eDest) +{ + if (!eDest) + eDest = token->location(); + + EstateView *evDest = findEstateView(eDest); + if (!evDest) + return QPoint(0, 0); + + int x = 0, y = 0; + if (token->player()->inJail()) + { + x = evDest->geometry().right() - token->width() - 2; + y = evDest->geometry().top(); + } + else + { + x = evDest->geometry().center().x() - (token->width()/2); + y = evDest->geometry().center().y() - (token->height()/2); + +/* + // Re-center because of EstateView headers + switch(evDest->orientation()) + { + case North: + y += evDest->height()/8; break; + case East: + x -= evDest->width()/8; break; + case South: + y -= evDest->height()/8; break; + case West: + x += evDest->width()/8; break; + } +*/ + } + return QPoint(x, y); +} + +void AtlantikBoard::slotMoveToken() +{ + // Requires a core with estates to operate on + if (!m_atlanticCore) + { + kdDebug() << "slotMoveToken ignored - no atlanticCore" << endl; + return; + } + + // Do we actually have a token to move? + if (!m_movingToken) + { + m_timer->stop(); + return; + } + + // Where are we? + int xCurrent = m_movingToken->geometry().x(); + int yCurrent = m_movingToken->geometry().y(); + + // Where do we want to go today? + Estate *eDest = m_atlanticCore->estateAfter(m_movingToken->location()); + QPoint tGeom = calculateTokenDestination(m_movingToken, eDest); + + int xDest = tGeom.x(); + int yDest = tGeom.y(); + + if (xDest - xCurrent > 1) + xDest = xCurrent + 2; + else if (xCurrent - xDest > 1) + xDest = xCurrent - 2; + else + xDest = xCurrent; + + if (yDest - yCurrent > 1) + yDest = yCurrent + 2; + else if (yCurrent - yDest > 1) + yDest = yCurrent - 2; + else + yDest = yCurrent; + +// kdDebug() << "TOKEN: at " << xCurrent << "," << yCurrent << " and going to " << xDest << "," << yDest << endl; + + if (xCurrent != xDest || yCurrent != yDest) + { + m_movingToken->setGeometry(xDest, yDest, m_movingToken->width(), m_movingToken->height()); + return; + } + + // We have arrived at our destination! + m_movingToken->setLocation(eDest); + m_movingToken->player()->setLocation(eDest); + emit tokenConfirmation(eDest); + + // We have arrived at our _final_ destination! + if (eDest == m_movingToken->destination()) + { + m_movingToken->setDestination(0); + m_movingToken->player()->setDestination(0); + + m_timer->stop(); + m_movingToken = 0; + } +} + +void AtlantikBoard::resizeEvent(QResizeEvent *) +{ + // Stop moving tokens, slotResizeAftermath will re-enable this + if (m_timer!=0 && m_timer->isActive()) + { + m_timer->stop(); + m_resumeTimer=true; + } + +/* + // Adjust spacer to make sure board stays a square + int q = e->size().width() - e->size().height(); + if (q > 0) + { + QSize s(q, 0); + spacer->setFixedSize(s); + } + else + { + QSize s(0, -q); + spacer->setFixedSize(s); + } +*/ + // Timer to reinit the gameboard _after_ resizeEvent + QTimer::singleShot(0, this, SLOT(slotResizeAftermath())); +} + +void AtlantikBoard::slotResizeAftermath() +{ + kdDebug() << "AtlantikBoard::slotResizeAftermath" << endl; + // Move tokens back to their last known location (this has to be done + // _after_ resizeEvent has returned to make sure we have the correct + // adjusted estate geometries. + + Token *token = 0; + for (QPtrListIterator it(m_tokens); (token = *it) ; ++it) + jumpToken(token); + + // Restart the timer that was stopped in resizeEvent + if (m_resumeTimer && m_timer!=0 && !m_timer->isActive()) + { + m_timer->start(15); + m_resumeTimer=false; + } +} + +void AtlantikBoard::displayDefault() +{ + switch(m_displayQueue.count()) + { + case 0: + m_displayQueue.prepend(new QWidget(this)); + break; + case 1: + if (EstateDetails *display = dynamic_cast(m_lastServerDisplay)) + display->setEstate(0); + break; + default: + if (m_displayQueue.getFirst() == m_lastServerDisplay) + m_lastServerDisplay = 0; + m_displayQueue.removeFirst(); + break; + } + updateCenter(); +} + +void AtlantikBoard::displayButton(QString command, QString caption, bool enabled) +{ + if (EstateDetails *display = dynamic_cast(m_lastServerDisplay)) + display->addButton(command, caption, enabled); +} + +void AtlantikBoard::addCloseButton() +{ + EstateDetails *eDetails = 0; + if ((eDetails = dynamic_cast(m_lastServerDisplay)) && eDetails != m_displayQueue.getLast()) + eDetails->addCloseButton(); +} + +void AtlantikBoard::insertDetails(QString text, bool clearText, bool clearButtons, Estate *estate) +{ + EstateDetails *eDetails = 0; + + if ((eDetails = dynamic_cast(m_lastServerDisplay))) + { + if (clearText) + eDetails->setText(text); + else + eDetails->appendText(text); + + if (clearButtons) + eDetails->clearButtons(); + + eDetails->setEstate(estate); + return; + } + + if (m_displayQueue.getFirst() != m_lastServerDisplay) + m_displayQueue.removeFirst(); + + eDetails = new EstateDetails(estate, text, this); + m_lastServerDisplay = eDetails; + connect(eDetails, SIGNAL(buttonCommand(QString)), this, SIGNAL(buttonCommand(QString))); + connect(eDetails, SIGNAL(buttonClose()), this, SLOT(displayDefault())); + + m_displayQueue.insert(0, eDetails); + updateCenter(); +} + +void AtlantikBoard::prependEstateDetails(Estate *estate) +{ + if (!estate) + return; + + EstateDetails *eDetails = 0; + + if (m_displayQueue.getFirst() == m_lastServerDisplay) + { + eDetails = new EstateDetails(estate, QString::null, this); + m_displayQueue.prepend(eDetails); + + connect(eDetails, SIGNAL(buttonCommand(QString)), this, SIGNAL(buttonCommand(QString))); + connect(eDetails, SIGNAL(buttonClose()), this, SLOT(displayDefault())); + } + else + { + eDetails = dynamic_cast ( m_displayQueue.getFirst() ); + if (eDetails) + { + eDetails->setEstate(estate); + eDetails->setText( QString::null ); + // eDetails->clearButtons(); + } + else + { + kdDebug() << "manual estatedetails with first in queue neither server nor details" << endl; + return; + } + } + + eDetails->addDetails(); + eDetails->addCloseButton(); + + updateCenter(); +} + +void AtlantikBoard::updateCenter() +{ + QWidget *center = m_displayQueue.getFirst(); + m_gridLayout->addMultiCellWidget(center, 1, m_gridLayout->numRows()-2, 1, m_gridLayout->numCols()-2); + center->show(); +} + +QWidget *AtlantikBoard::centerWidget() +{ + return m_displayQueue.getFirst(); +} diff --git a/atlantik/libatlantikui/board.h b/atlantik/libatlantikui/board.h new file mode 100644 index 00000000..deedb3d6 --- /dev/null +++ b/atlantik/libatlantikui/board.h @@ -0,0 +1,102 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_BOARD_H +#define ATLANTIK_BOARD_H + +#include +#include +#include +#include +#include "libatlantikui_export.h" +class QPoint; + +class AtlanticCore; +class Auction; +class Estate; +class Player; +class Token; + +class EstateView; + +class LIBATLANTIKUI_EXPORT AtlantikBoard : public QWidget +{ +Q_OBJECT + +public: + enum DisplayMode { Play, Edit }; + + AtlantikBoard(AtlanticCore *atlanticCore, int maxEstates, DisplayMode mode, QWidget *parent, const char *name=0); + ~AtlantikBoard(); + void reset(); + + void setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, bool animateTokens); + int heightForWidth(int); + void addEstateView(Estate *estate, bool indicateUnowned = false, bool highliteUnowned = false, bool darkenMortgaged = false, bool quartzEffects = false); + void addAuctionWidget(Auction *auction); + + void addToken(Player *player); + void removeToken(Player *player); + + void indicateUnownedChanged(); + EstateView *findEstateView(Estate *estate); + QWidget *centerWidget(); + +public slots: + void slotMoveToken(); + void slotResizeAftermath(); + void displayDefault(); + +private slots: + void playerChanged(Player *player); + void displayButton(QString command, QString caption, bool enabled); + void prependEstateDetails(Estate *); + void insertDetails(QString text, bool clearText, bool clearButtons, Estate *estate = 0); + void addCloseButton(); + +signals: + void tokenConfirmation(Estate *estate); + void buttonCommand(QString command); + +protected: + void resizeEvent(QResizeEvent *); + +private: + Token *findToken(Player *player); + void jumpToken(Token *token); + void moveToken(Token *token); + QPoint calculateTokenDestination(Token *token, Estate *estate = 0); + + void updateCenter(); + + AtlanticCore *m_atlanticCore; + DisplayMode m_mode; + + QWidget *spacer, *m_lastServerDisplay; + QGridLayout *m_gridLayout; + Token *m_movingToken; + QTimer *m_timer; + bool m_resumeTimer; + + bool m_animateTokens; + int m_maxEstates; + + QPtrList m_estateViews; + QPtrList m_tokens; + QPtrList m_displayQueue; +}; + +#endif diff --git a/atlantik/libatlantikui/estatedetails.cpp b/atlantik/libatlantikui/estatedetails.cpp new file mode 100644 index 00000000..d143033c --- /dev/null +++ b/atlantik/libatlantikui/estatedetails.cpp @@ -0,0 +1,327 @@ +// Copyright (c) 2002-2004 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "estatedetails.h" +#include "kwrappedlistviewitem.h" + +EstateDetails::EstateDetails(Estate *estate, QString text, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_pixmap = 0; + m_quartzBlocks = 0; + b_recreate = true; + m_recreateQuartz = true; + + m_estate = 0; + + m_closeButton = 0; + m_buttons.setAutoDelete(true); + + m_mainLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + Q_CHECK_PTR(m_mainLayout); + + m_mainLayout->addItem(new QSpacerItem(KDialog::spacingHint(), KDialog::spacingHint()+50, QSizePolicy::Fixed, QSizePolicy::Minimum)); + + m_infoListView = new KListView(this, "infoListView"); + m_infoListView->addColumn(m_estate ? m_estate->name() : QString("") ); + m_infoListView->setSorting(-1); + m_mainLayout->addWidget(m_infoListView); + + appendText(text); + + m_buttonBox = new QHBoxLayout(m_mainLayout, KDialog::spacingHint()); + m_buttonBox->setMargin(0); + + m_buttonBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + setEstate(estate); +} + +EstateDetails::~EstateDetails() +{ + delete m_pixmap; + delete m_quartzBlocks; + delete m_infoListView; +} + +void EstateDetails::paintEvent(QPaintEvent *) +{ + if (m_recreateQuartz) + { +/* + if (m_quartzBlocks) + { + delete m_quartzBlocks; + m_quartzBlocks = 0; + } + + if (m_estate->color().isValid()) + { + m_quartzBlocks = new KPixmap(); + + if (m_orientation == North || m_orientation == South) + m_quartzBlocks->resize(25, m_titleHeight-2); + else + m_quartzBlocks->resize(25, m_titleWidth-2); + + drawQuartzBlocks(m_quartzBlocks, *m_quartzBlocks, m_estate->color().light(60), m_estate->color()); + m_quartzBlocks = rotatePixmap(m_quartzBlocks); + } +*/ + m_recreateQuartz = false; + b_recreate = true; + } + + if (b_recreate) + { + delete m_pixmap; + m_pixmap = new QPixmap(width(), height()); + + QColor greenHouse(0, 255, 0); + QColor redHotel(255, 51, 51); + QPainter painter; + painter.begin(m_pixmap, this); + + painter.setPen(Qt::black); + + painter.setBrush(m_estate ? m_estate->bgColor() : Qt::white); + painter.drawRect(rect()); + +/* + // Paint icon only when it exists and fits + if (icon!=0 && width() > icon->width() && height() > icon->height()) + painter.drawPixmap( (width() - icon->width())/2, (height() - icon->height())/2, *icon); +*/ + + if (m_estate) + { + int titleHeight = 50; + QColor titleColor = (m_estate->color().isValid() ? m_estate->color() : m_estate->bgColor().light(80)); + + KPixmap* quartzBuffer = new KPixmap; + quartzBuffer->resize(25, (height()/4)-2); + + QPainter quartzPainter; + quartzPainter.begin(quartzBuffer, this); + + painter.setBrush(titleColor); + painter.drawRect(0, 0, width(), titleHeight); + + if (m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + int titleWidth = width() / 5; + + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, titleWidth-4, titleHeight-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = titleHeight-4, w = titleWidth-4; + for ( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), 2, w, h); + } + } + + quartzPainter.end(); + delete quartzBuffer; + + // TODO: steal blur code from kicker/taskbar/taskcontainer.cpp + + // Estate name + painter.setPen(Qt::white); + int fontSize = KGlobalSettings::generalFont().pointSize(); + if (fontSize == -1) + fontSize = KGlobalSettings::generalFont().pixelSize(); + + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize * 2, QFont::Bold)); + painter.drawText(KDialog::marginHint(), KDialog::marginHint(), width()-KDialog::marginHint(), titleHeight, Qt::AlignJustify, m_estate->name()); + + painter.setPen(Qt::black); + + int xText = 0; + + // Estate group + if (m_estate->estateGroup()) + { + xText = titleHeight - fontSize - KDialog::marginHint(); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize, QFont::Bold)); + painter.drawText(5, xText, width()-10, titleHeight, Qt::AlignRight, m_estate->estateGroup()->name().upper()); + } + + xText = titleHeight + fontSize + 5; + painter.setFont(QFont(KGlobalSettings::generalFont().family(), fontSize, QFont::Normal)); + } + b_recreate = false; + + } + bitBlt(this, 0, 0, m_pixmap); +} + +void EstateDetails::resizeEvent(QResizeEvent *) +{ + m_recreateQuartz = true; + b_recreate = true; +} + +void EstateDetails::addDetails() +{ + if (m_estate) + { + QListViewItem *infoText = 0; + + // Price + if (m_estate->price()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Price: %1").arg(m_estate->price())); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + } + + // Owner, houses, isMortgaged + if (m_estate && m_estate->canBeOwned()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Owner: %1").arg(m_estate->owner() ? m_estate->owner()->name() : i18n("unowned"))); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + + if (m_estate->isOwned()) + { + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Houses: %1").arg(m_estate->houses())); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + + infoText = new QListViewItem(m_infoListView, m_infoListView->lastItem(), i18n("Mortgaged: %1").arg(m_estate->isMortgaged() ? i18n("Yes") : i18n("No"))); + infoText->setPixmap(0, QPixmap(SmallIcon("info"))); + } + } + } +} + +void EstateDetails::addButton(QString command, QString caption, bool enabled) +{ + KPushButton *button = new KPushButton(caption, this); + m_buttons.append(button); + m_buttonCommandMap[(QObject *)button] = command; + m_buttonBox->addWidget(button); + + if (m_estate) + { + QColor bgColor, fgColor; + bgColor = m_estate->bgColor().light(110); + fgColor = ( bgColor.red() + bgColor.green() + bgColor.blue() < 255 ) ? Qt::white : Qt::black; + + button->setPaletteForegroundColor( fgColor ); + button->setPaletteBackgroundColor( bgColor ); + } + button->setEnabled(enabled); + button->show(); + + connect(button, SIGNAL(pressed()), this, SLOT(buttonPressed())); +} + +void EstateDetails::addCloseButton() +{ + if (!m_closeButton) + { + m_closeButton = new KPushButton(KStdGuiItem::close(), this); + m_buttonBox->addWidget(m_closeButton); + m_closeButton->show(); + connect(m_closeButton, SIGNAL(pressed()), this, SIGNAL(buttonClose())); + } +} + +void EstateDetails::setEstate(Estate *estate) +{ + if (m_estate != estate) + { + m_estate = estate; + + m_infoListView->setColumnText( 0, m_estate ? m_estate->name() : QString("") ); + + b_recreate = true; + update(); + } +} + +void EstateDetails::setText(QString text) +{ + m_infoListView->clear(); + appendText(text); +} + +void EstateDetails::appendText(QString text) +{ + if ( text.isEmpty() ) + return; + + KWrappedListViewItem *infoText = new KWrappedListViewItem(m_infoListView, m_infoListView->lastItem(), text); + + if ( text.find( QRegExp("rolls") ) != -1 ) + infoText->setPixmap(0, QPixmap(SmallIcon("roll"))); + else + infoText->setPixmap(0, QPixmap(SmallIcon("atlantik"))); + + m_infoListView->ensureItemVisible( infoText ); +} + +void EstateDetails::clearButtons() +{ + if (m_closeButton) + { + delete m_closeButton; + m_closeButton = 0; + } + + // Delete buttons + m_buttons.clear(); + m_buttonCommandMap.clear(); +} + +void EstateDetails::buttonPressed() +{ + emit buttonCommand(QString(m_buttonCommandMap[(QObject *)QObject::sender()])); +} + +#include "estatedetails.moc" diff --git a/atlantik/libatlantikui/estatedetails.h b/atlantik/libatlantikui/estatedetails.h new file mode 100644 index 00000000..b8264a51 --- /dev/null +++ b/atlantik/libatlantikui/estatedetails.h @@ -0,0 +1,77 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ESTATEDETAILS_H +#define ATLANTIK_ESTATEDETAILS_H + +#include + +class QPixmap; +class QString; +class QHBoxLayout; +class QVBoxLayout; +class QVGroupBox; + +class KListView; +class KPixmap; +class KPushButton; + +class Player; +class Estate; + +class EstateDetails : public QWidget +{ +Q_OBJECT + +public: + EstateDetails(Estate *estate, QString text, QWidget *parent, const char *name = 0); + ~EstateDetails(); + Estate *estate() { return m_estate; } + + void addDetails(); + void addButton(const QString command, const QString caption, bool enabled); + void addCloseButton(); + void setEstate(Estate *estate); + void setText(QString text); + void appendText(QString text); + void clearButtons(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +private slots: + void buttonPressed(); + +signals: + void buttonCommand(QString); + void buttonClose(); + +private: + Estate *m_estate; + QPixmap *m_pixmap; + KPixmap *m_quartzBlocks; + KListView *m_infoListView; + KPushButton *m_closeButton; + bool b_recreate, m_recreateQuartz; + QVBoxLayout *m_mainLayout; + QHBoxLayout *m_buttonBox; + QVGroupBox *m_textGroupBox; + QMap m_buttonCommandMap; + QPtrList m_buttons; +}; + +#endif diff --git a/atlantik/libatlantikui/estateview.cpp b/atlantik/libatlantikui/estateview.cpp new file mode 100644 index 00000000..b8c3f38c --- /dev/null +++ b/atlantik/libatlantikui/estateview.cpp @@ -0,0 +1,558 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "estateview.moc" +#include "config.h" + +EstateView::EstateView(Estate *estate, EstateOrientation orientation, const QString &_icon, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, QWidget *parent, const char *name) : QWidget(parent, name, WResizeNoErase) +{ + m_estate = estate; + m_orientation = orientation; + + m_indicateUnowned = indicateUnowned; + m_highliteUnowned = highliteUnowned; + m_darkenMortgaged = darkenMortgaged; + m_quartzEffects = quartzEffects; + + setBackgroundMode(NoBackground); // avoid flickering + + qpixmap = 0; + b_recreate = true; + + m_quartzBlocks = 0; + m_recreateQuartz = true; + + pe = 0; + updatePE(); + + icon = new QPixmap(locate("data", "atlantik/pics/" + _icon)); + icon = rotatePixmap(icon); + + updateToolTip(); +} + +void EstateView::updateToolTip() +{ + QToolTip::remove(this); + + if ( m_estate ) + { + QString toolTip = m_estate->name(); + if ( m_estate->isOwned() ) + { + toolTip.append( "\n" + i18n("Owner: %1").arg( m_estate->owner()->name() ) ); + if ( m_estate->isMortgaged() ) + toolTip.append( "\n" + i18n("Unmortgage Price: %1").arg( m_estate->unmortgagePrice() ) ); + else + toolTip.append( "\n" + i18n("Mortgage Value: %1").arg( m_estate->mortgagePrice() ) ); + if ( m_estate->canSellHouses() ) + toolTip.append( "\n" + i18n("House Value: %1").arg( m_estate->houseSellPrice() ) ); + if ( m_estate->canBuyHouses() ) + toolTip.append( "\n" + i18n("House Price: %1").arg( m_estate->housePrice() ) ); + } + else if ( m_estate->canBeOwned() ) + toolTip.append( "\n" + i18n("Price: %1").arg( m_estate->price() ) ); + else if ( m_estate->money() ) + toolTip.append( "\n" + i18n("Money: %1").arg( m_estate->money() ) ); + + QToolTip::add( this, toolTip ); + } +} + +void EstateView::setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects) +{ + if (m_indicateUnowned != indicateUnowned) + { + m_indicateUnowned = indicateUnowned; + b_recreate = true; + updatePE(); + } + + if (m_highliteUnowned != highliteUnowned) + { + m_highliteUnowned = highliteUnowned; + b_recreate = true; + } + + if (m_darkenMortgaged != darkenMortgaged) + { + m_darkenMortgaged = darkenMortgaged; + b_recreate = true; + } + + if (m_quartzEffects != quartzEffects) + { + m_quartzEffects = quartzEffects; + b_recreate = true; +// m_recreateQuartz = true; + } + + if (b_recreate || m_recreateQuartz) + update(); +} + +QPixmap *EstateView::rotatePixmap(QPixmap *p) +{ + if (p==0 || p->isNull()) + return 0; + + QWMatrix m; + + switch(m_orientation) + { + case East: + m.rotate(90); + break; + case West: + m.rotate(-90); + break; + case South: + m.rotate(180); + break; + default:; + } + *p = p->xForm(m); + return p; +} + +KPixmap *EstateView::rotatePixmap(KPixmap *p) +{ + if (p==0 || p->isNull()) + return 0; + + QWMatrix m; + + switch(m_orientation) + { + case East: + m.rotate(90); + break; + case West: + m.rotate(-90); + break; + case South: + m.rotate(180); + break; + default:; + } + *p = p->xForm(m); + return p; +} + +void EstateView::updatePE() +{ + // Don't show a when a property is not unowned, cannot be owned at all + // or when the user has configured Atlantik not to show them. + if (m_estate->isOwned() || !m_estate->canBeOwned() || m_indicateUnowned==false) + { + delete pe; + pe = 0; + } + else + { + if (pe==0) + { + // Display a coloured portfolioestate to indicate property is + // for sale + pe = new PortfolioEstate(m_estate, 0, true, this, "board-portfolioestate"); + repositionPortfolioEstate(); + + pe->show(); + } + else if (!pe->isVisible()) + pe->show(); + } +} + +void EstateView::estateChanged() +{ + updateToolTip(); + + b_recreate = true; + m_recreateQuartz = true; + + update(); + updatePE(); +} + +void EstateView::repositionPortfolioEstate() +{ + if (pe!=0) + { + int x = (m_orientation == West ? (width()-2 - pe->width()) : 2); + int y = (m_orientation == North ? (height()-2 - pe->height()) : 2); + pe->setGeometry(x, y, pe->width(), pe->height()); + } +} + +void EstateView::paintEvent(QPaintEvent *) +{ + m_titleHeight = height()/4; + m_titleWidth = width()/4; + + if (m_recreateQuartz) + { + if (m_quartzBlocks) + { + delete m_quartzBlocks; + m_quartzBlocks = 0; + } + + if (m_estate->color().isValid()) + { + m_quartzBlocks = new KPixmap(); + + if (m_orientation == North || m_orientation == South) + m_quartzBlocks->resize(25, m_titleHeight-2); + else + m_quartzBlocks->resize(25, m_titleWidth-2); + + drawQuartzBlocks(m_quartzBlocks, *m_quartzBlocks, m_estate->color().light(60), m_estate->color()); + m_quartzBlocks = rotatePixmap(m_quartzBlocks); + } + + m_recreateQuartz = false; + b_recreate = true; + } + + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QColor greenHouse(0, 255, 0); + QColor redHotel(255, 51, 51); + QPainter painter; + painter.begin(qpixmap, this); + + painter.setPen(Qt::black); + + if (m_darkenMortgaged==true && m_estate->isMortgaged()) + painter.setBrush(m_estate->bgColor().light(10)); + else if (m_highliteUnowned==true && m_estate->canBeOwned() && !m_estate->isOwned()) + painter.setBrush(m_estate->bgColor().light(190)); + else + painter.setBrush(m_estate->bgColor()); + + painter.drawRect(rect()); + + // Paint icon only when it exists and fits + if (icon!=0 && width() > icon->width() && height() > icon->height()) + painter.drawPixmap( (width() - icon->width())/2, (height() - icon->height())/2, *icon); + + if (m_estate->color().isValid()) + { + KPixmap* quartzBuffer = new KPixmap; + if (m_orientation == North || m_orientation == South) + quartzBuffer->resize(25, m_titleHeight-2); + else + quartzBuffer->resize(m_titleWidth-2, 25); + + QPainter quartzPainter; + quartzPainter.begin(quartzBuffer, this); + + painter.setBrush(m_estate->color()); + switch(m_orientation) + { + case North: + painter.drawRect(0, 0, width(), m_titleHeight); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, (width()/2)-4, (m_titleHeight)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), 2, w, h); + } + } + break; + case South: + painter.drawRect(0, height()-(m_titleHeight), width(), m_titleHeight); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(width()-quartzBuffer->width()-1, height()-m_titleHeight+1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, (3*(m_titleHeight))+2, (width()/2)-4, (m_titleHeight)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2+(i*(w+2)), (3*(m_titleHeight))+2, w, h); + } + } + break; + case West: + painter.drawRect(0, 0, m_titleWidth, height()); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(1, height()-quartzBuffer->height()-1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect(2, 2, (m_titleWidth)-4, (height()/2)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect(2, 2+(i*(h+2)), w, h); + } + } + break; + case East: + painter.drawRect(width()-(m_titleWidth), 0, m_titleWidth, height()); + + if (m_quartzEffects && m_quartzBlocks) + { + quartzPainter.drawPixmap(0, 0, *m_quartzBlocks); + painter.drawPixmap(width()-quartzBuffer->width()-1, 1, *quartzBuffer); + } + + if (m_estate->houses() > 0) + { + if (m_estate->houses() == 5) + { + // Hotel + painter.setBrush(redHotel); + painter.drawRect((3*(m_titleWidth))+2, 2, (m_titleWidth)-4, (height()/2)-4); + } + else + { + // Houses + painter.setBrush(greenHouse); + int h = (m_titleHeight)-4, w = (m_titleWidth)-4; + for( unsigned int i=0 ; i < m_estate->houses() ; i++ ) + painter.drawRect((3*(m_titleWidth))+2, 2+(i*(h+2)), w, h); + } + } + break; + } + + + quartzPainter.end(); + delete quartzBuffer; + } + + QFont font = QFont( KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal ); + painter.setFont(font); + QString estateName = m_estate->name(); +#if defined(KDE_MAKE_VERSION) +#if KDE_VERSION >= KDE_MAKE_VERSION(3,2,0) + if ( m_estate->color().isValid() && ( m_orientation == West || m_orientation == East ) ) + estateName = KStringHandler::rPixelSqueeze( m_estate->name(), QFontMetrics( font ), 3*width()/4 ); + else + estateName = KStringHandler::rPixelSqueeze( m_estate->name(), QFontMetrics( font ), width() ); +#endif +#endif + if (m_estate->color().isValid() && m_orientation == West) + painter.drawText( width()/4 + 2, height()/2, estateName ); + else + painter.drawText(2, height()/2, estateName ); + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void EstateView::resizeEvent(QResizeEvent *) +{ + m_recreateQuartz = true; + b_recreate = true; + + QTimer::singleShot(0, this, SLOT(slotResizeAftermath())); +} + +void EstateView::mousePressEvent(QMouseEvent *e) +{ + if (e->button()==RightButton && m_estate->isOwned()) + { + KPopupMenu *rmbMenu = new KPopupMenu(this); + rmbMenu->insertTitle(m_estate->name()); + + if (m_estate->isOwnedBySelf()) + { + Player *player = m_estate->owner(); + + // Mortgage toggle + if (m_estate->isMortgaged()) + { + rmbMenu->insertItem(i18n("Unmortgage"), 0); + if (!m_estate->canToggleMortgage() || player->hasDebt()) + rmbMenu->setItemEnabled(0, false); + } + else + { + rmbMenu->insertItem(i18n("Mortgage"), 0); + if (!m_estate->canToggleMortgage()) + rmbMenu->setItemEnabled(0, false); + } + + // Estate construction + if (m_estate->houses()>=4) + rmbMenu->insertItem(i18n("Build Hotel"), 1); + else + rmbMenu->insertItem(i18n("Build House"), 1); + + if (!m_estate->canBuyHouses() || player->hasDebt()) + rmbMenu->setItemEnabled(1, false); + + // Estate destruction + if (m_estate->houses()==5) + rmbMenu->insertItem(i18n("Sell Hotel"), 2); + else + rmbMenu->insertItem(i18n("Sell House"), 2); + + if (!(m_estate->canSellHouses())) + rmbMenu->setItemEnabled(2, false); + } + else + { + // Request trade + if (Player *player = m_estate->owner()) + rmbMenu->insertItem(i18n("Request Trade with %1").arg(player->name()), 3); + } + + KPopupMenu *pm = dynamic_cast(rmbMenu); + if (pm) { + connect(pm, SIGNAL(activated(int)), this, SLOT(slotMenuAction(int))); + } + QPoint g = QCursor::pos(); + rmbMenu->exec(g); + delete rmbMenu; + } + else if (e->button()==LeftButton) + emit LMBClicked(m_estate); +} + +void EstateView::slotResizeAftermath() +{ + repositionPortfolioEstate(); +} + +void EstateView::slotMenuAction(int item) +{ + switch (item) + { + case 0: + emit estateToggleMortgage(m_estate); + break; + + case 1: + emit estateHouseBuy(m_estate); + break; + + case 2: + emit estateHouseSell(m_estate); + break; + + case 3: + emit newTrade(m_estate->owner()); + break; + } +} + +// Kudos to Gallium for writing the Quartz KWin style and +// letting me use the ultra slick algorithm! +void EstateView::drawQuartzBlocks(KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2) +{ + QPainter px; + + if (pi==0 || pi->isNull()) + return; + + px.begin(pi); + + KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient); + + px.fillRect( 2, 1, 3, 3, c1.light(120) ); + px.fillRect( 2, 5, 3, 3, c1 ); + px.fillRect( 2, 9, 3, 3, c1.light(110) ); + px.fillRect( 2, 13, 3, 3, c1 ); + + px.fillRect( 6, 1, 3, 3, c1.light(110) ); + px.fillRect( 6, 5, 3, 3, c2.light(110) ); + px.fillRect( 6, 9, 3, 3, c1.light(120) ); + px.fillRect( 6, 13, 3, 3, c2.light(130) ); + + px.fillRect( 10, 5, 3, 3, c1.light(110) ); + px.fillRect( 10, 9, 3, 3, c2.light(120) ); + px.fillRect( 10, 13, 3, 3, c2.light(150) ); + + px.fillRect( 14, 1, 3, 3, c1.dark(110) ); + px.fillRect( 14, 9, 3, 3, c2.light(120) ); + px.fillRect( 14, 13, 3, 3, c1.dark(120) ); + + px.fillRect( 18, 5, 3, 3, c1.light(110) ); + px.fillRect( 18, 13, 3, 3, c1.dark(110) ); + + px.fillRect( 22, 9, 3, 3, c2.light(120)); + px.fillRect( 22, 13, 3, 3, c2.light(110) ); +} diff --git a/atlantik/libatlantikui/estateview.h b/atlantik/libatlantikui/estateview.h new file mode 100644 index 00000000..864c8983 --- /dev/null +++ b/atlantik/libatlantikui/estateview.h @@ -0,0 +1,80 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_ESTATEVIEW_H +#define ATLANTIK_ESTATEVIEW_H + +#include +#include + +#include + +#include "portfolioestate.h" + +enum EstateOrientation { North=0, East=1, South=2, West=3 }; + +class Player; +class Estate; + +class EstateView : public QWidget +{ +Q_OBJECT + + public: + EstateView(Estate *estate, EstateOrientation orientation, const QString &, bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects, QWidget *parent, const char *name = 0); + void setViewProperties(bool indicateUnowned, bool highliteUnowned, bool darkenMortgaged, bool quartzEffects); + Estate *estate() { return m_estate; } + void updatePE(); + EstateOrientation orientation() { return m_orientation; } + + public slots: + void slotResizeAftermath(); + + signals: + void estateToggleMortgage(Estate *estate); + void estateHouseBuy(Estate *estate); + void estateHouseSell(Estate *estate); + void newTrade(Player *player); + void LMBClicked(Estate *estate); + + protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mousePressEvent(QMouseEvent *); + +private: + void updateToolTip(); + + QPixmap *rotatePixmap(QPixmap *); + KPixmap *rotatePixmap(KPixmap *); + void drawQuartzBlocks(KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2); + void repositionPortfolioEstate(); + + Estate *m_estate; + QPixmap *qpixmap, *icon; + KPixmap *m_quartzBlocks; + bool m_indicateUnowned, m_highliteUnowned, m_darkenMortgaged, m_quartzEffects; + bool b_recreate, m_recreateQuartz; + int m_titleWidth, m_titleHeight; + EstateOrientation m_orientation; + PortfolioEstate *pe; + + private slots: + void slotMenuAction(int); + void estateChanged(); +}; + +#endif diff --git a/atlantik/libatlantikui/kwrappedlistviewitem.cpp b/atlantik/libatlantikui/kwrappedlistviewitem.cpp new file mode 100644 index 00000000..7bd9e2cf --- /dev/null +++ b/atlantik/libatlantikui/kwrappedlistviewitem.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2004 Rob Kaper . All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include +#include + +#include +#include +#include + +#include "kwrappedlistviewitem.h" + +KWrappedListViewItem::KWrappedListViewItem( QListView *parent, QString text, QString t2 ) +: QObject(), KListViewItem( parent ) +{ + init( parent, text, t2 ); +} + +KWrappedListViewItem::KWrappedListViewItem( QListView *parent, QListViewItem *after, QString text, QString t2 ) +: QObject(), KListViewItem( parent, after ) +{ + init( parent, text, t2 ); +} + +void KWrappedListViewItem::setup() +{ + widthChanged(); +} + +/* +int KWrappedListViewItem::width( const QFontMetrics&, const QListView*, int ) const +{ + return m_wrap->boundingRect().width(); +} +*/ + +void KWrappedListViewItem::wrapColumn( int c ) +{ + if ( c != m_wrapColumn ) + return; + + QListView *lv = listView(); + if ( !lv ) + return; + + QFont font = QFont( KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal ); + QFontMetrics fm = QFontMetrics( font ); + + int wrapWidth = lv->width(); + for ( int i = 0 ; i < m_wrapColumn ; i++ ) + wrapWidth -= ( width(fm, lv, i) + lv->itemMargin() ); + + if ( pixmap(c) ) + wrapWidth -= ( pixmap( c )->width() + lv->itemMargin() ); + + QScrollBar *scrollBar = lv->verticalScrollBar(); + if ( !scrollBar->isHidden() ) + wrapWidth -= scrollBar->width(); + + QRect rect = QRect( 0, 0, wrapWidth - 20, -1 ); + + KWordWrap *wrap = KWordWrap::formatText( fm, rect, 0, m_origText ); + setText( c, wrap->wrappedString() ); + + int lc = text(c).contains( QChar( '\n' ) ) + 1; + setHeight( wrap->boundingRect().height() + lc*lv->itemMargin() ); + + widthChanged( c ); + + delete wrap; +} + +void KWrappedListViewItem::init( QListView *parent, QString text, QString t2 ) +{ + m_wrapColumn = 0; + setMultiLinesEnabled( true ); + parent->setResizeMode( QListView::LastColumn ); + + m_origText = text; + + if ( !t2.isNull() ) + { + setText( 0, text ); + m_origText = t2; + m_wrapColumn = 1; + } + else + m_origText = text; + + wrapColumn( m_wrapColumn ); + + connect( parent->header(), SIGNAL(sizeChange(int, int, int)), this, SLOT(wrapColumn(int))); +} + +#include "kwrappedlistviewitem.moc" diff --git a/atlantik/libatlantikui/kwrappedlistviewitem.h b/atlantik/libatlantikui/kwrappedlistviewitem.h new file mode 100644 index 00000000..056cef6d --- /dev/null +++ b/atlantik/libatlantikui/kwrappedlistviewitem.h @@ -0,0 +1,54 @@ +// Copyright (c) 2004 Rob Kaper . All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#ifndef KWRAPPEDLISTVIEWITEM_H +#define KWRAPPEDLISTVIEWITEM_H + +#include +#include + +#include + +class KWordWrap; + +class KWrappedListViewItem : public QObject, public KListViewItem +{ +Q_OBJECT + +public: + KWrappedListViewItem( QListView *parent, QString text, QString=QString::null ); + KWrappedListViewItem( QListView *parent, QListViewItem *after, QString text, QString=QString::null ); + void setup(); +// int width(const QFontMetrics& fm, const QListView* lv, int c) const; + +private slots: + void wrapColumn( int c ); + +private: + void init( QListView *parent, QString text, QString=QString::null ); + QString m_origText; + int m_wrapColumn; +}; + +#endif diff --git a/atlantik/libatlantikui/libatlantikui_export.h b/atlantik/libatlantikui/libatlantikui_export.h new file mode 100644 index 00000000..9ea1695f --- /dev/null +++ b/atlantik/libatlantikui/libatlantikui_export.h @@ -0,0 +1,25 @@ +// Copyright (c) 2004 Dirk Mueller + +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef LIBATLANTIKUI_EXPORT_H +#define LIBATLANTIKUI_EXPORT_H + +#include + +#define LIBATLANTIKUI_EXPORT KDE_EXPORT + +#endif diff --git a/atlantik/libatlantikui/portfolioestate.cpp b/atlantik/libatlantikui/portfolioestate.cpp new file mode 100644 index 00000000..625fb055 --- /dev/null +++ b/atlantik/libatlantikui/portfolioestate.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include +#include + +#include "portfolioestate.moc" +#include "estate.h" + +PortfolioEstate::PortfolioEstate(Estate *estate, Player *player, bool alwaysOwned, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_estate = estate; + m_player = player; + m_alwaysOwned = alwaysOwned; + + QSize s(PE_WIDTH, PE_HEIGHT); + setFixedSize(s); + + b_recreate = true; +} + +void PortfolioEstate::estateChanged() +{ + b_recreate = true; + update(); +} + +QPixmap PortfolioEstate::drawPixmap(Estate *estate, Player *player, bool alwaysOwned) +{ + QColor lightGray(204, 204, 204), darkGray(153, 153, 153); + QPixmap qpixmap(PE_WIDTH, PE_HEIGHT); + QPainter painter; + painter.begin(&qpixmap); + + painter.setPen(lightGray); + painter.setBrush(white); + painter.drawRect(QRect(0, 0, PE_WIDTH, PE_HEIGHT)); + if (alwaysOwned || (estate && estate->isOwned() && player == estate->owner())) + { + painter.setPen(darkGray); + for (int y=5;y<=13;y+=2) + painter.drawLine(2, y, 10, y); + + painter.setPen(Qt::white); + painter.drawPoint(8, 5); + painter.drawPoint(8, 7); + painter.drawPoint(8, 9); + painter.drawPoint(5, 11); + painter.drawPoint(9, 11); + painter.drawPoint(3, 13); + painter.drawPoint(10, 13); + + painter.setPen(estate->color()); + painter.setBrush(estate->color()); + } + else + { + painter.setPen(lightGray); + painter.setBrush(lightGray); + } + painter.drawRect(0, 0, PE_WIDTH, 3); + + return qpixmap; +} + +void PortfolioEstate::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + m_pixmap = drawPixmap(m_estate, m_player, m_alwaysOwned); + b_recreate = false; + } + bitBlt(this, 0, 0, &m_pixmap); +} + +void PortfolioEstate::mousePressEvent(QMouseEvent *e) +{ + if (e->button()==LeftButton) + emit estateClicked(m_estate); +} diff --git a/atlantik/libatlantikui/portfolioestate.h b/atlantik/libatlantikui/portfolioestate.h new file mode 100644 index 00000000..65bd5db3 --- /dev/null +++ b/atlantik/libatlantikui/portfolioestate.h @@ -0,0 +1,55 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_PORTFOLIOESTATE_H +#define ATLANTIK_PORTFOLIOESTATE_H + +#include +#include + +#define PE_WIDTH 13 +#define PE_HEIGHT 16 + +class Estate; +class Player; + +class PortfolioEstate : public QWidget +{ +Q_OBJECT + +public: + PortfolioEstate(Estate *estate, Player *player, bool alwaysOwned, QWidget *parent, const char *name = 0); + Estate *estate() { return m_estate; } + static QPixmap drawPixmap(Estate *estate, Player *player = 0, bool alwaysOwned = true); + +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + +private slots: + void estateChanged(); + +signals: + void estateClicked(Estate *estate); + +private: + Estate *m_estate; + Player *m_player; + QPixmap m_pixmap; + bool b_recreate, m_alwaysOwned; +}; + +#endif diff --git a/atlantik/libatlantikui/portfolioview.cpp b/atlantik/libatlantikui/portfolioview.cpp new file mode 100644 index 00000000..c07d426b --- /dev/null +++ b/atlantik/libatlantikui/portfolioview.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "portfolioview.moc" + +#define PE_DISTW 4 +#define PE_DISTH 4 +#define PE_SPACE 2 +#define PE_MARGINW 5 +#define PE_MARGINH 2 +#define ICONSIZE 48 + +PortfolioView::PortfolioView(AtlanticCore *core, Player *player, QColor activeColor, QColor inactiveColor, QWidget *parent, const char *name) : QWidget(parent, name) +{ + m_atlanticCore = core; + m_player = player; + m_activeColor = activeColor; + m_inactiveColor = inactiveColor; + m_lastPE = 0; + + qpixmap = 0; + b_recreate = true; + + m_portfolioEstates.setAutoDelete(true); + setBackgroundColor(Qt::white); + setMinimumHeight(ICONSIZE); + + // Init icon + m_image = 0; + m_imageName = "hamburger.png"; + loadIcon(); +} + +PortfolioView::~PortfolioView() +{ + clearPortfolio(); + delete m_image; + delete qpixmap; +} + +Player *PortfolioView::player() +{ + return m_player; +} + +void PortfolioView::buildPortfolio() +{ + if ( m_portfolioEstates.count() ) + clearPortfolio(); + + // Loop through estate groups in order + QPtrList estateGroups = m_atlanticCore->estateGroups(); + PortfolioEstate *lastPE = 0, *firstPEprevGroup = 0; + + int x = 100, y = 25, marginHint = 5, bottom; + bottom = ICONSIZE - PE_HEIGHT - marginHint; + + EstateGroup *estateGroup; + for (QPtrListIterator it(estateGroups); *it; ++it) + { + if ((estateGroup = *it)) + { + // New group + lastPE = 0; + + // Loop through estates + QPtrList estates = m_atlanticCore->estates(); + Estate *estate; + for (QPtrListIterator it(estates); *it; ++it) + { + if ((estate = *it) && estate->estateGroup() == estateGroup) + { + // Create PE + PortfolioEstate *portfolioEstate = new PortfolioEstate(estate, m_player, false, this, "portfolioestate"); + m_portfolioEstates.append(portfolioEstate); + + connect(portfolioEstate, SIGNAL(estateClicked(Estate *)), this, SIGNAL(estateClicked(Estate *))); + if (lastPE) + { + x = lastPE->x() + 2; + y = lastPE->y() + 4; + if (y > bottom) + bottom = y; + } + else if (firstPEprevGroup) + { + x = firstPEprevGroup->x() + PE_WIDTH + 8; + y = 20 + marginHint; + firstPEprevGroup = portfolioEstate; + } + else + { + x = ICONSIZE + marginHint; + y = 20 + marginHint; + if (y > bottom) + bottom = y; + firstPEprevGroup = portfolioEstate; + } + + portfolioEstate->setGeometry(x, y, portfolioEstate->width(), portfolioEstate->height()); + portfolioEstate->show(); + + connect(estate, SIGNAL(changed()), portfolioEstate, SLOT(estateChanged())); + + lastPE = portfolioEstate; + } + } + } + } + setMinimumWidth(x + PE_WIDTH + marginHint); + int minHeight = bottom + PE_HEIGHT + marginHint; + if (minHeight > minimumHeight()) + setMinimumHeight(minHeight); +} + +void PortfolioView::clearPortfolio() +{ + m_portfolioEstates.clear(); +} + +void PortfolioView::loadIcon() +{ + if (m_imageName == m_player->image()) + return; + m_imageName = m_player->image(); + + delete m_image; + m_image = 0; + + if (!m_imageName.isEmpty()) + { + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + if (!m_image) + { + return; + +/* + m_imageName = "hamburger.png"; + + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); +*/ + } + else if (ICONSIZE > minimumHeight()) + setMinimumHeight(ICONSIZE); + + QWMatrix m; + m.scale(double(ICONSIZE) / m_image->width(), double(ICONSIZE) / m_image->height()); + QPixmap *scaledPixmap = new QPixmap(ICONSIZE, ICONSIZE); + *scaledPixmap = m_image->xForm(m); + + delete m_image; + m_image = scaledPixmap; +} + +void PortfolioView::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QPainter painter; + painter.begin(qpixmap, this); + + painter.setPen(Qt::white); + painter.setBrush(Qt::white); + painter.drawRect(rect()); + + painter.setPen(m_player->hasTurn() ? m_activeColor : Qt::black); + painter.setBrush(m_player->hasTurn() ? m_activeColor : Qt::black); + painter.drawRect(0, 0, width(), 20); + + if (m_image) + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + painter.drawRect(0, 0, ICONSIZE, ICONSIZE); + + painter.drawPixmap(0, 0, *m_image); + } + + painter.setPen(Qt::white); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Bold)); + painter.drawText(ICONSIZE + KDialog::marginHint(), 15, m_player->name()); + + if ( m_portfolioEstates.count() ) + painter.drawText(width() - 50, 15, QString::number(m_player->money())); + else + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::Normal)); + painter.drawText(ICONSIZE + KDialog::marginHint(), 30, m_player->host()); + } + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void PortfolioView::resizeEvent(QResizeEvent *) +{ + b_recreate = true; +} + +void PortfolioView::playerChanged() +{ + loadIcon(); + + b_recreate = true; + update(); +} + +void PortfolioView::mousePressEvent(QMouseEvent *e) +{ + Player *playerSelf = m_atlanticCore->playerSelf(); + + if ( e->button()==RightButton && (m_player != playerSelf) ) + { + KPopupMenu *rmbMenu = new KPopupMenu(this); + rmbMenu->insertTitle(m_player->name()); + + if ( m_portfolioEstates.count() ) + { + // Start trade + rmbMenu->insertItem(i18n("Request Trade with %1").arg(m_player->name()), 0); + } + else + { + // Kick player + rmbMenu->insertItem(i18n("Boot Player %1 to Lounge").arg(m_player->name()), 0); + rmbMenu->setItemEnabled( 0, m_atlanticCore->selfIsMaster() ); + } + + connect(rmbMenu, SIGNAL(activated(int)), this, SLOT(slotMenuAction(int))); + QPoint g = QCursor::pos(); + rmbMenu->exec(g); + } +} + +void PortfolioView::slotMenuAction(int item) +{ + switch (item) + { + case 0: + if ( m_portfolioEstates.count() ) + emit newTrade(m_player); + else + emit kickPlayer(m_player); + break; + } +} +#undef PE_DISTW +#undef PE_DISTH +#undef PE_SPACE +#undef PE_MARGINW +#undef PE_MARGINH +#undef ICONSIZE diff --git a/atlantik/libatlantikui/portfolioview.h b/atlantik/libatlantikui/portfolioview.h new file mode 100644 index 00000000..26317e2b --- /dev/null +++ b/atlantik/libatlantikui/portfolioview.h @@ -0,0 +1,73 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_PORTFOLIOVIEW_H +#define ATLANTIK_PORTFOLIOVIEW_H + +#include +#include +#include + +#include "portfolioestate.h" +#include "libatlantikui_export.h" +class QColor; +class QString; + +class AtlanticCore; +class Player; +class Estate; + +class LIBATLANTIKUI_EXPORT PortfolioView : public QWidget +{ +Q_OBJECT + +public: + PortfolioView(AtlanticCore *core, Player *_player, QColor activeColor, QColor inactiveColor, QWidget *parent, const char *name = 0); + ~PortfolioView(); + + void buildPortfolio(); + void clearPortfolio(); + + Player *player(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void mousePressEvent(QMouseEvent *); + +signals: + void newTrade(Player *player); + void kickPlayer(Player *player); + void estateClicked(Estate *); + +private slots: + void playerChanged(); + void slotMenuAction(int item); + +private: + void loadIcon(); + + AtlanticCore *m_atlanticCore; + Player *m_player; + PortfolioEstate *m_lastPE; + QColor m_activeColor, m_inactiveColor; + QPixmap *qpixmap, *m_image; + QString m_imageName; + bool b_recreate; + QPtrList m_portfolioEstates; +}; + +#endif diff --git a/atlantik/libatlantikui/token.cpp b/atlantik/libatlantikui/token.cpp new file mode 100644 index 00000000..6f13333f --- /dev/null +++ b/atlantik/libatlantikui/token.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include + +#include + +#include +#include + +#include "board.h" +#include "estate.h" +#include "player.h" + +#include "token.moc" + +#define TOKEN_ICONSIZE 32 + +Token::Token(Player *player, AtlantikBoard *parent, const char *name) : QWidget(parent, name) +{ + setBackgroundMode(NoBackground); // avoid flickering + + m_parentBoard = parent; + + m_player = player; + connect(m_player, SIGNAL(changed(Player *)), this, SLOT(playerChanged())); + + m_inJail = m_player->inJail(); + m_location = m_player->location(); + m_destination = 0; + + qpixmap = 0; + b_recreate = true; + + // Init icon + m_image = 0; + loadIcon(); + + setFixedSize(QSize(TOKEN_ICONSIZE, TOKEN_ICONSIZE + KGlobalSettings::generalFont().pointSize())); +} + +Token::~Token() +{ + delete m_image; +} + +Player *Token::player() +{ + return m_player; +} + +void Token::setLocation(Estate *location) +{ + if (m_location != location) + m_location = location; +} + +void Token::setDestination(Estate *estateView) +{ + if (m_destination != estateView) + m_destination = estateView; +} + +void Token::playerChanged() +{ + if (m_imageName != m_player->image()) + loadIcon(); + + b_recreate = true; + update(); +} + +void Token::loadIcon() +{ + m_imageName = m_player->image(); + + delete m_image; + m_image = 0; + + if (!m_imageName.isEmpty()) + { + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + if (!m_image) + { + m_imageName = "hamburger.png"; + + QString filename = locate("data", "atlantik/themes/default/tokens/" + m_imageName); + if (KStandardDirs::exists(filename)) + m_image = new QPixmap(filename); + } + + QWMatrix m; + m.scale(double(TOKEN_ICONSIZE) / m_image->width(), double(TOKEN_ICONSIZE) / m_image->height()); + QPixmap *scaledPixmap = new QPixmap(TOKEN_ICONSIZE, TOKEN_ICONSIZE); + *scaledPixmap = m_image->xForm(m); + + delete m_image; + m_image = scaledPixmap; +} + +void Token::paintEvent(QPaintEvent *) +{ + if (b_recreate) + { + delete qpixmap; + qpixmap = new QPixmap(width(), height()); + + QPainter painter; + painter.begin(qpixmap, this); + + if (m_image) + { + painter.setPen(Qt::black); + painter.setBrush(Qt::white); + painter.drawRect(0, 0, TOKEN_ICONSIZE, TOKEN_ICONSIZE); + + painter.drawPixmap(0, 0, *m_image); + } + + painter.setPen(Qt::black); + painter.setBrush(Qt::black); + painter.drawRect(0, TOKEN_ICONSIZE, width(), KGlobalSettings::generalFont().pointSize()); + + painter.setPen(Qt::white); + painter.setFont(QFont(KGlobalSettings::generalFont().family(), KGlobalSettings::generalFont().pointSize(), QFont::DemiBold)); + painter.drawText(1, height()-1, (m_player ? m_player->name() : QString::null)); + + b_recreate = false; + } + bitBlt(this, 0, 0, qpixmap); +} + +void Token::resizeEvent(QResizeEvent *) +{ + b_recreate = true; +} diff --git a/atlantik/libatlantikui/token.h b/atlantik/libatlantikui/token.h new file mode 100644 index 00000000..f0e52f2b --- /dev/null +++ b/atlantik/libatlantikui/token.h @@ -0,0 +1,62 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef ATLANTIK_TOKEN_H +#define ATLANTIK_TOKEN_H + +#include + +class QPixmap; + +class Player; +class Estate; +class AtlantikBoard; + +class Token : public QWidget +{ +Q_OBJECT + + public: + Token (Player *player, AtlantikBoard *parent, const char *name = 0); + ~Token(); + Player *player(); + void setLocation(Estate *estate); + Estate *location() { return m_location; } + void setDestination(Estate *estate); + Estate *destination() { return m_destination; } + void setInJail (bool inJail) { m_inJail = inJail; } + bool inJail() { return m_inJail; } + + private slots: + void playerChanged(); + + protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +private: + void loadIcon(); + + Player *m_player; + Estate *m_location, *m_destination; + bool m_inJail; + AtlantikBoard *m_parentBoard; + bool b_recreate; + QPixmap *qpixmap, *m_image; + QString m_imageName; +}; + +#endif diff --git a/atlantik/libatlantikui/trade_widget.cpp b/atlantik/libatlantikui/trade_widget.cpp new file mode 100644 index 00000000..b2658abb --- /dev/null +++ b/atlantik/libatlantikui/trade_widget.cpp @@ -0,0 +1,374 @@ +// Copyright (c) 2002-2003 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "trade_widget.moc" + +TradeDisplay::TradeDisplay(Trade *trade, AtlanticCore *atlanticCore, QWidget *parent, const char *name) + : QWidget(parent, name, + WType_Dialog | WStyle_Customize | WStyle_DialogBorder | WStyle_Title | + WStyle_Minimize | WStyle_ContextHelp ) +{ + m_trade = trade; + m_atlanticCore = atlanticCore; + + setCaption(i18n("Trade %1").arg(trade->tradeId())); + + QVBoxLayout *listCompBox = new QVBoxLayout(this, KDialog::marginHint()); + + m_updateComponentBox = new QHGroupBox(i18n("Add Component"), this); + listCompBox->addWidget(m_updateComponentBox); + + m_editTypeCombo = new KComboBox(m_updateComponentBox); + m_editTypeCombo->insertItem(i18n("Estate")); + m_editTypeCombo->insertItem(i18n("Money")); + + connect(m_editTypeCombo, SIGNAL(activated(int)), this, SLOT(setTypeCombo(int))); + + m_estateCombo = new KComboBox(m_updateComponentBox); + QPtrList estateList = m_atlanticCore->estates(); + Estate *estate; + for (QPtrListIterator it(estateList); *it; ++it) + { + if ((estate = *it) && estate->isOwned()) + { + m_estateCombo->insertItem( PortfolioEstate::drawPixmap(estate), estate->name() ); + m_estateMap[m_estateCombo->count() - 1] = estate; + m_estateRevMap[estate] = m_estateCombo->count() - 1; + } + } + + connect(m_estateCombo, SIGNAL(activated(int)), this, SLOT(setEstateCombo(int))); + + m_moneyBox = new QSpinBox(0, 10000, 1, m_updateComponentBox); + + QPtrList playerList = m_atlanticCore->players(); + Player *player, *pSelf = m_atlanticCore->playerSelf(); + + m_fromLabel = new QLabel(m_updateComponentBox); + m_fromLabel->setText(i18n("From")); + m_playerFromCombo = new KComboBox(m_updateComponentBox); + + m_toLabel = new QLabel(m_updateComponentBox); + m_toLabel->setText(i18n("To")); + m_playerTargetCombo = new KComboBox(m_updateComponentBox); + + for (QPtrListIterator it(playerList); *it; ++it) + { + if ((player = *it) && player->game() == pSelf->game()) + { + m_playerFromCombo->insertItem(player->name()); + m_playerFromMap[m_playerFromCombo->count() - 1] = player; + m_playerFromRevMap[player] = m_playerFromCombo->count() - 1; + + m_playerTargetCombo->insertItem(player->name()); + m_playerTargetMap[m_playerTargetCombo->count() - 1] = player; + m_playerTargetRevMap[player] = m_playerTargetCombo->count() - 1; + + connect(player, SIGNAL(changed(Player *)), this, SLOT(playerChanged(Player *))); + } + } + + m_updateButton = new KPushButton(i18n("Update"), m_updateComponentBox); + m_updateButton->setEnabled(false); + + connect(m_updateButton, SIGNAL(clicked()), this, SLOT(updateComponent())); + + m_componentList = new KListView(this, "componentList"); + listCompBox->addWidget(m_componentList); + + m_componentList->addColumn(i18n("Player")); + m_componentList->addColumn(i18n("Gives")); + m_componentList->addColumn(i18n("Player")); + m_componentList->addColumn(i18n("Item")); + + connect(m_componentList, SIGNAL(contextMenu(KListView*, QListViewItem *, const QPoint&)), SLOT(contextMenu(KListView *, QListViewItem *, const QPoint&))); + connect(m_componentList, SIGNAL(clicked(QListViewItem *)), this, SLOT(setCombos(QListViewItem *))); + + QHBoxLayout *actionBox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + listCompBox->addItem(actionBox); + + actionBox->addItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + m_rejectButton = new KPushButton(BarIcon("cancel", KIcon::SizeSmall), i18n("Reject"), this); + actionBox->addWidget(m_rejectButton); + + connect(m_rejectButton, SIGNAL(clicked()), this, SLOT(reject())); + + m_acceptButton = new KPushButton(BarIcon("ok", KIcon::SizeSmall), i18n("Accept"), this); +// m_acceptButton->setEnabled(false); + actionBox->addWidget(m_acceptButton); + + connect(m_acceptButton, SIGNAL(clicked()), this, SLOT(accept())); + + m_status = new QLabel(this); + listCompBox->addWidget(m_status); + m_status->setText( i18n( "%1 out of %2 players accept current trade proposal." ).arg( m_trade->count( true ) ).arg( m_trade->count( false ) ) ); + +// mPlayerList->header()->hide(); +// mPlayerList->setRootIsDecorated(true); +// mPlayerList->setResizeMode(KListView::AllColumns); + + connect(m_trade, SIGNAL(itemAdded(TradeItem *)), this, SLOT(tradeItemAdded(TradeItem *))); + connect(m_trade, SIGNAL(itemRemoved(TradeItem *)), this, SLOT(tradeItemRemoved(TradeItem *))); + connect(m_trade, SIGNAL(changed(Trade *)), this, SLOT(tradeChanged())); + connect(m_trade, SIGNAL(rejected(Player *)), this, SLOT(tradeRejected(Player *))); + connect(this, SIGNAL(updateEstate(Trade *, Estate *, Player *)), m_trade, SIGNAL(updateEstate(Trade *, Estate *, Player *))); + connect(this, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *)), m_trade, SIGNAL(updateMoney(Trade *, unsigned int, Player *, Player *))); + connect(this, SIGNAL(reject(Trade *)), m_trade, SIGNAL(reject(Trade *))); + connect(this, SIGNAL(accept(Trade *)), m_trade, SIGNAL(accept(Trade *))); + + setTypeCombo(m_editTypeCombo->currentItem()); + setEstateCombo(m_estateCombo->currentItem()); + + m_contextTradeItem = 0; +} + +void TradeDisplay::closeEvent(QCloseEvent *e) +{ + // Don't send network event when trade is already rejected + if (m_trade->isRejected()) + m_atlanticCore->removeTrade(m_trade); + else + emit reject(m_trade); + + e->accept(); +} + +void TradeDisplay::tradeItemAdded(TradeItem *tradeItem) +{ + KListViewItem *item = new KListViewItem(m_componentList, (tradeItem->from() ? tradeItem->from()->name() : QString("?")), i18n("gives is transitive ;)", "gives"), (tradeItem->to() ? tradeItem->to()->name() : QString("?")), tradeItem->text()); + connect(tradeItem, SIGNAL(changed(TradeItem *)), this, SLOT(tradeItemChanged(TradeItem *))); + + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + item->setPixmap(2, QPixmap(SmallIcon("personal"))); + + if (TradeEstate *tradeEstate = dynamic_cast(tradeItem)) + item->setPixmap(3, PortfolioEstate::drawPixmap(tradeEstate->estate())); +// else if (TradeMoney *tradeMoney = dynamic_cast(tradeItem)) +// item->setPixmap(3, PortfolioEstate::pixMap(tradeEstate->estate())); + + m_componentMap[tradeItem] = item; + m_componentRevMap[item] = tradeItem; +} + +void TradeDisplay::tradeItemRemoved(TradeItem *t) +{ + KListViewItem *item = m_componentMap[t]; + delete item; + m_componentMap[t] = 0; +} + +void TradeDisplay::tradeItemChanged(TradeItem *t) +{ + KListViewItem *item = m_componentMap[t]; + if (item) + { + item->setText(0, t->from() ? t->from()->name() : QString("?")); + item->setPixmap(0, QPixmap(SmallIcon("personal"))); + item->setText(2, t->to() ? t->to()->name() : QString("?")); + item->setPixmap(2, QPixmap(SmallIcon("personal"))); + item->setText(3, t->text()); + } +} + +void TradeDisplay::tradeChanged() +{ + // TODO: add notification whether playerSelf has accepted or not and + // enable/disable accept button based on that + m_status->setText( i18n( "%1 out of %2 players accept current trade proposal." ).arg( m_trade->count( true ) ).arg( m_trade->count( false ) ) ); +} + +void TradeDisplay::playerChanged(Player *player) +{ + m_playerFromCombo->changeItem(player->name(), m_playerFromRevMap[player]); + m_playerTargetCombo->changeItem(player->name(), m_playerTargetRevMap[player]); + + TradeItem *item = 0; + for (QMap::Iterator it=m_componentRevMap.begin() ; it != m_componentRevMap.end() && (item = *it) ; ++it) + tradeItemChanged(item); +} + +void TradeDisplay::tradeRejected(Player *player) +{ + if (player) + m_status->setText(i18n("Trade proposal was rejected by %1.").arg(player->name())); + else + m_status->setText(i18n("Trade proposal was rejected.")); + + // Disable GUI elements + m_updateButton->setEnabled(false); + m_componentList->setEnabled(false); + m_rejectButton->setEnabled(false); + m_acceptButton->setEnabled(false); + + // TODO: add/enable close button +} + +void TradeDisplay::setTypeCombo(int index) +{ + switch (index) + { + case 0: + // Editing estate component + + m_estateCombo->show(); + m_estateCombo->setMaximumWidth(9999); + + m_moneyBox->hide(); + m_moneyBox->setMaximumWidth(0); + + setEstateCombo(m_estateCombo->currentItem()); // also updates playerfromCombo + m_playerFromCombo->setEnabled(false); + + m_updateButton->setEnabled( m_estateCombo->count() > 0 ); + + break; + + case 1: + // Editing money component + + m_estateCombo->hide(); + m_estateCombo->setMaximumWidth(0); + + m_moneyBox->show(); + m_moneyBox->setMaximumWidth(9999); + + m_playerFromCombo->setEnabled(true); + + m_updateButton->setEnabled(true); + + break; + } +} + +void TradeDisplay::setEstateCombo(int index) +{ + if (m_estateCombo->currentItem() != index) + m_estateCombo->setCurrentItem(index); + + if (Estate *estate = m_estateMap[index]) + m_playerFromCombo->setCurrentItem( m_playerFromRevMap[estate->owner()] ); +} + +void TradeDisplay::setCombos(QListViewItem *i) +{ + TradeItem *item = m_componentRevMap[(KListViewItem *)(i)]; + if (TradeEstate *tradeEstate = dynamic_cast(item)) + { + setTypeCombo(0); + setEstateCombo( m_estateRevMap[tradeEstate->estate()] ); // also updates playerFromCombo + m_playerTargetCombo->setCurrentItem( m_playerTargetRevMap[tradeEstate->to()] ); + } + else if (TradeMoney *tradeMoney = dynamic_cast(item)) + { + setTypeCombo(1); + m_moneyBox->setValue( tradeMoney->money() ); + m_playerFromCombo->setCurrentItem( m_playerFromRevMap[tradeMoney->from()] ); + m_playerTargetCombo->setCurrentItem( m_playerTargetRevMap[tradeMoney->to()] ); + } +} + +void TradeDisplay::updateComponent() +{ + Estate *estate; + Player *pFrom, *pTarget; + + switch (m_editTypeCombo->currentItem()) + { + case 0: + // Updating estate component + estate = m_estateMap[m_estateCombo->currentItem()]; + pTarget = m_playerTargetMap[m_playerTargetCombo->currentItem()]; + + if (estate && pTarget) + emit updateEstate(m_trade, estate, pTarget); + + break; + + case 1: + // Updating money component + pFrom = m_playerFromMap[m_playerFromCombo->currentItem()]; + pTarget = m_playerTargetMap[m_playerTargetCombo->currentItem()]; + + if (pFrom && pTarget) + emit updateMoney(m_trade, m_moneyBox->value(), pFrom, pTarget); + + break; + } +} + +void TradeDisplay::reject() +{ + emit reject(m_trade); +} + +void TradeDisplay::accept() +{ + emit accept(m_trade); +} + +void TradeDisplay::contextMenu(KListView *, QListViewItem *i, const QPoint& p) +{ + m_contextTradeItem = m_componentRevMap[(KListViewItem *)(i)]; + + KPopupMenu *rmbMenu = new KPopupMenu(this); +// rmbMenu->insertTitle( ... ); + rmbMenu->insertItem(i18n("Remove From Trade"), 0); + + connect(rmbMenu, SIGNAL(activated(int)), this, SLOT(contextMenuClicked(int))); + rmbMenu->exec(p); +} + +void TradeDisplay::contextMenuClicked(int) +{ + if (!m_contextTradeItem) + return; + + if (TradeEstate *tradeEstate = dynamic_cast(m_contextTradeItem)) + emit updateEstate(m_trade, tradeEstate->estate(), 0); + else if (TradeMoney *tradeMoney = dynamic_cast(m_contextTradeItem)) + emit updateMoney(m_trade, 0, tradeMoney->from(), tradeMoney->to()); + + m_contextTradeItem = 0; +} diff --git a/atlantik/libatlantikui/trade_widget.h b/atlantik/libatlantikui/trade_widget.h new file mode 100644 index 00000000..642cc919 --- /dev/null +++ b/atlantik/libatlantikui/trade_widget.h @@ -0,0 +1,98 @@ +// Copyright (c) 2002 Rob Kaper +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License version 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this library; see the file COPYING.LIB. If not, write to +// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +#ifndef TRADEWIDGET_H +#define TRADEWIDGET_H + +#include +#include +#include "libatlantikui_export.h" + +class QHGroupBox; +class QLabel; +class QListViewItem; +class QSpinBox; + +class KListView; +class KListViewItem; +class KComboBox; +class KPushButton; + +class AtlanticCore; +class Player; +class Trade; +class TradeItem; + +class LIBATLANTIKUI_EXPORT TradeDisplay : public QWidget +{ +Q_OBJECT + +public: + TradeDisplay(Trade *trade, AtlanticCore *atlanticCore, QWidget *parent=0, const char *name = 0); + + Trade *trade() { return mTrade; } + +protected: + void closeEvent(QCloseEvent *e); + +private slots: + void tradeItemAdded(TradeItem *); + void tradeItemRemoved(TradeItem *); + void tradeItemChanged(TradeItem *); + void tradeChanged(); + void playerChanged(Player *player); + void tradeRejected(Player *); + + void setTypeCombo(int); + void setEstateCombo(int); + void setCombos(QListViewItem *i); + + void updateComponent(); + void reject(); + void accept(); + + void contextMenu(KListView *l, QListViewItem *i, const QPoint& p); + void contextMenuClicked(int item); + +signals: + void updateEstate(Trade *trade, Estate *estate, Player *to); + void updateMoney(Trade *trade, unsigned int money, Player *from, Player *to); + void reject(Trade *trade); + void accept(Trade *trade); + +private: + QHGroupBox *m_updateComponentBox; + QLabel *m_status, *m_fromLabel, *m_toLabel; + QSpinBox *m_moneyBox; + + KComboBox *m_editTypeCombo, *m_playerFromCombo, *m_playerTargetCombo, *m_estateCombo; + KListView *m_componentList; + KPushButton *m_updateButton, *m_rejectButton, *m_acceptButton; + + AtlanticCore *m_atlanticCore; + Trade *mTrade, *m_trade; + TradeItem *m_contextTradeItem; + + // TODO: Wouldn't QPair make more sense here? + QMap m_componentMap; + QMap m_componentRevMap; + QMap m_estateMap; + QMap m_estateRevMap; + QMap m_playerFromMap, m_playerTargetMap; + QMap m_playerFromRevMap, m_playerTargetRevMap; +}; + +#endif diff --git a/atlantik/pics/Makefile.am b/atlantik/pics/Makefile.am new file mode 100644 index 00000000..e0fd3547 --- /dev/null +++ b/atlantik/pics/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = board misc toolbar + +KDE_ICON = atlantik diff --git a/atlantik/pics/board/Makefile.am b/atlantik/pics/board/Makefile.am new file mode 100644 index 00000000..1a6d0d6c --- /dev/null +++ b/atlantik/pics/board/Makefile.am @@ -0,0 +1,2 @@ +boardicondir = $(kde_datadir)/atlantik/pics +boardicon_DATA = arrow.png qmark-blue.png qmark-red.png token.png train.png diff --git a/atlantik/pics/board/arrow.png b/atlantik/pics/board/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..167d47660e1abbf18f789bb69d04ca2a4aea6da5 GIT binary patch literal 674 zcmV;T0$u%yP)%bFC^;H`TPO zXhZ=UdltrED4C?4k&ID3>B%-p(C_`RpF>J15D~Q2?dXb#dh6lm=V)TfsN7%U#H~PByabcH3J|En_r{yt`&*-8`EX zwWFgY#LPNL5+6)o(&#B<;j`ywFHm36r&Fkfe|Z5`D^FPGD{>oI3;+6R;N#dg-$}K1 zA!gQTnqv3<-oZBx5Yc4MSZXq$(=Y=C*B(GML| z%9M?T9f3z#f~yKkDRCnumQ{siRhbMZrHGleP7?fFulpr5%z#@qZq)7CjXGe!+$;?v z?}GmYerQxxpt3X$n0IR;B28(E-Q8Uatm7E`^fb2lC!@BJStoRoAiKZsGJt8{g?f$5 zIspLL!$UZY%)JZQJplmOXne|OejwL0Z+QXDFh*IYU6o&>7pGn2o@ipQ+xxdy)LZ;OZ46}v4r}AJA|B^^W~m@u)f5+Ga8Ph0#A7cV!Y7%(M}4WeZ9$`V*n>|9{>}YOzHk=r%U=P zOtL@pr3p;{yzp}2-RrPI`(~?HfcdrhZg-)u{BR&t{jEF&{{YGWwqs*Ec5VPtMOd>w zfOh~MI(?<-nF~O0agb}!q*h)7%5xy8@5^cQ^aAV^50JK;GSuL! ztbI(&EEXVbIn}If8r9Qcy64b&6b6wnhy*Y{nY45pE;|`BrJ%^kSRQ$ym(0JIJu6GO z2K;a!3?gym9%B%pbCe}+c7VK9et_8k(pqk7O}nA=`UD$^YOX{%97g~E002ovPDHLk FV1jjivBm%Z literal 0 HcmV?d00001 diff --git a/atlantik/pics/board/qmark-red.png b/atlantik/pics/board/qmark-red.png new file mode 100644 index 0000000000000000000000000000000000000000..6aff93b3f585b54cfb43eac1924ed0ef751e556e GIT binary patch literal 392 zcmV;30eAk1P)b<842J(!mAbWCr?w*!ggxUsLnI0lH()Q*lgbfjL=TWL zNE`uWszBY4!UeLBjT5*74UJktpPd(_Rr#c+yuq@6Jv+96|IQi5*q{59CMhd`<2vXy zdWCT=Oq{&UeY#9iOdls$FJq&?uK*GNI?zY$j0D_`L)^?Fw2eaV=mH20?9LfJE5Yam z@^nAGho1Io8RPzWTCITWS;PM5TVbsNOl<; zKP)1p*m#NvAOZ-VM?~r0uxw`(rJ%~r_;=8|N}fHLwaa5kOD<5eUG8Bx)G} mwy9kJn=A#{ku0JoeiiSW8Eb=Vh;M8F0000#*j@Ad7%!+>aa?O9kymi_a0 z&i&3Y0RL-10Kjk7?>yuL$n%gB03RmICT6YWbb6%k>)E{Ld7XAglEru|F{T$%snox) zAmjl6Jmd(YLq4^AU=eJ(xa-osPh8wOZn~~(S{8($)ois|t(mFmWHK3vL?DD;tJ~~8 z_ZcCK3XqpE-`=S^HCs>TR7J%YyRPebp6~mJR`x z06Y&l8T0k$drM>U7-P@Fp63xlBuP3rIB2z6yfaZ>3xWUufDrP1pHe!kV)*@ui3yJ5dcEGgd-n(-A3uKF+S)ogI{J*` zAqOnduUSFB;_-@JJs*b%U`ZuC4_{;csw47L_!t-pj0YdyLQcS90*|$ z1nqWvcYkkyb2>e*>xPDK97hzza=Co@@@40X_D@VqY;SM7t_vZ2_Uze}D_3mW<~WYy zI0zxe`1s^_fFpe$d$`~4d!84I#meRKxpU`C(+s;71VIo47-LzMLuupV!nO5x2)rLwZJlFeozgpo)j8jT(t9PID! z*J?Fg*JoyCFvbX>lXi0?7t??EW>J|%p_P)!-Bbbchl*# z<2XFe&&|zwp7-|c+ow;TZftB|j0Hi+<#MKJYMKTB_|V)-pV5U;nFsuj+5#G`AXQb@ z*VhF>5Coyq>4d}3?RJyNBx9`6Xdr|TLLA4LrYVXdfbc_e@6>#rC3v4vACSRO42dHn zBde>clarH@BmoE+W8H376vZG2Y}bqe4pd84X!Hgi)Un9}x11_;d99KRN#?{su7vN_8t`W@-Qc002ovPDHLk FV1hk9uRQ<& literal 0 HcmV?d00001 diff --git a/atlantik/pics/board/train.png b/atlantik/pics/board/train.png new file mode 100644 index 0000000000000000000000000000000000000000..4603e537a688e5c965b18920e40d20d81fb4913f GIT binary patch literal 1099 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 FV1n`~w(tM| literal 0 HcmV?d00001 diff --git a/atlantik/pics/hi48-app-atlantik.png b/atlantik/pics/hi48-app-atlantik.png new file mode 100644 index 0000000000000000000000000000000000000000..5a259675763e895dfccf654e1395feae934caf35 GIT binary patch literal 3791 zcmV;=4lwbFP)RB79aU8HTBQfsrjA`K)$up)tOhzNv) zys-(yn5@@!JY##_-)HVUryuUjof*fm*{Z7c8C~5u=iGD7^FPnad7g6w;L~;UKHyCt z%x@P>DJ7qk(!L7x0CPYNC<6|Fq3h;-;c$55<(L0HynXw2y1VNbu3fvv)yP$z+V_-K zH#RoL!Gj0A@`)2C*uQ_jS3Z6EG<){!Y2xDTx8G*(-o5z!egKXf`3X-y{YPH=v17+L zaNxjg+|SKL*X-T<2g8X(d|pbYt%3PU?HdDl^W@35!@;#J0Hmz6H^5!LH^9}SSBZz> zUR~PAFl)|w<(P;uC8hz;gkX+&;(qsgLo)y<1MVIV&eGP__MK!h`F(J%04X336l;Aoe*4s>iEPboSBg%c0|)^opaTM23DDgg_Evu0 znQs~(CDTMI(R$fVF_oY8$_vF9Q`sp1bfGg>m}9Qwj^WdN0OXt;#e9)uFbP0jTM7WAWg1YJ#TWn!MH4Gvc^w?v!M1I$ET#0kjjrjG?J|aL z5b}r6bPZkTo@&*mm&hh{NvG2keXe8a3Uqn(iA;hY{q4VzoJ;(Nl+HEa5-7b+ni_hDmFv)dQeunx_DYW&jnt za+`rS@uJejYs*@-{x!}IM`X&B=pH25{;?f6~P!&acM>|^8 zZMyv4?m9ij)$L3lm~umjWB+>0t4rlll=8(TzxMzLAvk#O;2mw?2NT;-(w#K9kmSat z8+7+|(-H2#3RrCU-7O^Nl8j#-=k6VMdp=*&H1EFGWqh#>9R0x?2$AMa z7_{XKq&A6CidZy8EE2;X^wS=0r=_h0B^BjD8BNph2mH+vXxO}zXx^xrR7u^gMvdyF z8~U>RY0I{{qoGhN+*%+NOEEYyNT4-EXH`XwJYo3e$eUagv!;}kUZcW@` z)3!}?^>o!&yvlUVi0kXHwC>mF(fLOKIC=D>SC`BtDdmeS-uQxYQwZlSMO^ovZn*#?wCN{N&bD`2r^(;6Jx!7vO4)(_zG`9J~^*Vvu<_tJ46M>ZOEQY2Cgy8A>TmpFT~t#SP{XcEVL5$5kQ6b|~hHl!_%Pr3!Z0_QD5U z*D-VhpXP=UhHj9^CNOjZvuvX28k(VD_zVowKr=OTLr3#zbtA9Rx%bX_|7%i-r{k0g zE7$Rg@nAu(;ok*jkqL3?4DONzbMrvqos895Uz@qDkZLbafRz&U8kk3 zg>))Sdv`lKAKZzqyDGROEt$I90f179%a<>c3c8`hTw#uKu7oxqF@r(2ZCFP*7$#(d zuyhM_n4h0#Vq$`ci3tFPhKAU%VFNuqJ>IHHDT$}zWacx>-JIjs)4!%~s1L(5`25o6 z4DT4mXWhZWG)SOQDlZQJ6f-k3q^n7NtPsa3IP`DnXHCx_ecgQkl!_%v)!cY`dYa3Z zFZ1!o9|N#+=T0ok!m=zhO~bZrvbiibzP!Qg^eoBQBp;pnh|#A;F-(KVRD|ySZmgi? zN%Bws@)ZDnalGykli6f-KU#Jp)7ZCfFZ2Ed0A`aBL{iX38iVyN3g*RElUcij*wnQNQ5N2YX4^KJrePR{=Q$k5p%{y}j6)E2?xMLnZ2-#k}>_TO2=roKHUagz@olbY17g7hgn5$;p!^ zIez>&7cN|&udk0vrNZ;iKY!ZYLsbD=bk)dLNNBcfxtmB;@fYGT_sk2}y?ZyickgD`u3dC>bul_R zN-mefwrxg6MtJP8$JnxE3!zYm`|i7MNdSRBfKVue5Q5I$PC9xzC>2U{e9?i#{a*c! z;#+^V@3ypaE;h&5$_HD5O`A3oAn11f6*ynjMXHqIi6@@GaU75Nn!DV#Z5zYG!(Lr2 z1kdO5L?RKoy1Fn;lVC825Q47WF2em`7H%yxF>%M3i2r3m(^$9I&)1$xrLtrhAR3Kw z_Uu_6eDFa!J3GDc{6Rm#)*x2E;^9{w_Ucc*e)8*NXObm!$$;u+*I+o{x=JAgV`F1{ z^wCEQ4i0kY&>;-N@ECpLjW@V>@ghAvJsdi8h;n)Pz^g0VMNfYZ$%SN7E}&2>J^)xm zssTV#qXBSz;d7F@o75M|1uQJ2bWbbZeDh7tojb?S&=8eMh3B4oj!LD%nKNg2_uY57 zbms>ecMty_>UV&vMsYcd>2Twk4NtVq${NKKl&Ymi+i1e~Rh%Q7%?E`olN8 zZ;II>W8Xc3Vcwo!CFc@{m2%Ek0c5D(5xWwQ(!Lt~RrF^KeN5nK$8p%Ybt|P(iII^J zIyyQS9UUc;$xx|O*t&Hq`Fx)B>(>(sg?Q+phsfpJg8|F32nK_E@x>R+%*;oLdra)8YeprDT#t7kiB)fL7egvv%Om@4fflKYaZ0$2T50aDe{)e$NZl zJW*r07VJqWo4nW2qeuDl(@&iX7cNZ4LBF-=WP5sgMak3=GG z1DAnmT(z8~Zivgr9L*pq4UbKO{?`E1bW6j%NDcyBz_)>6>UXnC-V)wn($9fYKoVDd zY+NAZ_50N^gjvQZo~XFd9DBe002ovPDHLk FV1m0BQZ)bo literal 0 HcmV?d00001 diff --git a/atlantik/pics/misc/Makefile.am b/atlantik/pics/misc/Makefile.am new file mode 100644 index 00000000..9652d96c --- /dev/null +++ b/atlantik/pics/misc/Makefile.am @@ -0,0 +1,2 @@ +atlantikicondir = $(kde_datadir)/atlantik/icons +atlantikicon_ICON = AUTO diff --git a/atlantik/pics/misc/cr32-action-monop_board.png b/atlantik/pics/misc/cr32-action-monop_board.png new file mode 100644 index 0000000000000000000000000000000000000000..c610aadc29495e6c0059b42825185345fd2f84bb GIT binary patch literal 1257 zcmV=P)ohLp-rs|+ZaD$I9OQ$cf^NXEaK69BJ zpYK3K5Cjon*Van`54Ml7QuJvTKfc>|xAY6PWyGlE{Ne&5FW`1c2l8SjKK%%J43F znTSmgz{2b4?y($5K1-sMVpIQS?ZA!Y8-`$vd672AYQwYE1HG0bXfXYrFn0xpNz_m`DHpg(_)}}m%Z!O*WLWithem1-xc>$Om zoaOr9bpVTjaDNzp+CmIuz!OgrY+JpJ_~UpE0~xOivIZ~_n*dT0&(%PYE!c24kCS%fcA&Q| z+{!XAJ}?fz24MT9Mo0~;OyRBu)Vi*f@cnQO1J(PgjdSSEv|{gJFS`%#2Hd!sF<4CjArXa8IKxP9?9bD=pPYb6adf1hMRVOxu1*oE*309qY6BSJZxY6HLCk2Q_}~bELVijtqKuMyB)a` zTr<%7EdUF{m<~iwMTyczptp_K=~#}M-6AWLosw%1c zzJmGCykV>O?gPV-j`bZ@A8;$&9_gj*-CauQCGeH|GLM^Ucjgy!qRwXn$NK=l^x5f* zIZnf;4a}UIp}wP@*}+-fdEa`Ed;7f+M1<7&ls?3Ex+T&A0&}4`ZS#&>FQN;>3v{gS z(9aD1G3anWgvQSrH3NxriEPu!(?o3Bg?kscIds$Pj;{UAVS_q{{q_0EKF|LHHUT@00000NkvXXu0mjftgK~V literal 0 HcmV?d00001 diff --git a/atlantik/pics/toolbar/Makefile.am b/atlantik/pics/toolbar/Makefile.am new file mode 100644 index 00000000..76f0a0a7 --- /dev/null +++ b/atlantik/pics/toolbar/Makefile.am @@ -0,0 +1,2 @@ +atlantiktoolbardir = $(kde_datadir)/atlantik/icons +atlantiktoolbar_ICON = AUTO diff --git a/atlantik/pics/toolbar/cr16-action-jail_pay.png b/atlantik/pics/toolbar/cr16-action-jail_pay.png new file mode 100644 index 0000000000000000000000000000000000000000..952b0ead1d8afba9bbc941eced924d6796bb277c GIT binary patch literal 1025 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 O0000 literal 0 HcmV?d00001 diff --git a/atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png b/atlantik/pics/toolbar/cr32-action-atlantik_buy_estate.png new file mode 100644 index 0000000000000000000000000000000000000000..e1a86e47e78851fd4c00925f84a0a83344a3e18a GIT binary patch literal 643 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 wr3a>i_@% literal 0 HcmV?d00001 diff --git a/atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png b/atlantik/pics/toolbar/lo16-action-atlantik_buy_estate.png new file mode 100644 index 0000000000000000000000000000000000000000..3007a2df0e55266bc4175f269da9e881c55d9a9d GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6-FJY5_^ zD(1|c6v@Y|$m9H0@Vl1nO&;^b>c#d_5?oW3PE^`>y;b=_AUy1 zti0Z);k41Mo2KdtHd9&uF*XC*GR8NXKF48y}&0_m}(t>3#gi2(V hvAlbp8)(vHe!M1e``5ZIQJ`BHJYD@<);T3K0RV}3S-=1Q literal 0 HcmV?d00001 diff --git a/atlantik/themes/Makefile.am b/atlantik/themes/Makefile.am new file mode 100644 index 00000000..dcc4764a --- /dev/null +++ b/atlantik/themes/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = default diff --git a/atlantik/themes/default/Makefile.am b/atlantik/themes/default/Makefile.am new file mode 100644 index 00000000..7890f6ec --- /dev/null +++ b/atlantik/themes/default/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = tokens diff --git a/atlantik/themes/default/tokens/Makefile.am b/atlantik/themes/default/tokens/Makefile.am new file mode 100644 index 00000000..17e38396 --- /dev/null +++ b/atlantik/themes/default/tokens/Makefile.am @@ -0,0 +1,5 @@ +icondir = $(kde_datadir)/atlantik/themes/default/tokens +icon_DATA = badge.png beachball.png bell.png bomb.png cat.png cookie.png \ + cube.png eyeball.png flag.png ghost.png globe.png hamburger.png \ + lips.png puzzle.png pyramid.png skull.png traffic_light.png \ + wizard.png diff --git a/atlantik/themes/default/tokens/badge.png b/atlantik/themes/default/tokens/badge.png new file mode 100644 index 0000000000000000000000000000000000000000..e0b58dee7d7770349dde12df8b790427c5a402d2 GIT binary patch literal 7365 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 Vj-<)wx8VQ)002ovPDHLkV1iM)nK1wW literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/bell.png b/atlantik/themes/default/tokens/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..cf5ac8c215144d6f002d7b17ccbab0f41c48fac4 GIT binary patch literal 7221 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 B2|NG* literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/cat.png b/atlantik/themes/default/tokens/cat.png new file mode 100644 index 0000000000000000000000000000000000000000..78b51245725c37c5b52568969d4863817cef7f87 GIT binary patch literal 7819 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 %F(y`pC74zh(eo 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 literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/cookie.png b/atlantik/themes/default/tokens/cookie.png new file mode 100644 index 0000000000000000000000000000000000000000..8944fa295f33aeb803713e3dc5ecbb8e69bf9382 GIT binary patch literal 8472 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 XLquhvf3`>h00000NkvXXu0mjf&=E6| literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/flag.png b/atlantik/themes/default/tokens/flag.png new file mode 100644 index 0000000000000000000000000000000000000000..d0b1f15f8e6f477e59041a117008179fb38027b3 GIT binary patch literal 7436 zcmV+n9rNOeP)xX&=_B{K|?_6J;n!D7V zoQa2=0yP>es77gFVeW!r{ksw1cOMr+r+PaEj-R;d=YRfRZgdw?Tb#w^GPRh)aD5^u z_;^_8_(oXtpBcTN+O(eDmy!dgyO#Iv{qnnA!`ou!p8M%}v559c$Qj z)nuZ@>Swn?G~g-|Q>B+eQ9!esbOQ@x|%s z713Dql2`A$|3ku)J4=q!qM0U&5327XG#y6>yjX}>(+bL3S5Uoq6O9|!6Rxen5e6Am z!xbKC!9icjEBeer>xGhm9BndX?pS#&;01C4_*65UzZo7u;sau>ieQ%erO<8 z=s6ZU*L65tI6E4b*#f@n=&t9iQ5|cmJvi8rkp60Cc`+PBL3je+6S#^>RtpfRt|Pgs zm4>YwDci6XQCp3r1<|!A4mufW5LY$I3n`X{2bns2jNW6NjQRNu7bKvpq9GekR4yz` z4FB!)=(+vC^xG-4uXnt%r>AGtkt0W%9ouO~NwgO1RLiYj{NwskDi?|6(!onLHyA0U z^pNd@r0oKrC<5Pik-m=<;Gkh^Ibt)3m(F z_KweQ`^bO#CtpfuBu5VSD5|Q)CnqPj?%%)veXeV7DHaP`Z@u-l8q+d^j_nwlZYV`N zL{OFN+_o8+aF{;u3XWpq+j$BC&<%WDM~}zQ<1u2@)g)_ci8r(otg0kbRgR2?@q+<8 zA;>8jV4!;{amT`+pJ#bsfc_JmEDn$0XH$eMn~0_o@jN`+K@aH^Y^(0|o}OA@;=BSH z+O}D}haR42srWr!e({xC&JK?~4m>?Nk<(B0oUTe|(%ZiC-8-*dm``6ZK032$X(_D> z#lbLCT3cJ0pD&=Q0j|9IGFG;?bJupC_wFmM*j&atjzEjxB><=pTTswU{}8KnEv83I9#>#wq2+8h7rZwtWqz3+a48O)W293B3500$dpz_|3~* zynJGra7EKLMhEW56)aY)XvNTzSW1?*mF-;hfy?RZ??WgGXU+`K-#?ntm8uh7VoNR`CdKt5n#kGn!W&ztW(M%V`^$@;` zf`_U|G~dCO9zuk0g-S-#pe&A2mY}S@o~p(MqV27euWU!Ftilh3F%j7LJi2D$EvEN8 z_7}GsdvE^>`@8~1$FigQetxJlxf~6xYgkDxRU#TQ`0VFD!4H0LH?D7UZg`k`?wuhJ z4Ds88HLi#>Ey?qo-6HV7x-M*4vKJ_d)JICs#IzIR5oB7Vyzs5_CJjf1J zpt%m7?*TrlDiBBvAyHHnp{uAy7$p=$#3JYw2_jV$M3Qxc%d61K%P=a-5s@fLD1_@t zgs+m-0|;9sCKRHML%yeK)wT1cTMUh#H|gaP2M-=Y(|yK9$0!vof<}numKu&9>%=N~)Ya8+^WKm1{qNs}y;NfO z+yu9O`})4DO41pA`l2q z0zR(v5W+{O0?#o~TnCxAuojoeO-wU9G)!jb9M1e4hUXDbMVVh;eHHNFc?Fb}C#PH5 zS|(9a^}W5Fw6(WWS6jvFuO3G?6im~?Fbn{a$v8)k9>q2x90_sDAAXWM?|6Wj=|!ez zruoJrna`0rluykySrJjVglqaze&#wg6sQ&?ESNvBaP3u#*Tj*Wo<2z==Q5~)ZK5{blBT~HK+RM50A zA{au6M2S{bQMPV9kqzq$!;06q_vYVEzi9^m#N)BYw{G3^^^xKI$=SJCo_cB)%Pw-| zo;_4ll=0w?AI5Qfe*EJ{FfEfvB*eAXeu(E^*pKfOSX!EAbaWgc1kKIOlu9K|oH&7| zY3$jvn{;}SUp{e$GcUeQt1K{Z{1DlZam;iI>;j4>F*FSv7hM5Q_&CCckcJry5{M-T z$KnJdar8)>P%KU;7K4O_9tolwK?0EkN+b%gH=ZM`fP$wRVSQ-iTHulMb^yQ+?|bY= zKfL4N&tHA@4J<9>85rn8Q3RD0Rm{y#bJbN>GBZ2J>AnGUL*eQVT*dL@-K3V6sH&=9 zer^#B!3{Tjl&Y#EcinX#0D(XN*KrU|fp#m!4s(e*GlwQ6CN8ex;1I?{N0dcLwymJD zy@f<$J;7uWwW1a+96=&bbsZ@LQiwOYLVe@Tp}7*(mEid}u8Zrrc%=dhb90PL&754n zW$TrjZ@MA(rX2vVqNVwsWO?lKM~@z)BH2VsOCzyZjH5?8Q52U$D>g2dCqLWw+G{^pzkBza3rNJ1ovp1aP8~eZwNWL? z+KyJ*+gEb<@CgC|jo!W<(#s`+!5Dk@?&YPIUZKBln1R7TZomDTd=x!EL|4#sPjzu_^F8+`z`~mf zNG77@y48)3z4GF(H>&DNCdL=&J2ix56}jdkA0|;2W8b4c!?H@;dB@$ju18f>E!SUv zEkF6mJ`_bEl}gjtSdZ)al$XbN>ZxDj`z|f5E!_OMo4NPz|2LWD^<4R-TXA&{3!joF zP!$E$^+@JT3jJe@9X-m_iIe1~rqPOd0zwe?T%`1ns>D)UY(v2+ixIA@CRtNUqPh;F zyaFYezz>B8M4|)(8i7y%qLI}_+q-4tz~TSZdihTIuL@Y(-oRszJ@$B8OU+kGW{&9I6axeOxUNq;9wVPG@WS)^35UYm zbkoN;dGaL3PaI=>Y?Pn>_-ADD&^b|HEaOv`REgjtb44T2NOJ@2n>t9YXdv3qh*6%z2^u)620}r23Yz!EUBfktm=(lIEcMGjp|q!Qjl~#bfIGVKDLzP5^<7ktJr+WcB(gRM5}M07z|Pp0@HQy zq>JwR1adj7**TVmhFKgLB{wyJn_5Pu3i!695Q{Y~?c913aQAQP75`iSz@2yg=riB= z)(`J*Y+Ns*;c_m&Vmrx-7~lT(w+V-0c%B3#)zvk8@r$>zZ{K4qEG!_U#83-ZFJohJf(UU4cYU(lzpiMllqidhKdf zZr?^?!xjS7H8{GCBUC)8ph*qY6&R^GCQo)UdHf`~bHjLx3uumsruay|fa*(BPs8;M zvf(K9n%3SOpWC~A`zLO)-&DZy6W!Hc{p;I1r>Alabv11$ib^&+PbRm_%{Tucy{CFP zbm$<#V31@oiQ~9z*|M3PJ9ly4-47srNli^9nyxT4HOZ>gD;Xafqf`{^`M}kzYpLah zAAEytqDZS^qj(;c5+hVoPhw>=_1iX4xqb~sT{WhvkQW-BF9}JXu zOMRWle2$<{kd}p_2!tYVd=IIpZ~S;_Zl-GOIx5$$Bf6^1S;oI%^RCOEeNzDdfAZzO zeBg=4pa0CwpZp{0>gxIBlRqQ1JWo89z_KhX%jA=L_p<*tzagDY6OF}aX=!2n+&HV+ zS7F&D&YT$}o``bGEq}nX&pyNO#0)|YP+cA;l$m8)d61oLD`;(ML2qj(*3^iI1n>+6 zNB9UuK}boo=un!OXW{H1<0np#9~r@&nI`18gcTRhb#OfeQ&BO)A!KzWO`A7RwQ&o< z))tiNN_71W^=?rlw{V z7Z*q^FA|HzFwG*K?-GfIsi-KYzrPQ~Q)p^##P?l>hlePYiahej!&H`+am^(itZZ$k zp|zE&`ZAJbI8bcOc&dVehh|!6xeSGw87BJ&$n^HIJUohDC}MaXnlI6%htLE$ zPr=qg7)|xmZrVh{)=P-5T8$EoQdGdw6fgn=Y>!~EggZOQ=#kei@9FPr21ehs1G-KR zXkYv4*T;tj7waO?N;F;P4?gutdQNrm!n4ny8#+=^@sMoTxQXAp=34Il;a%XG2zXZx1TtZr+ey1bllKtZSqmg^veLs*A^>qFL}v@plQ#29mDhsaG#;x8=X%rBrj z4gpa@R|TF_uskp|9WxjpT3<_3#}-<)Y$e#f0&g|(vW(N96 zO-&$+i>R(cR0$w#iH3$JdE;8vz}Hojc!Eev3pMLE(71go;o3S%!6>e(;rlK@UBmEw zG~1=Pu*l5GZq6P&z;gd-l8%LDnt+0*>MSJ6o?P*f58rg&?W2l{%KJhg{Y#ErP~Z2y zoh&UcQkq!ALh$Psp2aEUi3o=+>s#1$>2|hm-b7>@03EPe8f^e2)r6LHHoG0FKZw!#Xk?B~(#CT~jlat2R)%vK_S|j#`z(RG=h1 zY(+)KCE!_zOcr}~p0TcOrn-B`&CH=^^Ms^FjcX$v8^thiR2_m58n$h}w4$Y@;=BSH zn;J*LVdG#vU)sHHO*4aMEhZ;yG|gjid4dmL_aC@q>tF`jii#Bo;77s)>YAut)lNn03LsZo3@qJXsA#9pt=jO-{4>8~0&(gV3+=V67l8GTCNhH265sLE0tFmYuqp^;LZQE(P zbO*tf)|!cYw&T2)0Kg}1{LGhoPxXJdx~i7yswy^Z+Q=o_w$RYhgb@j1Sw(c;$FN;A z)5e>bV7#-F*`8iX<6{`b90BQIXaYP9%NO{;2+bRJF~L2;lPKkzT$>NEQo7+ z=vIM1YLVgB4lsG(0M^(rYCenZJ6MWKKo8(q65BVB$qFjBZ(`k^%ZaqKqDJGmzK10g zg3=@4m&nY{vvlGx<6WKPre|=|X+oZd;d=<%Ca4K~O~Y0dtZ*0^i4w1?qq4r9Xlo;h zhC0H@Bzio78jhkGAza_b#zH|5cLavx;mk~P?&uL_&-9ZWKgX0R-}({&*#F!!A8V|t z{^`nw6x#wR#n?70sy03Y173 zPpCMG#KuL_HGIz_C{$G2BRju{m!73GIm7J9A+qNtC@jun*;(|MiXRQwW@AI@f&_GR zort+3BfW(~N1O8p4-;4_;z$o$I9Q&8ry7Vv1&y0GQ+CM)V$DtX@dSDPQHbZDc@9Rl zz}%^RW;?sd4W2=yQv@}MP<>2a!B-Si-5^p^P2<+Blx@3C966uLati=V)REok<3g5Bug@Ugep061*j?m_P-RKj#dLmaI?#;#{ z6Bi@^;EvDV`fYjqYWM+PZ~5yXWqI zG6HbHBQh(i>mNN!-&cyBz>5cIdH)qu?b?af*hEPWQdBg2-zVT$$hmozx=u2A=rE;; zNlMx58*h+ZAJz4-T$i-2;lx5D*LJXR=MEAb+c9byv4R1zs)`{YECi*QX(nGk#$;C? zh1q#z$;7e>D6s%uNF!5|M1&)gfk^m3(RELh@Q;VX!GV(WvzF@?zxBYQ=iTnVU;)wc zvQwq%#KBT|5GKcu3=$*8Cz(?P*AgJmIemtfAJMYj~_$JrHCm$n&S}Ebn+gg zgn=51Qr6T++vZJFZR#M-jAgddK=^=%O?OE8u$I*4%K!n=1Rn%|3jOw-P@XBKp41uL6sES5F zQ89`s-03OCUq8s?i7vcs5riQG<0%^v!r%Q)0E7 zI$kVFQW{>lHnYUppZtRHmtVu1pF#I54C!Kf5=Ygsqj8Mp23G9YO4XJQD2pT1I9aJ6 zg^wZ~g84ko{33(hr&#Jbf;lxq&{tg7@kfdQVTo%ZdJ3r2Sj)OE&V@#5_fcri0|S`kF4!rOMz@`WYz8~NOp9fH#Xt~4Q$sz6&{Wv zQSt@sp$QgFbg|e!KyG0c%PKfwU+r~;y1$?(&lZH*n_Stp@F&mx{GyEOoR7C90HEv1 zUwyRud*6McDx1@Sw#1hT*>D(R-5OS3^?s78*Wrc&n7V@L*+hhflFCw=oM!gqON@6O z$DB#y=Jf- z{$`ZwYCKmVZJDS-60vQJnHgq|bu#e$i@5U(Xtw3s3JWEj$8%BRA=mLb|N89Eo9^Ae z7rbo&n?L-K+zVg*%O?kC%(a*O;iqW&_(#aAK91!h6puu4nWa;`^uKtJ*%LijnG{m< zOc(b=!Pg#CB8f-8(tG${Y*GDN@s37i8+YuyPd2Rla&pfu%{2t!l7~Mz$MEsv3_bG; zQX``{j*n{uO8Ic$xq|M0UnZlk{mqN7y(PjYGc!|?b zJc%_uN6E8EtI{*0!DsWT_+GNP>GeOk|ABwgHa{QlcvxCB9i?GTxs)2N{k z>4e5&MfISA{8Auby!clSJ#1afWZwhyL>lc0detLU+Vy4_I3o%-uTVAZ}9#0E? zb+GMJrfkKkj{AT5=sS-0Z^iGFaD4mfW9652oSrD9F@0}yGM;F=Sc$z? z{7wQsc+*F7O|30ItFNeLVR*beB-Lv#R$}iJzjMoOXV7K=HC$H%q` zAuj0eD*rEVQ3UMTwM#ZOHr`)TQ{x>uazrT>i?>{ixbFoQ?ZKtIy!>Q)d;9Uzr%#j5 z=WjZ2;6U_Z#eO%rXacUe=9+v{Q`0X@(`001q(Mr#=VHZvH@T>fu5E2?Pc}3(qz)cD zsJpKF$%`5L-QuDOXl-rnU$tu0p}xL8(&_Zoot>S{7c>65!T$w6z0_s+GpI%Y0000< KMNUMnLSTYYTXDPq literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/ghost.png b/atlantik/themes/default/tokens/ghost.png new file mode 100644 index 0000000000000000000000000000000000000000..03efcc2a1cfe750d65fabc2738db06caa578d22e GIT binary patch literal 6041 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 Td5bq|00000NkvXXu0mjfu^z@L literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/globe.png b/atlantik/themes/default/tokens/globe.png new file mode 100644 index 0000000000000000000000000000000000000000..33741975d1e59e6b1646f5f95e976f1cb55a8c16 GIT binary patch literal 10104 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 axBm~xyK8Qgpniq`0000E+LS>MmAW0 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 b-!c7vMZ}zXq7{$U00000NkvXXu0mjf3##{~ literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/pyramid.png b/atlantik/themes/default/tokens/pyramid.png new file mode 100644 index 0000000000000000000000000000000000000000..e0792d20fb108501483b94d09c526d3c8ff3dd75 GIT binary patch literal 6477 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 literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/skull.png b/atlantik/themes/default/tokens/skull.png new file mode 100644 index 0000000000000000000000000000000000000000..073611225e5d14ba640bc1c774f29016b2f268cb GIT binary patch literal 7975 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 literal 0 HcmV?d00001 diff --git a/atlantik/themes/default/tokens/traffic_light.png b/atlantik/themes/default/tokens/traffic_light.png new file mode 100644 index 0000000000000000000000000000000000000000..0f2f647657a7b9a54f08cbbc9d792f91d33d6d76 GIT binary patch literal 5285 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 elK)@6iTqz6q=l6O`ktEr0000eklo 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 + + + + +]> + + + + +The &atlantik; Handbook + + + +Rob +Kaper + +
kaper@kde.org
+
+
+ +
+ +&FDLNotice; + + +20022004 +&Rob.Kaper; + + +2005-12-10 +0.7.5 + + + +&atlantik; is a &kde; client for playing Monopoly-like boardgames on the +monopd network. + + + + + + +KDE +kdegames +Atlantik +monopd + + +
+ + +Introduction + +Purpose of the &atlantik; board game is to acquire land in major +cities in North America and Europe while being a transatlantic traveler. To +win the game, players improve monopolized land with profitable buildings in +the hopes of bankrupting all other players. + +All game modes are served by monopd, a dedicated game server designed +for &atlantik;. One of the game modes plays like the popular real estate +board game known as Monopoly. + + + + +Connecting to a Server + +&atlantik; requires a game server to connect to. If you have an +Internet connection, you can request a list of public Internet servers and +you will not require additional software. If you want to play &atlantik; +locally, on a LAN or on a private Internet server, you +can enter the hostname and port to connect to. In this case, you will need +the +monopd server +software installed and running on the host you are connecting to. + +If you have problems connecting to a server, the following troubleshoot +notes might help you: + + +Try another server. The public server list is updated every +three minutes, and the server you are trying to connect to might not be +available any longer. + + +Check your firewall and masquerading settings. Restrictions might be in +place preventing you from connecting to servers or receiving reply traffic. +By default, monopd servers use TCP port 1234. If you're not sure, contact +your system administrator. + +&atlantik; makes use of +KExtendedSocket for network connections, which in +turn uses QDns. This might cause issues with IPv6 +and/or resolving hostnames. + + + + + +Menu Reference + + +<guimenu>Game</guimenu> Menu + + + +&Ctrl;L +GameShow Event Log +Display the event log + + + +&Ctrl;Q +GameQuit +Quits &atlantik;. + + + + + +<guimenu>Move</guimenu> Menu + + + +&Ctrl;R +MoveRoll Dice +As you may expect, roll the dice. + + + + +MoveEnd Turn + +Let the other player know you have finished +moving. + + + +&Ctrl;R +MoveRoll Dice +As you may expect, roll the dice. + + + +&Ctrl;B +MoveBuy +Buy land or buy buildings on your properties. + + + +&Ctrl;A +MoveAuction +Start an auction. + + + + +MoveUse Card to Leave Jail +Use a card to leave jail + + + +&Ctrl;P +MovePay to Leave Jail +Pay money to leave the jail. + + + +&Ctrl;J +MoveRoll to Leave Jail +Roll the dice to leave the jail. + + + + + +<guimenu>Settings</guimenu> Menu + + + + +Settings +Configure Notifications... + + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &atlantik;. + + + + + +Settings +Configure &atlantik;... +Opens the configuration +dialog which lets you tweak a lot of &atlantik;'s options. + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Reporting Bugs + +If you think you have found a bug in &atlantik;, please report +it. Developers often catch and fix bugs themselves, but you might +experience issues not yet known. Not reporting your problems might +very well prevent them from being fixed indefinitely. + +Some bugs might be in the monopd server instead of &atlantik;, but you +don't need to worry about that: you can report all bugs under the &atlantik; +package on the &kde; bug +wizard. Use the +HelpReport +Bug menu item to visit the bug wizard with some +details of your &atlantik; version filled in automatically. + +Please specify details in your bug report, such as game +conditions. For example, a bug might only occur when people are in jail, or +during an auction. If possible, save the event log and attach it to the bug +report or send it to the author. It might contain vital clues about behavior +that could lead to a faster resolution for the bug. + + + + +Download + +&atlantik; is part of &kde; releases and as such the recommended +releases are those shipped with &kde;, part of the &package; package. Some +vendors might have individual packages for &atlantik; based on these +releases. + +Users interested in the development version can likewise use the +standard &kde; CVS +instructions to acquire &atlantik; from the kdegames module. + +Bleeding edge development requiring the development version of monopd +is done using arch. For more information on this, or releases in general, +visit the &atlantik; download +page. + + + + +Answers to Frequently Asked Questions + + + + + +How do I add a computer player? + + +Unfortunately, computer opponents are not yet supported by &atlantik;. +Work is in progress to development a network bot that can connect to +servers, called Pacifik, but is has +not yet reached a useful level. +Users are advised to take advantage of the public +Internet servers or a private LAN server. + + + + + +How do I trade? + + +Click on a player portfolio or estate with the &RMB; and you can open +a trade. +There are a two usability issues with trades that can cause confusion. +If you in any way include another player in a trade, that player will get +the trade window. Any player can reject the terms, which unnecessarily ends +the trade session for all other players as well. A trade has to be accepted +by all players in it before it can completed, which can be complicated +because players remain a participant in deals even when they are no longer +involved with tradeable items. + + + + + + + +Credits and Licenses + +The program &atlantik; and the documentation are copyright © +1998-2004 Rob Kaper kaper@kde.org. + + + +&underFDL; +&underGPL; + + + + +&documentation.index; + +
+ + diff --git a/doc/atlantik/man-atlantik.6.docbook b/doc/atlantik/man-atlantik.6.docbook new file mode 100644 index 00000000..340650a6 --- /dev/null +++ b/doc/atlantik/man-atlantik.6.docbook @@ -0,0 +1,86 @@ + + +]> + + + +LauriWatts +lauri@kde.org +March 7, 2003 + + + +atlantik +6 + + + +atlantik +&kde; monopd client + + + + +atlantik + host + port + game + + + + + + + +Description + +&atlantik; is a &kde; client for playing Monopoly-like +boardgames on the monopd network. + +Purpose of the atlantik board game is to acquire land in major +cities in North America and Europe while being a transatlantic +traveller. All game modes are served by monopd, a dedicated game +server designed for &atlantik;. One of the game modes plays like the +popular real estate board game known as Monopoly. + + + + +Options + + +&atlantik; options + + host + +Connect to this host. + + + + + port +Connect to this port + + + + game +Join this game + + + + + + +See Also + +More detailed user documentation is available from help:/atlantik (either enter this +URL into &konqueror;, or run +khelpcenter +help:/atlantik). + + + + diff --git a/doc/kasteroids/Makefile.am b/doc/kasteroids/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kasteroids/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kasteroids/fuel.png b/doc/kasteroids/fuel.png new file mode 100644 index 0000000000000000000000000000000000000000..7ee0ff731f7873527df1aab728ee9079e71ca027 GIT binary patch literal 353 zcmV-n0iOPeP)PgWV1FIsh`$#=0FY50JeNK+}X*bZ2 zZ2ff9Wtiy3mYBFM%!d5_t>0Y$7h+~+?mT?|Ym_sMQ_17%08MGMrfDLg5W?YkrIqko z2%)o>=PALuFthhQt$5z`V&n4x?|pH*ialxN-d<@P$Zmg&_r0Pm>G3bivVWJN>B{QK zIV?jvW}fG{^RS9N*-`hWMSCUV=>OjZ0ASGznwgf8aNUuh00000NkvXXu0mjf4yu|8 literal 0 HcmV?d00001 diff --git a/doc/kasteroids/index.docbook b/doc/kasteroids/index.docbook new file mode 100644 index 00000000..61563db0 --- /dev/null +++ b/doc/kasteroids/index.docbook @@ -0,0 +1,328 @@ + + + + + +]> + + + + + +The &kasteroids; Handbook + + + +&Martin.R.Jones; &Martin.R.Jones.mail; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + + + + +2000 +Martin R. Jones + + + +2001-2005 +Philip Rodrigues + +&FDLNotice; + +2005-01-18 +2.3 + +&kasteroids; is, as you would expect, the &kde; clone of the +popular Asteroids game. + + +KDE +games +linux +asteroids + + + + + +Introduction + +The objective of &kasteroids; is to destroy all the asteroids on the +screen to advance to the next level. Your ship is destroyed if it makes contact +with an asteroid. + + + + +Your Ship + +When your ship is first launched, it has only basic abilities: Turning, +Thrusting and Shooting + +In order to move your ship, rotate it to face the direction you wish to +travel and press the thrust key. Of course the ship has momentum, so you will +have to compensate when you select the angle of the ship. Thrusting uses fuel, +so you should not use your thrusters more than necessary. + +You have an unlimited amount of ammunition, so shoot away. Only a limited +number of shots can be active at once, though. If you repeatedly shoot and +miss, you will find that you are unable to shoot until some of the shots have +expired. + +Your ship has a limited amount of fuel. When all the ship's fuel has been +consumed it is unable to thrust anymore. This leaves you immobile, but not +defenseless ‐ you can still rotate and shoot. + +Fortunately the asteroids occasionally release fuel when they are shot. +Fly your ship into the fuel symbol to collect it. + + +Fuel Upgrade + + + + + +Fuel Upgrade + +Fuel Upgrade + + + + + + +Ship Upgrades + +Occasionally when you shoot an asteroid a symbol will be produced. These +symbols represent upgrades to your ship. Fly over the symbol to install it in +your ship. The upgrades have a cumulative effect, up to a maximum of 5. + +The improvements available are: + + + +>Brakes +Brakes stop your ship as quickly as possible. The more brake +upgrades you have the faster you can stop, with lower fuel +usage. + + + +Shields +Shields absorb the collision with asteroids, but use a lot of +fuel. You need at least two shield upgrades to survive a hit from a medium +sized rock, and 3 to survive a large rock. You will also need to have enough +fuel to maintain the shield during the strike. + + + + + + +Controls + +The key bindings are configurable via the +SettingsConfigure +Shortcuts... menu item. + +The defaults are: + + +Rotate Anti-Clockwise - Left +Arrow +Rotate Clockwise - Right +Arrow +Thrust - Up +Arrow +Shoot - Space +Bar +Shields - S +Pause - P +Brake - X + + + + + +Configuring &kasteroids; +&kasteroids; only has a few options, which can be reached from +SettingsConfigure +KAsteroids.... + + +Start new game with n +ships +When you start a new game, you have ships in +reserve, in case of an accident ;-). This specifies how many ships you have +in total when the game starts. + + + +Show Highscores on Game Over +If selected, shows all of the highscores each time you finish a +game, regardless of whether or not you achieve a highscore. + + + +Player can destroy Powerups +If selected, shooting a powerup destroys it. If unselected, the +powerup remains, allowing you to still pick it up. + + + + + + + + +Tips + +Here are some tips that may help: + +If you want to stop your ship, use the brakes rather than stopping +manually. The brakes stop you faster, with less fuel usage. + +Shields are expensive. They should be used as a last resort. Try to +use your thrusters to avoid collisions when practical. + +Avoid the edges of the playing field. It's more difficult to see +asteroids approaching from the opposite side of the field. + + + +Menu Reference + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game of +&kasteroids; + + +P +Game +Pause + +Pauses the game + + + +&Ctrl;H + +Game +Show Highscores + +Shows the High Scores for +&kasteroids; + + + +&Ctrl;Q + +Game +Quit + +Quits &kasteroids; + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsConfigure Shortcuts... +Brings up the Shortcuts Configuration dialog. This is +a standard &kde; shortcuts configuration dialog, which you are +probably familiar with. See for details of the +default controls. + + + + + +Settings +Configure KAsteroids... + +Brings up the configuration dialog for +&kasteroids;. See . + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + +Credits + +&kasteroids; + +Program Copyright 1997 &Martin.R.Jones; &Martin.R.Jones.mail; + +Documentation based on the original by &Martin.R.Jones;, currently +maintained by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kasteroids; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + + + diff --git a/doc/katomic/Makefile.am b/doc/katomic/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/katomic/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/katomic/index.docbook b/doc/katomic/index.docbook new file mode 100644 index 00000000..c1ffb2c7 --- /dev/null +++ b/doc/katomic/index.docbook @@ -0,0 +1,358 @@ + + + + + +]> + + + +The Atomic Entertainment Handbook + + + +Dirk +Doerflinger + + + +StephanKulowDeveloper
&Stephan.Kulow.mail;
+
+ +CristianTibirnaDeveloper
&Cristian.Tibirna.mail;
+
+ +MikeMcBride +Reviewer +
&Mike.McBride.mail;
+
+ +
+ +2005-12-14 +2.0 + + +This Handbook describes &katomic; Version 2.0 + + + +KDE +kdegames +KAtomic +game +atomic entertainment + +
+ + +Introduction + +Atomic Entertainment is a small game which resembles +Sokoban. The object of the game is to build chemical +molecules on a Sokoban like board. + + + + +Playing <application>Atomic Entertainment</application> + + +Rules + +The aim of Atomic Entertainment is to build +chemical molecules using basic atoms you are given. The molecule being built is +shown in a frame in the main window. + +Clicking on an atom will cause arrows to appear beside it. These arrows +show the direction the atom can be moved. After an arrow is clicked, the atom +will move in this direction until it reaches the next border or another atom. If +two atoms touch each other with the corresponding connectors, they form a +molecule. The atoms can only be moved one at a time. + +The level is solved when the new molecule has the same structure as shown +in the preview window. + +In the higher levels, some tactical skill will be necessary for solving +the puzzle. + +The best score in this game is actually the lower score, because the +goal is to solve a level with as few moves as +possible. Highscore: in the main-window shows +the lowest number of moves used for this level. Your score +so far: shows the current number of moves. + +The scrollbar on the top right of the main window changes the game +level. + + + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + + +F5 +Game +Restart Game +This will restart the current level. + + + + +&Ctrl;H + +Game +Show Highscores +This will show the best scores for the current level. + + + + +&Ctrl;Q + +Game +Quit +Selecting this item will end your current game, and exit the +&katomic;. + + + + + +The <guimenu>Move</guimenu> Menu + + + + + + +&Ctrl;Z + + +Move +Undo + + + +Undo the last move you made. + + + + + + +&Ctrl;&Shift;Z + +Move +Redo + + +If you have previously undone a move, you can redo it +here. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsConfigure +Shortcuts... +This item lets you change the key settings of +Atomic Entertainment. See the section for a list of the +defaults. + + + +Settings +Configure &katomic;... +In the dialog box that appears, the animation-speed of the +atomic movement can be set. + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Shortcuts + +Default shortcuts are: + + +Shortcuts + + + +Restart Game + +F5 + + + +Quit + +&Ctrl;Q + + + +Show Highscores + +&Ctrl;H + + + +Undo + +&Ctrl;Z + + + +Redo + +&Ctrl;&Shift;Z + + + +Atom Down + +Down Arrow + + + +Atom Left + +Left Arrow + + + +Atom Right + +Right Arrow + + + +Atom Up + +Up Arrow + + + +Next Atom +Tab + + +Previous Atom +&Shift; + + +Help +F1 + + +What's this? + +&Shift;F1 + + + + +
+ +
+
+ + + +Credits and License + + +Atomic Entertainment Copyright 1999 &Stephan.Kulow; +&Stephan.Kulow.mail; and Cristian Tibirna +&Cristian.Tibirna.mail;. + + +Atomic Copyright Andreas Wuest Andreas Wuest@gmx.de. + + +Documentation Copyright 2000 &Dirk.Doerflinger; +ddoerflinger@gmx.net + + +Proofreading by Michael McBride +&Mike.McBride.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + +How to obtain <application>Atomic Entertainment</application> + + +Atomic Entertainment (&katomic;) is written for the &kde; project +http://www.kde.org by &Stephan.Kulow; +&Stephan.Kulow.mail; and Cristian Tiberna +&Cristian.Tibirna.mail;. +It is based on Atomic 1.0.67 by Andreas Wuest +AndreasWuest@gmx.de. + + +&install.intro.documentation; + + + + +Requirements + +In order to successfully compile Atomic Entertainment, you need &kde; 3.0 + +All required libraries as well as Atomic Entertainment itself can be found on +&kde-ftp;. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ + + diff --git a/doc/kbackgammon/Makefile.am b/doc/kbackgammon/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kbackgammon/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kbackgammon/board.png b/doc/kbackgammon/board.png new file mode 100644 index 0000000000000000000000000000000000000000..2556cbcdcf89dbbcc21070a2b77f111857132f42 GIT binary patch literal 18755 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 literal 0 HcmV?d00001 diff --git a/doc/kbackgammon/index.docbook b/doc/kbackgammon/index.docbook new file mode 100644 index 00000000..d8dfe39d --- /dev/null +++ b/doc/kbackgammon/index.docbook @@ -0,0 +1,622 @@ + + + + + +]> + + + + +The &kbackgammon; Handbook + + + +Jens +Hoefkens +
&Jens.Hoefkens.mail;
+
+ + +Bo +Thorsen +
&Bo.Thorsen.mail;
+Developer +
+ +
+ + +19992000 +&Jens.Hoefkens; + + +&FDLNotice; + +2005-12-22 +2.6.0 + + +&kbackgammon; is a graphical backgammon program for &kde;. It +supports backgammon games with other players, games against computer +engines like &GNU; bg and even on-line games +on the First Internet Backgammon +Server. + + + +KDE +game +backgammon +FIBS +gnubg + + +
+ + +Introduction + +&kbackgammon; is a graphical backgammon program. The objective +of backgammon is to move your checkers off the board before your +opponent does. While the rules of backgammon are moderately difficult +(the most difficult part apparently being the initial positioning of +the checkers), this document does not try to teach these rules: please +refer to the Web, a book, or (probably best) a friend for this. + +&kbackgammon; offers you a graphical backgammon board that can +be used almost entirely by using the mouse (although textual commands +are also available and it should be possible to play backgammon +without the mouse, too). Therefore, &kbackgammon; is easy to use and +allows you to concentrate on the important aspects of playing +backgammon. + +In order to play backgammon, you usually need an +opponent. &kbackgammon; offers you to play against hundreds of +different opponents of various strength on the First Internet Backgammon +Server. If you prefer, you may also use the Offline Engine which allows you to +play against yourself or friends that are with you at your +computer. + +In the near future &kbackgammon; will allow you to play against +&GNU; Backgammon, which is a +powerful backgammon program that usually runs without a graphical +frontend. It plays on FIBS with a rating of about +2000 (which is quite high). + +Additionally, plans are on the way that will allow games over +the Internet between two &kbackgammon; programs (and their respective +users). Finally, the architecture of &kbackgammon; is quite open and +it is possible to include support for other engines easily (if you are +interested in this, please contact the author). + + + + +How to use the Board + +&kbackgammon; is centered around a graphical backgammon +board. This board consists of the checkers, the dice, and the +cube. All these game elements can be conveniently manipulated with the +mouse. + +The following image shows a typical game situation with the +white player moving from 1 to 24 and the black player moving from 24 +to 1. Currently, the white player owns the cube and has just rolled 3 +and 4. + + +The backgammon board + + + + + +The Board + + + + +If it is their turn, players can roll the dice by double +clicking on the squares representing the dice or by using the +corresponding menu entry or toolbar icon (if the toolbar is visible +and rolling has been included in the toolbar). + +If they own the cube, players can double the cube by either +double clicking on the square representing the cube or by using the +corresponding menu entry or toolbar icon (if the toolbar is visible +and the cube has been included in the toolbar). + +If it is their turn to move, players can move checkers by +clicking on them and moving the mouse pointer to the desired location +(while holding the mouse button continuously pressed). This is called +dragging the checker. Depending on the selection made in the current +engine's configuration, the move will be finished once +the maximum number of checkers has been moved (this may be anywhere +between 1 and 4 checkers, although it will usually be 2 +checkers). + +In addition to dragging the checkers, the board offers a feature +called short move feature. If this has been +enabled in the board's configuration menu, checkers can be moved by +simply clicking on them. The board will then automatically make the +shortest move possible away from the current field. If the short move +feature is enabled, it may be set to +single or double click. This option is very useful for users of +touchpads that are sometimes difficult to use for dragging. + +Finally, the board has a certain smartness: it will only accept +moves that are allowed (based on the current dice). While the offline engine allows this to be +switched off (for the Edit Mode), this is usually +very helpful for unexperienced users (and advanced players will +probably never notice this). This feature can also be used to cancel a +move in progress: just drop it over an illegal field. + + + + +Backgammon Engines + +&kbackgammon; is built around the backgammon board, which +doesn't know anything about how to play backgammon. The whole +infrastructure (rolling the dice, &etc;) that allows backgammon games +is handled by backgammon engines. + + +The Offline Engine + +The offline engine Open Board allows you to play against yourself, or +probably more fun, another person sitting with you. + + + + +The <acronym>FIBS</acronym> Engine + +FIBS is the First Internet Backgammon +Server, a 24-hour international online community of backgammon +players of all ages and experience from Beginner to Experts. + +As well as playing the game, FIBS allows you +to save games, chat with other players, and has a complex rating +system. + +There is also a large website well worth some of your +time, if you are a backgammon fan. + + + + +The &GNU; Backgammon Engine + +The GNUbg engine does not yet exist (although it already has an +entry in the Engine menu). Therefore, the +remainder of this section should be taken with a grain of salt. + +Use of the GNUbg engine requires a working installation of the +actual GNUbg program. &kbackgammon; then starts GNUbg in the +background and communicates with it. All this is transparent to the +user (&ie; it is hidden from the user) and all interaction with GNUbg +is through the &GUI; elements of &kbackgammon;. + +If there are other enhancements that can help using GNUbg from +&kbackgammon;, please contact the author of &kbackgammon; (or better: +send a patch). Some of the more advanced features will probably be +position analysis and save/restore functions. However, playing is the +most pressing issue. + +The one thing that will never happen is a combination of the +GNUbg and the FIBS engine! Using computer programs +to enhance user ratings is cheating! + + + + + + +Game options + +The options to &kbackgammon; are set by selecting +Settings Configure &kbackgammon;... + from the menubar. This will bring up a dialog +box. + + + + +Menu Reference/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game. This item opens a dialog to enter the nicknames of both players. + + + + + + +&Ctrl;P + +Game +Print... + + +Print a picture of the board. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quit and close &kbackgammon; + + + + + + + +<guimenu>Move</guimenu> Menu + + + + + +&Ctrl;Z + + +Move +Undo + + + +Undo the last move you made. + + + + + + +&Ctrl;&Shift;Z + +Move +Redo + + +If you have previously undone a move, you can redo it +here. + + + + + +Move +Redisplay + +Redraw the board. + + + + + +&Ctrl;R + + +Move +Roll Dice + + +As you may expect, roll the dice. + + + + + +Move +End Turn + + +Let the other player know you have finished +moving. + + + + + +Move +Double Cube + + +Offer the opponent player during the course of a game +(just before you roll the dice) to continue the game at twice the current stakes. +If the opponent rejects this offer, you win the game. + + + + + + +Move +Engine + + + +Choose from the various game engines (Open Board, FIBS, GNU Backgammon, Next Generation) here. + + + + + + + + +<guimenu>Command</guimenu> Menu + +This menu has different items depending on the chosen game engine. + +Open Board engine + + + +Command +New Game + + +Start a new game. This item opens a dialog to enter the nicknames of both players. + + + + + +Command +Edit Mode + + + +In this mode the board accepts moves that are not allowed +(based on the current dice). + + + + + +Command +Swap Colors + + + +Swap the colors of the checkers between player 1 and player 2. + + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + + +Settings +Show Menubar + + +Toggle on and off the menubar. + + + + + +Settings +Toolbars +Main (&kbackgammon;) + +Toggle the Main Toolbar + + + + +Settings +Toolbars +Command Characters (&kbackgammon;) + +Toggle the Command Toolbar + + + + +Settings +Show Statusbar + + +Toggle on and off the statusbar. + + + + + +Settings +Save Settings + + + +Saves the current settings to become the default for &kbackgammon;. + + + + + +Settings +Configure Shortcuts... + +Opens a dialog for changing the key bindings. +Using this option you can change the standard key shortcut for &kbackgammon;'s commands +or create new ones. + + + + +Settings +Configure Toolbars... + +Opens a dialog for configuring the toolbar. You +can add and remove toolbuttons for &kbackgammon;'s commands with this +option. + + + + +Settings +Configure &kbackgammon;... + +Opens a dialog for changing some options for +&kbackgammon;. + + + + + + + + +<guimenu>Help</guimenu> Menu + +&kbackgammon; has a standard &kde; Help as described +below, with one addition: + + + +Help +Backgammon on the Web + + +Opens the websites of FIBS Home, Backgammon Rules, or &kbackgammon; in &konqueror;. + + + + +The standard &kde; Help entries are: + +&help.menu.documentation; + + + +Default Shortcuts + +The following tables show you the default shortcuts of +&kbackgammon;. + + +Key bindings + +Key ComboAction + +&Ctrl;NNew Game +&Ctrl;RRoll Dice +&Ctrl;PPrint Game +&Ctrl;QQuit &kbackgammon; +F1Help Contents +&Shift;F1What's This? Help +&Ctrl;MShow Menubar +&Ctrl;ZUndo move +&Ctrl;&Shift;ZRedo previously undone move + + +
+ +These shortcuts can be changed by selecting +Settings Configure +Shortcuts from the menubar. + +
+ +
+ + +Credits and License + +&kbackgammon; + +Program copyright 1999-2000 &Jens.Hoefkens; +&Jens.Hoefkens.mail; + +Documentation copyright 2000 &Jens.Hoefkens; +&Jens.Hoefkens.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kbackgammon; + +&install.intro.documentation; + +The home page of &kbackgammon; is located at http://backgammon.sourceforge.net. +It will usually contain the most up-to-date information the program +available. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +
+ + + diff --git a/doc/kbattleship/Makefile.am b/doc/kbattleship/Makefile.am new file mode 100644 index 00000000..41691557 --- /dev/null +++ b/doc/kbattleship/Makefile.am @@ -0,0 +1,3 @@ +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kbattleship/index.docbook b/doc/kbattleship/index.docbook new file mode 100644 index 00000000..9e380d17 --- /dev/null +++ b/doc/kbattleship/index.docbook @@ -0,0 +1,485 @@ + + + + + +]> + + + +The &kbattleship; Handbook + + + +&Daniel.Molkentin; &Daniel.Molkentin.mail; + + + +&Nikolas.Zimmermann; &Nikolas.Zimmermann.mail; + + + +&Frerich.Raabe; + +&Frerich.Raabe.mail; + + + + + + +&FDLNotice; + + +2001 +&Daniel.Molkentin; + + +2001 +&Nikolas.Zimmermann; + + +2001 +Kevin Krammer + + + +2005-12-14 +1.1 + + + +&kbattleship; is a network-enabled implementation of the famous Battle Ship game for &kde;. + + + +KDE +kdegames +kbattleship +game +battleship +battle + + + + + +Introduction + + +Features + +Network Gaming +Computer Player (AI) +Sounds +Chat +Statistics +Highscore List + + + + +&kbattleship; uses an &XML; based communication protocol so you can write +clients for every platform and in any language. If you want to write +one in your favorite programming language or/and environment, contact +us. We would really like to hear of it. + + + + + + + +Using &kbattleship; + + +If you want to play &kbattleship;, you will need two players, either play +against the computer or in a network against another player. + +In a network game one player has to open the game via +Game Start +Server or by pressing F3. + + + +A dialog box opens which will ask you for for a +Nick name: and Port:. Normally, +&kbattleship; will suggest your login name but you can enter any string +you want. The predefined port should be OK. However, if you encounter +problems, you can choose any other free ports above 1024. + + + +You need to tell the other player in case you use a port other than +the default as both players need to use the same port in order to be able +to establish a connection. + + + +The other player has to choose Game +Connect to Server, or press +F2. Again, a Nick name: is +suggested, but you can choose any name you like. + + + +An important point is the field Server:. Here, you +have to enter the host name of the server (the machine of the player +that initiated the game). + + + +Another possibility is to play &kbattleship; against your +computer. Select Single Player from the +Game Menu, or press F4. + + + +When you are done, you can start the game. Simply follow the +instructions in the statusbar. It will issue hints and suggest what to +do next. When you now look at the screen, you will find two grid fields, +the so-called battle areas. The left area belongs to +you. This is where you place your ships and where you can follow the +military actions of your enemy. The right area is where your enemy's +fleet is located. When it's your turn to fire, you need to click on a +certain sector (a field of the battle area) where you suppose the ships +to be located. + + + +First, you need to place your ships. The game initiator starts. When +he/she is done, player two sets his/her ships. + + + +Ship placement is very easy: Simply click on the field where you want to +place your ship. The first one will have a length of four squares, the next +will be three squares long &etc;. Click on the field where you want to start +the placement. If you click with the &LMB; the ship will be placed horizontally, +a &Shift; &LMB; click will cause it to be placed +vertically. &Shift; itself will flip the ship placement preview. + + + +Now you can blindly fire with a &LMB; click on the enemy battle area. The status bar indicates +who is about to shoot. + + + +The first player destroying all their opponents ships wins the game! + + + + + +The Menus + + +The <guimenu>Game</guimenu> menu + + + + + +F2 +Game +Connect to Server... + + + + +Initiate a connection to another player's server. + + + + + + + +F3 +Game +Start Server... + + + + +Start the server so another player can connect to you. + + + + + + + +F4 +Game +Single Player... + + + + +Start a game with your computer as the opponent. + + + + + + + +&Ctrl;H +Game +Show Highscores + + + + +Show the highest scores so far. + + + + + + + +F11 +Game +Enemy Info + + +Show the enemy's client (might also be &Mac;), the client +version, a short description and the protocol version used. + + + + + + + +&Ctrl;Q + +Game +Quit + + + + +Exit &kbattleship; + + + + + + + +The <guimenu>Settings</guimenu> menu + + + + + +Settings +Show Statusbar + + + + +Toggle on or off the display of the statusbar. The +default is on. + + + + + + + + +Settings +Show Grid + + + + +Toggle on and off the display of a grid on the playing +field. The default is off. + + + + + + + +Settings +Play Sounds + + + + +Toggle whether sounds (played when shooting) should be played. +The default is on. + + + + + + + +Settings +Configure Shortcuts... + + + + +Configure the keyboard shortcuts used by &kbattleship;. + + + + + + + +Settings +Configure Notifications... + + + + +Configure the audio and visual notifications used by &kbattleship;. + + + + + + + + + +The <guimenu>Help</guimenu> menu + +&help.menu.documentation; + + + + + +Questions, Answers, and Tips + + +Frequently asked questions + + +I get the error: Couldn't connect to &arts; +Soundserver. Sound deactivated. + + +&kbattleship; relies on &arts;, &kde;'s soundserver, to play any +sound. Enable &arts; in the &kcontrolcenter; by browsing to +Sound & Multimedia +Sound System and making sure that +the box labelled Enable the sound system is checked. + + + + + +I have a question that is likely to become a &FAQ;. Who should I +contact? + + +Contact the authors. They will most likely add it here. + + + + + + + +Credits and Licenses + +&kbattleship; Copyright 2000, 2001 + + +Authors + + +&Nikolas.Zimmermann; &Nikolas.Zimmermann.mail; + + + + +&Daniel.Molkentin; &Daniel.Molkentin.mail; + + + + +Kevin Krammer kevin.krammer@gmx.at + + + + + +Contributors + + +Benjamin Adler benadler@bigfoot.de + + + + +Nils Trzebin nils.trzebin@stud.uni-hannover.de + + + + +Elmar Hoefner elmar.hoefner@uibk.ac.at + + + + +Documentation updated for &kde; 3.4 by +BrianBeck +brian.beck@mchsi.com + + + +&underFDL; +&underGPL; + + + + +Installation + +&install.intro.documentation; + + +Requirements + + +At the time of writing, &kbattleship; requires &kde; 3.x or greater and +&Qt; 3.x or greater. + + + + + +Compiling + +&install.compile.documentation; + + + + + + + + diff --git a/doc/kblackbox/Makefile.am b/doc/kblackbox/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kblackbox/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kblackbox/index.docbook b/doc/kblackbox/index.docbook new file mode 100644 index 00000000..d45c2f76 --- /dev/null +++ b/doc/kblackbox/index.docbook @@ -0,0 +1,491 @@ + + + + + +]> + + + + +&kblackbox; Game Manual + + + +&Robert.Cimrman; &Robert.Cimrman.mail; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Robert.Cimrman; &Robert.Cimrman.mail; + + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + + +19982000 +&Robert.Cimrman; + + + +2001-2003 +&Philip.Rodrigues; + + +&FDLNotice; + +2005-12-10 +0.3.0 + +&kblackbox; is a superb graphical logical game, inspired +by the emacs blackbox game. + + +KDE +KBlackBox +kdegames +blackbox +game + + + + + +What is &kblackbox;? + +&kblackbox; is a superb :-) graphical logical game, inspired by +the emacs blackbox game. A major part of this help +file is based on the original emacs +help. + +&kblackbox; is a game of hide and seek played on a grid of +boxes. Your opponent (the Random number generator, in this case) has +hidden several balls within this box. By shooting rays into the box and +observing where they emerge it is possible to deduce the positions of +the hidden balls. The fewer rays you use to find the balls, the better +(the lower) your score. + + + + +Game Description + +In the first part of this section a description of the game board +will be given. The second part deals with user interaction with the +game board and finally in the third part the actual game rules are +explained. + + +Game Board Description + +The following types of field are found on the game board: + + + +Black squares +The black box. Here you must mark the squares you think +a ball is in. + + + +Green squares +These are the lasers, shooting rays of light when +switched on. + + + +Light Gray squares +Nothing here of interest, this is just a +border :-). + + + +Blue balls +There must be one there! you think. These mark +where you suspect a ball is placed in the black box. + + + +Cyan balls +Show where the balls actually are. + + + +Red balls +Incorrectly positioned balls you have marked are +indicated in red. + + + +Brown squares +Marking color + + + +The names of colors are used just for identifying the +different types of the fields in this text. They might +not be in any relation with the actual color of the +fields. Simply said: the black box is in the center, around are the +lasers and around them is the border. Remap the colors yourself +:-). + + + + +User Interaction + +The cursor can be moved around the box with the +standard cursor movement keys or the mouse. Switching of lasers or +marking of black boxes is done with the &LMB;, or by pressing the +Return or &Enter; key. + +You can mark the fields where you think a ball cannot be, too. Just press +the &RMB;. It often helps you to find an area where a ball could possibly +be. To clear any marks (blue or brown) press the +&LMB;. Brown marks cannot overwrite blue +marks. This way you cannot erase the blue marks (guessed balls) by accident when +playing with the &RMB;. + +When you think the configuration of balls you have placed is +correct, press the &MMB;. You +will be informed whether you are correct or not, and be given your +score. Your score is the number of letters and numbers around the +outside of the box plus five for each incorrectly placed ball. If you +placed any balls incorrectly, they will be indicated with red fields, +and their actual positions indicated with cyan fields. + + + + +Game Rules + +You have to find balls hidden in the black box. Your means are +limited - you can just fire lasers which are around the box. There are +three possible outcomes for each ray you send into the box: + + + + + +Detour +The ray is deflected and emerges somewhere other than +where you sent it in. On the playfield, detours are denoted by matching +pairs of numbers - one where the ray went in, and the other where it +came out. + + + +Reflection +The ray is reflected and emerges in the same place it +was sent in. On the playfield, reflections are denoted by the letter +R. + + + +Hit +The ray strikes a ball directly and is absorbed. It does not +emerge from the box. On the playfield, hits are denoted by the letter +H. + + + +The rules for how balls deflect rays are simple and are best shown by +example. + +As a ray approaches a ball it is deflected ninety degrees. Rays +can be deflected multiple times. In the diagrams below, the dashes +represent empty box locations and the letter O +represents a ball. The entrance and exit points of each ray are marked +with numbers as described under Detour +above. Note that the entrance and exit points are always +interchangeable. * denotes the path taken by the +ray. + +Note carefully the relative positions of the ball and the ninety +degree deflection it causes. + + + 1 + - * - - - - - - - - - - - - - - - - - - - - - - + - * - - - - - - - - - - - - - - - - - - - - - - +1 * * - - - - - - - - - - - - - - - O - - - - O - + - - O - - - - - - - O - - - - - - - * * * * - - + - - - - - - - - - - - * * * * * 2 3 * * * - - * - - + - - - - - - - - - - - * - - - - - - - O - * - - + - - - - - - - - - - - * - - - - - - - - * * - - + - - - - - - - - - - - * - - - - - - - - * - O - + 2 3 + + + +As mentioned above, a reflection occurs when a ray emerges from the same +point it was sent in. This can happen in several ways: + + + + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - O - - - - - O - O - - - - - - - - - - - +R * * * * - - - - - - - * - - - - O - - - - - - - + - - - - O - - - - - - * - - - - R - - - - - - - - + - - - - - - - - - - - * - - - - - - - - - - - - + - - - - - - - - - - - * - - - - - - - - - - - - + - - - - - - - - R * * * * - - - - - - - - - - - - + - - - - - - - - - - - - O - - - - - - - - - - - + + + +In the first example, the ray is deflected downwards by the upper +ball, then left by the lower ball, and finally retraces its path to its +point of origin. The second example is similar. The third example is a +bit anomalous but can be rationalized by realizing the ray never gets a +chance to get into the box. Alternatively, the ray can be thought of as +being deflected downwards and immediately emerging from the box. + +A hit occurs when a ray runs straight into a ball: + + + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - O - - - + - - - - - - - - - - - - O - - - H * * * * - - - - + - - - - - - - - H * * * * O - - - - - - * - - - - + - - - - - - - - - - - - O - - - - - - O - - - - +H * * * O - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + + + +Be sure to compare the second example of a hit with the first +example of a reflection. + + + + +&GUI; description + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New +Starts a new game (and abandons the current, if +any.) + + + +Game +Give Up +Shows you positions of the balls. + + + +Game +Done +Checks whether all balls are placed. If yes, it terminates +the current game, computes the final score and indicates real positions of +the balls. The middle mouse button has the same +function. + + + +Game +Resize +Resizes the main window, so that its contents fit perfectly. This is useful when you accidentally change the size of the +window... + + + + + +&Ctrl;Q + +Game +Quit +Quits &kblackbox; + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsShow/Hide Toolbar + + + +Shows or hides the &kblackbox; toolbar. + + + + + +SettingsShow/Hide Statusbar + + +Shows or hides the &kblackbox; status bar at the base of the screen. + + + + +Settings +Size +Sets the size of the game field (black box). You may choose +between 8 x 8, 10 x 10 and +12 x 12. The default is 8 x +8. + + + +Settings +Balls +Sets the number of balls in the black box. You may choose +between 4 (the default), 6 +or 8. + + + + +Settings +Tutorial +Switches the tutorial mode on or off. In tutorial mode, +you can see where the balls actually are. Note that you have to start a new +game for this change to take effect. + + + +Settings +Configure Shortcuts... +Displays a standard &kde; shortcut configuration dialog, in +which you can change the keyboard shortcuts used by &kblackbox;. + + + + +SettingsConfigure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &kblackbox; toolbar. + + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + +The Toolbar + + +&kblackbox;'s toolbar + + + + + + + +The &kblackbox; toolbar offers quick access to the most commonly used +&kblackbox; functions. From left to right, the icons are: + + + + +New +Starts a new game. + + + +Give Up +Shows you positions of the balls. + + + +Done +Checks whether all balls are placed. If yes, it terminates +the current game, computes the final score and indicates real positions of +the balls. The middle mouse button has the same +function. + + + + + + + +Credits and License + +&kblackbox; + +Program Copyright 1998-2000 &Robert.Cimrman; &Robert.Cimrman.mail; + +Documentation by &Robert.Cimrman;. Updated and converted to Docbook +for &kde; 2.0 by &Lauri.Watts; &Lauri.Watts.mail; + + +Current maintainer &Philip.Rodrigues; &Philip.Rodrigues.mail; + + + + +&underFDL; +&underGPL; + + + + + +Installation + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + diff --git a/doc/kblackbox/kblackboxtbar.png b/doc/kblackbox/kblackboxtbar.png new file mode 100644 index 0000000000000000000000000000000000000000..1f129bc8d8ba4cc4c7ad1434c89f901ba8728f35 GIT binary patch literal 1901 zcmV-z2a@=SP)RN4etiFDzG+zljYcDp@OJbX0{d_4b-Xe7zrKL)-w@CY03d)9isZpN|5zsh z)_?#sQhjlvAVn(hO;C|l8X&8b@P6h=d8qJe56p<_03)bBDt}QaP81})7I*l{bx>(m znjy`#t@T41*Ftlb0+6D+SLF#1!N>yy64E{GmA|Ug>fO0-h7L-RQUU-PjC&$({)<;h}mSuDjlFTkYb9L4m8+A^(&?ruM?ugq@R_|!r*PcaDU0Ig- z!puP_ND4~pGKPi}1PusMnwvhO%i^lVT^2m?gZd+bLff)7ANm||Cn1H|;cxz>G&|+d z9w2D^oVRkX9EPdkEX>%W~J zKkZhlem_}R(cb$qZYgyh$?USY%6kZ(0*~*|b_vbmsxCr;+4TNSl>|W$wHrT&#hZ(z z&u%Z@Eid0)PR;7g?DP?{W7s76%u0_AvIeJ?Km&0|gEhB3dUl!e{qX)O1ct5hu)8J& zjHrUJ+DS+<+kX8)#RONs4liH1+-6ECm;SW0`0=7a*7B#z8H7w`mnSfU>@O=lI!GO5 z>>QlERjbw0-?7vlczwb7@hY|!6&OT#Si10TK>n>h_dKixB2?W4kos~el^8=EVityCVYK@gx`W^X3A=d+A8Lr|y2U%c9DwSa(%msbXB@DH!y z=}qV7gdKu_GZ7%H1wWXk1ShFs7?kg>6wel1*Dc*D5y-HOch0|aa5Xt7DCU%_>Y+=2 zTqNeT?W%2=*RIU6<}r7){LivCRXi4_tpaJwC4lCOlpw^FYHPbv9jj+eg8+Vbhobf1 zKargeFfxi9;QNp6RZJ`5Ev7TY6k3-8Up}aQ`0>K}c9ps2hrWdL&U@{Pl0pJK35Rw5 z#-fZO${X&G?VTGVk|f9*DXiYFl%58XOlW0Uwl_K6KT|MsMu!0aGm`+AAzB_F0^$vD zT($VUOT*x0`HL~Y0)QL=faMIoZvjwWlo`dCkiuk!K=3RyiE7NIVNCzBNCI3}rN1we zLGCG+@)SM|{U?v6-JmZj<-R_VhYN5^fMFyf@G*pMch-lL_&NdTva<9=~)9M399AiXzL@= zQIgZ8OMhLOK06jgp&`=G@y>aU<2XZ3Z%&t9zBuy}&vS_^?6CvBXeSS>C56+!M1fbr%Nes_>cW? zpRb*tgib%lwk$r1lhoprr=CR;bn9NZ;D?3;48xo9ZhvsK^Je>JitG(Wvp&f`9He(y zdz_E}aApDZTHJa>?L|2zq%dbLPOpH*Ps*<2y|em>&b>bugaLr*_h#ec&X5gZzA!WM z@ehIc7aeYD{J4?m*=6k^Z3l2#pNN{%txY?`jQ!6}GeUj%mx;dC%z?)9-Bw2&teu^{N2o{23rAmn=0M|{_ + + + + +]> + + + + +The &kbounce; Handbook + + + +&Aaron.J.Seigo; &Aaron.J.Seigo.mail; + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + +&FDLNotice; + +2005-12-10 +0.5 + + + + + +&kbounce; is a ball game for &kde;. + + + + + +KDE +kdegames +jezzball + + + + + +How To Play + +&kbounce; is played on a field, surrounded by a wall, with two or +more balls that move about in the field bouncing off of the +walls. + +Walls are a darker color while the active areas of the field are +a lighter color. + +The size of the active area of the field is decreased by making +new walls that enclose areas without balls in them. To complete a +level, the player must decrease the size of the active field by at +least 75% within the time allowed. + +With each new level, another ball is added to the field, and the +player is given one more life than in the level before. The time +allotted to complete the level is also increased. + +Scoring is based on how much of the field is cleared. + +New walls are built by clicking the +left mouse button in an active area of the +field, at which point two walls will begin to grow in opposite +directions from the square the mouse was clicked in. Only two walls +may be growing on the screen at any given time. + + +A new wall growing + + + + + +A new wall growing + + + + +When the mouse is on the field, the cursor is shown as a pair of +arrows pointing in opposite directions, either horizontally or +vertically. The arrows point in the direction the walls will grow +when the left is clicked. This direction +can be changed by clicking the right mouse +button. + +A new wall has a head which moves away from the +point where the mouse was clicked. A wall is not permanent until this +head runs into another wall. If a ball collides with +any part of the wall except the head, before the head has run into +another wall, the new wall will disappear completely and one life will +be lost. If a ball collides with the head in the direction of the +wall's growth, the wall will stop growing there, and become permanent, +with no loss of life. If a ball collides with the head from any other +side, the ball will bounce off and the wall will continue to grow +normally. + + + + +Strategy + +Many players find the game becomes quite difficult by only the +third or fourth level, given the number of balls on the field at +once. + +The trick to playing &kbounce; successfully is to build +corridors. To build a corridor, start a pair of walls +growing close to another wall, and time it such that one of the walls +will be hit by a ball and one of the walls will not, and thus become +permanent. + + +Building a corridor + + + + + +Building a corridor + + + + +This will leave a narrow corridor only a few squares high on the +field, surrounded on three sides by walls. Wait for the balls to +bounce into the open end of the corridor, and close the corridor +behind the ball with a new wall. Although you will most likely lose a +life for each corridor created, you can trap several balls in a single +corridor. + + +Capturing balls in a corridor + + + + + +Capturing balls in a corridor + + + + +A final tip — take your time! There is plenty of time, as +shown in the Time display on the right hand side +of the screen. Rushing will just get you in trouble! + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game. + + + + + + +&Ctrl;End + + +Game +End Game + + +End the current game. + + + + + + +P +Game +Pause + + +Pause or resume the game. + + + + + + +&Ctrl;H + +Game +Show Highscores + + +Opens a dialog that displays different high score tables. +Export the high scores into a file or click on +Configure to open a dialog to customize your Nickname +and add a Comment. + + + + + +&Ctrl;Q + +Game +Quit + + +Quit and close &kbounce;. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Select Background Folder... + + +Opens a dialog to select a folder for the background images. + + + + + +Settings +Show Backgrounds + + +Shows the background images in the selected folder. Only enabled, if a background folder is already selected + + + + + +Settings +Show/Hide Toolbar + + +Shows/hides the &kbounce; toolbar. + + + + + +Settings +Show/Hide Statusbar + + +Shows/hides the &kbounce; statusbar. + + + + + +Settings +Play Sounds + + +If checked, &kbounce; game sounds are played. + + + + + +Settings +Configure Shortcuts... + + +Opens a standard &kde; shortcut configuration dialog, in which +you can change the keyboard shortcuts used by &kbounce;. + + + + + +Settings +Configure Toolbars... + + +Opens the standard &kde; toolbar configuration dialog to customize the &kbounce; toolbar. + + + + + +Settings +Configure Highscores... + + +Opens a dialog to customize your Nickname and +add a Comment. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + +Credits and Licenses + + +&kbounce; + +Copyright 2000, Stefan Schimanski + + +Developers + +Stefan Schimanski schimmi@kde.org + + +Sandro Sigala ssigala@globalnet.it - +Highscore + + + +Documentation copyright 2002, &Aaron.J.Seigo; +&Aaron.J.Seigo.mail; + +This handbook is dedicated to Dennis E. Powell. + + + +&underFDL; + +&underGPL; + + + + +Installation + +&install.intro.documentation; + +&install.compile.documentation; + + + +&documentation.index; + + + diff --git a/doc/kbounce/jezball_corridor1.png b/doc/kbounce/jezball_corridor1.png new file mode 100644 index 0000000000000000000000000000000000000000..42df31c8d60d0c8033016235bf45419cd82e1993 GIT binary patch literal 14148 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 z zv5Ndbe5z0WDD&o;#4!kDHk$IWw%Zz6ja#rk~e;;^t=Zo{*ax4;={#GU0dM+>hC z2mj+yo*@*ohG;NomD_Z|zT++nQV(B({~$0movUT4H}CyFJiO2< literal 0 HcmV?d00001 diff --git a/doc/kbounce/jezball_corridor2.png b/doc/kbounce/jezball_corridor2.png new file mode 100644 index 0000000000000000000000000000000000000000..38c84169e5b510f84441e3a5caf0f41588bb8ca8 GIT binary patch literal 14351 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 IMRWiE0a+%?Jpcdz literal 0 HcmV?d00001 diff --git a/doc/kbounce/jezball_newWall.png b/doc/kbounce/jezball_newWall.png new file mode 100644 index 0000000000000000000000000000000000000000..4700df3ec7cb9aecbef04b997a343b624b3c3844 GIT binary patch literal 14646 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
ianw@netspace.net.au
+ + +Marco +Krüger + + + + + + +2003 +Ian WadhamMarco Krüger + + +&FDLNotice; + +2005-12-10 +2.0 + + + + + + +&kgoldrunner;, a game of action and puzzle solving. Run through +the maze, dodge your enemies, collect all the gold and climb up to the +next level. + + + +KDE +KGoldrunner + + + + +Playing &kgoldrunner; + + +Introduction + +&kgoldrunner; is an action game where the hero + + + +runs through a maze and dodges enemies + + + + + +You must guide the hero with the mouse or keyboard and collect +all the gold nuggets, then you can climb up into the next level. Your +enemies are also after the gold. Worse still, they are after you! They +will kill you if they catch you! + +The problem is you have no weapon to kill +them. All you can do is run away, dig holes in +the floor to trap them or lure them into some area where they cannot +hurt you. After a short time a trapped enemy climbs out of his hole, +but if it closes before that, he will die and reappear somewhere else. + + +If you have never played before, try the Tutorial +game, which teaches you the rules and basic skills. Then try the +Initiation game. Experts might enjoy the +Challenge or Vengeance of Peter W games, +but the Initiation game has 100 levels and some are +very difficult. It is a good game for achieving a High Score. + + As you move to more advanced levels, you will find that +&kgoldrunner; combines action, strategy, tactics and puzzle +solving — all in one game. Good luck! + + + + +A Typical Game + +Below is a typical game in progress. The hero (green) is at +level 4 in the Advanced Tutorial game. The window's status bar shows +how many lives he has left and how many points he has scored. It also +shows that there is a hint available for this level (as there always +is in tutorial games) and that the action is stopped (while taking +this snapshot), but can be continued by pressing P or +Esc. + +The hero started at the top left, dug a hole and dropped into a +line of false bricks and gold where you can now see him falling. The +enemy above him will fall into the hole, then climb out and be trapped +up there till the end of the level. He could cause the hero some +problems then, because that is where the hidden ladders appear when +all the gold is gone. The hero has to use them to get to the next +level. + + +&kgoldrunner; typical game + + +&kgoldrunner; typical game + + + +The enemy on the right is falling and carrying some gold. +Notice his gold outline. The third enemy is about to come down a ladder to +chase the hero. He will have to dodge both enemies when he reaches +the floor ... This level also shows diggable bricks and undiggable +concrete and has lots of traps (false bricks), which look just like +ordinary bricks. + + + + +How To Play + + +Starting the Game + +When the &kgoldrunner; window appears, click on the name of one +of the games in the list displayed, then click on the Start +Game button. Level 1 of the game you selected will appear +and the mouse pointer will be over the hero. You start play by moving +the mouse, clicking the mouse or pressing any key that can control the +hero (⪚ Space), but avoid using +Q, S, P or &Esc; at +this stage. You start each level in the same way. + +You have five lives at the start and gain a bonus life for each +level completed. + + + + +Moving Around + +By default you control the hero (green) with the mouse, but you +can also control him with the keyboard (see below). He moves towards +wherever you place the mouse pointer. He cannot move as fast as you +can move the mouse, but he will try hard to catch up. If the mouse +pointer is above or below his level, he will always go up or down when +there is a ladder available or a place to fall into. Otherwise he will +move horizontally until he is above, below or right at the mouse +pointer. You can make him follow simple paths (like _ | L or U), but +be careful not to get too many twists and turns ahead of him. + +You can move left or right over bricks, concrete, ladders or +bars and up or down over ladders. If you are on an empty square or a +gold nugget and there is no brick, concrete, ladder or bar to hold you +up, gravity takes over and you fall. You can also fall by moving down +from a bar or the bottom of a ladder, so keep the mouse pointer up +there if you want to hold on. + + + + +Keyboard Control + +If you prefer, you can use the keyboard to control the hero. The +default keys are the arrow keys and I, +J, K and L (in +honor of the original Apple II game), with the keys +Z and C or U and +O for digging. You can play one-handed by using +U, I, O, +J, K, L and +Space (on a QWERTY keyboard) or +two-handed by using the arrow keys, Space, +Z and C. You can also choose other +keys, on the Settings menu, if you have the &kde; +desktop version of &kgoldrunner;. + +You enter keyboard mode by pressing one of the keys that can +control the hero or by selecting Keyboard +Controls Hero on the Settings +menu. + +The movement keys (I, J, +K, L or arrows) start the hero +moving up, down, left or right. He continues moving in that direction, +if he can, until you press another movement key or +Space to stop. In mouse mode, he stops automatically +when he gets to the mouse pointer. + +A word of warning: using the keyboard is much harder, in the +long run, than using the mouse. Think of the mouse as a +joystick. + + + + +Taking a Break + +You can stop the game at any time by pressing a Pause key +(either P or &Esc;). You can resume by pressing the +P or &Esc; key again. There is also a +Pause option in the Game +menu, but the problem is to get up there without moving the hero and +getting into trouble. + +When you want to use the &kgoldrunner; menu or do +some work in another window, always press a Pause key and stop the +game. Otherwise the hero will go on following the mouse pointer and +might get into trouble. + + + + +Winning a Level + +The object of the game is to collect all the gold, by moving or +falling onto it. When there is none left, you must move to the top of +the playing area to get to the next level. Often, as you collect the +last piece of gold, hidden ladders will appear and you can use them to +climb to the top. + + + + +Beware of False Bricks + +False bricks, otherwise known as fall-through bricks or traps, +are the other hidden feature of the game. They look just like ordinary +bricks, but if you run over one you fall. This can be bad if there is +an enemy or a pit down there, or good if there is some gold. + + + + +Enemies + +Enemies move in very much the same way as the hero. The main +difference is when they fall into a hole the hero has dug (see Rules of Digging below). They will +pick up gold either always or at random, depending on the game +settings, and will drop gold at random on brick, concrete or the top +of a ladder. An enemy who is carrying gold has a gold outline. You +must get the gold away from him before you can finish the +level. + +If an enemy touches you or you move down onto one, you die and +must start the level again, if you have any lives left. However, it is +possible to walk or stand on an enemy's head and you can ride an enemy +down when he is falling. Some advanced levels require such a +move. + + + + +Losing a Level + +As explained above, you can lose a level and a life by touching +an enemy. You can also lose by getting caught in a dug brick when it +closes. Otherwise you can commit suicide by pressing key +Q or using the menu item +GameKill +Hero. Why would you want to do that? Well, +sometimes you can get trapped in a place from which you cannot +escape. You will stay imprisoned for ever unless you commit +suicide! + + + + +The Importance of Digging + +Digging is one of the most powerful tactics you have. To dig a +hole in a brick to the left or right, just click the +Left or Right +mouse button, or use one of the digging keys (defaults +Z, C, U, +O.) + +You can dodge an oncoming enemy by digging a hole, waiting for +him to fall in and then running quickly over his head. At the same +time you can collect any gold he was carrying. Usually you can dig two +holes to capture and run over two enemies who are close together, but +this rarely works if the enemies are separated or there are more than +two of them. + +You can kill enemies by digging enough holes in their path. It +usually takes two or more holes to kill one enemy and up to eight or +more holes to kill four or five. When enemies die, they reappear +immediately, either where they started the level or near the top of +the playing area, depending on whether the game follows +&kgoldrunner; or Traditional rules and +settings (see Choice of Rules). + + + + +The Rules of Digging + +You click the Left or +Right mouse button, to dig a hole to the +left or right, or use the Z, C, +U or O keys. Only bricks can be dug, +not anything else. The hole will appear in the floor on one side or +the other (&ie; below and to the left or right of the hero). You can +be running, standing or falling when you dig. There must be an empty +space or hole above the brick (&ie; no digging under a ladder, bar, +gold, brick, concrete, false brick or enemy). + +The hero can move in any direction through dug holes, including +falling right through a hole to escape an enemy. After a short time, +dug holes close up, so you must use them quickly. Plan your digging in +advance. There is no time to stop and think while you dig. + +Enemies are always captured if they fall into a dug hole and +will always climb UP to get out of it. Their time in the hole is less +than the time the hole stays open, so to kill enemies you usually have +to dig several holes in quick succession. Enemies always give up gold +as they fall into a hole, so you can quickly run onto the enemy's head +and grab the gold before he climbs out. + +Enemies will never fall down through a hole from above, but +depending on the game rules and Settings, they can +run horizontally into a dug hole and not be captured and can fall from +there or run into another hole or run out of the hole. Several levels +that follow Traditional rules depend on an enemy being +able to run through a hole. + + + + +Winning and Losing the Game + +You win by completing the last level in the game. You lose when +your last life is gone. Either way, if you have achieved one of the +ten highest scores for that game, you can record your achievement in +the KGoldrunner Hall of Fame + + + + + +Scoring + + + +Start with 5 lives. + + +Lose a life and repeat a level when the hero dies + + +Gain a life when you complete a level + + +75 points for trapping an enemy + + +75 points for killing an enemy + + +250 points for collecting gold + + +1500 points for completing a level + + + + + + +Choice of Rules + +Most of the rules of &kgoldrunner; are covered in the section +How to Play, however there are two +major rule settings that affect the style of play: known as +Traditional and &kgoldrunner;. It is +important to know which settings are in force when you are playing a +game. Many Traditional levels are impossible with +&kgoldrunner; settings. Most +&kgoldrunner; levels are easier with +Traditional settings, but the +Traditional games are no pushover. To make life easier, +&kgoldrunner; automatically chooses the correct settings for a game +when you go into it, but you can can change them if you wish. + +The main difference between Traditional and +&kgoldrunner; settings is the method by which the +enemies choose a path to the hero. + + + +In the Traditional search method, enemies look +for vertical paths (ladders and falls) and they try to stay above the +hero or at the same height. They go below him only as a last +resort. The enemies make no attempt to chase the hero in the +horizontal direction until they are at the same height and can find a +horizontal path. This leads to situations where you can +control enemies who are far away from you and make them +work for you or at least keep out of your hair. + + +The &kgoldrunner; method searches alternately for +horizontal and vertical paths, which tends to keep the enemies over +your side of the playing area and at about the same height (&ie; they +are more aggressive and not so easy to manipulate). That leads to a +more action-packed game, but with less opportunity for strategy and +puzzle-solving. + + + +The other differences are listed below: + + + +In &kgoldrunner; play the enemies and hero always +move at the same speed and bricks always take the same time to +close. In Traditional play the game speed depends on +the number of enemies in a level. The more enemies, the slower they +run. The hero also becomes slower, but not so noticeably. + + +In Traditional play, enemies always collect gold +nuggets when they run into them. In &kgoldrunner; play +it is a random choice. The enemies drop nuggets after a random time in +both types of play. + + +Enemies can run horizontally through holes in +Traditional play but not in +&kgoldrunner; play. The trick in several +Traditional levels depends on releasing an enemy from +behind a brick wall. When you dig away the bricks, he runs out through +the hole. + + +When enemies die in &kgoldrunner; play, they go +back to where they started the level. In Traditional +play, they reappear at a random place near the top of the playing +area. This makes a big difference, especially in levels where you have +to get enemies to fetch nuggets down for you, but also because the +enemies keep falling down onto you as fast as you can kill +them. + + + + + + + +The Level Selection Dialog + + +Using the Level Selection Dialog Box + + +The Level Selection dialog + + + + + +The Level Selection dialog + + + + +This box appears when the game starts and also when you select +options from the Game or Editor +menus. You use it to choose a game and a level to play, edit, save, +move or delete. The main button at the bottom is the +OK" button. Its label changes according to what +you are doing: in this case choosing a game and level to edit. + + +Selecting a Game + +There are several System games that come with the +&kgoldrunner; release and they appear at the top of the list box. If +you have composed games and levels yourself, they appear below the +System games. To select one of the games in the list, +just click on its name. + +When you select a game, the dialog automatically shows what rule +settings apply (see Choice of Rules) +and how many levels there are. The More Info... +button shows you further information about the game. + + + + +Selecting a Level + +When you are starting a game, the level is fixed at +001. In other cases, such as this example, you can +choose a level. The default selection will be something reasonable, +such as the last level you played or edited. + +You can select a level either by typing it in or by moving the +slider. The arrows at the end increase or decrease the number by +one. Also you can drag the slider with the left mouse button held down +and change the number rapidly or you can change it by 10 if you click +in the space to the left or right of the slider. + +As the level number changes, a preview of the level appears in the +small preview window and the level's name (if it has one) appears +below the slider. + + + + +Completing your Selection + +When you have chosen a game and a level, just click the main +button at the bottom to proceed with your edit or play. + +Until you do that, nothing changes, so you can always click +Cancel and go back to what you were doing +earlier. Note that the game action is frozen while this dialog box +appears, so you can continue playing where you left off if you choose +Cancel. + + + + +Saving Changes + +If you select an action on the Game or +Editor menu and you were previously editing and had +not saved your changes, you will get a message asking you to save, +abandon or continue your work. The level selection dialog box for the +new action will not appear until you have made a decision about your +previous work. + + + + +Playing or Editing <quote>Any</quote> Level + +Note that the menus allow you to select and play or edit +any level. If you start a game after level 1, that is +OK, you will just not get such a high score. Dedicated players often +prefer to play high-numbered levels for fun, rather than go for a high +score, or they might like to train on higher levels +before attempting a high score. + +If you select a System level for editing, that is OK too, but +you must save it in one of your own games: not back in the System +game. If it is a very difficult level, you might want to change it so +that you don't go back to the start of the level every time you +die. + +To see what this means, try the Challenge game, +level 16, The Three Musketeers. That level has about +ten difficult puzzles in it. When you have solved puzzle 1, you don't +want to keep repeating it while you work on puzzle 2. The secret is to +make an editable copy, then keep updating it and changing the hero's +starting point, until you have solved all the puzzles. Then you can +attempt the real thing. + + + + + + +The Menu Reference + + +Overview of Menus + + + +Game + +This menu contains options affecting game play, such as starting +and finishing games, saving games, loading saved games, showing high +scores and getting a hint (if available). The +Game Menu also contains the +Quit option. You can also +quit by clicking the X at the top right of the +&kgoldrunner; window. + + + + +Editor + +This menu has everything you need to create your own games and +levels and maintain them, including features to help you re-order levels +or move them between games or to delete them when no longer needed. When +you reorganize your levels, the level numbers in your game remain +consecutive (no gaps) and the levels are automatically +renumbered. +When you are using the Game Editor a graphical toolbar with tool +tip text appears under the menu bar. See the Game Editor for more details of how to +create and edit &kgoldrunner; levels. + + + +Landscapes + +This menu offers you a choice of landscapes (actually color +schemes) in which to play. All the landscapes have keyboard shortcuts, +so you can actually change the landscape as the game is playing. Try +Shift+A, to switch to the nostalgic Apple II landscape. + + + + +Settings + +This menu has four groups of settings: the choice of mouse or +keyboard control, the game-speed options, the choice of of +&kgoldrunner; or Traditional playing rules +(see Choice of Rules) and +lastly the options to increase or decrease the size of the playing area. +The current selections are checked. The settings are automatically +selected when you start playing, but you can use this menu to vary them +if you wish. +On the &kde; desktop version of &kgoldrunner;, there is also an +option to re-assign keyboard shortcuts and hero-control keys. + + + + +Help + +The Help menu contains an option +to get a hint (if available), access to the +&kgoldrunner; Handbook (this document), About +&kgoldrunner; and About &kde; (or +About Qt Toolkit). +In the &kde; desktop version, there is also an option to report a +Bug or Wishlist item. +If you would like to make a suggestion or you have some new levels +to contribute, the current author's email address is in the +About &kgoldrunner; menu item. + + + + + + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N +GameNew Game... + + + +Shows the level selection dialog box, where you choose a game +(which will start at level 1). + + + + + +&Ctrl;O +GameLoad Saved Game... + + + +Brings up a table of previously saved games, sorted with the +latest first. Each line lists the game, level, number of lives, score, +day of week, date and time. If you select a line and click the +OK button, that game starts at the beginning of +that level, with the lives and score you saved. + + + + + +GamePlay Any Level... + + +Shows the level selection dialog box, where you choose +a game and a level to play. + + + + + +GamePlay Next Level... + + + +Shows the level selection dialog box with the game +and level set to one more than previously. You can then choose to play +that level or another. + + + +S +GameSave Game... + + + +Saves the current game, level, lives and +score. The option will only save the position and score as at the start +of the current level. You can save when you are mid-way through a +level, but &kgoldrunner; will still save the position (and score) as it +was at the start of the level. You will receive a warning message about +that. Before using this option, you must press a Pause key +(P or &Esc;) to freeze the game as you move the mouse, +but it is much easier to use the S key as a +short-cut. + + + + + +&Ctrl;S +GameSave Edits... + + +This option is enabled only if you have been using +the Game Editor. It brings up the level selection dialog box and lets +you choose a level number and game in which to save your work. Its +action is exactly the same as the Save +Edits option on the Editor menu and +the disk icon on the Editor toolbar. + + + + + +P or &Esc; +GamePause + + + +Stops or restarts the game action. + + + + + +&Ctrl;H +GameShow Highscores + + + +Shows a table of up to ten high scores for the currently selected +game. Each line shows the player's rank and name, the level reached, +the score achieved, the day of the week and the date. + + + + + +GameGet Hint + + + +If the current level has a hint, this option pops it up. The +option is grayed out if there is no hint. The status bar +also has a section to show you whether there is a hint for the current +level. It is customary for championship and tutorial games to have a hint +on every level. + + + + +Q +GameKill Hero + + + +Kills the hero when he is in a position from which he cannot +escape. + + + + + +&Ctrl;Q +GameQuit + + + +Terminates &kgoldrunner; immediately. If a game is in +progress, it disappears forever (unless you have just saved it) and +there is no check for a high score. If you were editing a level, you +are given an opportunity to save your work. + + + + + + + + +The <guimenu>Editor</guimenu> Menu + + + + +EditorCreate Level + + + +Shows the Editor toolbar and a blank playing area +on which you can draw a new level. When done, use +Save Edits to assign the new level to a game +and level number. + + + + + +EditorEdit Any Level... + + + +Shows the level selection dialog box, where +you choose a game and level to edit. You can choose a System game and +level, but you get a warning that you can only save the edited level +in one of your own games. + + + + + +EditorEdit Next Level... + + + +Shows the level selection dialog box with the game +and level set to one more than current. You can then choose to edit +that level or another. + + + + + +&Ctrl;S +EditorSave Edits... + + + +Shows the level selection dialog box with +appropriate default settings for saving a new or edited level. You can +change the game and level number and achieve a Save As... +effect. This dialog box has a special button, Edit Level Name and +Hint, which shows a dialog box where you can add an optional +name and hint to your level. + + + + + +EditorMove Level... + + + +This is a two-part operation. Before using Move +Level, you must load a level to be moved by selecting it +for play or edit. The Move Level option +then shows the level selection dialog box, where you choose a new place +for the loaded level. You can change both the game and the level +number to move a level to another game or you can just change the level +number to re-order levels within a game. You cannot move a System +level. + + + + + +EditorDelete Level... + + + +Shows the level selection dialog box, where you choose a level +to delete. You cannot delete a System level. + + + + + +EditorCreate Game... + + + +You need to use this option before you start +creating your own &kgoldrunner; levels. It shows a dialog box where you +can enter the name of a new game, a 1-5 character prefix for +level-file names, the default rules for your game +(&kgoldrunner; or Traditional) and an +optional description or comment about the game. + + + + + +EditorEdit Game Info... + + + +Shows the level selection dialog box, where +you choose a game to be edited, then shows the game information in a +dialog box where you can edit the name, rules and description, but +not the file name prefix, in case you have saved some levels. That +is because the prefix is used in level-file names internally. + + + + + + + + +The <guimenu>Landscapes</guimenu> Menu + + + + + +&Shift;G +Landscapes&kgoldrunner; + + +Gives you the default &kgoldrunner; landscape, with +red bricks and wooden ladders. + + + + + +&Shift;A +LandscapesApple II + + +Gives you a nostalgic look at the original Apple II game, as it +was when you plugged your Apple II into your TV set — otherwise you had +a monochrome monitor. + +Actually, the hero was white, the enemies had white +pants and orange (flesh-tint) tops and the gold was white boxes with orange +panels. The whole effect was fuzzier (a sort of hardware anti-aliasing), +which made the bricks look almost three-dimensional — difficult to +recreate on today's pin-sharp monitors. + + + + + +&Shift;I +LandscapesIce Cave + + +For fans of the movie Superman or the +James Bond movie Die Another Day — or you can +use it on a hot day. + + + + + +&Shift;M +LandscapesMidnight + + +For real &kgoldrunner; addicts, on those late-night sessions. + + + + + +&Shift;K +Landscapes&kde; Kool + + +Inspired by the icon colors on the &kde; desktop. + + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsMouse Controls Hero + + +Sets mouse control of the hero +(see Moving Around). + + + + + +SettingsKeyboard Controls Hero + + +Sets keyboard control of the hero +(see Keyboard Control). + + + + + +SettingsNormal Speed + + +Sets normal game speed (12 units). + + + + + +SettingsBeginner Speed + + +Sets beginner game speed (6 units, half of normal speed). + + + + + +SettingsChampion Speed + + +Sets champion game speed (18 units, 1.5 times normal speed). + + + + ++ +SettingsIncrease Speed + + +Increases the game speed by one unit, up to a maximum of 24 +units (twice normal speed). You can use the + key +as a shortcut. + + + + + +- +SettingsDecrease Speed + + +Decreases the game speed by one unit, down to a minimum of 3 +units (a quarter of normal speed). You can use the - key +as a shortcut. + + + + + +SettingsTraditional Rules + + +Sets Traditional default playing rules +(see Choice of Rules). + + + + + +Settings&kgoldrunner; Rules + + +Sets &kgoldrunner; default playing rules +(see Choice of Rules). + + + + + +SettingsLarger Playing Area + + +When &kgoldrunner; starts, it chooses the size of the playing area +(in pixels), based on your screen's resolution (e.g. 1024x768). This +option allows you to expand the playing area, up to a maximum, and +enlarge all the corresponding graphics. + + + + + +SettingsSmaller Playing Area + + +When &kgoldrunner; starts, it chooses the size of the playing area +(in pixels), based on your screen's resolution (e.g. 1024x768). This +option allows you to reduce the size of the playing area, down to a minimum, +and shrink all the corresponding graphics. + + + + + +SettingsConfigure Shortcuts... + + +In the &kde; version only, this item allows you to change the keyboard +assignments (⪚ for keyboard control of the hero) or to assign your own +shortcut keys for menu items. + + + + + + + +The <guimenu>Help</guimenu> Menu + +In addition to the standard items listed below, &kgoldrunner; has +the following special entry: + + + + + +HelpGet Hint + +If the current level has a hint, this option pops it up. The +option is grayed out if there is no hint. The status bar +also has a section to show you whether there is a hint for the current +level. It is customary for championship and tutorial games to have a hint +on every level. + + + + + +The following are standard items: + +&help.menu.documentation; + + + + + + +The Game Editor + + +Getting Started with the Editor + + +Creating a Game + +Before you create or edit a &kgoldrunner; level, you must create +a game in which to save it. Use Create Game +on the Editor menu. If you +forget, you will be reminded. + +The most important decisions to make when creating a game are to +choose the rules you are going to follow (Traditional +or &kgoldrunner;, see Choice of Rules) and to choose a +unique 1-5 character file name prefix for your game and levels. You +also need a name and description for your game, but those can easily +be changed later. + +The prefix is used internally by &kgoldrunner; to identify level +files, high score files and saved games. You can use your initials as +a prefix provided they are not the same as a &kgoldrunner; prefix. So +far, the prefixes level, plws, +wad, plwv, tute and +tutea have been used and trad and +chmp are reserved for future use. + +The four-letter tute prefix is reserved for +tutorial games, which show the game description and hints on levels as +they play. If you compose your own Tutorial game you could use the +prefix tutex, to make it run as a tutorial but not get +its files confused with those of the basic Tutorial or Advanced Tutorial +(prefixes tute and tutea). + + + + +Creating a Level + +When you have a game set up, use Create +Level on the Editor menu to start +creating a level. It provides you with a blank playing area of 28x20 +squares, with the hero at the top left. You can put the hero somewhere +else if you prefer and you do not have to use the whole 28x20 +area. Lots of interesting levels use smaller areas. + +The minimum requirement for a level to be playable is to have a +hero, a gold nugget he can get to and a visible or hidden ladder +leading to the top of the playing area. You do not have to have +enemies, bars or even bricks. There are many challenging levels that +have no enemies or no bricks. + + + + + +Editing and Testing + + +Editing a Level + +Use Edit Any Level or +Edit Next Level on the +Editor menu to start editing an existing level or use +Create Level to start editing a new level. + +If you choose +a System level, you are warned that you will have to save it in one of +your own games. Otherwise, the default is to save the level back where +it came from, but you can vary that. + + + + +Painting Objects in a Level + +The picture below shows the menubar and the edit toolbar, which +appears when you first choose Create +Level, Edit Any Level or +Edit Next Level from the +Editor menu and so change from Play to Edit +mode. It disappears when you go back to Play mode. Note also that, in +Edit mode, false bricks and hidden ladders are made visible in the +playing area. + +The three icons on the left of the edit toolbar have the same +actions as the menu options Create Level, +Edit Any Level and Save +Edits. The other eleven icons are for editing and +painting objects in your level. + + +From left to right the other eleven icons are Edit Name/Hint + (light bulb), Empty space (background), +Hero, +Enemy, Brick (can dig), +Concrete (cannot dig), Trap +(can fall through), Ladder, +Hidden ladder, Pole (or bar) and +Gold nugget. + + +Editing a level + + + + +Editing a level + + + +The Edit Name/Hint icon pops up a dialog in which +you can create or edit your level's name and hint. Both are optional, +but they are essential for tutorial levels and it is usual to provide +them with very difficult levels (see the Vengeance of Peter +W game). + +When you click on one of the other 10 icons, the mouse becomes a +brush that paints that icon. Initially the brush is set +to brick. + +You paint either by pointing and clicking the &LMB;, to place a +single object in a square, or by holding the &LMB; down and dragging +the mouse, to fill a line or area, such as a long ladder, a large +block of bricks or a concrete floor. Painting stops wherever you +release the mouse button. If you make a mistake, you can use the +Empty space icon to erase it. + +The hero icon works differently, because there can be only one +hero. When you paint the hero, he moves from his previous position to +wherever you release the mouse button. + + + + +Saving Your Work + +When you have finished painting, save your work using the +disk icon or Save Edits +in the Editor menu or Save +Edits in the Game menu. You must +always save into your own games, never into the System games. + +If you are creating a level, you use the Level Selection Dialog +to assign it to a game and level number. You use the same dialog if +you have been editing a level, but the default is to save it where it +came from. You can change the game and level, to get a Save +as... effect. If you have been editing a System level you must +save it as a copy in one of your own games. + + + + +Adding a Level Name and Hint + +On the Save version of the Level Selection Dialog box is a +button marked Edit Level Name & Hint. You can use +this to put the finishing touches on your level by adding a name and +hint. Both are optional, but they are essential for tutorial levels +and it is usual to provide them with very difficult levels (see the +Vengeance of Peter W game). Of course you can also use +the Edit Name/Hint icon, at any time, to add or edit a +name and hint. + + + + +Testing a Level + +After saving an edited level, you can test it by using +Play Any Level in the +Game menu. By default the game and level number are +remembered and you can get straight into the level with just a few +clicks. If you then want to edit some more, the same is true when you +use Edit Any Level again. + + + + + +Reorganizing Games and Levels + + +Moving a Level + +You can use Move Level on the +Editor menu to re-order or re-number the levels in +a game or to move a level from one game to another. Move +Level is a two-part operation. You must first load a +level by selecting it for editing or play, then when you use +Move Level, the Level Selection Dialog box +appears and you can select the new level number and game (as +required). + +Moves leave no gaps in the sequence of level numbers in a +game. For example, if you move level 10 of game A to level 3 of game +B, levels 11 and above in game A are re-numbered down by one (to close +the gap) and levels 3 and above in game B are re-numbered up by one +(to make room for the newcomer). + + + + +Deleting a Level + +You can use Delete Level on the +Editor menu to remove an unwanted level. The other +levels are re-numbered so as to close the gap in the sequence. + + + + + + + +Questions, Answers, and Tips + + +Overview + +This chapter presents some general hints for playing &kgoldrunner;, +as opposed to the specific hints you will find on the levels in tutorial and +championship games (see The Help Menu). +The hints are grouped as follows: + + + + + +Dealing with Enemies + + + + +Digging Puzzles + + + + +Tricks with Gold + + + + +Solving Difficult Levels + + + + + + +Dealing with Enemies + + + +You can always trap a single oncoming enemy in one hole, get his +gold and run on over him without killing him. It is often a good idea +to dig behind you and delay him further, but that might kill him. In +some levels it is not a good idea to kill enemies. + + +You can usually trap two oncoming enemies in two holes, get +their gold and run on over them, but this is dangerous if the enemies +are separated by two or three spaces. Sometimes two holes kill one of +the enemies and the other escapes and sometimes both escape. + + +It is nearly always impossible to trap three oncoming enemies +and run on over them. You will have to kill them, dig and drop through +the floor or simply run away. + + +Try to get all the enemies running together in one group. That +way you will not get surrounded and you can do such fun things as +collect gold while they run along behind you. + + +Sometimes you can kill an enemy with two holes, but it often +takes three to nine holes to kill one to five enemies. + + +It is possible to kill an enemy with one hole if he is far +enough away when you dig it. + + +In the Traditional game, you can trap an enemy +permanently in a pit of brick or concrete. Stand near the edge of the +pit on the opposite side to the enemy. As he approaches the pit, move +down one square (to the level of the floor of the pit) and the enemy +will usually run into the pit. + + +In some levels in the Traditional game, you can +force an enemy to climb a ladder ahead of you if you stand somewhere +above the bottom of the ladder. + + + + + + +Digging Puzzles + + + +To dig through more than one layer of bricks, start by digging +as many bricks as there are layers, jump in, dig one brick less and so +on. + + +If you dig next to a ladder or a succession of bars, you can dig +down through any number of layers and up to five or six bricks +horizontally. Just return to the ladder or bars after digging each +layer, then move down one square, run out and dig the next +layer. + + +If a digging puzzle looks impossible, consider that there might +be some false bricks in it. Maybe the bottom layer is false or maybe +you can stand in a false brick and dig next to it. + + +Think about the order in which you will collect the gold. If you +pick the right order, the digging might be easier. + + +Some levels require you to dig away a brick walll, run in, +collect some gold and run out again before the bricks close up and +trap you. + + + + + + +Tricks with Gold + + + +If you cannot get to a piece of gold, think about getting an +enemy to go there and get it. Even think about using an enemy's head +as a bridge to get across a pit or precipice. + + +In some levels there is a nugget that must be collected last of +all, because you will need the hidden ladders to get away from that +position. + + +In some situations an enemy can be killed without giving up the +gold he is carrying. The gold becomes a lost +nugget. You score no points for making an enemy lose a nugget, +but at least you can finish the level. + + +If there is nowhere to dig, keep the enemies running over +concrete and the tops of ladders and so make them release their gold +at random. + + + + + + +Solving Difficult Levels + + + +Try changing the speed settings to Beginner or even +lower. + + +Use the Editor feature to peek at where the false +bricks and hidden ladders are. + + +Use the Editor feature to save the level in one of your own +games, then you can edit it so that you do not always have to start at +the beginning when you die. This allows you to develop the solution in +easier stages. + + +In digging puzzles, look for false bricks you can fall into and +thus have one less brick to dig. Or consider using an enemy to go +ahead of you, so that you can stand on his head and dig. + + +Look for a theme or trick in the level, such as riding down on +an enemy's head, trapping all the enemies in a pit, luring the enemies +to a corner where they will stay and not chase you, luring the enemies +into pits you must cross to get to some gold, getting the enemies to +fetch gold for you or finding spots where you can stand and make an +enemy move to where you want him. + + + + + + + + + +Credits and Licenses + +&kgoldrunner; copyright 2003 Ian Wadham and Marco Krüger. + +&kgoldrunner; is inspired by an early computer game called +Lode Runner which was written in the USA +by Doug Smith and first released +in 1983 by Broderbund Software. It +appeared originally on the Apple II and Commodore 64 computers, which was +where the Wadham family and Marco Krüger got hooked. It was a major +best-seller in its day and is one of the all-time great computer games. + +You can find out more about Lode Runner and +the various versions that have come out since 1983, on the +website Jason's Lode Runner +Archive (http://entropymine.com/jason/lr/). +That site also has the story of the game's original development, in the file + +misc/ldhist.html. + +&kgoldrunner; is an attempt to preserve the spirit of the original +classic game on a platform that will be portable and will last more than +a few years. It is available as free software in source code form. Copies +of the original game are unobtainable now and the machines it ran on are +going into museums. + +Marco Krüger developed &kgoldrunner; as far as v0.3, out of +nostalgia for the original Commodore 64 game. Ian Wadham added +several features and levels and created the current version. Ian's +elder son Peter composed the 100 levels in the "Initiation" game and +the 20 tricky levels in "Vengeance of Peter W". The other levels +were composed by Ian, Peter, Marco Krüger and friends and Ian's other +children, Simon and Genevieve. + + +&underFDL; +&underGPL; + + + + +Installation + + +How to Obtain &kgoldrunner; +&install.intro.documentation; + + + +Compilation and Installation +&install.compile.documentation; + + + + + +Porting &kgoldrunner; to Other Platforms + +&kgoldrunner; is written in C++, using &Linux; and the free-software +version of the portable &Qt; object and &GUI; library. The current version +can be compiled and run either with &kde; 3 and &Qt; 3 or with &Qt; 3 alone. + +The &Qt;-only version of &kgoldrunner; is intended to be almost +independent of &Linux; and the &kde; desktop. That means it should be +fairly easy to port to other operating systems, such as &Windows; +and Macintosh, because the &Qt; library and C++ language are portable +to other operating systems and the &kgoldrunner; source code is +available as free software. + +If you would like to port &kgoldrunner;, please email +Ian Wadham at ianw@netspace.net.au, to obtain +the latest version of the &Qt;-only code and the Technical Details +document. Of course, you must have your own C++ development +setup on your target machine. Also, the &Qt; library is +not free software on &Windows; and Macintosh, +so you will need to have access to a system with a paid +&Qt; Development licence. + + + + + + diff --git a/doc/kgoldrunner/kgoldrunner.png b/doc/kgoldrunner/kgoldrunner.png new file mode 100644 index 0000000000000000000000000000000000000000..23429eb4aa8f08e9eb92da0f763d32d688377b0c GIT binary patch literal 803 zcmV+;1Kj+HP)ojhnbh>alW(nTAF73LFL%G`mq*Vj z^>CL60^BNfGy&=?vkZ4WnaBK0HQD_BY&y3xxwyMFNhIsi6u$a;k7$TXgw^0N_UR}E z+s)ffD^HO9HQVi9(`#&RYdZZ%J~mZL;#zmAJB@4C0~)uBA*|tU@ODX)(^fnz!p8y5 z^DgVqjXLN?*;p+~qAae?V=Ss8%0*c>9FMFQhU>wnJSaQ8rg&JOcvK)fDBYvC zQ}Zb5Pgcjexq79F{^X);u0QA-WCmSSE75N7W1?Yf8{q~1+)*-$AD@eg{_jnZhaMz$l=-6OR3TT317yZpoSxOPJ|i922dioldWFF@<=ah7YlJ0<6Z5gKhE2USG>j?Ni hy~0mePgq|a{sF`BCRK(BztsQ$002ovPDHLkV1oZwd(Hp= literal 0 HcmV?d00001 diff --git a/doc/kgoldrunner/level010.png b/doc/kgoldrunner/level010.png new file mode 100644 index 0000000000000000000000000000000000000000..06126f83941fc4a7f073d36d41d67eeb53af1c43 GIT binary patch literal 844 zcmeAS@N?(olHy`uVBq!ia0vp^{Xm?~!2~3?iM441DYhhUcNd2LAh=-f^2tCE&H|6f zVg?3oArNM~bhqvgP>{XE)7O>#GAA#)pyj&hhXoiIm~MKyIEGZrc{|rOPs&lkVfqPI zuIYi3n4j=1>=1d*qFAd^yruVv-4B*R74av0G7J;E#Wj^C9@0L|V_@+6|FrK?k5kKk z9JzevyP+}n;;&gVIazZXcU1f`p8s@io8oav|7GiU^*IV%5aZ09h(_$VQT-)wLLJh+T(D$COEL-Yd;ah*DG%DvTBn9iDt1|$cEt_Yg+CJ7-6 zbnee@cdOqUzO=uW%;X%cvc=U2l~^?6^a-SI+E_$bn(;Kx+D*UX1bc z%li5Gd^~Tf;D_J0r{8U!JH^=aN=s~E+iT6f7Dym{d%pHZTZa^9(fNtJ61?oi9skah zyxD#=l*#_^A;r$M6ANyf=4C#x2Na&$>U7`!{9L)={f3>o{GQd?lgyg8g=ri;?4h(z zWy;29Mw9M!IEv}O4ECth_VYJuHj`?r1%+1zoS3rx>a9;+_SXwnxrF|T0y%o3#=hU5 x^NOyPsuZI_Wx44$rjF6*2Ung9#odvO2& literal 0 HcmV?d00001 diff --git a/doc/kgoldrunner/select.png b/doc/kgoldrunner/select.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c50766b46f5c9a1691464bb541f2cd5a8f74fc GIT binary patch literal 20980 zcmaI81yohxx;4Cz5D^drq#Nn(M!LHj>F!QJKtj5^LApy?y1To(yZ`06=iKj%bMO7P zW20lS*1O+$Vm@;|+h92vQ3N%Eb>TS8#dBH-yElV`&Ba9!ou&qroF=En==)Ji!2l8_qEP-( z)*As|^jI@ABsfa!`9mYBO=bD07S%z^tjCcpl2d$&S&LKC_w{4vo_zO9vM2rO9B$qm z#2g&4efeAKQ`(2>XFA=ouYr!pmAP69c$Kc@L zK;{yl%!QdNcNuP5Vs?PibjkB0kj*z>F~X!7M#-e`&ENm%VD7wqMMtPkvQ-3=7#+ZW zJ!LKNaKwLBzUwukS5Sb>4vz12>n3MGf14@s#2oF;2?%(5XGlF$&XIbgD{aD2E{ z=$mkF_2d>`I9b3 zZ0$szNgBRCe}pJhXYQ`a6xlu|*=`nAYZNP}V**;p5&SoH2=K#WK{cH@Dg}l#U@}B3M825b(#Bpf9<60O1KL zGI;{jC~NTH%b3rcSmzl15rTBi>kh8TfgF#=MJ zjD9kk6T)Vbaao;7A|d(v;rg!tcf4; zI4&$(uFG?RuS`~PS#=KAQ7|y*O~1SvJr8hQP@gM*D1YjrwmOmEoKrMD-pJ1tWpfBU zGs+*;#FFD3>g>!BzRVwN)44icJ9`p+yhyojyei34p&u(XIJC0!T1HjkuQt)7rjwJq zU-~!+3&c({8pkA&WxuyNGW$)dXfiZ~?Icp|7gvZ{qS0X;+HBtDA=lu*Y!t<%=s%f1X#aLQZC27)CuO zVb=bmu5yqWB&yTHNx)gYP0Z~1Z;?>S5OWC2dfyzy4BmZ?L%m1yBJ`2;(zl`Sq1U1v zV%+<9%^%8=a<+-CPtR#Bunmk}%}tkozfLuV<>eZ=H5QxsGQKI$GlEkQAx9F1zE6(3KLi;cD;2A|Eg9t(h zg1$h~!_$~xLemXSVPYn$P@(BqCWqu2E37p7F)>2}A}cSozxKw_7zrJv>WOb*_~E{A z^zmNU>MbukL!Np}a>?r6wo3mtR<58cSRB0@Fft;OO>cH(yS@_a))hCRnmUwGoYG_{ zN+f915?OZZd9@XJWxveWsZ}XHMkyX~QW}xdHYYdm+t#5>&TelHHmJ)hcU_c(tFqE) z`oNkm{)p_&2+Y+P2`+mxfrF(_{wYx^m&$X;@intr_NuMX63N5%=SlcUW|IVb2{E}; z7hEiY`z-lvKPUucs-u2>sD1gH?+PxddWo|@u|OuRs90q9`K9Xw|Mw{G8~f6jqhKP( zva%w4qFcG~eVFyxrX3!1gr`f7{D_FrM}*qxC!?9~jX3@NPhqIuFY8j7R_ z01>PZk`S_Kvaz~I&zN!gTO>4l%!K={A>^j8x9LI7_4yRVR8USl(d@7y-$21@BomJ1vu;cO8T4iu{uO3@M0hBCDOH0@@!7ne*6&;5P4kx{+ zwN(-IvDs7St`Apcjlb&Z?&t3cc$-x^lgBGp%zvvo9b816((I=%au&aKm7_5hIW^ed z=0gSiEJ7zqzq2mMS>jyHWlx~hriA-5-$IoVAtiUOWSicyGJbFqHIQ+GcTJ;u-ejcf z9HhkR(4^0rL@hr}*(;OOlBoQ|6m22Cx9N@RJiKRk-vrLXsCUD4w_O!qN;fZ3o<<;m zuWqRNMsHrg(-5#_4Q5K>2uH`6Peh&3+qe>;mCT3{+-1GlGs}m~?&!iC<`#vOg@R5| zEHG2DM;1ya6Ja8I!|k5j|89+>0ac=d(U1JX@0a{GyaU%eS{8L4CDd}+f?*+MlymV< z>(C6d@o_Ix+Brl*zfhI(OA30VOXVokYNMSLsRrE5sh|@|RT#b?3E{hh5i)ffGVZ|9 zB4EQ3+4`j&?|e_|-RrQ$TshNTZkd6Gg;B24LLgN)(%6xIDfCF@W{QGNTCOmIu@kkQ z?r9N?$e=$TN#laxwe~8?1$P2ypSxX^@^+=mHlN`_FyJeLj>@z zPmey$R9q(P7KbgYx(`^76a_54KAI?m%#%U@1MxJ2rp+7>Dz}&MtTI4Iz9;k5Y%yEe z-98lXOSx#ZHN!HPgqHT?C1)#j=IG~FAXACb=BbL8GqE;B)2zCS?(^4q65hKt$mEap z*7HZl$8jb%xx08Myq-*%GWFQoFu<3^!0DIUP9=&WL|jhb%Tsfq(`wqQY}gfdmf#OW zc@B7yv75=g&&s3?6k#Dg+?L2FH!ePBd4`kRI7PTE$ou#*G|A z&`^k`!0kuCSy^d<$>|G=Bg-z}s#D`1O@3Io4ZH42gtV?67(S=g;B+P`Tb(Os{<+wz zEZcz%kNw@t@~dNfjVTjdvfH>dJ_8SvW{;c@65WcsVSSk0`ivkyYOQ&rOaas0b)%o8 zG#84^>yCT&A3ay#;Lvy5v*}C-o_E>Fs+Mz|H&cx1+3kh$x~=K0O)f6=YkiIfQ_cPXG+MRviIpsNUJqA%Iy}e0 zMSwkq;M?+&gk}cI=&xPyPBPUn$Gx$v?MGT@7?_{tMGU!ocIiBlAAU)sM7-OoDLhGC zp*;SjL|JI-ci>oG8U?vL|2F3HPGNn|y-!LQ{}BhcWt|n-KhS zFDfi9JK_6=j5R(!4_zn4mI{m&w;aM)~u5Hof66;h|eH6a37CdZktr(^{v z{I(xciS@SH`yhb6At>NmJ=JN@c2SnIU5p0_qp@z!$T?@EHuF z*YK|PV9NMthFITf6gSyr#WP6L6bGAP>{@e|9opsAEad^ppJuX%+;UpsTHPG3FH?EH z51s^1_+jK56U>%hy#RLar@Lv3Soc#N4cZ7%bvUs|HF zE3CDMs}@&kX^l|z(aW4H`GjiO=mSe*gU%Gj-grGJf$Gz9*uXjOMxpP zGs>Fe7MO3A{QGiG-8{bIJ6R)}CrZRBlm+B`P~fC)rk!<{6rp$T%&AL9 z|KVbDbb9*k%c0bt=O#8lZ`({eVnDs^_2u!j-l?FRrx<&u(r7R~Z?+onw6t0Ir1F~V z^*qEc+Bl4o9%Nwy1(VP?UG9=#@qRYFfRl4zC1i5mWdskG-F8jL=aspo!Six`JV*2` z%N|)blYUnaZqsdY|!^oo_aO&ls~b8LxPPpXB$jY;t>U@!_#|eqY8T)2-y( zePhRsvv{yrTsQMP{MyWiy)QasakV=B?P9oNVph&NiE3c(t`X7-M| z(Pgd6Xz1q3)F0ei)A4%yw%gfyHEZ&86)}Dy&RQYfYcMLjRlT>6DHP=c6^~$c z+nwRsF!3tKKzh`7ZL@J{m{-w5?U7kQ2KT-8wNIUYJcgsQIX!MH+wO|_mCeJBSGlWZ z(d#hdC$v4exvHKk=yrz6C(Qh``Rd5-Ms42+D_@#GtgVMtzZnzCOkKo~6;uBpjrqaJ z+mf1T5g;PpVhqe5cyxogYqo2_1Yl~tP2aE^=jO%>K*0>u;f<+6} zWhbigymmX>h_Kw`u@drWx#=O>3m%X7-uSqlv_U4r)R(F%W0fmXs>S)mRWr&%q4XvV zd1?fA}yG{VByi)|$0aLH&oi!4U&V=c^OcbpC)RfhX&x`$o-<{R$rPXfhv zsDmh(FVC=ZGd*}D1ypDkPjhPVE3g2;8!CenUiY3cci(hN?8H2_ zkHrw!q8-rux#XGOvW5HlSUEo+0;II6`2GC#IwzRF5Y3ux4_{@Tyo%+ALu!kl0-)}e zJW`$aZ*quzw76atq2mPhPw;yh9#KRH=Y;Idln%1)QF+-`z11W@c==BVv znFlNnJ=)#!i*3}1=VfNAPZ{Ody4tdB{hN)g;na=+RtMM`~1~OL{rhamnD6MS! zD!*?J@}sgU5pd*s4XLgA_{wU+?VSF(@kd)9xkL9^bO*%RJvK4{6N9rUY-UJJ(59#}3 z3lbzhwU)_apDUtB=RzUAwOT0BJ-Z97;|fvm2x-u=*TtaMF=%WTz6(*f58xWb7^XCf zD=1BCGqfdsJ^A1t|uLVPq6!+)$K`)Ch;%o^_O$}P?ewN@Z>3L+CKKTNsSf!~tsFDwrw z?;0I@b4N?|@#R~)?-wC{S7-?lIAjU;&tcJ=tX~-XzOw)EDcm zn!FB4b?9@i;QHSkFVv1k)ECHRs1&P&eZ^?j6Wd3N&NHhwJ>LlGGk<~bMeC$gUvgl1 zijIz6aM@F%C2tVyl@S*g4{y%P&qs81(YuEuM2?!K{=w;Zgo>xbLQtV)SbK1OejY!C z?s;z%5IGhcLu$291417SwY&jc@9Q|nD=;bZc-+9j!If#2zXZpS9q*uMjdKdiiP0LY z{}JY@v+;P^%bF64N%m~S@;!bXcUHrr4V*XYK{_P*1@RiVE>3!BaI)A8)9*VB20aOfw=ANjHw3|t%{U!rwEw( z3@)1Ccs$N8O9BA`UysFtDy$xFL_gr~Be=)(8`|MB8dlUWISrr!K|g~kaF%`6+HY{V za=k*bVF}?ox5zSJFBbf-&U-soJZ$=s_+Ek{B2Ya$1?b$xJ8=_{r~cnApz+nzk1pf7?6W|}ARm(#dhUBd|_BDQetWbI}P9-AYd`uqAI5?)XtXbz8=k2N9Bk}E@-xS3qIZ) z=VWKcQcl-e%$Bn3h!q2J)yBgO4IKOXnEsJdQ%a)uLXoO8B)WV)gYk!Bnl`tESjd>D zTHaTQ47y(TpMk4`X(m$EnT65jMhBPEc@!)SyU~@F*Ce)Cc}hFGYLt&N%$8FPioJ;D zwOGK;ND`C&Rv#Q;mepb%m*(j{B?p^|alE42zJn>u^<&RVq`O#hf!@1}kpj$Ra?s44c z#();^Lv?syB;SmamDRZ83EEXYYIg7teIoSy(5CITSuLZw-7ue@0OdzlH}{Qh!4Sl| z8@7M=;Q#Zl4FxR@^V{)?uWFfMm3oIW84A%EsPcpm?I{`0{^;*00`w*k|K+Qw3M*G( zQD}R=9A2TX0|`D0iH)egqSGBO{dOcx2-UvWN8p!8h@|#>kby*Z7mWN!mD|ctwTk%4 zT&LNU8F-QsFI~;P@C^NJ^eyX4W~0sPLP&%L^vV5;r09wQ-pcdQvdHO_vKMcK-9TrP z%!EK>Gyw^4cRV&De*U;?#Zm9G+{Oz}Qafp1I}bX=0_*4V?!ZotIMY`Qai|r|j?W-J-E_SFftu6)A^`#%hc@R%@iUN~<@ zWQe|+#As!i&zjtoO*`mP(?C(k2n@V>Uevx~evaustUW=S{+0GK?o_9VV3ge~*{!k$ zsY|)}r2|H!=YVmN+9%>v?ALO$g{fhQR+hW%BOI_-&1BgE@kQu3|1FzOr^C+ol(s^w zOBfx=T&4GgcD>%jTrZapIld?fo{#ryz6eXaxMORlijO&~d+Y|bikdb6z3IfnYJ?^B zRNv;%`0;mAx=xGWW)ZD>j`nFQOUS$#;WuC)WqDwayIs0rAae+$DG-MT){(~S-$Ha0eaUu!h1?k~1K-#+mn zSKW2}5EG+7n`m^{U8pgeEY+-Ethd1q>!DIDE77QhlfiH-tK@y-woW^dK?Q*&j9&Ww-yH+BO5-ZGjAeJIpvNI6D%hS`4F)1Ts)ejoc zM3&q2EE6nu-~zH0@+3qf@ZxYNLaM^f_9qLLe>*7_Pq((V#>ZzZufZxf2E-;O9~>SY z?(N0XXd>cqyPj`!gCS0w)?&6yT1tu;E9@;I&K*d!;6mfDSugoPzptvUCX-GE;}1Sp zuq!ecz9Ua+p?sxF;H6D(eJz)woU+K4o3_kl?&Q+|x zJU_a(te85oGdS;#q+zpJ$KFtc5c$)?8?gO>e2WOq{7a$I4jH>&Gd#Fr7V9NfaLVD~ zJ6HkO4EkLIaa6^s(>njDE^ARc4xKGzflhp6Z7vhh$wT-CiA_{ zyU|0@>AWAVC`K>qt(P?#?emC7!2wQYN;ScRrA#@!J(%F2-p0bfzyK1%(vp(I{@h*< zQ{pgK%%&CQ(?zkdvHag*)D7cb!M!5!N5#hWI9u!Rdb|k;2ry$#-Wg88=k=V6+66Zo z0f$|#RAshQ(`B{QD~;31eG}oax;KGZ-TwAu`C@ypc=~|T>43ezJ^Hic#^xq55s~}V zUe>rN92O4$h3vZbQ?B>(!|U^@kJYlU{7@~Q=~yQC(H!Sy^XF%06;=!jBCr6+0W^3& z|CT8TjfjW{4z~MbwE%vAMmk7-IEBC_zI=*s-{{cu@xh$c<*1 zEi5m$fE$WVr{kz>U#d}CHAKaL0Z$oczS8VAp_{(j($X?HIXNCHnIF00b-TRW7y^O2gdaei&i6;a8!DYwsyr={glyDuaz^y~BO>ibx&$$V+> zRB4a#xtxbgnKf3>?~m(O|8#cp@bLU&WbjPnvIGJUuvx`?oH&<3OG+EMq|zZFBl}x< zdpT&)Cj9XCA4=zWe7e63_nR$Nr5h!2eaYB4FYneM4kB9X0`S2VnHjGV^`EV9x1C0W1^z601qDAXZTK6S6BAn zu|MD%v6)Q?5(Yt>IbCm|!)n-THO+FzyYoS2(fRa5c3uA<08K!%&h zUoGE^J%fo0bRIk&cSA!%$D=tr(CyBi^3)Dz4NJ%O9ecxZJs!cm@_F@Y`uoAL%!FI> z)h6+6U;qG(sy70^&2BTW->^uvdDg^h{n5_Bd6tTp%3a(wjih#IHEwi!|L z`6?suRKWQaD;M9abwCCPKmg{o3yi^W+2rAy7@*K5fdnX0%H;d{SGO-9q zo36BY)Z47;&?by#@U6gr6!LwmF38QtUCidvU^O?4i(CpjWv5;8J%)?6H; zvLNySy?fZ$!PE2UJ^booSgO%{O-;=kG~pKa>z^W=;lNz* zT}105Aa~r<{pN^*N41juw`YjOi^(jlW}OsjcSQv~xKU_maT!mZ&)2hj&sQ-j#mjY8 znx3AX{F3d~ThEWT<)CBuh6ofY&47Hk-DZzQsfxWBxCmQYThLmu?Bp|TAH`_-lQ({Y zi^%YKY3z#SD*N%cKq2Fmn~)17YbT5rM-PS)+Yl;00ecAcZZrzowZ+x{C@#e8kfXQd zgF!>mZF{LQ8l?U)lFBjJ>g6FTE1M*>b8@0nsF3&66;nX$*G#L^eRlf;-&+mb9PoH0 zeMZxH7QxI1<`%1!X7wyNFw(DqjH&BqB-Ci8PqbRD&ks(6Qf5FRi^U9xE?2!Dt@dq9 z|GxGl^Z#6WX7Se39Nk52$gJN)A>#8kgNX_Rfc@L{p_{=dWSlN1ma}cgj!G8kJRYPi zEeE^1CU!OHvgXSx)klkI9u~6<8JEh)TgvavF1GrQ7wd0tZ|j|G}{}|&19OE-E+xo2Cz@i`|)CFc+ zk)IfLXKVbS2G0-Ir3nvJtcu{%VInzG^8J%lxWkYxCqaXQM08SFe4=0~ng&Y=_v-^A z%;Xeivx%RWOsx+GREM(3V(b~=8coi}{{H?oHOz+(!&rF=ZeVH!?ecnmau>QP1RO+I zRSY=mc#Gc*GNQO#&dolryg>7L4|Wp>Cu%KJyF<|O%G;GoHIy&*Jvk4+qy0pqfr^Et zQEe>U^ezmODIfe~Fzg@9SMR>xLPIWY^)IENSI2pb@8)ymcI?%6%vozrO6{Zc9dUA(Ki zTYbr7C^2e~jL;CoLlfDRB00!(BnlD%hAwji8Sm>j)tNu>>%1@jZD!)~vDip@+LLxJ z`4t>3*ATA%ghXX9x#(W2g5@=|hp4YO>(@|ZH;ZzVs#Jd}VJM|Q2+Lg=?tkF>|1P}Y zWWK{7=7G^v$~BhV z@#1O#i{OkYGl+3nh>1HVn&*Ez9Zuv)qQM0mD^%DwHiPN;-Me=k9UYJ`@OtVq{Wxli z5NwN6oqzt&YBlJkrJPQ4G=_Uz)|2TZB?7#Kq|Bs*1sf@hfi@EB4A{Hw|Qt&p8ZvE*4MR6}sl`d}X z(rT`ym0Dg-*GYeJa?Z{t7pSv<<%czxNevB2bl&BvT~dl1|M~NWdbQyr6o0ZTCn$@7K~hvyluoTe zuPX@oA7R>yno0b4M7723l9UkSJV-&4Sh-#Gu{p*E>)2Plq<>5e3`L+oTr5Wp#>XoA zZ3&B4ScN00;GV4QK!l5ASfMbkOMvM9<;i`YLn4+ysp~(z9;!FUksJrxfh{ zl_Xlb0|@FG?59q)`A`E1x=}3`{&#{)?43}cu-uW}mVKkbX9M+t{pge+=qRN0%^?v; zh7@@*SO6%MQ?w5gn`{QKVBp_igmsxRBMZ0+RCrRu`Vqjsk)E8Olb+NF3smWYiopv| zS|B7!vm(&zLlu@Ul#|0VXMy~g993-|0wdr4@!@SadR4WP`uw~75bfy~Ej)?890#f3W)_&yq1tu6_BJoC;fect- zq<`FgnZ_GwbKN5ZD5ds|?N6K25O!IzPNLETWdo?t1dvF!YkRA5%zdZ?4Q2>1f*^n< zfTfH}&fc^9E>4dH;%@bLVZt~C$9c{;39v;Y4CcE);TC(iD!JU3k)xC>&}+y;jAMfH z%^J0==`p189m7;Y1t_}(CX5m&4lP(yIKp!<1Ihe%`3=8)1$Os#1)vC^ppbztNKl1c zTlN7m2{fDV+@npb9L*_cOoZp$vY=QVe#ULG14U$;Dh7K-*Fr%-Ny#{|V6@fS8Hd{4 zL~c(eilXxQ&cnmQf|*uaasd`197UKvlDDAm9EZ?<*DZ}U1&Iib7B9)pE^O;?_XE%` zi?{pw>ym(Or#d$}F6nc~n^8Oe-FJi{G2+9;rpYk2!|a{QTrS>^*SCptoPZuU(5+Pin+@!o7~XI*o-@lwvD;G#>`~=i@yHG zW^V*&c_{_-7pK+Ck!f)h;Mq%zib2Vv21}ln951+Nygn!Y(qI58OlEIj5U@|@>H@Z| zT|;)=%se?{tDPOmKY!-P)^OVO)&cmDf(XP`Z);;UomEIt{UUW_C(E4Eey4G|*p-e> zeA6jMDMfuO;>3(MzxRbjOiPq65-puk2+)KJnpG@36) z3&@EKa!PCmu%(l6_e#3pymxOYqoN(**>`ig?7&B{zYsTwfW-z4?C*6k5HC9K+3(+c-zi;? z!C{*C=)c);IeTh{^Lif}tXMGm+xhlfl$bAx**O+mPFmVx-AjYf?s`mHd5~_8ou|IU zCx9g1kmK)?n0oEF>A*Hew7Isy3ifF+usEErG-%Q(e|b)BS8z_d~rEQ;5~&m#87{A`SIlb;QzTVNR~7R!0E+Z zYAf&x?LmWVAq&U!e%wLG8Be6L9~{ZPi}QYg09kI49Ku=orov_@ZbCn&FhdH#1jPog zzp;|nCT8#Q_be9JB~We9J5n+vwO>x00fKoH03MrFDogGs2Cx49gb@hZA3+`zTDsD8 zMl8_B9E1eIpdn>{LDFJ+pn(;rPq(=_Sh^Y54t;l6CngZ>O%W3P8*Ab{#GlL7B%=b) zfUy=)nXQoF6Wvhh#hy*_ICMb?QPkJC>?~Vcdp+%DjfG2fjyAbr$CM#H_c3k1Qgi% zmD?}b@?DPj9y)h$8*vJTgSZ-eWTk|<1>fGtq+li-{vv>l;Y)Gjqo{QIp14;;*wR$v zgxctYq$vs>1PM{lm*?-}YVaw};TYh`b!oKJScoPj?vqf^*<4ndS*;dp%_e_qFV_8t zz`?v&c}!uouDIqn45b5`)>y3YoefZksb40@N`C4OP`wf{Vn1}zya#ZQiQJWfC zb;hfS;~z3OgV4}6b9KGqNNjAkuhb8mqkW~{=QtOnx4R<;x~fc82m)9XQ zd)GVp@OYm#wZcIvsWNP=Y}5>-pHeud=-;`alEFTBDIlu1m;% zzJeR_CQhPzVA~(bcMJYgPKdoGtMs5DqZx087Og5dDzo(Gm8Q-06A^U3_zDq9nHrl( z=%l}QTQ9vLU~#O)s{Q#DBM=#ikc;c_2g@o;3CbWn z{I)sG*Ku?*eS;iu&)Kd1B>J9uVW@Cu6;DgF5bv+I4DQaMTKsKPCS{_+1{^t;!wywd zd*~2o@Rjl;jA}J#T=q+NQ5DV&#+o3DgoP}ZTMhaX9Gp0DKZ$wE6)W*~Y$r1p;mflu zb%NZ~WQ;H&q0_-snKBh62m@(oq|0Y7xg$26+yvxfrAR?i6y$qAXhLfPlM@+|CtX2( zxV@b>ZaRdsd(of%xo*i+W2DNyG4(4(Ld0-pWJqy*F4<3W>d~}<{U54Zq$)u! zD4pPYvojZ|)YNYN5e@|`k&eZ1uou=QlN|i+l zAS-CNYqWWL;e(Wq-F&0NEJ$EPfM9m3Z++Rr`E;pqFv_5x_`cu3Y_RtAPo+4QPa03k zAT))Uz=PpXBB^w;T2qO3lk>n#;KJN1}8=)ab)T z+jD(O%aR&=6cmT^31QK2XSb_8`O)+iKR>_2nPQ)fEpcFPw(S)pE%S1IAmX0P7uRds z%zzPz{vT?vbh1YMPPJ7Hz;m3=3eD1D~GsAC;R#-Ea{dEYuePL(IbRfR{r}`($$!Rn&RZ$T# zTdv(==@44Uj9Q9ICY3r@Xwa+4W>aImZ2qS!_!Bs^i`9Jdeit1doo=guePv4OB@Sn= zGPJ@H#Z`UV^3oC`Q#Cga4?w`hwKyz&*f&o5_yK3G)!;{stUM}VuJmG^L2uaWW&|I& z;Uj5$ZRXQ1&Mgc>e3CHI|EVHgWVkJUb4GFZy(5$AyZ?kw_^0l85ue91mu-&*zk$Pb zZ*0EpMTs2serUH)u|U=9Hcg?>)21&Hq+B$YRH=1ohtcVIUlUW7w^Db4EUd$pXef(BCgd|E}WvBGhVKYsjpF*w>=s*Mr2KVRJ&h=2__2;F*bY-3iGQ_5z+ z`{Fw1af-lk*^3YAv*xw#BbUzM+9hQD^Ds3ja=sh&#uu>^_9`oRlDqLV8hzXLgFjxS zMRJ%gsWX^vsH-hXZ1B%cN4dDfX*1@Ae_FiHy;IneZt{hE+cTVWdA6RjwYA0Ju#12} z-yW6Yg*#J%iOxpG!}A?U|K8RvqkeUz(qZ@E8XKnhG91O`6PQp1Va?>)ykEVaS#4JI z*$4v!q7oBbUtgX-@ZlVgfTw(c(xyFAzWSGJ`}K=FPr~VV!3k_K9D@4xOQV+6axO7F zoxyzwV^NNJ5Jcx{P0Z#~^n_o-k~Tkr$|G1VXZCaSTKBWJeWBALAkW32j*y_{0t@Sd zxk|xFkTqnsnyHgcDbsFZx37_00$E;h@o@$QG)n&(>q!;+5-@duoIEJ<;NajKb)^K< zube2d5HXTBJnb#}U8AHbkjO=5eIJ5WdpH&R^JfhLSXhlF?h3MeyaeG)i;1kMX*Z9@t;VOF zJ|Fv$N=?QQIyrB+W`BM!7fx)Tb=kpt^;byV`Uv58rQ&Xjhco)>Q>e-y6+w9@VyMlB zVuOxo^%xWu20vn8e4EAlw`POCy$4`1D~zVv zYVl9O({4TG&K139M}p95`2j@-FSj8w`YAUEuD_N;Fj61tCh8wg{IfmbznPSOT>Q6S z`cE#2!dk$+Qg|N@Yq#@9g!AbNlh?yfnY5-Yrf4u!D1`-qZ3-`x4mxj$=r#(`aSgi} zCWkd^g2Kaj-LFhvz7HjGFtM;)??rXaga-!)gDA2LEPau=d$Uv7ENrLS^{GkBmeb!X z!{%+*7bzDTAFDOVEE8G433J$hq~D4tC=?jpXIGn@13HYbXm3~dFoQ#Cc}80eNpQw1-)cZjQ{QOIX>v${_4<6Q?3$1r_@ZoG3|gJ$t>G1$l}4{((NVn6Pju<5 z$BKhq%MUO%v*+iVWL(acvxnDUyFrGDH>p&rBiJI)`2&%3rRi~yj*p8|eOH*l`pN(9 z?hd4rv>HA4M~*c?qN*>BttpFCHr;-$-Pg@wm)HAh%$h?giPH(Jj6mU}@`nC@p#55( zobkI=$hTMS&upvS;JIqI==>$Xxkvll%wj3Eny~^LA1hgh$3nhxrj1;v7Ak?0pn?^! zw6tRCO?!Mi(zI#O&M_X|&+svxEm&s9&4UKM#!;FW8ELmU4^K`K^YG+IxHdF3Jq}~f z9=q2$*DTMPGCN-ytapC+VZB<}xwzP**c-0h%aiQOU2}{cylZx!wn|#-NMD;0xPpv*YoCy zY4Y5--I+3RG4Bg@@K^hcW6aMpnl^d4xg}30O)&*B;%Kqj8PKf^YBZDW^fiMH*25Ru zt9V@QhV5LZI6TfJCZjr#E4X{yK{?p~5WWc)i;&X+06&MmqR(#lA`vxS?JwTO$o9Cq zHnf3J9sQGE%mn$lKvn)&z|~BO&6{?RYd&3Vv!Ta8#N&*0(0*)|mY+h`9V!A*>h?ff z>K!%G|HSM+h)1!)U;sdwmVEkOF@Wrh&7?@-|M;u1#;PmjS(tEeh~u z9A51xKFVKujOMCv4 zF#72qRx3&wE6;UU<#D&xnovL66P9u?Q%sjII2iZda`HC|p!7>F%PvQROeQ_Hh!{xZ zeRgQ#P3xtr)ui?@@j4GkNm=ZXPKl@0+&SH4GoR`^-^67-?Df9rSACBTqO%$<_IS{6 zWs*}tHjo};%G_yFB#cxh6$u;rkR)gsc5}nn!`;n#v985fi6$_Ad1<*^tJN@#tqGc) z%=)ICAM7l7HoZz5cb1jDI2*5sD3DH}q&eUwqqRP1cC%WjvzW|xYO1sPfX~6W)pE;b zwY2u6u=AQd7ca8^E5x2WkLo7Q6(lp|D)og`Xjdz~cIHc`+#WA~H`=f-c@*ou;P@C# z2D&>8(5R(T;C!4X`N_Z+m6bd<`We=>|AFmpA6IxL@e=8@svxuxmhzvKU_nWJKXmoGxEmSKbaS|e169)-zCwv-i# z{$tGhyv*n9g!_2yN=S?UZovE7are(e`9IQLuF&_>1wvtH(bcBG4<5R=`lo;A`t6XI zP|WNtm|1Co6lXDcLOzRS&W+kpuRmD-U2goR+H!|e=qTTI_+{{=oNIP#fAzs*HN4_w zC9U?=m@6EO-bLkffrk7j8e?x^uR`Qsjf1&e-@I}+_`M$}@7M2=jbFRoy>=DW${g-s zC2SqL*6#?{ai<*H+WD&y|2zGf>j?W_B-83BA3?Ku7uPBL-Q_6Xd#q0MYGW3&g(2}R zg(tA<&*^yP803DThw&EhL6eJo+B$#E?aC320GqlhOv+bGMp7vfOCKK|9v>mwbxlSW z!oYC2336W<5%{4+m@Htkm){P;ae0^A#g8`icp7w!yYaCse==R~THDRQYv_;B85wjy zjm7Lr)bgM@D?7XEKx{u4!*h7DA|-giIHlHX7+jMP8++C9LlBgI@zKy~pB8QWzJG^k z2N|+ymsODW^0?X?2jww7r-9fLu;Ju%M%w)Q{lh(1q2S4?k6gYKCa^bFISIB7GF4YK zH#Z0RaD#gMS7(Ddp~SQ_9;byG$j1+chQ{X1$zX9X7uJ?}SWo0uvDsj;NhSOp!Dt|M z{P23>#XlH@%jwAVa3>7xI*?9eAKx&UJ<--=wc$J;934z3uV9yWu*)TC(eb&MD*V9v zRN?dL4OCd}M^~G?N=P))lVns#WVS3tQB<^|!jq?0 z-ZL1b%O6g-@)Yh@A_+r3(bV0kS9)>?%L97MSeHNLiV9;TjPfMn9&YxT4Eyu|0-?D& zug9Dp5mzuR{e}ZfJI`+sQ=bp%WMx0p2!r*t-eG*{aLR6T4beO^05<9z_im)q87)l7 z#}r6KRbS9*bJkmq3(zL`JUe-&GoX_T=k_UFq&WK#aC_dF_DAOxqkQDWd<@>2`z7BA z7Q@Ejl45F;U~>yM@mFz0A?z`pL3KLb*h2Dki4xW8eOc*^NS)Zc^1YhbHnK(LPE4LN;xrTgU1o=)z=EV!F6?w zg~G{KE~f_{fPO8lb-Bs8gOP%okiouDt1!Bf`ucQq6rDmyz%`r+8==c`Wb$(9mHO`Z zmTwHz{`~pFVYzk_ui2l54Qu#-uS|4QBpO-yTUb%~ZV=4y7*O#d%I z?!Lf$TlIc5nA|C~X_+sc!xYR~7#f5N4sxvX{YIz=ese~E-|g2lJiWQdND&2=&^ik` zT<`n&`QIf+mI6Uiq>#YB6d1%nCF_;nEq0eGW^Je6_`M#k6Bw9iN|%q)o~8$*s;$Tn?{Ch2AAA0tJfT#vaLTxP-jtp^Rw8`UH6Z&}T++|~RavsoBo7w9MT6r2?(W|zpmy77{Tay^ zCS{U^Mb0Z~Rce0&s}a_rCnn1V?^Qi~fXg?@ynVP!hbE&x@vLwD*Sx~X+Y1jJ=YiOH z%N=%5X-G#(e;fV{Ao%1f4XnY!5&_=`U~zhrhK&J#M6E`3G3IBuk0NLxD;#8lv=M_Xq~usIe9?Njj)A z;fx}u?+&GYfeJ@?2fwwdH=^r#w-T^k&%vYHy(v_<8)9Ywhbzf|2+nbGoEi#2(k5%dE zlu1Lq%Pr=466Njb?07=_u|LfgdY{?DQK0w>2CHHtEW&)r&U*2EoKC0aR~I#h>3KD* z@-DXDyEk4oafD#;GmnmbR8=CYaaDSj-pPE=8`4X`ZfJC(K zE4JP7-};yHbFmWTU?K9KOB^npE1Hn;}pczOadlfkLyK$;$2Rjv@j-7^UxeW zTw4eMz}@EUD%i-KlCiY#!0mElc{`G}OQ&_!*)t3mu(AIAE&50XaRA?&9y=ja;q+Z- zf&3BO4hI}UPZi_%JWnkPc=XYX!-dx2t9geu_3IXmQgQE#yNB9hI@`ueFK6&Sa`@|D zBLk>lu*mE9|Jpe7aHzLGjt@mBWopQvLCDSxp<=9KZLFoNm8_HHl68a@TZ3pUlYJRG zGeVY0nikPCmoW?)OWBu3lu1L#?|bk4{eI7Ve$VfD&j05;=X}n2&UwGy?-OYRK{yCe zju!YH>|P$?b@=KFz9%JQB!sXQ;V}|wZqayUR94gK;x8|wkAxtM-+fRQv7V{_Ee8qFa0)7U&Vy&hM)v1FvhKRSdxN z&t{NHwP#bfwZ42u7x^Vh@#*!l^`@iXG(nIANmfoSf;DjNrK@A7*=7?y`PKLHF;&(r0xxRl1AQTqF_0wI(?LL8#kC2VEcp-<99HHop* zQ=e2^Z?hHzQ}H+qFGyV&xxWnAk|ey3noV!cp>C@?>wcQKzPbV}e`7_eY_=VQxF7K0*=wBDq5I6P@ANp42@O<@xcs%SsityT@kpS(v! z|FIJw(98B;_J3m_;h^|obkrRzHkBo55}MQ3QEH|k8Vch8iRHf;p{dYHgjpc}*NaRB zNe^@z?mfd;Zh2jA7Q}oSH~7K6*JjT@_9BYEaA)>)o2vM!^b@UtYtoC6xWaKvc5l4z zxCnr)mAy?A^K1|~(n$(S`iV(@MI(?f9siD-`? zst;0BCk7A^a@Ru$rz@%}qodHy#KtpM)!bJXB>^*NqdSQ~h^^-&9mY*W295um%`GZd zXbZULJ26K)l?Ic}qC70ra~v5}-%FZ1S`czKGmY28=9NB~9}GnC2EtyzTqe&BdKGPZlGC;CL1CmQ4G#wNcJP-b4TFT+`vc0~zqr3Y_c! zg00ECT_3%)Ts+0kL|_#bHYqs?1)bYK=ezqtJ?!eHScgFztY%U7Nc>dHVE+fp5^lrF zuXVZj13y-o--bl2Uja;&=Vof&tm?dWzxWih#ThXD?`(FvKHyGo z3uJC?(oOCg1#3W6aWVEgR?Ey9y5E%UDtVg&6vMsbpz|SCKAOjD$ zGZ1&B=BzF5A;n!n6|YK%tvx!{wkopc;OWz1S?R!UBR=CmGzF{3c@_o6AUh?bm&4CS zmX>AB6qkriZ|%T`iS{icm#I$OrWdn$FGLrYoDM&!>^mO$`G;AHhzeD058dRgGK{u6 zGYYy%csw@V7Vwmz-43ft;z2KJYWtfjN&1s-TfF;)uWmTlOOGNd2+&e(b|O}@Pir2x z^e!isDjvE~FulUHKn=^VXnZAsJ+1)QWcL6pg(g5xQj;K%I6#^C^I)Vzg|MNWF+Lp9z8|wYQO7N-=MrL z%(W!0xXC#}#dszf-t3aT6{6`_qTi}^#dp*KVgyOH!B9!0r54rsS(h7|UuWrnRq#sJ zOhNXXR~G$GvLa~EFdZF7?Fm&({71F+9obRJ=Aj&cTz-dzya91Rd#Lz)}EdVF(^%r{T`_VC1ir z)SU{sdwdK7(e%S>YQ?nEOB)abT}vM*y=6v)k)C|v=GIaIU9vskVy9VUsYtq~x`%H2 zSC()>X8&Z2cDEeLulC0#n1Fb6I0Hh)%awVh?KHv^1u2u-)Y^q#ZA7^9H%8ZVlb)@*iI zY9SpV-VD}X6O>Zp6g$rmkAp{aAhXbPaM{|6$=UCetNV=}Ys&-;c06jWOZu@oxVnf> z&TsRZBwrqN>yi0lTLia;BxnaS>V;v0gjn{DD7GFb8AfDv3)j@R{vs#6ZfSzmi|?_? zgP+%wF=-s72n(pI2Lop9#0#yMySN|&6+598W?}x%KLRvrhs7d~WOOV$9{3U83FJWl z&PlrM@qEK@%z1tO`Em%mMMIM;y8W@6^i4e)RX*I+cL{!#%cte6s%nmA!7wTvI1Iik z&YC|gQaDiub0R8;nv@mCzyo{o|A`B(#mfGNS-Zty%GV3A?tS0B7vICkW^{5R9+X>f zc~j((LpEcbV(BBHCJ*u+b%~7B}cK0;N+sOwD}4}ryd7GU?wo-6#?Uj zdb-7~U9Ptvy34qB$17^DBUoG4pa`3+^$c)%qP&_XOn>AbQvK0He~Td&vI=eQLV{YB?;ZI+Dt5h(Tjm0a(60IH|W%8SER#qMIc z3==akmX@5l1alSlc0e6o*P{)jxk;#8K^$<;8tPNGM4xmFAZn^%|ORaY=P5Im5sNK zPP^dVZeM_QqsJDq-6PmC5pVwvQ*;_q+^5U%`gK73*A9DSFc=qq?iSI7AcBxes8+yK zLG#icMRg^f#FSq+b!!NfW|l?wI{FvBXDiN%jXWSYR!3O))l{3WUU$p&*6(^=xhwaH z_^~yX^^yZWR4+jsMx{PF^IT6U#|m$+ER5;o91V6OKhucwuO{l79X_FQ0%>dA8e8T3 z_bVs=xI{oodV0`U$MjSF8w~^`y6(%|^-szs&*Ji&nV{04W!d|D|K3Zij{8LL^EB5pFxOp_7N zJ{a~tegXGKs#2|1!Vov2CH^P)(i?q%*Xrw^K!!7xo1sv=odQ>>^RA1?K4Z{JFOAP( z%(s)GZb6!ALPtT2TE|W&LQNU~;ht)BdC=Im>CbODLV$1^1J#Pd;gXY+vmVUmL7^zY zHLsfXu<{{q78`3Ga+()6o^~+7)E+`@2DYPEde0vrJo$*tI%;Coyoxf83QY?6xJYM( z`LsCyx;>FpWxXs<=P{bn)ue?02CTD0S?GOg;DUFe7nlfm7jRPIGi={;7XH5_9S2>; z8~#H+_&+dBHuPga4HyciFASw>R6&q(hM9WP6!J&e1~9bP)dHp@1`-uZ0hzvYUg9P+$&ATGKFriO#0`2UYNd$xYe*LAhG`+d_7FZHX^!jaS>wSxL_T*C$ksG~ zE}e2L>f}1NkkQkij(I-H1=&{*d|(PzUQe8ig<>q;Po-Fx3Vq>waGbEqbxoe%XvNU} zT6-=9?2w@#oD079yyN9b-nit-J5uVDJ+4gWpwgoP>Q{kp96^={8&jh3wR`^pR6oY( literal 0 HcmV?d00001 diff --git a/doc/kgoldrunner/tute008.png b/doc/kgoldrunner/tute008.png new file mode 100644 index 0000000000000000000000000000000000000000..ee5b5c8bedba76cc06691a1ea50b74c070022507 GIT binary patch literal 41471 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 IZ$JKj0LkjR!vFvP literal 0 HcmV?d00001 diff --git a/doc/kjumpingcube/Makefile.am b/doc/kjumpingcube/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kjumpingcube/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kjumpingcube/index.docbook b/doc/kjumpingcube/index.docbook new file mode 100644 index 00000000..55810a5a --- /dev/null +++ b/doc/kjumpingcube/index.docbook @@ -0,0 +1,368 @@ + + + + + +]> + + + +The &kjumpingcube; Handbook + + + +Matthias +Kiefer + +
&Matthias.Kiefer.mail;
+
+
+ +
+ + +1999 +2000 +&Matthias.Kiefer; + + + +&FDLNotice; + +2005-12-10 +1.1 + +&kjumpingcube; is a simple tactical game you can play against a friend +or the computer. + + + +KDE +KJumpingCube +kdegames +game +strategy + + +
+ + + +Introduction + +&kjumpingcube; is a simple tactical game. You can play it against the computer +or against a friend. The playing field consists of squares that contains +points. By clicking on the squares you can increase the points, and if the +points reach a maximum the points will jump to the squares neighbors and take +them over. The winner is the one who owns all squares. + + + + + +Playing &kjumpingcube; + + +Rules + + +A move consists of increasing the points of a square by clicking +on it. You can only increase a square that doesn't belong to your opponent. By +clicking a square that has no owner, you become the owner. +If a square has more points than it has neighbors, the points +jump to its neighbors and take them over. +Neighbor-Fields are only direct neighbors, not the diagonal +neighbors. +The winner is the one who owns all squares. + + + + + + + +Command Reference + + +The Menu Bar + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N +GameNew + +Start a new game. + + + + +&Ctrl;O +GameLoad... + +Open a previously saved game. + + + + +&Ctrl;S +GameSave + +Save the current game. + + + + +GameSave As... + + +Save the current game with a different +name. + + + + +Escape +GameStop Thinking +Stop the computer opponent thinking about its next +move. The computer opponent will then make the best move it has found +up to the moment you told it to stop. + + + + + +&Ctrl;Q +GameQuit +Quits &kjumpingcube;. + + + + + + +The <guimenu>Move</guimenu> Menu + + + +&Ctrl;Z + +MoveUndo +Undo the last move you made. + + + + +H +MoveHint +Get a hint as to the best next +move. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsShow +Toolbar +Toggle the display of the toolbar. + + + +SettingsShow + Statusbar +Toggle the display of the statusbar. + + + +SettingsConfigure Shortcuts... +Open a dialog where you can configure the +shortcuts for &kjumpingcube; + + + +SettingsConfigure Toolbars... +Open a dialog where you can configure the toolbars for &kjumpingcube; + + + +Settings +Configure &kjumpingcube;... + + +Open a dialog where you can configure the following items: + + + +Computer Skill +Lets you choose your playing skill from a +slider. This decides how clever your computer opponent is, if you are +playing against the computer. +You can choose from: + +Beginner +Average +Expert + + + + +Board Size +Lets you choose the playfield +sizes. +Use the slider to select a value between +5x5 squares and 10x10 +squares in size. + + + + +Computer Plays As +Sets the computer opponent to be Player +1, Player 2 or both. Normally you +would set the computer to play one player, and you would play the +other. Player 1 always starts first. + + + +Board Color +Choose a color for each player + + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +The &kjumpingcube; Toolbar + + + +New +Starts a new game + + + +Save +Save the current game. + + + +Stop Thinking +Stop the computer opponent thinking about it's next +move. The computer opponent will then make the best move it has found +up to the moment you told it to stop. + + + +Undo +Undo the last move you made. + + + + + + + + +Questions, Answers, and Tips + + +Strategy tips + + +Try to avoid increasing a square when your opponent owns a +neighbor square, that reaches its maximum earlier than your +square. + + + + + + + +Credits and License + +&kjumpingcube; is Copyright 1998,1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + +&kjumpingcube; was inspired by a game that came out for the Commodore64 and for +other home computers, too. Sorry, that I don't know the original author's +name. + +Documentation Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + +Documentation updated for &kde; 2 and 3 by &Lauri.Watts; +&Lauri.Watts.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kjumpingcube; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully compile &kjumpingcube;, you need &kde; 2.0. +All required libraries as well as &kjumpingcube; itself can be found on +&kde-ftp;. + + + + + +Compilation and Installation + +&install.compile.documentation; + +That should do it! Should you run into any problems, please report them +to the author, at &Matthias.Kiefer.mail; + + + + +
+ + diff --git a/doc/klickety/Makefile.am b/doc/klickety/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/klickety/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/klickety/index.docbook b/doc/klickety/index.docbook new file mode 100644 index 00000000..b38f81c6 --- /dev/null +++ b/doc/klickety/index.docbook @@ -0,0 +1,425 @@ + + + Klickety"> + + + + +]> + + + + +The &klickety; Handbook + + + + +Thomas +Davey + +thomas.davey@gmail.com + + +&Philip.Rodrigues; +&Philip.Rodrigues.mail; + + + + + + +2005 +Thomas Davey + +&FDLNotice; + +2006-01-21 +3.5 + + + +&klickety; is a strategy game for &kde;, an adaption of the Clickomania game. + + + + +KDE +kdegames + + + + + + +Introduction + +The objective of &klickety; is to clear the game board of all +the colored blocks in the least possible time (although having a short time is +only a secondary objective). The overall aim is to get the lowest score +possible. It will provide entertainment for all abilities, but a +challenge in logical thought if you want to get a really low score. + + + + +The Game Screen + + + + + + + + + + + + + + + + + + + +This is a colored block, you click on these to play the game. + + + +This is what was referred to in the introduction as the game +board, this is where the game actually takes place. + + + + +This is the game timer, it starts the first time that you click on a +removable block. + + + + +This the remaining block counter, which also doubles up the score +counter. It is usually black at the start of a game, it changes to +blue if the score is good enough to get onto your local highscore +table, and red if it would make the top spot. + + + + + + +Playing the game + +When you start the program you will get a screen that looks +something like the screenshot in the previous section. The idea of the +game is to remove blocks, which you do by clicking on them. + +However, you can only remove a block if it is adjacent to one or +more other blocks of the same color on one or more of its +sides. Diagonal connections do not count. You get a random board every +time you start a new game. + +If you click on a block which is connected as described, it will +disappear. Any adjacent blocks of the same color will also disappear, +along with all of the same color adjacent to them, and so +on. Any blocks above them drop down to fill the gaps left by the +disappeared pieces. If you clear an entire column, all the columns to +the right move across to fill the gap. + + +The game ends when there are no blocks adjacent to other blocks +of the same colour + + + +Scoring + +Scoring is very simple. Your score is the number of remaining +blocks when the game ends. In the case of having two high scores of +the same score, the one completed in the shortest time is listed in +the higher position. + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + +GameNew Game + (&Ctrl;N +) +Starts a new game before the previous one has finished +(once you end a game, a button appears on the game board labeled +'start' which does the same thing). + + + + + +GamePause + (P +) +Not yet implemented. + + + + +GameShow +Highscores (&Ctrl;H ) + +Shows the Highscore Table for checking a target or for +bragging purposes. (This is the same highscore table as is displayed +if you get a new highscore at the end of a game.) There are also tabs +for seeing all the players who have turned in scores and for looking +at some statistical analysis of the performance of individual +players. There are also links to the worldwide highscore and player +lists on the web. + + + + +GameQuit (&Ctrl;Q ) + +Ends the program. + + + + + + + +The <guimenu>View</guimenu> Menu + + + +ViewZoom In + (&Ctrl;+ +) + +Zooms the game board in, increases the window size as +necessary. + + + + +ViewZoom Out + (&Ctrl;- +) +Zooms the game board out. Window size will decrease around it. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + +SettingsHide Menu Bar + (&Ctrl;M +) + +Hides the menu bar. It can be brought back with the +right-click menu or by pressing &Ctrl;M . + + + + +SettingsConfigure Shortcuts... + +Allows you to change the shortcut keys listed above. + + + + +SettingsConfigure Notifications... + + +Allows you to change the notifications for Game +over and the removal of a column (listed as Line +removed), such as playing a sound when you remove a column, or +starting your favorite IRC client when you finish the game so that you +can tell all your friends about your &klickety; skills. + + + + +SettingsConfigure Highscores... + + +Displays the highscores configuration dialog, which has two +tabs: + +On the Main tab, you can change the default +nickname that is entered onto the highscore table if you get a +highscore, add a comment to go with your entry on the players tab of +the highscore table. You can also activate the World-wide highscores +enabled feature so that you can compare scores with players +from around the globe. If you are connected to the +Internet, &klickety; will send your score automatically at the end of the +game to the highscore web server (klickety.sf.net). + +The Advanced tab displays your Registration Data +on klickety.sf.net. If worldwide highscores have been activated, +this tab shows the Nickname: from the Main +tab with which you are registered and the cryptographic Key: +used to prevent any attempts at cheating. This key was generated when you +registered on klickety.sf.net by selecting +World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + +SettingsConfigure &klickety;... + + +Brings up the main configuration dialog, this will be +dealt with in the next chapter. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Configuring &klickety; + +Each page of the &klickety; configuration dialog has buttons +labeled Help and +Defaults.The Help button +brings up this handbook, and the Defaults +button restores all the settings to their default values. + +The individual options are as follows: + + +The <guilabel>Appearance</guilabel> tab + + + +Enable Animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + + + +Background + +Allows you to change the background color and change +its opacity. Trivia: if it has some transparency it doesn't actually +show what's directly beneath &klickety;, but rather it shows the +desktop wallpaper. + + + + + + + + +The <guimenu>Colors</guimenu> tab + +Colors #1 through #5 Allow you to change the colors of the +colored blocks. For an extra challenge, make them all the same +color. N.B. You must restart &klickety; for these changes to take +effect. + + + + + +Tips + + +Try to remove sets of two blocks to allow single blocks to join +up with other ones of the same color, it's your only way to get rid of +them. + +Spend time thinking about your move: you primarily get higher places on the highscore table by getting rid of more pieces than doing it quickly. + + + + + + + +Credits and License + +&klickety; Program Copyright 1995 Eirik Eng and 1996-2004 Nicolas Hadacek + +&klickety; is based on clickomania. + +The &klickety; website can be found at http://klickety.sourceforge.net. + +First version of documentation by Thomas Davey, Copyright 2005. + +Reviewed by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + +Further markup and reviewing by &Francis.Giannaros; +&Francis.Giannaros.mail;. + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &klickety; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; + + + diff --git a/doc/klickety/screenshot.png b/doc/klickety/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c59d09f9f42a1c69dd275529a4c9d3616f22a6d4 GIT binary patch literal 18483 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 + + + + +]> + + + + +The &kolorlines; Handbook + + + +Roman +Razilov + +

Roman.Razilov@gmx.de
+ + + +Roman +Merzlyakov + +
roman@sbrf.barrt.ru
+
+
+ + + + + +2000 +2001 +Roman Razilov, +Roman Merzlyakov + +&FDLNotice; + +2001-05-20 +1.00.00 + +&kolorlines; is a nice little game meant to amuse you for +anywhere from 3 minutes to 6 days(straight). + + +KDE +kdegames +klines + + + + + +Introduction + +&kolorlines; is a simple game. It is played by one player, so there is +only one winner :-) You play for fun and against the high score. It has +been inspired by well known game - Color +lines, written for DOS by Olga Demina, Igor Ivkin and +Gennady Denisov back at 1992. + +In 2000 Roman Merzlyakov wrote a original version for &kde;. Roman +Roazilov added to it a cool povray generated +animation. Later in 2001 he ported it to &kde; 2 and made it conform to +the &kde; games standard. + + + + + +Using &kolorlines; + +The main rules of game is as simple as possible: you move (using +mouse) marbles from cell to cell and build lines (horizontal, vertical +or diagonal). When a line contains 5 or more marbles - they are removed +from the field and your score grows. After each of your turns computer +drops three more marbles onto the field. + + The increase in score depends on the amount of erased marbles +and if &kolorlines; shows the next balls. The increase will be smaller if +you use the information of what's next. + + +More &kolorlines; features + +&kolorlines; is wonderful in that it will allow you to kill 5 minutes +while you wait for that program to compile. Or, if nothing +else, it will allow you to kill 5 minutes. + + + + + +Command Reference + + +The main &kolorlines; window + +There is a large area with marbles. At the right side a bar +showing the next three marbles to be dropped on the field. Underneath +the field is the status bar. Above it is the menu bar. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New game + +Starts a new game + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high score table + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kolorlines; + + + + + + +The <guimenu>Move</guimenu> Menu + + + + + +&Ctrl;Z + +Move +Undo + +This will undo the last move. + + + + +MoveEnd Turn + + +Skip your move, immediately play the next marbles without you moving +any. + + + + + +D +Move +Start Tutorial + +Show a short, animated, non-interactive demonstration of how +to play the game. If the tutorial is running, the statusbar will show +Level: Tutorial, and this menu entry will now show +Stop Tutorial. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Statusbar + +Toggles the display of the statusbar. + + + + +&Ctrl;P + +Settings +Show Next + +Toggles the information on next +marbles. + + + +SettingsUse Numbered Balls + + +Number the balls according to color. This may be of assistance to +players who are color blind, have other vision impairments that make the +colors hard to distinguish, or those of you who simply like the look. + + + +SettingsChoose Game +Type + +Choose a difficulty level, from Very Easy +through Very Hard. + + + +SettingsConfigure +Shortcuts + +Displays a standard &kde; shortcut configuration dialog, allowing you +to customize the keys used in the game. + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&kolorlines; + +Program copyright 2000 Roman Merzlyakov +roman@sbrf.barrt.ru +Contributors: + +Roman Razilov Roman.Razilov@gmx.de +Redesign, Graphik, animation, partial re-write. Special thanks to my wife Larissa Juschkin for +testing and discussions. + + + + +Documentation copyright 2001 Roman Razilov +Roman.Razilov@gmx.de + + + +&underFDL; + + +&underGPL; + + + + +Installation + + +How to obtain &kolorlines; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +Generating marbles with <application>Povray</application> + +All pictures (klines/*.jpg) are +rendered with Povray 3.0 and manipulated +with ImageMagick. +Povray & bash scripts are in the folder >klines/povray. +Customize balls.pov as you want, +render and install it. + +You need Povray 3.0 to render. In newer Versions &ie; 3.1 +halo is suppressed. + +In order to generate marbles on your system, type the following +in the base folder of the &package; distribution: + + + +% cd klines +% make install + + + + + + +&documentation.index; + + + + diff --git a/doc/kmahjongg/Makefile.am b/doc/kmahjongg/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/kmahjongg/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kmahjongg/background.png b/doc/kmahjongg/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f4ba2700be5a8b2b04c268f0e0b74debf4921b79 GIT binary patch literal 35234 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

_k5 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 literal 0 HcmV?d00001 diff --git a/doc/kmahjongg/gamescreen.png b/doc/kmahjongg/gamescreen.png new file mode 100644 index 0000000000000000000000000000000000000000..22d6c603bc0234c0a25a3810278c9a9059dccf0a GIT binary patch literal 67210 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 I=akR{0L#4lqyPW_ literal 0 HcmV?d00001 diff --git a/doc/kmahjongg/highscore.png b/doc/kmahjongg/highscore.png new file mode 100644 index 0000000000000000000000000000000000000000..4d5667d23c86165a36fe6ed32aeaf53b607c44b7 GIT binary patch literal 12866 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 + + + + + + + JohnHayes'> + justlinux@bellsouth.net'> + + MathiasMueller'> + in5y158@public.uni-hamburg.de'> + AlbertAstalsCid'> + astals11@terra.es'> + DavidBlack'> + david.black@lutris.com'> + MichaelHaertjens'> + mhaertjens@modusoperandi.com'> + OsvaldoStark'> + starko@dnet.it'> + BenjaminMeyer'> + ben+kmahjongg@meyerhome.net'> + +]> + + + + + +The &kmahjongg; Handbook + + + +&John.Hayes; +&John.Hayes.mail; + + + +&Mathias.Mueller; &Mathias.Mueller.mail; + + + +&Albert.Astals.Cid; &Albert.Astals.Cid.mail; + + + +&David.Black; &David.Black.mail; + + + +&Michael.Haertjens; &Michael.Haertjens.mail; + + + +&Osvaldo.Stark; &Osvaldo.Stark.mail; + + + +&Benjamin.Meyer; &Benjamin.Meyer.mail; + + + + + + + +2005 +John Hayes + + +&FDLNotice; + +2006-01-23 +0.7.6 + + + + +&kmahjongg; is a game similar to famous oriental game Mahjongg. + + + + + +KDE +kdegames +KMahjongg +Mahjongg + + + + + +Introduction + + +&kmahjongg; is a game similar to the famous oriental game Mahjongg. The purpose of the game +is to remove all of the tiles from the gameboard. Tiles are removed by matching tiles in +pairs. The matched tiles must have a free side on the left or on the right. Please report +any problems or feature requests to the &kde; mailing lists. + + + + +Gameplay + + + + +Starting a New Game + +When &kmahjongg; is started, the board is setup and the game timer is started, waiting +for your first move. A new game can be started from selecting + +GameNew + + or pressing +&Ctrl;N +. + + + +There is also the option of starting a specific number games. All of the games are numbered and they are randomly selected from + +GameNew + +. + +A specific number game can be started by selecting + +GameNew Numbered Game + + + + + + +Aim of the Game + +The purpose of the game is to remove all of the tiles from the gameboard. Tiles are +removed by matching tiles in pairs, which then disappear. The matched tiles must have a +free side on the left or on the right. + + + + + +Playing the Game + + +Locate and select with the mouse, matching tiles that have a free side on the left or right side. +The tiles disappear as they are selected and matched. + + + +There are two options to help find matching tiles that have a free side. +One option is to get a hint by selecting + +MoveHint + + or pressing H +which will flash a pair of matching tiles. +The second option is to activate + +SettingsShow Matching Tiles +, +which will highlight the selected tile and flash any matching tiles that have a free side +with each move. No additional tiles will be highlighted if there are no matching tiles with a free side. + + + + + +The Game Screen + + + + +A screenshot of &kmahjongg; + + + + + + Screenshot + + + + +A quick explanation of the parts of the game screen... + + + + + +Game Screen + + +The Game screen is in the center of the screen and is the largest section of the window. +The background is populated with the game tiles. + + + + + +Game Timer + + +The Game Timer is located on the top right of the screen. The timer keeps track of the +elapsed time from the start of the game until the game is finished. + + + + + + +Statusbar + + +The Statusbar is located at the bottom of the screen, and contains three panes. The first +pane lists the removed/total tiles and the number of matching tiles that have free sides. +The second pane gives the sequence number of the game you are playing. In the last pane +lets the player know when the game is ready. + + + + + + + + + + + +Configuration and Default Keybindings + +The configuration options are as follows: + + + +Configure &kmahjongg; + + + +Configure Dialog + + + + + + Configure Dialog + + + + + +SettingsConfigure &kmahjongg;... +opens the Configure &kmahjongg; Dialog. + +<guilabel>General</guilabel> options + + + +Show removed tiles + + +Places a grid to the right of the gameboard where matching tiles are stacked after being removed from the gameboard. + +Here's a screenshot of &kmahjongg; showing removed tiles. + + + + + + Removed tiles. + + + + + + + + +Generate solvable games + + +Selects only games that can be solved when you start a new game. + + + + + +Play winning animation + + +Shows a replay of all moves when you win a game. + + + + + + + +<guilabel>Tiles</guilabel> options + + + + +Draw shadows + + +Draws shadows on the tiles to give a 3D appearance. + + + + + +Use mini-tiles + + +Sets the tile size to small. + + + + + + + +<guilabel>Background</guilabel> options + + + + +Scaled + + +Scales the background image. + + + + + +Tiled + + +Tiles the background image. + + + + + + + + + + + + + +Default Keybindings + +SettingsConfigure Shortcuts... allows you to change the default keyboard bindings. + + +Keybindings Configure Dialog + + + + + + + Keybindings Configure Dialog + + + + + +The default kebindings are as follows: + + + + + + +D + + +Playes the game in Demo mode. + + + + + +H + + +Gives you a hint by flashing two matching tiles. + + + + + +F1 + + +Displays this handbook. + + + + + +&Ctrl;O + + +Loads a previously saved game. + + + + + +&Ctrl;N + + +Starts a new game. + + + + + +P + + +Pauses or resumes a game. + + + + + +&Ctrl;Q + + +Quits the game. + + + + + +&Ctrl;&Shift;Z + + +Move redo. + + + + + +F5 + + +Restart the game. + + + + + +&Ctrl;S + + +Saves the game. + + + + + +&Ctrl;H + + +Show the highscores. + + + + + +&Ctrl;Z + + +Move undo. + + + + + +&Shift;F1 + + +What's this? Help + + + + + + + + + + + + +Command Reference + + + + +The main &kmahjongg; window + + +A screenshot of &kmahjongg; + + + + + + Screenshot + + + + + +The <guimenu>Game</guimenu> Menu + + + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;O + +Game +Load... + +Loads a saved game. + + + + + +F5 + +Game +Restart Game + +Restarts the numbered game being played. + + + + +Game +New Numbered Game... + +Starts a specific numbered game. + +Start a Numbered Game Dialog + + + + + + Start a Numbered Game Dialog + + + + + + + + +Game +Open Theme... + +Allows you to load a theme with different backgrounds and tile styles. + +Load Theme Dialog + + + + + + Load Theme Dialog + + + + + +The available options: + +default (shown) +pirates + + + + + + + +Game +Open Tileset... + +Allows you to load a different tileset. + +Open Tileset Dialog + + + + + + Open Tileset Dialog + + + + + +The available options: + +default (shown) +pirates +traditional + + + + + + + +Game +Open Background... + +Allows you to load a different background image. + +Load Background Dialog + + + + + + Load Background Dialog + + + + + +The available options: + +default (shown) +haze +pirates +slate +wood + + + + + + + +Game +Open Layout... + +Allows you to load a different tile layout. + +Load Layout Dialog + + + + + + Load Layout Dialog + + + + + +The available options: + +default (shown) +cross +pirates +pyramid +stax +tower +triangle + + + + + + + + +&Ctrl;S + +Game +Save + +Saves the current game. + + + + +Game +Save Theme... + +Saves the current theme. + + + + + +P + +Game +Pause + +Pauses or resumes the game. While the game is paused, all tiles are hidden. + + + + + +&Ctrl;H + +Game +Show Highscores + +Shows the highscore list. + +Show Highscores + + + + + + Show Highscores + + + + + +When your game scores exceed the top ten scores listed, you are prompted to enter your name. + + + + + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kmahjongg;. + + + + + + + + +<guimenu>Edit</guimenu> Menu + + + + +Edit +Board Editor + +Opens the gameboard editor. + +Gameboard Editor + + + + + + Gameboard Editor + + + + + +The gameboard editor lets you create your own tile layouts for gameplay. + + + + + + + + + +<guimenu>Move</guimenu> Menu + + + + + + +&Ctrl;Z + +Move +Undo + +Undo your last move. + + + + + +&Ctrl;&Shift;Z + +Move +Redo + +Redo a move the was undone. + + + + + +H + +Move +Hint + +Gives a Hint by flashing a matching pair of tiles. + + + + + +D + +Move +Demo + +Starts the game Demo mode. + + + + +Move +Shuffle + +Shuffles the tiles on the board. + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +SettingsShow/Hide Toolbar + +Shows or hides the Toolbar. + + + + +SettingsShow/Hide Statusbar + +Shows or hides the Statusbar. + + + + +SettingsShow/Hide Matching Tiles + +Shows or hides matching tiles by not flashing the matching free tiles when a tile is selected. + + + + +SettingsConfigure Shortcuts... + +Displays a standard &kde; shortcut configuration dialog to change the +keyboard shortcuts for &kmahjongg;. + + + + +SettingsConfigure Toolbars... + +Displays a standard &kde; dialog where you can configure the toolbar icons. + + + + +SettingsConfigure &kmahjongg;... + +Opens a dialog to configure &kmahjongg;. For details, +see here. + + + + + + + + + + +The <guimenu>Help</guimenu> Menu + + + + + + +&help.menu.documentation; + + + + + + + + + + + + +Credits and License + + +&kmahjongg; for KDE + + + +Program Copyright © 1997 &Mathias.Mueller; &Mathias.Mueller.mail; + + + +Contributors: + +&David.Black; &David.Black.mail; +&Michael.Haertjens; &Michael.Haertjens.mail; +&Osvaldo.Stark; &Osvaldo.Stark.mail; +&Benjamin.Meyer; &Benjamin.Meyer.mail; +&Albert.Astals.Cid; &Albert.Astals.Cid.mail; + + + + +Documentation Copyright © 2005 &John.Hayes; &John.Hayes.mail; + + + + +&underFDL; + +&underGPL; + + + + + diff --git a/doc/kmahjongg/layout.png b/doc/kmahjongg/layout.png new file mode 100644 index 0000000000000000000000000000000000000000..38be51e48a4ae63813266e74ff26a2fb5f90957a GIT binary patch literal 26235 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 B)+PV| literal 0 HcmV?d00001 diff --git a/doc/kmahjongg/numbered.png b/doc/kmahjongg/numbered.png new file mode 100644 index 0000000000000000000000000000000000000000..142d402ebaee56fa43fd77bc9e85b9a591fa6205 GIT binary patch literal 4675 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 literal 0 HcmV?d00001 diff --git a/doc/kmahjongg/showremoved.png b/doc/kmahjongg/showremoved.png new file mode 100644 index 0000000000000000000000000000000000000000..33b49957dff875cda257d00cdb467b8035673242 GIT binary patch literal 84972 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�GYw6-paHeo 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 literal 0 HcmV?d00001 diff --git a/doc/kmahjongg/theme.png b/doc/kmahjongg/theme.png new file mode 100644 index 0000000000000000000000000000000000000000..693373cacbd4755b6b826ba03848d7efbf6df7d0 GIT binary patch literal 25955 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 aB;=cdCR-9-0mMxcNGb{%^3}2yq5lhtMszX& literal 0 HcmV?d00001 diff --git a/doc/kmines/Makefile.am b/doc/kmines/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kmines/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kmines/index.docbook b/doc/kmines/index.docbook new file mode 100644 index 00000000..423a60a8 --- /dev/null +++ b/doc/kmines/index.docbook @@ -0,0 +1,617 @@ + + + + + +]> + + + + +The &kmines; Handbook + + + +Nicolas +Hadacek + +

&Nicolas.Hadacek.mail;
+ + + +Michael +McBride +
mmcbride@ematic.org
+
+ +Nicolas +Hadacek + +
&Nicolas.Hadacek.mail;
+
+Developer +
+ +Lauri +Watts +
&Lauri.Watts.mail;
+Reviewer +
+ + + + +2000 +&Nicolas.Hadacek;, Michael McBride + + +&FDLNotice; + +2005-12-28 +2.1.10 + +&kmines; is the &kde; version of the classic minesweeper +game. + + +KDE +game +minesweeper + + + + + +Introduction + +&kmines; is the classic Minesweeper game. You must uncover all the empty +squares without blowing up a mine. + +When you uncover a square, a number appears: it indicates how many mines +surround this square. If there is no number the neighboring squares are automatically +uncovered. When you find a square that you are sure contains a mine, it is +very useful to put a flag on it (by right-clicking). + + + + +How to Play + +You have to use the mouse and its three buttons to uncover or to flag the +squares (with two-buttoned mice, clicking the +middle button is generally achieved by +simultaneously pressing the left and the +right buttons). Here are the details: + + +The left button will +uncover a square. If there is a bomb below that square, the bomb will +explode, and the game will be over. +If there is not a bomb under the square, a number will appear. +That is the number of neighboring squares that contain bombs. +Which ones? you ask: That's the point of the game. +For each square (excluding edge and corner squares), there are 8 +neighboring squares. +If none of the neighboring squares has a bomb, then a blank will +be shown under the square, and all neighboring squares will be +automatically uncovered. +Left clicking a flagged square is +safe and does nothing. +The right button will mark +a square as containing a mine (by drawing a red flag on it) or, if the option is +set, as being uncertain (by drawing a question mark on it). The uncertain tag can +be useful when you are puzzled about the positions of +mines. +The middle button will +clear the surrounding squares if the right number of squares is +already flagged. It is very useful since it is much quicker than +uncovering all individual squares. +If your flags are not properly positioned, you will +explode a bomb. +Pressing the yellow smiley will start a new +game. + + +These are the default settings. The mouse buttons can be +reprogrammed. For more information, see the section entitled Game Options. + +The &kmines; Screen + + +The &kmines; Screen + + +The &kmines; Screen + + + +The &kmines; screen consists of: + + +A Box showing you the number of mines still left to be marked. +Every time you flag a bomb site, this number will decrease by one. +This box does not determine if you are right or wrong, but only how +many mines you need to theoretically mark before you are done with this +game. +In this example, there are 40 mines to be marked. +A Yellow Smiley Face. Clicking this will start a new game. If +you lose the game, it will become a frown. +The box on the right shows how much time this round has taken. +The shortest time for each level is listed as the high +score. +The playing area. This will vary in size depending on the +difficulty level of the game. In this example, it consists of 256 squares. This is where you play the game. + + + + + +Game Options +The options to &kmines; are set by selecting +Settings Configure &kmines; + from the menubar. This will bring up a dialog box. + + +&kmines; Preferences + + +&kmines; Preferences + + + +This dialog box is divided into three sections. + + + +Game + +The first option, labeled Enable ? mark, +determines whether you can mark squares as +questionable. If this option is checked, then right clicking the mouse will first cause a square to be +flagged. If you right click again on the same square, the flag will change to +a ?, to indicate you don't know if there is a bomb +there or not. If you click again, the square will change back to +blank. + +If there is not a mark in front of this option, the first +right mouse click will change the square to a +red flag. The second will make it blank again, thus skipping the ? +symbol. + +The next option down, labeled Enable +keyboard, determines if the keyboard shortcuts will work +while playing the game. The default keyboard shortcuts are given in , and can be changed by selecting +SettingsConfigure Shortcuts.... + +The only keyboard shortcuts affected are those +responsible for playing the game. Keyboard commands such as +New Game and +Quit, still work, regardless of this +option. + +The Pause if windows loses focus option will +automatically pause the game if the &kmines; window loses focus. When the game +is paused, the gameboard disappears, so you cannot use pausing the game as a way +to cheat! + +The "Magic" reveal option switches on Magic +reveal mode. In this mode, &kmines; does most of the gameplay for you: +If you have uncovered enough squares to be able to tell with certainty that a +square contains a mine, &kmines; will automatically mark it as a mine, and +perform an autoreveal on squares for which it has marked the correct number of +mines. Note that when you have this mode switched on, your scores do not count +as highscores. + + +The last section of this tab allows you to change the action of each of +the three mouse buttons. Your options are: + + + +Reveal +This will reveal the contents of the square. If there is no bomb, +then a number will appear. If there is a bomb under that square, then the bomb +will explode, and the game will be over. + + + +Autoreveal +Clicking on a square with this button will automatically reveal +all neighboring squares that are not marked with red flags. If one of those +neighboring squares has a bomb under it, the bomb will explode, and the game will +be over. + + + +Toggle Flag +Clicking with this button on a square will cycle through blank, +red flag, and question mark. + + + +Toggle ? Flag +Clicking with this button on a square will mark/unmark with a +question mark. + + + + + + +Appearance + +The top three color buttons determine the +Flag Color, Explosion Color +and Error Color. + +The rest of this page is concerned with mines colors. To adjust, +simply click the color button beside each description, and select the +new color. + + + + +Custom Game + +Finally, if you are not finding the existing configurations +challenging enough, you can set up a custom game here. Use the +sliders to change the Width, +Height, and the percentage of the board covered +with Mines. The default game has 15 percent of +the board covered, so there is plenty of room to make the game +harder. + + + + +Saving your changes +Once your changes are complete, click +OK to make them permanent. + +If you want to abandon your changes, click +Cancel to abort the changes. + +If you click on Defaults, the default +settings will be restored. Click OK to make +these default changes permanent, or edit the settings some +more. + + + + + + + +Commands/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + +The Game menu consists of 4 options. + + + + + +&Ctrl;N + +Game +New +Starts a new game. + + + + + +P + +Game +Pause +Pauses or resumes the game. This will hide the board (no cheating), and +give you a button to press when you want to continue this +game. + + + + + +&Ctrl;H + +Game +Show Highscores +This shows you the high score (the shortest time) for each +difficulty level. + + + + + +&Ctrl;Q + +Game +Quit +Quits &kmines; + + + + +<guimenu>Move</guimenu> Menu + + + + + +H + +Move +Hint +If you use this item, you will get a hint about where +you should click next. If you do that your score will not be added to the high-scores. + + + + + +Move +Solve +This item tries to solve the current game. + + + + + +Move +Solving Rate... +This item shows a dialog that calculates the solving rate for the current game type. + + + + + +Move +View Log +This item shows a dialog with the log of the last game. + + + + + +Move +Replay Log +This item replays the current log. + + + + + +Move +Save Log... +This item shows a dialog to save the current log. + + + + + +Move +Load Log... +This item shows a dialog to load a log. + + + + + + +<guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ + +View +Zoom In +Increase the size of the gameplay canvas. + + + + + + +&Ctrl;- + +View +Zoom Out +Decrease the size of the gameplay canvas. + + + + + + +<guimenu>Settings</guimenu> Menu + + + +Settings +Show Menubar +If there is a check in front of this option, then the menubar +will be visible. Selecting this option, hides the menubar. +To restore a hidden menubar, click outside the game play area, with +the right mouse button. This will bring up a small sub-menu. Simply select +Show Menubar from this sub-menu, and the menubar +will be visible again. + + + + +Settings +Choose Game Type + + +Lets you set the difficulty level from a sub-menu. +There are three default levels of difficulty: +Easy (64 squares, 10 mines), +Normal (256 squares, 40 mines), and +Expert (480 squares, 99 mines). There is also a +Custom... level settings. +If you select Custom..., then the +settings you have configured in the Configure +&kmines; dialog will be used. + + + +SettingsConfigure +Shortcuts... +This item lets you change the keyboard shortcuts used by &kmines;. + + + +SettingsConfigure +Notifications... +This item displays a standard &kde; notifications configuration +dialog, where you can change the notifications (sounds, visible messages, +&etc;) used by &kmines;. + + + +SettingsConfigure +Highscores... +On the Main tab you can change your +Nickname and add a +cute Comment about yourself, to show in the high +score table. You can also activate the World-wide highscores +enabled checkbox, and share your skill at &kmines; across +the world with an online highscore server. If you are connected to the +Internet, &kmines; will send your score automatically at the end of the +game to the highscore web server (kmines.sf.net). +The Advanced tab displays your Registration Data +on kmines.sf.net: Your Nickname: from the Main +tab and the Key:. This key was generated when you registered on kmines.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + +Settings +Configure &kmines;... +Allows you to adjust game options. For more detailed +information, see the section entitled Game +Options. + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + +Default Keyboard Shortcuts +The following table shows you the default keyboard shortcuts. + + + +Keyboard Shortcuts + +Key ComboAction + +ArrowsMove cursor in playing area. +PageDownMove to bottom +edge +PageUpMove to top +edge +HomeMove to left edge +EndMove to right edge +SpaceReveal square. +WMark square as mine +ReturnAutomatically reveal all +surrounding squares not marked by a flag. +&Ctrl;NNew Game +PPause Game +&Ctrl;QQuit &kmines; +F1&kmines; Handbook +&Shift;F1What's This Help +&Ctrl;HShow Highscores +&Ctrl;MShow Menubar +&Ctrl;+Zoom In +&Ctrl;-Zoom Out + + + +
+ +These shortcuts can be changed by selecting +Settings Configure +Shortcuts... from the menubar. + +
+ +
+ +Credits and License + + +&kmines; + + +Program copyright 1996-2000 &Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Documentation copyright 2000 &Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Documentation updated for &kde; 2.0 by &Mike.McBride; +&Mike.McBride.mail; +Some changes for &kde; 3.2 by &Philip.Rodrigues; &Philip.Rodrigues.mail; + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kmines; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + + diff --git a/doc/kmines/kmines1.png b/doc/kmines/kmines1.png new file mode 100644 index 0000000000000000000000000000000000000000..d872f73514136d019b7ac89cb5a399b99992316d GIT binary patch literal 9222 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 DcZb&P literal 0 HcmV?d00001 diff --git a/doc/kmines/kmines2.png b/doc/kmines/kmines2.png new file mode 100644 index 0000000000000000000000000000000000000000..961acdbdc2aa871fc60fd7ab438bbb2ff75b377a GIT binary patch literal 34300 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 literal 0 HcmV?d00001 diff --git a/doc/kolf/Makefile.am b/doc/kolf/Makefile.am new file mode 100644 index 00000000..171f575c --- /dev/null +++ b/doc/kolf/Makefile.am @@ -0,0 +1,2 @@ +KDE_LANG = en +KDE_DOCS = AUTO diff --git a/doc/kolf/index.docbook b/doc/kolf/index.docbook new file mode 100644 index 00000000..9da81ae3 --- /dev/null +++ b/doc/kolf/index.docbook @@ -0,0 +1,833 @@ + + + + + +]> + + + + +The &kolf; Handbook + + + +Jason +Katz-Brown + +
jasonkb@mit.edu
+
+
+ +
+ +&FDLNotice; + +2005-12-18 +1.1 + + + +&kolf; is a miniature golf game for &kde;. + + + + + +KDE +golf +miniature +arcade + + +
+ + Introduction +&kolf; is a miniature golf game with block graphics and a 2d top-down view. Courses are dynamic, and up to 10 people can play at once in competition. +&kolf; comes with a tutorial course. Playing this course is the easiest way to get started. + + + + +Tutorial + +&kolf;'s game-play is simple. It is easiest to learn how to play hands-on, and thus a tutorial course is included to guide you through the basics. This chapter will guide you through this tutorial. To start the tutorial, choose HelpTutorial. + + +Hitting the Ball +To aim the ball, either line up the mouse behind the ball so the +putter aims in the direction you want to hit it or press one of the +Left Arrow (counterclockwise) or Right Arrow (clockwise) keys, to rotate the putter. + +To rotate the putter more quickly with the keyboard, hold down Shift while pressing or holding the arrow keys. To rotate more slowly, hold down Control. + +To hit the ball, press and hold either the &LMB; or Down +Arrow key. The longer you hold down the key or button, the +more strength the putt will have. It will take practice to get a good +feel for speed. + +To stop abort the putt while you are still holding moving the putter back in your stroke, press Escape. + + +Hit the ball into the hole (gray circle) to move on the next. + + + +Slopes +Slopes are areas of slanted down that the golf ball rolls down in an expected manner. The light of the golf course is from the upper-left, so the brightest slopes will push the ball towards the upper-left corner of the hole. Choose HoleShow Info to see an arrow that points in the direction the slope will roll the ball. +On this tutorial hole, hit the ball onto the upward-pointing +slope and let it roll into the cup to move onto the next hole. + + Directions +On the next hole, experiment with the other kinds of slopes. There are elliptical, vertical, horizontal, and diagonal slopes in &kolf;. Hit the ball into the hole. + + Steepness +The next tutorial hole has two slopes of differing steepness. If you choose HoleShow Info, the steepness of the slope will appear as a number from 1-8, where 8 is steepest and 1 is shallowest. Also, the more extreme shading the slope has, the steeper it is. Hit the ball into the hole and continue to the next hole. + + + + Walls +Balls bounce of walls as one would expect them to, with a little bit of dampening. Deflect the ball off the walls and into the cup to advance to the next hole. + + + Puddles and Sand + Puddles +Puddles are blue. When your ball rolls into a puddle, a penalty stroke is added to your score and your ball is placed outside the puddle. + + Sand +Sand is yellow. Balls roll through sand very slowly. +Hit the ball around the puddles and through the sand into the hole to continue to the next tutorial hole. + + + + Windmills +Windmills are compound objects with brown border walls and a moving arm at either the top or bottom of the object. Half-walls create the opening to the windmill, which a black arm swings across. It takes practice to be able to time stroke of the ball to pass through the opening. Hit the ball through the windmill into the hole to continue. + + + Black Holes +When you hit your ball into a black hole, it is transported to the exit and ejected at the angle of the exit at a speed directly relational to the speed your ball was going. Choose HoleShow Info to see which Black Hole goes to which exit and which direction the ball will come out of the exit. The rim around Black Holes and their corresponding exits are also the same color. Hit the ball into the black hole, which will then eject the ball into the cup so you can go to the next hole. + + + Floaters +Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. This one is a pretty slow floater, but you still must time your shot so you can go over the wall and into the cup. + + + Bridges +Bridges are simple - they just lift the ball above the things below them, like slopes, sand, and puddles. They can have brown walls on their perimeter. + + + + Basics +Here's in-depth description of &kolf;'s basic features. + + +Starting a Game + +To start a game, choose +GameNew. +This opens a dialog where you can configure your new game. + + +More third-party &kolf; courses are available at the &kolf; website. See the &kolf; user-uploaded courses page. + + + +<guilabel>Players</guilabel> Page +To add a player to your game, click the New +Player button. + +The text boxes in the grass area are the names of the players in +this game. To change the name of the players, change the text in the +text boxes. To change the ball color of the players' balls, click the +middle button that has a color rectangle, and a dialog will open where +you can select the new ball color. The rectangle on the button is the +current player's ball color. + +To remove a player, click the Remove +button next to the player's name. + + + + <guilabel>Course</guilabel> Page +To choose the course that you want to play, click it's entry in the list box on the left of this page. &kolf; comes with many built-in courses that you can play. +To add a course that you have downloaded or made, click on the Add... button on the bottom of this page, and choose the course file in the file dialog. + + <guilabel>Options</guilabel> Page +On this page you can choose whether or not this game will be played in Strict mode. In strict mode, there is no undo, moving holes, or editing of the course. This mode is generally for competition. Only in strict mode are high scores kept. + + + + Saved Games +&kolf; can keep a history of your rounds that are still in progress and save them to disk so you can load them and play them later. Saved games include the current hole, names and colors of all players in game, and the scores of the players. + Saving Your Game +To save your game while still playing, choose GameSave Game +. If you have not saved a game on this game before, you will be prompted for the file to save to. + + Loading Your Game +To load the game that you saved, choose GameLoad Saved Game... +. You can then choose the saved game to open. + + + + Practicing +There are a few features that make practicing your golf easier. These features are not enabled during strict mode. + Undo +To undo a bad shot, choose HoleUndo Shot. + + Switching Holes +The Go menu contains many menu items for switching to different holes that are quite self explanatory. See the Go menu reference. + + + + Putting Options +&kolf; has a few ways to putt. The basic putting method is described above. +The mouse is enabled by default. This means that whenever the mouse is moved, the putter aligns to the line between the mouse and ball. +If you prefer to only use the keyboard and arrow keys, you can disable mouse putting by unchecking SettingsEnable Mouse for Moving Putter. + + Advanced Putting +Advanced putting can be enabled by checking SettingsEnable Advanced Putting. This putting mode is slightly more challenging than the regular click-and-hold mode, and can be used with either the mouse or keyboard. + +To putt in this mode: + + +Click or press the Down Arrow key once to begin the swing. The putting indicator will appear, and the strength gauge will start filling up with a color, which changes as the gauge fills up. + + +Click or press the Down Arrow key when the strength gauge has filled up the amount that you want. The farther around the circle the strength gauge has filled up, the stronger the putt will be. It takes practice to be able to get the correct strength on putts. +After this, the color in the putting indicator will start to wind down back to where it started. + + +Click or press the Down Arrow key again when the strength-indicator is as close as possible to the center line of the putting indicator. + + +The closer the final click (or key press) is, the more true the putt is to the original line. + + + + + Editing Courses +&kolf; comes with a versatile editor for its course file format. Courses can have any number of holes, and there is no limiting the creativity of the course designer. + + Creating a New Course +To create a new course, choose GameNew. Go to the Course page of the dialog, and choose the Create New course. Make sure that, under the Options page of the dialog, Strict mode is unchecked. + + +To play this new course later, click the Add... button to add the course to your list of courses. + + + + Adding Holes +Choose HoleNew to add a new hole at the end of the course. + +You can restore a hole to a blank state, similar to when it was first created, by choosing HoleClear. + +Two items will appear on new holes: a cup, and a white ball. The white ball marks where players' balls start the hole. + + + Editing Basics +The basic building block of &kolf; courses is the object. Holes are made up of a bunch of objects. Objects can be moved to any location, and the different kinds of objects each have different properties you can set. +To start editing a hole, toggle the HoleEdit menu item, or press the pencil icon on the toolbar. To leave editing mode, uncheck this menu item (or the toolbar icon). + + Adding Objects +To add a new object to the course, choose it's name in the list box labeled Add object:. The object will appear in the center of the course. + +Bridges and signs will cover up new objects if they were already in the center of the hole. + + +You can add more than one cup to a hole! Doing so can create interesting hole designs. + +Some &kolf; objects are available as plugins. If you add these objects to your course, course players will need to download the plugin. +Third-party &kolf; plugins are available at the &kolf; website. See the &kolf; plugins download page. + + + Moving and Resizing Objects +Move your mouse cursor over the object that you want to move, and the mouse cursor will change to a hand cursor. Then click and drag the object to where you want it. +Puddles, sand, floaters, bridges, and signs can be resized by dragging the small circle at their lower-right corner. + + +Walls can be moved by clicking and dragging their endpoints, or you can move the whole wall by clicking and dragging on the middle part of the wall as normal. + + + + Objects' Settings +Objects' properties can be changed in the area at the lower-right corner of the &kolf; window. To edit an object, move your mouse cursor over it until the cursor changes to the hand cursor. Then click. Various controls for that object will appear in the lower-right corner of the &kolf; window. + General Hole Settings +To edit general hole settings, click an area of the hole that is not covered by an object. When this is possible, the cursor will be the normal pointer cursor. + + + Course name: + +This is the name that is shown in the new game dialog and in the About Course dialog. + + + Course author: + +This is the author name that is shown in the About Course dialog. + + + Par: + +The par of the hole. + + + Maximum: + +The maximum number of strokes a player can take on the hole. + + + Show border walls + +Whether or not to show walls around the border of the hole. + + + + + + Slopes + + + Type + +Choose the type of the slope - Vertical, Horizontal, Diagonal, Opposite Diagonal, Circular. Diagonal and opposite diagonal slopes are triangles, while circular slopes are circles. Vertical and horizontal slopes are rectangles of course. + + + Reverse direction + +Whether or not to reverse the slant of the slope. For example, consider an elliptical slope (a circular slope). When it is not reversed, it pushes balls outward like a mound. When it is reversed, it sucks balls inward like a hole. + + + Grade: + +Choose the grade (steepness) of the slope by moving the slider so the grade moves towards 0 or 8, where 8 is steepest and 0 is flat. Steeper slopes push the ball more. + + + Unmovable + +Whether or not this can be moved by other objects, like floaters. + +If a floater overlaps with this object at any point on this path, and the slope is large and thus won't move the floater, you must make the slope Unmovable, or &kolf; will suffer poor performance! + + + + + + + Puddles and Sand +Puddles and Sand have the same configuration options as each other. + + + Enable show/hide + +Checking this enables the flashing of this puddle or sand. + + + Show/Hide speed + +The more towards Fast the slider is, the faster the puddle or sand flashes. + + + + + + Bridges, Windmills, Floaters, and Signs +Bridges, windmills, floaters, and signs all have similar settings for configuring on which sides there are border walls. To show the wall on a side, check the check box for that side. +There are also some more specific options for windmills, floaters, and signs. + + Windmills + + Windmill on bottom + +Whether or not the black windmill arm is on bottom or top - the default is that it is on the bottom. The two half-walls will always be with the arm. Note that you cannot enable a border wall on the side that the arm is. + + + Speed + +The more towards Fast the slider is, the faster the black windmill arm moves. + + + + + + Floaters +The path the floater moves along is shown by the wall it's connected to. + + Moving speed + +The more towards Fast the slider is, the faster the floater moves. If the slider is all the way at the Slow end, the floater will stop. + + + + + + Signs + + Sign HTML: + +You can enter any valid &HTML; here. For example, entering Hit it <i>softly</i> towards the slope! will create the text Hit it softly towards the slope!. + + + + + + + + + + + +Command and Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N + +Game +New... + + +Start a new game + + + + + +&Ctrl;O + +Game +Load Saved Game... + + +Load a previously saved game. + + + + + +&Ctrl;S + +Game +Save Course + + +Save the current course using it's current file +name. + + + + + +GameSave Course As... + + +Open a file dialog allowing you to save the current +course using a new file name. + + + + + +GameSave Game + + +Save the current game with the last saved game filename, or under a filename you specify if you have not saved the game yet. + + + + + +Game +Save Game As... + + +Opens a file dialog allowing you to save the current +game with a filename of your choice. + + + + + +&Ctrl;End + +Game +End Game + + +End the current game, without closing &kolf; + + + + + +&Ctrl;H + +Game +Show Highscores + + +Display the high score table. + + + + + +&Ctrl;P + +GamePrint... + + +Print the current screen. + + + + + +GameAbout Course + + +Show name, author, and par of course. + + + + + +&Ctrl;Q + +GameQuit + +Quit &kolf; + + + + + + +The <guimenu>Hole</guimenu> Menu + + + + +&Ctrl;E + +Hole +Edit + + +Switch to editing mode to edit the current hole. + + + + + +&Ctrl;&Shift;N + +Hole +New + + +Create a new hole. (Only while editing.) + + + + + +&Ctrl;Delete + +Hole +Clear + + +Remove all objects from the current +hole. (Only while editing.) + + + + + +&Ctrl;R +Hole +Reset + + +Reset the current hole to it's starting +position, so that you can start over. + + + + + +&Ctrl;I + +Hole +Show Info + + +Show information about objects on the current hole. + + + + + +&Ctrl;Z + +Hole +Undo Shot + + +Undo the last shot you made. + + + + + + + +The <guimenu>Go</guimenu> Menu + + + + +Go +Switch to Hole + + +Go directly to another hole within the +course. A list of the hole numbers allows you to choose +which hole you wish to go to. + + + + + +&Alt;Right Arrow +Go +Next Hole + + +Go the the next hole. + + + + + +&Alt;Left Arrow +Go +Previous Hole + + +Go to the previous hole. + + + + + +&Ctrl;Home +Go +First Hole + + +Go to the first hole in the course. + + + + + + +&Ctrl;&Shift;End + + +Go +Last Hole + + + +Go to the last hole in the course. + + + + + +Go +Random Hole + + +Go to a random hole from the current course. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + +Toggle on and off the main toolbar. + + + + + +Settings +Show Statusbar + + +Toggle on and off the statusbar. + + + + + +Settings +Enable Mouse for Moving Putter + + +Allow the use of the mouse to move the +putter. + + + + + +Settings +Enable Advanced Putting + +Enable the advanced putting mode, as +described in its own +section. + + + + + +Settings +Show Putter Guideline + + +Toggle on and off the putter guideline. + + + + + +Settings +Play Sounds + + +Toggle on and off the sound effects. + + + + + +Settings +Enable All Dialog Boxes + + +Re-enables any dialog boxes that you disabled by clicking on +Do not show this message again or similar. + + + + + +Settings +Reload Plugins + + +Reload plugins. + + + + + +Settings +Show Plugins + + +Display the currently loaded plugins. + + + + + +Settings +Configure Shortcuts... + + +Customize the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + + +Open a dialog where you can configure the toolbars for &kolf;. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Credits and Licenses + +&kolf; copyright 2001, 2002 &Jason.Katz-Brown; + + +Developers + +&Jason.Katz-Brown; jasonkb@mit.edu + + +Niklas Knutsson + + +Ryan Cumming + + +Daniel Matza-Brown + + + +Documentation copyright 2002, &Jason.Katz-Brown; + + + +&underFDL; +&underGPL; + + + + +Installation + +&install.intro.documentation; +&install.compile.documentation; + + + +&documentation.index; + +
diff --git a/doc/konquest/Makefile.am b/doc/konquest/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/konquest/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/konquest/index.docbook b/doc/konquest/index.docbook new file mode 100644 index 00000000..34a70b66 --- /dev/null +++ b/doc/konquest/index.docbook @@ -0,0 +1,295 @@ + + + + + +]> + + + + +The &konquest; Handbook + + + +Nicholas +Robbins + +
&Nicholas.Robbins.mail;
+
+
+ +
+ + +Russ +Steffen + +
&Russ.Steffen.mail;
+Developer +
+ + +Lauri +Watts + +
&Lauri.Watts.mail;
+
+Reviewer +
+ + +2000 +Nicholas Robbins + + +&FDLNotice; + +2005-12-16 +1.1 + +&konquest; is a wonderfully complex game for universal +domination, or konquest, if you like. :) + + +KDE +Konquest +kdegames + + +
+ + +Introduction + +This the &kde; version of Gnu-Lactic +&konquest;, a multi-player strategy game. The goal of the +game is to expand your interstellar empire across the galaxy and of +course, crush your rivals in the process. + + + + +Using &konquest; + + +More &konquest; features + +A really entertaining game of universal domination. + + + + + +Configuration + +When you start a &konquest; game, a settings window pops up. +From here you need to enter player names, decide the number of +planets, and how many turns are allowed. (Don't worry, you can add +more later) You can also reject the map, until a you find a galaxy +layout you like. After this, just click OK +to start the game. + + + + + + +Command Reference + + +The Main &konquest; Window + +The Planet Status indicator on the right is +your source for intelligence on the state of the Galaxy. Your spies +will report on the status of your rivals, but not on the +native-controlled planets. The information present in the display is: +Planet name, Owner, +Production rate, fleet strength +(Ships and Kill +percent). + +The Production rate is the number of ships +that planet will construct in one turn. The fleet strength is the +number of Ships currently in place at the +planet. And, the Kill percent is a measure of the +effectiveness of the ships produced at that planet. Attack fleets take +the kill percentage of their planet of departure, and defense fleets +use the kill percentage of the planet they are defending. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;End + +Game +End Game + +End the current game, without closing &konquest; + + + + +Game +Measure Distance + +Click with the &LMB; on two planets to see their distance. + + + + +Game +Show Standings + +Opens a window to display the detailed current +standings for all players + + + + +Game +Fleet Overview + +Opens a window to display detailed informations +for all fleets + + + + + +&Ctrl;Q + +Game +Quit + +Quits the game. + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + +Toggle the toolbar display on and off. + + + + + +Settings +Configure Shortcuts... + + +Opens a standard &kde; shortcut configuration dialog to +change the keyboard shortcuts used by &konquest;. + + + + + +Settings +Configure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &konquest; toolbar icons. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&konquest; + +&GNU;-Lactic Conquest Project, &kde; version by &Russ.Steffen;, +&Russ.Steffen.mail;. + +Copyright © 1998 by the &GNU;-Lactic Conquest Project + +Documentation copyright 2000 Nicholas Robbins +&Nicholas.Robbins.mail; + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &konquest; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully use &konquest;, you need &kde; 3.0. It +is also reccomended that you have your X server set to over 8bpp, as +the plants are scanned and require a rather high color depth. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; + +
+ + diff --git a/doc/kpat/Makefile.am b/doc/kpat/Makefile.am new file mode 100644 index 00000000..60062a5e --- /dev/null +++ b/doc/kpat/Makefile.am @@ -0,0 +1,5 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + +KDE_MANS = AUTO diff --git a/doc/kpat/clubs.png b/doc/kpat/clubs.png new file mode 100644 index 0000000000000000000000000000000000000000..55a6f6ed7036b3a51a500b13783d8340c1d5c2fe GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv|!2~1)6P3RKDVB6cUq=Rpjs4tz5?O(KixSs} zlHmNblJdl&RECH!g|O73;>`R!1!Fx!gG2>|pwu!e1w$i!BTIct69o;R4GIN`CCM2I zhI&StcSZF)fo4>Bx;TbZ%t`+9@i{wlWJ9M-E)Nfn&JGoe2nz+Cn7%`K>M_z%vp4u9 zF^J8bF)d;1v{hw%Eq-S*4oDe>cyXTC*x$_?eum@ugRVjohH(Ev4#7;tO{W}%<}Z19 zMD%w>5UXiScaDu=j$2GC>&&H`h7Es;IF@Ih+%kEa72`o0>-{NRzl?ycU@!6Xb!ETE Y#LvOSa-=2N94N%#>FVdQ&MBb@0C^TzI{*Lx literal 0 HcmV?d00001 diff --git a/doc/kpat/diamonds.png b/doc/kpat/diamonds.png new file mode 100644 index 0000000000000000000000000000000000000000..b57215a8c716256b480b45ca50bc8e3031c2927c GIT binary patch literal 1035 zcmV+m1oZofP)g^yJk%Iy`iPaJ;gt z>D0UX_2f)nR16Xb#Kgpef|7i@SA2eRv7(7AVh_~NOk`THab)@H-1+$rd~RGUGA%?}R6L3tCQBS&%QReBOziCJjFg33SzI(eHf(QfBr++e z$ei25oJp!AGCnMajf5mECqz$7IFL$&c4TS9hWrBjII3iPs^s|eh@{%AbJ#&ll2nw2 zWDt0E43(6Z*KrKHxmtIkc*0oN@Ypb-RNvfEWOigfZ!}nvILxJqxaGe5{zJU%A08hpb~KE_bbN|<%(|r9%Cux+Uu0=yTxeKST1@QZ(CqW(Ors}MT2!Qz zeLPD%{NvK3j&#JepHyH}6cq@}#>$L_jKHCqg#Z8mICMo=bW?9;ba!ELWdK-KAXa5^ zb7^mGATus8Fkv7dQe}2JATcs8GC40fG$1HYPDdbcVRU0?ATcg7Dcuz=NdN!ZVi@UirfFWOSJs8M(jls{5a5{km}3G*pva3rQ+g=Cr0xV@M-|r zNX65HEBm-ZfhIoyt+@7j4QY|Z;FbeWteOrJ+nWwB-a*@58UuRsdH^v??Si-^RCyl| zEBr>dfe!n3Bo9{Kq`Uz}by|DYG;6~E000McNliru(E<+$4+AHcI?(_C002ovPDHLk FV1nHn%jy6C literal 0 HcmV?d00001 diff --git a/doc/kpat/hearts.png b/doc/kpat/hearts.png new file mode 100644 index 0000000000000000000000000000000000000000..9769cd2ab143a30dfc829d0f41b25fb91ab26e8c GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv|!2~1)6P3RKDVB6cUq=Rpjs4tz5?O(KixSs} zlHmNblJdl&RECH!g|O73;>`R!1!Fx!gG2>|pwu!e1w$i!BTIct69o;R4GIN`CCM2I zhI&StcSZF)fo2qXx;TbZ%t`+9@i{wlWJ9M-uE@(2$ESq~HwxktM3+p;o_Myccq7-M z7cRk#><2O-JtWYX6|6QUM`;GZWEN0x&oVQ@>icJz_k1q8n_MP22W7bAV z_pcF-+gRFv7vAQ)*kFAoV@lfxH=&Pgg&ebxsLQ0Mt8PN&o-= literal 0 HcmV?d00001 diff --git a/doc/kpat/index.docbook b/doc/kpat/index.docbook new file mode 100644 index 00000000..3fcda113 --- /dev/null +++ b/doc/kpat/index.docbook @@ -0,0 +1,811 @@ + + + + + +]> + + + +The &kpatience; Handbook + + + +Paul Olav +Tvete + + +Maren +Pakura + + +Stephan +Kulow + + + +MikeMcBride +Reviewer +
mpmcbride7@yahoo.com
+
+ + +Paul Olav +Tvete +Developer + + + +Stephan +Kulow +Developer + +
+ + + + +2000 +Paul Olav Tvete + + +20012004 +Maren Pakura + + +&FDLNotice; + +2004-02-07 +3.2 + + +&kpatience; is a highly addictive card game for &kde; + + + + +KDE +game +patience +solitaire + +
+ + +Introduction + + +To play a patience you need, as the name suggests, patience. For simple games, +where the way the game goes depends only on how the cards fall, your patience is +even the only thing you need. + + + +But there are also patience where you also need to plan your strategy and think +ahead to win. + + + +All the games have in common that you have to put the cards in a special order +in moving, turning and reordering them. + + + + + +Rules + +suits +A card game contains one or more card decks of 52 cards each. Each deck contains +the four suits: + + + + + + + + + + + + + + + + + +Clubs + + + + + + + + + + + + +Spades + + + + + + + + + + + + +Hearts + + + + + + + + + + + + +Diamonds + + + + + + + + +Each of these suits contains itself the following cards types: ace, two, three, +four, five, six, seven, eight, nine, ten, jack, queen and king. + + +family +This complete order of cards is called a family. There are real families and +alternating ones. The real families are all of one suit (⪚ Hearts) while the +alternating families the card color changes from one to another between a black +color (Clubs and Spades) and a red color (Hearts and Diamonds). + + + +If the family is not complete, it's called a sequence. + + + + + +The game field of &kpatience; + + + + +There are only a few different elements in typical patience game. These should +be introduced in the following. + + + +1. Talon + +talon +In the beginning of each game all cards are mixed in the deck. But not in every +game all cards are dealt out, but some reside in the deck. These cards are put +down on the so called talon, which you can find quite easy as it is in most +games the only pile showing the reverse. + + + +If no card shows the reverse side, all cards are dealt out and there is no +talon. These card games are the hardest as there is no randomness involved after +the start of the game, so there is nothing to blame + + + + + +2. Waste Pile + +waste pile +Many games put cards from the talon first on a waste pile when you click on the +reverse of the top card on the talon. + + + +From there you can take the card (which is then face up) and put +it into the game. + + + + + + +3. Foundation + +foundation +The foundation piles is where you want to have your cards in the end. If all +cards end up there in the right order, you've won. + + + +In most card games these piles are empty in the beginning, but &kpatience; drops +cards there when it sees them fitting to save you this most often boring part. + + + + + +4. Playing Piles + +playing piles +These piles are the ones where the actual reordering happens. The rules for +these piles vary a lot between the games. Some show all cards on them, some +not. Some allow only card to be removed, some allow every card to be removed, +etc. See the following chapters for details. + + + + + + +General Use + + +Use of the menu is too easy to be described now. + + + + + +Klondike + + +Klondike +Klondike is the most famous patience - most likely because an often used +operating system comes with it. It is played with one deck. + + + +Goal of Klondike is to put all cards as real families ascending on the +foundation. This works usually pretty well as soon as all cards are lying face +up in the playing piles. + + + +The sequences on the playing piles have to be put there descending while the cards +should alternate in colors (red and black). You can move whole sequences or +parts of it if the first card fits on another pile. + + + +On a free pile you can put a king of any color or a sequence starting with a +king. + + + +When you click on the talon, one card from will be moved to the waste pile, +from where you can move it to the playing piles or the foundation. +Is the talon empty, you can move the complete waste pile on the talon +in clicking on the empty talon. + + + +You can look through the cards on the talon without any limit, but you should +learn fast that you've lost if you can't find one card to move and that a new +shift doesn't help you in finding new solutions. + + + + +Grandfather + + +Grandfather +This game has been introduced to Paul by his grandfather, so it got this +name. There aren't any other patience games known implementing this patience +game. + + + +Grandfather deals one deck to seven playing piles while some cards on each pile +are face down on initial deal. + + + +The goal is to put all cards as real families ascending on the foundations. + + + +You can move every card on every pile if it fits on another card to build a real +sequence with descending order. For example you can move the five of spades on +top of the six of spades, no matter how many cards on are on top of the five of +spades. Just the six of spades has to be on top of its pile. + + + +On a free pile you can place a king (again no matter how many cards are on top of +it) + + + +If no more cards can be moved, you can re-deal up to two times. All cards already +in the foundations stay there, but the cards in the playing piles are reordered +to give you a new chance to find a solution. + + + +Even though the rules are simple and allow quite some moves, the game is still +hard to win, but is still a joy to play (or because of that). + + + + +Aces Up + + +Aces Up +This patience has very simple rules, but is still hard to win. It is played +with one deck and the goal is to put all cards beside the aces to the +foundation. After that there should be an ace on every playing pile left. + + + +Each top card that is of the same suit (⪚ spades) and has a lower +value than another top card (⪚ six of spades and four of spades) can be +put on the foundation by clicking on it. + + + +If you can't move any more cards to the foundation, you can get a new card for +each playing pile in clicking on the talon. + + + +On a free pile you can move every other card on top of a pile. You should choose +these moves to free piles the way that new cards can be moved to the foundation +after that. + + + +The auto drop feature is disabled for this patience. + + + + + +Freecell + + +Freecell +Freecell is playing with one card deck. You have four free cells in the left top +corner. Beside that there are the four foundation piles and below it there are +eight playing piles. + + + +The goal of the game is to have all cards as real families ascending on the +foundation. This works quite often if you know how to play as Freecell is +solvable at a rate of 99.9% approximately (of the first 32000 deals there is only +one unsolvable - 11982 if you want to know). + + + +In the playing piles you have to build descending sequences, where red and black +cards alternate. In a free cell you can put any card. + + + +You can only move one single card that lays on top of a pile or a free cell. +Sequences can only be moved if you have enough free room (free cells or free +playing piles) to put the cards in between you move. + + + +The moving of sequences will be supported by &kpatience; if there is enough room. The +maximum of cards you can move is calculated by: + + +(#{free cells} + 1) * 2#{free piles} + + + +To solve this game it is recommended to grab the cards out of the playing +sequences in the same order they have to be put into the foundation (first the +aces, then the twos, &etc;) + + + +You should try to keep as many free cells and/or playing piles empty, so you can +build as long sequences as possible. + + + + + +Napoleon's Tomb + +Napoleon's Tomb +Napoleon's Tomb is played with one deck. Goal is to put all cards on the +foundations as ascending families without caring about the card colors or suits. + + + +The foundation is made out of five piles, that are put as an X. On the four +corners of this X you have to build sequences starting with seven and ending +with king. + + + +The pile in the middle takes four times the sequence from six down to ace. The +color doesn't matter for all five piles. + + + +The other four piles on the sides of the pile in the middle (or in between the X +- however you want to put it) can take one card each to place cards temporarily. +You can go through the talon only once. + + + +[auto drop] is disabled for this patience. + + + + +Mod3 + + +Mod3 +Mod-3 is played with two card decks. Goal of the game is to put all +cards on the top three rows. In those you have to build sequences of the same +color. In the first row you have to create the sequence 2-5-8-B, in the second +row the sequence 3-6-9-D and in the third row the sequence 4-7-10-K. The suit of +the cards has to be the same in each sequence, so you can put a five of hearts +only on top of a two of hearts. + + + +The fourth row is waste pile and playing pile at once. On an empty slot you can +put any card from the first three rows or one of top of the fourth row. + + + +Aces you can put on the aces piles on top of the talon. They are in the game +so you have a starting point for creating free slots. + + + +If you can't move any more cards, you can get new cards on the fourth row in +clicking on the talon. + + + +[auto drop] is disabled for this patience. + + + + +Calculation + + +Calculation +Calculation is surely a patience you have to get used to. It is played with one +card deck and the goal of the game is to put all cards in the four foundation +piles as ascending families without caring the color of the cards. + + + +For the foundation you have to follow these orders: + + +
+ + + + + +1st pile +A-2-3-4-5-6-7-8-9-10-J-Q-K + + + +2nd pile +2-4-6-8-10-Q-A-3-5-7-9-J-K + + + +3rd pile +3-6-9-Q-2-5-8-J-A-4-7-10-K + + + +4th pile +4-8-Q-3-7-J-2-6-10-A-5-9-K + + + + +
+ + +The card on top of the talon can be put on any of the four playing piles. +You should do this, so you can put following cards easier on the foundation. + +
+ + +Gypsy + +Gypsy +Gypsy is played with two card decks. Goal of the game is to put all cards in +real families ascending on the foundation. + + + +The playing piles have to be descending while red and black cards have to +alternate. You can only move sequences or single cards. On a free slot you can +put any card or sequence. + + + +If you can't move any more cards, you can click on the talon to get a new bunch +of cards on each playing pile. + + + +In using the undo feature you can ease the game quite a lot as you have to take +quite some decisions and some of them might turn out to be wrong after you +clicked the talon. + + + + + +Forty And Eight + + +Forty And Eight +Forty and Eight is played with two card decks. Goal of the game is to put all +cards as real families on the foundation. + + + +The playing piles have to be descending while you have to care about colors. So +you can only put a five of hearts on a six of hearts. + + + +You can only move one single card on top of a pile. In a free slot you can put +any card. + + + +In clicking on the talon you can put a card on the waste pile, from where you +can put it on a playing pile or the foundation (this &kpatience; will do for you). +Is the talon empty you can put all cards on the waste pile back on the talon. +This works only once, after the second time the talon empties, the game is over. + + + +This patience isn't really easy to solve, but with some experience, you can +solve many deals, especially if you use the undo feature from time to time to +correct your decisions, and the decisions &kpatience; does in putting cards on the +foundation. + + + + +Simple Simon + + +Simple Simon +Simple Simon is played with one card deck. Goal of the game is to put all cards +as real families on the foundation. + + + +In the playing piles you can build sequences. In general you don't have to care +about the suits of the cards, but sequences can only be moved if they are part +of a real sequence. (⪚ you can move the six of spades only if on top of it is +the five of spades and not move it if on top of it is the five of clubs). + + + +The cards can only be moved to the foundation if all 13 cards of one family lay +on top of each other in the playing piles. + + + +Suggestion + +You should try as soon as possible to move away the cards on the right piles to +get free piles to place cards temporarily as you can put on those any card. + + + +With enough free room you can build families on free slots independently of the +color. If you have all cards in such families you can sort them after their +color, so they can be moved to the foundation. + + + + + + +Yukon + + +Yukon +Yukon is played with one card deck. Goal of the game is to put all cards as real +families ascending on the foundation. + + + +The sequences on the playing piles have to be descending with alternating red and +black cards. You can move every face up card no matter how many cards are on top +of it. So you can put a five of hearts on a six of spades if this one is on top +of its pile. + + + +In a free slot you can put a king of any color (again, no matter how many cards are on top +of it) + + + + +Grandfather's Clock + + +Grandfathers Clock +Grandfather's clock is a simple patience and after some experiments you should +be able to solve most deals. It is played with one card deck and goal of the +game is put the cards as real ascending sequences on the foundation. + + + +The foundation is on the right side and consists of 12 piles that form the shape +of a clock. The ace is on one o'clock, jack is on 11 o'clock and queen on 12 +o'clock. + + + +There are 8 playing piles beside the clock and on each are 5 cards. On the +playing piles you can build descending sequences without caring on the color of the +cards. You can only move one single card at a time. + + +[auto drop] is disabled for this patience. + + + + +Kings + + + +Kings +The patience Kings is played with two card decks. The cards are dealt in a way +that below each playing pile is a king. The cards between two kings are put on +of them (the first cards are obviously put on the first king). This way you can +get piles with very different lengths. + + + +Goal of the game is to put all cards as real families ascending on the +foundation (on the right side of the playing piles). + + + +The sequences on the playing piles have to be descending while red and black +cards have to alternate. Several cards can only be moved when they are part of +an alternating sequence. + + + +In the 8 free cells on the top of the playing field you can put a single card +temporarily. On a free playing pile you can put any card or sequence. + + + + + +Spider + + +Spider +Spider is played with two card decks. The cards are dealt out into 10 playing +piles, 4 of 6 cards and 6 of 5 cards each. This leaves 50 cards that can be +dealt out 10 at a time, one on each playing pile. + + + +In the playing piles, a card can be placed on another card of any suit and +of one higher value. A sequence of ascending cards of the same suit may be +moved from one playing pile to another. + + + +The goal of spider is to put all cards as real families descending from +Kings anywhere in the playing piles. When such a family is built in a +playing pile, it is removed to the lower-left corner of the window. + + + +The different levels determine how many suits are dealt - Easy uses 1 suit, +Medium uses 2 suits, and Hard uses all 4 suits. The game is fairly easy to +win at Easy, and very difficult to win at Hard. + + + + + +Golf + +Golf +Golf is played with one card deck. The goal of Golf is to move all the cards on the tableau to the foundation. + +The layout of golf solitare is straight forward. At the beginning of the game you will see, the tableau, on it are seven columns each containing five cards. Below that is the talon and the foundation. + +Playing golf solitare is simple, but does require strategy to win. The cards at the base of each column on the tableau are available for play. Available cards are built upon the top foundation card in ascending or descending sequence regardless of suit. If there are no moves available a card may be dealt from the talon to the foundation. A game is over when all the cards in the talon have been dealt and there are no more possible moves. + + + +
+ + +Credits and License + + +&kpatience; Copyright 1995-2000 Paul Olav Tvete + + + +&kpatience; Copyright 2001 Stephan Kulow +coolo@kde.org + + + +Freecell Solver by Shlomi Fish +shlomif@vipe.technion.ac.il + + + +Documentation Copyright 2000 Paul Olav Tvete + + + +Documentation updated for KDE 2.0 by Michael McBride +mpmcbride7@yahoo.com + + + +Documentation rewritten for &kpatience; 2.0 (KDE 2.1) by Maren Pakura +maren@kde.org + + + + +&underFDL; +&underX11License; + + + +&documentation.index; + +
+ + + diff --git a/doc/kpat/man-kpat.6.docbook b/doc/kpat/man-kpat.6.docbook new file mode 100644 index 00000000..7ea3ce27 --- /dev/null +++ b/doc/kpat/man-kpat.6.docbook @@ -0,0 +1,340 @@ + + + +]> + + + +KPAT +6 +January 24, 2002 +kde-games +KDE User's Manual + + + +&kappname; +A highly addictive &kde; card game. + + + + +kpat +Qt-option +KDE-option + + + + +DESCRIPTION + +&kpat; is a compendium of several well known patience card games, +ranging from the well known Klondike and Freecell, to lesser known +games such as Grandfather's Clock and Mod3. In all there are 13 +variations for you to while away time. + + + +OPTIONS + +This program follows the usual GNU command line syntax, +with long options starting with two dashes (`--'). A +summary of the options supported by kpat +is included below. + + + +Generic Options + + + + + +Show author information + + + + +--help + +Show help about options + + + + + + +Show Qt specific options + + + + + + +Show &kde; specific options + + + + + + +Show all options + + + + + + +Show license information + + + + +, + +Show version information and exit. + + + + + + + + +Qt Options + + + + + +Use the X-server display displayname. + + + + + + + +Restore the application for the given sessionId. + + + + + + + + +Causes the application to install a private +color map on an 8-bit display. + + + + + + + + +Limits the number of colors allocated in the +color cube on a 8-bit display, if the application +is using the QApplication::ManyColor +color specification. + + + + + + +tells Qt to never grab the mouse or the keyboard. + + + + + + +running under a debugger can cause an implicit +, use +to override. + + + + + + +switches to synchronous mode for debugging. + + + +, + +defines the application font. + + + +, + + +sets the default background color and an +application palette (light and dark shades +are calculated). + + + + +, + +sets the default foreground color. + + + +, + +sets the default button color. + + + + + +sets the application name. + + + + + +sets the application title (caption). + + + + + + +forces the application to use a TrueColor +visual on an 8-bit display. + + + + + + + +sets XIM (X Input Method) input style. Possible +values are onthespot, overthespot, offthespot +and root. + + + + + + +set XIM server. + + + + + +disable XIM. + + + + + + +KDE Options + + + + +Use caption as name in the titlebar. + + + + + + +Use icon as the application icon. + + + + + + +Use icon as the icon in the titlebar. + + + + + + + +Use the DCOP Server specified by server. + + + + + + + +Disable crash handler, to get core dumps. + + + + + + + +Waits for a WM_NET compatible windowmanager. + + + + + + + +sets the application &GUI; style. + + + + + + + +sets the client geometry of the main widget. + + + + + + + + + + + +SEE ALSO + +&kpat; is documented in detail in The &kpat; +Handbook +($KDEDIRshare/doc/HTML/en/kpat/index.html +or enter help://kpat/index.html in +&konqueror;). + +&kde-http; + + + +AUTHOR + +&kpat; is by: + +Paul Olav Tvete +Mario Weilguni mweilguni@kde.org +Matthias Ettrich ettrich@kde.org +Rodolfo Borges barrett@labma.ufrj.br +Peter H. Ruegg kpat@incense.org +Michael Koch koch@kde.org +Marcus Meissner mm@caldera.de +Shlomi Fish shlomif@vipe.technion.ac.il +Stephan Kulow coolo@kde.org + + + diff --git a/doc/kpat/playfield.png b/doc/kpat/playfield.png new file mode 100644 index 0000000000000000000000000000000000000000..ad16caab91da5173d7107cd99b752db106689ea1 GIT binary patch literal 117084 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

&Jochen.Tuchbreiter.mail;
+ + + +Andreas +Beckermann + +
&Andreas.Beckermann.mail;
+
+
+ + + + +19972001 +&Jochen.Tuchbreiter;, &Andreas.Beckermann; + + +&FDLNotice; + +2005-12-14 +1.1 + + +This is the helpfile of &kpoker;, a little poker game. + + + + +KDE +kpoker +kdegames +games +game +poker +play cards + + + + +Introduction + + +What is &kpoker;? + +&kpoker; is a &kde; compliant clone +of those highly addictive pocket video poker games which are sometimes called +Videopoker as well. + + + +Where do I get the latest version of &kpoker;? + +The latest version of &kpoker; is always available at + + +The &kpoker; homepage: http://kpoker.sourceforge.net/ + + +The &kde; homepage: &kde-http; + + + + + + + + +Starting a new game + +You can start a new game two ways: + + + +By selecting Game +New from the +menubar. + +Selecting &Ctrl;N + + +Either way, you will be presented with a dialog box. + + +AI Configure Dialog + + +AI Configure Dialog + + + + + +How many players do you want? +Will determine if you play a one +player game (against the house), or a two +player game. + + + +Your name: +Lets you personalize &kpoker; so that it calls you by name +(instead of Youyou>). + + + +Player's starting money: +Lets you set the money you start each game +with. + + + +The names of your opponents: + lets you set the name of the other player. + + + +Show this dialog every time on +startup +If this is checked, the first game after you start +&kpoker; will show this menu. If this option is not checked, then the +game will not show this menu for the first game. + + + +When you are satisfied with your settings, click +OK, to start the game. Clicking +Cancel, will cancel the start of a new +game. + + + + +One player game + + +How do I play ? + +At the beginning of the game you get $100. You draw five cards +and decide which ones you want to keep. You indicate which cards you +want to keep by clicking on the face of the card. The word +Held will appear above the card. + +After doing this you draw new cards for those you did not want +to keep. This is accomplished by clicking on +Draw New Cards. + + The game now looks if you have any poker +hand and gives cash according to the kind of hand you got (see +scoring). + +Now you start over, drawing five cards, deciding which ones you want to +keep ... + +The game ends if you can not pay for another round or if you close the +&kpoker; window. + + + + +What are possible poker hands ? + +Possible poker hands are: + + + +Jacks or higher +Two cards of the same rank, both Jacks or +higher + + + +Two pairs +Two cards of one rank and two cards of another +rank + + + +Three of a kind +Three cards of the same rank + + + +Four of a kind +Four cards of the same rank + + + +Full House +Three cards of one rank and two cards of +another + + + +Straight +Five cards of consecutive rank including the combo +ace-2-3-4-5 + + + +Flush +Five cards of the same suit + + + +Straight flush +Five cards of the same suit and of consecutive +rank + + + +Royal flush +Ace, king, queen, jack, and ten of the same +suit + + + + + + + +How many bucks do I get for what hand ? + + + + + + +A pair of Jacks or higher cards$5 + Two Pairs $10 + Three of a kind $15 + Straight $20 + Flush $25 + Full House $40 + Four of a kind $125 + Straight Flush $250 + Royal Flush$2000 + + + + + + + + +Two player game + +The two player game is very different from a one player game. + + +Starting a two player game + +To start a two player game, select Game +New on the menubar. This will +bring up a dialog. Simply select the number of players on the slider or +in the spinbox labeled How many players do you want?. +Then click OK. + + + +Playing a two player game + +There are four phases to a two-player game: + + +you begin to draw cards +then you bet some money +after that you exchange your cards +then you raise and finally you will see the cards of the other +player and the winner will get all the +money. + + + +Draw +You will begin the game with $100. After clicking on the +Draw New Cards +button you will get five cards. That's all, here. + + + +Bet +Now you decide if your cards are good or +not. If they are, you bet some money - at least $5 and maximal $20. Just click +on the +$5 and similar buttons. + + +Exchange +Now you decide which cards you want to keep. Click on +them. If you have a royal flush then don't exchange any cards! When +you think you are ready then click again on the Draw New Cards +button - you will probably get new cards. + +Perhaps you do not get new cards - then the computer player has +quite good cards (or at least not very bad cards) and he has +raised. You have to decide to adjust your bet or to get out of the +round. By default you will adjust your bet. Click again on the +Draw New Cards button when ready. + + + +Raise + + After you got new cards you are allowed to raise a +little bit. It is the same as the bet phase so I don't explain it +again. Click on the draw button (which is now labeled with +See!) when ready. + + + +See / Draw +I know I said there are four phases and this one is the +fifth. But it is nearly the same as the draw phase. You will also see the cards +of the computer player and the winning cards are blinking. The winner gets all +money. Click on draw to begin a new round! + +The blinking cards are not necessarily the best cards of +that player. Only the cards which caused the win are blinking. So if +you have two pairs (⪚ 2 * 2 and 2 * 3) and an ace, but only the ace +is blinking, then the computer player also has these two pairs (2 * 2 +and 2 * 3) but not an ace. + + + + + + + + +Commands and Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +<guimenu>Game</guimenu> Menu + +The Game menu consists of three options. + + + + + +&Ctrl;N + +Game +New +Starts a new game of &kpoker;. For more +information, see the section entitled Starting a new +game. + + + + +&Ctrl;S + +Game +Save +Saves your current game to disk. This will +replace any previously saved games. + + + + +&Ctrl;Q + +Game +Quit +Quits &kpoker; + + + + + +<guimenu>Settings</guimenu> Menu + +The settings menu is used to adjust the sound, look and behavior of &kpoker;. + + + + + + +&Ctrl;M + +SettingsShow Menubar +This toggles the menubar on or off. If it is off, and you need to use the menubar, you can right click in the playing area of &kpoker; and a menu will appear. You can then select Show Menubar to turn it back on. + + + + +SettingsShow Statusbar +This option will toggle the status bar on or +off. The status bar is located at the bottom of the &kpoker; window, and contains instructions for play, and how much you won on the previous hand. + + + + +SettingsSound +This option will toggle the sound on or +off. + + + +Settings +Blinking Cards +If this option has a check beside it, then when you win a hand, +the cards which won you the money will blink. If this option +is not checked, no cards blink. + + + +SettingsAdjust Bet is +Default. +If this option does not have a check in front of it, and you are +playing a 2 person game, you will be responsible for clicking Adjust +Bet, if your opponent raises the stakes, because the default action +will be to fold your hand in defeat. +If, on the other hand, the option does have a check in front of it, and +you are in the same situation, the default action of the game, is to match your +opponents bet, and you will be responsible for folding your +hand. + + + +SettingsSave +Settings +This saves all your options to your hard drive. These options +will be restored automatically when you restart &kpoker;. + + + + +Settings +Configure Shortcuts... + + +Customize the keyboard shortcuts. + + + + + +SettingsConfigure Carddecks... +This will open a new window where you can select the front and back of the cards for &kpoker;. + + + + +SettingsConfigure KPoker... +This opens a dialog box. + +AI Configure Dialog + + + + +There are either one or three options to adjust: + +The top text box determines in milliseconds how long to delay +before showing the next card. This can be used to speed up the deals, +if you are not interested in the more realistic default speed. + +The text box labeled Maximal bet:, +determines the maximum bet for each hand. + +The text box labeled Minimal bet: determines +the smallest allowable bet for each hand. + +The Maximal bet: and Minimal +bet: lines are not shown in one player mode. In single +player mode, only the card delay can be adjusted. + +As you can see, the changes will not go into effect until a new +round is started. + + + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Default Keyboard Shortcuts + + + +Key ComboAction + +&Enter;Draw +1Exchange Card 1 +2Exchange Card 2 +3Exchange Card 3 +4Exchange Card 4 +5Exchange Card 5 +&Ctrl;QQuit &kpoker; +&Ctrl;NNew Game +&Ctrl;SSave Game +&Ctrl;MShow Menubar +&Ctrl;F1What's This Help +F1Help Contents + + + + + +Credits and License + +&kpoker; + + +Program copyright 1997-2000 &Jochen.Tuchbreiter;&Jochen.Tuchbreiter.mail;, &Andreas.Beckermann; &Andreas.Beckermann.mail; +Persons helping me: + +Chris Holmes - idea of writing this game and for parts of the +visual appearance +John Fitzgibbon - provided the card images +Nico Schirwing - drew the backs of the cards +&Andreas.Beckermann; - currently maintaining the game + + +Documentation updated for &kde; 2.0 by &Mike.McBride; +&Mike.McBride.mail; + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kpoker; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + diff --git a/doc/kpoker/kpoker1.png b/doc/kpoker/kpoker1.png new file mode 100644 index 0000000000000000000000000000000000000000..bd409a6e0a566990481b2798dc5364adeb2d889f GIT binary patch literal 15904 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 z + + + + +]> + + + +The &kreversi; Handbook + + + + +Clay +Pradarits + +
&Clay.Pradarits.mail;
+
+
+ + +Mario +Weilguni + +
&Mario.Weilguni.mail;
+Developer +
+ + +Lauri +Watts +Reviewer +
+&Lauri.Watts.mail; +
+
+ + + +
+ +2005-12-14 +1.7 + +&kreversi; is a simple one player strategy game. + + + +KDE +kdegames +game +KReversi + +
+ + +Introduction + + +What is &kreversi;? + +&kreversi; is a simple one player strategy game played against the +computer. The playing field is an 8 by 8 square board divided into 64 +squares. The game piece used is a colored stone - one side of it is red, the +other blue. If a piece is captured by an opposing player, that piece is turned +to reveal the color of that player. A winner is declared when one player has +more pieces of his own color on the board and if there are no more possible +moves. + + + + +Object of the Game + +The object of the game is to control the majority of squares on the +board. + + + + + + + +Playing + + +Basics + +A move consists of outflanking your opponents disc(s), then flipping the +outflanked disc(s) to reveal your color. A move is performed by placing the +mouse pointer over the desired square then &LMB; click. + +To outflank means to place a disc on the board so that your opponent's +row(s) of disc(s) is bordered at each end by a disc of your color. A row may be +made up of one or more discs. + +The game starts with each player having two discs automatically placed +in the center four squares of the board in the following pattern: + + +&kreversi; opening position + + +&kreversi; opening position +Board Layout + + + + + +Menu Bar + +The Menu Bar contains several choices of +drop-down menus. These are Game, Move, View, Settings, and Help. Click with the &LMB; or +&Alt;the underlined +letter to show the drop-down menu. + + + + + + +Toolbar +Provides the player with icon shortcuts for commonly +used actions. These are New, +Stop Thinking, Continue Thinking, +Undo, Show Menubar, Hint, +Show Last Move, and Show Legal Moves. +Place the mouse pointer over any of these icons and click with the &LMB; to +activate. + + + + + +Game Board +The game board consists of a 8 by 8 square board +divided into 64 squares. + + + + + +Status Bar +The status bar displays whose turn it is. + + + +The field on the right side of the game board contains useful information such as +which color represents each player, the number of +squares each player controls on the board and displays all moves in the current game. + + + +Rules + +Blue always moves first. + +If on your turn you cannot outflank and flip at least one +opposing disc, your turn is forfeited and your opponent moves +again. However, if a move is available to you, you may not forfeit +your turn. + +A disc may outflank any number of discs in one or more rows in +any number of directions at the same time - horizontally, vertically +or diagonally. A row is defined as one or more discs in a continuous +straight line. + +You may not skip over your own color disc to outflank an +opposing disc. + +Discs may only be outflanked as a direct result of a move and +must fall in the direct line of the disc placed down. + +All discs outflanked in any one move must be flipped, even if it +is to the player's advantage not to flip them. + +Once a disc is placed on a square, it can never be moved to +another square later in the game. + +When it is no longer possible for either player to move, the +game is over. Discs are then counted and the player with the majority +of his or her color discs on the board is declared the winner. + +It is possible for a game to end before all 64 squares are +filled. + + + + +Tips + +Try to place pieces on the edges whenever possible. Your +opponent cannot outflank these pieces. + +Avoid placing pieces on one of the three neighbor fields of a +corner unless you are absolutely sure that your opponent will not be +able to put a piece on a corner. + +Sometimes it is better to offer some pieces to your +opponent + +Try to put pieces on fields which prevent your opponent from +moving. + +Try to force your opponent to put a piece in a neighboring field +of a corner. + + + + + + +User Interface + + +<guimenu>Game</guimenu> Menu + + + +&Ctrl;N +GameNew +Starts a new game. + + + + +&Ctrl;O +GameLoad... +Loads a saved game. + + + + +&Ctrl;S +GameSave +Saves the current game. + + + + +&Ctrl;H +GameShow Highscores +Opens a dialog that displays different high score tables. +Export the high scores into a file or click on +Configure to open a dialog to customize your +Nickname and add a Comment. + + + +&Ctrl;Q +FileQuit +Quits &kreversi;. + + + + + +<guimenu>Move</guimenu> Menu + + + +&Ctrl;Z +MoveUndo +Removes your last move as well as the computer's last +move from the board. + + + + +HMoveHint + +The computer will provide a hint for your next +move. + + + +MoveSwitch +Sides +Will make you the opposite color. + + + +Escape +MoveStop +Thinking +Stops the computer's depth search, then you choose the +computer's next move, or select the Continue Thinking +menu item to continue the computer's thinking process. + + + + +Move +Continue Thinking +Continue the computer's thinking process if it was +previously stopped using the Stop Thinking menu +item. + + + + + + +<guimenu>View</guimenu> Menu + + + +&Ctrl;+ +ViewZoom In +Enlarges the game board. + + + +&Ctrl;- +ViewZoom Out +Shrinks the game board. + + + + + +<guimenu>Settings</guimenu> Menu + + + +&Ctrl;MSettings +Hide Menubar +Toggles whether the menubar is visible. + + + + +Settings +Toolbars +Main Toolbar (&kreversi;) + +Toggle the Main Toolbar + + + + +Settings +Toolbars +View Toolbar (&kreversi;) + +Toggle the View Toolbar + + + +Settings +Configure Shortcuts... +Opens a dialog which lets you configure all the keyboard shortcuts which +are available in &kreversi;. + + + + +Settings +Configure Toolbars... + + +Open a dialog where you can configure the toolbars for &kreversi; + + + + +Settings +Configure &kreversi;... +Opens the configuration +dialog which lets you tweak a lot of &kreversi;'s +options. + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Configuration + +Selecting the Settings +Configure &kreversi;... menu item will +open a configuration dialog which lets you adjust many of &kreversi;'s +options. + + +&kreversi; configuration dialog + + +&kreversi; configuration dialog + + + + + +Grayscale chips +Check this box to use grayscale chips instead of blue and +red ones; this might improve the look on very low color +displays. + + + +Play Game +Check a radiobutton whether to play Casually +or Competitively. + + + +Animation +If this option is checked, a short animation will be shown +when a disc changes it's color. + + + +Animation Speed +If the Animation option is enabled, you +can use this slider to define how fast the animation should be played; move the +slider to the left for a slower animation, moving the slider to the right will +play the animation faster. + + + +Computer Skill +This slider lets you define the skill level of the computer +player. There are seven different skill levels available, ranging from beginner +to expert. Move the slider to the left for an easier opponent, or to the right +to get a more difficult opponent. + + + +Background +Here you can define the appearance of the playground. Select +the Color: option and press the colored button at the right +if you want the background to have a single color. Select the +Image: option and enter the path to a picture in the input +field at the right in case you want to use a specific image as the background +wallpaper. + + + +Help +Opens &kreversi; help. + + +Defaults +Reverts settings to defaults. + + +OK +Saves changes and closes dialog. + + +Apply +Saves changes but doesn't close dialog. + + +Cancel +Cancels all your changes and closes the dialog. + + + + + +Credits and License + +Program Copyright 1998-2000 &Mario.Weilguni; +Mats Luthman - Designer of the move engine. + +Original documentation by &Mario.Weilguni; +Edited by Robert Williams + +Documentation re-written and updated for &kde; 2.0 by &Clay.Pradarits; +&Clay.Pradarits.mail; + + + +&underFDL; +&underGPL; + + + +Installation + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + + + + +&documentation.index; +
+ + + + + + + + + + diff --git a/doc/kreversi/kreversi-configuration.png b/doc/kreversi/kreversi-configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..9c8df2d0562de33b53f9b8b8b94a518b0d552807 GIT binary patch literal 30239 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 literal 0 HcmV?d00001 diff --git a/doc/kreversi/kreversi1.png b/doc/kreversi/kreversi1.png new file mode 100644 index 0000000000000000000000000000000000000000..fe32be359d32120a139437a3188870b4440df55e GIT binary patch literal 68912 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¬k)YK^DX&Hkdb$r~sg&)5Q|}2T(&{^b?n-oBH?_ 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 literal 0 HcmV?d00001 diff --git a/doc/ksame/Makefile.am b/doc/ksame/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksame/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksame/index.docbook b/doc/ksame/index.docbook new file mode 100644 index 00000000..4e9cb5e9 --- /dev/null +++ b/doc/ksame/index.docbook @@ -0,0 +1,295 @@ + + + + + +]> + + + + +The &ksame; Handbook + + + +Nicholas +Robbins + +
&Nicholas.Robbins.mail;
+
+
+ +
+ + +2000 +Nicholas Robbins + +&FDLNotice; + +2005-12-17 +0.5.1 + +&ksame; is a nice little program meant to amuse you for +anywhere from 3 minutes to 6 days (straight). + + +KDE +kdegames +ksame + + +
+ + +Introduction + +&ksame; is a simple game. It is played by one player, so there is only one +winner :-) You play for fun and against the high score. It has been inspired by +SameGame, which is only really famous on the Macintosh. + +In 1997 &Stephan.Kulow; and Marcus Kreutzberger wrote Probiere (a German pun). It was their first attempt to +write a X11 application. They decided that it would be a good idea to port it to +&kde;, and here we are. + + + + + +Using &ksame; + +A very simple premise. There are a bunch of marbles. Get rid of them +all. ;-) + +You can erase same marbles when they are connected vertically or +horizontally when you click them. If there are pieces over the erased ones, +they will drop down. If all of the pieces on the vertical line are erased, all +pieces on the right side will slide to the left. + +The score is calculated as follows: +Subtract 2 from the number of marbles erased, and square the result. +As an example, if you erase 7 balls, then you get 25 points (7 minus 2 = 5. 5 squared is 25 points).If you erase 8 +balls, then you get 36 points (8 minus 2 = 6. 6 squared is 36 points. + +Get as many of the +same pieces as you can, and then erase them in one click. That way you will get +a higher score. The game is over when there are no pieces that can be +erased. + +The score will then be decreased according to the number of remaining +pieces. If you erase all pieces 1,000 bonus points will be added to the final +score. + + + +More &ksame; Features + +&ksame; is wonderful in that it will allow you to kill 5 minutes while you +wait for that program to compile. + + Or, if nothing else, it will allow you to kill 5 minutes. + + + + + +Command Reference + + +The Main &ksame; Window + +There is a large area with lots of marbles. Underneath is the status +bar. Above is the menu bar. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. If Settings +Random Board is enabled, a random game is started. +Otherwise you are presented a dialog in which you can choose a specific board. + + + + + + +&Ctrl;R + +Game +Restart This Board + +Restarts the current board. + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high score table. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksame;. + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + +&Ctrl;Z + +Edit +Undo + +This will undo the last move. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Random Board + +Enabling this will make the Game +New start with a random board instead of letting +you choose a specific board. + + + + +Settings +Show Number Remaining + +Displays the remaining numbers of marbles for each color in the status bar. + + + + +Settings +Configure Shortcuts... + + +Configure the keyboard keys you use to access the +different actions. + + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &ksame;. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + + + +Credits and License + +&ksame; + +Program copyright 1997 Marcus Kreutzberger +kreutzbe@informatik.mu-luebeck.de +Contributors: + +&Stephan.Kulow; &Stephan.Kulow.mail; + + + +Documentation copyright 2000 Nicholas R. Robbins +logik9000@home.com + + +&underFDL; + + +&underGPL; + + + + +Installation + + +How to obtain &ksame; + +&install.intro.documentation; + + + + +Requirements + +In order to successfully use &ksame;, you need &kde; 3.0. Or at least the +kdelibs package. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + +&documentation.index; +
+ + diff --git a/doc/kshisen/Makefile.am b/doc/kshisen/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kshisen/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kshisen/index.docbook b/doc/kshisen/index.docbook new file mode 100644 index 00000000..483c4eb7 --- /dev/null +++ b/doc/kshisen/index.docbook @@ -0,0 +1,423 @@ + + + + + +]> + + + +The &kshisen; Handbook + + + +Dirk +Doerflinger + +
&Dirk.Doerflinger.mail;
+
+
+ +FrerichRaabe +Reviewer +
&Frerich.Raabe.mail;
+
+ +
+ + +2006-06-17 +1.5.1 + + +&kshisen; is a game similar to Mahjongg for &kde;. + + + + KDE + kdegames + kshisen + game + shisen-sho + + +
+ + + +Introduction + +&kshisen; is a single-player-game similar to Mahjongg and uses the same +set of tiles as Mahjongg. + +The object of the game is to remove all tiles from the field. + + + + +Playing &kshisen; + + +Rules + +The aim of the game is to remove all tiles from the board. Only two +matching tiles can be removed at a time. Two tiles can only be removed if they +can be connected with a maximum of three connected lines. Lines can be +horizontal or vertical, but not diagonal. + +You don't have to draw the lines by yourself, the game does this for +you. Just mark two matching tiles on the board, if they can be connected with a +maximum of three lines, the lines will be drawn and the tiles are +removed. + +Remember that lines only may cross the empty border. If you are stuck, you +can use the Hint feature to find two tiles which may be removed. Clicking a +tile with the &RMB; will show you all corresponding tiles, no matter if they are +removable at the moment or not. + +The game is over when no moves are possible. This could be due to +clearing all the tiles from the field or reaching a point in the game +where no two matching tiles can be connected by three lines. +Some games are unsolvable, if you would like to avoid unsolvable +games uncheck the option Allow unsolvable +games in the configuration dialog. + +The resulting score S is calculated using the +following formula, assuming that n represents the +number of tiles present when the game started and t +standing for the time (in seconds) it took to clear the +field: + + + + +S=(n/t)*sqrt(n/84)/0.0014 + + +If you played with the Gravity setting enabled, this score +will additionally get multiplied by two. + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + +The Game menu lets you control the status of the +current game: + + + + +&Ctrl; +N +GameNew +Finish the actual game set and start a new +session with new tiles. + + + + +F5 +GameRestart +Game +Restart the current game with the same +tiles. + + + + +P +Game +Pause +Pauses the entire game, especially the timer +which affects the scoring. The menu entry is also used for resuming the +game. + + + + +&Ctrl;H + +GameShow +Highscores +Shows the (local) Top-Ten charts of &kshisen;. + + + + +&Ctrl; +Q +GameQuit +Quits &kshisen;. + + + + +Some of the menu items can also be controlled by keykoard shortcuts. +See for a list. + + + + +The <guimenu>Move</guimenu> Menu + + + + &Ctrl;Z +Move +Undo +Undo the last step. Same as the +Undo button in the toolbar. + + + +&Ctrl;&Shift;Z +Move +Redo +Redo the last step. Same as the +Redo button in the toolbar. + + + + +H +Move +Hint +Show a tip, which two tiles to remove +next. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Toolbar + + + +Toggle on and off the display of the toolbar. + + + + + + +Settings +Show Statusbar + + + +Toggle on and off the display of the status bar. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + +Displays a &kde; standard dialog where you can configure the toolbar icons. + + + + +Settings +Configure &kshisen;... + + +Opens the configuration +dialog to change &kshisen; settings. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Shortcuts + +Default shortcuts are: + + + + + +New&Ctrl;N +Restart GameF5 +PauseP +Show Highscores&Ctrl;H +Quit&Ctrl;Q +Undo&Ctrl;Z +Redo&Ctrl;&Shift;Z +HintH +&kshisen; HandbookF1 +What's This&Ctrl;&Shift;F1 + + + + + + + + +Configuration + +Selecting the Settings +Configure &kshisen;... menu item will +open a configuration dialog which lets you alter the behavior of the game. + + +&kshisen; configuration dialog + + +&kshisen; configuration dialog + + + + + +Gravity +Checking this makes the game even harder: If a tile is +removed, all tiles lying above it will fall down one step. + + + +Allow unsolvable games +If checked, only solvable games will be +created when a new game is started. Note: You may still choose the wrong way and +have to try again if you didn't solve the game, but if it's checked, the game +will be solvable. + + + +Board Difficulty +The slider controls the difficulty of the board. There are three +options Easy, Medium and +Hard. + + + +Piece Removal Speed +Adjusting this slider alters the speed at which the pieces are +removed from the screen after a match has been made. + + + +Prefer Unscaled Tiles +Resizing the window causes the tiles on the board to be scaled to +match the window size. When this option is checked, the window is resized to fit +the tiles' natural size. You can still adjust the size of the window. Doing so +causes this mode to be disengaged. + + + +Tile Size +This slider allows you to can change the number of tiles +on the board. The more tiles you have, the harder (and longer) +the game will be. + + + +Help +Opens &kshisen; help pages. (this document). + + +Defaults +Reverts all settings to defaults. + + +OK +Saves your changes and closes the dialog. + + +Apply +Saves your changes but doesn't close the dialog. + + +Cancel +Cancels all your changes and closes the dialog. + + + + + +Credits and License + + +&kshisen; Copyright 1999 &Mario.Weilguni; +&Mario.Weilguni.mail; + + + + +Documentation Copyright 2000 &Dirk.Doerflinger; +ddoerflinger@gmx.net + + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kshisen; + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &kshisen;, you need &kde; 3.x. +All required libraries as well as &kshisen; itself can be found on +&kde-ftp;. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ diff --git a/doc/kshisen/kshisen-configuration.png b/doc/kshisen/kshisen-configuration.png new file mode 100644 index 0000000000000000000000000000000000000000..cbcfb1098ace8a6c9f71283aaaedc9ed8e0f1a7f GIT binary patch literal 25016 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 S6b + + + + +]> + + + +The &ksirtet; Handbook + + +&Nicolas.Hadacek; + + + + + + +19992000 +&Nicolas.Hadacek; + + +&FDLNotice; + +2005-12-17 +2.1.11b + + +&ksirtet; is a clone of the well known game Tetris. You must fit the +falling pieces to form full lines. + + + +KDE +game +tetris + + + + +Introduction + + +&ksirtet; is a clone of the well known game Tetris. You +must fit the falling pieces to form full lines. You can rotate and translate +the falling piece. The game ends when no more pieces can fall, &ie; when your +incomplete lines reach the top of the board. + + + +Every time you have destroyed 10 lines, you advance to the next level and the +pieces fall quicker (to be precise, the pieces fall from the top of the screen each +1/(1+level) second). + + + + +How to Play + + +Handling the Falling Piece + + +You can use the keyboard to rotate, translate or drop down the falling piece. The +keys are configurable via the menu item Settings +Configure Shortcuts.... + + + + + +Game Types +&ksirtet; supports two types of single player game: Normal and Arcade. In +Normal mode, points totals are kept, and high scores recorded. Also, when advancing +to the next level, play continues without clearing the gameboard. In Arcade +mode, no points totals are kept, although an Elapsed time +counter is shown. When you advance to the next level in Arcade mode, the +gameboard is cleared of pieces. +You can choose the game mode in the Mode menu. + + + +How to gain Points + +There are two sources of points: + + + + +Dropping down a piece using the Down Arrow gives a small +number of points which is equal to the drop height. It is not much each time +but is particularly relevant in the earlier levels, when the pieces fall more +slowly. + + + + + +The biggest part of the score comes from destroying full lines. It is very +important to note that destroying multiple lines at the same time gives you a +lot more points. A four-lines destruction is called a tetris: +it gives a maximum amount of points. + + + +The points gained are 40 for a one-liner, 100 for two lines, 300 for three lines +and 1200 for a tetris. These numbers are multiplied by the current +level. For example, destroying four lines in level 10 gives 12,000 points. + + + + + + + +Configuring &ksirtet; + + +The &ksirtet; configuration is accessed from the menu item +Settings +Configure &ksirtet;.... The +options are as follows: + + + +Game Configuration + + + + +Initial level: +Set the level which you will play at startup, from 1 (easiest) +to 20 (hardest). Default is level one. + + + +Direct drop down +If checked, pressing the Down Arrow will cause +a tile to immediately fall to the bottom of the screen. If unchecked, pressing +the Down Arrow only causes the piece to fall until the key is released. + + + +Old rotation style +&ksirtet; has two ways of rotating each piece: +Old style and New style. Each style uses a +different point to rotate the piece around. Try both, and see which one you prefer. + + + + + + + + +Appearance Configuration + + + + +Enable animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + +Show piece's shadow +If checked, a shadow is placed beneath the game board +showing where the piece will fall. + + + +Show next piece +If checked, shows the next tile that will fall onto the +game board. + + +Show detailed "removed lines" +field If checked, the Removed +Lines counter on the left of the screen shows how many times each +number of lines have been removed. If unchecked, just the total number removed +is shown. + + + + +Background +Select the color and the opacity for &ksirtet; background. +An opacity of zero makes the &ksirtet; background +completely transparent, and a setting of one makes the &ksirtet; background +completely opaque. + + + + + + + +Colors Configuration +Here you can select the colors used for the tiles in &ksirtet;. + + + +A.I. Configuration +Here you can configure the A.I. in &ksirtet;. + + + + + +Configuring Highscores + +The configuration is accessed from +Settings +Configure Highscores.... The +options are as follows: + + + +The <guilabel>Main</guilabel> Tab + + + + +Nickname: +Displays your current nickname and allows you to change it. + + + + +Comment +A comment about yourself. You choose... + + + + +World-wide highscores enabled +If checked and if you are connected to the Internet, +&ksirtet; will send your score automatically at the end of the game +to the highscore web server (ksirtet.sf.net). + + + + + + +The <guilabel>Advanced</guilabel> Tab + +This tab displays your Registration Data +on ksirtet.sf.net: + + + +Nickname: +Displays your current nickname from the Main tab. + + + + +Key: +This key was generated when you registered on ksirtet.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + + + + + + + + + +Multiplayer + + +The multiplayer option allows you to play with others players on the same computer. +You can also play against an AI (artificial intelligence) player which is currently not +very good. + + + +Basically when destroying lines, a player sends garbage at the +bottom of the game of his next opponent: nothing for one line destroyed, one +garbage line for two lines destroyed and so on. + + + + + +Command Reference + + +The Main &ksirtet; Window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Opens a dialog that displays different high score tables. +Clicking on the links below the tables downloads world-wide high scores. +Export the high scores into a file or click on +Configure to open a dialog to customize your Nickname and add a Comment. + + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksirtet; + + + + + + + +The <guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ +View +Zoom In + +Enlarges the game board + + + + + +&Ctrl;- +View +Zoom Out + +Reduces the game board size + + + + + + + + +The <guimenu>Mode</guimenu> Menu + + + + + +Mode +Single Human (Normal) + +Sets the multiplayer mode to single +player normal. See . + + + + +Mode +Single Human (Arcade) + +Sets the multiplayer mode to single +player arcade. See . + + + + + +Mode +Human vs Human + +Sets the multiplayer mode to two player with two human +players. + + + + +Mode +Human vs Computer + +Sets the multiplayer mode to two player with one human +and one computer player. + + + + +Mode +More... + +Displays the multiplayer +options dialog. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + +Settings +Show Menubar + +Shows or hides the Menubar.To return the +menubar, right-click anywhere on the gameboard and select Show +Menubar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; key bindings +configuration dialog to change the keyboard shortcuts for &ksirtet;. + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &ksirtet;. + + + + +Settings +Configure Highscores... + +Displays the high score configuration dialog, +in which you can change several settings that affect how &ksirtet; treats highscores. + + + + +Settings +Configure &ksirtet;... + +Opens the &ksirtet; +configuration dialog, where you can change the options of the game. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + +Credits and Licenses + + +Developers + +Erik Eng - Wrote the generic tetris code +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; + + + +Authors +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; +Robert Williams rwilliams@kde.org - Editor +Documentation updated for &kde; 3.2 by &Philip.Rodrigues;. + + + +&underFDL; +&underGPL; + + + + + + + + + + diff --git a/doc/ksmiletris/Makefile.am b/doc/ksmiletris/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksmiletris/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksmiletris/gamescreen.png b/doc/ksmiletris/gamescreen.png new file mode 100644 index 0000000000000000000000000000000000000000..9cb50cf6b93a8815e241e942101180bbe7058fe1 GIT binary patch literal 64345 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%0DUh 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⪻K|>UV(TD@~kcu4Hy5qDgt1t*>I)0?%|3 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
&Anders.Widell.mail;
+ + + + + + +2000 +&Anders.Widell; + + +&FDLNotice; + +2006-06-19 +0.4.2 + + + +&ksokoban; is a &kde; implementation of the Japanese warehouse keeper +game sokoban. + + + + +KDE +kdegames +game +sokoban +ksokoban + + + + +Introduction + + +The first sokoban game was created in 1982 by Hiroyuki Imabayashi at +the Japanese company Thinking Rabbit, Inc. Sokoban is +japanese for warehouse keeper. The idea is that you +are a warehouse keeper trying to push crates to their proper locations +in a warehouse. + + + +The problem is that you cannot pull the crates or step over them. If +you are not careful, some of the crates can get stuck in wrong places +and/or block your way. + + + +It can be rather difficult just to solve a level. But if you want to +make it even harder, you can try to minimize the number of moves +and/or pushes you use to solve the level. + + + +To make the game more fun for small kids (below 10 years or so), some +collections with easier levels are also included in &ksokoban;. These +are marked (easy) in the level collection menu. +Of course, these levels can be fun for adults too, for example if you +don't want to expose yourself to too much mental strain. + + + + +The Game + + +The objective of the game is to push all the red gems to the goal +squares, which are marked with green glassy round things on the floor. + + + +Use the cursor keys or the mouse to move about. If you move onto a gem +using the cursor keys or the middle mouse +button, and there is nothing blocking it on the opposite side, then you +will push the gem. It isn't possible to pull the gems, so if you push +a gem into a corner it will be stuck there. You cannot step over the +gems either, so you can also get trapped in a part of the maze by +blocking the way with gems. Also note that it isn't possible to push +more than one gem at a time. + + + +If you notice that you have moved the gems in such a way that the +level is impossible to solve, then you can always use the undo feature +to go back to a position where the mistake has not yet been made. You +can of course also restart the level from the very beginning. + + + +Controls + + + + + +KeyAction + + + +Cursor keys Move one square in a direction +&Ctrl;Cursor keys Move as far as possible in a direction without pushing any gems +&Shift;Cursor keys Move as far as possible in a direction, pushing any gem in the way +Left mouse button Move to any place in the maze that can be reached without pushing any gems +Middle mouse button or &Ctrl;ZUndo the last move +Right mouse button Move up/down/left/right in a straight line, pushing any gem in the way +Mouse wheelTraverse the history +&Ctrl;&Shift;ZRedo the last undone move +N Go to the next level in the current level collection +P Go to the previous level in the current level collection +&Esc; Restart the current level +&Ctrl;QQuit the game + + + + + + + +Loading external levels + + +&ksokoban; has the ability to load external sokoban levels from text +files. You can load levels using the menu entry +Game Load +Levels... , or by specifying the level file +&URL; as a command line argument when starting &ksokoban; from a +shell. + + + +The external levels must be defined using the standard characters +shown in the table below. If the file contains more than one level, +the levels should be separated by blank lines. The file may also +contain text between the levels. + + + + + +CharacterMeaning + + + +#Wall +(space)Empty square +.Goal square +$Object on an empty square +*Object on a goal square +@Start position on an empty square ++Start position on a goal square + + + + + +As an example, below is a text representation of the first level in the +Microban level collection: + + + + +#### +# .# +# ### +#*@ # +# $ # +# ### +#### + + + + + + +Menu Reference + +This is a complete guide to the menus of &ksokoban;. + + +The <guimenu>Game</guimenu> Menu + + + + +Game +Load Levels... + + +Load an external level. See the section Loading External Levels for +more information. + + + + + +N +Game +Next Level + + +Load the next level. + + + + + +P +Game +Previous Level + + +Go back to the previous level. + + + + + +&Esc; +Game +Restart Level + + +Restart the current level. + + + + + +Game +Level Collection + + +Change to a different set of levels. &ksokoban; +comes with several level sets, and you can load more that you can find +on the internet. + + + + + +&Ctrl;Z +Game +Undo + + +Undo the last move + + + + + +&Ctrl;&Shift;Z +Game +Redo + + +Redo the last move you undid with the menu item +above. + + + + + +&Ctrl;Q +Game +Quit + + +Exit &ksokoban;. + + + + + + +The <guimenu>Animation</guimenu> Menu + + +The Animation Menu allows you to specify the speed that movement replay animations are shown at. + + + + + +Animation +Slow + + +Show replay animations at a slow pace. + + + + + +Animation +Medium + + +This is the default setting, and shows replay animations at a slightly faster pace. + + + + + +Animation +Fast + + +Show replay animations at the fastest pace. + + + + + +Animation +Off + + +Do not animate replay animations. + + + + + + + + +The <guimenu>Bookmarks</guimenu> Menu + + + + +Bookmarks +Set Bookmark + + +&ksokoban; allows you to set bookmarks with a level at a +particular state. You might use this to save yourself +repeating the same initial steps in a level. +You can have up to ten bookmarks at a time, and access them via the +hotkey &Ctrl;X, +where X is any digit between 0 and 9. + + + + + +Bookmarks +Go to Bookmark + + +Jump to a state you have previously saved as a +bookmark. + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + +Credits and License + + +&ksokoban; is copyright © 1998-2000 by &Anders.Widell; +&Anders.Widell.mail;. For +the latest info on &ksokoban;, see the &ksokoban; home page at + +http://hem.passagen.se/awl/ksokoban/ + + + +The background graphics starfield was taken from the +Gimp. All other graphics +were created by &Anders.Widell; &Anders.Widell.mail; using +the Povray ray tracer. + + + +All the currently included sokoban levels were created by David W. +Skinner sasquatch@bentonrea.com. See his sokoban page +at +http://users.bentonrea.com/~sasquatch/sokoban/ + + + +&underFDL; +&underGPL; + + + +Installation + +&install.intro.documentation; + +&install.compile.documentation; + + + +&documentation.index; + + diff --git a/doc/kspaceduel/Makefile.am b/doc/kspaceduel/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/kspaceduel/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/kspaceduel/index.docbook b/doc/kspaceduel/index.docbook new file mode 100644 index 00000000..2d3cc2b7 --- /dev/null +++ b/doc/kspaceduel/index.docbook @@ -0,0 +1,659 @@ + + + + + +]> + + + +The &kspaceduel; Handbook + + + +Andreas +Zehender + +
az@azweb.de
+
+
+
+ + + +19992000 +Andreas Zehender + + +&FDLNotice; + +2003-09-17 +1.1 + + +&kspaceduel; is an space arcade game. +Two ships fly around the sun and try to shoot eachother. + + + + +Space duel +KDE +game +ships +shooting + + +
+ + +Introduction + +&kspaceduel; is an space arcade game for two players. + +Each player controls a ship that flies around the sun and tries to shoot +at the other ship. You can play &kspaceduel; with another person, against the +computer, or you can have the computer control both ships and play each +other. + + + + +The Game +The idea of the game is simple (but addictive). You try to destroy your opponent before he destroys you. +Be careful not to hit the sun. + +Rules of the Game + +Ship Movement +Each player controls one ship. +The ships can rotate, accelerate, shoot and lay mines. + + + +Energy + +Each ship has an amount of energy (refer to the game +options for values). + +Ships need energy for rotation, acceleration, shooting and laying mines. A +ship gets energy from its solar panels. The amount of energy a ship gets depends +on the distance from and direction to the sun. A ship gets more energy near the +sun and less energy near the border. It gets the full amount of energy if the +sun shines directly on the panels and less or even no energy if the sun shines +at an angle to the panel or to the side of the panel. + +If a ship has no energy it can't navigate or shoot. + + + + +Hit points + +Collisions with own or other bullets or mines decrease the hit points of a +ship. If two ships collide, the weaker ship is destroyed and the hit points of +the stronger ship are decreased by the hit points of the weaker ship plus an +amount (Crash Damage). A ship is destroyed when it flies into the sun. + + + + +Bullets and Mines + +Bullets fly around the sun like a ship. + +Mines have an amount of energy to stay at the same position. When the +energy is spent, the mine falls into the sun. Mines near the sun need more +energy than those which are further away. + +Mines can be destroyed with bullets. + +By default a ship can have 5 bullets and 3 mines on the screen. + + + + +Powerups + +From time to time powerups appear on the play field. + +There are four different powerups: + + + +Mine +The maximum number of mines is increased for the +player. + + +Bullet +The maximum number of bullets is increased for the +player. + + +Energy (yellow sphere) +The player gets energy. + + +Shield (blue sphere) +The player gets hit points + + + + + + +The &kspaceduel; screen + + +&kspaceduel; Main Screen + + +&kspaceduel; Main Screen + + + +You can see that the central part of the &kspaceduel; screen is the playing +area. To the left and the right are the Red Player's and the Blue Player's +stats. +The top box on the player stats, represents the Hit Points of the +craft. +The middle box on the player stats, represents the battery power. +The bottom box on the player stats, shows the number of wins. +The top of the playing area is occupied by the menubar and toolbar. +The bottom of the playing area is the status bar. + + + + + +Game Options + +&kspaceduel; has many, many options you can adjust to increase the enjoyment +of this game. + +Configuration is divided into two sections: + + +Player Keys +Game Settings + + + +Keys + +&kspaceduel; has a default set of keys to control the game. For a list of +default keys see the section entitled Default key +bindings. + +Keys can be configured by: + +Selecting SettingsConfigure +Shortcuts... from the menubar. + +This will bring up a dialog box which lets you configure all hotkeys, +including the keys used for steering the space ships, for firing bullets, and +for laying mines. + +You can see that each player (Red and Blue), have 5 keystrokes +corresponding to Rotate Left, Rotate Right, Accelerate, Shot, and Mines. + +When you are finished configuring your keys, you should click on +OK to apply your changes. + +If you want to restore the default keys, simply click on the +Default button once. To make these changes permenant, +press OK. + +If you want to abandon the changes you made, and return to your previously +selected keys, simply click Cancel, and your changes +will be lost. + + + +Game Configuration + +All game settings of &kspaceduel; are configurable. + +If you want to change the game settings, simply select +SettingsConfigure &kspaceduel;... + from the menubar. + +This will bring up a dialog box with two pages, labelled +General and Game. The first page +is rather simple, the second one has seven tabs across the top. + +Settings are collected in different configurations. You can choose from +several pre-defined configurations. + +If you choose the Custom configuration, you can +define all settings for yourself. + +If you have not selected Custom, you will not +be able to make any changes to these options. + + If you have found an interesting configuration, mail it to the +author of the game. It can be implemented in a future version (send the part +[Game] in the file +~/.kde/share/config/kspaceduelrc). + +When you are finished altering the options, you should click on +OK to apply your changes. + +If you want to restore the default, simply click on the +Default button once. To make these changes permenant, +press OK. + +If you want to abandon the changes you made, and return to your previous +options, simply click Cancel, and your changes will be +lost. + +The settings are: + + +<guilabel>General</guilabel> + + + +Hit Points +These two sliders allow you to define the hitpoints for each +of the two players; you might want to decrease the hitpoints for a player +to give that player a handycap. + + + +Refresh time +Time between two screen refreshes in milliseconds. All other +settings are independent of the refresh time. + + + +Red Player +Here you can define whether the red player gets controlled by +the AI, and also choose the skill level of the AI for this player. Just +experiment with the different skill level to find one which fits you. + + + + +Blue Player +This works the same as the options for the Red +Player described above. + + + + + + +<guilabel>Game</guilabel> + + + +Game speed +Controls the speed of the whole game. + + + + + + +<guilabel>Bullet</guilabel> + + + +Shot speed +The speed of bullets + + +Energy need +The amount of energy needed for one shot. + + +Max number +The maximum number of bullets a player can have on the +screen. + + +Damage +The number of hit points damage done when a bullet hits a +ship. + + +Life time +The maximum life time of a bullet. + + +Reload time +The time a ship needs to reload a bullet. + + + + + +Mine + + + +Mine fuel +The amount of fuel on a mine. + + +Energy need +The amount of energy needed for laying a mine. + + +Activate time +The time a mine is inactive. + + +Damage +The number of hit points of damage done when a ship hits a +mine. + + +Max number +Maximum number of mines a player can have on the +screen. + + +Reload time +The time a ship needs to reload a mine. + + + + + + +<guilabel>Ship</guilabel> + + + +Acceleration +Acceleration of the ships + + +Energy need +The energy needed to accelerate a ship. + + +Rotation speed +The speed at which a ship rotates. + + +Energy need +The energy needed to rotate a ship. + + +Crash damage +The number of hit points damage done when two ships +collide. + + + + + + +<guilabel>Sun</guilabel> + + + +Sun energy +The strength of the sun. The higher the value, the quicker +ships will be recharged. + + +Gravity +The strength of the gravitational pull of the +sun. + + + + + + +<guilabel>Start</guilabel> + + + +Position X and Position Y +The ships position at the beginning of a new round. The ships +start at opposite sides of the sun. + + +Velocity X and Velocity Y +Velocity at the beginning of a new round. + + + + + + +<guilabel>Powerups</guilabel> + + + +Appearance time +Maximal time between the appearance of two +powerups. + + +Life time +Maximal life time of a powerup. + + +Energy amount +Amount of energy a player gets from an energy +powerup. + + +Shield amount +Amount of hit points a player gets from a shield +powerup. + + + + + + + + + +Commands/Keyboard Shortcuts + +The following sections briefly describe each menubar option. + + +The <guimenu>Game</guimenu> Menu + +The Game menu is used to start and pause the game. + + + + + +&Ctrl;N +GameNew +Starts a new game of &kspaceduel;. + + + + + +&Ctrl;N +GameNew Round +Starts a new round, at the current level. + + + + +P +GamePause +Pauses and unpauses the game. + + + + +&Ctrl; +Q +GameQuit + +Quits &kspaceduel; + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +SettingsShow Toolbar +When selected, the toolbar will be visible. When not selected, +the toolbar will be hidden. + + + + +SettingsShow Statusbar + +When selected, the status bar (the bar along the bottom of the +screen which gives textual information) will be visible. When not selected, the +status bar will be hidden. + + + + +Settings +Configure Shortcuts... + +Allows you to change the keyboard shortcuts for &kspaceduel;, +including the keys for steering the space ship, firing bullets &etc;. + + + + + +Settings +Configure Toolbars... + +Display the standard KDE Toolbar Configuration Dialog. + + + + + +Settings +Configure &kspaceduel;... + +Opens a configuration dialog which lets you define many +settings of the game, refer to the chapter Game +Options for further information. + + + + + + +<guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + +Default Key Bindings + +The following tables show you the default key bindings. + +Menu bindings + + + +Key ComboAction + +&Ctrl;QQuit &kspaceduel; +&Ctrl;NNew Game +&Ctrl;NNew Round +PPause Game +F1Help Contents +&Shift;F1Whats This Help +SpaceStart Game + + + + +The Menu Key Bindings can be changed by selecting +Settings Configure Key Bindings + + +Game Play + + + + + + +ActionRed PlayerBlue Player + +Rotate LeftSLeft Arrow +Rotate RightFRight Arrow +AccelerateEUp Arrow +ShotDDown Arrow +MinesAInsert + + + + +To change these keys, refer to the section entitled Player Keys.... + + + + + +Credits and License + + +&kspaceduel; + + +Program copyright 1999-2000 Andreas Zehender az@azweb.de +Documentation copyright 2000 Andreas Zehender az@azweb.de +Documentation updated for &kde; 2.0 by Mike McBride mpmcbride7@yahoo.com + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kspaceduel; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
+ + diff --git a/doc/kspaceduel/kspaceduel3.png b/doc/kspaceduel/kspaceduel3.png new file mode 100644 index 0000000000000000000000000000000000000000..a23bc50e526b82d24a5f76796c4405a538d74e99 GIT binary patch literal 34970 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 + + + + +]> + + + +The &ktron; Handbook + + + +Fabian +Dal Santo + +
&Fabian.DalSanto.mail;
+
+
+ + +Matthias +Kiefer + +
&Matthias.Kiefer.mail;
+
+Developer +
+ + +Lauri +Watts + +
&Lauri.Watts.mail;
+
+Reviewer +
+ +
+ + +1999 +2000 +&Matthias.Kiefer; + + + +2001 +&Fabian.Dal.Santo; + + +&FDLNotice; + +2005-12-16 +1.1 + + + +&ktron; is a simple Tron clone for &kde;, which you can +play alone or against a friend. + + + + +KDE +kdegames +KTron +game +tron + +
+ + +Introduction + + +&ktron; is a simple Tron-Clone for the +K Desktop Environment. You can play +&ktron; against the computer or a friend. + + + +The aim of the game is to live longer than your opponent. To do that, +avoid running into a wall, your own tail and that of your opponent. + + + + + +Playing &ktron; + + +Rules + + +Once a round is started, the players do not stop moving forward (unless the game +is paused). All you have to do is avoid crashing by changing your players +direction. Additionally you can try to hinder your opponent. For this it is +possible to increase the velocity by pressing your accelerator key. + + + +A round starts when all human players press a direction key. The initial moving +direction is then in this direction. + + + +If you want to interrupt playing, select +Pause from the Game menu +or hit the keyboard shortcut (see section Default +Shortcuts). Additionally the game is paused when the game loses the +keyboard focus, ⪚ when switching to an other window. + + +To continue the game, select the menu item +Pause again, or the keyboard shortcut. The +game will also continue if the human players press one of their direction keys. +But be careful, your player switches to this direction immediately. + + + +A game consists of several rounds, and ends if a player has at least +nine points, and additionally two more points than the opponent. +The current score is always displayed in the status bar. + + + + + +The Computer Player + + +You can let the computer be Player One, Player Two or both players. +There are three difficulty levels: Beginner, Average and Expert. + + + +If you choose Beginner, the computer doesn't care about +the opponent and just moves around. This is the same algorithm as used by +xtron-1.1. + + +At skill levels Average and Expert, the computer tries to hinder the +opponent when he comes near. + + + +See the Settings +Menu section for information on how to configure the computer player. + + + + + +Using the Keyboard + + +Each player has five keys. Four keys for changing the direction and one to +accelerate. + + + +The direction keys do not have to be held down. Simply press them once to change +the direction of your player. + + + +Acceleration only occurs while the acceleration key remains depressed. When you +release the acceleration key, velocity returns to normal. + + + +See section Settings +Menu for information how to change the default keys. + + + + + +Command Reference + +The following sections briefly describe each menubar option. + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Toggles whether the game is paused. + + + + + +&Ctrl;Q + +Game +Exit + +Quits. + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings +Show Statusbar + +Shows or hides the Statusbar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; shortcuts configurator. + + + + +Settings +Configure &kappname;... + +Opens a comprehensive dialog +to configure various options. + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + +Default Shortcuts + +The following tables show you the default shortcuts. + + + +Player 1 Shortcuts + + + +Key Combo +Action + + +R +Up + + +F +Down + + +G +Right + + +D +Left + + +A +Accelerate + + + +
+ + +Player 2 Shortcuts + + + +Key Combo +Action + + + + +Up Arrow +Up + + +Down Arrow +Down + + +Right Arrow +Right + + +Left Arrow +Left + + +0 +Accelerate + + + +
+ + +General Shortcuts + + + +Key Combo +Action + + + + +P +Pause/Resume Game + + +&Ctrl;N +New Game + + +&Ctrl;Q +Quit &ktron; + + +F1 +Help Contents + + +ShiftF1 +What's This Help + + + +
+ + +These shortcuts can be changed by selecting +Settings Configure Shortcuts... + from the menubar. + + +
+ +
+ + + +The Configuration Dialog + +Selecting the Configure &kappname;... option +in the Settings menu will open a further dialog which lets +you tweak &kappname;'s behavior. + +This dialog is divided into three pages. + + +General Configuration + + + +Show winner by changing color +Enable this box to improve visualizing that the game is over +by making &ktron; change the color of the loser's trail to the color of +the winner. + + + +Disable acceleration +Checking this box will disable the acceleration feature - +pressing the acceleration key will have no effect, both vehicles will always +travel with constant velocity. + + + +Crash when moving in the opposite direction +Enable this to make a vehicle crash into itself as soon as a +player attempts to move into the opposite direction (&ie; a vehicle moves left, +and the player tries to move to the right in one step). If this box is +not checked, nothing happens when a player attempts to move into the opposite +direction. + + + +Player Names +Specify custom names to use for the players to override the use of default ones. + + + +Speed +Use this slider to define how fast the vehicles move; moving the +slider to the left will make the vehicles move slower, moving it to the right +will make them move faster. + + + + + +<acronym>AI</acronym> Configuration + + + +Computer Controls +Use these two checkable boxes to define which players should +be controlled by the computer. + + + +Intelligence: +Selects the skill level of the computer player, +Beginner, Average or +Expert +Beginner ignores the opponent, and just moves +randomly. Average or +Expert causes the computer to actively hinder the +opponent. + + + + + +Appearance Configuration + + + +Line style: +Here you can choose one of four different line styles to be +used for drawing the vehicles. The names of the styles are self-explanatory: +3D Line, 3D Rectangles, +Flat and Circles. + + + + +Line Size +Use this slider to define how broad the trail of a vehicle should +be. Moving the slider towards the left will make the trail more narrow, moving +it to the right will result in a wider trail. + + + +Background +Here you can define the appearance of the playground's +background. If you prefer a simple solid color, select the +Color: option and click on the button at the right of it +to open a convenient color-selection dialog. +You can also choose a background image for the playground. To do so, +select the Image: option, and then provide the filename +of a wallpaper image to use in the input field at the right. Alternatively you +can click on the little button at the very right to open the well-known +file-selection dialog. + + + + +Player 1 color: +Click on the colored rectangle to open a color-selection dialog +which lets you define the color to be used for the vehicle of the first +player. + + + +Player 2 color: +Click on the colored rectangle to open a color-selection dialog +which lets you define the color to be used for the vehicle of the second +player. + + + + + + + +Credits and License + + +&ktron; + +Program Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + + + +Parts of the code are from xtron-1.1 by Rhett D. Jacobs +rhett@hotel.canberra.edu.au> + + + +Documentation Copyright 1999 &Matthias.Kiefer; +&Matthias.Kiefer.mail; + + +Documentation updated for &kde; 2.0 by &Fabian.Dal.Santo; +&Fabian.DalSanto.mail; + + +&underFDL; +&underGPL; + + + + + +Installation + + +How to obtain &ktron; + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &ktron;, you need &kde; 3.0. All +required libraries as well as &ktron; itself can be found on &kde-ftp;. + + + +Compilation and Installation + +&install.compile.documentation; + +Should you run into problems, please report them to the the +author at &Matthias.Kiefer; + + + + + +
+ + diff --git a/doc/ktuberling/Makefile.am b/doc/ktuberling/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ktuberling/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ktuberling/button-new.png b/doc/ktuberling/button-new.png new file mode 100644 index 0000000000000000000000000000000000000000..4e744438fa51148b407b794cdf9c66605179dc4b GIT binary patch literal 712 zcmV;(0yq7MP)jua9Ul7J8o3kfzFEd2|_N~~-HHkPIeXQy3kgxJVw zA(s>(2^g{#flU#d14DM<7G{bCEp|7*?s9v%90b${%P_l-`MmGEH*eUHgM**Hxdgj% z8PI)}eN&r?8fQdEb61dlEG*|QC11kog0Cq6} z3rk`wum#vxr)aKusQt6)7gC(TGsSAxM@8F|M2oWH;BEPss5?BjxOytid?ZZYuA{zn@sxBNG3(48V0^2DgVnz9h3;O|VZorNQJBo=H zv)I~4IPUquJo$3um4BxgVn6rZ9uJxZqgaT-eJvLDx>t&yV={i%+QLhP9; z3twird#6Hq5U`Fx7V^A+(e%>3IS)~Am>Owyznq7n_=JDi1v+zj+J@Gv&uc^J)q_~> uvI~pv0MHmDNBKJM{Irey|C9LZ!G8es`T7~Bvp{eF0000M4$twI6**XNFi6g12vbph(wCWq)?d{0>Ob4Mx8sSO%;gC zK#Z?3D1{*^Vse29ogv1jB4h;;-UT1t0WZu04|GfA1 zPCb77@NeEgy!9ra|AS8kIQXv#q=ep21-67spF}A28o-15!fV+8V1W+LsF+I*#CIpE$qNIIB?sjyBSNZt);zSp38u}K&=2;&Cd{!mx{LdCojv2i11X))xm(D3T~;e6rU z`Yq$qhYfgJ)87Z{AX1v0dlrT{oz~u zHy03Kkb0;NAltL#{S+~Vuhx^7gLy{r=2phT9~a1SNM=Da6c%y=X%2+}<22bm7z5eJ z!UzU`q(~_=Ld7tEcB|f7b;aTp_wHIctq64*bPT`lT9SFqa1YW0NV5u-Ta1C?kwHo@ z2N^h+4;9uybDoK9RaYE%3;VwdD@!2)WCAA847dI7MgUj!uhSIb;Zh3BL||;SPgre- zbt)68+KNR4cJ61aEo%mkARGBRH>FoOhFS-iDKMG%>ro1v^@*H!Z>2qXcGnbZG@ExV zoyCOw3B(GNg9r#?5CalGI19=ZL@`L{LADfd8uO$8QAhyIvlF>>aN#UJ_YGG+3Td3F z7SG^EWIVPPOliR>tjJ+6;}jx-brHR-f~HI#*!8!QXwtoc58l_u*H43{>Te)a8UoNt zK>#Z7knwka2;C_*PgI=mXMFx?bL#H#Og3UcgrbycWENN%4;2{h8oC$i{W}#*pxD~Z zIRAx#3Lxz^2FZ1Mq{{hI4x~&Cbzl8t* N002ovPDHLkV1m&PsU!dZ literal 0 HcmV?d00001 diff --git a/doc/ktuberling/button-print.png b/doc/ktuberling/button-print.png new file mode 100644 index 0000000000000000000000000000000000000000..8a48c035114d8d1df16adeddde15d494f74894f0 GIT binary patch literal 847 zcmV-V1F-ywP)*oNgcPmhUN&{HcEgkq|;G!)4W%PWc5`k5RU~qFqHyp`D z*;q2!t10<(io`AAZxWJbyo5p>DQA(YI*4HWe1a`XL|`x&(7wLD6V`$sKN)d&a7gz? zFGPHtqaLb%YS`(S)=;NPB0^bJr1$rib}DNbJ{fWNFkN9>yEr7qzmK_lCxr-v&?1vp z8J+J3+gr**d7{KVN1_D9AyI<01w@wa&F=PyZ3Q9_u;D$M3vDsWK2;wc=YwPF!#*1s zIyR@(gsd#-H4W`%o1cFfUk>iH+YrgJnC#s0W)VXP4w#-mS$lArky8<%_fGlYy0E`@ z2GTXvz5;f2r3R=HH)3X9$Y$`eST%4_s(H+hA!WfCk)^$@;Fm`)Fi}k0N@%xYKq*dy z?5}#a->ypxaYHDrr7D%;q`=PY;|deOPkjMNs1&S+Vh;JcaDR8cg;=01 z0E*xkKhNr^1w?{GvI7`M8ZnBJAPk=kL#pbCv_6WYUgxjFdV21AtC?`SKOjL+ugEHQ z6>J2gVGs*xO&Uc+ql^#rFaNaG`dvTrpnNgL({=K?)=Za}0mxK4Zp&F$TiXuHo zN$;na*dS3I!4}=^b}0*sG{7KCjtd6;-U@i-0s$C3dqH|HMNx`l%kBPde-!{Yn7qyD zv|1?2@ad3857U(p*WN{U`&~xAK1S!z>koDVF3v3Gvz+XY5j2GKV6c|5HsVvUWkqN2 zRy~R?2Rk3OzM19VtBnJq_HVP~r3EPv1Vy*%N-c%}$2)#I%6Rl(Gk9ynpHsfPANnf; Ze+09Dc%uUIrRD$t002ovPDHLkV1h;Kh`#^; literal 0 HcmV?d00001 diff --git a/doc/ktuberling/button-redo.png b/doc/ktuberling/button-redo.png new file mode 100644 index 0000000000000000000000000000000000000000..67733a914f5c952c9812f330d0e76bc5b856191a GIT binary patch literal 611 zcmV-p0-XJcP)aI5H1$lK@w3J|)tYgLbjA8o$g z4-xq@-W7JYgwx9gr3oKUYy>)P&43i>5>uWEu*Y?*jM0X#SAE>b;_Xd;a9Anf_+0~~ zz|6q>shHBVYE7YE7gnpn znFK4H^r=SD=5xt)GYyi0%3|kp_O*fRjdC%meqq%oH*R616I1}2xEFwmd@8rW$sQ1# z;IwdMHSuPjR|6uC%020d4h47MM=kU;e5(PW$D2F7(V^kJcs;V6#o6otRY3*3b*b|FO;Y=kuHKhQ2<<62sYrT;+j2M8&x1pNnA)@fszG8Q3egrst} zSTX|{m;*bY#msw~K;Da4&=kpShIun@&YW}Zz3;7^o&EaFEyRP{0KEsk8^BfDf}g{5 z<{$x;P=F}PJdcVV0ZL`A2*Ar-yV35=R z4f-(N+GMmbydGI%HusR?&4EeTyV|13_7g@M#^zrD`*TMOOSNtUEO|&Hd=l{-CeOwg zJ(MEOEt%ApQ@O00&zR0UKDbU>+8HmF9`7F>L4sIkZ?Qm0fT1WP;k-jZOufIbenVg{ z7$$J#<{8ZLSS}S4C`>bG5K?|liull-h}{h&MGV8t81x5N9|nDcb+F3vdSy_-ux|`4 zuPbH6;wp@#n5C`Y#rEU;FoFvaQel@MCf_ssV!#Ee7F|1w_sM1Qm&nD7ci6q7P(XgeM1sf<&v_>a1xes zG1+>~ZYpcCex-8qLHH=5A~6U*&kWoJrujpmrN>zdVpQJO5gi_jSbLfTRSzZTVTFmI zMHv%N^|I8^bgY#<9@HO+1S);EE|G?5Jy*V@O9Dj{d?({}j|G07U#{C=t0o;)A(`O6+kj+38LSQKXDk^zD2LX(J4_wt$WA@cFVn^xe~AAx@zckgHK c9{9h-AF+<(!*1(VmH+?%07*qoM6N<$f=tOiegFUf literal 0 HcmV?d00001 diff --git a/doc/ktuberling/button-undo.png b/doc/ktuberling/button-undo.png new file mode 100644 index 0000000000000000000000000000000000000000..fb9184364ce2911cb2b055713da0c155a2e76bd4 GIT binary patch literal 598 zcmV-c0;&CpP)CH!Ou^905Sjypyx~BH*U~@N#->(VF~8+;@4e&m`@P?TH?Y3G_Jd=H zQ^$e!;XeEy!p{i~W;gwi~WrK5>Oj(T@2*Wl)q z;3jQkTbe7YAo%33!<{SO4C#5l1(i=Q^H$Mp3o@wqGA)q=e+dKJcmmaigPUNZ4Z6pE z0L=jS=A*A2U|29HV3)p49gxOgZVBbyZMA5ym;Nu%clN1(#AT*v_YL zacAOIBhax&k&%#M%=uuBmGKD&{QUZlbxbBM9ZxVDAa79vsuo$O!`^N*1>-!MG7hZW z_~|Ye_nF|bEPiXCZ){pUW_ac%GKGI zJDW83j;ivBKmGu;RRJ-zDEMt6qHqU)8`bb`Iw##^d>j9pBh5gUm+%36WaB5goAHF(3t+wAzJ(7j5$E$hn*+xCz&(%gve2u8XWzoC zoD&yy5zZw5OuQ*7CzTU$l6;oU_m4h?Sv?Q4@(8NLWwm%hFT9to8nq)D57``$e-h{= zdx1-{hFN%c0m&_20cCAWB`+q!xs;U0WR!o1&B~J%kyMPY$2s0p3^X3UDH?al+&oL^ zW+`9dQo?K^iB)UXK!TbWb1@Y_Z4=IsuNwY!(vMIw`0?pK0<$MG5jev);O*sNVPPo= zT`I?x%pVlyX7d`G0_IBYFwMhkDrdAdAQ~5=%~TafC4LIV=t|3bC`k>n4-e!?y@J_< zP3=E`+B?C+%p)CfI+AxOz{U${c2x14&?`hsSiSABgEg5hsK3n}E_>aGFbk0#iFf>) zatNcA22KXF;a<*WAtbWh;Q)!7T60~|Zbzp~ixO_7&6*DS&58KrQJZg_z%Vm>fy@Eo z70`*&C7;j5NLXrz*N-NHyx9zM9?CG*?S17kn3c=9Cxd_(&pZw28h{4)2y}KL9tDbD z7=p=F6Cw%sG}(PbMg=71op2=>C)g3Qh^q>MZBll*5`5jz($FmeiC z7ImTPg4uo`R2jtEk*>+%%vGci=`CxC)XN^sOM=-{#Sy1-m^HzyjU>@PKC8f21w-S6 zxR^y~R7M;G>H&VIrmmA3#Rrb$$}smh=j% zU>vjiu~CbN^%C_vqw_f+y;hDrGW$CVo5MSJl7{SV31-dKg;Ht^lvZ9A6b-Ox+-8_tCKniO zJd?r1T#uI$J~@eO_BxR97MTqA;;$l77y7*9d|qLk1DzbgD9a7XF}y$^?sECamS84a zW!>A})|uJC+U%y$0-0wy8#BWV?O~R3w4?QQw$N|Cew^Nayom}F_z|0jlb@;Rho~MPUl^w1Y|zlkvi&WYi4TUK;5$#zs1hq25x?2l4s0eeFM-6_WhrH&$&BZ7jMg=EO`s~z zVdgReUfW)qX zAm1c-%|P}L99=~= zP%t_G_M&pp7y{r7cCCA(K8G?n+lwE+q(z7|!aSJ}HVBU_Dw7#oaxV}{Us|P-BauD? z<+tU}1VSU3qlu9{jc4`3;<2b0Xv&8`*@c%_pgaUZ<5gX2ZKeDanR92`V~kj@ygb*UIUfNDSrswnYiqTz%NJoD0A_*i3788T zn88P;(3^3YetacKCBKW!$!1p3JITo$6`FmNkTgt|vr1H6p^1CMT zvoDsS6x&>&;4q~8u9Ryl9&PU78O-J-#xm5B6FDIq{XhOj4)a|}{}Nw6HUz3F+bG{fcC*s?ez^m4 zBy+OnHeAkd&U}=n>0T8NkqGfH6kcGYvdX0cch~k~w%tK{!cO>I5oC@kCX#_%V<(C2Y>E4UK5cd21u2!@F){ z$mL_;TA(B2@mibDz^ojV2pIT5Vwoc4dZDkeKLImPs^qJ}f~3umwt*S5B`Oqo6i8Qp};w3b8c#=%i5$1DcB_x+ri157mSGXP*@v7GC0(R(zW5evt+9J>^x=qMbu`zH??%670hRovsqRN#oO8} z{pS!eFB#0$j&!Mv#q<(Z%>(7(=JL@Te#n0w2=9EDv+W zW>bmXFsL-U_N2}}l!{{d(a_+EfoQ{JWL{R51}H~2lo+dxVyGk={zJ)4jyweI2jK@X z4NP|E%A>5>i4vlf2NGyiQC$&IT*6r}5Hc__hgq?~g*IOaGnt9-ZbU!;fuW1l9BwtHFp!%Nt@|@KZopKDzM#Ry=0||+F6Yd za%~SrG++PcE-4h6Kwd5}pb^Oo|I&2@lM7*1Di`LMPEkWl$HI>ueH~pqBeuifVFesprND`K`Rr?f|v{%u9Eu7D8kD50xuEqP1@{k#p-TlPco~f z@&j6oY9Mo$JVV7XP!)pvvE(_yT;MxwqRpAnL6RJ8joy54QUnl=7!~|lN?Pg2W53AOmuz@bZV6lA&ELsSuC0vlMI$d3*HH;s%S+lwJ6yvQ`IMYNE?_9%MG1j)-d0y@VWgQ*J6+8f<9PS%km;Fq^0Jg#?c}W&VOig>^)E z1(3+t3@|IUQXGF5*4m7rj4GTVB}Zq0cY|Dy1^;t+)m2dY^Z;omN%*WcEaR*CqD?<%tKl>ZIeu zl@QZ_&(H)#$?TBIV4b*;(%iZjar>CR@Z6Io`GUa<@d1oWKKrCeTyDBZ<}#iUBXuQ_ zhizDtOy~JFhv%Ki#NgIVPbaBF>-98*Nhh3w1Aw7n+=>wD3}jReBT@nmV-bIxeMtXs zcQfMA88XbI;^`;R(_7703%b0-9N^MJ=d{ZzCjftu!21sh}WtS6j@EN(PJ-H5_a)X%LF zRYlNQk55T+3hVDDS9kZubN`m9@jRJ|BBD)Xu!@!CS#bv2e^E1%#T+zaP&Wz(_HnlQ ziQ$i6mcG7C?QPnqJKMN-G(M2I`@iNp=WOGX`l<05xRKNi`+lhmMs`Ck%ekeYa!>p2 zR^Nts-iijM_eO9%ELTj0m=Vfuf6(`xI_68y-cKj6-CL{{rc-d&dfML=YyC8QNkS#a zHhcB~=wv@BQ>ZG`+Ot4oP6TBat~#05*K(;{EVhj>=OPguK($Io?bK@2{_3VS&n30z zJXgfEK6w)8OKg5vL#Je0&J@=7wAqyNjh&?FmkDQ@5MaB@rF(tF>n5^Tt$V{1A^j`~ zm3+ee>WqTRHT&Dm2Mjv0>B2O}tmfiRvN^LU>17s zn^nbrMGemCyU3s25MhqB%&Wd=4M*5AtyqVTlv3OJL{$P!mm8MGd<*8X1}=8jNHA-j z302@6B6O1zE|ecQKV2=wo7YF(-RYO`4h5ebI}l(^n0jD0gA!noX4j*t_dhAu+N?LP zFiY310&;l*Fhz>;s(n?=kN_Tc?4FEIc-k4v)|FWxz0zjY`m*DEt~yU9c23ifA-4=={&Gj%O`?w72uZx;H(OwI4 zmNJQDqILSM^`jAR{t%m$+nHzMO*W--=fW5m^6~qQC}Gnno%%$vVP?KN-V89Si+LkO z?rpG2I&0AW=wEHtpbKwGi&6a{FBi!D?b4Jh3qvEBJJyVP+)<19<7}2|v$@J@I`Sz# zaoe-1)%NiBYBRvxi8n;KPX~e4_RV(PtChtdXzFel^2(Roz0Rk&4n=|2%0GD`-aKbv z{pZ+xxrCsN`$H4fKF?|CcAJa!apP zg%en>MPvS|Hq#hJilM&8*_Mwd6T2rS69+bfb< zxn+@+=@07~x^+~eew3uzOS!HZdOGzFg!R8qo24bv(tm+nuIw9dJ3N!MG9z`T&-aJG4d7{Bpr+QI#u{{pL%j2Ty!j9I(@;1` z!{F6ZmInBMVZI?)%&|oP7#aE#plV77~bfqkpnz0@ObDG0bAmVg2 z=x1Q22;IN7-GiC%^=*cqv71qydrJ|GG?Tv9rrCoT47z${X_TJG#-J-Ly>x=Skqex+ z#a(|R%oAm`uv$KK;YIEHULpkS9mG;GMYN`nSvxYn`8h;_o zf)7($#?tS+8P-Rn_4GDL_!aysz+67>HD@-c&D~IHgj(6oD`$Jy1V|BQrS-#v>t74A zN15#A2YjxIU}rx~V4idaekC9+_m_@oOrIHa1-xq5G%8U=9x!VH$Su>ZPgI2dmD)@t zMK1Q%XPnIgFOYRZPF)I3$-c=~e<0t4aR!4f;+*$%xKV4AV$WTePZd6#pw07tO*DL8 zDU~cj4#D0?2}VnUDipQNBZS7R8s>*9U~?P-qnWW|L~kKisGW#)&RE9C7dRAqUXp~43o4cW?O&o5{gxG12mh!bFoA1t~`Ws+YQkqb)sCM1R zP@|~JrS`JITSR*nll7Z6^UlH=(bA}+fAT!Wn_?t22yw^EnExKpP}s#P)+(01ed(>I z8YKJ|EepFjOg&kIm}t{;j&CU&W1l(O_iCBdEjGuyx}EP94kVam-s0opXDH>n)TJ%% z?fxkJ@RZA{8d1esBx>?AJ+VZag44qPP_s1l0?z?x7zGWXES5qn%GE7rTa&lF=WgGv zwk2ab2VH76eyoNrOc}09eS9!OGW*ZR=d58)%X#fbsP^N{(*KLa|MF};`-MaSW<_-F zm-BjoH2-&DZLYIW1)<$ND$TDWp=(`?4^AExNjddCX zG~%o!!K_sQZiaU=OVnmrmjwbLPJf%i+r#kjn8fD!UWU79Ug@gowd3tI?{e`B%;V#8 z%%6aHIp-!F)_8%VJ@qmpa6SUgTB^DSgU;I;PmX-`9<^(w>?cG+R<4WNByPj3#70Ti zY}Rl}yIv(?RldwKb#7-!?o5r(*$hlRN1JZ;Y?h{2YuGb-1_ws=f-H?XkAJFLqMX#C z&h7-%wQ=orJUmWq=P>6ecU{(1Z?)WpcCLjVc?)J*5yGz#n13Al511>Zr2+o7>}d#8 z{hSSg9&Mvx(3*N->e{eDx0;6bfXwx3Pt9|l0gHCLpQb8c?hBaZKFq-R!{O!S<>15( zm_-%JM5W2sKKMg0pON&be zjoZFl;w%xS~lrUOP-fNaBDTnzZFo)I3eTrL3 zHk_5t$xPQ)p|uAeLv5|Z+^JFzZMMC6Ju#*)qjjlq(IVQMm^n&yTQuwwY<5SWGGP1y zmwfrx$mYdjCp#hZWO1oLopAm~*z7GjfH_;{k0VBkZEDnJ3#LZ5>xo4DRX2dHQ%k9# zm>c68>r4Q&oSQo^KW4Mis24AIg-Ex{7G{b4@v&zsPNW9m5;2;i{{c2PU`;d3;#{i6 ze6bUgSr}`hiULbU2o;VALvR`qt^8f%m6gFs>(V21#NZ+FteGi z+&Y?uC>w1o7JT>`(6r3)AG9W><9MCGU&+8~-h=YGk*wIG_4 zgNu?AY4c#~T_dh0+UC)?I!Q*CA^lo|n}ZA7N^=9~=^2Br-(hp>!tjS+!>vP{672if z;?zI-Dq)9sEEa1O7#n{UW)HP5%GXLe#Mx-{5W_|I@ehVZDP(UYipIUk!fTHu8~^yK zu7OYjT?h=dp*yyk6|JS4AG5jNVRQT9MI+dB(b(`XOG^_d9nC@k;SkicQi?y%W*!Bx zNZg=tbQZE#2C#ph+ubt|nNk3VyZ4Z(knkZ^jj#vVAhmzDnz`gr)WJ zrCCH3$sE-A45~2^&P->vG$ypBVXu_BX~zBzn^yy2T}Shx3p0rMR*eD=U)1wCR;bUY zl3*{!ZWL0_kF@!93p1N@OypqMIhn(3Bb5o`(b65C{|_^1;bmZR6py`c0!FQgb=eW@ z;slssZSf+i2}bw0cr3A*B{=dK2sM#Wp)`AA@bUszt$xhrjc6<_%W||ca4@{;3}*PkfbOButZ~V5Vsye; z`;Pohz+1H5NMU3Zc)+mrLgg?UF<|d0nY{>&bK`6*V`Vm%v;Ssr1W7eODmgAEREJb{ zVCBttmcUSGU9X}QI|{9m7W3?>!03+tTMg2UWz7vX4-?pA$4?vk1e;Ck!S%j2W37`m zOA-uYP%;etYD>4d1t9k|Fb-cVe2f!eN-!%2JCI%Vq%tEM%q76t8}P!XUQt|vxz21p zm)Z;_oki*bok98GZBL%d6Wo~bbR*cNtCKO@4cpKVTXS`0U({(*e9dL$)0~!@=N|xC zj*b(TpJKBkXc=a51nF+Z<_Ty0VYehM=~$bs$+F_SWZCgkPR6bz-1a0;gqSbZ4&2h*z!67%>!VX zJ{$;+2Luij^LbsT2P(xK$0vk&Uco$d_u1S=A3OI@M1)zv!&=sVPh&EmC*rg6Fl%_7 zJ$ee=(BpG!I)FRHR82r}^ zh4|=>XgtH_V{!vunAg7EV#IkBX;!ho;V(3YfZgb!przsL&^sDRK#nbZL?9XhcGg%v z2;V~3sndFl1J3Ug11+rOlpi+d$a**-}PE!XIsG5>Q42P001BW zNklULX4fZA&I zR9?=rGcrzJzn(wI=7U!b1{u)4N^fc4Ix;3S+QN}fhrl2jb$}>Z8pz>Sp1COKqXd}Y z{dQP#jCAf0W-l79R={~xx+h9aRSov4hk41#9M*tGAM+YJep0|^6T`5(KTbRX4o2xC%FEP~YO$~In1Dj0+ zax6k7L2DZ#Da>+nvBYKsbr5}5PbhE*GrqNncB7$}*gS#$RnrP)Tr~vX4=^XAIR%eM z7=X`UKA$R*4n%owvT54@bB{r~tUnYfMnTwml>yBB68J$IN`&Rka49|mW@C$)!B`}GrAxP~E6qS2gSPPfU_!1cr&i3YsH-oX$Awnm3AD~7qlH{RiN zjtDboaB3?dQVzx{nMA__N5UBWLDCHkhKkm`))!Wc%vlr@RS{(J95AeXkfY8JjhdW^ zHV7eB|HUOWz=JiHDg)Um?baO#co2WUW)}h(c?Kn8^MparTr|8+x9EEVo6*v!P?+ng zAG!(M6m>DU(o;u*jT8rxN6`S7U0M_~CI{Ne4jIguEyu#Kgpl2cF(QL;G%?Qp#1Q0X zZKiKfFoip=kqM68kb8-&*4~(->8-29TzPFjkyra@w;ONXuz3S>8$Ip7>VnNJS{Gpm z1Q%OQNi$@{Flz@K))gqUv!2Ztd>eQdO%U);WMH#{ue#nz;ea8vhM5z7S(kwjV~F!1 zGK&O7;{^Wp@H*s)7OfM}V`9jeI*-c6W7 zMb5vf_h2@er4i*ez!Pl19R=&JI{kycwME$q==Qj4f}; z1zj#uuF~~*uv>{n73T7W2wml(yB#yHzE3WBfu6#SIvPRJH9AG;aH!jbc}RA10p60|%I@w=!kla$v)grQMj7br!*N}NI1eHL%C+FP zZNjWtA>^Vwn}w0C-4+mts+5aDI;JYio>aLmlc8}9@n(EN4(J<6scj7DGDPay?UVu8}qq;oA z9D{WL^Qw#zwOL_^QcAgqYTi~I$?CPcv%wor6#&%W?rk%g_&SRKhXNlwkhU} z1{GPqoIO!E2-2i%@CFrXGsBEPclDGvaRpN&hL$7Dmg4%C+;09L$ary6&xtDR`cl^! zlsqIR(=J&X7^3z`KpRXT<@21lRrx=u7an3FoS)(*R$HLirJ6 zUN-`ixy^Ec0z(+0a$q|T6roe_(w*pg9A!*CA+YULT8|~JS~Do6^`#Vzrx=_^(ME7k z1`5(eCnZ)D`5SefGRd@=QrcpoB%2P!)(QtPS&g@+_>|H7?N(cnwVHA<=?dFbmtP%9 zQgI@XaQ2w{MRYyP8jXyBlSCW{MHidd!)8*NbEIHjUSMY3vVR~_#3*7s~iwag7?nxIZN zpT->K#jnp;xngzDP~4?4Xv8EQeI0=g!$rt!3+Kaev}!EET%ipOD3=R5f+dc%VLUA6 zFvqGqfH@O`QXn=YFOQzl-|jJ)(ACj!i_v?p99k9 z*5ZK``YwPod^1%RU~;s?D>{b#7v=?)E7hl}BV6o6l;to&i0u?AHKh)_A{Fl@1G738 zgl#enY04Q9`OIyybL_AM(NiUF}@AfH%-5^d);*Q8f9c(4R|rD;x3tWdoSy1sbQSfvR6 zF=3i-h7BJvH8q#MuaEo_y^b6u+%SQR`TBnp<_$-4>aH}qusi66!>oG_vkz_LLD0@u z%>}!foNisLWu7m$#M>X}b%g2TPa3b;Fhg9LeC|c!tiX+5}Dh2{8Zq_3GDd8=CLxLr12oCo_UOF~m%4l4&ElVWvTIbO`Z~-; zoPP24gJPh#TtVonc>cC+9xH0mgc?1GnrfvOZQbLK&~h*R)F7}8k9q_)nI3!D6Rg0( z9_#WBjd6ihG#l2LlzQrG__EZTIaGGt(d%n$rWMLfb^7ru;tW>g>Gb0bi2XH~w4`fM>-G1a8k z0P-}>_gbJKjnDJ3B=TG=XKsk2IIU50zn=A0Im+Vm60If7;w=D-S3d#g-fY+`o)dp9 zObN4N8oxy@|9XG>ae9A$2T$Pr7bOnAK{TRitHWxuqSdI6{@s%UARaUj2I)ATk1y>0 z?XMRIk1i&zHsCrXB=y9W6(yA=S0f;)S1?;tj8{h3U%i#G0WcQ^f|Tk`Pkos${yJqW z4rKoIej+aa`uTp+AB1^+K<&^URbrXpjJ?jTnpSWx^y1$30=hihYWGLt%<$>dvyib- zam6G-?J)Ej<|G&q=M~J-Orhl32$1(_@@YVv0-KeI!{d)r2D7O)AS1hh+eqfmv3b7J zHvH;Kv%xEfYHcV*qfPtNqaRbMc-}acC6PV7{rC}u0H-^hsC*z+3j~;RDwBUtYW@E6*RSfnr7_=K zU3K16U!CXH_#Q=R=jaJ;+UHhmeaSK3!tAgt6kATR6w4LNq6Oofnto+jZf(IeLu6st zd+ib3sqZ>m)>HIwL>88YntuG62%{ebL4SEd1EeRtvNUp-e}qsU?L#}`^t9bPKPS1%)JzE@a6ZX|{rQNJSAxNX9M5u)?x_c6WM_mJF7r;iKM9{A+zQ2URs2IY;eZfDBLLv1lBgN4 zm@_Cgi@n~W7ZcnQ9bF9antu?!r=%R7odwMNk;Gt}7YyRI+;g_)uK?Z3%+83zBJVVh z>^Tqfi>ES>`FPyzsz^<6Xl8@taungKQUreSar85*GGjPTzrAiZS`;HKgB}1e(@Jb} z**HbZ+Ua7+q-;CAzXt(H!LI7lTCvtx53_Fa7Qv(R^P=2*b6v}HABMpB$W%VQ9B(wQ zM4W|hT%Ey;MM?SUh$3%QO$HsAagow}Z}L9Y$qQ5{&U+vOVfDrfG-c-Hl!2U^;niLC z&W{GIdBbMY=0oEwSFNG;EeucH7iKbDj<;K}3T$SEmMofd(*Hqu$`>Oky+Fn}Gn?yG zlrE2HNHCjEZ$C;Nql>?Bw&jEh!CGEX3}c7MC7b8x^?BaSpva9;fn{-_J;;3Lfr3PM zh<8w4Eny&W9KbH()RP6wU7SE^?yMT2-U^&wyL{F5ZRkjWHX&}L&Nu?%%%g2g10BFO z^s?#l?ukrHmR|{k-hqGQB_ONNnqdyR%MM)o7KXPgUT!GH24)hER!F+;IZY6&=Kh{hA#NmV|=Q_epr8gylDG9sAAY7h>LAjAV`bVEdkgx%>IrPlNi7us#l!1aRW<2_u9%D@A;oz8f~Xg3YBL z7<(4o&}QQC`DH5>H(-_%+H{x^STNcoDRS~)sTqZSAUG#eZ@(!?H$mQEbL>14r?}r|Ghta9xYT<>|AIZl2ESt}_wfUZGkUYOUFB%U^2>#Im z0*MKtFf$A>ek!wlOi=v(eLUX}ug!%vui31hVRMk@;~#@rDLYMXQ{2{h+b?CdB`r80 zu*1af=x_$Q~d%M2_&hv9@9#@&G8`?bY@%iNmm^rs9KL|56%m6dPVtvj_I?JM2EC^+XK=YHN$7Mym|j?0bETPyo!oZ{joWX!B~3BO5J^hkQOhhFOMM zw`qAEW~_jjPUL0xKxqorP1p`b?Xww-#6|H{xh=aeD_8ph&93gL?{NJ}ZCrQRd@P23 z_;W;nk7t58$9g}Gm)Jbt)8_d;&JSQtt&pl#Jqxp;<#O!nPt0fo%0KBsh5L3dbTo36 zW9v5ci`g>T8O&N>Q8!+wQWJ0BPeJD@frkhj1DuF3>BW)g%rGDCI@`>$arI%?=d`&@ zQr&`CXVakElQ0XMa7twwh%P7waQWxY?hPCX?WRp2RJN@FA|sdS3VYc$ivGY?No--3 z)cu!gt6uBCYrGQKT=C#+|7JQPs6i{}Y9`eP+i^|FIU52!o99QC#=S`0eVAoMQ+^e$ zBqfR&@3&zV6vaW9!PN$I5oSdCjT>u(2MKIe?e)%snus&Qj6IH|s9%Z1Bo?op*tpLr zPBA7n6RKV)j!}|-drte?ze}rQ-)}Y<3mH;Tv$c2Qbq|!U>G2|4A^v zmM{a(mXqU>W2zT@z5C%Anrs_j){_%fPR!reKxAJDYda^!ZNyoTLHGLFy|Fp> z+NbtYlk{v>Z7T6!XgN;Yj2Sf+PNk({F3e`x4zVWyHP!>EHdF}P+ulQst_s7P==^X4 z9MXqyENg>2n_YsL2f~?mkKd!suEG{sx}wQX4&K(yaZr%s5OKCV24WculDP{NbV^Kp zH0Vrgawi2g)wH};WXvNtF4SmIn=SQu!uBx&ARt$$)~h` zGTQcw5jx_%q9vx=-5On~XjLkweGOgG+m|(&k9!{gGCDYkVNsFeaX!S%_6wC}!Oj_V zCD?hPEgbHi=?jFgAJ^`7a{Z6-7V4UhX?UOwt#RN&WvpK-n+~WOfl%#jIxg+nWKlm1 zGcKpxNSX~H@d|H;-*8D=bz3BM42^upO9nH-7wR(EPN6(eKhB|uA&UNsWd31^s7L5+J{-WQ>=yQ>i+T_Ft0bWyOS1;^MHB#fwjT| z{BkTv4L6R*Bq}A$F9GJNdGR57+OFCgbo(&JZv$5+=KtwGg|G-u7H{?$Bq}qC4RHK7 zSB7B2CaiB97#n2Eb++Gp%grZ5Lxn)z;va4?)Bvw-Mc7AYplEo&5NuB0M|?N(fd`Ks zdwlErFfX%bDZ_K0%};NKov^+a5MxJP`KX}xHhW^+gf?^Ey>9C$KFsE6yey)1Sc!wf zf(fgADAT%83ykoA$!E|`5dnS7$(4j9F1{TgNz28G({Nm4kNS2wfOHw}UR?6yKV0`CIW24Q|LdhoTy&!$C@nU~aTtn~ynhd4yv*4t_{>_+@P{XIP|O$!OhOVHbxqD%E}$%+*VFZb+!9!skUK!DN|0Wn!^wp|X zwP4b^P^~Xs{=00BeLwTf=;{5PmlxokEOwFrGwTG|nP5DRqk(%W8FVi!y@=^cIS>Zl zFdEOl_Xj{?a|k3-5WIyK#xPG(e4A+e3v_n5FeBP6cC2@A(_~{y7UV2=fi5)Yzj#lD zS4F5;8Axygk~>!2F?cY<_?ut|UiU$^ygo9v5Aidp+!xvWlD@oufi$BnLF>YU2Z*bS z)S6ZbQ1*g$M#|ZcM@TS7dPcvbP>FzVRFZozAN0uRPC=hT*baV<|7Fo|Oz8kMz&ui5 zS#yVl)@mQqfFoIgQaa<={KB{$eH3D2cf5G#Fd%+kG-#FN@U0}K!WGG!Lo90GCvo)-=uY<@XeUO}VTntHiUAlW7I_CavZ3Ffaa#n!dm(0>$eIN}#?8r(6*OGC@cOrMIs7Es9 z78Z@RSeK*Kmtvu{s~*jGH94CD+_||C;u}77a$hq5Wuvk>>&jh9ZT=F@1am~Wa#!bW zbg6~?*Z4V1WMv_agJ1Uo9|9sm^!Wn2P-F0;Biq&##=dm@O z&0N(R-!-@^t<2c0#}Q7JnKtjFL47bkPmJDy0h_UAWLDU}gbM5@FO%|1I3K9E3klke z?G-oIj#o!XF@;rM3$7)0i~{vt132*aE2FO z&Fy)O(lP?cz}^UHB*F??~cx~_Fg+Tn0*ALT@CsT+de&S4r}h0fW$**uXS>G1(q zJvZ<;G=RxC zVs=#z>N0?~OF0RwgrfQRgTX~}MyWwpu6iEF>uB!4?E4MAi2VGp3et-!t;VPSI4=9FD_~g)#GA}Xb`pH`SwFSwID%~$ybW>%pC86o-H6Ynb*pnT0~}B1`y56AxCTf0D-XWepTv2rsLlw` z9g(cp_Anmi5g6?Pl}S%v1Be|0GvW`9!y4>}(pm-{wi>JggxQTkmGiw7d;l7Q@drQ| zEP#1FTmgp#KGf#8ppZ7_0JBmS1;$n`D@qtJ_^B@~2=j#=}NsE zX+!ZCTWn0z3bm>?*8{x10lwp7U1Matv?oT;Zea5PryL#5XIz0E>oVaPK}B%p$obIX zz^cUnBdenFj2748#xe%@9Z=4N!Dw?cHsh>Y^;wvaCs-#&3uUWiJswvWb(IACVv1J4 z?sIMmhz6Pvh}?WYN&}VQEzdIv1FstgJI~|!iX4Ks2+~1=qy(pAy^z3DWMf71g5G}-Z|Y}D$oa-X6zvl(bSBfcO9@Kv5?pfef23^RTJGr)WXc+YrZ zi-(ELYnZWVuJ-de+jSG-mf3tgACB$y@B*65WqwqC2|$BrI7K1f$(iV?)L6DV)Z;J` z%)(x0y)@tjj^@*BZYy!eP12PVPA*0@JJk>BQ*3Dj+qCRjVOz99&qe5`O|jab)NdaRk*1-Jxy^VU=~mNL;|+tZ3hoKK&f@4Il+tL>wJ$h(02B(WNU+zhSbzvX z!`=oBIxtA6I8|d6j2J@80W?0~+XHs96f+xi;__*jZ!RVfDhF;aIPgi)z`49tGmk{p z2CtU}$Xs2rE|QLq&;n4N&1g3Zkl(d?lGt3?+@e!j zEXU^B%8|(o;~qfXfg$TRe;_B_Q6i!N3YR+ahpSBO?Z?>(eok2;R1; zX{L}Az)M<%9DIQmOnfI+bAGV1@fF521#BJ&dt|fXc2ch8*a*%8(j1{1$1lN*!J^%* zNRn;@T(+N#?o)(rq#&|h0Gz9zYqAk6{G7o}UgWi9EGZCbFc3;}RCKMtb2YJW2hh}} zh4HI)SPTHrwH%SB`iI>>XxpN(3LXi?r)`}Aq4qN7flv^Q**u8QeR(^0b3=v}R}8}8 z^DtkGE28##rxBvd>N)&$CnjwxDLoRy`lX>y0OP{QmXAKF-UquGJ{8Qi2$cmwQ=|_c zWyH0y^dE*9eHdR3s>7;h*Dplq_&aVr$TylPV(Q5ibAZ$srUtnyhS92a$4HV_{M2xw zjMwldvL{NkxWKZyMDM_46E*j9fq!PZ9>7ML&i z>msSnGqy1?57~@CrmI|vRn3M>%@_cl}BwtuXQemw<-`%OE^{kDVYD=a($O7$bDb0HUSw{Q;5Zk z)oQV<|HJ&OXr$Wab|VV8UL-Cjvj1mbeyRR_I!D>a*<2OZx0ZhUzczoUUcd4qU|X;{ zgLpL#hARL6i^eBpgAvXQjlHe?FS2>A{(YRI4H3B9uC-xDEqwXYe+6duZ=;;gb8Y=) v2jOam%kKX$e`?kmbGcp9c_kQ8m@D@`nF(t~S@nZU00000NkvXXu0mjfN(sQ` literal 0 HcmV?d00001 diff --git a/doc/ktuberling/index.docbook b/doc/ktuberling/index.docbook new file mode 100644 index 00000000..33b87804 --- /dev/null +++ b/doc/ktuberling/index.docbook @@ -0,0 +1,811 @@ + + + + + + +]> + + + +The &ktuberling; Handbook + + + +Éric +Bischoff + +
&Eric.Bischoff.mail;
+
+ + +Paul +E. +Ahlquist +Jr. + +
&Paul.E.Ahlquist.Jr.mail;
+
+
+ + +Lauri +Watts +Reviewer + + + + +
+ +2006-05-05 +0.05.01 + + +1999200020022006 +Éric Bischoff + + + +2001 +&Paul.E.Ahlquist.Jr; + + +&FDLNotice; + + + +&ktuberling; is a game intended for small children. + + + + +KDE +KTuberling +game +children +tuberling +potato +penguin +aquarium + +
+ + +Introduction + + + + + +&ktuberling; is a game intended for small children. Of course, it may be +suitable for adults who have remained young at heart. + + + +It is a potato editor. That means that you can drag and drop +eyes, mouths, mustache, and other parts of face and goodies onto a potato-like +guy. Similarly, you have a penguin and an aquarium on which you can drop other stuff. + + + +There is no winner for the game. The only purpose is to make the funniest faces +you can. + + + +There is a museum (like a Madame Tusseau gallery) where +you can find many funny examples of decorated potatoes, penguins and +aquariums. Of course, you can send your own creations to the programmer, +Éric Bischoff, who will +include them in the museum if he gets some spare time. + + + +&ktuberling; can also speak. It will spell out the name of +the objects you drag and drop. It will speak in a language +that you can chose. You can even use it to learn a bit of vocabulary +in foreign languages. Currently, &ktuberling; can speak +Danish, German, English, Spanish, French, Italian, Dutch, Portuguese, Romanian, +Serbian, Slovak, Slovenian and Swedish. + + + + +On Screen Fundamentals + + +Mouse Operation + + +There are two areas in the main window: + + + +Playground area, on the left side. + + +Objects area, on the right side, where you select objects to +place on your playground. + + + + +Main Window of &ktuberling; + + + + + + + +Main Window + + + + +Placing an Object + + +To drag an object, move the mouse pointer to the objects +area on the right. Click on the object you want to drag, but do not release +the mouse button until you have moved the mouse pointer to the location in +the playground area where you want to drop the object. + + + + +Moving an Object + +Once dropped in the playground area, an object can be moved. +Just click on the object to select it for further drag and drop. When you +click on it, it goes on top of other objects that were partially hiding it. +This trick is useful for getting the glasses and eyes placed correctly. + + + + +Removing an Object + +To remove an object that has been dropped in the playground +area, drag it back from the playground area to the +objects area. + + + + + +The Tool Bar + + + + + + + + +Toolbar + + + +The toolbar provides buttons for the commonly used functions. + + + +Toolbar Buttons + + + + +Button +Name +Menu Equivalent +Action + + + + + + + + + + + + + + New Game Button + + + + New + + + Game New + + + +Resets the playground area. This cleans all parts off the +playground so a new decoration may be created. + + + + + + + + + + + + + + Load Button + + +Load + + + Game Load... + + + +Opens an existing tuberling file from the museum or from other folders. + + + + + + + + + + + + + Save Button + + +Save + + + Game Save + + + +Saves your creation to your home folder, or to some other folder like +the museum if you wish. The tuberling is saved to a small file where only the +position of objects are saved. + + + + + + + + + + + + + + Print Button + + + +Print + + + + Game Print + + + +Prints your picture (&PostScript; format). + + + + + + + + + + + + + Undo Button + + + + Undo + + + + Edit Undo + + +Undoes last operation. + + + + + + + + + + + + Redo Button + + + +Redo + + + + Edit Redo + + + +Re-does last operation. + + + + + + +
+
+ + +The Menu Items + + + + + + + + +Menu Bar + + + +The <guimenu>Game</guimenu> Menu + + + + + + + +File Menu + + + + + + + +&Ctrl;N + +Game + New + +Clears the playground +area + + + + + +&Ctrl;O + +Game + Load... + + Opens an existing tuberling file from +the museum or from somewhere else if you wish. + + + + + +&Ctrl;S + +Game +Save + +Saves your creation. The tuberling is +saved to a small file where only the position of objects are saved. + + + + + +Game +Save as Picture... + +Creates a graphics file +containing a picture of your tuberling. Available file formats +are XPM, JPEG, PNG and BMP. + + + + + + +&Ctrl;P + +Game +Print... + +Print your tuberling picture using + &PostScript; format. + + + + + + +&Ctrl;Q + +Game +Quit + +Quit &ktuberling;. + + + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + + + +Edit Menu + + + + + + + +&Ctrl;Z + +Edit +Undo + +Undo the last +object placement. + + + + + + +&Ctrl;Shift +Z + +Edit +Redo + +Re-does the last object +placement. This menu option is active only if you have previously used +Undo. + + + + + + +&Ctrl;C + +Edit +Copy + +Copy the playground area to the +clipboard. + + + + + + + +The <guimenu>Playground</guimenu> Menu + + + + + + + + +Playground Menu + + + + + + +Playground +Potato Guy + +Switches to potato playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + +Playground +Penguin + +Switches to penguin playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + +Playground +Aquarium + +Switches to aquarium playground. +&ktuberling; remembers the last chosen playground the next +time it starts up. + + + + + +The <guimenu>Speech</guimenu> Menu + + + + + + + + +Speech Menu + + + +Please note that you need to have kdemultimedia installed +and &artsd; running to be able to hear sounds. + + + + + + +Speech +No Sound + +Toggles sound off. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +Danish + +Toggles sound on and speaks Danish. +If Danish sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +German + +Toggles sound on and speaks German. +If German sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + + +Speech +English + +Toggles sound on and speaks English. +If English sounds are not installed then this option is grayed out. &ktuberling; +remembers of this option the next time it starts up. + + + +etc... +Same for the other languages. + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +Settings Menu + + + + + + +Settings +Show/Hide Toolbar + +Toggle the Toolbar display on and off. + + + + + +Settings +Configure Shortcuts... + +Opens a standard &kde; shortcut configuration dialog, where you can change the +keyboard shortcuts used by &ktuberling;. + + + + + +Settings +Configure Toolbars... + +Display the standard &kde; toolbar configuration dialog. + + + + + + + +The <guimenu>Help</guimenu> Menu + + + + + + + +Help Menu + + +&help.menu.documentation; + + + +
+ +&technical.reference; + + +Credits and License + +&ktuberling; + + + +John Calhoun - Original idea, original pictures and English +sounds + + + +Éric Bischoff &Eric.Bischoff.mail; - &kde; +Programming + + + +François-Xavier Duranceau duranceau@free.fr - Tests, +advice and help + + + +Agnieszka Czajkowska agnieszka@imagegalaxy.de - Penguin graphics + + + +Bas Willems next@euronet.nl - Graphics reworks and aquarium theme + + + +Roger Larsson roger.larsson@norran.net - Sounds tuning + + + +Dolores Almansa dolores.almansa@corazondemaria.org - Educative graphics for COR-EDUX initiative + + + +Peter Silva peter.silva@videotron.ca - Proofreading of +the documentation + + + +Paul Ahlquist &Paul.E.Ahlquist.Jr.mail; - Bettering of +documentation + + + +This game is dedicated to my little daughter Sunniva Bischoff + +Thanks to Apple Computer and to the &LinuxPPC; project for having made +ports of &Linux; to the &Mac;. &ktuberling; would never have existed without +that! + + + +&underFDL; +&underGPL; + + + + + +Installation + + +How to obtain <application>ktuberling</application> + +&install.intro.documentation; + + + + +Requirements + + +In order to successfully compile &ktuberling;, you need &kde; 3.5. +All required libraries as well as &ktuberling; itself can be found on +&kde-ftp;. + + +To be able to hear the sounds, you need to have kdemultimedia installed. + + + + +Compilation and Installation + +&install.compile.documentation; + + + + +
diff --git a/doc/ktuberling/ktuberling.png b/doc/ktuberling/ktuberling.png new file mode 100644 index 0000000000000000000000000000000000000000..3b44966d99b287bc5d495d66a926fcc8590fef3b GIT binary patch literal 5916 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 literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-edit.png b/doc/ktuberling/menu-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..d5090713b64817e5d29260ca7e51e8661791cb7c GIT binary patch literal 4579 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 N002ovPDHLkV1gR~E5rZ* literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-game.png b/doc/ktuberling/menu-game.png new file mode 100644 index 0000000000000000000000000000000000000000..93981973ab982b0185d61abdff74783db1693ddb GIT binary patch literal 9785 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 F{s&LfLWKYT literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-help.png b/doc/ktuberling/menu-help.png new file mode 100644 index 0000000000000000000000000000000000000000..dd02e340b39cfb491f055a6a554e59ae72e4f21b GIT binary patch literal 10124 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 IJ+}z|KY+a{hyVZp literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-playground.png b/doc/ktuberling/menu-playground.png new file mode 100644 index 0000000000000000000000000000000000000000..c85a7e4528190e9afb0e0a995d625cffa6b52d07 GIT binary patch literal 3059 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 BAQk`s literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-raw.png b/doc/ktuberling/menu-raw.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0276c69900059ccb91c13af0b765cc1831ad3c GIT binary patch literal 9022 zcmZX4WmFtpu{DJsZl002laFKc5=_~Awe_%Km$;e zk<{|aILr*Rc{kg5-R4i$!BZ7VmivP;mn7<2xDJ1d7r)2v8Bo~Fw-Oz98V$3f(~ZwZPwk350e_dLAIkRXX<+B$aD3G z*2qrUMRIO89?d-plIq&%D;-M3s5uYdX*@e3JG~-4Bd!5MDHTTPvQ!%(L76KQfr8&E ztYP8g-*$tBPumRfCgT%CQfwY(bM0%3238DARvKuG*G>;5n7zSq=N^L*5>@YS;-Ba0 z-0+s{6j70ba^4YrPp-(lNx*P|&?5&)s7G_6wN1YXJj?mIVki0E3v3!kf!hWwp%OHN zS~NxFbHwvXG=z&Yn-Ef1y*7T31kH!h=cZ|7OjKk^ASQ@`5R(sGV~a-lD^o-p)1|6~ z0y%Ge#V?f?(N*yo(7*e2$^PFKRbp7PEMpNh2YkI#&KSJA2<~_3iK{|NEQFXx4%*|y z7BS^RULPa?h8~vjkjBWQOmsrIyRD^gE48wO#Q#K@YW9Afru+Cz>4eLm97=D#XSO|)6vO)p)cZmz4^lX*F%Ixz8wxCtU zYHUyed?SRjM>X6&9?ALm8pDeNz7dJHFdnDuV0Z*6@VeXwZ z)7l7KPH`JuJ#xS6f)4+3mXgQgtE=OP3qPaJ&7wU3fruPq?kuTdK?F5D<4}hvxdoo9 zqV`|1P^?rFToYR7{yv=v zr~$fZhRC@lea92YqgZna1Cgq`uHKVQ9X z?TMm9D{b9(1G>4IlWeh3gVz-XpE$8qV<>SMKLLNH9IfOPXOpiEt+lXVe5|l;X%-a> zkR+2Q7zana(a=gNE(ap58uKHe8=_8)7x9*JyeZr=_HGcIJNMzMU57(CnUe0!##{6Q zKLmpD*{=tFks|zRy-W##WP;xtUbGlv{R%TqDz+<9Ruyv{&5;{l^H$b8W;d#{4p6C z_xFG}Y^+$o-%fl+;n!%%iPC2%_Nyz?Geri7Y&|7)w6XgtT+<5>aemjgSGth!H<(8% zEcT^bX>^Oq26@owBmIGsUJoC9Q%&J8j?_~^%jOe1An<*~ft594e51tt4d~Mkn%sIU z0J?EDS2GQH-P(0jRyNeeK>A?;7c~Cb(QsG6qt$9IMYDY2V%gff+&~LZoUmq@c9|4Z z8Y#k*F5bf&l)sqx$>b3U@X%`fb6n1MM4{2nEn!(%lP(L4nv{w=`!RZi`wvOkauOrq zVzt(oHX>D2&9lV1MiHa1=pcUl(RpDt7b!VbNRXtHKCr__v(EyjR7)4<>NCE^*y=UZ zq6HuDSy&=LF7d!mQo95FoapB|#Yb>Or07b^Sfhc3?al~+q1c%v$N7*5t^03c2Bpo; z*1G2F`uMeT$2!#`A8pEjg{b3kB$Z}iEXDVVUb|?)z^a_`aB=?4)VNAHpzN?;yL5^H zffJ;2zD{Jx$$Pi?&CI*#0%=9HNn{JlBErZrmP0gOCQh8Ve5qh=f);!bl8OS2hU;jZ zJrb@H-1rsh>S{f(9KU2zvt$$zrra~fx^#=_ZMmux$2s1(;riQV7hW2fjT8isG2uQ` z#v-|}fLHqovwrBf($oY0h*X1v5`d!f+p3rL?aji@s`N=FE{{bhqE8IQ!)^jqm zR>*DAVRu)ITJ7!E$?^92sf>W#Nw4Rm;=ibBA}b%S41Z9%Li#l&3?6PqXiVvG03o9g zI~kIkWqBZAyTEO&wwyRo;>cPH{rl9p!-jN z#NV-y@|IC|5_Fy&B-7Pg8YHlksBiM0w4p9Wz`KBIwC~@fvsJ2pS$1kkedBusOHV}>dT8I*u4^!V0KhJw zLJ69a{snD~Qv$S0EQ5Z-5q*3%3@1_Wn6;)NlZ=7c#-t05wcHhY8aMB+A%+k|R?^XU zwfEh~T(0-_3FfwgnHYvDg+)z)i3&Wql0o@YcYVBUfGSF8k*dO5?-laa8-gZJdz9r$p}uQPuAeKDJ%0$MSxHE_1wrM2Lm!Qri~1sMCP9> z8B|r^7L$|`ej!Drm>b6gY{Bn0iK1+fnJW9j9;qI*8Z8B8B;=L%kD2-?kAtS=5O$lA zIGwK~NvZXO|2GG}h$b>$WBFR9gYX?}>{z1o4T2;#ti2+a5BtzbV$2t^&_lEk4sQjz{6~Jgf2_O^BM00cP$EoJ3Y6S( zROF5x2^-cI|K%q9k2C#`M>+Z+%dB^HWlm++;+{@aVov3yy7q+ul6|DvAM4qK0bGl) zQuGpj$7sJi|JC8mfx0I_T?3EE7`bclnO5)f?K>C+e;~ue(_BzoLfW4}Z9E7#ns=wT zDvgwC1^+NV5qG)|`uR+!pnNDn`a00F!SkYWwqn>A#&qh6P@^Sg;tzP`Dc;V-VCa1gld*PL`llcx|jQjPm3X{)n&CXsf`}>el>!PRosg5E8 zVsX2`^pzlmB$olZ-L{ORXZ0+boOn@Imgs zJHzZS^Z?jR)*nlw&a=nf{oK&c0((h25y@{yqxhh>Y&dCmF*f(GMr3aBk)(<3z6xn4 zwbug~4~G()tmy~Gjt;jyIe`-bf`)YpI@_gOROb&iJN$W3p8T&%@`z-0MCm4*&lmtx zgl&Ajc9MYqcB-K4?{BVL=X#UJz@Do@$LzVcVHizYJ+el?h{IX1Oko!z7j6aB7Gz;C z_{w82vx&`GEESb#Ll9As-ZLyr4bN`SIO@i+FI?v_XywyUwgh#ze*|vji3@Sv7#?l; zr_Blz_%WTZD&3Zce27jKr1O9!A3KvrYUDY_|%cGY>1%em#2~%|qaA z(O3BH#}{35I&w#WcKI7Ad9zm5Y7d1wbJretm1?(V7rP@Bc}ta%M(LTNr|H&2oPpMs zZ&&auYo2ulyVJhd{pJ+b1OF?|h2ivSjKpIaKhXGkW#fOh?(r1kjVFH68I7cT@c7#B zczoJtQdseLSSfG3hh}O*z6|I2L$K&tW<7gS02VriJ%O6zbC$xv9H-PEMj!X zGawo9)V(_$$TsSL=c=Uy?X=Qv?N2I;n>bv1I?ByB*rwL36oWU52be-`!3s&E4kh!3 zD7T)=LrBry>-Vxv?l0CvNuZ=mgQb#uDa3K=k3)^`6y z7YLhAB4{MM?c>*H4Dj18Z`S`bN;P+zFjE@1XPl+Mprj{_qbG1RoIItbMtQ3*mYZ<@ zTp+w=!aiin+sN_fE*>_glr1iL6Y?}xIf^G%MvDGH)2zog6Qj9{DQoEP*H@F>m9^LqKO2%Nx^XQM-V{Y4ayo1o+iPBnJ)r``6E?9hrrpNMV8wW0VseV-DQ z@$&E?7|Nl)d}FR)tME*d=--23`jT;s%b%O`p`%fvS@*U9ex^H|`j3bdTY6HCN4M&C zUL4~$?R}6^^Twg4>*-c@*^dTY?~>xmwGs?G48d{(^}lq8pC`{(=P7wDgrTl-a8n_XdlP%(`ebV z%MJt%yyj(rLNlYH0vm)%Iqkw!{5LAd?Mged^FQR?|D_zgx;(-o4TH*r1w|$A-b`6! zQ`^zc#NSRgb}dYqby8eL2}dOJJkT1wz8TD%70YJ(1MA%8^#vt|8?k7*mG;z5STiy5 z$Zeake0y+99>0XS%3tjTxD;7gcl|VTI=V8DN|V!A)5x7uuGMC#WQZ`W`}|D*E88eZ$uk zHwoBTtU6)@85CJSp) zWqF zu6jX?z>;F{fNY!HxU#yCJpVGZv?8wiitt&SNyDELKOi+@N|MB4vckEzXnIBa_NRNZ&lh{C zTcU_ZmgU=pe$7$&iOOpXYJYFZxvfrD!EDR(f%Yjwkz$=B)?7x}Sr^ZZU2&_FI#Wfw zW5K?a$CRJMp})G5+sRN}?5bZj>uT#QIQ1u7P`?doR3WOczZ%%ei#a!$wwc!dyJbuMj1r&^a?gc*^D z=)_IZ)3lrhp8rBHXQr`AUn^G~Rf$%{JR(;}Htd6La?l^4x{O!dywl&mh zKf2}ST+a93ae7uro@r~Kq2V4Jss(dCdHdkQ5fMGO{8dQdc~4Fx5flvYRWP#$g>|Oj=32R+T>A-%;xu7 zYn$XXW(+&x_aQRM>>c4i{e0CrzGZTDXbCtGcs%vmp`)AbGqfB}D!R|?+U^+1^-PeP z3C8d!yO7UYs(xC|!+6vA?qFbx`}}m*Gb>HxL5?juFFq=Nu^_Ky!$|F&a5|Eyn%b4P zN6ukyh<&WzupkTFe-TMKfpwDVD$w%0{hry%)RwsC zb(ojoO{9LEZ`!*l;1e)+zTRz%%_n|-{+xbve>Ko9pLOiD9yAPPWRM4Er9t+!!64wU#nD0$2$<+$BwG&L=L?y8>u0x#(VJWf ze!)?Ybl`sAComymufK~E5%cIp$G+PLA$5a&(IvMZyt#w?TBt2#u_=w3_Hit)gD8pC zG{zN}_MXe*2Z9=n{MXx}UlJ1KxYo#FOh+uwz~?^dtYrmG;DYY3ZA;_ngl^lFv+qpe zF4jvCCn{(zO6`Bq+y9^WCJ&h^m>5?6WPB;!Ift-~YnKyL_+!Pq)D+8DmFq}rjaV{X z){FZ8k3)ysmkw>IY@f;bx1~$a+!W>KgGlGeHBjsqYh|4`@;N^fG1u#32d(n`y#a6U z1A%87xQeC(u>bhl43OVjsep$k zQycrnr9m@-UJLJjvTLE#ufl*Xx59X9o^cH6I!6lgl~8pQ$dB-ndv9Gh*m$3b5HtA; zeEal}&BU8j*7()0W-I_nJNoafu^HWcsgF%9R@f1gk=b>I7)vpwrBRbvA!(3RB%)fB zl%QtrzKUUh?{^HVH|=F4dW0eci3X8p5_V>vhL(mhe>u7C1d9* zPPYlG^ZJFBP$pS`==k;eSqghf@79)5f97bQ&UG-^OBNEjJ`b)!(d_}BG26-K;M)OS zOT-TmX$3Eh{b}NQH4nD8UrL2%NuPZ>cwcaByHq1dpFnTTL09L_TkU0PCB8!ZrYH%p zr^=8Chj!6_F{7RQHEmmJ*>E0by-tO`j=gG92DK_qcN<<*>#lzi4I3)O~J<&w2& zM!oSUc3f|#g3YKub+G5;gMig24H91#umSrn&?G?+m*GzfY;>L!TzA;#pm=Q7iQY^C zG@}?KvzWE5Sdi`{Bs5Nc3~~O;>xIEkf|wGN4n99&Wd}7a#|faGq$2 zTuIz_2rfvGWdK6pT$3)Y8vtL2Je)7v(XoaJniq->|B!lbKCh0P&pT~{J1c#+p1)PK zod5GE;saWe@$Yb&2 zyb^)NtQc8rT1Tvr+^5Bl7u#CBj-oS+=2RRgMC~+Kh#Y$-n;WA%`${lcDe)|dZ{sX! z_kunI9XokO`^tULJyoZ~WT*^_Go=HqG@O1ex-=FcG&IEjRJ;I9diq3+|8pv*5Z&g? z481*ymCZxIU95nAFSneWJ{V(R4Q)0HTx=)7YaKZS*FxmZW%5n2HbACKRjTqqTP7{h z2wZ3HNJEOXtZ?M&Ueq}%nhy8a7SmS?A*1!C@3F9k8l8|Y*gs}UdPGA^furKJcxttx z9+h-Y&cA7Dcl49f)hJQ900c9JGeh%GbD%9=0jE`@N@!wEjUteOghUqzNL|V}H zOK9gQ8*Gy&H9Oe3yJ=$tc|v?hZ7=u0CV#v3K@Zj1pY=RNbHHC}gBO>|uhiP`IpduS|_F9$xh{gI&^qf6FYpJ$JNY44|S< z#(y_Xrvbg|%)*2_KaA+$7Nt4U-r~j*(b$jF?XVT7i0ji7GxNSBu6w5g+)CP6I#>;p^pCOf`?QCy$B339Cq&IV zoPg!PT2hW^23cB{I|~#qX{uO+4*!0%VR?!zT#`oBYvcdz4G_4{-(H>E%7>7FF$Sy8 zl)~rCO@1~~&2gu}T~f)0a7Q7Ou)mJ&MK%2M@@gt#aBU*#}17hg2i7yOx^9 ztDr%~p=h^Ty7mI5-UkrEm4`2UuaKaiduF0`Q{*~WqMJ|@FLT*Nx1!g(J39p@g zTK|X&{)7HnX7ipgl4-Sw-;rq!x;CsG!++7ugTEt;N2wK0gmE}RL{D1$Q^_XUWE-Sn zbHpuZ?bAHn6UkcjtIEgnTQ8vFO{)Ek3vmZ`w=7nOX80qB(zBm9yW=t^Uc`G`mXQKS zC(d@1Ve_fFcQfvg%X_m%mZkUkdw&xMl_GY;std1XCMSwgO}n!Ezh5)gihtjv_}wQ3xes^NlCN@SQ>ADU{5Sa< z{_F0C&S*$?__F1w9ze=Kde3b>o-Iu)6eo;r;f(VZQ&_pG;>CRkTjS3Fo2~3C_pY{) z2&{7)1DwIml`~`STbypXLo!JcWe>)bDcVJJR4cUukyS ze7puK3#{$B^h~Zf=zVsDsU{cfNhSCS`WoQ;Rggo(>S{Xt&^Kicg(s#pdq}`=96ABX zkS70rIH!4zUE>@y^sWY%<`_r%NZT!o5^>?!i) zSMdzU>RNkMUHy|KH_Pj=)b48%@lpJu5fzDntq{@Rojd3!SjL~!ljn?BnF6!dn08Wl zJ*Z&-tQyqkZ|?qseIcE_n@;t}l7M150OmgXC>l}&HN)F(2N)-Np3&nFC>3%nnO^&FlP?wW3Xo#0w`}$3RqX-c zdz`iiZjq`ewUGzv`PJv5$pOZA#nb!xRx~^yQhPLBiW!K(_ZnGJwYVdd1~7$m z{vCG{17V3Z<1dfEA*QP!8=AV#s2ues6h5&fWyd_J0-`ywq4KHHAFdOAOk(V9hC*W4aVO-nq!}EY0>-8 zSixon@PGY30-O|_X0ZJ?)7mJ8REYjA%zg~t@$Gn##R`_v7}448)H&jV8G><`38!M` zqp)V!0|wpOMW%ABRGFrk+$~gx$S?_0X*qkM6423<$+W+tqBDxEMQi^=O8BbmvW-mB zam7xFE&?~3g?8Z6<#c+y@31;@D7A|qwc7UP%0c~|rWpUyIHZk86G2Yh|7{eysir2| zokjDik&4bfjVGCHtq&i$%vF1Ot-_eFkQEtQY7tLpZ*Y&xT&`$;Njk$wOY4P;b>&Wxr49^E|L0Rq zJj7h-FwxQpoy{|qLDvg1Zt_xKhGo|ba_}&6rUl+94*q*n^=^*J0Yl<5u#rk-p+=QU zLI$CGtGqa_-FR&$>;QXd4+|lDf0s7{(FhPOBTi_5ih~-6S@(((raB6C2ypDC|J%TV zIwFX^_$A0SpvK4cXA|%h$5EDpk53zem_+YPKYbJVYM_#S_D7#~UW>4xH=xiA=A>0* zqpCkAEtIJZQ1~~SmS%3^L<&}NcGazS4+a3-S1>DJXN+)B!F3_%o%-O(p4Y-`#8E+f zjGwo8IRJos``;`8p38a}#X_NFC7l8d0ZR^Q2cK^61mKK~7gHNRP|c-m`pgY zA=q^?=Wcjo9uYr;7|2FQvvK4Jqd~y*mUe(DX4B|9t@K! zrryx264m~iq5R5?|DnOQY)42kTlFKrf#Qex)?JvTuvqdABNHMdhUJd&*~65|aqJk- z>0#)#d7tWlE@EaitJ$k&@V;W7vw5qkGh^X(k898(>dy>dQaa08XUG=!+!jlQ(r_*z z0Gm+p_ia{O5b%fAxpXa}&*&fJlODAORK_8M*w$uSncjyn+&AOIc|FfE0W=V8w@xh)}?T|}W{n|p_75b%D%T!oSZGwtes1arN zLLJlbk3$a~39Rvho&ydZ)dI7NE`mEw1BTEI0^ns?&-IVczyyoYj}@t@)&ShllytnT zY|Q;m6Y*5bg>MAU&1YvlyV2_FUFHay<~)dD!>yr5q%EC4V-TSGx-J~jfh@^eb}ZDv zt0+^%nK~XgTJG7;&T~~x6|>fnY2AkM`@s!kL9!V#|Aylq6@E8Wg~z(r;WGEC5Pwd^ZV=AaxOJ1$0nhwTr5H*9oms!F821r1jwvIz@s^FGVXpr@gl zIgdL<94;>7fCNOK(H3 z=twb-KK;8#b<*rypf&U``y=68^u^Es3-~h2;mFSt`I6%ycYr#dF)9SEHYxY0m|o^$ O0E)7oWh$ghgZ~d>i>=uJ literal 0 HcmV?d00001 diff --git a/doc/ktuberling/menu-settings.png b/doc/ktuberling/menu-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..c581dac74876171345cd5815517639736656fe2e GIT binary patch literal 4836 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 literal 0 HcmV?d00001 diff --git a/doc/ktuberling/technical-reference.docbook b/doc/ktuberling/technical-reference.docbook new file mode 100644 index 00000000..94abf945 --- /dev/null +++ b/doc/ktuberling/technical-reference.docbook @@ -0,0 +1,262 @@ + + + + + +Éric +Bischoff + + + + +2006-05-05 +0.05.01 + + +KDE +KTuberling +technical reference + + + +Technical reference + + +&ktuberling; offers a gentle and rewarding introduction to &kde; customization +and programming. The application can be extended. For example, without any +coding, new playgrounds can be added by changing the graphics files. By +adding appropriate sound files, translators can change the sounds to their +native tongue! + + + +If you extend or add to the game please consider sending your additions to the +developer Éric Bischoff for +inclusion in future releases. + + + +For artists + + +The size and shape of the playground and the number of objects can be +changed. New playgrounds can be added. Only two image files need to be +created for each playground: a gameboard and a mask. A maximum of 8 +playgrounds is allowed, out of which only 3 are currently used. + + + +Six images are used in &ktuberling;: potato-game.png, +potato-mask.png, penguin-game.png, +penguin-mask.png, aquarium-game.png +and aquarium-mask.png. The standard location +for these files is the folder $KDEDIR/share/apps/ktuberling/pics/. + + + +The first type of images, *-game.png holds the playground +and the objects that the user selects. This is the graphic that the user sees +when playing the game. + + + +The second type of images, *-mask.png, contains only masks of the +objects. The masks are used to delimit the borders of the objects and, in some +cases, give the object some transparency (for example, the spectacles). It is +mandatory to put the objects at the same position in +the gameboard file as in the mask file. + + + +In the same folder, a file named layout.xml. +($KDEDIR/share/apps/ktuberling/pics/layout.xml) tells +which images to use and links them to menu entries. It also contains the +position parameters of the playground and the objects in the gameboard and +in the masks. It assigns the sounds to objects and places the +objects in groups. It finally declares languages as sets of translated sounds. +It follows standard &XML; syntax (see details +below). + + + +Still in the same folder, a file named layout.i18n +($KDEDIR/share/apps/ktuberling/pics/layout.xml) +recapitulates the strings in layout.xml that can be +translated: + +The menu entries that allow to choose the playground and the language +The names of the categories of objects + + + + +One folder above, a file named ktuberlingui.rc +($KDEDIR/share/apps/ktuberling/ktuberlingui.rc) is a +second &XML; file describing the menus of &ktuberling;. It should contain +one <action> tag per playground and language. +The symbolic name of the action in this file should be identical to +the symbolic name of the action in layout.xml. + + + + + +Translation + + +Besides the usual .po files mechanism for +translating program labels and prompts, the sounds can be localized too. + + + +If the various +translators can record their voice to a .wav +file, they can store that file to the language-specific subfolder of the +sounds folder. The name of the sound is then assigned to a file in the +layout.xml file. For example, if destination language is +Italian, translators can record their voice in .wav files located in +$KDEDIR/share/apps/ktuberling/sounds/it. Then they can +assign the sound named hat to the filename +it/cappello.wav. + + + +In a future release, &ktuberling; will use OGG Vorbis rc3 file format for sounds. +At that moment, it will be possible to convert the WAV files to OGG Vorbis rc3 +through the following command line: + +$ oggenc -q 10 -o sound.ogg sound.wav + + + + +Information on how to work with the translation mechanisms in &kde; is available +in The +&kde; Translation HOWTO. + + + + + + +For programmers +&ktuberling; isn't really difficult to extend for programmers. + + +C++ classes + + + +TopLevel + +Top-level window and basic program management + + + + +PlayGround + +Description of one of the game levels + + + +ToDraw + +Description of one of the graphical objects to be +drawn + + + +SoundFactory + +Description of one of the languages and its sounds + + + +Action + +One of the user's manipulation in the undo/redo stack + + + + + + +<literal role="extension">.tuberling</literal> files structure + +A .tuberling file holds all the +necessary data to redraw a tuberling. It can be edited with an ordinary text +editor. + +The first line holds the number of the playground. + +On all other lines, there is one graphical object per line, in the order +that is used to draw them. Each line contains 5 numbers: the identifier of the object, +and the rectangle where it should be drawn (left, top, right, bottom). The numbers are +separated by whitespaces. + + + + + +Structure of the layout file (<filename>layout.xml</filename>) + + +The top-level tag is unique and is named <ktuberling>. +It contains several <playground> tags, one per +playground, and several <language> tags, one per language. + + + +The <playground> tag has two attributes: gameboard +and masks. These attributes give the name of the files holding the +pictures. The <playground> tag also contains one +<menuitem> tag, one <editablearea> +tag, several <category> tags, and several +<object> tags. + + + +The <menuitem> tag describes the action identifier +of the menu item allowing to select position of the +area where you can drop objects, and the label of this menu item. +This action identifier should be identical to the one in +ktuberlingui.rc. + + + +The <editablearea> tag describes the position of the +area where you can drop objects, and the name of the sound associated with it. + + + +The <category> tag describes the position and +the label of a text describing a group of objects. For example, it +can describe the position and the text of the group of goodies. + + + +The <object> tag describes the position (in the +gameboard and in the masks) of an object, as well as the name of the sound +associated with it. + + + +The <language> tag has one attribute: code +This attribute give the code of the locale for that language. +The <language> tag also contains one +<menuitem> tag and several +<sound> tags. + + + +The lower level tags are not explained here, since their meaning is +quite straightforward. If you modify layout.xml, +don't forget to modify layout.i18n and +ktuberlingui.rc accordingly. + + + + diff --git a/doc/ktuberling/toolbar.png b/doc/ktuberling/toolbar.png new file mode 100644 index 0000000000000000000000000000000000000000..9ff20d9afb3d3a3b74144469cf0a0c72cea42da3 GIT binary patch literal 3804 zcmV<24kPi2P)g1tLr z$KEAmf?;Dbu#PtnJ6Tv~Y}hc)L$EyHjTA^?8P;(dY+Qm7RD(sTz>Vv&F82PcF3dhu zOLC&_R!eb`NuqlA-IQCWs&1e2opbJK>4880-=ARt2dlcO-=02j=gybn2rXB2Rlhxb z;D7(;&R>GRuj-Fm|B9AyK+AACtlg;PuUB8~j94AZ}a2>1zV?M7{I zW7XIn;rcuC88mk?^nlqCx%Sd$5kNo)3Z&)hp{eT45>s62}2FS3N1kp=4M?7eKNYIy4*#cP5dT$;FA*%!gajbjb+*) zX^kcUOk4@$gk*d65CFy}qvhVMv5RcZ0)UwX(4`&I&9%-=dk-J?UjD>e@v64dP|M4I zl)pahJ$&4I;{sne%ZHA8&z$wnO?}gAIpPQ`a&5K(Oy|$ER4!i7dtJ=}Kr|i0q3ekB z1y`?y)y-u{Tbmnxjh$OPz!<9lL#lHZqc7i+@CKq=7=AAB3eBWM;`v?vq2JvsmwYL- zy~D35t)!%K0HEkfP;vu*5qRePETh(hO}nvtuP$~nW1;exW|wxh@_yzzlekDCK!DAQw_xG5f7b~;Bz_W6H zx!G?n<(sYRv-v||LnYYI?~4;ad_h3tYMpkl8T8^8yD_p$jqbwE=hXANaP%L7V<)u~ z+~-F;&jkQ8XWkIQPY1_N>u9EH(vUz?tJ^BoP3tQC?fvYoPPL|8Uc4ram^2GkW}E+} z7O)_xGyTnZHd&W_r}5UVw9@atxf_Q*z?p0JfoioGJp9k9>_(?v#6Z6f2jL8^CJ1K` zCh&{if!+RaDcW*G=?wRQXND;ediFkg|y_aOLpw0JrV8Bb4Zzi|uZb2%WDnTh&0nehuedcZTn#BT}f=7bkB;9$^ z`}mhDxz@`1&5llXD~_f@Sz8Ty&!L0}qeWfgjEiEmjWvOV$v^TLME@NPKN)zwTFeFj zI>XTqQaB{#;M?)wdJixHbzrY_>URujsmV)I5orKrXaM9@8ZBU;sf{)|)3GLLXxY}< zoSr*7KJ@fZd2NN3X%cJF80107CdLdb#6qX0`sAQ&uF{ej&zwFtvS+xJ&9?n!l(<{o zR3qzC^mtJNw15M#*uNPsp9bI!Z;U(NZA;Y|9{-&pF`jt)x0^$|;TB?XBx>NjN&n!7 zsP?6m$GqIt{625&6HL~nQqs%4*2?F5Mo5S>C zC_7HIv<8bUNBI70K7sHvQ|VLd$}P;oBGwrEbu_fK7z7IW2WcQZIQ%bH?~Hfq=FWW- z`i}&LY3^5frB4G`rX*-|rb(n3VL_(M7+#OQxOB9G&GFC12iFflnk>9@Wpds6s-VR? z3B-wU5dj_?D!6GD6G@&i?tN#weltvD%bTjwhoHBGeNBvA!>PSyWFy<%Z99G8q<8Kb z-rQyO|GdS|(#n=Mm9ZEsHP1v11id)5ciA2B0?+2tB`v{psi^>zfw9Po>!vx2ltB5A zs{hu1Hoz1h@V7>2q>(pNZoh{lb2lU<_4g;Ef%U~aKN2KGz&r>2;j(`IsiOF5B7(8- z1Xr&CVN|Vj5mlhE@CwRUh|H1fZZji`BM^F4jHS*LjW$d1EV)f7fEEx1YZAJ?196(9 z(FA~$%|uU8e!`%L@tJ6#SoU1+4bx{2(v)5yMr9BnZpG1uHNOx1jIwp4bA5K^f+?+aD?d|S5wtK? zX#!N*!Vvq*9vn>msd?#m2Fc`2Y98F{cdife08pZ;+kz%^3DD4j1fdN9OA@r^-Lqp+ zwscoj{#gJhahhfRRpX(7)FRTTXOOv-X|9#UK#Fpo46Uu)bH3a9?EEibU=(10!)Kwj zVgX`d0D*abFGQe#a*=3N)onR~J11H{(VY{7Ma!Rh{0QK9Z~(yAX2l6XRL8T&y*z%z z!JQM!tk1fiZ#MT`6)-zTGHEWWWbdix2Gnd3J$cBv=tc0#^RNb=jHPzC9Ub%fY-}R= z@;?LJfa?MZv4PGI&LGX8i8zJUh#Nq|MAs8$Y2bRHqlY#v6;Cv<#>{7SHY+DhVr~U< zCnNJ0T(Uk(NhL5ev=EWHE!L(n8Mm!k(@aJSLQ4^W1J6<2*JqWr3(Ppvhp{PD7SO^k zpbmBTgY@&mfWyU1I`N4d{tpgS+Gdh=8Wi=Zz3#q$Ps@FtTbS!8OAT8 zT5-J|uotyy6#Ed)Agm+4jiiCN0o_1)J7oqv6T`CPdd8w$l45U*i2Ukvfg~KHk%=0b z1ZDQ(Q%x` z-Sg~T9o>dAmkoQHeNv?lwHfcBze^81B_jg}iUJF*<@Z-`;v$mU5XJXjlqGqySVuM! zz`l3VQs8;g?8*h8MbrRw`uXA31YLq>_v*6en{y7Q91rza?e@`#F%`iTWU}Gaq3EMf z`dW5axi!MU#AG_M%}asUA_l^1Fo4cULx4yLt;q~1C};)8XkzDfLHo*YUSXl{k`6>5 zPIJ>A1ROD>t3V5Zu-Pi+Uf9WIo=}pRIGITSo*MAz2(83qOo?K8j`(X>EQse&9KfdI zmvh{S0oqtzdcIj%%YhOm>#E$>EdDBeh_&~|AMwTqDWCy?Bk1wqKzibjUxgHw&9hvG zHxrP3@5J}iN5{Zo?BX~;fiT}L1APd=cuL}X&bzJ}9M}#E zfk^A=$kIF$N6x>3hYrNyj9)4OK;GGuia^l=JOH#v8W6-EAC^jKg**6jR0AOU-bv|u zuAr`g+wsT$LRMBk$DDYZh8q=C`f9F*&JX( zQ1Ypl1+a8|2FHloY%(rQjs=edb?r(`M5^0|#1XAyui&qYXS9@p zeDNCo=6D=Nf(N}~A&&~sf)+XhOgvPPQ!m0@Dal$=O)DRFqf;-rm9@(tSqq-shxulH zJg5MujYpwV^njozUI&{gF!pKs+g+Zo-~)INSOXXs3lMqB$-K<60)iGPW$e>bXI)hu zhDezL(j1anlVc!Hj+mQ6S!(f2{Gb#s?tCBh_tdEkjh-)yh}DBsjMwNuhTT)?>X4DM=S~Ja4q#@rr9iwG59m{NU)XBVi zB8&yVe0Pc#X+oXh=xZZO#^~Utb>kD~wC2D-b%`aI1r);RsP<_{!SIeMTJy47p8eqT zTbOSyI+o^{gNZB2;I9BH0BJ=)TELpT`!lo6Ev|fOB%sb@XrQZ3Qydxmc~E`g!2OMH zTDnEI`gD~I`h6W?md|GmHuMK(tpufj_k8o^oAB%hq$`|n>f33)SPKuES4&XRE)zxvJluE_|PMVZM34zjeN + + + + +]> + + + +Four Wins + + + +Martin +Heni + +
&Martin.Heni.mail;
+
+
+ + +
+ + +19952002 +&Martin.Heni; + + +&FDLNotice; + +2006-06-17 +1.1.0 + + + +&k4wins; is a four-in-a-row game for &kde;. + + + +KDE +kdegames +game +board +board game +four wins +four +four in a row +win4 +kwin4 +connect four +connect 4 + +
+ + +Overview + + +Four wins is a game for two players. + + + +Each player is represented by a color (yellow and red). The goal of the +game is to get four connected pieces of your color into a row, column or +any diagonal. This is done by placing one of your pieces into any of +the seven columns. + + + +A piece will begin to fill a column from the bottom, &ie; it +will fall down until it reaches the ground level or another stone. +After a move is done it is the turn of the other player. + + + +This is repeated until the game is over, which is when one of the +players has four pieces in a row, column or diagonal or no more moves +are possible because the board is filled. + + + + + +Rules + + +The board is separated into three regions. + + + +The game board is constructed out of 7x6 fields +which will be filled from bottom to top. The fields are marked in the +color of the player who made the current move. On top of each column a +colored arrow shows were the last piece had been put. + + + +The status display shows which player color +starts and which color is played by whom (player, computer, remote +connection). It further shows the level of the computer opponent, the +number of moves done as well as the computer calculated chance of +winning. This chance is calculated only if the computer opponent makes a +move. A positive number means that the player has an advantage, a +negative number means that the computer thinks he is better. + + + +The table display shows the number of won, lost +and drawn games is noted for both player. Also the number of aborted +games (Brk) and the sum of games is shown. + + + + + +Remote connections + + +It is possible to play the game over a network connection with another +computer. +One of the computers will act as game server. This one can determine +who should play which color. You can configure the network options +in the menu GameNetwork Configuration... +There you also find a chat dialog +box which allows you to speak with your friend. + + + + +When a network connection is build you are asked to enter a remote host +and a port. The port can usually just be left untouched, but if you know +what you are doing replace it by another number, which has to be the +same in both player games of course. The hostname should be the name of +the remote host to which you are connecting. Only the client in +the connection has to supply a hostname. It is often wise that the player +behind a firewall chooses to be client as the firewall might not allow +incoming connections. + + + + + +Menus + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + + +Starts a new game. In a network game this option is only available for +the network server. The network client will be automatically started by +the server. + + + + + + + +&Ctrl;O + +Game +Load... + + + +Loads a saved game. + + + + + + + +&Ctrl;S + +Game +Save + + + +Saves the current game. + + + + + + + +&Ctrl;End + +Game +End Game + + +Aborts a running game. + + + + + + +Game +Network Configuration... + + +Pops up a dialog for the network configuration. You can choose to be +server or client. If you are server you can also choose what color the +remote player should take over. If a network game is running you can +also disconnect it in this menu. + + + + + + +Game +Network Chat... + + +Pops up a chat widget which allows you to send messages to the +other party. + + + + + + + +H + +Game +Hint + + + +The computer will calculate the best possible move and mark it with a +small circle on the board. How good the move is depends on the level of +the computer. + + + + + + + +&Ctrl;Q + +Game +Quit + + + +Quits the program. + + + + + + + + +<guimenu>Edit</guimenu> Menu + + + + +&Ctrl;Z + +Edit +Undo + + + +Undo the last move. If the previous player is played by the computer two +moves are taken back so that it is the player's turn again. + + + + + + + +&Ctrl;&Shift;Z + +Edit +Redo + + + +Replay a move which had been undone. + + + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + + +Settings +Show Toolbar + + + +Toggle on and off the display of the toolbar. + + + + + + +Settings +Show Statusbar + + + +Toggle on and off the display of the status bar. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + +Settings +Configure Toolbars... + +Displays a &kde; standard dialog where you can configure the toolbar icons. + + + + +Settings +Configure &k4wins;... + +Displays the &k4wins; +configuration dialog with these options: +Starting Player Color: Determine which color player has the first move in the next game. +Player Names: Change the names of the players. +Yellow Plays With: +Choose who should play for player 1 (yellow). It can be either +Mouse or Keyboard, +&ie; a local player using the mouse or keyboard +as input device or Computer, &ie; the computer +plays for this player. +Red Plays With: Same as Yellow Playes With but for player 2 +(red). +Computer Difficulty: Select the level of the computer player. + + + + + + + +<guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + +Credits and License + + +&k4wins; + + +Program copyright 1995-2002 &Martin.Heni; &Martin.Heni.mail; + + +Documentation copyright 2002 &Martin.Heni; &Martin.Heni.mail; + + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &k4wins; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + +&documentation.index; +
+ + diff --git a/doc/lskat/Makefile.am b/doc/lskat/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/lskat/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/lskat/index.docbook b/doc/lskat/index.docbook new file mode 100644 index 00000000..198d3ff6 --- /dev/null +++ b/doc/lskat/index.docbook @@ -0,0 +1,467 @@ + + + + + +]> + + + +Lieutnant Skat + + +Martin +Heni + +
martin@heni-online.de
+
+
+ +MikeMcBride +Reviewer + +
mpmcbride7@yahoo.com
+
+ + + +
+ +2003-09-16 +0.09.01 + + +This documentation describes &lskat; version 0.9.1 + + + +KDE +kdegames +game +playing cards +skat +lieutenant +lskat + +
+ + +Overview + + +Lieutenant skat (from German Offiziersskat) is a card game for two +players. It is roughly played according to the rules of +Skat but with only two players and simplified +rules. + + + +Every player has a set of cards in front of him/her, half of them +covered and half of them open. Both players try to win more than 60 of +the 120 possible points. After 16 moves all cards are played and the +game ends. + + + + + +Rules + + +The game has a randomly chosen trump card. It is shown as a small suit +symbol in the upper right corner of the score field. All four Jacks are +also counted as trumps and do not belong to their +suits. So if heart is trump all seven hearts as +well as all four Jacks count as trumps, making 11 trumps. + + + + +The Jack of clubs does not count as club but as +trump. + + + + +A special trump is the Grand. In this game no +suit is trump and only the four Jacks count as trump. In the game this +is indicated by the symbol of the Jack's head. + + + +Every player owns 16 cards. 8 of them covered 8 of them open. The start +player can play any of his/her open cards. + + + +The second player has following choices: + + + + +The second player has in his/her open cards the same suit. +He/she has to play one of this suit then. The player with +the higher card (see table below) wins both cards. + + +The second player does not have a card of the played suit. +He/she can now play any other card. If it is a trump the +second player will win both cards. Otherwise the +first player will win both cards. + + + + +If there is still a covered card below a played card it will be uncovered +and is from now on an open card. + + + +The player who won the cards can make the next turn. + + + +The sequence of the cards (from the highest to the lowest - +holding for any trump): + +Jack of clubs +Jack of spades +Jack of hearts +Jack of diamonds +Ace +Ten +King +Queen +Nine +Eight +Seven + + + +The player who wins both cards will add their value to his score. The +values of the cards are: + + + + + Card Value + + + Ace 11 + Ten 10 + King 4 + Queen 3 + Jack 2 + Nine 0 + Eight 0 + Seven 0 + + + + + +Scoring + + +The game is won if you have more than 60 points. With both players having +60 points the game ends in a draw. + + + + + +PointsScore + + +601 +61...902 +91...1193 +1204 + + + + + + + + +Remote connections + + +It is possible to play the game over a network connection with another +computer. To do so both players on both computers have to select one +color played by the local player and the other by the remote player. Who +chooses which side does not matter. It even does not matter if both +choose to play the same player as this will automatically be +interchanged by the game. + + + +One of the computers will act as game server. Only this one can start a +new network game. Also all its game data will be transfered to the +client computer. You can force your computer to be server if you do not +supply a hostname in the network dialog. This dialog pops up if you +start a new network game. As a client you enter the hostname of the +machine on which the server is running. The port does not usually need to be +changed, but you can enter any valid port number there. + + + + + +Menus + + +<guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Starts a new game. In a network game this option is +only available for the network server. The network client will be +automatically started by the server. + + + + + + +Game +End Game + + +Aborts a running game. + + + + + +Game +Clear Statistics + + +Clears the all time game memory. This statistic is +usually saved when the game ended and shows the amount of +played and won games per player. This is cleared with this menu +option. + + + + + + +&Ctrl;M + +Game +Send Message... + + +If connected to another game in a remote session this +option allows you to send a message to the remote +player. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quits the program. + + + + + + + +<guimenu>Settings</guimenu> Menu + + + + +Settings +Statusbar + + +Shows or hides the statusbar. + + + + + +Settings +Starting Player + +Selects whether player 1 (top) or player two (bottom) +begins the next game. + + + + + +Settings +Player 1 Played By + + + +Chooses who should play for player 1 (top). It can be one of the +following: + + + + +A local player using the mouse as input device. For this option, choose +Player. + + + +An artificual computer player. For this option, choose +Computer. + + + +A remote player, who is connected to this computer through a network. +For this option choose Remote. + + + + + + +SettingsPlayer 2 Played By + +Same as the Player 1 Played By option, only this refers to Player 2. + + + + + +Settings +Level + + +Selects the level of the computer player. + + + + + +Settings +Select Card Deck + + +Choose the face and back of the carddeck used for playing. You +can select these in a preview dialog showing all carddecks installed for +&kde;. + + + + + +Settings +Change Names... + + +Change the names of the players. + + + + + + +Settings +Configure Shortcuts... + + +Open a dialog which lets you redefine all the keyboard shortcuts. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + + +&help.menu.documentation; + + + + + + +Credits and License + + +&lskat; + + +Program copyright 2000,2001 Martin Heni martin@heni-online.de + + +Documentation copyright 2000,2001 Martin Heni martin@heni-online.de + + +Documentation converted to &kde; 2.0 by Mike McBride +mpmcbride7@yahoo.com + + + + +&underFDL; +&underGPL; + + + + + +Installation + +&install.intro.documentation; + + + +Compilation and Installation + +&install.compile.documentation; + + + + +&documentation.index; +
+ + + + diff --git a/kasteroids/ChangeLog b/kasteroids/ChangeLog new file mode 100644 index 00000000..ccccd0c2 --- /dev/null +++ b/kasteroids/ChangeLog @@ -0,0 +1,35 @@ +Version 2.2 + * [Martin Jones] Show "Game Over" and stats in the game window instead + of displaying many annoying dialogs at the end of the + game. + +Version 2.1 + * [Martin Jones] + Shooting uses energy too. + Added firepower powerup. + Adjusted speed of asteroids to make game playable again. + +Version 1.90 + * [Martin Jones] KDE 2.0 and QCanvas port + Introduce ship's energy supply. Thrusting, shields, + etc. use power, and when you run out you're basically + screwed. Fortunately some asteroids release fuel + when you destroy them. + Make brakes, etc. permanent features once you catch them. + Made ship much cooler. + +Version 0.3.1 + * [Kristof ] + Add brakes, shield, teleport, configureable keys, + highscores. + Also the cool shield graphics. + +Verison 0.2.0 + * [Martin Jones] Use QwSpriteField-1.4 from kdesupport + * [Martin Jones] Preliminary sound support + +Version 0.1.0 + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Renamed Kasteroids.kdelnk to kasteroids.kdelnk + * [Robert Williams] Added -caption "%c" to kasteroids.kdelnk + * [Robert Williams] Added getCaption() diff --git a/kasteroids/Makefile.am b/kasteroids/Makefile.am new file mode 100644 index 00000000..3ac5f46a --- /dev/null +++ b/kasteroids/Makefile.am @@ -0,0 +1,26 @@ +SUBDIRS = . sprites sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kasteroids +kasteroids_SOURCES = main.cpp view.cpp ledmeter.cpp toplevel.cpp settings.kcfgc +kasteroids_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kasteroids_LDADD = $(LIB_KDEGAMES) -lsoundserver_idl -lqtmcop +kasteroids_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +picsdir = $(kde_datadir)/kasteroids/sprites +pics_DATA = bg.png + +xdg_apps_DATA = kasteroids.desktop +kde_kcfg_DATA = kasteroids.kcfg + +EXTRA_DIST = $(pics_DATA) +KDE_ICON = kasteroids + +rcdir = $(kde_datadir)/kasteroids +rc_DATA = kasteroidsui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kasteroids.pot + diff --git a/kasteroids/bg.png b/kasteroids/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..e4c5adc0c127f290283316f38e2c637810346a5f GIT binary patch literal 7843 zcmZ8`cTf}W_w|Mvq!=(FO^QT_8fivA2vv%Rp{P+%gA{2>m(WW4fsz&-b5q=DoAYZgzGi^W1&zx#yhwz{bj4c%Rfh004w7&Y4^U z00?*q0iayqi2*ZE2TwfiMpz>Ns7MpscH#wp3;3P0zY73@qW^s$(YJ19fH(OAEwHBi z1Kh&gzyYN=?Lq*M)vz!zx*U?X@T@0oqHC3w#G#bbRUCd#Ix}bp8(n7eYr^Dv6riy; zxZL?X-NSy)nx0JLtm#HX<88vEEl`=Jvymd)*t#5)?JC8g<&k=n>OHeMjlEvROs+s!bRBvH)%DGGjocsSk$CRT&Ml1!Z_ck@ zza}T&s|n$7^hz}Ll!aJKa0X=F3x`|PgOQJ=1OcP#r?HpduPDb&ZP8d=J-ugr)1`A7 zT@=a}^upAXn|{F|Q(Gnh&&1(U3}r7c-t+e@H?u2XDcU!Hh*6qYv1bm2(pbu>O?e8r zz^F#iYZoY#kCiBT0NS$1Nusgzv8x4_TA;`wVy_!j;;sxyy@+yLd;>*KBo|Y_Kx3(D z?r%K` zwTEfP63Ofs7*^Dc_haQJ!mvCPjm^EFlw3g~k-pu2HK((d7*&BvdccZ>o7&^U-#%~4iXPF5dXzklQIACDZ({y(Z6 zspaxe3T0m`p!(k71|QsXbM|f7)N+G2i#^gZ7p`5Y_Vcg#(!D8?KD}UNZSBqS(T`X0{E(UQ+6ITy$_3oU@7y$4$!0Jv zQPtq40#v`zxyP>daMPS3BbfYq?maWx>T4w`s;XIto@ER)R@5sjT!82#aWiuDTis}s zZf1CX1Pb2A;RuB=#SQq@12VF2dbIYtk9~IO9I1!+54!t(eTR(9rvH9z zT`=3|P{vgB6QI_Mtz=J&d#PHb<@mC=7!-cS2> zETV-3sCA7snEM^l-&h_#xGe3PKt^I2D0(^0**6Z;9j)PG3jE05*`Di2EJdEjGhOX} zkud+r!n2JkiOww!X?@*vIjQ$VQXM8*`D#EV>Jpr{(KUbCGe^ln%rs+L(7-*t_PUJ$ z*Y#WZ(+RqiIjK<|m-)p-6?!G6uR@pR-+ZlPce!8>ZT}R;NR0jvnKLq$#-&=T1?g^= zj?Q%*>g`Q&wTA)o?bI;TML{5YZQ5Wu?l3q*58f9*4dAiR&TQa^wqN%lYcYE{h za^x9$rFN>h-{f$-fpzmpMY-@pxn{{ZCt!K5d= z3RQAUFi)z~Hdp(TAHMzb7H*K??0y?_GswCDl#90Q*j4ycXqAi+@QMhFuUZd_Jl*+s z4zWi@K3vx`vgnrhNDeGZRkk?toPOzYwcM?CG?wNc1dnH?(*G$E&eKw@qaW8P#8^K3 zLPnlv$L#Mj@}Z>9ZM3pOWvdHC>$OJ=Zy)q9Z`9K5ZulXPx6{h@om<9j5&kmEVzLyL25)I&e^ClQ}#yI)`{V;)1H9^wlLhhJ@#@FhNT)n6P*!ghl6}D2F1B+EWBXTjr(IT%&#^VDVfMp?d}b<*urZy7)L2lf++NTWZMl~2@8 zeM_nI)}>ee4hW5m9QF;g!840IjXAgV#l(n3$>D4F&{!-3><#NfeX@i|Ti%3oygET4 z76n=(mzbxIx2)-NV%nY30U6yEPOS)$hxkY8&AS$krg{js7}13k1jKG=TwDOS4y#hL zqKxI6>lB6p9VceKuh1Lwa)i>Fexx>sB`G zH1yr7%`GW%UU#uRT73t`j&GaFGfv`2n$y zdl%uRGBnSe&J5HzW~k?YP*41Y<&m!#e#wHwf>OkG`g`f>jNvq_Ar5D?&5tgGB zesI)zc0?v~wy}nvjOF*`4HNA-%1LT}5iGQGFWhA6&3=)~G5MqeB7#f;0sojX*MrO$ zJsmhEBER{1>quaNa(jkb+&4n+U8e^wQ|EYz>Rm2@99igq^2OJ0YvR0F$?7zJr7R0P z(>HMBs9E0t?!KXC=*Y@>+&5%m_p8Y2WP9G6Ubp!H-(Kq$9PSwyd3j*6>pj7)qVgCM z@OB~JAntPV6<})ljKkQ=q-F$`QMf6OOH-c;b->}8XK=Rg5M>vTU8o6#Z$nM^lP>{Y ztWr6rw8bY2Vu!cqn_ABN#VBnn%$U;JHx`K5uKrH#+xoh?R3&23)%ErDt5>f^texuG znvJ)-@@7$TTBN);sGR#o8~wKiT?5aQbgBDaB&%QCNz0n3eC-KCln^2;>J&Mcmej8#zS{ zJ1ixcGGH<^fBMYHy#TW3k3gN(NV&aXt9R;HhQG|L=y#~Y3{)xFSn~_I21Q?wL>iRe zQM~^GMMq;z7g*-U0px-m^Rxi58ss(fN);6qn-&?~GY3sK|Bh3`TEq#D6%PWJ7w)-( zkO3>ZnC*(g{q2>GAJl78-qQ;cdHHcVIlo29?=KGhIMIvDQ00{}9a|ZaTLr6&fLD5Opj0Osb0n-)HV# zUl6{!p%-zXHes0v4E#u{OWuR~KNEO}>7ud}Xs6mw&5iWc*mSNyq1iDrj5Y4|q$=-) z1XrHlM@=b)Pj()D4aRtpko(~}+%VFqSmB#50Y^QFz;7PTs^@@RIaAwynLLoc-p-$X z2~CkDB#oDbIa_@%@;u@x+#%h44Hn>PY~{B-ta3!N|7e*fNdkd$l5GEa75A+Y)w8qv(7(oTr0v67v8xQhWl0 zBt&vu%W;>UJ7YSObnlQ?tsTn7h-b5AVK`MTn$={=N`MoM3=%{g?Z0b{ zMlAPgjph{;G?gYj?8Nl3lJZG%j{Umrw*1r-`3M}7FuJasB@h6$Rimx%{HG7jO7?=p zxM;?$O4O&I(5|rKRBREdxlZ^-dign@^?Sxw<23cB`sEJKRho*mBHq-}GwzpdwYJ%z z#FIXFX)@3!g{VzUP1XhaHOxg%q3a&*!Q_aF+95B^;?2|}l}sj6OG~RnQ&)QBEC29s z`|}}=QejB;UB^dt-nv-E-tKzaSI$IIU>IMzcZbC9)Q7H;w~;mzRd|>@;4!iEVesr5 z@oRaa&mX0(}G~YCrt~kqalupJcTIwxLFm3w7t~C$k-PrzC zr@9Gs{FyrMnk*Tg;VPNtF0(a%EYbPl54<##1sNf_r>@{4R*?Xe6`cB(J2czBw7!Xo#ch-XA+R%7gYY4kzB)b{h|&t9xuu;P~m z-d9bf8zOoj!2SCJ!NSMwEmOa=rca*PA8Qr`J{Yyf8I9&F)D_#c+)ycKBfZ9c)TF{b ztEAb9wfNNoj%#hvd%N4)GER17J;cdwrKcv3=S#T2Y6Sr*)ZN|vX7tYL=H{lC=0qDo zW_oH$38eNyAFdpgPmNi0IlzDO=R*l$WYd>b?D`IO$PYG~Z4kK?XLY)`quQY?dh_?= zw(Z}lAkaEV$mYd$l&q3=HXV|?7SD*-c#sgw(hS|o} zh6X3y3q^+wq&NSjJzdoG2@IYZw0$dS#1Q=j*f`1AZ{xEM!sP)c3C%X>VCqGsoyiaz z+&46qNdR}Fi&u+7uoAb}$+|@HpGHvXc(W2N&xM6?#C^gvncmA#vZ=_Y+Oe zvU&NdTmoC>!Gyq;Fwi2wTR;Q{^mYFY6mb*8$q?HC#wmIL>C$X0z z*uy^?-63P+8y*!gfh815CPIJ;OVLCKz4GC*zAzUqA7INoo$2;`Jt*j={dvXjgl7l~ z91cp_7b|a(Y5Q4BU{0|IFqk21-?}9VtPRE~TZnZHf?E^baOkuaC4F70WyA2O7*PAD zLH7^H_Ez(zXWV`=%puWzpjhY3V;;yjIliC9OVn0uzbNn;Rm@BZd&|DWk9ib zfH>omGnMrhhT7gZxkb6FsCqcb-ujN50MW5LMdD>yqiHr+QK{jHPKz2 z4i~LWbY?6Hsd=bzM1S!1mt^Qk)qkvv`*$hZHK(YFy^-zeZEhh-PWR!LIKonhO!#AF zqlJ-9Bsk(Bbh?bTL`XxU-}*WDyLtQXDM}K$3BPmM$s!rTEan{g@Qh7E31f6 z^Y30Tp8V;K(tS0@=^B#xdw!mQ)f!lanvm^T&(5sws?_lw88!d>{Cv>dpsE!yjz#wc zeV$V}^xlY!1myylz-*aRadTM>M7CL0mwz7jg&&;*=w1Szsuq^4JR!9QLJAlW`srxK3aLgq%G!VXvX|&$DjN;SJHV$MsLXFg#JiIE`ea!S1B~j=5RO&i;d>K%K5dmwZ}K3Jd1N* zc}m4Q(Qqzlac6lzk=?i4FB`A7wPxF6{`b#FTyQuoP?fIHclVD(q-PgWykPpw@x0;T zVZ4LGzJZUEeUj-kME#+reSikWi`CWE?e^>D1J9i0O$_$Z*AdOXr8eYvVERm~+U)A2PFa zN2)(Qn8iBI?R;8&vt}`P_3>PWk6C9#);Jed>g~3#1nIQbnz+apvEbI5V#PFZ@*d*>fwT7%m*?DulHd zLaJX`Si7QeDl=J)Y-uyiRpibw1YT z8KolKY)>-i95F;)>`H0cU$hg552gZ7`e=IE@U1B{}&@bKP z9TV7A_%}4lboot?32Wetr7IxjfvrPCW!}3XNuJ+>6QplTuMyTe6z(=trB_q|)|f)T z$>LFW9g|$X3e_+6*Fts-C9l9UV+ItNBJu~(oHWQx5THad5*BTXcS_fF{&`Mn-vAr1 z@fW?{ZF{G9bP2Yg@B&zA`g1%nmdBt=!p-2faH{Y!{e^HD&NL!M(;_9}L zORWD$Om-(m8HMqeX*_eaXAAQO@w=VaoI`%(UI0{yyLWAr<15#={{%#)IiKsNhDoCK zxGBEeA=)P^6sx(L&!>4S@8@#|oP$01LmqX^h#|jBaas{AclbO0iZ^^7+tJ^W?Y6SZ zKAqbuo^*dYwETv!W$2vX5A~a_h9as|HIOR~4GqoA9P`boy;c%bK@zF1dyG5>IG6^w z9O26LVrdFdD^X0s^ztV7kVuDzBk%PqIYqvMm3%)~mT&}6c4YHknyc(9chRy8*CDqm zBN|`*a(^LbLE}{aN~27}<#a&k(SrC-Ckd<93^J08?1VVhTr0Wq7O$y3@?lr|`Psh` zhA~!o%p@{2*0IHbEdh%;KK+tQlv;#L>~*`Rc~ZA@2N>4BemM72I*?4NMEN5I@2s(E z@ywbc&$H?qiLda#)2@aoB0#f0r1t`21;f^E=`Im$SC=cl#(Ruz9aSHGTG+?*8@g95 zc_t8ap40m8|19m7s|py>%<73#4+;-&+M(86zeJ(jX4RI?h3pUe@f4g&D7k#{mtmkM zw9O_!#|5FqV?ZMeH!FyRjyhOL?@MgQpqkYR-`Db{5Dci|t*|aL z-pJQNQ0L!74VzPZW0JGG))zb)K(D-z8Am!7=LA&^I6+<5N3XQ`>=JzKC#2k*`UzF* zt@GvEP(`*+e%~}01bleDyZ!fohKK;M!HoZij>Uh&2@;9F$EBdPJ5Z;kzTO5$sIdDv zI?CR->V1;juK`+yRwvig06{+8kMryg&P%M(-5||eS9`uwsjl|To-?h6g2xk`%kI61 z%gfJ?+S^%YkG9=JiQ{nho$4o)#f||smLUa#|9M?s35OrtAB2UJ`(#PaG%hxlQYfId zm1_Rvku$~Fb*|9X)6>({1*a9Y-Ub+qi7lK5BGO8sNX0{XqsnMWEh`I*s>usVS#nVu z<0SA4w7HeD9=O^kl6j2E=_{~zTuYx-U_gZ2e8qSDvaQj-Dz)CK;z^CA<9ojP^F~CX z+zR;~BQaQwxVfh$>=1wcUHG{6=cZt`O}!W)599#r!@;91(R;E3V<3^w*Lr59s;YX> z)E}>fWynKIOG`V|A_7ilaJ9Dq5ofh`jm2!-jk@oFsU3}6AMWX~$SJBCnxCMh1qTQB zs-8FnrU;yNL0r75hMpA7Xp4k@ay43Eb>xnhyBWCa4(1j_K9?@Y%fnzW^L7eFkIt%n zcT)kt)%>I6<>hUU?~e}I+tqVxUsx#d%-P$XHek*XmXr3!_PX5-4V9#+i=VO!$SID! zcM9>|fBXHH=+MvxP`>42ZQ)rxlT%Yu?C3pCn!#>8-L~G;(iw;I)6x87je~zcV_CIe z!Lp;)o7io8QU4O=8k^&v&oyp0EBw8bVB9^Mw*TcPq+zGs~P^Y2mC0C4Kpq)ik z`-cJD`?xnt}(jq zUL|??88}>{|CZa^pn>Zjjpbt<>AX{$;y5I31vlmBXDjehUwMWR2(A)lIYpuQG=DI$ zd!c3BuVqDBuUR%RSGyN;_P-uJ1#AOckN%;tFTknTLDQw>K7+mOAdKIdN;aXJ9QaDQb8y*kBd1&2HXWiEdLlMt3SDuk!dV# zoF;?=;&S0E7Zx}0EM2JuldjN!ht($@D0h+|;7LviTi%iTQyi0zQ)`6FzHpl&6Uk1i zH>C6sK*_x;mfbr0sp)QQAkGFtj32=cvexYq_F^bk2=Dt9RcQMJ41?2wbKUa2g=ga~ oN1u6q%FrBofe)PR!l-+Y{H&>OTzqtJdJF&-rdB4U#*VT72euLdZU6uP literal 0 HcmV?d00001 diff --git a/kasteroids/configure.in.in b/kasteroids/configure.in.in new file mode 100644 index 00000000..17ee6ce2 --- /dev/null +++ b/kasteroids/configure.in.in @@ -0,0 +1,5 @@ +dnl KDE_CHECK_QWSPRITEFIELD(DO_NOT_COMPILE="$DO_NOT_COMPILE kasteroids") + +if test "x$build_arts" = "xno"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kasteroids" +fi diff --git a/kasteroids/hi128-app-kasteroids.png b/kasteroids/hi128-app-kasteroids.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5307ffd12faaff8acb4c69e10f553016672d0f GIT binary patch literal 17919 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 A*#H0l literal 0 HcmV?d00001 diff --git a/kasteroids/hi32-app-kasteroids.png b/kasteroids/hi32-app-kasteroids.png new file mode 100644 index 0000000000000000000000000000000000000000..7416e7fb602d08a63db89241b6b5d33a8106d613 GIT binary patch literal 2331 zcmV+$3FP*PP)tElMqaH-ei-Ytdy0A2e5-;NJ3CnQ4qlf69~3jVB^G&?O0OO8d|<;-M&-b@t@8) zRi~ybTJ52c6Q@a1P!v^A-}}@@QQuo|=S@8O?6V3GZ}&S3awmH(U%srKfBwb#%9Zb~ zLVRyo)@P0B`j=mR;j34!Tv`7=16W;MJ^sK0@Bht*9(m-q-+lhVuP-kz+>_@;C8Q$w z!0zsi&F_5YThCm*`ug|Yc;mJ2Z*FdG{I38!{P2fdmsGNr#}9t z?|a|FzmsKIW{g2dfs~TP#RaOWqIZrE0$tm2bTp!ETdJx=Nkv{1WLh&BkKerf{5QV* z%rk%UC7}7G03Lnxu}h!)pGnGIM=hkcSsb8d(W>jJT{~?7E;1{$L7{H zAw&jsMT(J)^>rp)&;9qko7L5KvG(J&Z$I_alb3dOcHX+BquxpUqaXd)AO8ODefqBx z4BEEE_C0OWGMmlVxv|S+GNJE1#v1zGlSs5(3rIrnv`vR~4k0D`2P59PzDeI(&Ye4V z_j}%R-v_?=&96TTxOW7os_OJ-KJ!O^pJ32+Eh!~hW9f`xHk%PcxY=VMk&sdnW5hd; zHJ0&Y%49mDEOH`(`QaQ=z#DI@;k@I*g$t{Dd%IWH*RQ<*Aa4hF@WG3}A+niES)RCUE*egTB$;NXDe6DL0iECTyK2k^ubPdtjOAK%*Erfm&A z1SZoNDJ6Q>LyQn3BK^!rB=~?a9k%T-ou%tNy$>KHw(nV7IL`cVp44{4l+c+vy|i@l z+`+-lPXV5O`sp)kZ{2vRGqZejG-lSdIOoZsE|P)W!pTMVXKC^JU4M^x=TN`lS{N|_MeA)+Vr7TY!8EFnc=5-1T7LXw0ar9`GP zY-f(v)?WXL2Do(T(yvZt{U3}bV-Ai+OvYnu*P)U_=1>hZ8J;@zhyap^C`+gYlo==q zN=1b0p_D``XoEw!4(A%OkRh{#*NP^Al#1oMRz3nOYCs&0+D{LLv2L1{>12v+r-DB84K$9<7np4DWF6`X-%#)S*{2|5u6|;iz1*Z%}iCydQaQ?n`^#D1w-s%HiN1v z&n+*XdO!n9^9v__Yt}YcYssQR78%A#lCgwt3?`!-z7aA!O^eu#lO9>%`)P=rB4+evk5aNolErsxuwZ__raxF0$gbl<- zl0-qK=ctRCOlyI%lCBk(wj09SR9DqZAoR<~RX;uvilb(jx?TspxytXeo<1 zYps%@r%XLcdTORrG#Ke9lY=f0q9kBIEFjAQtrR4QZ47pFNF48xnlUOyq|gYNAyk1# zktiY|d0f;R(7SkI{ibR6_A=T1x@|6X$#UXj2sYF7>KnjlJ13G!u_NUXo_~G6cu8#d*0^7ep z=NAG11Ym!+aW8%C)!iTc;QHZ>?Qv(v#`{^MZ-jJX<9gp3A&iy1wW8?^8wca=xtF$n z@Wp@K`uzIT{~fUPUugW&U$dN>9_}QtqF^q|WC#$uPTlWI+ z8Zi3r0^iB!RB|(Ox3Z^z>}Tc%=zw?!vH$1r)|$*lkY002ovPDHLkV1jg1 BarXcK literal 0 HcmV?d00001 diff --git a/kasteroids/hi48-app-kasteroids.png b/kasteroids/hi48-app-kasteroids.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce848f8d5bf2b1050bfe2bdb0c968d22f67826b GIT binary patch literal 4360 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 DBu; + + + + + + true + + + + false + + + + 3 + + + + + + true + + + + Explosion.wav + + + + ploop.wav + + + diff --git a/kasteroids/kasteroidsui.rc b/kasteroids/kasteroidsui.rc new file mode 100644 index 00000000..8305e3d6 --- /dev/null +++ b/kasteroids/kasteroidsui.rc @@ -0,0 +1,6 @@ + + + + + + diff --git a/kasteroids/ledmeter.cpp b/kasteroids/ledmeter.cpp new file mode 100644 index 00000000..3df87b8f --- /dev/null +++ b/kasteroids/ledmeter.cpp @@ -0,0 +1,118 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#include +#include "ledmeter.h" +#include "ledmeter.moc" + +KALedMeter::KALedMeter( QWidget *parent ) : QFrame( parent ) +{ + mCRanges.setAutoDelete( true ); + mRange = 100; + mCount = 20; + mCurrentCount = 0; + mValue = 0; + setMinimumWidth( mCount * 2 + frameWidth() ); +} + +void KALedMeter::setRange( int r ) +{ + mRange = r; + if ( mRange < 1 ) + mRange = 1; + setValue( mValue ); + update(); +} + +void KALedMeter::setCount( int c ) +{ + mCount = c; + if ( mCount < 1 ) + mCount = 1; + setMinimumWidth( mCount * 2 + frameWidth() ); + calcColorRanges(); + setValue( mValue ); + update(); +} + +void KALedMeter::setValue( int v ) +{ + mValue = v; + if ( mValue > mRange ) + mValue = mRange; + else if ( mValue < 0 ) + mValue = 0; + int c = ( mValue + mRange / mCount - 1 ) * mCount / mRange; + if ( c != mCurrentCount ) + { + mCurrentCount = c; + update(); + } +} + +void KALedMeter::addColorRange( int pc, const QColor &c ) +{ + ColorRange *cr = new ColorRange; + cr->mPc = pc; + cr->mColor = c; + mCRanges.append( cr ); + calcColorRanges(); +} + +void KALedMeter::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + int w = ( width() - frameWidth() - 2 ) / mCount * mCount; + w += frameWidth() + 2; + setFrameRect( QRect( 0, 0, w, height() ) ); +} + +void KALedMeter::drawContents( QPainter *p ) +{ + QRect b = contentsRect(); + + unsigned cidx = 0; + int ncol = mCount; + QColor col = colorGroup().foreground(); + + if ( !mCRanges.isEmpty() ) + { + col = mCRanges.at( cidx )->mColor; + ncol = mCRanges.at( cidx )->mValue; + } + p->setBrush( col ); + p->setPen( col ); + + int lw = b.width() / mCount; + int lx = b.left() + 1; + for ( int i = 0; i < mCurrentCount; i++, lx += lw ) + { + if ( i > ncol ) + { + if ( ++cidx < mCRanges.count() ) + { + col = mCRanges.at( cidx )->mColor; + ncol = mCRanges.at( cidx )->mValue; + p->setBrush( col ); + p->setPen( col ); + } + } + + p->drawRect( lx, b.top() + 1, lw - 1, b.height() - 2 ); + } +} + +void KALedMeter::calcColorRanges() +{ + int prev = 0; + ColorRange *cr; + for ( cr = mCRanges.first(); cr; cr = mCRanges.next() ) + { + cr->mValue = prev + cr->mPc * mCount / 100; + prev = cr->mValue; + } +} + diff --git a/kasteroids/ledmeter.h b/kasteroids/ledmeter.h new file mode 100644 index 00000000..ea9b7e96 --- /dev/null +++ b/kasteroids/ledmeter.h @@ -0,0 +1,54 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __LEDMETER_H__ +#define __LEDMETER_H__ + +#include +#include + + +class KALedMeter : public QFrame +{ + Q_OBJECT +public: + KALedMeter( QWidget *parent ); + + int range() const { return mRange; } + void setRange( int r ); + + int count() const { return mCount; } + void setCount( int c ); + + int value () const { return mValue; } + + void addColorRange( int pc, const QColor &c ); + +public slots: + void setValue( int v ); + +protected: + virtual void resizeEvent( QResizeEvent * ); + virtual void drawContents( QPainter * ); + void calcColorRanges(); + +protected: + struct ColorRange + { + int mPc; + int mValue; + QColor mColor; + }; + + int mRange; + int mCount; + int mCurrentCount; + int mValue; + QPtrList mCRanges; +}; + +#endif + diff --git a/kasteroids/main.cpp b/kasteroids/main.cpp new file mode 100644 index 00000000..361c9c77 --- /dev/null +++ b/kasteroids/main.cpp @@ -0,0 +1,51 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ +#include + +#include +#include +#include +#include + +#include "version.h" +#include "toplevel.h" + +#ifdef KA_ENABLE_SOUND +#include +#endif + + +static const char description[] = + I18N_NOOP("KDE Space Game"); + +int main( int argc, char *argv[] ) +{ + KAboutData aboutData( "kasteroids", I18N_NOOP("KAsteroids"), + KASTEROIDS_VERSION, description, KAboutData::License_GPL, + "(c) 1997, Martin R. Jones"); + aboutData.addAuthor("Martin R. Jones",0, "mjones@kde.org"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + KGlobal::dirs()->addResourceType("sprite", KStandardDirs::kde_default("data") + "kasteroids/sprites/"); + KGlobal::dirs()->addResourceType("sounds", KStandardDirs::kde_default("data") + "kasteroids/sounds/"); + +#ifdef KA_ENABLE_SOUND + // setup mcop communication + Arts::Dispatcher dispatcher; +#endif + + if( app.isRestored() ) + RESTORE(KAstTopLevel) + else { + KAstTopLevel *w = new KAstTopLevel; + app.setMainWidget(w); + w->show(); + } + return app.exec(); +} + diff --git a/kasteroids/settings.kcfgc b/kasteroids/settings.kcfgc new file mode 100644 index 00000000..f8d9e21d --- /dev/null +++ b/kasteroids/settings.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=kasteroids.kcfg +ClassName=Settings +Singleton=true +Mutators=false diff --git a/kasteroids/sounds/Explosion.wav b/kasteroids/sounds/Explosion.wav new file mode 100644 index 0000000000000000000000000000000000000000..7b140b1c69effb447b5a15f54098de83f35e30c4 GIT binary patch literal 18427 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 + +#define ID_ROCK_LARGE 1024 +#define ID_ROCK_MEDIUM 1025 +#define ID_ROCK_SMALL 1026 + +#define ID_MISSILE 1030 + +#define ID_BIT 1040 +#define ID_EXHAUST 1041 + +#define ID_ENERGY_POWERUP 1310 +#define ID_TELEPORT_POWERUP 1311 +#define ID_BRAKE_POWERUP 1312 +#define ID_SHIELD_POWERUP 1313 +#define ID_SHOOT_POWERUP 1314 + +#define ID_SHIP 1350 +#define ID_SHIELD 1351 + +#define MAX_SHIELD_AGE 350 +#define MAX_POWERUP_AGE 500 +#define MAX_MISSILE_AGE 40 + +class KMissile : public QCanvasSprite +{ +public: + KMissile( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { myAge = 0; } + + virtual int rtti() const { return ID_MISSILE; } + + void growOlder() { myAge++; } + bool expired() { return myAge > MAX_MISSILE_AGE; } + +private: + int myAge; +}; + +class KBit : public QCanvasSprite +{ +public: + KBit( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { death = 7; } + + virtual int rtti() const { return ID_BIT; } + + void setDeath( int d ) { death = d; } + void growOlder() { death--; } + bool expired() { return death <= 0; } + +private: + int death; +}; + +class KExhaust : public QCanvasSprite +{ +public: + KExhaust( QCanvasPixmapArray *s, QCanvas *c ) : QCanvasSprite( s, c ) + { death = 1; } + + virtual int rtti() const { return ID_EXHAUST; } + + void setDeath( int d ) { death = d; } + void growOlder() { death--; } + bool expired() { return death <= 0; } + +private: + int death; +}; + +class KPowerup : public QCanvasSprite +{ +public: + KPowerup( QCanvasPixmapArray *s, QCanvas *c, int t ) : QCanvasSprite( s, c ), + myAge( 0 ), type(t) { } + + virtual int rtti() const { return type; } + + void growOlder() { myAge++; } + bool expired() const { return myAge > MAX_POWERUP_AGE; } + +protected: + int myAge; + int type; +}; + +class KRock : public QCanvasSprite +{ +public: + KRock (QCanvasPixmapArray *s, QCanvas *c, int t, int sk, int st) : QCanvasSprite( s, c ) + { type = t; skip = cskip = sk; step = st; } + + void nextFrame() + { + if (cskip-- <= 0) { + setFrame( (frame()+step+frameCount())%frameCount() ); + cskip = QABS(skip); + } + } + + virtual int rtti() const { return type; } + +private: + int type; + int skip; + int cskip; + int step; +}; + +class KShield : public QCanvasSprite +{ +public: + KShield( QCanvasPixmapArray *s, QCanvas *c ) + : QCanvasSprite( s, c ) {} + + virtual int rtti() const { return ID_SHIELD; } +}; + +#endif diff --git a/kasteroids/sprites/.pbm b/kasteroids/sprites/.pbm new file mode 100644 index 00000000..e69de29b diff --git a/kasteroids/sprites/Makefile.am b/kasteroids/sprites/Makefile.am new file mode 100644 index 00000000..f11d78f9 --- /dev/null +++ b/kasteroids/sprites/Makefile.am @@ -0,0 +1,36 @@ + +SUBDIRS = bits missile rock1 rock2 rock3 ship powerups exhaust + +POVRAY=x-povray # Must be Povray 3.0 + +#all: pngtoppmpgm + +#clean: +# rm -f *~ pngtoppmpgm + +#distclean: clean +# rm -f Makefile + +FILES = `cd $(srcdir) && find . \( -name "*.png" \) -print` +spritesdir = $(kde_datadir)/kasteroids/sprites + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock1 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock2 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/rock3 + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/ship + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/bits + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/missile + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/powerups + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/shield + $(mkinstalldirs) $(DESTDIR)$(spritesdir)/exhaust + @for file in $(FILES); do \ + echo $(INSTALL_DATA) $(srcdir)/$$file $(DESTDIR)$(spritesdir)/$$file ;\ + $(INSTALL_DATA) $(srcdir)/$$file $(DESTDIR)$(spritesdir)/$$file ;\ + done + +uninstall-local: + for file in $(FILES); do \ + rm -f $(DESTDIR)$(spritesdir)/$$file ;\ + done + diff --git a/kasteroids/sprites/bits/Makefile.am b/kasteroids/sprites/bits/Makefile.am new file mode 100644 index 00000000..21484518 --- /dev/null +++ b/kasteroids/sprites/bits/Makefile.am @@ -0,0 +1,17 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#bits: bits.pov +# $(POVRAY) +L/usr/lib/povray3/include bits.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = bits0000.png bits0001.png bits0002.png bits0003.png \ + bits0004.png bits0005.png bits0006.png bits0007.png \ + bits0008.png bits0009.png bits0010.png bits0011.png \ + bits0012.png bits0013.png bits0014.png bits0015.png \ + bits.ini bits.pov + diff --git a/kasteroids/sprites/bits/bits.ini b/kasteroids/sprites/bits/bits.ini new file mode 100644 index 00000000..cb2976f8 --- /dev/null +++ b/kasteroids/sprites/bits/bits.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=12 +Height=12 +Final_frame=16 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=bits.pov diff --git a/kasteroids/sprites/bits/bits.pov b/kasteroids/sprites/bits/bits.pov new file mode 100644 index 00000000..9be7ccb6 --- /dev/null +++ b/kasteroids/sprites/bits/bits.pov @@ -0,0 +1,31 @@ + +#version 3.0 +global_settings { assumed_gamma 2.0 } + +#include "colors.inc" +#include "textures.inc" +#include "metals.inc" + +camera { + location <15, -15, -100> + look_at <0, 0, 0> +} + +light_source { <50, 50, -50> colour White } +light_source { <0, 0, -50> colour White } + +prism { + linear_sweep + linear_spline + 0, + 0.2, + 5, + <2, 0>, <0, 2>, <-1, 1>, <0, -3>, <2, 0> + texture { T_Silver_2A } + + rotate <360*clock, 50, 30> + scale <20, 20, 20> +} + + + diff --git a/kasteroids/sprites/bits/bits0000.png b/kasteroids/sprites/bits/bits0000.png new file mode 100644 index 0000000000000000000000000000000000000000..04c6482bc8e4a1663a9d33d774dbca7c8169132f GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YOqc)B=-RLn_EkO)|?YE_D)u~YTr+9E-R z@0aM4vyT1p?Cfpf8zP4q=gkWf6c=BvbP0l+XkK DAzEq} literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0001.png b/kasteroids/sprites/bits/bits0001.png new file mode 100644 index 0000000000000000000000000000000000000000..b4aa04f346bebac6f50935f72f14b0c0ca72e0eb GIT binary patch literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YO~dAc};RLn_ESYVoPMD4`+^XjV?C}bz5tdKr+?AS4b;%kcxX2@{&96Wtk zH#;#gX2tU5vZqd;R{s3=cX^xt{Cx&CwZHzbY)aHwr}u63k)y7e_&pV+kKXS79&TUv=g*U$pQo$exOp>m=FFKt z{{8!V`osJC_HE(o@6DbP0l+XkK(h_{C literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0002.png b/kasteroids/sprites/bits/bits0002.png new file mode 100644 index 0000000000000000000000000000000000000000..3a654cd8c6e99db3bcef74e0b93855da5d43ec4a GIT binary patch literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YQYd%8G=RLn_ESith(L&0gGpu;B)960dcS=N59*jUvmbLY-&oIJU?vAetb z;p^+`{q1Ugd}*0Dabn`9DVEa=c+czn7^N&uRs5($mpF|9NkJ{rY-axM`u9npV=uvqxPGp0r6ZFeKddI%49nRtM-+22WQ%mvv4FO#pl# BiN*i` literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0003.png b/kasteroids/sprites/bits/bits0003.png new file mode 100644 index 0000000000000000000000000000000000000000..483ecb783ff62676fb2bc4a3e73f937fd2ae0e82 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YQodb&7-jwK`)X2ueEO=*Y;HbX$B&l>2-^AN^{ak- z_}H0$f1mN6A0H<-&hGYRQ#;<{%M1jEXK#PMj`PFk%;MtWlXrIiFL>V0KY!iBpl+rw mU$=B0IpU(z8DwL?z%Vl`DeEooia)@BVDNPHb6Mw<&;$TxN1ea` literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0004.png b/kasteroids/sprites/bits/bits0004.png new file mode 100644 index 0000000000000000000000000000000000000000..fc4f681a9dd14950dd9d7fb6c0721ed2af2c9403 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YN{dAc};RLn_Eb6}Qx=eIf{Vus|sJ+X2GYtHvQ-3tc6r{q?+mK2QGrebqiw+ML__#rIeKes}VG z{QiI=hL0H8>i+)v`|{@J>FWRf|33cX)79CJcU%dSXJDw~Oy6?DvUx4gw+x=HelF{r G5}E+~Mvqzm literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0005.png b/kasteroids/sprites/bits/bits0005.png new file mode 100644 index 0000000000000000000000000000000000000000..ffbdcf007447e2b192824b7bfaf8a398133edc33 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YOidAc};RLq&$XUNCsDA9WV%n7k+OoF@} zS@+&mz4mUu^rX3=o*rpU;rrhI|5SHC;PyH5lD!wEt_|C4E>mK@UBTwpzo$RW&B#nN zl1rUh#JMu4$ZB?ZM-n^3j+e6MEOkyhO;V|lm1bpQQ)Fp*(BbqeA!=d3me)OuTBkSM zmKTd})lga)()Rl=qtc}P9DQEFM??;n{(WnG`XvhkN8UmeE6?-R^&GcY86wtQ`}^9o kz$a#Q)Y@DBye~ZE49p7ZIG3lM4fGX*r>mdKI;Vst098G2s{jB1 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0006.png b/kasteroids/sprites/bits/bits0006.png new file mode 100644 index 0000000000000000000000000000000000000000..0c6b0752854b38c269edd1af3f42b7674f7fa6a8 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YPld%8G=RLn_ESfCaVGKJe!Jo(V_gNN^CPTjx%xYi<*j?T`AqM{;$4S{QpyLMMzM`P0 zmv!>Mfdd7Pj@+~>v0Ni{MD>V{ynS8Oqo3W=)jKX7IC0=a!QW$Vug34MtM2iYV=gEz iK3N3BLSddP3=FOR)oORGE>ZzHk-^i|&t;ucLK6T-&0zQd literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0007.png b/kasteroids/sprites/bits/bits0007.png new file mode 100644 index 0000000000000000000000000000000000000000..302a9535212f28c99c571607b7e45f854e6bd8f6 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YO`JzX3_D&{07NE9qszdW|(O!}ea5n*npF5SANGbQ8lYOPQG++3ed3oW(K zxw8NKxob>^OV;>JnmBDsN@8M~MahpNSxsuig@vb{JzZj>c1n^52#&c`xdsIZP22Ir paL$+S@8jFj&&SEA-(p+E%y8L8v|PZ(HxOtsgQu&X%Q~loCIGyVMvDLd literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0008.png b/kasteroids/sprites/bits/bits0008.png new file mode 100644 index 0000000000000000000000000000000000000000..aae11420fdba9bab5618b6096e0a16b594f76b3e GIT binary patch literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YPRJY5_^D&{07NE9e&oY3z+lYVIVjq}&{YUrB2o7mAYLDi>+b^4>w)nRLQ z6+I1kv2t>{zd*^cKb4pJ{AXSHegE3EB+NSwZ|_C(jTL zAgJL;=>YOKd%8G=RLn_EkSIvVSRmbXCjH}NSh!<chdNBuww-DV`2 zhAs_ISHB%U-IAo^n`?@q?8-mYS&*~9iHE3taMvJUtf5Y zg1+>r0|yR#`1*Rgdi?&{`cH3PZ$A!XRDXJQcK72D&M6By7_RC$$G$1~Rt0oEgQu&X J%Q~loCIEFwbXoua literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0010.png b/kasteroids/sprites/bits/bits0010.png new file mode 100644 index 0000000000000000000000000000000000000000..130699920748fba3508191a89697e3d850df76a9 GIT binary patch literal 303 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YPtdAc};RLn_EkO)|?c(GtsZf;nM&atL58#kK0pFDZ;wve!(4GF2qsXu;y z*FW4ZZ!f7AyKCF8&(HPyB_$*zW=t?GC@eh1!Ob06^Y>Tp!^_Lh^Th9~N&WHhv3m2W zt|Sc~-mbRA$NfbA|M=Ma@OS@vp8NZ1(|`Q?%YJx${C)`+mlmCbtQiwc|NQ&=`|{4> z=~DH7e*ByX1oNAlW1rpMU(ZuhQ*-P2`S`pe7Z{k=$L+PO`+V*0&i?r}x4-`T+n)XB u&z@U6JUl%>!)*OZItqbq5n4NAA22+Q1bt-;{Ja?GLk3S*KbLh*2~7YkZ-(>$ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0011.png b/kasteroids/sprites/bits/bits0011.png new file mode 100644 index 0000000000000000000000000000000000000000..742ae1b6c16c79beb0a373f3b393099ff11a014d GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YP-c)B=-RLn_ESYVpq^6bNhg44HdM}PhI@9Xc*6)QIIaC37XeyFVe>;2v8 z3YleXtggTR|L4hCb!y?C8XKOPKYMug?6Cpzy{28=`gW6n>X{R->i_=x>*?lpskwc2 z^t1m!)%)xICnh8$JgENh;^fxy_jWPKKwg4V`QwL&)w}ln|8ere)7R#0=jYoS*Zlt0 z+c>?QeK#);5XA4VtN!%u?e6TZuA+tY|NkZb`2Ah~@OOE8ySqD!>rLC1Ph>PVpPo|_ z^FvupO|1>6!pLX#z6Qb7lPBa$@A7)7HyY>9KVBFZDCm8uWFj|% Y%(a+BXABFVdQ&MBb@0CcLE5C8xG literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0012.png b/kasteroids/sprites/bits/bits0012.png new file mode 100644 index 0000000000000000000000000000000000000000..d10fdba50383b2662e824845faa5b9358bb77e71 GIT binary patch literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YOSc)B=-RLog>ac|b40EuHCkE?a7weTHnI^r1cL2OT(pV*t$FHW<5zqse8 z*Ht94#nq^S(UYCCHR0am{>3K?1zeTXy5Il!^V3(qp#O5|+RD|TpPYB!mD=+y-Yzt> zaQp3so8PL;q&D{lhksV!IB|9pPus_gvfXnV7p;)!y}8r=$-%V88%=x_g&39k+#c^N zzqy`)L1((Qwx5tdp-W)Sw5Iv-w}q0}&pA7IO<_`cXL0VnPFIouf8cuiuUw1_HMRWr zTLdNqHEEQ1GYXcAbx+-TfBA(it0K7=5{%~V|Gn@1t6gPmUV;pL^((Kg@;(1MouPn1 Yye_Bnqx$NSwZ|_C(jTL zAgJL;=>YPNc)B=-RLt3W&XMn+g9Ph?<)*&DLfRVxlyY=;Jz>*R*!ZNz)+6=+Q?IXd ziOA&uPwTXZ4wofYj_t3}kH5$N=l7L08!qSZsz1JXX45WaUuDtr(bnhJ&0x*inl)db zDQ)q+#U@|ktW~+EoN8OC!Meg#RP%YA-U3GpRt8N~v)8$HH`uNSIw|;Cerr$u=l#+0 zTkC`b9j2xITh~93iQl7P^>a>qpb?L;IKzROhqpW@HN10ru3j+N+(7K-`=9O`O2W6T ed;P4sj(wAT(0;?eUp#>RVeoYIb6Mw<&;$Tqk!+R# literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0014.png b/kasteroids/sprites/bits/bits0014.png new file mode 100644 index 0000000000000000000000000000000000000000..167ebe4a2d4e7a1dfd17b2a2e76be70edd4f03f6 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YN}9^X3iD_53?=z5&eU=F@egrL7PD=9izh;B?|u1wFm2gC|cO zEO_!_W!)kZ508$>f4{zVKfJTkyVfQ1!Q-bBfByU|Zl?K0s-j{?>iK!QJ08l+k#ccq w(S3KXc7xPxnK0h@_VtyYUR|C2_{I~4;sttkB}unAfUafmboFyt=akR{0DXjKPXGV_ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/bits/bits0015.png b/kasteroids/sprites/bits/bits0015.png new file mode 100644 index 0000000000000000000000000000000000000000..15a287e35cdbf945fb78d22e09f7cbb016bfb597 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NSwZ|_C(jTL zAgJL;=>YOuJzX3_D&{07NE9R_B?)w$Nk6pQ!dN-+>h10C=P7g>J`P&5bZOqn5C82S zzcdyP6E106vSi1bEt+$Wb859TE^v6S6eyS&nNuWDw_&51cGd59wufJv=gSM5%xrIu zzIO8Bvi^+aH*U=M?&;AH`6!sVeA12?E1o|0kDs@v;$xS^`KYKF0#8L4)a_Ij7C!bZ Q0Xm4m)78&qol`;+09OlC{Qv*} literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/exhaust/Makefile.am b/kasteroids/sprites/exhaust/Makefile.am new file mode 100644 index 00000000..4493dbd7 --- /dev/null +++ b/kasteroids/sprites/exhaust/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = exhaust.png diff --git a/kasteroids/sprites/exhaust/exhaust.png b/kasteroids/sprites/exhaust/exhaust.png new file mode 100644 index 0000000000000000000000000000000000000000..78f987e3838572497d7681d80dc3b42424473539 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1|-8Yw(bW~Ea{HEjtmSN`?>!lvI6;>1s;*b z3=G^tAk28_ZrvZCpn<20V@SoETKwzm5`I;;Kv gq$ec&I183yxbMc(eaSw81*n(7)78&qol`;+0GdB4UjP6A literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/missile/Makefile.am b/kasteroids/sprites/missile/Makefile.am new file mode 100644 index 00000000..cc06088f --- /dev/null +++ b/kasteroids/sprites/missile/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = missile.png diff --git a/kasteroids/sprites/missile/missile.png b/kasteroids/sprites/missile/missile.png new file mode 100644 index 0000000000000000000000000000000000000000..e78a0ac004758245575684c2bd69a7ef381d2f16 GIT binary patch literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^%plCc1SD^IDZKzvEa{HEjtmSN`?>!lvI6;>1s;*b z3=G^tAk28_ZrvZCppvJHV@SoE!lvI6;>1s;*b z3=G_YAk0{w5MCX zN%*YEvnoq(+i-@95&6 zbKWA|*JdXzbS4NqTeYw=TJ2oAVbo^U=bJx#_UXRUU2k-O?Z^eT+;xYnh2L^F#tT^< tvh`&7X0j^q-9f>p->0p--g5mXcaLe@+)tc(pMlO~@O1TaS?83{1OUGgTm}FD literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/powerups/energy.png b/kasteroids/sprites/powerups/energy.png new file mode 100644 index 0000000000000000000000000000000000000000..f8df39f9f1da27037ece30c5c50c8eefaa00da17 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=G_YAk0{w5!lvI6;>1s;*b z3=G^tAk28_ZrvZCV1uWNV@SoEvy%?;H7M{n-|zXaTOX%?MZ@4q_eODvExtD!;vJoY zst!j+JFyrVXTG;)o;)wLO|{cg)5A(h{;Nn|SgXehuH3SROWE#4zbQT9?d$c@r|nT9 z{~OjL+ZYA5?sFHu>UrsVIp?|Jz8!Ov7amtGdp=hs`_7fOI#r){?mNo&xwmGs+O!ve QK({b>y85}Sb4q9e04ico{Qv*} literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/powerups/shoot.png b/kasteroids/sprites/powerups/shoot.png new file mode 100644 index 0000000000000000000000000000000000000000..bebdb5ef9d820942340b3a0ec8a15c266e7cde2c GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;RN#5=* z4F5rJ!QSPQfg+p*9+AZi4BSAi+yabimG_nc1=&kHeO=iv@bIz9m@hse#sU<&=IP=X zQZZ-h2ruh2iZ`Ury!~LZejf=h6h1V`%ZxBdhSb0F^%!L<|)TZRl zPw;JNF%VzS_I2jzFyX(~t{&9YG})z6S{*ytK{D*xn~;Cy%&)d{Zmc?W?6UZ)|JN3* zJ~jEG>cWN3R$t1Gu6WI|oh>%%!PhNE4Cb?5=>Br*gf%!lvI6;>1s;*b z3=G^tAk28_ZrvZCV1=iPV@SoEt&;-z4k&Oqr+(jG{5_ecmC5ktk3i0){)eLK*$zD4 zclf%^+zUR>PqTS(T#%lqRFUGo`Le~&*zopr0HUl=y8r+H literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/Makefile.am b/kasteroids/sprites/rock1/Makefile.am new file mode 100644 index 00000000..c8d5c04b --- /dev/null +++ b/kasteroids/sprites/rock1/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock1.pov +# gforge -s 0 -m 256 -crat 1.9 1.5 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 6 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock1.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock10000.png rock10001.png rock10002.png rock10003.png \ + rock10004.png rock10005.png rock10006.png rock10007.png \ + rock10008.png rock10009.png rock10010.png rock10011.png \ + rock10012.png rock10013.png rock10014.png rock10015.png \ + rock10016.png rock10017.png rock10018.png rock10019.png \ + rock10020.png rock10021.png rock10022.png rock10023.png \ + rock10024.png rock10025.png rock10026.png rock10027.png \ + rock10028.png rock10029.png rock10030.png rock10031.png \ + rock1.ini rock1.pov + diff --git a/kasteroids/sprites/rock1/rock1.ini b/kasteroids/sprites/rock1/rock1.ini new file mode 100644 index 00000000..e42fc766 --- /dev/null +++ b/kasteroids/sprites/rock1/rock1.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=48 +Height=48 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock1.pov diff --git a/kasteroids/sprites/rock1/rock1.pov b/kasteroids/sprites/rock1/rock1.pov new file mode 100644 index 00000000..58298c05 --- /dev/null +++ b/kasteroids/sprites/rock1/rock1.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 45, 360*clock> +} + diff --git a/kasteroids/sprites/rock1/rock10000.png b/kasteroids/sprites/rock1/rock10000.png new file mode 100644 index 0000000000000000000000000000000000000000..ddcc7c378aa46bd7b2ac7f8a64bfb3d3f1d914bd GIT binary patch literal 3237 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 XOS&?msF~Xo00000NkvXXu0mjfMI$L8 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10001.png b/kasteroids/sprites/rock1/rock10001.png new file mode 100644 index 0000000000000000000000000000000000000000..41ab94ee3db97a726c7ca9d8a517ac7753db8f29 GIT binary patch literal 3197 zcmWkxdpwiv8=phYvb@8bVzoD`RLU$Oj7F>G&C3&0EHTVsQO@UBDu>i)6*+{K9)~g_ zb4Zax3$YMlm9paJl*7X0_q@;Nb3gaz{^R*P-|KsQzt?r$H%>d)D=TOzz+f3=`q-0`yn~kw6(X~yva$TaWyiuNGq;%SRPOO3%)+TEXA74}C%sX7a z&}IjdNF2zJ4txV)j#0h-mgt#@V{& z$cd2g-T7^=O-Q3ZMKK2w=3)}oIBc3X*$!z}<3^H6-cDxiB&s%f$fBe(Qf^FqECpZR zrOY;MRu?pdFW1bm^dc5&gD43r!@lwBOMG(|2@pKSLlrDYfAE*r^)tr4xTmoz!xf?l)BLz%7@9aj!{u(mUYMKz(>D|TQDEimRcudY)wu09 zFz|BQr$J;Ik#t*)p3TVk_IyAAh%FlXA2g_T$m=DDtuDqF!}8 zlJz!!KXMtF8SFVM*}^HmtF4@+K@l6L7JW7sL~SMUF+A0wu=z%LJY4eqT*Q+-x6txL z=6kGiA_(3>SAB%bc2q?zywRxK?2{-3qt&MJ!rV2;te?x=rFHl42oaH)t$+P`ksS0* zSOydjEr-e0q>`CbO)|?7P@|HIrnNFsx-#RJTn;>~W;uPgCbMF0PKaj?X@{&3NYD~3F8ax+Zi7t?@)jn-0R zjFfacs%}g$Ha2W6H^sOVPkqMuBYBWWG!De4fuJ8w7gXRFPW+BcFT%X@Sp+K4L=MbR zQq4xL(!F8rxMvM6kt%ip-dyf=Z{uj~<-S|gA@Lz?)z-?OiK*bA*|Vy?oxPWtXuKT0 z`lsaQC?^Ko86f1}-Mgnbc6Hdk-~Mr>6p?x9kC+^5dAb50iGg#qv6 zG>(J_R4}Q`FY|IJX(Cgy5LB@FFy0iq+OOE;A$&*(+6QBzsiIH_Z;}xbWE#ZjR zuU>peD+-g^IB(PVk>*#In@yGY@U{lrQ@BzH?>`}SZ&(qkC>KA6WmD@L#D)xOzB3LN zEfU-4Gurz4x=tv5kHR}5C{Z_sr0Ye}WEM+1SxAz=q)MWureeRpeWa0(NnAe)JD<}& zRbAAn=wB1!?)&!?i))a2;Vq{GbHbXbG=E1FWWT`RH`&0<5J^E6?eiNiaN*v8a^QZdpCCh9}Yy zv8wPXa8fGlQJA|nQFSR=_gCZRZ*5Zk>Kwy>J}X58p{ruAg1HJt9&IYIB_uG1bj_moXXuEtT3V9Oq8{zB zB4J=Ku_X}+R6O|r$?m?z_J1NpV*(~xp8r#+qe{Zm6!|Y=OE){2YSPKk>8R4&npqzg zqajcU)=^C47zXR4b$PE@tCZi7%3;U-{qU{n(e+g8OTmOlitv?z#`Z{a| zFb$%wXl5S89X>oCSAZqmM!%XFZNO^n%3*uv$gpX**k*_6;&myZT|iVJCiUq|d!)tp zRpHC-^Y3l>*q#EbG^z4?vKGekh1LU4RmyrZlmQ<(i`y_ zMAd96A%~q!?Rs=l<22%`zMSUP=@a?r-LkT+vXZWQ8Gqo3nACf{aXk+-&m)hdJj_Dx zhTy2}%Gyn4K`~_5Nf8`R16M)Gj;e)?-~rjdz@=cF+~L5jiPIyIrkI7oF1`p_z~I=&Yg2^?tf0Mh*Uy!w8^aFOh|RZBU(lU>mO?g03e4tnA^QORkb(wvndWDqbMFlrCdL87FZmoXu#s2!e#Lrd9J6FJcSU==%_vHEnL&Zvb@w2Ea zXw>BC5b4yFiH~L5i|CU8g5W{?(bw53lA@2Ty%a@6%vnaMl+f1xG!5}?Ux%yeL!Ptw z&`2jMdz0CtK@}EGtmn>joUSwQ20MMQ7@p+t_^KfftEC*j%A>qo!c3F3TbiD?U`aUK zTIi7=M(N^1K5eXc^)CMszH9(Z>?geIV0fN_Gj#MaiTw00^Y;vSyLn8C_-nzYkEKYj z(=t=){2L`l#24<$*Z919o=Avkrn89KtXDZ%V6u)#TbGy#&+_4u(Q)0!CaUU0xdQQY zStd9Cxr8@4e8L0X(6C-6>4TJUL_qq2CKdn~9&6%7T6h}$@IoG`4Klv}?hOA(>Djl* z$*5+%U}4X=+r+XHY0{fKPM#{Vc$%4cZU{wk`ubvf?m2Ggm9_OG$cW+Q$dsEP=uLLe zF@RjlrX6!kuM9vF-~N-Z79sHP-S_wcN+6;n=McDKTeG^-TU${b9XtE6J5w(}Exf8+ zCWoy`*hYz6_}deih3@jlag}n<@E#eSwEXj%FVb6?U7vB@UJClN!Kpj+y35oj)*TaF z{woy38!(4kX-hr>(IwX!JRsUX?DxdSAWQRN({c|*Pe;jWqgcu{2j0J|F9!_+9aRUF zyk1Q!KqUcHLclwn2Gn|UpVgb`r|wWHwtRjYO^aeZ6Rk~eE@g!c1y9-3mUhO#AfPF& zUi&HW>ZPU;^lS9GCQyEip4;86oFDD4C{Q`hWDE!P7!-W+Hnn&-M*p=ky16n!8-(hy zTT9jt_sgQAR@;?DAhVQY;tY9ExisVz4ftQk+2x6x-4F|( zTK)TlE7*SW1k{OQLPKk+%X^CZkOCE`V7aUsX+@>mP25@O!zz!~LIYe8#z-kc#qxKi zQ^BCswte5C#{EU@UYSPeaGP>WxLLVEQt-fE-=grvo`<%_#LP7=jz4)Vydi<%U5YT( z$#++O0lDeY;^aQNohpf|7Op0Tif?y>9y$A9@@(u<-{XrGVBl}tm5&J~u5JKOU4&VI zL;#UwPI6LeZ4pT!2yDUJwiFbR$rPH=au)jR3mO;7C>zm1ll4RSodri{O^D3!Q~btR z?(AM(OrArUZ@yV(5hjgh%N0%LUVMqo>ltJ!3tniZgv12|l#PsxfX$m5!ZJmTo|pbP g?QRAOD%G2^_;Cb6@1d7Hbbo*m2@c09ZO$kCAA$!P;s5{u literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10002.png b/kasteroids/sprites/rock1/rock10002.png new file mode 100644 index 0000000000000000000000000000000000000000..f96a3c51e0c5fcaf428fe086e9946428fa8958c7 GIT binary patch literal 3218 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10003.png b/kasteroids/sprites/rock1/rock10003.png new file mode 100644 index 0000000000000000000000000000000000000000..6d6abce3963b5065d122d02dea0f7908a898f163 GIT binary patch literal 3167 zcmV-l450IgP)NDFO+Yz-&~vI*cofOkgXjkjJHa5kh@*-0=E$Oc3?Pz)im zsRv1kqBxpI&-C-Y@2$F7+@7{VOXLhWagYKU=&A0xRezmx{_~$xEjXd!``+cReeEx< z^m@JZqA0j{@d-9JFW@+e(P&Ij6ig;lKKS6)kAL~g*Z=OM))go5kWQ!bFJJo7`Im>o z0YV5)o$B-LZ$F3cdnl#Y+uNtADxxR`Kx=*Xr$7DI7jECabL~;rwjT8X{eJ(-g$s|r zB!s}WZO)y0j9xDw2s%h9Nz)W5B>+Lt!Sg)MoLTF9{p)}E((ix&{%e!T^zQ!&0MGL- zKmPc}HKpv!j^kiik|5|11RaLM0sVd#+qNl+0uX4eX}4Q+yB)MP_`b(CzVYl!w{L%P zeP?I)`or!+KFmKUr8b7c;TuATb=y`Lqwzc!+qSVR3(K-7OHEM}2qBqHXSCZ*q?F`& z@fnK*{NM+Ff2G&!Zagf2hqA!w(`&B+>+9=h0XTK4k1-Yfet_qBc%DnI*QF>5wzqc? zLg4#8d7k6@9=5F*4hJMjf|P=)s(AbD-@W=L0U)K^Xti4Ztdvc!7f=-C$~_)?>@=-b zlRPgO4*QHo2N+}M_j?>1Ob`Nu5P(1_i*~!kVi9uw{QBjst)1&zTieHWrDMBLuh+W* zpp?RO9nv(#7(-cVMxz5<*I_v96GaJ0l8_`RlgX5_EXlGQV+>MCk|bq&dl%1hsj7
LelGXSKpaV=Y(NQp65&^GmJ4bn+*WE-2m5h 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10004.png b/kasteroids/sprites/rock1/rock10004.png new file mode 100644 index 0000000000000000000000000000000000000000..94d2e34d7094538486eed30e1e4a27cc5c0eacfb GIT binary patch literal 3150 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10005.png b/kasteroids/sprites/rock1/rock10005.png new file mode 100644 index 0000000000000000000000000000000000000000..163c4f0fa6b44cd88e6944a1c6dce00d2c21d7e3 GIT binary patch literal 3089 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10006.png b/kasteroids/sprites/rock1/rock10006.png new file mode 100644 index 0000000000000000000000000000000000000000..3d1fb0ed6d72d72bb96c0c11a85a87df6f431682 GIT binary patch literal 3079 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10007.png b/kasteroids/sprites/rock1/rock10007.png new file mode 100644 index 0000000000000000000000000000000000000000..c172d03e703c1337f7ee69e84c1775d00480635b GIT binary patch literal 3121 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 LNkvXXu0mjfnUV+r literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10008.png b/kasteroids/sprites/rock1/rock10008.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8e33fa55c77649aed236f6a65f5a8f2bccbfdb GIT binary patch literal 2977 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10009.png b/kasteroids/sprites/rock1/rock10009.png new file mode 100644 index 0000000000000000000000000000000000000000..b97c42b0e28402f7ef2ab24086f830a07571fdd7 GIT binary patch literal 2872 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10011.png b/kasteroids/sprites/rock1/rock10011.png new file mode 100644 index 0000000000000000000000000000000000000000..7ac34a2e99a141bdd4de8c3292434d03fabe8dfc GIT binary patch literal 2786 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10012.png b/kasteroids/sprites/rock1/rock10012.png new file mode 100644 index 0000000000000000000000000000000000000000..5607651beb8d09c09e6a9b7d7af0f7102487c7ec GIT binary patch literal 2767 zcmV;=3NZDFP)`s?b^S+^~onUu07BAFPuQT-P+eQ9pCpio6Y7`RaG{2cX!y^ z`#z)57$F2JD-B+GWfR}``Pt8Y{?^AIfAY*E)E8y=g$rwMzyA7nueaN+{o!!Duj~4o zj$>ECFhU4HyWL_knbK&~aU2`hbBg=aq=#gdhFrZ};2n)}Jvqa9wBb2S51!rw<-#%PEwU`bvMjc?E)YctQl@}{ zloHo<*xuf{bmht$`@i_buYdBb5wLCh?a5@imt`4JW@ws^j<)lg_2iJ9Q9Gh2P-M$nAq4)XcH-Ghw z5~$bfzfx6oquH$Ex(=n1%jV_Fdxl|L0qA!7tgJMMAJpm@?I20^m+rbEF%mf8jTvdt`o-*Sq9~@3*?)6Nlu7->Nt+c zvWy^za#~r2VHjAJ$@jkZ`jr<>pjNBBtE%e8@_K!Jl~${X5P~2Gc<`V@zdyh<4FJmJ zQepTqqyUm6AxUDABq3Y4A&MfxFgh{Y3HQ7R0+wa%pEMp|I-Q{?3awU?YSqi9&EWq1 zhs1G07>2}gLJ))qA(&2mq?AZ0iQ|O%JXny+!%2u--3TEt41*v3@UQn@2mxK!_bQdj zJCEHXiXx*mgDaKS)g=Jk) z6ot!|Uwb56pP7Jd+kdk9aU3ru5@?!65CoJ;4z*g9jg2+3j3PX`-9FQ)4`j?{!6KzP z#aKy_Fq_Rtl9c&8U^pC+rYXC-JI_x*QIt)~vQBkn6h&y7MifOPNxH~BDTZNCsg!VC zhb+rT)0FXe%KiHf$+C8F3t=>ly$-5OVqQUw&;SIu`-gbsx$5OKnrF zdQ>W9!Z5=3XK0$nY&J*Jas$TW3G;ctY&NG4Bx{Jc)jPh+!CHSuV&XlPQ*El4W^QqbP#$ zc!K9um`;6ERYeG~SalUCGYmsV(=@^`B8u{QvNYatY;;{mO3CfpcMhJ5fNk5S=Kp0Z zODPvRC}cdIkftf7X`-qMx}F=UX)2vgk6v%E$S`vhMPW9ZFNB@IFm!ZXr&g;j(rr%o z_rL$*YkPt-2|THQEs^X01lM)&{WG8?yc6+7x-|hBMRTV{1&~*(($vu&# zDV0hI%gUSi;c!HyQo*t;f*|Dh_#VC904XJ&S6;N3X|ds#Lhrrz{^QIB|4agL96z!G zgTd$|Ip*`AXkJPs7ex^ahxq}P=ap$R>fF71pPik*K!`j6tgWq*rYW~>eMzr3MANjz z{?Ql=hA4{Q(@#G?{_Sr+J+rw#lfY;+`sb6^wrxN1JPadLRVg$}xm+r8eoo3E2=Z#% zXw*s5lvb_+JbLLu}jP@bC`P>5T2|E$-dBPrpB;-ydSz zCQ%ds^2qo7q7Rr%rjI!^1mVzy2{vk~cMB7!pM>-EN=5!!M6Jo$k2^oaYC3To^W~RE z2L}iL{myCgoQ89Yxj2qLdOW3`#8P5=UZuFVY|_8;on0);Vtsv;>D1@w=q{7V6iw48 zm0Xe}C64ph|IKecI=AuglqYlZ`TSpP+um0crSkZ*Wd&a5cgQ_csg&`3pK7&A7>0{> zag6WJNRot7$)VH9tNQ!z|L0q`ZXG_=w}Ph%Kv)R0U%Co|#B^r%7<#LI7 zJwH;f*K1gowID3%cKh^t16W9B>JJ@HeKsnkJXRFNGYq3>wre#H%d+Tp z`?T9Fq9`U!Q!LBCwk>vdUuA1+gD?!SZHrRLrCzTVdMXI!Ke=(^fB*4mZGmU}#H1+7 zX0zG6ep*)2G>w&&I&qv5Mfukm$Fce9s|RdtU0`Qtn^MUoiX!^`;qiO#{rlfYDbJO0 zUKoKTEX&%jR4VU0PNZJ1VVVZ(>unxB>=cJw!13zp%EtQo>cKC6`RlJeE;%n=$Va~A zf!FfnPE}PlHr9@_On!9p=Ha!`Xnc^S=`*kLw+2N~HeJ_!`$;{-OT5HOyu|-M{2yx~ VT(whmttJ2f002ovPDHLkV1j96QZ)bo literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10013.png b/kasteroids/sprites/rock1/rock10013.png new file mode 100644 index 0000000000000000000000000000000000000000..e5ce1f285eb601a31c93cc5175d43ba812b31b10 GIT binary patch literal 2837 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 Rh8+L^002ovPDHLkV1gr1&iMcU literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10016.png b/kasteroids/sprites/rock1/rock10016.png new file mode 100644 index 0000000000000000000000000000000000000000..fcef0110ca12a7afc2f7af580c417bc132f75c77 GIT binary patch literal 3112 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10020.png b/kasteroids/sprites/rock1/rock10020.png new file mode 100644 index 0000000000000000000000000000000000000000..44560d5ed24bca7a926fcb69047c6e3f03faae2c GIT binary patch literal 3317 zcmWkx2{e@J8=nwzZABVG*{00Zl%XMME|F1_EnP#CeVMMoSh6qKk}_o*8H}|d^0~-N zaV>*zElrw+YI4U?xzTX#G0FD-{@*$8_nz;Z_dM_O{GR9c`<*Z2s{JK78JG+N0+GXA zwz>xHsQ)i%N$~9=ws{ZSB>u3lvw%RFvVYn3-v9saa7&z%H25S)``w2?WJ_>X7EUoG z3q>~B@UQvl<@$>SuLoZby5b}8qesS%M5?7A_sPKstCBXXRR&>Lei!n38^TpUA=zS$ z7@gL!7Oc2_&LiXyYUl$#~6f1C))YHnz~rpI?WG7Ri$`siE_|3VX|QeqW}{7eu8p$#Lwc z>G4*UetmuYq-?4ppUjvyZ4gum$i4yr%(ht*NA#!1#F42C3u7g~X3iXjo=( zw)yVIq>^*2I5N4OTXg2+bs83$o?L;D@guwp3vg?TSm|G%ckxuTv>osbIhm!j7b4b; z9?tY=+#2`Ic28PZ{Tjl47u=#~iGn_?1t`vS7Ob&urtK?cl`{)RmB1Z(L>xN9#cYm) zvCl?DZ7jx>b*-Zy_9d33_FFS7uGu=JA#mOs`gk4|TQ+SHd81x3FC)8}6k&RrK=T%; z1P0!I#k3rCO4YO)MN=3RV?oQ)8eO~F3xd7Kd5e7|2(hf`@s6m4IWF2R5XQ@W<851-kVn&# zPl37qzaPvd-K0xuhL*rcbOV@O=!D98Tq%Yt6tzohJ>>^hk*>5aXs2n~oxy~Zty)UU zUex8T9e*ZeB>d}a^NspwXP5ZP)K5KkFD+xr+3ed+c%tD-=){Rk>!WtR3!?;9jt}RX z2iMf`ct_>C`T1sbb+>~A+MHW{gla&qQ^f9y-&EWGb|EVGEbL7j)vv^SPukl=hZ}yQ z{)Cj&y_m^ICiYL?2cThcAR1JOd-ufs2n?i6TC#K9zGSlNAoC%ysIdB_l7m~)&ygn& z&UPMzq>O_Ae7 zAJ$IqHJ}l-(=Ol-&#lAsJAWuNCg?!a2V${&qclZyu{_1lT(l6S#4R3~k8C187#=1o zV=YD_5|SunSFqiFU-ZK}0KMe>Z_{>mE&>3UVuO48>i7JUQ>J0cohr#>fj)=XaT<4s zUhPdd0U;7{t5sQqzE@WYZVbw*f}}EwkUfF&hX<>WyI! z^s`5BoN8vjAHf=rM^J2)v9E6)3@f>cKWr)8xwBALqqeUXMe_s+lLtRgTzo#wd+^Ph zW5YMa!777WuDiN|#RnLvx$}qC#N7E;-^VJaYQiZ#5$-zDmH_Z&yj3m@f}=ho=LPZ` zFgp`VX5kD(0$GPe-ok37jRv>B3ox$ar3um9&8Luolh4t1mHfa;#H*gzOgQc;J|Dv! zVwRv}RpkdB5i`ieV5`AdZJ1YAm*t@k_oA*oifvGQ@0cBKpif zXLdimnp&+2X(oeIP4%*FLBgh1opg>VePB9yU1K`K0dlR>5}wdzOW zg__ZRk@yJuB8~XP#5-gO0o!~fno2ZAym|PF75yR#>;`8kX zCSv>{SjV7!t^jaoz<@wIh&8ah!1mCg_%IMN+ncN^(yItq4pFt<`3^?E&|X{_+aES3 zt#zMKcw@L4afN}frbCRMPdCT`yD>fBvfS@%HwuzA)RasVBe;-vU*M zwE6JCSw-2ZdMx5PLHOAu64Db2&g3Ao!KP%$#N9=JR>5-g&W1A{QBuVt!TAA0Bv4pCLCe>X}sT|@M znY*@hnfg2wW?gxJKszPBu)Tz%`an(7AG6r(fj4i=q@=__2&PxIci}i*WF~@w;2gp_ zqU`R(vHK%nRQQk~P$LP^%$jy3o^z1$BFO71l>di-)ULP3skrb1e`3|R|$*m zP!nq^d=*mz=rIsraWvo!(^m2hF9W^Ww0}6a7P!Lp=#5l+&`|6{I3ZdJVYNhekFow3 zWG0L)W4Hrr2|XxnZn)XcZ%SWG2vr(YBnljdpgdgSo*b(cyU+M$L%wY)XCPY&$2E}y zc^~KgtglBE#S~Vjfa*1I0x;#y>R7rc?cUu`>2$kcy*nO^M}$ zTXD1BOtX9$2vAQ_HLE-tbBU_y-p`tw#qJYpgIZkw{PVkXuWBW)(8L}T{!Viu3K|xQ z@=DsvdFk~$r-ZFkp5l9S6UhnIaGVE#LfdWq+{Q}NGWdad=R6_0!kIO+mVncsF#OPT zO^zV|e2O)TUP^qfXm0FawkZKxxTT|?TgwXrSY|f_JI(~}XP5pfsnt1|2>6x6|=M6PD=CHi;>rUc5`+c6mZ?kJ>VJQn+n=My?^HxPb2Jdj2xiir@@Qmrn-tOzca;XN~<|!2ViHfVZ}`4ufGGm08RY z9lN~t5O(BN#6Acdw_09JntV9jSF5KYUUzPw@qN%umTK?kLJ&U06|IIo%f zf$suZZ8#2C7S@QM^17hVzkh$MLEv_)wpBja#NOZdXqw{o%-N7Gvtn#gWYTg4q`3!` z;7`~hI~I4%G|xus)w#;pZQpnZ=!+BUIq?Og#e!y6i5 zdg`=WOxj78V_(^#T{SH)6x5M+c)ICQrK9K5Gk44Pwx>LA@9ijfoX^rwc%hRACsmFL zJ9Id!KY4r_JOu~<0~|}*HvNNJMv%!L>hq*_=%Y`|`ZnZEC9KI4i>7}5t)IA)?QL?N zZNH6o#^;w;3y-?<%TIDf4y5J?(S2(PQPHBCr78 z_l=K_4wLuW{V|XY3WY$>-m%i(^_zLAr0$5gf*eGXyVg#kPdX}pr6W0>bcBS(;NqfL zGx<>ZK^n2CE9w5c@wud3BoouP*|q((5rklEtIJf_?yw*7TIV2^7l_B(-Brxg(YzF& ztaG}Gq_>l(pfxip1TPna>@99`%>)S**s0o(x$HlA{v9JrB9FN!!7oSOTH*25m7BMG zjUOzktE*4KGW%O}m6lSDOie){g-b`=_&bhWQx>$tOymT8m{HGW?-XeC3-|E9D8{-*;t2LPhDq>!T%Nr4r_1KVCj|mKXNH z)t&BM)y(=DJ#|(0lcIT2%bNnO9|srDa#cvo1REW8(a`D4*YiCX+35HdfAGA+xIsN= zs(Pp#UL$=(QR+*P5vMdR&b!vqre)?@xg?q5|3LrpNQ}ZyhK*6U#{l*RQ0a$P|a{M9C+_CRy)N#1DR6p1(F^+iGz#=Z1Z9 z;Trc`@~Wz&K-g@?ov)fACQXi0L*>(II2qC9@$DZC8BPX^F*WvU!yi7x6&;V)n)JC? z@0ao6L#Xpev1?u6?;m`&cIJYQm$|T%WD4F^^)C@KkWiCj93-i)nBvBinx7{gl=d0G z^M`mTcvpOj_an-@qi0x{(4_bZyeKzU#j%Ee`hKEz zXl_V5sU(%nH{EzGdwTqBi-im#hmt6vkCRc1?IfJAWGIQ4*%HdS8!hiL-FTfTIlNL*GnMvgnWzheY#f=t z#@+)H7}jps>{kpCdi$26lI0xi!rE$0px`qM8=6Dr`e(e@n}1F$2>z8f+q335tvs}^ zK}&>}rk#cvnisupmlf8IC`DIbRHnFw$%q24SC307Jp!hcko{dFIGia91XO(>8!2l*>ojEn)VOaO!@PDuSf!6+p>7w_)iMs zaqlGJe=7yIO8A+yuD{P_#sKAHTDIW7Aj}6P@&Fvr3wEc1wp*794y7E z!{~+c1rMff?L5#VZMCI%RiO5}K-8%ynfO4y&gS#-eO|EjzZ>&W<^OP(iGTn2p%aUX zrK(o``nwS*meLVqx;uGh^p`9J?3Jo+ZK-}-}x7E;;-qu>kE%h zYKpMp1}hYj+?U3;f#9$%?NRgk3(KdHubnN!qht_w@7_hLX4R`_z*3HyR*b!5EN5TO!Cp@WNH*9{tv6=Q~{QN5@6 zJRl|K3uv%p6rvO%g9z5=$cPz~Whx<~Ge7k1yk91T+Jwrxsto(^fmAxaW_gauICPD4 zJleJdS({gsLMXQPm3M(uL6V|F!pWM?1fx^^Tf~jaG3Qc_rI5mB+pMdHJE2yq@KbD* zp>8Vb6_yP&TnPkl90~WKEo(LL2z_L_9GTM4L3}c=4dncQr?YH#ax6L`*u2 z+yLWj4W7wBl~GmWaiH`^#H;B_8Jq-MtY@-AHG4Z}rjq`3eptt@Sm2Og&k7YU3rqP7 zX!o$m6P%!~u0E@=B3TaoabZEkL_#uNi?g(toHue52rW4c1J(-<=K|?G>i%} z``#voF_nn5w7OU%7F7b#56lOg0Th_8DIW%?a#?oJl(iT7~x!PKcO&hv{OvS68=hu)ECOq*Cy{48{##460kR zn-lZtP7XySwD3l?-5{X=ur(1gZ_SkfXqGzdI#|NAXgv|Ai9{?AO;^{^z4sPm%6eF& zPm?TCTRSJS@jmM#F+a)G2>hofFsr1%UC#yGJOY2rR4kh$X#>bbHd()|HiONFLHRVP zT3EO=s#i>&!c^O(N?J0$uhCmzaJW}cP+mcSf#r?wFtMbP0e||-y_rkP?~lh1eoq8} z%^PYUsEsnUqPr)+p6e6M=VJ%3uCAw(B$G;v1q+~`>-~=PXac4Hvp#p^C8V@IP(E*O zk2kEHC+2bAw{N;lj-IA!Aj6$m5-I=rDeS9m&HPqvdQi7!nY~=YfMZP(P6p7xD{Noj zUDd4BvTr1W^p38DQ_|erIUvWXZ=Bz?_I|a^ z2zV5f91y|MR#Us?kZoQOL~?4`k6ISE`l+gIwaUv~v6oys@7oDmA{YZUnST5AG1Vk_ zW^-Dj0$mtt^`Y4hKn}w;*hP*Z90w5K#rzq4mkZA!STb|_p~`nOG}DpJ?@`7F1Cj}xT7oQ zkgXR=YH#y5@0hwdOypnQi1c}5&+l{Z2VWrnQ4iE!4hE9hse(K39$+xJ1AivX&7dRV_@ zKYGB{`%a>B$UY^IQtWBE&JdIr42&VRssMcA6;(U96X5}f!5G?HZb<2bKW92OH@8Ne zrU!@1UeZeLa(h^kIyR<@1_v#T!*Mccl;Z*d79KuF>l+&QAg>)oDHTq=4FXZ@?F>!f zXvGK1{+dQc7A?&1St+h%wYkc)f?)I|n^hbRCq@@WXbc4hyVSpWb*)eucr;c5nnzHl zx#MstkBEN*nvs(`5izN3r30vQj>Bd3j@)VbbsPJo{F>VMyX~%Fal@^xV!eAai@|7c zK~GPQw6~X6Ge0lMyQuvtney7vDkLQ2G5VEH6B)1x2OXv&Fw>Ihy zl6{&0#&~*8!vT_D&k=VR?D6)upYF~6etPTO-oK?^)*P64m1$#?US|5T#UR8o*rl)8 z4?r-RUSuYCVuaA(_Y{@_m>e1m>^#EgyVK-OHPY}IBurGL*ztVO!G2eA%YQr5w075n z42y#4hGl_>NumLL%{tq&PPmvZ&VRDHS{fpxSEV`OPlA4iJa=(>{m(EoSzT^Aj!FfL zW0`osv$vlb7WsN?^uPBH_EuJAZRO_sRyz}KV?!>}8vPj15d%7Vw(@CadN^nE;c`3Y z4Pjm!7+5~!q^ZipKh8XJlE7Kw4-XeOhF~sk0{k)tBvZ8evxfMjWGn5;QX)@&R4PWy zsb+=~Cn1tx2y6sSM=~;}23QQ6Wq_|#s-^uifX!WQf}PVe=`@VCd>BAIm-f)LQleqa zzA``P%=ubB+cc-zL*Fv)2D z=fz3!FTvs4w(xtWG5x^Q*mBXzX#nc?-Z|t*bSi+as;YC?Y%PNcQwE~fKOo?gV(=p_ zlSo`75#R0kkSP^O+=B}?!@LJh&Lfe-n$M)E!@qVa=RX490Pu&G-Fn{JxKX!E!21^MRim5YpWGO0B6|^#2kqe4PLQ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10022.png b/kasteroids/sprites/rock1/rock10022.png new file mode 100644 index 0000000000000000000000000000000000000000..ccb7d8e6fb16ecf34143b739165768f4453c01b0 GIT binary patch literal 3383 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 DYGyJL literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10026.png b/kasteroids/sprites/rock1/rock10026.png new file mode 100644 index 0000000000000000000000000000000000000000..6eb57dd84e94a6ca0e7ecf43fd2ac1dd0870a3a5 GIT binary patch literal 3145 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10027.png b/kasteroids/sprites/rock1/rock10027.png new file mode 100644 index 0000000000000000000000000000000000000000..56d7a090c4c9fc14173cbe34c6f401dda50844c5 GIT binary patch literal 3257 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 IR^EyK2ip5u`Tzg` literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10029.png b/kasteroids/sprites/rock1/rock10029.png new file mode 100644 index 0000000000000000000000000000000000000000..18eb8aea2c528807b55f639547616a76c23834b1 GIT binary patch literal 3306 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock1/rock10030.png b/kasteroids/sprites/rock1/rock10030.png new file mode 100644 index 0000000000000000000000000000000000000000..955307d92a1b00d62dc6011db112d01eaf39d9a3 GIT binary patch literal 3346 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 aaQrV^6j0T2q~-De0000y5p literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/Makefile.am b/kasteroids/sprites/rock2/Makefile.am new file mode 100644 index 00000000..05db3bf6 --- /dev/null +++ b/kasteroids/sprites/rock2/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock2.pov +# gforge -s 1 -m 128 -dim 2.6 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 5 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock2.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock20000.png rock20001.png rock20002.png rock20003.png \ + rock20004.png rock20005.png rock20006.png rock20007.png \ + rock20008.png rock20009.png rock20010.png rock20011.png \ + rock20012.png rock20013.png rock20014.png rock20015.png \ + rock20016.png rock20017.png rock20018.png rock20019.png \ + rock20020.png rock20021.png rock20022.png rock20023.png \ + rock20024.png rock20025.png rock20026.png rock20027.png \ + rock20028.png rock20029.png rock20030.png rock20031.png \ + rock2.ini rock2.pov + diff --git a/kasteroids/sprites/rock2/rock2.ini b/kasteroids/sprites/rock2/rock2.ini new file mode 100644 index 00000000..d50e6fad --- /dev/null +++ b/kasteroids/sprites/rock2/rock2.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=32 +Height=32 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock2.pov diff --git a/kasteroids/sprites/rock2/rock2.pov b/kasteroids/sprites/rock2/rock2.pov new file mode 100644 index 00000000..2f37a206 --- /dev/null +++ b/kasteroids/sprites/rock2/rock2.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 30, 360*clock> +} + diff --git a/kasteroids/sprites/rock2/rock20000.png b/kasteroids/sprites/rock2/rock20000.png new file mode 100644 index 0000000000000000000000000000000000000000..4c4831164542330df77d8dc3d3b280bdfb4a010b GIT binary patch literal 1553 zcmV+s2JZQZP)(NzR)!pug8$0ff z6$L3#m_VW#f=#3#kw`)8&0@qVniV_3ngvT1B$~g0{{To3K!YZcAR{Yc5@igD5_h-T z?XlgDdUaJ-6^qyw%*gGwSF@P2s>iwKeD|Dl&pisqkg2J{;^N}#H_x1T`MMDFx3{~E z1=Z8(na$598yXul`@*9A%vh-n z0Max?2+3YxRaNDK4{qElm8R;)257f8>z?N`JzXJ+M&$E0yQm01eHq8`Xtg%T=W~Q%gs$rhhao{Q z+>HQaG8%GD0oZnywY3&eG@@FqplKST(FjFR2*VIXk>qkV zQc8Ng0ZEb&MG;Ao@bKZ|yT<}36bhg1J(tTx`uzbL8yz%F<@D)!(lo`gvH%PQ4!vF< z%d&`~5uWGo+bNZbN3Q`*(-sZG*stJXaSBycxqtrwK@ecu7Ugo0Z1zRv$8pRrzdWN* z$RPv-!4Oqdh~tDxrBpjIz+UM048wcBeRmv}c6*aFP1)J$^89&=G)>vs+QIjI^7$OPt}z%mbUIsf zyM4ki!nQ4%&GiGj;eh~2lC1p}!Z0$7Mk5M^JeHNE*Xz+}tfQ(bilX3o{(e7j90w^S zQVN>Q^~RT9-nn&T007r@_ZQ}J_WpN8QP4CEDFtB|((ez5;}{_Xs;aWR-Nmx9MA3-H zkAEI}7mNk?&oopjC6Xk;wyoWW#0&-wVHn{!E}rL;&)bAyh^A=-!H{Rq)($*^hXU-0 zX&NZX3)NPu4L~NNQ!EztFCHNTuInMCq+XvxN`d3JJbCi$_yD@DF92k-CZ6Zh?+@s9 z`xr)sBuVLXwus{dRaL20E2L?Pl#YH(eC>TF${x3 zA&2XFeEaS9pR`)-u>}YdoC3#jZtsFHJzb(+KfzmXEpX+^0*az=@??!D8Zk3dWqy7R zUDx^Ii?2WW@yC?|o6qkyc$h+(ru}R-`xhYZI357)_9jsjp=lbvAMDNtHl~^7{{5BZ zJ9qvz@l>o%qO`NK^IoM=`8zD&_wgG;=w7v0AMhIQjfv{%7S^|4V!^JEWQ200000NkvXXu0mjf DB(m_l literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20001.png b/kasteroids/sprites/rock2/rock20001.png new file mode 100644 index 0000000000000000000000000000000000000000..8041395ae1c623598d4afe337ff7ef3da7d374d6 GIT binary patch literal 1599 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20003.png b/kasteroids/sprites/rock2/rock20003.png new file mode 100644 index 0000000000000000000000000000000000000000..31f41b16d9ce1fce36296df960bf5d021f41895c GIT binary patch literal 1656 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 Y1q({kMk?}{_y7O^07*qoM6N<$f<)&F&j0`b literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20008.png b/kasteroids/sprites/rock2/rock20008.png new file mode 100644 index 0000000000000000000000000000000000000000..2601a824f9ea3711e3e53cf64c96193a4127e5fc GIT binary patch literal 1570 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 LNkvXXu0mjf5n}h9 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20010.png b/kasteroids/sprites/rock2/rock20010.png new file mode 100644 index 0000000000000000000000000000000000000000..a3d198512d9b222af9e60efa8756e4dff26dee0b GIT binary patch literal 1541 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 A3;+NC literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20012.png b/kasteroids/sprites/rock2/rock20012.png new file mode 100644 index 0000000000000000000000000000000000000000..227125917783e578158b2bcd5b2e63bda5938b37 GIT binary patch literal 1576 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20014.png b/kasteroids/sprites/rock2/rock20014.png new file mode 100644 index 0000000000000000000000000000000000000000..b76af51d007cabf40da9265cb5a5774192e8219a GIT binary patch literal 1611 zcmV-R2DJH!P)y-ci#C9{IAF`Lit1@o8^`P1$HIzT#|{_xth%QwfzGdV>8-v?Dy$>k<^{J6MWtJPUsdvIrK zYiH#@4v=;E zTU-c{Us$;C`MGn`G@EUj%{GI9jb$|`6pCEF{8Q@nCabG!#N$zBXY+)^VQ$>`#jRv= z>{xT2ssloZ{Pc8w=gl`S5|76S1_PL8h|$PFN{w>4LbYmf|NbiPzI&ZZmlnxp(`cHC zX&TYp-My8aozlZs?m#M)`rNjMn5Kd6`;^NExUPqyC}^6b-R|(sHyb#P%hjtt!!!*x zH@6W&pzAt|ix-ad!HED;$}6g><_sf*@B7qh7Q^9)+1WhFWP-21e#H6nGql@nve^tm z2x>J8+qQ9Cmq0*rFQSG!@VDdHe07yKo|auIo1eT-U>O9TJH+nx;`MA7B_E!eJA` z2(h%ZfRqv;1isI+_Xf5-L{XqpsqUT`KuY<-JgTaqX&R-{KAldNUawC$Y$Bz`U|=Jq z1VB*~z^B{okx0aFU6;YY=HbJQFU}00s%q}}^Yru-`TP{y+a;=13)3{H*BgwFr!fqJ zTFqi(<1vb&;QP?;570D?YSm&eu$iBqyZkBud4TcpG(rfH$pmwAGc+1aYPCAwep_TX z95FwCj;X0hwzo?(8chm?B8Cy-@UVvO``EUP@B2sA&|U`cTn7h-c%DbM+hcq@O&}mq z6orkAO_IqmbUjEuKgoOVy^XF1v8+0YM4U*(#P@xC-)CjzAIoP3==b}}(P;EHPXkAz z*XyHc8tHTrMNt?G2JGyV5JKUNH!k2f4u%oJvKsVyJxtRilSy&!-rtTaej)j^ps!Yd<#J4a@~ zsQ_HpElH^{F){uuggOKQ0V|r=R}i*M&mySOcC4 z0AMh%TaM$tuc~TvdU}#zFo2XAwml>ki=Ze9p^#28nZU9dJb3V^^zp}k{8h2I^}Cby zKGhKnhr^Y6y?)y;LVt)xBUo00TFt_7927;N)9KP~ckz9%bocJPJL~I(<!H30r9stIOACE`?MmFFb4;fnBzFn zaM;B2{4c)za&56(KKS!li~k_$x_)(lBjFqhr#6jKnC?0@)ZrBEV>z*tHwQwuDI3 zg_3w3&Wt$ME^Gx^NR(oy=%(-PTg-VMobP<+oPqzb#8>%PRh4o&otg^*KjQm-=Vf9< zUeB;wUxhz2S^Uv?z7#=PxxvpDnwc0#*@aV>W1)x|gEKN*|-xwIkEaY-o z_V?@PdYpFKz_KhZU%p7Y-JxD@e0=NHowrYm_p`2`R4OiBzkcmARaHv){2;FDvcA4S zxje$xUq9gD#aUi|eV$lMBb`po>3Ym~@?`C+elgFS0a=z8-+JqJU%vL*B@{&=8rAsl z!@n{+JI%v~|E5sL^UgbO6NVw8D6+SAz}Q%s>(~F-*F!ifKsK9Ma$S#3$G~+xHaEAZ z*PGnEyTaby0oShmmS(fXx8FXd*=&=~=ZMFnT(~f`_=^BkDq~BcC}7(?c6MrXyB5Ch z;kqsW)#?u2uF2@=2({Wi@pz0N2zdA1Kizy~fbsFlayp$VMIr*byEPICok%1?Hk$!J zRTXl%EYWC`FbwJSdI*9*yKOK%J-P7ao4;9lB>+`bmXgT?dwT~2LCDBRfz{QgOihh5 zGE!h}Zia({2EASnMUe@DfO@?_I-Nui`j^?M0Lf(XreT=0T5am}27`kGJbALll`Fp@ zpU-jr{1k~q0>dznBpHa1Na*-}faAFMe!##$ru@yU%MBcxg}wlSAe1#tTLMs3mDSZ} zIF5_!dh~h@f*>$Hehyid==B^7qs#jG2Az&Ur(+-p;)|kmPWI>rpeV}1PtUDZn^voh zX<9sbv`P>JD2ha(kY{1xGP)k8*K^2Z(g66rM=F)%;Gj`GIH+$P=OOh2I8r1Mp;Rj3 zc^=tp8cC8^TYFvyhS6nqcA78@DHaPa=0Fq6vXNyOQ4}eci>0Zl@x|lXa$f-7_p1P5 z7~**z6BFl1r4q#BahlB*-+c2gDwWX}^)Q5O+r;BB+U*WO5YlKg$z)P^p2tZu;j}CG zv5nU2O>Fy+=g%z8xpQR{MW$YFvcA5-;b9Ncw2>qc-w)W`-NP`t93J*)wc00o^v!_d zI6qP!wcJj}U}k0lO;bsyQ*3SRkk97`f&f7fXtfM%+s1Jmve^ut=droDy>gOA-we2} z`;}>$e+G~w5lvI6)%Mxk+@{+#85tR7XlRfy4DtN{!!R&SlS*Zj`T2_of`F!J)N1?3 z7T~D>@VpTL=z1JQQRsF}?%#iiVRVrsk*TS1+U*YCf4_#NX;dnsB$EjwNhTWAuq+GH zG*6Cy8i3tR_oUwpCh@x6QZKR7A=nfd_0(9rW0OcVt&nKY(p^4)h&iAJLo3VDjfAr23F7_{3RR#yI5 zy?y&{S8dxqvHFj6=A~2+gmNyI`*Ln>rgY)LG=|aT>C<&Q&tr77ge=SGdaU}_Lb9)@AZah#PzBJq1gQIbcac5`!^R;!JssVkp;`u9IrmUU+Hp9KIA1VPpF z{Ketn{2WBs*w`YQO>^bSuleMY&(4(cUqq=?a!C+`awd~mo}3(Co}a&Trr)st>p1-f XN^++eaBYdX00000NkvXXu0mjf0}&UG literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20016.png b/kasteroids/sprites/rock2/rock20016.png new file mode 100644 index 0000000000000000000000000000000000000000..f220642d11c70b10ae4e271b2471c96a5527c5f4 GIT binary patch literal 1615 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 Z@h|2r&`i;A`6B=T002ovPDHLkV1jhZAb|h? literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20018.png b/kasteroids/sprites/rock2/rock20018.png new file mode 100644 index 0000000000000000000000000000000000000000..6adac3fe17084dde3b4100675e367d22ffcde8ac GIT binary patch literal 1589 zcmV-52Fm$~P)vHt;eU$mPnqy8O@7ugobIyPc&l0}0oy0eU+H3FnUT-&iIjwSIe z4u?Z>UbhQbN`cCfCAYgCV1T*6+;hG;_jm3&DsN%MVqrC#)s*Qp^v>%ebDmAQt}g&9 z%gZfB&oXt*z~cXZ59=b%PLM;iHd!w;jg`l}Z`I&;ckG3w-mg-e&J%*@O%J6l8u!MpEXUA=bgoz?HY`~K_GAl?YTahy*~)BHWa%F54q z^5hxiatTFI$Y!$`hK^;KgkeM`qauXh;>EdIr_*zP_~Ge^!W5?k7eXxPy1sU}_B*mL&`akW1d_MmtRaI+;>w4W~XQ#quhxzYa2kNBstW+w>tOiSQdoE|8 zX&TR;Z=q=!rqe0yb{E^u;rkZce*8VYAJFOa7z{kNwi+}VFA+k) zwsSNZFELF6DJ9)*pL)H)ix=B&uh&0&1^_(ITbELzsw(AjkvNXo+iTJ3^oXK}(I{Yd zw?(Vf!88rx_~4k@?JliW8`Ct|-EICy{U6$?GvQ=1xfh1vMi_=GhG85HAC8m5_XC7b z7!F6IX^Lf;sH%$Zj~I;t27@7KD*5)?$M;_6|BdDWUDt1=X}Y8+3WkxTUU#{D`!^Vd z&SWxurTpou@=Njk5z}c%v)Lhz6ZiAaAN=;M0Kzcb$Y!%^Ns>AT)ew`(XjH2e()2)h zySq(<5L7B<=H@Qoc|&qJll%9-{6o9leeH~Rss;ekG;MgEw;lumNs?lkCjI^ZAp}xN zE?%6&wk>vc_SoEff}$LBMc3WAx4HS`wdsFj065S>uh;*hu4fTKu(#JlRaL6h3i-TE zwOV0e;S!mQhHYEq^EQtj{q4lyX955ikH_~kO?7oW%V;#F)9De%F-ejTh7n1Uu)Xcl z>GbjafFFOXA6xtXZf6?HrIg;@Uh7VhB*Q3*mppIiIF3!RmG%c<|Nj zR;%-m|0=+t1wrsINfI{*CaaxJ*E0;mX*OFM&!2DAN~NM(uQ%48K7Ia|^Y}iO*>>(| n;wcpJ3xz`d*w*`hJI?+E?GM%d{(;!$00000NkvXXu0mjfusICl literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20019.png b/kasteroids/sprites/rock2/rock20019.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb54c95ec4d90a29b5784d24614d071cd1328d1 GIT binary patch literal 1587 zcmV-32F&@1P)3re@dhrn+zF zg^jxxrmzvMZ2Byp-aY4c&-veT&xPOFGVikJqmQnn%gZn{!rA@BD`%QCNBzICDwPs9Z!Ud)>C%N;?!v<1i^q;Fva#_J z$Fcd~gA$4&Q!M6DRh3eyP+eZWc{@oGe){S8gZBm?Nz&rgs~`T|Gz}`1GOp{Psv5R! zVHi5&vCDWo!L}{>{Q+mroVfPtRr`6PvHASH0GOuvm!+i-|9tf50+WeHsZ>OgGMHwT zt*sG35MY`n^Yayi5EzER_3Kw}U$}7g;`iULuFTxregG`Xx>Kt)7z~Cyefo@Q^)S`y zVFrUOI-MSdVPKkB3_~ZM&rvQHDVK{ZE&cKK+kC$jz_P3_WLd6uIz65|dCIT9{)Vcm z*7=x~|jd z^blfOgtc0OD2h0AXb#WwNs@#pipXR@k|gqZ2Y@t9ktB&ur}y$e07;VUE_m%kRaI=; zrq$}u>-EWGGB}Qn?+3(jOuOCP5n~pBAP6zdEUxRZynN%}B?y9G1;8}3^!o#%C?-u4 zWLaimVY?aaAQFaCTCEP_@q{$p7M~AHaA;zyFDDo=7%4CJa`7+KRQqpg>t!s z5Q0vpOB}~crvgYB42HDZT}Gn`0JU0^X0wIw2SicCbSidx!hrz>gTbe;eH0zX=E#vl z3owpd@^pwVce>pDURDwQ&iA3u5HOt7zkBnW~PUDt29 zu2;=uGQ@Gr*4BsTkE+COmX}42Or&C_NYSZfth@y!5_aEGw<#*pxF+dbW&wby2w76LP zT-P<4%@&6b&odm3c5lR1tAp!$R4Qe@`R3a%zx(c=ci$TTK$0YN&-1q{X?pxmCxpNz>%sa5#K8 l8jT6V={r5s{%^mF{TuXx$*rRtT2KH0002ovPDHLkV1fdN`yc=S literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20020.png b/kasteroids/sprites/rock2/rock20020.png new file mode 100644 index 0000000000000000000000000000000000000000..e8bfd2d41d84b68bc5273ec61cf78a3973831cdb GIT binary patch literal 1606 zcmV-M2D$l(P)Pi^lTEE;T6ZSNq}^qsth!5fStW1ab*9M^v=2~CcHLx|NoJ&GCaGO3mL ziXcc31PG8gUFb4d*plf?FN^*w4Cb8weCNL(_%Dkx$YR^J7e_`4Q@XBUn&xyEMwLg8 z9xwmL0A7E6q4L^mm#5M+<;sK2tej#7M07BKfUwLuNR*@dCJU88Qad1 z$*82MBnScwL*Tj|j^pIteDm7xE?hYO{;gYgUdlD60vLv|I5#)@>Fn$@)oKmXG|1;2 z!m!Kk?la2e2`*fiLs4LCY=m>?W~R=bonDCJ#NXK1I@Kko0+dSQpUut9F6Q$&EXzdG zGysAiU}z}M$Vh>FK1UcvMk_qVs7+^tle-aSdbdK!Rf8dGn)vB0fc zYYYz$aqirKi+jC3hG8HjbUGo$;wVuR0iY-fzTf7PPd@s~#~=UUi(drb`@xRyx7psV zAcW4$%o$A6KuSrPrkJJyNB}hLz@P_dYG4?G8#mrsx_b4};*0dZObmPk%0jc*A_zJR z4dvO{*(IOPF*9?9a(RLz>5-->wrvwdUH10&aU7fB;UP@Z%y&9r>xUn1e?71TR8^fe zO>?PUcR7FlEE5w&e*Eztq-lx}I=U_pLJ)=#y8Zh-1*<;lk7lFL~PS0jkv+ zuImv5A@9BS8^*`S&~=?k<)5T!O08C>-|wSo8ihh0DIt#IGxA?+hTovlfAutgwPLnNu-q2YIQoDkm2E>=a%SG z(-02QN(jO5@DTO7ORv|*acmlmCasoFzn=oIv9X1w>6A)Eve_)pp6wx}1SE>`{I+vq z3>v8GQJVDSQNfIo}1SI`_pG+o0yM6F~ zAb>PYD@Sh7b&dUfmqx=Q2!i7Rbd&+jW((WSQY?<*c}?ndmniBYgurzh_c9X1?RNW1fKI1_VHnKM&+_2GBU&w=LLrZ)X_U(o8=MM|B*{t~$EydGutTX-#57I5`DO)0QL!u&(=@nt?J6T91?J{vaU7fP zzWWv*Vfjl*Bj`%POIg!zP^d7s+gvMswxzVqjWkQ zJg>?3-~XLVMx|D(?SLD&o2gs?~iiU!G@ox60_~ z2on=Uo;};c_k+VWoqYY(SAV_vBE2sS0MP68?uKFbr!uC;GB!5)#k=qRR_1cK7tVhFm;YJ$7hA5-qbf7S&;S4c07*qoM6N<$ Ef;$%sxBvhE literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20021.png b/kasteroids/sprites/rock2/rock20021.png new file mode 100644 index 0000000000000000000000000000000000000000..33e313d7288a01a5a094fa52bef9710b0495e2ec GIT binary patch literal 1675 zcmV;626Xv}P)Z056^5Vr{-F3V{E#S-tQ;eb z9eYs&O<=1Af_fA952*bCS#%lL>nySk5cm%yyRND%vvGg`MN~vKS|@D{+fpUVmQ2x- zNKrCp$Qg3x-sysg;l{RP6$ntEeK)h1d(VCEd(L^!h5vOZ$5}i*JvHBG)aGlonK{>W zn#1AnajUg`b9Hrnsom}@{cHq0&%1W^?CDR7#R4Z!&XUbekftd~BG5FI(J00+^yXJz z{pq@4=#OvS{OiK&63~xY;5g3Q>}>t&nKN&qX&Sn&)9G|E41>4dKE=VoQ?l6!CMMi^ zqfz^GI2<djFADW6o37kItW<8D*c6U3RKYxy?sS4fh zKIL)=RaMb-jZSBe;c$qQl4kP>gF%R8nb@|C5Q2B!`Q^p`n82e)kFWc_PZUM8T000K zND={nVHgM@==FS(L=Xf4Qc5h##4rp1ve^lIKj6a;fB(u<97@?e!%Q(jb3kD1Ey)PvC({XcpN7vih`zTxWLslgmx~xS!D|CW?k! zx^!Xw*aU3bo(HH@N>nOi6Kb_~Fbth$vqh)VrQ1EAR+~mu6~ZvYacneALseBQ%cR%y z5n^m!rBd;QI(TIb*tR|Iy6y+1Qjt=rfU0Wr`vdm&_HkVoUDr@mg={uUE|pGN5Uj0}ph@xmAiiWJNt`h_U(o~Wp z<4T{HaFJ3Xgd_|j(iEgj34%VwVu3UTUDq%S11VF4klerj&lgi}^algT)^u0y-sVK5kQaBu*OTg&tE^m@S$lB+B)uj2awprGqIQjX`znKP&6etO2C zMij^Kl3^ITO8T-RZ7ap~I2VjrmhfRwTshT#{cY0mX} zK|M(l9LL6SY__*|iJ~F3T9xJH2fX#xNk*fXt*vb~H@CQb`|r)Qwe`<;c6RTDVbuF+ z>|;I40VXFWA8VRc&tzQQdvA`Fl{HjV#q;va%v8}d4bRK-{r7j8i;GLY4T64CO8NLz z@m@=T=MjeC7rL(3lO*xW^O1a7xUB8Q!kj!|M6dqe*sb; V(EKctLQeny002ovPDHLkV1jqxGvxpP literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20022.png b/kasteroids/sprites/rock2/rock20022.png new file mode 100644 index 0000000000000000000000000000000000000000..cf94837a03c4a020d5a7166f54a2508a4b53f141 GIT binary patch literal 1670 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 JPDHLkV1hLy7jyss literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20024.png b/kasteroids/sprites/rock2/rock20024.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe628fafe54ee2818775d9b795a43a56bd6e41c GIT binary patch literal 1690 zcmV;L24(q)P)ww*ql6W1?Ewn)kn zN%67dKDf)>?LmeQwq@E)Z_NOE+uwXYGxMAI4g9~G^ivMUVtVbwiN%W_ee~P4`T5$1 zvMkeTb?&^058+Kk^ZDG$$&<&|W@ZXipjs^ESzMgQ^Lz{=Mz`C$^~ooHT=|~^Xqr|_ zr&FsReDLclmSvMpr|^BB+1V1OPoE?VLxLd0vTVXIU~jM8xN_xBwP}7|s|1USM?X7% z{+EBwWYQlN3V9NV1nqVQQ54X1jbt)`W!YphY0Bjy@pzntg(InB$L5#r+_`tlaa{Ai z21q88m*0Q?SC`&-=PWZb1yoffibC$+-=tJ3Vi*RgRFYz`K(Uxdk|ZQaMAtPI7Z;W^ zO{?C$`{34i?7bA?l>nvE%x6cBE?lbBDin)(QmF*>dXvFmh+)Ktq7X%q(RGcvxmjdc zCI|vVQ3OEKRCHZiI(v3`^}FwW{Hx>$E z8$j3fRiJ8F7Eu(@>-A~1Iur^ybX_G3BQ`f5qiHINB2y~OAc`WEWiuFlOcQG}+c3{dc8qIQB=z188l5n*EL2X3){Al zWeGumW5*V#RLV~yb=lkNpsFhMdXpc1xXZ(bTMPy!tyTxivH^(4jq2&sZ=IhCpeS

k#ckSGdCB;wOEkjbQLl}efI?FQxY3;?RC^6=pn=guvY&8C3}08P_y92djT zaosVwT$cIyDxT-j?eMVjFAW0I25o33^MYFj} zqp?G~-C2KL=THFO_ty`EBKm!kFpMzGA*MORaomH$Pg@TliXwJ*TclD+bX`Z&ROaSp z@jRd9Gb<1_4*E~s*uT~ z5k-Msug})jHd|ZUjT<-CuRgCKzY4%{oW}ls4}fAZhh^FL{)F}Qf09fln4O)$FbuNU zG`U-26!M%nv52awG@DP>M>YVT>QKEg0!#N!5%BtDylUw-+I7ovwLB^ZrHSA!sE0NAzz2pm7Y zz?n0@U^1EDI0p}p=Xp;*pPijuI-M@AJI41XnC6JjKmXg+d-pb8xPUO_IgX;p41%B$ zi^W#Q<1w11QYsanEg*xzkaOpjiN_6cxh#gEbL!Ly3?oLrKS0+tzWVCswJ*N-`^CdF zrYfe<7#BrROdX74hcJxjc6+$43kWPM9KrWJ`hAmr-^6iT1VNxs$n)u^fBB8; z9=;L1_D+~erPeZ;%%wshho-4KdD3D$9+ODKxp3iK1VO+wM|hq`r_)7I)YG) kTb}2?(C0tf&o<5e1G~)E8Zx5!)&Kwi07*qoM6N<$f)ckZ6#xJL literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20025.png b/kasteroids/sprites/rock2/rock20025.png new file mode 100644 index 0000000000000000000000000000000000000000..3403c2e0eecb8beee098a6157df50ad85be49d55 GIT binary patch literal 1685 zcmV;G25R|F1u`@ZkYJe+t??4O zP8>&;EK#B*{{FFpQ=_Zxzzeq1`dkPQAip1e-{<|l58?m4gx7sAms6+TdFRsSp664q z*BYHpclnDiZeIVN17x$x^wiYUwS|S|wX^?DIHh* z+_~oGrBdl?KCiR8J0h1;F^nRrnq^_3Nt%M9$V5?$s%DWSiN(eFYm1BXmph%EmSqh# zUkgAK#p!aneCy=NsY~^GjedWCqR1FVf$`X(*?faEO_3y#BuSWb8QjwD< zr#N$F22m7{B$17cXM|yhAP6{)OBhCsMq@^!5tlFj;%c|sYdw4Ry!CGmA0=Ka7H=4a z@tZh~@%@0Ut!)N_AzNG9IF8GyQw_@H5~gX;>sjpV^l7)d2!cqVP#_E=R5eSbVj{~j zuIuvt`@g#JL!Hb~0H#^GBFh;D1Do~rr|7yyx7#C05)7kA7={doHmj>o5JiznrOds1 zf2Z5+v9xrKa@pkJ!$-(+hI+k*X_koN_<+u#4ydYn#W0GEVzEH4*XQ~37Z^r?vu9`b z8!($yh@u$7D55AbK@gzp8u`3VKChD`2^TIbkt0BuRp4mZ;ULNRmVpMd-SQZIAFgpXuonD2l>(yw`liVu7itNgT(e z-?xyYgWr3o1B#;jtXi!wGt(eVQ+z+*#f!bQpPQRya&m$+P0_R*gMp3fx>PDAxtvNi zt1vlPd+9=qEN8H+0q4&B^w6a~vI(oz3ca31yLvG)`%bRb`Q7je!$mLWtEr;v6 zcwT^I4X9Mgh@wak1k`F1q$x;}#M06NJ3D>8{`%hXu>d^JTV7k+U~O%K+1b<7>or{0 zLy|I7t9v5ju|pikj7DP=MP_a7DX#01&ub`(j4Wp;6!LVtJ#2fIBuRMk$1VV~v!~H@4N(+G)08Ah z01yP&-tMC7I+is!;J~3tn5L;61i_t3rF=06f(GmzG)a;miUOYJ69ge)7~=Z@lan=~ zC?W_#9zXsTMNude^7~s=mSsNs>`%X4S!o?O4~~rDG)?V7p>Qcl(s_VlF;5&PxUR=| z?9lHI2*Z#licl1pOh!UgRa|$^5yx@Rw7pzdU46o*pZ@VZ&-3kL0{{d;KrWZN0#L8l zkR%CF6p`f&xm=>vTE(&k)M^v^vw>v|k!6{Fe}Jm01VO-XXtTb)abWu$%Yi70TCyz9 zXEK?2$8ky16w@@QRQ8VMPNz$|-QoQCd7>!7vWARCV}?VUM~@ye9PYBR@{i@!)hDk9 z01(IVW+s!Fmn5m-x*kCg5XUh|lCZJy9LI6V=XG+q91k8m+@--XiFRvTT{Hoy7ipMUuB%fDWJmH4p$ zz7LTk$z~Wv7fU4r-`|^#=H|{IiXz2gfm&?>-w!r#-u%nYx3@b-+WsE{z+ON2zJCkS zmg{;rj%x^lP_(Qej^nVf(B$5|zb)Urd;dM(_g^^)f4^S)#{@u8l#Aajg|2H@*5J^s f_GA56ud06m2ekmx!YA&G00000NkvXXu0mjf_8&Jz literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20026.png b/kasteroids/sprites/rock2/rock20026.png new file mode 100644 index 0000000000000000000000000000000000000000..b71629b97b5231b4fd8b3793e52ea061ba679519 GIT binary patch literal 1606 zcmV-M2D$l(P)fGfNrxf`a7ys7Fje%c5V{1U8W5T1n|OD&^U2r)kze~R!!P6 zEmFhVTsfC6=n73t$#&YVns3L0!|#30JKwx>;Qzf+KlYFF^VJKBi{=Hx(AnGTt}iUq z&(>yBq`f?^Ua@MGtIgwNm+8a9NBD!LLrYNWvSQa`QU@!t&5`A z+S&QX#~T~BK0c~n`gz1F5r1StE;Cm3=Lh^$mL|_<_bhn zgy;EKRv&=DU`P;5SXz1kRaM(>z4dnEhrNZTiDxpIRnuI!B#IfrFy!IGN9ejr9LHp{ z8HU3VUw*m4?b~Si48!Qiq|*Ybs$knTH zPm&~5tCh!zCm{#|?e;FV?Xb0Vm-+eWpk^{6nx>*CIg%tHoffE8%S3(hG#)1YMs7Qz`}h{t(x7@jMUT z_qlUtXJ#DF1c;)j{q48g-&-4x9nxumeBK~Q5~3(%I2@rUIb>Pp(=a13v51C9ROeWLVRTKqX*U0A$ ze1B@YD5eiWDhxwLBb#^LdGF$IIC=tN<`70v^u)$YMr32-Yjk}&)q}y1VsQ@Fbr}qX z)M{0l%>@iYM-Wo<`_n84!zo0ob^kB3v}OX>w!H>m7{+f7Hi{x~908Cd2cYC~GOp_( zNm)9bJ;r03OeRA*oyM{TxUNgL+dK45aA*?Rw!P+g?ys}i3;?>W9tanU1(F1MJqy?M zDVIw`QH1MySk{2iXw3HZT{btr2Eg}&!(@*B7Fbq)4QK;&I$d%(8C6v%my0YfFA;_z zpMQRxR0LNje{fq2V|Vl}eexV933D zZMxlk27@8>dX;{E*#6{`D;I6sIrJj>+yDS^9JhlYSa%%fJwXtRTuxpQ1fgB4RgEZ$ z*xTF3ah&#NpItrI?e72mS^NH1N>P;6r|Z056^0+q_YBD)DN>>=k@~J` zTSA;g7IqP!tDyY@N`F9pL6cvRMH`@N!>a@VVsw{Dzs1(p4#T035Q22tsz{sl^SxH9y)!DG{3_z*^2Bf7eDm_vYIWLPTAIhQ zObUfO#bTabuTQVn=is12r_&=0Llzh3?6=Xno7o(e#cq-rjg`{c=! z9fS}#&VaAJ`UjrpaqHHfP!xqysYtmz&g^WJm6e~evGI`M(5KO8QYhpqX9kcYsah(P zZp*S<84f)(O{H3$CJaMbtu|X*HDp;PlS$(^1IpzRg+iV;-nhht3rpnlIi{!2oe1#K zGcYzbc2iZ=ivX5ovbuU6MNyDtnbpDGApd z_8&fc^nnU!RPhYSD2lx zkj-Xr-62sFkxrWcL{UV!TpD>0m623OdmqQ~v$0Gjjb)jHVMM*YhvN*8Wrc}}5~gX8 zPN$!(Qj#Pj38<<{6h#0`PoEp<1tS4W(_DLAT-P<0mluel2;UEgq8MFQQ52Qg*$Syt zirw8h?RJMkAxA1DF*7rT=f%jfjN=UW{PXqerv~_bWV0De(?HWSc6S@Nu1mWeFh4&- zI&CpAQNr_lTz7~h$#|ZJ;|vJH2q6TDqHyot{gD78&%kl%9Y?R{AW1TcA`=9`zvvYi zRaHq6n46n?N<;4bUGc9F&uh8f~ILiQOvjB zK02~n@O>ZK&JulrlJvKKV-T9AJK!}(mfjAZ{EzMCZ9(w)#`+q|S zK@fy=yFI@A@@rhzBcHdKpRXcB%+;$OzSrsWUacWzPbu zC6!98^!pCFerUgy$`nZgwwlQdok za2&qx*DT9=2f*`u;y7k$hb3k$Q1jTN|e@4-<=lp%_u+O=yR{p@A=Uur~Q z7_P^0T$5$F5(J0s*!K2Q78YhH77J`_Jj8JZq*5tV^{`LSb-nif`@egsIGnO69SjB^ z_`d%K%d*}H!w?|^t=0j>Vu3go7>17Ld9+$>ip2sSfBgGj?(R0e{?Pycgb=kTiZ)Es zT=P61)6{E{B-#DGgJI}28hdvdjpp^wKKt{p_V${m5sS2PdN)^B`^r~SP hXZ}0-fBbL9KLLiFnLz7;Rg3@t002ovPDHLkV1juc1wH@( literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock2/rock20028.png b/kasteroids/sprites/rock2/rock20028.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9514dacf6187d47655d3328add52e77aef95f0 GIT binary patch literal 1600 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 Dt1tFm literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/Makefile.am b/kasteroids/sprites/rock3/Makefile.am new file mode 100644 index 00000000..8b63bef8 --- /dev/null +++ b/kasteroids/sprites/rock3/Makefile.am @@ -0,0 +1,24 @@ +#POVRAY=x-povray # Must be Povray 3.0 + +#rocks: rock3.pov +# gforge -s 2 -m 128 -dim 2.6 -type mat -na rock.mat +# orb rock.mat rock.inc 0.7 5 +# rm rock.mat +# $(POVRAY) +L/usr/lib/povray3/include rock3.ini + +#clean: +# rm -f *~ *.pgm + +#realclean: clean +# rm -f *.ppm *.pbm + +EXTRA_DIST = rock30000.png rock30001.png rock30002.png rock30003.png \ + rock30004.png rock30005.png rock30006.png rock30007.png \ + rock30008.png rock30009.png rock30010.png rock30011.png \ + rock30012.png rock30013.png rock30014.png rock30015.png \ + rock30016.png rock30017.png rock30018.png rock30019.png \ + rock30020.png rock30021.png rock30022.png rock30023.png \ + rock30024.png rock30025.png rock30026.png rock30027.png \ + rock30028.png rock30029.png rock30030.png rock30031.png \ + rock3.ini rock3.pov + diff --git a/kasteroids/sprites/rock3/rock3.ini b/kasteroids/sprites/rock3/rock3.ini new file mode 100644 index 00000000..26a3cf96 --- /dev/null +++ b/kasteroids/sprites/rock3/rock3.ini @@ -0,0 +1,9 @@ +Cyclic_Animation=On +Width=20 +Height=20 +Final_frame=32 ;; NR_ROTS +Antialias=On +Output_Alpha=On +Output_to_File=On +Output_File_Type=n +Input_File_Name=rock3.pov diff --git a/kasteroids/sprites/rock3/rock3.pov b/kasteroids/sprites/rock3/rock3.pov new file mode 100644 index 00000000..2f37a206 --- /dev/null +++ b/kasteroids/sprites/rock3/rock3.pov @@ -0,0 +1,26 @@ +#include "colors.inc" +#include "shapes.inc" +#include "textures.inc" +// #include "stones.inc" + +camera { + location <2,2,-6> + up <0, 1, 0> +// right <4/3, 0, 0> + look_at <0,0,0> +} + +object { light_source { <10, 5, -5> color red 1.1 green 1.1 blue 1.0 } } + +#declare Rock = +mesh { + #include "rock.inc" /* collection of triangle or smooth_triangle data */ +} + +object { + Rock + texture { pigment {White} } + scale 1.9 + rotate <60, 30, 360*clock> +} + diff --git a/kasteroids/sprites/rock3/rock30000.png b/kasteroids/sprites/rock3/rock30000.png new file mode 100644 index 0000000000000000000000000000000000000000..e16c5fa59cf93c700d85bd25a1e805ad4e507122 GIT binary patch literal 825 zcmV-91IGM`P)B;uRsvX;0 z?fdcGb&5p6vCGoW{CysMUVY#9Rhbc8*UML~e6o4__Qr$F%`fiRcE{@X2i8UN$}C}} zaxJ=h_iGjxFO$uhIL?TjokzD`zy9szqTNKcjwNR_`c8FT#iH{P8fz13i;CR?#{{T>Pr26PMst(O>;Y&&F*e(ePe=x z>$(V`B7{KGGz=qwqA0AcetP}Mlg8f6hKWSt9zeHy#?jF+%gc*oGMDJ}`WQx>UhfRU zNKmVNzEQ1K{?#%zNvNu72_T9>JkKYYGzh~GDFb4$7=~fsc|OD8TMC7IX==msc57>& z(QbF>cF#zql8i=UMx!yV>*0AGQ50bq3C3fWsX{5G1>iUigTWBr_c=T~Vla41KA*#N zU8D@@_09kVO^Zzzo+q77W7{2;mM#;9Auh+2c4#&rT5RdC9ibAVp(`dYS z*l4`iewY8?VuxX90U04g-89qWa#>DJES8rSSXn8fsw#@2;QPL{xA)^$@ADIv;(6Xn zfOI;=`g)c5`2we>e_@)Jh{a;$a#;=ze))M4dy#M-!1n_b1&YN2mer)yvQbrmQ@TUa4F~2!WK6WYR#&knQasZgso8!~ZMUV$sduc%V@00000NkvXXu0mjf DeQlU8 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30001.png b/kasteroids/sprites/rock3/rock30001.png new file mode 100644 index 0000000000000000000000000000000000000000..4614826210e4acdb5b29b9cae88d974de55502c2 GIT binary patch literal 828 zcmV-C1H=4@P)G=8r?c<`uhS;Ou;U;MnlD2hheZ1$d}hAUH zU;Z#0j_h{3`w^`;FYxBgukVyfMf!acMM*QAPH`NU)6)s5l+3ML-`_S2t-KVW5TbnJ zM)lt1%b(%89{IdMzi*MtsrY_?VQAR)m_#DU!NH%877_{}%7sFqqpAwN?~~6P0JyG; zs%A(gC4`7En}t-XyR{2jj>qHc0G2i6)vFe=d_kDsOK$c}}d(3n?B_5BHOeT>e3ELjyc|OmdAAE3&bGHm|a`F$3-t>+9bit*$;;(=-)^k;PyzM4@1&^LeB45K?;i^5va=2|nR}a+ z$~Q2K9DLu0gpI+UmaC`glR<-&AR5c058N+ow=(+~S zaWEP=FwGn!Ny4K?-)+tqR)dnF1oC_3-CPpI%lu~4~Un7}JfOC%R?ai8D zWXu2OuRTgCm3oy*rFIx&h-08oFrlg{Ofv^Xkp%p#Tsgjw8Hz^KS1dJM(jhqR19P*s7}5 zBuPRha}UeQB@BlSE-rdVrx##Zjr|WFth*HgKp2LfD5aG!j4FnaMLwScr3zZD4$jYQ z`{3Ykuiqbhy2{Pm;eQzn1}`Y3%}%FVN+c+X#XKzQj~Cr;*Wx_9w!b^aFpSr0Yv1nb fx>lY){e|-nB0XP0hOs<$00000NkvXXu0mjfVS|sR literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30003.png b/kasteroids/sprites/rock3/rock30003.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ef0bf717b353a35259131ad39bb0a3fd825e61 GIT binary patch literal 870 zcmV-s1DX7ZP)3`OIi5M=nX#RTXV)Tj zA}&fKr=Z0WOF^^SZDndoLRlWD|<7e2m zgR`?fdc6Tmvjs)Tf)E1V4GE`N8t{3q3?RyX+fUy`s5Fm;o z?CtH(uF&`W4aad?gMo#9e~8h@LpGa5k|Zz;9hpo9y47Dqs`4{Yo^(JGR{pE0sv!dm*@F>949qV6k(c2I6Q1U+S+<^-}C&| wBxdS>{ck9xbyZbs`Miex{m=J*g!$j{FVCi3>V$ychYqix~?}HjrwM-cDLzyUL}*sRDvKdcXxmP z@$m4^t^Wj+WqIYngCCC5=@hY;Mk+dv9x56&o5#ag+?Zm&hGCYy!awe*Aq>{Fn+psuSOV##N%;_#jnWc zb7V4UI-M@@c#PH6m1e!Z-gx!u^#%Lt=XRB57>0m^5CS1WlF1~#?_=8zmSt0|mNASx z+3Z5|)~&)yzkk;GM}HY8gy@t?%N!jY6GdPcIZjSaiK2+X;GAM{iC9cSRaFXwe5F?V zdgDSumx1^1f7`r$dj(07fQaF6jHYS$zK;+gi9{UNogzd?5cr&*pPO@mt=4YKaa_jZ z3FY!KyA1OcPbgp-pVvzf!>s$knTx~>z3A*0cFeue<;wn?pahxhM~SzD`;N+mzl7fsXXbh=oUjVw!?p7!Sh zEz5e;ZkrfJo};4<<#LfyX^HXJqSqUIoXHrD+5&sHzf?ij&0kIwzq#nQDok{ z`2*W_==TS_d)LM^yF^jM!a|xTiumwh^kvJg>uyaZ7KTxv-#^23ryqy8%--Gs!{G?a za`63td_H%fDW45CfbaV)A%qE3Mx!yk-hgtsNIV`Rn_XZ!opE^h7f}@M?CrhX`M37U zzYHLRFeOReoKB~>?u1%cIQUGAP5eG5ED()zOyVF zfKq80MNzPA+kEommxkwgox_XWu)HYhIwY`mx z0&1F8x_sG5%d)FI12@^t;%H_{~EEG(H5JaOZlB?BjFgf{M4+k) zra4zS73fq4kqC`Oi@{)sqA0j-n=lMfRRzP)NhT90N|edO=gO7xSLXt!)36qcMQJu& zq?AmjQxru3Kv5J_RpsEIiRXC;5oLM#^2$u$ty?!&qztfai}Cn~dflbl?IUG?u50vq zeGU(g7!F4#tse$HfByTb=XvbzR#{w}XEYiEkWQy?-8KOG`wbLDL5K*I%JS@lJ3If> z%H;*ZFr?GzQK>AUstQ5~T(?6mmn8@S3?mLiSXue<&P*Ucr_;r<3T$q^CY4H}>l*cX zlWNuB&6~eTr4o$ChyV2&n|)=^^VS{b!0Gh{936SQdi58Hgn{QBV_5~Zwzg@tx-^?D zq?F|Ix%XcB)W-&xOeVh#hvOTUE}0i#ie(j$Qj*D}v8)2ERvS%I@qNj|hd*=&Z37c)G5{PXII7k{jt+abVUFt~3RaeBQz zySr63H(w)!pxJDb&8BhPwzIvxb7~+ynDA}Nz6MZZ7b(W9Sk z?(OZLIb&x^2vN#rvunj-aat@Er`N81KD~GE+mGABv%sb~_gxSK&Tu$d_dKt5zUvd{ YUngTbO0&799{>OV07*qoM6N<$f^1`o#sB~S literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30006.png b/kasteroids/sprites/rock3/rock30006.png new file mode 100644 index 0000000000000000000000000000000000000000..58b79dff8e65404428c2f679070e1c94d24480ff GIT binary patch literal 821 zcmV-51Iqk~P))5qpr?ul263A}K&5FQ5K9|K4N*vJCvF`l zadCF&R#g;Q*!CP>z0XJQ`+M}h@IRM0YeTJ8*|>CRwXSI@+uP5Zj~_p6{wq+^)ar!` zpKfZJwsz&p*PJ`IjPHlUaqQJ7G`{*(RTn4}3>>FKnjYY~ zWn8z6X)Xb9_3F1L`lYuQ6h*0)%jMgHfzND~va({+?e=*6x=)f!5JDi!GPy!cyzsDXi+cUbvq4o|X!QF&LI?(f09lqX44q2l6UO6&gM&Bh>^vuq z#~6nGcKb=tvP^b&U$MIS8Kco2QM89;EfEAEQMAu^oFL0GLI@N^;o7xtHcka=wThRf zDX#0F>v<-V!$Alkm`{{{vD6t`t|QNPX&G7Z#qtiCr|!lKF@I7!#u-q$g^kL z^!q-Nlp~JE$8S}AFZl4`??32zo-BJqyWPQYO4znVl1z~#iI*>5(d+dIf{o;Z$?Wtm5ho^tc%k2l)w&e4m>y$j5;%#@^D zLsb`0l*0j}X^Lf8EG`nKAPBuvPZ&kfMi_?8$z;lK7_qhW2fiPo z>v=RyWj>#i&*vEod@l$B?|AP!L4eU{bYnJ~c|r)f-5%|BhX)U~5JIrOAJb~Jxp(h= zGYE#e$NNtGz9dPimP)10Q9ijGu3P5f#m`Yy<+nR`eqJ9AM;{LUgPNwfDM?aQRn>a2 zXrU;w*Xiu82SIq^`}(L-6s2JpM&myrA6tI`#o{D7H@!Y}j6o#Lf@p!Js4j#vu+97t+ zB0@?(_Av;lD2vh`kOjN0*+G6lw`^#BK-CpHDs`1j_yf+Osv;C5T9GPoCw3DPdA>aU zxEWs-5UC2Aw3OhTy+`fH-n>Rn9*9*Ld55NDc+dVo3)z44ntW>L4mH^uAw=67tM5W@Q zlwy9qR(tT^>mSbYWzH&2PnRAoE-rlQI7M__CyHWpT}MjE-k!(I%rv)d-B>xxYy7hy zgjgvSokymbA)n9D>jmU;g1Na0lL-t4Lkfigu3JV3QQX?v-ng*kd_KQonkGV62qCcT z9GOf8-w*Nq0MF}CDix7Za{vDFsg$RHQ&WXTu{ecc82EmO5Q3{$=NOMAxtt(L283Zm zp^(RMKB%p(e)H3XK%jPT&?S!h=(>(&WiibR-R>V)Ru2{BhQj(@AN~!Y`>bkZjghiUB*me%z z572duO2tLfG=d;vclQlxn$qbU5{5CaUhOo`1&+t#rfFu_-u{ht`+)1$=P8vO`u&9Q zSmL^6N+pMU-bPBv^XFTe7rx>Ce&^}&ar*S=s7Dw^v|1j9VK5vHQA(kdqF(Bg-_{X((y=QJp9LH;>X*Mj& zTJrsXIPRm=UsJ8sX1R8)j_(IZDd}_$n(ul~Z3=)=DjbbQKbxl6kn*@ztzMzk+DFP` zdOe>w?lYMv+U>WS&z`M+cPa1$Iv5O|Xqr}Z-I<1_X+%+-q{eK^{K5zrz@F z78ZWkdhlQax~`*MueBYg?(FXVvi+|NHBH;u*tqvZRn_&yMGMucfh@}qh9MM1S@-?m z@cg`Y_-&5N0~$u<38e(x?nf|IM87}4>(_6QBq^#@6OSMNv}0Ljxm zY}H`y z*U)S_POEi$b1tw_(YN9_2ImtL3I*7G_ii6F~nXf*2ZyfG*(U}a?)lu}q$wJ{f%rfJ9X#yB`QLKJZ5l!wj{KX;$3faD?&r6D}?W5MqWjO);5Fp=o7MN^o@a7D8lT ztcWxf?p)yMsk`^?-TUo3cUGb6W%PP|ynOjP7%SrB`LEk&XXjT-k*)&*03oDd zS=K(K)VX`N1zBcz^X3?eB4c&+79>dm=X~$Qi{JioB<3E6JkR|oigpPhI%8}z&jH)E zkYyPzFNc`TW_zz*{kfYYiTibKuHm<~jVK4=#P R8M^=g002ovPDHLkV1nzuh9Lj| literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30009.png b/kasteroids/sprites/rock3/rock30009.png new file mode 100644 index 0000000000000000000000000000000000000000..c8de18bde7bbce6285338ce4b29f97fb4a2386eb GIT binary patch literal 830 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 KMNUMnLSTY`a&j*K literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30014.png b/kasteroids/sprites/rock3/rock30014.png new file mode 100644 index 0000000000000000000000000000000000000000..4e2dd198a8b4d7c02f7a25bd8acc1468ef987cc9 GIT binary patch literal 825 zcmV-91IGM`P)AnD7{@<%@tyC*&e?Zo8G9OsTxbz66utIr5iA1q)WH<4c``|ooSQcbe=IM5 zm$NLB$B%#C>U6r5cwFAv*m!jRlZ28a6_=KZYsF$dH$9!8+w~X>hPZBn%aMwUcT6pc+7p3Lpr9!jWMi2xt8Jld@MpYGT+a!~*SXudTwqAEDr;IpGmQJVFEGu2` zygvK;hs@4iLY8Cbx<)Expz9h@1h;R0|I=A+tWZ&uJEAD^^5t8?Fr?S>$mM1jjYfpw z35`Yzh{)%^xpS^i*Y%$PG)?9G`#q}FIwvP1bX~(V(@0W`a(M??mQhs&%St;Fg|aLc z08BFlKr9wRRTYNAAyE`DH6;Q9uG_>grgK-X&J|AC*oTCoC^`U7pFRg*Fc@%fP(@J` zvRRuT2$-44;<`=3Fhmsp>oxX<0G`)By(h99qtof|?%ghx%HJ4963^?SC~<@{e`Ug> z#4*hji;G_qMG?Lqu(-HDGMPZvHAGS5!i5aFu2Zi!_9hAkgTeZ6=+kJlFpO#P`2{?$ z&v58t+a?z;W;r^l6NV@3?pF4;x67q-8%9wCz|KyEcKaC1O7rGTnJ9|r^*nsvr&e=$ z^5oC;v)t5&WI+&=;_>*cL_*JbUY}ac<;IO~dHuSK?+0j_!uEEVX0x^T;K9ZZzV9DT z6aq++^yS>#m7-~;=yZD6wn?kirrYf?8vWaHbMv=bjYjjYv)tH~ecvzb?;r5$)myG# zpC_Bm5CkEDAfRa~wOW1k@bIYge{O8UFbrQrQDi7eyr`-w&z?PJG#ViY0znXLJ$$%% zcN{x5q9{6!bLoC6WfUbzauyc8TAQE0Qv77mXO({dHH#-U!#@9f00000NkvXXu0mjf DV`_%% literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30015.png b/kasteroids/sprites/rock3/rock30015.png new file mode 100644 index 0000000000000000000000000000000000000000..ea905e224fc17941d2d9d56adf31ffe03069e5ba GIT binary patch literal 839 zcmV-N1GxN&P)>Wy+`noFbuucXq^8xo6Rl)nJZV8Z%!tYs^^V|Q4~8T&r7o~ zS(a@z=~ zrO~L9%VlXc8;emC4Y#*nZk(w?RaIAR-1z>tw{Q1|qKM0vzoJ;oVObW2p_59fD2l@E z+jkahySMYctJA=IKDXvLE^!=l;lgK_ra>4+ND}Ps?vc%Yie(wBu73H$v1^)!nWov% zb$$8u>m7ze7aeba_pfU{PB#H5OOb~=5 z$pj$;l}d@x=!jHGrPXRIf3!j&M72=J0gy_m1VPAXbc8Iw!!>OVUDw#(w@H$OcDuDQ z6I4}o<<+ad0EnWP-Q7Jr&tq%r3Ei&E;h}?~D5TRSnx;`Gv#Buxvz;#_NUTkpgT!t4f{zR4~>h&7ku1&9ZNF2vJdi3kvWA{G|Op?Sg z4C7WDC#2IBw%w=QZu0c$Hc=FzC<;Lk;<_W&*B|^C1i|}1J+;Cx3^#%x*i5BT%LfNG z!=X#5l&4xPv%ha6gdmC{bX{jSbay_?J#{FK<8(AlV}3r1rfKB!IR*m<-}g}zg>t#b z*4FQLPUfAu9Fin?BZT-)Rn_WX;LvI{kR+LY|Bx^Y@qK@Def_~tX9fYHDEebE;g)Hd zWHO)7?RF`ZiadV&|DS8&7@K(cT`oa)$6s+e}euu@elf(ELy4! Rd-nhU002ovPDHLkV1j_pjN$+Q literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30016.png b/kasteroids/sprites/rock3/rock30016.png new file mode 100644 index 0000000000000000000000000000000000000000..650b9af40549641ca8b4975f339203bf6b51f4de GIT binary patch literal 799 zcmV+)1K|9LP)Acj5*^O8yn962Akjl z#YqruBSESZi6WYG(4>h(L(LWB1~yxy-7A0_h*G5rE{K#U5&5u06b$$S+w+x>@fmxZ zB4V}KH8H!|r#i)XetPxloEVo(vAD2x_wMcOTeohmJC3_=+wRe@d$C_3OG}H7H#QzT zEfnS-X_|H~o1IEuyS8v;b@k3huh;V{m4n?&gQ}{|FD)%@*XvEDrzgqhXSja-D)D$6 z!_X;}7Vo`%`|eq%WB)X%dJ(w1d~^Hsw1t$Cxw#zq{47EU+HITZX$#BBu(|p0@jnI` zhM8t&P0Dat(=^iQ6p4h+#6${B(+I;5&-;R+D3nS!mZzqy`H^71AFcTQ7i`-lolc?a zI)eegfMRihX0wHDJE&@m&CQ3u{k1zb4Agaf&GUQ!9LGh<5W_Htq6ph|NG21wu8$A` z)686E!v3F&K0Et{>v|j=o$&SRH>%YWOf!RNW=N&RDHP_ej0B@7s-B-uPun<-OQA4F zt=2%wkZ!k&@B2t8iK4+z-JzhWs`CJbp>uW?P^&eOGDL_ma=96fkLv)mS{*8tgVE0> zilV9z0?RTn%?V;LmA$=J3-BIP4@FVPW~VUC2^2+PBnaSn-j=2b;&F{+(m+*J zCMPXsXS4KreZny0$&)|U+wIQi9iC6wXf)Ro2?O8%@zv_}CeNS0ILCf}`SSJ7-+LE= z06`G!ynkOQcRCK~bP7!q2r*XfbeyC8{SRBe>RuQj#ENN}+xPFU4)Xcj+J8cZC7+*N d`QNaAlkcgjJSH=|=r8~P002ovPDHLkV1lgRc@Y2r literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30017.png b/kasteroids/sprites/rock3/rock30017.png new file mode 100644 index 0000000000000000000000000000000000000000..1ef30eb269b47386e92e48a8e144d2d53cfc2fe6 GIT binary patch literal 817 zcmV-11J3-3P)1vj!84xmc*Y4HJ1#a2 zDUB2%6$uuNL2v`CjnvC%xC#~xi(Uhpm<`?Z25MoHvgrm?s>mS`II$;=&5V8K&(lR4 zM2PdV;9Z@?c^|!>-tQcV3+T#~O5^U`A2*hkzGX5I?dQ*bU+Z+<~ra?&HV5-Z?xR?pzq?IK_?Y*OwaQvcvN7cPNUC;}oe@7l`8o z+qQV{;HMLQ<@9v+Mw+JFySGZEQbJJ_JkQ58a~Os}psdU{M^#ns-@nuN+&dGfC`uJzJf0B8F|sT(olZGC93e>(gTW9*Q3!&N zg$1X2=E#{$rhar5LeTB*6NV9zB;mRP27@6f*(dZ*@ z-uy$Y_6>!CjcFPvib9&E`2LuKg8`0Hterb@k|evOQjs`L*xz?)wKh@J44S5WYCDs; zM6c%}eX%$*TMmMd;c$d)TNs8;6h*vx^%qH!5QY(kk;Ss|2qEb8++)iJ;#J!1Kbsc;uKzze`Mk;Y_AYPVZef}RqtQo% z2*~9OBuOF&f@9uOUy3ZtwyLW2D2geSiuC&fZr;4X-d+z))9^f>t*uVG+5Bzo+=Kw0 z=RG+xj>i)qB?tl*7i)AnI|w0o`t;e_m%dYh)9G}l+wDFKf&f{T*xugd{rf#86G0ru zyng+6`@@I56JOZOmX9QaX!iT=BjA@Xj3^W=R8^(bdigMlqV_D$nLzljpd?9lEX!K= vJiofR`Nt#IJ!qciyO1iCa{c^UUs?YG`b#?X&3rq900000NkvXXu0mjfoC$^^ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30018.png b/kasteroids/sprites/rock3/rock30018.png new file mode 100644 index 0000000000000000000000000000000000000000..40bddae390f51a378426ceb3a318a4a5c7edbaa2 GIT binary patch literal 830 zcmV-E1Ht@>P)cA*4#}1|<3d-LQqeNAm={0O<>`Vo~IzY6EiHgo27>l9V`(@%Upt?3p`r zyD3$g7`vOE)m_}*PycgtbabYaX_|L$-u(RWty`<#t*l)7A`E3eisFIq5BuZ(Ym;O` zi0bw0*B)KCaISLk;wKnJ3ez-EO6}jf_wDjHMxP|RdiC<78#g|yc%Db4@)1G^%H>6L zJ&~K4Nzc~n+mHW~PzX`Ia^-Sub@kKS++3b)HjA$7D5X$J;kq8>@={gT^})-Re{39Q z6XS%IW!)c*Mwq67?+>x888i*DSqsnzLrJ;3#O>Q(-apAE#|h`=3U|}#Gyv(e34r5x z1VKnXpF@Zwj^hIA6niRRCX;#Kc|OD85KYrar3AXJ)9v;Mf&eKalF1~VH>6Z5R_@&S z>cK=|6h(UguIusob>pAe*>(@ZFtF_|aU9cXwTYsLQt3=(qEOfMWq=?EX*Q48+}t9L zV+w^?=I4u~Qi5DAOFC`P?e(z@_3t^J5{6;8ad6OFFBZ?BltM~LKA$6#$>94wi9~|Iz(La>j^pVl`^{4?e=;1uJiZmqPP&D9qC9 z^;ucDgr;dcdGf>8+uOfCJjso1`FQJxhld|G8hiV1-t40kNEy*+?5^$XyjmZ}d@$kh zq`vQeA4L&iD65Htw!gJie|U7%-kHWuEg{5mHfxPNtp8j61)r~0vLlRbj{pDw07*qo IM6N<$g0%6T8vp~7fc$t5=7QA3pqPdsZiO6=s^|&Ye5oe5WWXx!gKJ2re#sy4@aY zYpVszN*b?Um-qe^sA*beYpb}kv5}6)V$n|>pB3Q z=TWO2W10zcJw^}+CKH!?_rATq5Y%=3`*1j%4uwK2Eyb{`B;)Y}$8m8S2g6uG(=-Ny z0lqKT+A7|i3#zJG^nIU7rHU-ej7Depz916O0I1cDv8*JDq9Dr>D=SuJE+~Yk12mg$ zUcC5~dcBEh8ce5C!eJFjl2H_ea5#+Tos-Xh`So0II-MS3&1QT4 zwK+dO-+%wU{@~=K1%R&WbUIx+oi6G07sO&wBuS#(?$U1mNvU+WKNkdW9Otp`i;rW{ z=@h%Wzc8In$!0UOS{+`#EOB}|XjCfIgUj8SFGUE^P*t^{C`!S$`hO@>KaE!HOAu!*ROA&X-flKqjedW?%8Es|Ycm{F3X31gD z4y~eSs(NUVIIzRmja{-ogfj2$jH8;#cUfBK|J zz`xY#bk@T#{Bkg`*TOKYUB3K2Q50d@u620$qW=H_(g7mOE%}z*)JAG@I_?A4QQRSIxqt24#qa0h<9l_BX%` z-DFY6G0w8@@_grgzH=7uk+?>yt9L85ZRNY&UgPMfy>)OTn}DU#%4^4&yFD`#N7prk2*~9eq9`JpwYYU_amh53OK;xn{Pv|+zEqIq z!oosvEes>Z<1wAi3AUXf3`3I11ipWUBuU)6cV}&JasJZkmw>t4cfX#V`Z$hFF6U6M zyTs!Lu6xMLOq^`iLWqFjaKy7`zf`UT>bhQ5RSiwkNF-+I_XlX2hGnJjygtL>2q6SQ z1gxywUbF3NVJa{TLkvS_Vc~mfwU20;#&|p?l}aMZGObnzP18PiOFVq|;OSK0U~uZT zS{-(G|3(Nwv6x4eWqQ2m1Fh5^lbMq~VA`_1rJbLs4=jZ2_a(xL1&}=r#!{KmSQ55#}-m|;= z7hxDuDlPN!YmnIK6LgMm-AS|bcYgc!21@dvM7{fQ(= zYpIlZ0s7S}15759zVG`l&d$z}B&lo|F+O~#q3b%G&Iykn|H$6n`}LijcNg+}<@x%r zOw+6=ijq$z6J^Uv=S?#SOd4BTZy$`uWA|FXG|94Dh{a-8-oyXD{skkyG9J`?6ASb81kD@t1?UU3Qnpc)RgluEs+1LE(PV)ogSj;o4qyWX%#7`s zIWv=0B{$S0>-=^{=X{_37wM$9BD$U{+O~C1QIujft5K`fm#%sbkQ#KMs%r7!!v`yh zqBySWasU1T>9pE-_H6m5_4WGGv$OWz|8epl+5CKYrBu4%Y;Nu{J$)U+7-DLwV3x~Y zE;`Os<<+Y{fB9tL+}u|`-@Q9uk!6`UjwzRCQ4|@|G|=@N*=)wKEUR3t)}DS;Xc$K2 z)~&CXJg-Y8lg2bn_V=4OP61ud(d+pL5ivhMR~ABC)a%=;{k-&{aAs!u2O$K-;w1I@ z4r60BK@i}1T_j1S)9K>-K9I1u_|3N;@|T3&Zm*KdWtp73hG7g*t!{Ah<}{Kd(P|x& z%VilG8^QBB%*;&R8yy`f_VY^rK~>d?VGKF8ZDAM&LI~nG!L}{JFr?S(q3c=ld6Tzq z_ehe<^Z$=9ilRmoMWj-&v-20WZIMo^93HlCoC0Aea9ximia0$zCrRM=_+)ROFplHB zX7iXZ6dWD>LoS!);=(1LH}U;|kr4|`%OFV-zV8!7(WMG6Z8@DzKiJ=II-O1j&+DM; zIhxHQ&d%Covl^B)%=q{yvMgiU7SEsm`s0;Lk!5LB2tliL%+b*amX&8=;SO)!tnvEw z8mg+IC<>+04KywD@d|?=Y)?)WzVG#Xf*|1b?Ku=hM%Q%=LuYyUcS@z}q*5um-5!!8 zU2JS@tqv5%aoh-kAg^g!B}o#(Fy!640}c;cEG*n%cDBUh$4?j^A4O4QwzhU__4@X2 z16u}Yx7&{n4i3Hma9s~oRdL-8FJG=ut*$dMF-E)Xa(;e+>w1kVTmCmmlC*^owIB#9 zCnu*SK%p?f+FFf5;Tl4O?C$>Ec=6(o@A|oc-&cQBRjmvU4=+iQw5MqqeBW=Froras f_M=bXKl8i?LCZIgnU(%I00000NkvXXu0mjf|Iw38 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30023.png b/kasteroids/sprites/rock3/rock30023.png new file mode 100644 index 0000000000000000000000000000000000000000..92d35979672e2a8f14d7324811b9588b0faa5913 GIT binary patch literal 829 zcmV-D1H$}?P)wPfr_19EKn7>>4{57iVz+|H05u z1GNZMNK+_MuDRvrd#_P%RQVWbKS0!;dP+V4J#dIBVJVHOUK|x7p7?-_vMjo=2~K8V!s{I2_I<5|f*w?p-D{>>!<1)W%cZGQ zYQ*Dllv4PGm#xCyqwed zc?&5mj*hAToSmJsyZak&-&P5QG(4}55PzfV>t7bH^8a|)K@eC%h(fd3D)_#SZFgB+ zT_K&GMM?`HG+w;eV=x$Cnm2j<`hc~y!h=%j_n%$YwE*;gH&n_mjg5^JrL^&VpL+cS z!_d)nK`xi!?%g$>KK%vH>vQ||3R2otDXq%24FUT7epLv4b86~l#xQhT*QMVdaCB6| zFm$@zE{lr`wA&ptP2=R`to-ujA3qHh20>ssj`Qna;AfNy3PAv-nZPh~E-qXw%cjvd zC7=I{O68bWum1e8(Ky{3D!d|n?)$#2lq$S?*NeAW9fT01QZuw#9V(S$_V*9IFPH!N z;VL`y`?`##Y1v35vZ3qx=H%ojnQpiHywi~-rBr1U`#;Hh&@xa)2AZ%C00000NkvXX Hu0mjfcJYaf literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30024.png b/kasteroids/sprites/rock3/rock30024.png new file mode 100644 index 0000000000000000000000000000000000000000..2749bc9460f6a85b11e379af9e34f38ca57c8eb9 GIT binary patch literal 840 zcmV-O1GoH%P)w&2AG{9L0ZQ&v-oJ@%SsYNn{5M z7$OoOK7?dvQ79YsmD~jn&?QpD6Z8exu&mS<$a~a5ifAQ-nkbP`U~ppmF|p^v;~B@( zMXRX9Zlp@x^}oCSd(Y81_n!NQ|G0$9HjBl=t7p%?Z5YOJQxwIr>$;zIcYl59d6Q57 znlP8kH8(caYMYynxO%n3(UHONafkKwH9mecwqL*g>H9^v=%VJz%5qatl-k|9cL0H8 zGJ$2;jK>qo+7yNwhj+JG%sQ!7aNi!)z{XlHBD3Lbh?bkV}?Tu5U5mE0eJJ~ zEzh5Sx3x&j9O2EI*S9=xLZz~bZ99Zvh#)|rutc%AL^K*Dkx=P&2c=vtTfUS~lB9Z} zkZ%ZrK&y4c{rel3=8)Me#Phtr&WN0zj)+7A9zU)(E+vdaBJ_F#s?`dnIV2W~k&1O(lg&+uc`t+MQ+v49B(P*?3h9UX<61Q&Mpxf;eh9TW<|D1d>@wt8b z2C^(Oo1K-?=~Vgjba)=4uM`S`Q1*SFe%~aSOz`DP4^>s@_e~6=jq8s2{JD+q2iUg5 zXk_!^#Sgz^GO6-Hg}(1Mbv;Qu9>en{gkeZLE+fk^c6Qz}o6YF;28g0at@aR2Q_=Ng z$u$2#VGsn(cKgI=w>ykRHl0ovMTt`^=BZRxxp!}aCr>uXWYV0RbkMW}K@fa(hjYQ6 zPN&A%*{oq%HlvY^BuRMQgkEoeW!a=sI{ADKT~88*;5aVt-u?dbcw&uZIP6vjWvy0WfhNtU7}Bz67_ zP9~U4p_>6~*G-|_OqhkU=(;aZzegXS%R*Plyg*9B&{bzs$|RW(2ux_K7}t$jOR{xk z$ye%Pn4xXrz(9BXmgn&O&UZL)fNw0}x+WoHac0J9Rw@{rLfCp&N7c3%B& z;Efwjduwa+@$9UFgM(u*#^Aa?!?H{~dGd5?fB&zYt9;^BMMY6s3k&m)<2V85IVe@I zva$q00N%VgK&$mjds>jvr3s?AXxsL7y}n{3$ryqFRI4UtXC1_G0$r~_mPw6LB|16z z`_k%{5}KybG)<${>71if5@DJ;bX|ii%V;!Kk!2GA0`A_u^T@WX#cP58q_f(bbJ(^8 z&U1*Oh&(Ugd0k}L1eRrhF@~Z*tzKVgUJKMTty!&_O+gT#sub(%b?ABpK@cJgBSg^< zfB-@WC@o`i^Zs_LwYfbNm}MClOW-&*4iAqZiXuiMhA0|>5CTF77)uaEL-cxmaGvAV zt%mzKL;5;$l8kZt_D=wS*RTIXqw(W^=E;Pk-+vF^AK<}*UlE28D5bT}{a;*JlB5k? zH(Hg73d7LBSc0Xco1m0J6a}oWuVQ(*hDKuzXJ=i^%{h>y5`rK+>U6qCmsaMSd*kug zo1dS} zfPViy(lmuE6AT6+f*^vbQuO;5I6XZ>7)Ibc_g=hs*}f780BM^3Q50x#&J9(i7>`p} zRuzO0IF5}xFR-@u12j!VmSx`a=fAgo|HIK#U{MsIuIr2Ca@nodml1{$vTOoD5D>=< zgF%2aO%cZlo;~~RUboxZoqCji6$IffV=R)S(nb`G3`HqJRTUIPfx#d^xm@=4_Fg`` zxbSzs_FntG0Dw}(m1Ws=oS9}(6kf0QZYPe})c5rr>J!Y6GS5a-CVT(@002ovPDHLk FV1lRgcSisK literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30026.png b/kasteroids/sprites/rock3/rock30026.png new file mode 100644 index 0000000000000000000000000000000000000000..e6af352f9f4f18f816401e04526af1e337766178 GIT binary patch literal 823 zcmV-71IYY|P)wzi!h|7{yN<`^L3%9Xlq8L)@lG zS|O+kQW+pf9S}n%CR7&Q0v-VI0%c<41?bEG8%u*K0aAyELPJ&6l(ZJ)k4wYB}^uYjtm)*Fp$>x+x$8lLBC^Yc|`S{jNng-T_nt|(L9n>X#| z9|Nn^+0|0XTIluqFbo}*m4j(!0mO)+7)wiw4PDPv-;Xc)#?6vZ~BDzaGvl&W~~ViTrmU^ooW z?e;(jfh0-LwDbb!yyZ9_T3`LbiO9*MO^AeQbryqxhfe1Zl%^3y5q$qMIOiZlf@S3) z2m-ENUA{drp&$s;l+wGLM<^6>7>_vsfH8(Bis5-ae18bW#!yuSs;XdqzG}BxTTfm0 zqjMr5A>^`Y8UTQU0~c`|qgpM)^L%I;MK-HrdHE7*wOKI6kWQ=U^#)j4y0G%oCv(o7 zZnuYW*+wpBqTe4tQKm3ASApZ~gL95VB7yOEjBdAw;qVBYb9_HSKL8nHO<9&vDit9J z0!E_|JkLj=kV6oJaNSSn_XjvS3ef4e@H`)``w<%(FTXV{9xo*bLQtxL^9UOouh4Gq zBA3e|omP=b5fCE5Gz}0!kW31A@ZjNgj9;)*|YU~K@bim0vTh?AP6%+qAtrR6pIBo&ORum5QGFoQA8X^(De)+KYn_r z-QM|OTN1~CUsbDBnzmiKEJ-5v_V#h{;&~{_6b1tyhllR-PUm3t_3O85$9)rlf*?%W z_UV;;KDV;7v)hzq*|zN>c6XioozB5eKZgHQUjSsVATU;-VY~nU002ovPDHLkV1huj BX8`~J literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30027.png b/kasteroids/sprites/rock3/rock30027.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac5276469f6b43adc62be30b10d4b4c59247dd8 GIT binary patch literal 827 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/rock3/rock30031.png b/kasteroids/sprites/rock3/rock30031.png new file mode 100644 index 0000000000000000000000000000000000000000..f5cce0fce5bd3c14bf06722a643bd182e44481aa GIT binary patch literal 819 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/shield/shield0003.png b/kasteroids/sprites/shield/shield0003.png new file mode 100644 index 0000000000000000000000000000000000000000..2ae53d4526d3368d0ef64551d60959d4da3836f9 GIT binary patch literal 3513 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/shield/shield0005.png b/kasteroids/sprites/shield/shield0005.png new file mode 100644 index 0000000000000000000000000000000000000000..7efa4f14b81e8350e7f136dbe886d1c07a925d4e GIT binary patch literal 3446 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 Bh++T$ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/shield/shield0006.png b/kasteroids/sprites/shield/shield0006.png new file mode 100644 index 0000000000000000000000000000000000000000..18eff6c668b743ffbe214b0de0fb533ea063a7a8 GIT binary patch literal 3368 zcmV+@4cGFCP)^3 zXY8?OJR^@|$90_Gh#-|9a9cWHgn&Rx8X_t{N=b>R3e7_wK!{cfmkKF;K=c8MM4CjZ z9b%+jBC5zhWN1hmrBK;Xk}=Rxtg**4p7FWu>sssQVb0o_OB{Q~mto2$9nGAXbN2qf zz5eyD@4qnIWZZrC-8IHoi*t@7NuafcwH8^HK?s3EhYn$CY6<|Ll)|1pd!Up;v)Kga z9E>q&t?|k$ui*IcgE z&-0LF+4N>?V>e1}@7}!)&iOZ`lrYBJNkmPpHIgJjnx+5%B0`?$&ojnU7>2(%G&BSN z7#bQv7>3xhXAg)7^?Drx0|PjC@E~^X+=(Cv003vto<*9b&|07OegEwzpM3Jl+}zyH z-*Tb{4jdSeQa-^L`=qs&wcBl#Wm&l&^E~fqt$zUEkCaj`SZh%fg<*_=h>lAs4*~#V z44&uh3xXhwq6pP$6=4`cN{NYy2{alFG#U*|PEMBlmobKO=g!fE3m0B`>7|!G)qenH zVPWBo_p(`ThA4m!8Dl;T0C}FH)oOurjx5Vyi~)e}8e={L07;Up{q9HeJTKp9t-qZl z$^Gqi8;mVKcemn;i;Iv_qSNVsbB>{*AvBv!OiWC$JkLJ};7tEAZ2`U(*}1>h7By?#B{ro4BG9s3lQG}@WKAa6RmZz5uz)> zIS&9R&N+k-Kf@UN0s#2Fzn60^>h(IJD8l6ABt}O^QLooQL?9x}&(G_N7cc(Bi!Z+T z2b&^#=+L276h$?yHCn9}u3WhStu@j#J!`Ff9l&GO+RK|IcRdh7e3^4z3xeP-Ddl0` z_c1y;ibkVRMsCI!k|g;CfKQz`abjleHLmHpOG`@#!w^ISwB6=u}<9`N_TWedlmebY2Id3YZKFk<9?)yGG&%@y0AO;5q5r$!#h%x}Lo<4oL z6cz8LbtQlujIkY@^Bu+*=A@Z1re&?2-%64I(2ohzMD#Ud%xs?LNRkA3o}(y=n$~(p zQ55@*96552`_4Mo4T9j&Fboj{0i=}ZcDvA8pDl{wIRNKwC!zO3nx@|a@IapDCm3T7 z7DWMTEvnV3Q&9&QW4{C7cY${y$~ph!;NW2S-WUTR#Oqq?&)+r{8-z;;y4@}~=TJ(4 zF$T`xaoBaP%NT>pJo>@Mq-ol|t(I>Lj{z7bqF*VBqU&t$zkXwUeEipdH4?DdY`#B= zqDKITJkOD)DY7j4pDfF!wt~U+fQS+zYFlgHAfi-jeXPG%0YGW3-wuM{<+UsN#UKbg z#u%)%E&y$T`I`d~!CG6|-cK?<17QEEJu0OH5uu+>IibEqW^W4L1aKYzi0DqO^%I

OdC8$< zLPRjeKuQUv6oe4l?s!>)|6+_in`If&G<`?ZTWgUd34Gr#$!*6KEmx59U5v4#jIrgy zn{!iR4B|LOQ4~ni6h%?2t?+FZJPm+|=s$xX7_Zf8>x0n`GbH+nDS(GK=SN+W2iTsj z>w>Eg0>1A9=m#Zh?U(wmGp+#U+_j&Z{@qp?y%{)#=X8PlTe)P6u?vi`gow%y_rvpj zobx?fX88sng!r_S@?qci;a;wuR1!7DJYkIa4`8_yt&~C#1Q0?@0C;lCtlt1U&l5^1 z;YfO(2LLFFVs*{P?M-X#Dv|G!+5hWs^&;Q*OC8`6lyyX%n0G$z_U(2+zuyYq_sc-K zjwt7R2N4Bv9HUyT!uNdyLEwfbepyP{>YuGsM0DO-dtu8My$d+!KWU8lQ@7<9V`Ubm zlsdZd{P-mR=NMzwxvo+Q!^6YawQHBxY&L5H0|PZ7#CL=cUz1WE-YQaSA%yUxlv6cyJ53twjjnkKN3J zD|fC`ck2q+u7Z{%2^eE=ReP5SIGGs7@y8isKMvsIMNur=W@4-0Y;)ZOR|ZPc6pXPi z0ywvhC=pF1NpjkW{Gp*C)M_<^VTf9-hGw&gIF2zhGjm5A$9JS@`UdCxl(qJuE#L^H z)R!4!57cTk2q9c^+ARi*F=wr{?R7H@eF=33=Ug(zhMe2FX%QEh-HL0iBV$Ys-~wZ; zLqwh1L~y4N;=`Qt2ZRvsi=wDJC0tW1P17F`(KAH!-|L7HQHzMaishQi{du03jT<)( z6&gXLMv7(*P#Xti2yUpz=e#{m5Ktt1NIaR7UK z-$$iVK@bE;lB8@@_icaMTKj_g!d1s_7-O#h_$#gTuO>-ScSW>DqXA&K;5|4v2$%1L zVd%uP#~Ax<0FQId7nM@8LWl}utOB6I7^@Ibyb)HM^CsuK$~k|WbN;MSYInU}=aotY zQ4~Q+i8M{o>2zSN{XHUj6u`4Y)GPU1>n2jlQO@}bLWm=7e67)F;GTQ#L8VfG)*9Vz z7fVY^=yWNw}T z?xJcC1gO<&001|!*6nuDZntNwwU1b9Pp#u|EuvcMShRMlEWLbu#r6t613?T%n)harjPC0DV>2%QPbX*tUAJ^RGbpR^`$Zi1TXZpzn zYPDJ^*4^>Z>-EYOClQ@|Hp2!a5YE?vUKix8-&Q9Ru3c7G(#b2dM}JSW_4w~=KTf*>fL zylZo~%Ayn2VHm>mymIQ(xt-R!W{fG7mLutTUYRnwYXZw%1-FXEn8aFpxj%YvRcyFg z%{l)EA;e%5MSG=`d&kGeF)=ZL!-o%}(P$tHL+sqSvrL)X*>>g16)Y|;Vt#%eozC)6 zn1njv?J_mY^BkV%m8-Dl`>;@UT-7e0YIfGNDPGMnTp%f!& znwB}S`=Oi8qVX3u0<_RNji=WxSB z`?!MT$P54=qM9-0aGvL7`EhY^u`EHlsGB4S;y5lF&qOo>V77mmt47ex;Fh$`q?893 zW54ft-V|f(PR7_p&iPxM^P`nYr6jMELLA4>ce~xWIF13}d28)I-3ps-X^)U|{s92{ y0bCM7oFk&2u-2CKoc_7_bz{t>TWQDtEBr5WsA-c6ngP@R0000 + right <130, 0, 0> + location <0, 0, -130> + look_at <0, 0, 0> +} + +light_source { <50, 25, -25> colour White } +light_source { <0, 0, -100> colour Gray80 } + +#declare ShipColor = color red 1.0 green 1.0 blue 0.9 + +#declare BaseTexture = +texture { + pigment { ShipColor } +} + +#declare Grubby = +texture { + pigment { + bozo + color_map { + [0.0 color rgbt <1, 1, 1, 1>] + [0.8 color rgbt <0.9, 0.9, 0.9, 0.5>] + [1.0 color rgbt <0.8, 0.8, 0.8, 0.5>] + } + turbulence 2.0 + scale 3 + } +} + +#declare ShipTexture = +texture { BaseTexture } +texture { Grubby } + +union { + cone { + <12, 0, 0>, 0.5 + <11, 0, 0>, 1.0 + texture { ShipTexture } + } + cone { + <11, 0, 0>, 1.0 + <8, 0, 0>, 2.0 + texture { ShipTexture } + } + cone { + <8, 0, 0>, 2.0 + <3.5, 0, 0>, 3.8 + texture { ShipTexture } + } + difference { + cone { + <8, 0, -0.01>, 2.0 + <3.5, 0, -0.01>, 3.8 + pigment { color Gray20 } + } + box { + <9, -4.0, -3.7>, + <2, 4.0, 10> + rotate <0, -18, 0> + } + box { + <6.5, -4.0, -8>, + <10, 4.0, 8> + } + box { + <2, -4.0, -8>, + <4.5, 4.0, 8> + } + } + cone { + <3.5, 0, 0>, 3.8 + <2, 0, 0>, 4.0 + texture { ShipTexture } + } + cylinder { + <2, 0, 0>, + <-9, 0, 0>, + 4.0 + texture { ShipTexture } + } + cone { + <-9, 0, 0>, 4.0 + <-10, 0, 0>, 3.5 + texture { ShipTexture } + } + prism { + linear_sweep + linear_spline + 0, + 0.5, + 4, + <7.5, 0>, <-7.5, 10>, <-7.5, -10>, <7.5, 0> + rotate <90, 0, 0> + texture { T_Silver_2A } + texture { ShipTexture } + } + prism { + linear_sweep + linear_spline + -0.5, + 0.5, + 4, + <4, 0>, <-7.5, 5>, <-7.5, -5>, <4, 0> + pigment { color White } + } + cone { + <-12, 0, 0>, 3.0 + <-10, 0, 0>, 2.0 + texture { T_Silver_2A } + pigment { color Gray60 } + } + + rotate <0, 0,-360*clock> + scale <5, 5, 5> +} + + diff --git a/kasteroids/sprites/ship/ship0000.png b/kasteroids/sprites/ship/ship0000.png new file mode 100644 index 0000000000000000000000000000000000000000..817fe1b4a01433be65d4e6e5f2f7493e3802b3e0 GIT binary patch literal 1965 zcmV;e2U7TnP)o>YOhTH3v^}6vQjma3Yy#~CskIPe)zqyIP<87c zK~dLsWl=zFshuWG6FZLWT;D(EoIPB}qbxA9C95>`D_!aM>dO9{@9*EahTFQW{{ubr z(5|st%^#l462?fd1awT4M84$3FY)nBB4C zhoiR)bltlCf-wfA1VRY%`5uH2M3Kc9P)cBoz*>0cowrWD^2#q(-QBK0K~SmGs8$zg zHk(XOU&r$rC?yf%<|jRGiQT(L#tt6*P3hr>M^-I&#Y#7oQk!$R-skVXf5(^*kV?4) zDJ4=WwAM%|vDT7Gxmarn!;ow?L#ySJN~Hmd<0!1PWHM>2wdHEHRxXvU6)#@=d$G}I zmi}W<*L64hzFz{k^UgaC-E~)CY}c*_0C1eRhe|1sQUVeo1WKt+M~K%F$8nHSq7*3Q z;5ZI_eZ54HC7;jXc|K>(oIQQ?=*Q!qe?C#{7Svf@3=a>#s$;lmbv_ z4e7K36cBGlh`6X+H$fs1Zzz+=Af=+OFUO4=6<&MoZ2%s8@B!X^_l-ldv-5oP(VvS) zkA76VdbM1>dbRv7&>he%TeggZVMwFVAe~ON!{+Ii7ju?y;wz-ZSR=9tRKpu6XLyC#R1d z{djztuT%9yLqp}^;o)}J(kSH+hK58!QLEJ$9NftI^&9Ew$)gm2B?uy#O&_2G6NVOJ z3{ezcT1P}tM5E!ObwI1t!Wh%J>h&h`^HriKBAZRq-=F8%XMZww;K06Di^boTo_z9& zy*D`ul!u0f3cbC(Xl-yDg)sr=&rdKkbT?bK?r7(zC~Vz&?-(%N0iB(l z#dTfk^*a6iJ)}}ewAKs`4w6d2*|VQ8H#bKnlO_n30DRUvIV3 zD8aRB)A+vM3H^l&pR>5w!1G%8evA3}Dwi%@*?;54T+#Eqau`OXn?R?grpm6HEVNo4 z>9it>6pe<5<4CjyDFu!r@%;#EZKt}G5=2pml<_I6(Foe`%dT3&Fv6I4FkHHH1<&)^ z!yv`P#1~X5Rc2;pi__CH#b$G9#opQhHO3SsC%-7p%*+(~`*WjPw%q&tayT7F5=E9| zQsH?4uA3y0h$}lbQy_%J+W51zF?1B!_C_MK*3|0_q9|nBwtG-YFgI5zpFH`={%Uoh zsI~siY1IKea^y&7N;!@ktyZh;>Q}@ElI6Ct3=%^b6N5UA1S}x1)*>ZXE81?xVXoEc zR4NrJmFnrKsp&mo7BQ-^FD@OhBobd{in0O z@0YdKW#9Liot+zBTJlc+JNH)7iieVK>Zzxm7~i?`N6+{5<)~C@xNd?*qruS79ZXNp zkjwR8j6h1r<$5s&N~LmneEh9FK@hCU%ixNRep9(zu8_<1qLd+@@55T?>B$1HapNYW z6f~P2LI||BoIQKK`2PF9e<28h@9o@w1q!sMj39~vlF7KahoQOY*jKMN%5T2;>pfq6 zb#>KWsk;J=qPP*KQb`gCr~PtgkW%6}3V;(QPVYZ__{iSX2K-*2^?JRmRNO-o2?wy9 z;#pi=EPwF9ANHO*ccJUE<(h#C5l@)7+AUhg_l_SwvH#eyzwEEo7FR77zfD~ilMs+d z#PhjUtCc_e@W|ft=l`~*E`D8If!1oZvN5o*P%EE1cd>Z*@E_MK7r#wifm&->Sg4f` zAO6$c3l}c_&s_Z8+OcE%E&qS?wr=aTR!RQ=)kcr=5h-B+00000NkvXXu0mjf_L|6` literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0001.png b/kasteroids/sprites/ship/ship0001.png new file mode 100644 index 0000000000000000000000000000000000000000..50cafa14f97e40214aedd0fd3334e0e272844adb GIT binary patch literal 2053 zcmV+g2>SPlP)Q}OSxho;>b7ge6i6lUU%KlXfo+=`|W$i4<7vW)Mi4RPa6i6 zQecdQ<4AVyynghlrw-5c^mG+A6YjIxFsRlBu<(5l!lI|Ad!ShS!>N&x9bb!YHw?OC z$MBex0a>$Ov2nC|JbfHny#>EU4F;G@Bk_Xz}pFzdZ8z zroHtVV=T1R1VM<_A;uWOFhFZ?-DqK3wq!VY za&CHlesQW+TP>Y9@nLawwLbS%K-Ia6d_F(6ef#$DbUHOwt5v7Ex^i@OX36EYAOu*J zg=K*;7D5=lXm4xH$C09iU43<7G?`3}MvLR%>#x6AeBp(EOq5EeFUl5c9=+z8Ywqmt z?;lf2WrUC;Tef7_y0wo?Cex0k7)r%RBw{#D^e&}CB9R~#bLi+u5RWI2QsFoW(&;qW zY?@>;O>b{EU0pf)`tsa-^RAJ-d%wS5N|CKrYt@yNN@*jYYh~H7W5>X?*ItX15xWqQ zj*c`zpb_DxKq*ON3xQG=nM{(WpZ*hv4?oGHkN%eX?)%w; zQ&WF8_uv2X3r=Ngpt)Rbv|KK8`t)gR+s1L?v|28Ui_3I&b~7|I!pO+Y+;Ynuq*C3q zS^-K~G@DJp&}td94r#RlTCEV@5AnPZtphx-MXS}KR`Xa`SU_tH!18jLdcDcktphyt z(Ea0Yyzz>;_uii#8X6k>LJqHabZ~I+SM&4p%+Jr$(UBk)Q*?G_v27b6G)qehEG{lk zuh%(#{9Uw;+UU8HABLJ%E5sO$);?i)?(=|H%pwRvq_mis`8Th9c3g79HpMgO+7c zt=6$^*b!PrFhOw1}pu2tG=vC{vZPSXhXrE!&Rs&O7hpdEVNVo;dMe zd_TaLs4at8t9H3uVPK$t95|!^T-Tj@>7|#pd7d{1(BI#8(6(cg%PS}qo%TkfiES%_ zARq`@C?yd>;JN~(1W{N>JTJPoECE=!u10GJf~bOTzx^(*>tc){m+NG9_N}$WM-9VJ zt*+vEE+2gG;Y6u)rU;)2{Bw{}%EFm5XG^hIY#;~%Y}-ak+3xMp_3CP!cw8Z+L~Gg! zArK;h7eXMVg=Iwp%(>21E?245RuRG?l}h0_aZa5o(QLXbFMl%KXf)>*78Z-H>y=#B z{aiyhZ?6aF?adznN_~C#F|Ez0lmaOQN+}YFL>o>47O_~YT~&!?3F2{k%^E^j7!w_= zTCGkHv~b-f)#@rsOUuRO<#MrJZ%l__c)`~CrJ(0@#$2UREjAjB;(E{itkn8n z0n37Hwu4k^2(XY+v?~QtLKwDxOj&@%%1Vtl-<&Nz`|MvQl#&BM5SGF)EUj0yO9l-? zjn<+)I7dTt5JUq=5Ey*l2cmX*^ysS-FTVKCiItVg96&AWolxx j4$2i>(G^|M|FQlDK|mtNS@$|NK2a!*%@#v~Ang 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 Sj{R-`00003iK literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0004.png b/kasteroids/sprites/ship/ship0004.png new file mode 100644 index 0000000000000000000000000000000000000000..fd71f2f2bd0cba5532c3ff1598f5953c0a5634d4 GIT binary patch literal 2181 zcmV;02zvL4P)t1Gd z!GJ6~s}VzGXF(*y216_f!EGQmHCR(0nkKSu?Mu`6WJolo4}GEf*a-GnCDvAf&K6q* zfh{G1!Va>#!0b1}+`ij6eVCO*SuP?rO}hVNGRfqe@B8I{{^x(r!2gRTCVsj9zwsXJ zyG%|dJ2IoAYsYu*{>dx%3g~9~PM}Xey=_lC9%I+89eY-V_2?Zi|nnR(-l z-(QGCOd=5-(Ad2BM|b_a+v%rE8-|YS zx0DR|)~(y`I1Yyn9U54@`hoqOoypO$v2|#gPOWy0M#HD8tBc1UA4AhLEKA_~O@|G` zz_tyhrlux$?)>>Z%j*rGJ9g~I$6~S3xw%<_z$cNgS-pBC%a#q`c@il-JkO)i@VI() zo@g}2*w`c7cV8btAc;mTEXzRGHQssWF9%+H@mD{-7oayR{R=O=u+KD2W@ct-X^9bu zL|C(CglN>pwoPo?CKj`a$D_2jw_;fqXU~4Y)YQL;$74jJ7Pf5x8nIZs?fCJNle4pP z-#G$bbM%>Ko_S^K)~$Pv9s7ijKmM5B-fnt&x)~l`iKZDuA{t5wG)>x$rNK_qqMfR;5fy?*w~Y|+?;o!uQ|GT^X7e0%GOh-P9X%?wncC6eYCVB zsMTsjA~u?)qw6}BWe|xNL?RJP(;|_G($W$qmFmDW4Njdp&FRx;=wd#>b#4t@0 z)3k_2qr~HJlF1I*+gmA>N=PYlLWs7{KKuNxVXuP@4Gq0wnr6CKEMgcM@wiPg*@0zQ zT)S3d+qUhbQr&cQ^^r)lQ7q0Z^r(rh>oj|{>9i0AQbI>ZD=jTCy1P4ByY}JS!w-)< zdG_q*Q}gqsZ=G2kpslSfXIa+pxpU`Uzj*QDA5y7~oMlB?ySh?nnnp6&&X->nsMRU} zgkivyD{}~;070$hB7}}@Ybd2*ng+3$jZzx6ZDX1?sZO7K(AU>XZ*LFLsD%(ZrfH(<8cL~!28f{RCWc`Gie$14(=^dEgLvG; zFbs6vTyWZ;tu2laIzlKMr^1_W{^`Kpy}!HT>bjwx5{dZprfF{J>`Vb5r6deP^o5aO zDA9EtrJz=8;CU`V5Ta>3dt3I%k-y)O zvp0acu9w$!EgOy6S$fML_|=Ff6QD^I#^IdvuHEd0e_QL$zAN^L%_iK!}hqlt>w%RES|{Ky%rp z1Su8ux<{iCqEvuV0a6AWK75p^sWSl7>vf)b>W5^r!`W1-BYXJp&7Hj|D8RGN?%Lng z)!8~Q(9en$D{x(pQ>Uh=RH_7lq+V|jhKe8v34&%klnPNw;``0K`o2f4R;N~Tsn;94 z{q~=6oH9x&Hf>tZ$jC547>tYzWwY6#CyyTe=fQftQNCl)!NE*!F@95^5v^IPKipTvar-4^}4%|Nr_S+&5Vcme#q6U4xfJdFOD5MNf?GmDcQ7X zJ*F8U2m)-|W^gc_9vxjf{@#0keam&-ufg6Fbp86=M1OyO4w~n>rfCQPmK7l$Pq1p$ zaymNNxO}Pvo zQthn|Jg{<0K7aJBMx*KMqM#c$j!i6Emd;5DhS5CJzpAC?tqB?p7b)dJ<>>VF_2M|C z182{kJ9zTs=~o+#My6hGq$`ywSFg@fE|=-)>ADV>Ot#}V<%QaFDVNJ!xzb#nnVC5n z4X-s4iE!r3XYayI{@3r_`&*@H&0Gr6%%~8}4?w9@Dx5oaK3^=(7fPja;lhPN{`~n1 z`ENc)*Y%8TTcd_yWWq353~DijVK`l_)~0Vau2`JE zZQ84B5lciGmwl7-Jl9sfjB`s-5|FilJ1&*%Xi9zW*00000NkvXX Hu0mjfp#VSg literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0005.png b/kasteroids/sprites/ship/ship0005.png new file mode 100644 index 0000000000000000000000000000000000000000..e6df9605e518f86d22c87c302eed449e575aee6a GIT binary patch literal 2197 zcmV;G2x|97+H8OML`e9!E7KfKO5w%2hI zf2?J@j#7f;tr7@C#h@xhDs_-bgHVDhRYHj50Eb+Ps-BP{l^cijz^O{$(hE=pMI{J3 zRie6(Iw^_mBzC^+#P+V&yEC)%(ZiaEfH4J|_LBc)W=EP=zxlua=Xu_Rby7NA-@0CF z{a{L`>s#M?>yJ}EB&@U^i#+}G&ki*;HD-Hyas%tJ-bz|CXhTD~^|8lx4QZOrOE3NA z=!OjqKZvXHS{Hcz^&=m&wQcG#O%>a=;~O_-vZqd+KCvEfU)Gu&-MKSA)YzEGs;UA= zsH(&hPdqx5$*jMtYjX7Emw$UKmuv5dB7x($SeAh-%LpMD89DpGdO^Y}j^=W?!(aX_ z%kq6Lb#-;*dwOyMiXx%w3PK1UmUwP$~YqxLT;_~Hjgoud6bRK%BpQfgbxNblYxHyhO zvA9IF>ht*HkCIB+1c5{lglL+EWvQG$|F4mK`(Dhi2k6%vJ#ys82W@R_*}He|a`Wa5 zlF2yncpclf{|H4<(RCHW&@fG%Sj@yQbk3juH-$oxR4RpO8tA%$s!B9BH)l(wrNzO)E_{6Z7*6 zBoa1JBq7T(npVrBrfKN9fn{0L)miwy&&0$ep68=!I$O51Wyi-S#x7l&SW{N-Wi*va z<#+Gi{fVL|%+Aiz*qFvNb=uq82!eq3-usYOUwsutk#L+vbX`W4Rb*M<`vO&!@VpRN zj_^E}g@pn)Zp<<`xSQtYjkL7%eXSn8i|#p^$z=AG%jM$i?Ch9jS-oFmu)aQp=lN{i z+QIhiJ4mM+iN)fSN(CSUWMo;n2W}V|s%nr(#K~mR+`2W(C!hQs+m0<>xbXS+&gwnT zLZL7`Gc$8yW@hF@EN1l9)x~<7o0}0r!Z36+O=WShfa`i>G8?&bX9nMQahw1UXj%4c#~EM{QaCW%CnhacXaZ)?lu z=jRK>8#iu!r_8RHo7UErsa&ozo5^gzvMfx~AQm&xbsgUivF#YTt`P*`a$#v?GD%cb z$1rqEQ$y2KgitXI70-)Er{hSHz;UWfOcO4 z^5jR{y*tl^3zs-}@K?Q_=O#Y=bZpi0>o*0AGzVGqon}5P_8~~EZBzyMkqO-FzfAQky zW2<$76+w6H${*gb6@H9 z`8OQLAq+#Bn>Vs^=Kzl5qG=ip4e9v6z(Yf$qvuEF=NG2G6?E6G{NZie`i6WzLRDp; z1{g&(;2;Q)B#Cm_!S{Wdnlf~BY#|o2xq0&zckav~gurz@!qCTY9301GcJ?k46IYiz zK}cU;4v=tN7a;_a1nG1tzGu%P2Zx8x{&iuYFuf}1^Uv=)uIqYSRRtjK`>O%9>lfQQ zJ$++jZf;@v=FQu~zF!?19sTU}iHR#CrBW#o1b)_WJQfxfDVH7Q=H_u-mrBJUnM}~# z-9f3e#L`lkN~MD5)%vhyne_B@51u|f{Eq8-_hNA0ba?K$Umox8?;E;yZF)oqL7`Bb zDin&-SFcVD16;XsZA1vc!a`wcrO13)nx^GrF)JU7Sp&8m%bTW|)wJ4KK-YC_+r}_- z48y>(3{+Jkl}eJyG@$DSAAdZ2;_bKpyl+L&|Ak~(ZZ!;jKv9%dNs?%2NDt_`Mssu1 zkZs%4)x{7(VsUZt#JlhQ<^Kn|nlw$z8-|`00$jH`;yBK??%V@C&;vct13l0Kt*ZY3 XR*rIEYSH-&00000NkvXXu0mjfGyg!G literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0006.png b/kasteroids/sprites/ship/ship0006.png new file mode 100644 index 0000000000000000000000000000000000000000..3d1b0f70993056317ba33cfddacc337a6515175f GIT binary patch literal 2209 zcmV;S2wwMzP)>4uoO{om+xc_nUuRl+ zr<5|K(00-;F-r#vvIeOTLtK+WV$>H*@IfEIXnfl2Ci`Z@H%T^{_~6T;`(&a*HX5@F zEh2wpp|EIMO8?L)*mgSYbnd@%_F-DwN&(l7aUb|5^Dy_%xu5$z-{0??3shKLUD=*W zj|l*@l7nsCx~1o-r=C1q$uTRg>grhPgAe{P^6964liRjc-qngZ`oQosadjwQb5)V(cYV1oWks4jiGBf-we42^eG8wQEO3 zUA-@8dwcugmX?+yOaD3Nce(WDn}6;#%^H()1&-sOP*{NL3OxV({zJ)Rb?O%f-MDdM zcE^q#Z>3VHL&0D$8x9*i4Gs0D9(m+9vb+27V*s#s@1Db%OiQP#QV1bYRi%Sc3RP7= z2t{}I;}vsNxz~yP`}ZFk8X5v)6irP{M<^BW`>U{f_bx~&d)BXS7}>C4gGng`K*2f1 z!h(fBzz;$wJP&AZ&vXKRtq3Sv>ENC{dk%MWbo2-zaQ*sq#N!b&77Lq?J+>9KwKd?} zhwAEZs8A^2!i7tyudjoy`w$FrC`!4Y0E*`3bnB^8r%y~wOpaC@w4tFPd+^}FV}@bi z?Afz$92kK0p`s9


"); + emit infoText(i18n("Disconnected.") + "
"); + + conAction->setEnabled(true); + newAction->setEnabled(true); + disAction->setEnabled(false); + + menu->setItemEnabled(joinMenuID, false); + menu->setItemEnabled( cmdMenuID, false); + menu->setItemEnabled(respMenuID, false); + menu->setItemEnabled(optsMenuID, false); +} + +/* + * To establish a connection, we need to query the server name, the port + * number, the login and the password. + */ +bool KBgEngineFIBS::queryConnection(const bool newlogin) +{ + QString text, msg; + bool first, ret = true; + + /* + * query the connection parameter + */ + if (newlogin || infoFIBS[FIBSHost].isEmpty()) { + + msg = KLineEditDlg::getText(i18n("Enter the name of the server you want to connect to.\n" + "This should almost always be \"fibs.com\"."), + infoFIBS[FIBSHost], &ret, (QWidget *)parent()); + + if (ret) + infoFIBS[FIBSHost] = msg; + else + return false; + + } + if (newlogin || infoFIBS[FIBSPort].isEmpty()) { + + msg = KLineEditDlg::getText(i18n("Enter the port number on the server. " + "It should almost always be \"4321\"."), + infoFIBS[FIBSPort], &ret, (QWidget *)parent()); + + if (ret) + infoFIBS[FIBSPort] = msg; + else + return false; + } + if (newlogin || infoFIBS[FIBSUser].isEmpty()) { + + if (newlogin) + + text = i18n("Enter the login you would like to use on the server %1. The login may not\n" + "contain spaces or colons. If the login you choose is not available, you'll later be\n" + "given the opportunity to pick another one.\n\n").arg(infoFIBS[FIBSHost]); + + else + + text = i18n("Enter your login on the server %1. If you don't have a login, you\n" + "should create one using the corresponding menu option.\n\n").arg(infoFIBS[FIBSHost]); + + + first = true; + do { + msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret, + (QWidget *)parent())).stripWhiteSpace(); + if (first) { + text += i18n("The login may not contain spaces or colons!"); + first = false; + } + + } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':'))); + + if (ret) + infoFIBS[FIBSUser] = msg; + else + return false; + } + if (newlogin || infoFIBS[FIBSPswd].isEmpty()) { + + if (newlogin) + + text = i18n("Enter the password you would like to use with the login %1\n" + "on the server %2. It may not contain colons.\n\n"). + arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]); + + else + + text = i18n("Enter the password for the login %1 on the server %2.\n\n"). + arg(infoFIBS[FIBSUser]).arg(infoFIBS[FIBSHost]); + + first = true; + do { + QCString password; + if (newlogin) + ret = (KPasswordDialog::getNewPassword(password, text) == KPasswordDialog::Accepted); + else + ret = (KPasswordDialog::getPassword(password, text) == KPasswordDialog::Accepted); + + password.stripWhiteSpace(); + msg = password; + + if (first) { + text += i18n("The password may not contain colons or spaces!"); + first = false; + } + + } while (ret && (msg.isEmpty() || msg.contains(' ') || msg.contains(':'))); + + if (ret) + infoFIBS[FIBSPswd] = msg; + else + return false; + } + + /* + * Made it here, all parameters acquired + */ + return true; +} + + +// == message parsing ========================================================== + +/* + * Pattern setup - rather long and boring + */ +void KBgEngineFIBS::initPattern() +{ + QString pattern; + + /* + * Initialize the search pattern array + */ + pat[Welcome] = QRegExp(pattern.sprintf("^%d ", CLIP_WELCOME)); + pat[OwnInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_OWN_INFO)); + pat[WhoInfo] = QRegExp(pattern.sprintf("^%d ", CLIP_WHO_INFO)); + pat[WhoEnde] = QRegExp(pattern.sprintf("^%d$", CLIP_WHO_END)); + pat[MotdBeg] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_BEGIN)); + pat[MotdEnd] = QRegExp(pattern.sprintf("^%d" , CLIP_MOTD_END)); + pat[MsgPers] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE)); + pat[MsgDeli] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_DELIVERED)); + pat[MsgSave] = QRegExp(pattern.sprintf("^%d ", CLIP_MESSAGE_SAVED)); + pat[ChatSay] = QRegExp(pattern.sprintf("^%d ", CLIP_SAYS)); + pat[ChatSht] = QRegExp(pattern.sprintf("^%d ", CLIP_SHOUTS)); + pat[ChatWis] = QRegExp(pattern.sprintf("^%d ", CLIP_WHISPERS)); + pat[ChatKib] = QRegExp(pattern.sprintf("^%d ", CLIP_KIBITZES)); + pat[SelfSay] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SAY)); + pat[SelfSht] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_SHOUT)); + pat[SelfWis] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_WHISPER)); + pat[SelfKib] = QRegExp(pattern.sprintf("^%d ", CLIP_YOU_KIBITZ)); + pat[UserLin] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGIN)); + pat[UserLot] = QRegExp(pattern.sprintf("^%d ", CLIP_LOGOUT)); + + pat[NoLogin] = QRegExp("\\*\\* Unknown command: 'login'"); + pat[BegRate] = QRegExp("^rating calculation:$"); + pat[EndRate] = QRegExp("^change for "); + pat[HTML_lt] = QRegExp("<"); + pat[HTML_gt] = QRegExp(">"); + pat[BoardSY] = QRegExp("^Value of 'boardstyle' set to 3"); + pat[BoardSN] = QRegExp("^Value of 'boardstyle' set to [^3]"); + pat[WhoisBG] = QRegExp("^Information about "); + pat[WhoisE1] = QRegExp("^ No email address\\.$"); + pat[WhoisE2] = QRegExp("^ Email address: "); + pat[SelfSlf] = QRegExp("^You say to yourself:"); + pat[Goodbye] = QRegExp("^ Goodbye\\."); + pat[GameSav] = QRegExp("The game was saved\\.$"); + pat[RawBord] = QRegExp("^board:"); + pat[YouTurn] = QRegExp("^It's your turn\\. Please roll or double"); + pat[PlsMove] = QRegExp("^Please move [1-6]+ pie"); + pat[EndWtch] = QRegExp("^You stop watching "); + pat[BegWtch] = QRegExp("^You're now watching "); + pat[BegGame] = QRegExp("^Starting a new game with "); + pat[Reload1] = QRegExp("^You are now playing with "); + pat[Reload2] = QRegExp(" has joined you. Your running match was loaded\\.$"); + pat[OneWave] = QRegExp(" waves goodbye.$"); + pat[TwoWave] = QRegExp(" waves goodbye again.$"); + pat[YouWave] = QRegExp("^You wave goodbye.$"); + pat[GameBG1] = QRegExp("start a [0-9]+ point match"); + pat[GameBG2] = QRegExp("start an unlimited match"); + pat[GameRE1] = QRegExp("are resuming their [0-9]+-point match"); + pat[GameRE2] = QRegExp("are resuming their unlimited match"); + pat[GameEnd] = QRegExp("point match against"); + pat[TabChar] = QRegExp("\\t"); + pat[PlsChar] = QRegExp("\\+"); + pat[Invite0] = QRegExp(" wants to play a [0-9]+ point match with you\\.$"); + pat[Invite1] = QRegExp("^.+ wants to play a "); + pat[Invite2] = QRegExp(" wants to resume a saved match with you\\.$"); + pat[Invite3] = QRegExp(" wants to play an unlimited match with you\\.$"); + pat[TypJoin] = QRegExp("^Type 'join "); + pat[OneName] = QRegExp("^ONE USERNAME PER PERSON ONLY!!!"); + pat[YouAway] = QRegExp("^You're away. Please type 'back'"); + pat[YouBack] = QRegExp("^Welcome back\\.$"); + pat[YouMove] = QRegExp("^It's your turn to move\\."); + pat[YouRoll] = QRegExp("^It's your turn to roll or double\\."); + pat[TwoStar] = QRegExp("^\\*\\* "); + pat[OthrNam] = QRegExp("^\\*\\* Please use another name\\. "); + pat[BoxHori] = QRegExp("^ *\\+-*\\+ *$"); + pat[BoxVer1] = QRegExp("^ *\\|"); + pat[BoxVer2] = QRegExp("\\| *$"); + pat[YourNam] = QRegExp("Your name will be "); + pat[GivePwd] = QRegExp("Please give your password:"); + pat[RetypeP] = QRegExp("Please retype your password:"); + pat[HelpTxt] = QRegExp("^NAME$"); + pat[MatchB1] = QRegExp(" has joined you for a [0-9]+ point match\\.$"); + pat[MatchB2] = QRegExp(" has joined you for an unlimited match\\.$"); + pat[EndLose] = QRegExp(" wins the [0-9]+ point match [0-9]+-[0-9]+"); + pat[EndVict] = QRegExp(" win the [0-9]+ point match [0-9]+-[0-9]+"); + pat[RejAcpt] = QRegExp("Type 'accept' or 'reject'\\.$"); + pat[YouAcpt] = QRegExp("^You accept the double\\. The cube shows [0-9]+\\."); + + pat[KeepAlv] = QRegExp("^\\*\\* Unknown command: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"); + pat[RatingY] = QRegExp("You'll see how the rating changes are calculated\\.$"); + pat[RatingN] = QRegExp("You won't see how the rating changes are calculated\\.$"); + + // FIXME same problem as in previous line + // mpgnu accepts the double.5 arthur_tn - gnu 1 0 1243.32 365 6 983722411 adsl-61-168-141.bna.bellsouth.net - - + + // FIXME: can't move. -- needs board reload... + + /* + + opponent matchlength score (your points first) + **gnu 1 0 - 0 + *blah 1 0 - 0 + kraut 1 0 - 0 + + logged in and ready ** + logged in * + otherwise " " + + */ + + pat[ConLeav] = QRegExp("^Type 'join' if you want to play the next game, type 'leave' if you don't\\.$"); + pat[GreedyY] = QRegExp("^\\*\\* Will use automatic greedy bearoffs\\."); + pat[GreedyN] = QRegExp("^\\*\\* Won't use automatic greedy bearoffs\\."); + pat[BegBlnd] = QRegExp("^\\*\\* You blind "); + pat[EndBlnd] = QRegExp("^\\*\\* You unblind "); + pat[MatchB3] = QRegExp("^\\*\\* You are now playing a [0-9]+ point match with "); + pat[MatchB4] = QRegExp("^\\*\\* You are now playing an unlimited match with "); + pat[RejCont] = QRegExp("^You reject\\. The game continues\\."); + pat[AcptWin] = QRegExp("^You accept and win "); + pat[YouGive] = QRegExp("^You give up\\."); + pat[DoubleY] = QRegExp("^\\*\\* You will be asked if you want to double\\."); + pat[DoubleN] = QRegExp("^\\*\\* You won't be asked if you want to double\\."); +} + +/* + * Parse an incoming line and notify all interested parties - first match + * decides. + */ +void KBgEngineFIBS::handleServerData(QString &line) +{ + QString rawline = line; // contains the line before it is HTML'fied + + /* + * Fix-up any HTML-like tags in the line + */ + line.replace(pat[HTML_lt], "<"); + line.replace(pat[HTML_gt], ">"); + + /* + * FIBS sometimes sends tabs, where it should send 8 spaces... + */ + line.replace(pat[TabChar], " "); + + switch (rxStatus) { + + case RxConnect: + handleMessageConnect(line, rawline); + break; + + case RxMotd: + handleMessageMotd(line); + return; + + case RxWhois: + handleMessageWhois(line); + break; + + case RxRating: + handleMessageRating(line); + break; + + case RxNewLogin: + handleMessageNewLogin(line); + break; + + case RxIgnore: + /* + * Ignore _ALL_ incoming strings - this is needed during the + * login phase, when the message box is open. + */ + break; + + case RxGoodbye: + /* + * Receive the logout sequence. The string will be flushed by the + * disconnectFIBS() callback + */ + rxCollect += QString("
") + line + "

"; + break; + + case RxNormal: + handleMessageNormal(line, rawline); + break; + + default: + /* + * This is a serious problem - latin1() is fine since the line comes from FIBS. + */ + std::cerr << "PROBLEM in KBgEngineFIBS::handleServerData: " << line.latin1() << std::endl; + } +} + +/* + * Handle messages during the RxWhois state + */ +void KBgEngineFIBS::handleMessageWhois(const QString &line) +{ + rxCollect += "
    " + line; + if (line.contains(pat[WhoisE1]) || line.contains(pat[WhoisE2])) { + rxStatus = RxNormal; + emit infoText("" + rxCollect + "
"); + } +} + +/* + * Handle messages during the RxRating state + */ +void KBgEngineFIBS::handleMessageRating(const QString &line) +{ + rxCollect += "
" + line; + if (line.contains(pat[EndRate]) && ++rxCount == 2) { + emit infoText("" + rxCollect + "
"); + rxStatus = RxNormal; + } +} + +/* + * Handle messages during the RxMotd state + */ +void KBgEngineFIBS::handleMessageMotd(const QString &line) +{ + if (line.contains(pat[MotdEnd])) { + rxStatus = RxNormal; + emit infoText("
" + rxCollect + "
"); + /* + * just to be on the safe side, we set the value of boardstyle. + * we do it here, since this is reasonably late in the login + * procedure + */ + emit serverString("set boardstyle 3"); + } else { + QString tline = line; + tline.replace(pat[BoxHori], "

"); + tline.replace(pat[BoxVer1], ""); + tline.replace(pat[BoxVer2], ""); + rxCollect += "
" + tline; + } +} + +/* + * Handle messages during the RxConnect state + */ +void KBgEngineFIBS::handleMessageConnect(const QString &line, const QString &rawline) +{ + /* + * Two possibilities: either we are logged in or we sent bad password/login + */ + if (line.contains("login:")) { + /* + * This can only happen if the password/login is wrong. + */ + if (rxCollect.isEmpty()) { + rxStatus = RxIgnore; + int ret = KMessageBox::warningContinueCancel + ((QWidget *)parent(), i18n("There was a problem with " + "your login and password. " + "You can reenter\n" + "your login and password and " + "try to reconnect."), + i18n("Wrong Login/Password"), + i18n("Reconnect")); + if (ret == KMessageBox::Continue) { + infoFIBS[FIBSUser] = ""; + infoFIBS[FIBSPswd] = ""; + login = true; + connectFIBS(); // will reset the rxStatus + } else { + rxStatus = RxConnect; + emit serverString(""); + emit serverString(""); + } + return; + } + emit infoText("
" + rxCollect + "

"); + rxCollect = ""; + return; + } + + /* + * Ok, we are logged in! Now receive personal information. These + * are completely useless but what the heck. + */ + if (line.contains(pat[Welcome])) { + char p[3][256]; + time_t tmp; + // Using latin1() is okay, since the string comes from FIBS. + int words = sscanf (line.latin1(), "%255s%255s%li%255s", p[0], p[1], &tmp, p[2]); + if (words >= 4) { + QDateTime d; d.setTime_t(tmp); + QString text = i18n("%1, last logged in from %2 at %3.").arg(p[1]).arg(p[2]).arg(d.toString()); + emit infoText("

" + text); + playerlist->setName(p[1]); + } + return; + } + + /* + * Initial parsing of user options and making sure that settings needed + * by us are at the correct value. We use and ignore values according + * to the following list: + * + * p[ 0] - CLIP_OWN_INFO + * p[ 1] - name -- IGNORE + * OptAllowPip + * n[ 0] - autoboard -- IGNORE + * OptAutoDouble + * OptAutoMove + * n[ 1] - away -- IGNORE + * n[ 2] - bell -- IGNORE + * OptCrawford + * n[ 3] - double -- IGNORE + * n[ 4] - expierience -- IGNORE + * OptGreedy + * n[ 6] - moreboards -- IGNORE and set to YES + * OptMoves + * n[ 8] - notify -- IGNORE and set to YES + * rating - rating -- IGNORE + * OptRatings + * OptReady + * n[10] - redoubles -- IGNORE + * n[11] - report -- IGNORE and set to YES + * OptSilent + * p[3] - timezone + * + */ + if (line.contains(pat[OwnInfo])) { + + rxStatus = RxNormal; + + int fibsOptions[NumFIBSOpt]; + + char p[3][256]; + int n[12]; + double rating; + + // Using latin1() is okay, since the string comes from FIBS. + int words = sscanf (line.latin1(), "%255s%255s%i%i%i%i%i%i%i%i%i%i%i%i%i%lf%i%i%i%i%i%255s", + p[0], p[1], + &fibsOptions[OptAllowPip], + &n[0], + &fibsOptions[OptDouble], + &fibsOptions[OptAutoMove], // equivalent to OptDouble, can be ignored + &n[1], &n[2], + &fibsOptions[OptCrawford], + &n[3], &n[4], + &fibsOptions[OptGreedy], + &n[6], + &fibsOptions[OptMoves], + &n[8], + &rating, + &fibsOptions[OptRatings], + &fibsOptions[OptReady], + &n[10], &n[11], + &fibsOptions[OptSilent], + p[2]); + + if (words >= 22 && n[6] != 1) { + /* + * need to get boards after new dice have arrived + */ + emit infoText("" + i18n("The moreboards toggle has been set.") + ""); + emit serverString("toggle moreboards"); + } + if (words >= 22 && n[8] != 1) { + /* + * need to know who logs out + */ + emit infoText("" + i18n("The notify toggle has been set.") + ""); + emit serverString("toggle notify"); + } + if (words >= 22 && n[11] != 1) { + /* + * want to know who starts playing games + */ + emit infoText("" + i18n("The report toggle has been set.") + ""); + emit serverString("toggle report"); + } + + /* + * Set the correct toggles in the options menu + */ + fibsOpt[OptReady]->setChecked(fibsOptions[OptReady]); + fibsOpt[OptDouble]->setChecked(!fibsOptions[OptDouble]); + fibsOpt[OptRatings]->setChecked(fibsOptions[OptRatings]); + + return; + } + + /* + * The beginning of a new login procedure starts starts here + */ + if (line.contains(pat[OneName])) { + rxStatus = RxNewLogin; + emit infoText(QString("") + rxCollect + ""); + rxCollect = ""; + QString tmp = rawline; + handleServerData(tmp); + return; + } + + /* + * Still in the middle of the login sequence, still collecting information + */ + rxCollect += "
" + line; +} + +/* + * Handle messages during the RxNewLogin state + */ +void KBgEngineFIBS::handleMessageNewLogin(const QString &line) +{ + /* + * Request the new login + */ + if (line.contains(pat[OneName])) { + emit serverString(QString("name ") + infoFIBS[FIBSUser]); + return; + } + /* + * Ooops, user name already exists + */ + if (line.contains(pat[OthrNam])) { + QString text = i18n("The selected login is alreay in use! Please select another one."); + bool ret, first = true; + QString msg; + + do { + msg = (KLineEditDlg::getText(text, infoFIBS[FIBSUser], &ret, + (QWidget *)parent())).stripWhiteSpace(); + if (first) { + text += i18n("\n\nThe login may not contain spaces or colons!"); + first = false; + } + } while (msg.contains(' ') || msg.contains(':')); + + if (ret) { + infoFIBS[FIBSUser] = msg; + emit serverString("name " + msg); + } else + emit serverString("bye"); + + return; + } + /* + * first time we send the password + */ + if (line.contains(pat[YourNam])) { + emit serverString(infoFIBS[FIBSPswd]); + return; + } + /* + * second time we send the password + */ + if (line.contains(pat[GivePwd])) { + emit serverString(infoFIBS[FIBSPswd]); + return; + } + /* + * at this point we are done creating the account + */ + if (line.contains(pat[RetypeP])) { + + QString text = i18n("Your account has been created. Your new login is %1. To fully activate " + "this account, I will now close the connection. Once you reconnect, you can start " + "playing backgammon on FIBS.").arg(infoFIBS[FIBSUser]); + emit infoText("

" + text + "

"); + emit serverString("bye"); + rxStatus = RxNormal; + rxCollect = ""; + return; + } + return; +} + +/* + * Handle all normal messages - during the RxNormal state + */ +void KBgEngineFIBS::handleMessageNormal(QString &line, QString &rawline) +{ + + // - ignored ---------------------------------------------------------------------- + + /* + * For now, the waves are ignored. They should probably go into + * the chat window -- but only optional + */ + if (line.contains(pat[OneWave]) || line.contains(pat[TwoWave]) || line.contains(pat[YouWave])) { + + return; + } + + /* + * These messages used to go into the games window. If KBackgammon + * ever gets a games window, they should be in there. For now, they + * are ignored. + */ + else if (line.contains(pat[GameBG1]) || line.contains(pat[GameBG2]) || line.contains(pat[GameRE1]) || + line.contains(pat[GameRE2]) || line.contains(pat[GameEnd])) { + + return; + } + + /* + * Artefact caused by the login test procedure utilized. + */ + else if (line.contains(pat[NoLogin])) { + + return; + } + + /* + * Connection keep-alive response + */ + else if (line.contains(pat[KeepAlv])) { + + return; + } + + // -------------------------------------------------------------------------------- + + /* + * Chat and personal messages - note that the chat window sends these messages + * back to us so we can display them if the user wants that. + */ + else if (line.contains(pat[ChatSay]) || line.contains(pat[ChatSht]) || line.contains(pat[ChatWis]) || + line.contains(pat[ChatKib]) || line.contains(pat[SelfSay]) || line.contains(pat[SelfSht]) || + line.contains(pat[SelfWis]) || line.contains(pat[SelfKib]) || line.contains(pat[SelfSlf]) || + line.contains(pat[MsgPers]) || line.contains(pat[MsgDeli]) || line.contains(pat[MsgSave])) { + + emit chatMessage(line); + return; + } + + // -------------------------------------------------------------------------------- + + /* + * Beginning of games. In all these cases we are playing and not watching. + */ + else if (line.contains(pat[MatchB1]) || line.contains(pat[MatchB2])) { + + if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty()) + emit serverString("kibitz " + autoMsg[MsgBeg]); + } + else if (line.contains(pat[MatchB3]) || line.contains(pat[MatchB4])) { + + if (useAutoMsg[MsgBeg] && !autoMsg[MsgBeg].stripWhiteSpace().isEmpty()) + emit serverString("kibitz " + autoMsg[MsgBeg]); + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * The help should be handled separately. A fairly complete implementation of a + * help parsing can be found in KFibs. + */ + else if (line.contains(pat[HelpTxt])) { + + // do nothing + } + + // -------------------------------------------------------------------------------- + + /* + * Simple cases without the need for many comments... + */ + else if (line.contains(pat[RawBord])) { + + /* + * Save the board string and create a new game state + */ + KBgStatus *st = new KBgStatus(currBoard = rawline); + + /* + * Save important state data and stop the timeout + */ + ct->stop(); + undoCounter = 0; + + pname[US ] = st->player(US); + pname[THEM] = st->player(THEM); + + playing = (QString("You") == pname[US]); + + toMove = st->moves(); + + /* + * Update the caption string + */ + if (st->turn() < 0) + caption = i18n("%1 (%2) vs. %3 (%4) - game over").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)); + else if (st->length() < 0) + caption = i18n("%1 (%2) vs. %3 (%4) - unlimited match").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)); + else + caption = i18n("%1 (%2) vs. %3 (%4) - %5 point match").arg(pname[US]). + arg(st->points(US)).arg(pname[THEM]).arg(st->points(THEM)). + arg(st->length()); + + emit statText(caption); + + /* + * Emit information and drop the state object + */ + emit allowMoving(playing && (st->turn() == US)); + emit newState(*st); + + delete st; + + /* + * Set the actions correctly + */ + emit allowCommand(Load, true ); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + emit allowCommand(Done, false); + + return; + } + else if (line.contains(pat[PlsMove]) || line.contains(pat[YouMove])) { + + KNotifyClient::event("move", i18n("Please make your move")); + + } + + // -------------------------------------------------------------------------------- + + /* + * Being away and coming back + */ + else if (line.contains(pat[YouAway])) { + + emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, true); + actBack->setEnabled(true); + line += "
  
" + i18n("(or use the corresponding menu entry to join the match)"); + } + else if (line.contains(pat[YouBack])) { + + emit changePlayerStatus(infoFIBS[FIBSUser], KFibsPlayerList::Away, false); + actBack->setEnabled(false); + actAway->setEnabled(true); + } + + // -------------------------------------------------------------------------------- + + /* + * Catch the response of the user responding to double or resign + */ + else if (line.contains(pat[YouGive]) || line.contains(pat[RejCont]) || line.contains(pat[AcptWin])) { + + actAccept->setEnabled(false); + actReject->setEnabled(false); + } + + // -------------------------------------------------------------------------------- + + /* + * Catch the responses to newly set toggles + */ + else if (line.contains(pat[GreedyY]) || line.contains(pat[GreedyN])) { + + fibsOpt[OptGreedy]->setChecked(line.contains(pat[GreedyY])); + line = "" + line + ""; + } + else if (line.contains(pat[DoubleY]) || line.contains(pat[DoubleN])) { + + fibsOpt[OptDouble]->setChecked(line.contains(pat[DoubleY])); + line = "" + line + ""; + } + + else if (line.contains(pat[RatingY]) || line.contains(pat[RatingN])) { + + fibsOpt[OptRatings]->setChecked(line.contains(pat[RatingY])); + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * It's our turn to roll or double + */ + else if (line.contains(pat[YouTurn]) || line.contains(pat[YouRoll])) { + + emit allowCommand(Cube, playing); + emit allowCommand(Roll, playing); + + emit statText(caption); // force a pip count recomputation by the board + + KNotifyClient::event("roll or double", i18n("It's your turn to roll the dice or double the cube")); + } + + // -------------------------------------------------------------------------------- + + /* + * Got an invitation for a match + */ + else if (line.contains(pat[Invite0]) || line.contains(pat[Invite2]) || line.contains(pat[Invite3])) { + + rxCollect = rawline.left(rawline.find(' ')); + emit serverString("rawwho " + rxCollect); + + if (line.contains(pat[Invite0])) { + rawline.replace(pat[Invite1], ""); + rawline = rxCollect + " "+ rawline.left(rawline.find(' ')); + } else if (line.contains(pat[Invite2])) { + rawline = rxCollect + " r"; + } else if (line.contains(pat[Invite3])) { + invitations += rxCollect + " u"; + } + invitations += rawline; + return; // will be printed once the rawwho is received + } + + // - rx status changes ------------------------------------------------------------ + + else if (line.contains(pat[WhoisBG])) { + rxStatus = RxWhois; + rxCollect = QString("
") + line + ""; + return; + } + else if (line.contains(pat[MotdBeg])) { + rxStatus = RxMotd; + rxCollect = ""; + return; + } + else if (line.contains(pat[BegRate])) { + rxStatus = RxRating; + rxCount = 0; + rxCollect = "
" + line; + return; + } + else if (line.contains(pat[Goodbye])) { + rxStatus = RxGoodbye; + rxCollect = "


"; + handleServerData(rawline); // danger: recursion! + return; + } + + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------- + + /* + * Continue a mutli game match? We have to either leave or continue + */ + else if (line.contains(pat[ConLeav])) { + actConti->setEnabled(true); + actLeave->setEnabled(true); + line.append("
  
" + i18n("(or use the corresponding menu " + "entry to leave or continue the match)")); + } + /* + * Beginning and end of user updates + */ + else if (line.contains(pat[WhoInfo])) { + rawline.replace(pat[WhoInfo], ""); + if (rawline.contains(QRegExp("^" + infoFIBS[FIBSUser] + " "))) { + int ready; + // Using latin1() is fine, since the string is coming from FIBS. + sscanf(rawline.latin1(), "%*s %*s %*s %i %*s %*s %*s %*s %*s %*s %*s %*s", &ready); + fibsOpt[OptReady]->setChecked(ready); + } + emit fibsWhoInfo(rawline); + return; + } + else if (line.contains(pat[WhoEnde])) { + emit fibsWhoEnd(); + return; + } + /* + * This message is ignored. The instruction is given elsewhere (and slightly + * delayed in the flow of time). + */ + if (line.contains(pat[TypJoin])) { + return; + } + /* + * Watching other players + */ + else if (line.contains(pat[BegWtch])) { + emit allowCommand(Load, true); + rawline.replace(pat[BegWtch], ""); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + load(); + } + else if (line.contains(pat[EndWtch])) { + emit gameOver(); + } + /* + * Blinding of players, the actual blind is handled by + * the player list + */ + else if (line.contains(pat[BegBlnd])) { + rawline.replace(pat[BegBlnd], ""); + rawline.truncate(rawline.length()-1); + emit changePlayerStatus(rawline, KFibsPlayerList::Blind, true); + line = "" + line + ""; + } + else if (line.contains(pat[EndBlnd])) { + rawline.replace(pat[EndBlnd], ""); + rawline.truncate(rawline.length()-1); + emit changePlayerStatus(rawline, KFibsPlayerList::Blind, false); + line = "" + line + ""; + } + /* + * Starting or reloading games or matches + */ + else if (line.contains(pat[BegGame])) { + rawline.replace(pat[BegGame], ""); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + } + else if (line.contains(pat[Reload1])) { + rawline.replace(pat[Reload1], ""); + rawline = rawline.left(rawline.find(' ')); + rawline.truncate(rawline.length()-1); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + load(); + } + else if (line.contains(pat[Reload2])) { + rawline.replace(pat[Reload2], ""); + emit fibsStartNewGame(rawline); + fibsOpt[OptDouble]->setChecked(true); + fibsOpt[OptGreedy]->setChecked(false); + actConti->setEnabled(false); + actLeave->setEnabled(false); + load(); + } + /* + * Opponent offered resignation or the cube. We have to accept + * or reject the offer. + */ + else if (line.contains(pat[RejAcpt])) { + actAccept->setEnabled(true); + actReject->setEnabled(true); + line += "
  
" + i18n("(or use the corresponding menu " + "entry to accept or reject the offer)"); + } + /* + * This is strange: FIBS seems to not send a newline at the + * end of this pattern. So we work around that. + */ + else if (line.contains(pat[YouAcpt])) { + actAccept->setEnabled(false); + actReject->setEnabled(false); + rawline.replace(pat[YouAcpt], ""); + line.truncate(line.length()-rawline.length()); + if (!rawline.stripWhiteSpace().isEmpty()) { + handleServerData(rawline); + } + } + /* + * Ending of games + */ + else if (line.contains(pat[EndLose])) { + if (playing) { + KNotifyClient::event("game over l", i18n("Sorry, you lost the game.")); + if (useAutoMsg[MsgLos] && !autoMsg[MsgLos].stripWhiteSpace().isEmpty()) + emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgLos]); + } + emit gameOver(); + } + else if (line.contains(pat[EndVict])) { + if (playing) { + KNotifyClient::event("game over w", i18n("Congratulations, you won the game!")); + if (useAutoMsg[MsgWin] && !autoMsg[MsgWin].stripWhiteSpace().isEmpty()) + emit serverString(QString("tell ") + pname[THEM] + " " + autoMsg[MsgWin]); + } + emit gameOver(); + } + else if (line.contains(pat[GameSav])) { + emit gameOver(); + } + /* + * User logs out. This has to be signalled to the player + * list. Get the true user names by working on the rawline. + */ + else if (line.contains(pat[UserLot])) { + rawline.replace(pat[UserLot], ""); + emit fibsLogout(rawline.left(rawline.find(' '))); + return; + } + /* + * Emit the name of the newly logged in user. + */ + else if (line.contains(pat[UserLin])) { + rawline.replace(pat[UserLin], ""); + emit fibsLogin(rawline.left(rawline.find(' '))); + return; + } + /* + * Special attention has to be paid to the proper setting of + * the 'boardstyle' variable, since we will not be able to display + * the board properly without it. + */ + else if (line.contains(pat[BoardSY])) { + // ignored + return; + } + else if (line.contains(pat[BoardSN])) { + emit serverString("set boardstyle 3"); + emit infoText(QString("
") + + i18n("You should never set the 'boardstyle' variable " + "by hand! It is vital for proper functioning of " + "this program that it remains set to 3. It has " + "been reset for you.") + + "
"); + return; + } + /* + * This is the final fall through: if the line started with ** and + * hasn't been processed, make it red, since it is a server resp. + * to something we just did. + */ + else if (line.contains(pat[TwoStar])) { + line = "" + line + ""; + } + + // -------------------------------------------------------------------------------- + + /* + * Print whatever part of the line made it here + */ + emit infoText(line); +} + +// EOF + + +// == constructor, destructor and setup ======================================== + +/* + * Constructor + */ +KBgEngineFIBS::KBgEngineFIBS(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + /* + * No connection, not playing, ready for login + */ + connection = new QSocket(parent, "fibs connection"); + playing = false; + login = true; + + connect(connection, SIGNAL(hostFound()), this, SLOT(hostFound())); + connect(connection, SIGNAL(connected()), this, SLOT(connected())); + connect(connection, SIGNAL(error(int)), this, SLOT(connError(int))); + connect(connection, SIGNAL(connectionClosed()), this, SLOT(connectionClosed())); + connect(connection, SIGNAL(delayedCloseFinished()), this, SLOT(connectionClosed())); + connect(connection, SIGNAL(readyRead()), this, SLOT(readData())); + + connect(this, SIGNAL(serverString(const QString &)), this, SLOT(sendData(const QString &))); + + /* + * No invitation dialog + */ + invitationDlg = 0; + + connect(this, SIGNAL(fibsWhoInfo(const QString &)), this, SLOT(changeJoin(const QString &))); + connect(this, SIGNAL(fibsLogout (const QString &)), this, SLOT(cancelJoin(const QString &))); + connect(this, SIGNAL(gameOver()), this, SLOT(endGame())); + + /* + * Creating, initializing and connecting the player list + */ + playerlist = new KFibsPlayerList(0, "fibs player list"); + + connect(this, SIGNAL(fibsWhoInfo(const QString &)), playerlist, SLOT(changePlayer(const QString &))); + connect(this, SIGNAL(fibsLogout (const QString &)), playerlist, SLOT(deletePlayer(const QString &))); + connect(this, SIGNAL(fibsWhoEnd()), playerlist, SLOT(stopUpdate())); + connect(this, SIGNAL(fibsConnectionClosed()), playerlist, SLOT(stopUpdate())); + connect(this, SIGNAL(changePlayerStatus(const QString &, int, bool)), + playerlist, SLOT(changePlayerStatus(const QString &, int, bool))); + connect(playerlist, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(playerlist, SIGNAL(fibsInvite(const QString &)), this, SLOT(fibsRequestInvitation(const QString &))); + + /* + * Create, initialize and connect the chat window + */ + chatWindow = new KBgChat(0, "chat window"); + + connect(this, SIGNAL(chatMessage(const QString &)), chatWindow, SLOT(handleData(const QString &))); + connect(this, SIGNAL(fibsStartNewGame(const QString &)), chatWindow, SLOT(startGame(const QString &))); + connect(this, SIGNAL(gameOver()), chatWindow, SLOT(endGame())); + connect(this, SIGNAL(fibsLogout (const QString &)), chatWindow, SLOT(deletePlayer(const QString &))); + connect(chatWindow, SIGNAL(fibsCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(chatWindow, SIGNAL(fibsRequestInvitation(const QString &)), this, SLOT(fibsRequestInvitation(const QString &))); + connect(chatWindow, SIGNAL(personalMessage(const QString &)), this, SLOT(personalMessage(const QString &))); + connect(playerlist, SIGNAL(fibsTalk(const QString &)), chatWindow, SLOT(fibsTalk(const QString &))); + + /* + * Creating, initializing and connecting the menu + * ---------------------------------------------- + */ + respMenu = new QPopupMenu(); + joinMenu = new QPopupMenu(); + cmdMenu = new QPopupMenu(); + optsMenu = new QPopupMenu(); + + /* + * Initialize the FIBS submenu - this is also put in the play menu + */ + conAction = new KAction(i18n("&Connect"), 0, this, SLOT( connectFIBS()), this); + newAction = new KAction(i18n("New Account"), 0, this, SLOT( newAccount()), this); + disAction = new KAction(i18n("&Disconnect"), 0, this, SLOT(disconnectFIBS()), this); + + conAction->setEnabled(true ); conAction->plug(menu); + disAction->setEnabled(false); disAction->plug(menu); + newAction->setEnabled(true ); newAction->plug(menu); + + menu->insertSeparator(); + + (invAction = new KAction(i18n("&Invite..."), 0, this, SLOT(inviteDialog()), this))->plug(menu); + + /* + * Create and fill the response menu. This is for all these: type this or + * that messages from FIBS. + */ + cmdMenuID = menu->insertItem(i18n("&Commands"), cmdMenu); { + + (actAway = new KAction(i18n("Away"), 0, this, SLOT(away()), this))->plug(cmdMenu); + (actBack = new KAction(i18n("Back"), 0, this, SLOT(back()), this))->plug(cmdMenu); + + actAway->setEnabled(true); + actBack->setEnabled(false); + } + + /* + * Create the server side options. This is preliminary and needs more work. + * The available options are skewed, since they refelect the needs of the + * author. Contact jens@hoefkens.com if your favorite option is missing. + */ + optsMenuID = menu->insertItem(i18n("&Options"), optsMenu); { + + for (int i = 0; i < NumFIBSOpt; i++) + fibsOpt[i] = 0; + + fibsOpt[OptReady] = new KToggleAction(i18n("Ready to Play"), + 0, this, SLOT(toggle_ready()), this); + fibsOpt[OptRatings] = new KToggleAction(i18n("Show Rating Computations"), + 0, this, SLOT(toggle_ratings()), this); + fibsOpt[OptRatings]->setCheckedState(i18n("Hide Rating Computations")); + fibsOpt[OptGreedy] = new KToggleAction(i18n("Greedy Bearoffs"), + 0, this, SLOT(toggle_greedy()), this); + fibsOpt[OptDouble] = new KToggleAction(i18n("Ask for Doubles"), + 0, this, SLOT(toggle_double()), this); + + for (int i = 0; i < NumFIBSOpt; i++) + if (fibsOpt[i]) + fibsOpt[i]->plug(optsMenu); + + } + + /* + * Create and fill the response menu. This is for all these: type this or + * that messages from FIBS. + */ + respMenuID = menu->insertItem(i18n("&Response"), respMenu); { + + (actAccept = new KAction(i18n("Accept"), 0, this, SLOT(accept()), this))->plug(respMenu); + (actReject = new KAction(i18n("Reject"), 0, this, SLOT(reject()), this))->plug(respMenu); + + actAccept->setEnabled(false); + actReject->setEnabled(false); + + respMenu->insertSeparator(); + + (actConti = new KAction(i18n("Join"), 0, this, SLOT(match_conti()), this))->plug(respMenu); + (actLeave = new KAction(i18n("Leave"), 0, this, SLOT(match_leave()), this))->plug(respMenu); + + actConti->setEnabled(false); + actLeave->setEnabled(false); + } + + /* + * Create the join menu and do not fill it (this happens at first + * action setup. + */ + joinMenuID = menu->insertItem(i18n("&Join"), joinMenu); { + numJoin = -1; + + actJoin[0] = new KAction("", 0, this, SLOT(join_0()), this); + actJoin[1] = new KAction("", 0, this, SLOT(join_1()), this); + actJoin[2] = new KAction("", 0, this, SLOT(join_2()), this); + actJoin[3] = new KAction("", 0, this, SLOT(join_3()), this); + actJoin[4] = new KAction("", 0, this, SLOT(join_4()), this); + actJoin[5] = new KAction("", 0, this, SLOT(join_5()), this); + actJoin[6] = new KAction("", 0, this, SLOT(join_6()), this); + actJoin[7] = new KAction("", 0, this, SLOT(join_7()), this); + } + + menu->setItemEnabled(joinMenuID, false); + menu->setItemEnabled( cmdMenuID, false); + menu->setItemEnabled(respMenuID, false); + menu->setItemEnabled(optsMenuID, false); + + /* + * Continue with the FIBS menu + */ + menu->insertSeparator(); + + (listAct = new KToggleAction(i18n("&Player List"), 0, this, SLOT(showList()), this))->plug(menu); + (chatAct = new KToggleAction(i18n("&Chat"), 0, this, SLOT(showChat()), this))->plug(menu); + + connect(playerlist, SIGNAL(windowVisible(bool)), listAct, SLOT(setChecked(bool))); + connect(chatWindow, SIGNAL(windowVisible(bool)), chatAct, SLOT(setChecked(bool))); + + /* + * Create message IDs. This sets up a lot of regular expressions. + */ + initPattern(); + + /* + * Restore old settings + */ + readConfig(); + + // FIXME: open the child windows in start() and not here + + /* + * Update the menu actions + */ + listAct->setChecked(playerlist->isVisible()); + chatAct->setChecked(chatWindow->isVisible()); + + /* + * Initialize the keepalive timer FIXME: make this a setting + */ + keepalive = true; + + // FIXME: move the start to connect... + + keepaliveTimer = new QTimer(this); + connect(keepaliveTimer, SIGNAL(timeout()), this, SLOT(keepAlive())); + keepaliveTimer->start(1200000); +} + +/* + * Destructor deletes child objects if necessary + */ +KBgEngineFIBS::~KBgEngineFIBS() +{ + delete joinMenu; + delete respMenu; + delete cmdMenu; + delete optsMenu; + + delete connection; + delete invitationDlg; + + delete playerlist; + delete chatWindow; +} + + diff --git a/kbackgammon/engines/fibs/kbgfibs.h b/kbackgammon/engines/fibs/kbgfibs.h new file mode 100644 index 00000000..1c14e0f3 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibs.h @@ -0,0 +1,479 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + + +#ifndef __KBGFIBS_H +#define __KBGFIBS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "kplayerlist.h" +#include "kbgfibschat.h" +#include "kbginvite.h" // TODO + +#include +#include +#include + +#include + +class QTimer; +class QSocket; +class QPopupMenu; +class QCheckBox; + +class KAction; +class KToggleAction; + +/** + * + * Special backgammon engine for games on the First Internet Backgammon Server + * + * @short The FIBS backgammon engine + * @author Jens Hoefkens + * + */ +class KBgEngineFIBS : public KBgEngine +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgEngineFIBS(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngineFIBS(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + virtual void start(); + + +public slots: + + /** + * Read and save user settings to the config file + */ + virtual void readConfig(); + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Commit a move + */ + virtual void done(); + + + // ########################################################################### + // + // + // + // TODO TODO TODO TODO TODO TODO TODO + // + // + // + // ########################################################################### + + /* + * Process the string cmd + */ + void handleCommand(const QString &cmd); + + void fibsRequestInvitation(const QString &player); + + void personalMessage(const QString &msg); + + + + /* + * Local configuration handling + */ + + void keepAlive(); + +signals: + + void serverString(const QString &s); + + void fibsWhoInfo(const QString &line); + void fibsWhoEnd(); + void fibsLogout(const QString &p); + void fibsLogin(const QString &p); + + void fibsConnectionClosed(); + + void changePlayerStatus(const QString &, int, bool); + + void chatMessage(const QString &msg); + + void fibsStartNewGame(const QString &msg); + void gameOver(); + +protected slots: + + void invitationDone(); + void inviteDialog(); + void showList(); + void showChat(); + + void endGame(); + +private: + + QTimer *keepaliveTimer; + + QString pname[2]; + + QString currBoard, caption; + + //KBgStatus *currBoard + //KBgFIBSBoard *boardHandler; + + QStringList invitations; + + /* + * special menu entries + */ + int respMenuID, cmdMenuID, joinMenuID, optsMenuID; + QPopupMenu *respMenu, *cmdMenu, *joinMenu, *optsMenu; + + /* + * child windows + */ + KFibsPlayerList *playerlist; + KBgChat *chatWindow; + KBgInvite *invitationDlg; + + /* + * Other stuff + */ + QString lastMove; + int toMove; + + QString lastAway; + bool playing; + bool redoPossible; + int undoCounter; + + KAction *conAction, *disAction, *newAction, *invAction; + + KAction *actAccept, *actReject, *actConti, *actLeave, *actAway, *actBack; + + KToggleAction *chatAct, *listAct; + + + // ########################################################################### + // + // + // + // DONE DONE DONE DONE DONE DONE DONE + // + // + // + // ########################################################################### + +private: + + /** + * Actions for responding to invitations. numJoin is he current + * number of active actions. The max. number of pending invitations + * is eight and this is hardcoded in a lot of places (not the least + * of which are the slots join_N(). + */ + KAction *actJoin[8]; + int numJoin; + +protected slots: + + /** + * Handle rawwho information for the purposes of the invitation + * submenu and the join entries + */ + void changeJoin(const QString &info); + + /** + * A player will be removed from the menu of pending invitations + * if necessary. + */ + void cancelJoin(const QString &info); + + /** + * We have up to 8 names in the join menu. They are the + * players that invited us to play games. Each action + * has its own slot and all slots call the common backend + * join(). + */ + void join(const QString &msg); + + void join_0(); + void join_1(); + void join_2(); + void join_3(); + void join_4(); + void join_5(); + void join_6(); + void join_7(); + + /** + * Simple slots that toggle FIBS server-side settings. The + * names of the functions reflect the name of the toggle on + * FIBS. + */ + void toggle_greedy(); + void toggle_ready(); + void toggle_double(); + void toggle_ratings(); + +private: + + /** + * Toggle actions for the FIBS server-side settings + */ + enum FIBSOpt {OptReady, OptGreedy, OptDouble, + OptAllowPip, OptAutoMove, OptCrawford, OptSilent, OptRatings, OptMoves, NumFIBSOpt}; + KToggleAction *fibsOpt[NumFIBSOpt]; + +public slots: + + /* + * Connection handling + * ------------------- + */ + + // initiate asynchronous connection establishment + void connectFIBS(); + + // take the connection down + void disconnectFIBS(); + + // create a new account and connect + void newAccount(); + + // called when the connection is down + void connectionClosed(); + + // the hostname has been resolved + void hostFound(); + + // a connection error occurred + void connError(int f); + + // connection has been established + void connected(); + + // data can be read from the socket + void readData(); + + // send the string s to the server + void sendData(const QString &s); + +protected: + + // get the connection parameters + bool queryConnection(const bool newlogin); + +private: + + // actual connection object + QSocket *connection; + + // flag if we have login information or new account + bool login; + +protected slots: + + /* + * FIBS command slots + * ------------------ + */ + + // go away and leave a message + void away(); + + // come back after being away + void back(); + + // roll dice + virtual void roll(); + + // double the cube + virtual void cube(); + + // reload the board to the last known sane state + virtual void load(); + + // accept an offer + void accept(); + + // reject an offer + void reject(); + + // continue a multi game match + void match_conti(); + + // leave a multi game match + void match_leave(); + +protected slots: + + /* + * All strings received from the server are given to handleServerData() for + * identification and processing. It implements a limited state machine to + * handle the incoming data correctly. The whole function could probably be + * made more efficient, but it is not time critical (and it appears to be + * easier to understand this way). + */ + void handleServerData(QString &line); + +protected: + + enum RxStatus {RxIgnore, RxConnect, RxWhois, RxMotd, RxRating, + RxNewLogin, RxGoodbye, RxNormal}; + + int rxStatus, rxCount; + + QString rxCollect; + + /* + * The following functions handle the individual states + * of the handleServerData() state machine, + */ + void handleMessageWhois(const QString &line); + void handleMessageRating(const QString &line); + void handleMessageMotd(const QString &line); + void handleMessageNewLogin(const QString &line); + void handleMessageConnect(const QString &line, const QString &rawline); + void handleMessageNormal(QString &line, QString &rawline); + + /* + * The next enumeration and the array of regular expressions is needed for the + * message identification in handleServerData(). + */ + enum Pattern {Welcome, OwnInfo, NoLogin, BegRate, EndRate, HTML_lt, HTML_gt, + BoardSY, BoardSN, WhoisBG, WhoisE1, WhoisE2, WhoEnde, WhoInfo, + MotdBeg, MotdEnd, MsgPers, MsgDeli, MsgSave, ChatSay, ChatSht, + ChatWis, ChatKib, SelfSay, SelfSlf, SelfSht, SelfWis, SelfKib, + UserLin, UserLot, Goodbye, GameSav, RawBord, YouTurn, PlsMove, + BegWtch, EndWtch, BegBlnd, EndBlnd, BegGame, OneWave, TwoWave, + YouWave, Reload1, Reload2, GameBG1, GameBG2, GameRE1, GameRE2, + GameEnd, EndLose, EndVict, MatchB1, MatchB2, MatchB3, MatchB4, + RejAcpt, YouAway, YouAcpt, HelpTxt, Invite0, Invite1, Invite2, + Invite3, ConLeav, TabChar, PlsChar, OneName, TypJoin, YouBack, + YouMove, YouRoll, TwoStar, BoxHori, BoxVer1, BoxVer2, OthrNam, + YourNam, GivePwd, RetypeP, GreedyY, GreedyN, RejCont, AcptWin, + YouGive, DoubleY, DoubleN, KeepAlv, RatingY, RatingN, + NumPattern}; + + QRegExp pat[NumPattern]; + + /* + * This function is simply filling the pat[] array with the proper values. + */ + void initPattern(); + +private: + + /* + * Local setup and config variables + * ================================ + */ + + /* + * Various options + */ + bool showMsg, whoisInvite; + QCheckBox *cbp, *cbi; + + QCheckBox *cbk; + bool keepalive; + + /* + * Connection setup + */ + enum FIBSInfo {FIBSHost, FIBSPort, FIBSUser, FIBSPswd, NumFIBS}; + QString infoFIBS[NumFIBS]; + QLineEdit *lec[NumFIBS]; + + /* + * Auto messages + */ + enum AutoMessages {MsgBeg, MsgLos, MsgWin, NumMsg}; + QLineEdit *lem[NumMsg]; + QCheckBox *cbm[NumMsg]; + bool useAutoMsg[NumMsg]; + QString autoMsg[NumMsg]; +}; + +#endif // __KBGFIBS_H diff --git a/kbackgammon/engines/fibs/kbgfibschat.cpp b/kbackgammon/engines/fibs/kbgfibschat.cpp new file mode 100644 index 00000000..45ba2bb7 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibschat.cpp @@ -0,0 +1,828 @@ + +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + + +#include "kbgfibschat.h" +#include "kbgfibschat.moc" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clip.h" +#include "version.h" + + +/* + * Private utility class that might become more generally useful in + * the future. Basically, it implements rich text QListBox items. + */ +class KLBT : public QListBoxText +{ + +public: + + /* + * Constructor + */ + KLBT(QWidget *parent, const QString &text = QString::null, const QString &player = QString::null) + : QListBoxText(text) + { + w = parent; + n = new QString(player); + t = new QSimpleRichText(text, w->font()); + + // FIXME: this is not yet perfect + t->setWidth(w->width()-20); + } + + /* + * Destructor + */ + virtual ~KLBT() + { + delete t; + delete n; + } + + /* + * Overloaded required members returning height + */ + virtual int height(const QListBox *) const + { + return (1+t->height()); + } + + /* + * Overloaded required members returning width + */ + virtual int width(const QListBox *) const + { + return t->width(); + } + + /* + * The context menu needs the name of the player. It's easier + * than extracting it from the text. + */ + QString player() const + { + return *n; + } + +protected: + + /* + * Required overloaded member to paint the text on the painter p. + */ + virtual void paint(QPainter *p) + { + t->draw(p, 1, 1, QRegion(p->viewport()), w->colorGroup()); + } + +private: + + QSimpleRichText *t; + QWidget *w; + QString *n; + +}; + + +class KBgChatPrivate +{ +public: + + /* + * Name of the users + */ + QString mName[2]; + + /* + * Hold and assemble info text + */ + QString mText; + + /* + * Numbers of the private action list. + */ + enum Privact {Inquire, InviteD, Invite1, Invite2, Invite3, Invite4, + Invite5, Invite6, Invite7, InviteR, InviteU, Silent, + Talk, Gag, Ungag, Cleargag, Copy, Clear, Close, MaxAction}; + + /* + * Available actions + */ + KAction *mAct[MaxAction]; + + /* + * Context menu and invitation menu + */ + QPopupMenu *mChat, *mInvt; + + /* + * list of users we do not want to hear shouting + */ + QStringList mGag; + + /* + * Listbox needed by the setup dialog + */ + QListBox *mLb; + + /* + * Internal ID to name mapping + */ + QDict *mName2ID; + +}; + + +// == constructor, destructor ================================================== + +/* + * Constructor of the chat window. + */ +KBgChat::KBgChat(QWidget *parent, const char *name) + : KChat(parent, false) +{ + d = new KBgChatPrivate(); + KActionCollection* actions = new KActionCollection(this); + + d->mName[0] = QString::null; + d->mChat = 0; + d->mInvt = new QPopupMenu(); + + setAutoAddMessages(false); // we get an echo from FIBS + setFromNickname(i18n("%1 user").arg(PROG_NAME)); + + if (!addSendingEntry(i18n("Kibitz to watchers and players"), CLIP_YOU_KIBITZ)) + kdDebug(10500) << "adding kibitz" << endl; + if (!addSendingEntry(i18n("Whisper to watchers only"), CLIP_YOU_WHISPER)) + kdDebug(10500) << "adding whisper" << endl; + + connect(this, SIGNAL(rightButtonClicked(QListBoxItem *, const QPoint &)), + this, SLOT(contextMenu(QListBoxItem *, const QPoint &))); + connect(this, SIGNAL(signalSendMessage(int, const QString &)), + this, SLOT(handleCommand(int, const QString &))); + + d->mName2ID = new QDict(17, true); + d->mName2ID->setAutoDelete(true); + + /* + * some eye candy :) + */ + setIcon(kapp->miniIcon()); + setCaption(i18n("Chat Window")); + + QWhatsThis::add(this, i18n("This is the chat window.\n\n" + "The text in this window is colored depending on whether " + "it is directed at you personally, shouted to the general " + "FIBS population, has been said by you, or is of general " + "interest. If you select the name of a player, the context " + "contains entries specifically geared towards that player.")); + /* + * Define set of available actions + */ + d->mAct[KBgChatPrivate::Inquire] = new KAction(i18n("Info On"), + QIconSet(kapp->iconLoader()->loadIcon( + "help.xpm", KIcon::Small)), + 0, this, SLOT(slotInquire()), actions); + d->mAct[KBgChatPrivate::Talk] = new KAction(i18n("Talk To"), + QIconSet(kapp->iconLoader()->loadIcon( + PROG_NAME "-chat.png", KIcon::Small)), + 0, this, SLOT(slotTalk()), actions); + + d->mAct[KBgChatPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this, + SLOT(slotInviteD()), actions); + d->mAct[KBgChatPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this, + SLOT(slotInvite1()), actions); + d->mAct[KBgChatPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this, + SLOT(slotInvite2()), actions); + d->mAct[KBgChatPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this, + SLOT(slotInvite3()), actions); + d->mAct[KBgChatPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this, + SLOT(slotInvite4()), actions); + d->mAct[KBgChatPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this, + SLOT(slotInvite5()), actions); + d->mAct[KBgChatPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this, + SLOT(slotInvite6()), actions); + d->mAct[KBgChatPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this, + SLOT(slotInvite7()), actions); + d->mAct[KBgChatPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this, + SLOT(slotInviteU()), actions); + d->mAct[KBgChatPrivate::InviteR] = new KAction(i18n("Resume"), 0, this, + SLOT(slotInviteR()), actions); + + d->mAct[KBgChatPrivate::InviteD]->plug(d->mInvt); + + d->mInvt->insertSeparator(); + + d->mAct[KBgChatPrivate::Invite1]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite2]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite3]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite4]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite5]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite6]->plug(d->mInvt); + d->mAct[KBgChatPrivate::Invite7]->plug(d->mInvt); + + d->mInvt->insertSeparator(); + + d->mAct[KBgChatPrivate::InviteU]->plug(d->mInvt); + d->mAct[KBgChatPrivate::InviteR]->plug(d->mInvt); + + d->mAct[KBgChatPrivate::Gag] = new KAction(i18n("Gag"), 0, this, SLOT(slotGag()), actions); + d->mAct[KBgChatPrivate::Ungag] = new KAction(i18n("Ungag"), 0, this, SLOT(slotUngag()), actions); + d->mAct[KBgChatPrivate::Cleargag] = new KAction(i18n("Clear Gag List"), 0, this, SLOT(slotCleargag()), actions); + d->mAct[KBgChatPrivate::Copy] = KStdAction::copy(this, SLOT(slotCopy()), actions); + d->mAct[KBgChatPrivate::Clear] = new KAction(i18n("Clear"), 0, this, SLOT(slotClear()), actions); + d->mAct[KBgChatPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions); + d->mAct[KBgChatPrivate::Silent] = new KToggleAction(i18n("Silent"), 0, this, SLOT(slotSilent()), actions); +} + + +/* + * Destructor + */ +KBgChat::~KBgChat() +{ + delete d->mName2ID; + delete d->mChat; // save to delete NULL pointers + delete d->mInvt; + delete d; +} + + +// == configuration handling =================================================== + +/* + * Restore the previously stored settings + */ +void KBgChat::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("chat window"); + + QPoint pos(10, 10); + + pos = config->readPointEntry("ori", &pos); + setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460), config->readNumEntry("hgt",200)); + + config->readBoolEntry("vis", false) ? show() : hide(); + + ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->setChecked(config->readBoolEntry("sil", false)); + + d->mGag = config->readListEntry("gag"); +} + +/* + * Save the current settings to disk + */ +void KBgChat::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("chat window"); + + config->writeEntry("ori", pos()); + config->writeEntry("hgt", height()); + config->writeEntry("wdt", width()); + config->writeEntry("vis", isVisible()); + + config->writeEntry("sil", ((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()); + + config->writeEntry("gag", d->mGag); +} + + +/* + * Setup dialog page of the player list - allow the user to select the + * columns to show + * + * FIXME: need to be able to set font here KChatBase::setBothFont(const QFont& font) + */ +void KBgChat::getSetupPages(KTabCtl *nb, int space) +{ + /* + * Main Widget + * =========== + */ + QWidget *w = new QWidget(nb); + QGridLayout *gl = new QGridLayout(w, 2, 1, space); + + d->mLb = new QListBox(w); + d->mLb->setMultiSelection(true); + + d->mLb->insertStringList(d->mGag); + + QLabel *info = new QLabel(w); + info->setText(i18n("Select users to be removed from the gag list.")); + + QWhatsThis::add(w, i18n("Select all the users you want " + "to remove from the gag list " + "and then click OK. Afterwards " + "you will again hear what they shout.")); + + gl->addWidget(d->mLb, 0, 0); + gl->addWidget(info, 1, 0); + + /* + * put in the page + * =============== + */ + gl->activate(); + w->adjustSize(); + w->setMinimumSize(w->size()); + nb->addTab(w, i18n("&Gag List")); +} + +/* + * Remove all the selected entries from the gag list + */ +void KBgChat::setupOk() +{ + for (uint i = 0; i < d->mLb->count(); ++i) { + if (d->mLb->isSelected(i)) + d->mGag.remove(d->mLb->text(i)); + } + d->mLb->clear(); + d->mLb->insertStringList(d->mGag); +} + +/* + * Don't do anything + */ +void KBgChat::setupCancel() +{ + // empty +} + +/* + * By default, all players stay in the gag list + */ +void KBgChat::setupDefault() +{ + d->mLb->clearSelection(); +} + + +// == various slots and functions ============================================== + +/* + * Overloaded member to create a QListBoxItem for the chat window. + */ +QListBoxItem* KBgChat::layoutMessage(const QString& fromName, const QString& text) +{ + QListBoxText* message = new KLBT(this, text, fromName); + return message; +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KBgChat::showEvent(QShowEvent *e) +{ + QFrame::showEvent(e); + emit windowVisible(true); +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KBgChat::hideEvent(QHideEvent *e) +{ + emit windowVisible(false); + QFrame::hideEvent(e); +} + +/* + * At the beginning of a game, add the name to the list and switch to + * kibitz mode. + */ +void KBgChat::startGame(const QString &name) +{ + int *id = d->mName2ID->find(d->mName[1] = name); + if (!id) { + id = new int(nextId()); + d->mName2ID->insert(name, id); + addSendingEntry(i18n("Talk to %1").arg(name), *id); + } + setSendingEntry(CLIP_YOU_KIBITZ); +} + +/* + * At the end of a game, we switch to talk mode. + */ +void KBgChat::endGame() +{ + int *id = d->mName2ID->find(d->mName[1]); + if (id) + setSendingEntry(*id); + else + setSendingEntry(SendToAll); +} + +/* + * Set the chat window ready to talk to name + */ +void KBgChat::fibsTalk(const QString &name) +{ + int *id = d->mName2ID->find(name); + if (!id) { + id = new int(nextId()); + d->mName2ID->insert(name, id); + addSendingEntry(i18n("Talk to %1").arg(name), *id); + } + setSendingEntry(*id); +} + +/* + * Remove the player from the combo box when he/she logs out. + */ +void KBgChat::deletePlayer(const QString &name) +{ + int *id = d->mName2ID->find(name); + if (id) { + removeSendingEntry(*id); + d->mName2ID->remove(name); + } +} + +/* + * Take action when the user presses return in the line edit control. + */ +void KBgChat::handleCommand(int id, const QString& msg) +{ + int realID = sendingEntry(); + + switch (realID) { + case SendToAll: + emit fibsCommand("shout " + msg); + break; + case CLIP_YOU_KIBITZ: + emit fibsCommand("kibitz " + msg); + break; + case CLIP_YOU_WHISPER: + emit fibsCommand("whisper " + msg); + break; + default: + QDictIterator it(*d->mName2ID); + while (it.current()) { + if (*it.current() == realID) { + emit fibsCommand("tell " + it.currentKey() + " " + msg); + return; + } + ++it; + } + kdDebug(10500) << "unrecognized ID in KBgChat::handleCommand" << endl; + } +} + + +// == handle strings from the server =========================================== + +/* + * A message from the server that should be handled by us. If necessary, + * we replace the CLIP number by a string and put the line into the window. + * + * This function emits the string in rich text format with the signal + * personalMessage - again: the string contains rich text! + */ +void KBgChat::handleData(const QString &msg) +{ + QString clip = msg.left(msg.find(' ')), user, cMsg = msg; + QDateTime date; + + bool flag = false; + int cmd = clip.toInt(&flag); + + if (flag) { + cMsg.replace(0, cMsg.find(' ')+1, ""); + + user = cMsg.left(cMsg.find(' ')); + + switch (cmd) { + case CLIP_SAYS: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 tells you: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_SHOUTS: + if ((!((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()) && (!d->mGag.contains(user))) { + cMsg = i18n("%1 shouts: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + } else + cMsg = ""; + break; + + case CLIP_WHISPERS: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 whispers: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_KIBITZES: + if (!d->mGag.contains(user)) { + cMsg = i18n("%1 kibitzes: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + } else + cMsg = ""; + break; + + case CLIP_YOU_SAY: + cMsg = i18n("You tell %1: %2").arg(user).arg(cMsg.replace(QRegExp("^" + user), "")); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_SHOUT: + cMsg = i18n("You shout: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_WHISPER: + cMsg = i18n("You whisper: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_YOU_KIBITZ: + cMsg = i18n("You kibitz: %1").arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE: + user = cMsg.left(cMsg.find(' ')+1); + cMsg.remove(0, cMsg.find(' ')+1); + date.setTime_t(cMsg.left(cMsg.find(' ')+1).toUInt()); + cMsg.remove(0, cMsg.find(' ')); + cMsg = i18n("User %1 left a message at %2: %3").arg(user).arg(date.toString()).arg(cMsg); + cMsg = "" + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE_DELIVERED: + cMsg = i18n("Your message for %1 has been delivered.").arg(user); + cMsg = QString("") + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + case CLIP_MESSAGE_SAVED: + cMsg = i18n("Your message for %1 has been saved.").arg(user); + cMsg = QString("") + cMsg + ""; + emit personalMessage(cMsg); + user = QString::null; + break; + + default: // ignore the message + return; + } + + } else { + + /* + * Special treatment for non-CLIP messages + */ + if (cMsg.contains(QRegExp("^You say to yourself: "))) { + cMsg.replace(QRegExp("^You say to yourself: "), + i18n("You say to yourself: ")); + } else { + kdDebug(user.isNull(), 10500) << "KBgChat::handleData unhandled message: " + << cMsg.latin1() << endl; + return; + } + } + + if (!cMsg.isEmpty()) + addMessage(user, cMsg); +} + + +// == context menu and related slots =========================================== + +/* + * RMB opens a context menu. + */ +void KBgChat::contextMenu(QListBoxItem *i, const QPoint &p) +{ + /* + * Even if i is non-null, user might still be QString::null + */ + d->mName[0] = (i == 0) ? QString::null : ((KLBT *)i)->player(); + d->mText = (i == 0) ? QString::null : ((KLBT *)i)->text(); + + /* + * Get a new context menu every time. Safe to delete the 0 + * pointer. + */ + delete d->mChat; d->mChat = new QPopupMenu(); + + /* + * Fill the context menu with actions + */ + if (!d->mName[0].isNull()) { + + d->mAct[KBgChatPrivate::Talk]->setText(i18n("Talk to %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Talk]->plug(d->mChat); + + d->mAct[KBgChatPrivate::Inquire]->setText(i18n("Info on %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Inquire]->plug(d->mChat); + + // invite menu is always the same + d->mChat->insertItem(i18n("Invite %1").arg(d->mName[0]), d->mInvt); + + d->mChat->insertSeparator(); + + if (d->mGag.contains(d->mName[0]) <= 0) { + d->mAct[KBgChatPrivate::Gag]->setText(i18n("Gag %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Gag]->plug(d->mChat); + } else { + d->mAct[KBgChatPrivate::Ungag]->setText(i18n("Ungag %1").arg(d->mName[0])); + d->mAct[KBgChatPrivate::Ungag]->plug(d->mChat); + } + } + if (d->mGag.count() > 0) + d->mAct[KBgChatPrivate::Cleargag]->plug(d->mChat); + + if ((d->mGag.count() > 0) || (!d->mName[0].isNull())) + d->mChat->insertSeparator(); + + d->mAct[KBgChatPrivate::Silent]->plug(d->mChat); + + d->mChat->insertSeparator(); + + d->mAct[KBgChatPrivate::Copy ]->plug(d->mChat); + d->mAct[KBgChatPrivate::Clear]->plug(d->mChat); + d->mAct[KBgChatPrivate::Close]->plug(d->mChat); + + d->mChat->popup(p); +} + +/* + * Clear the gag list + */ +void KBgChat::slotCleargag() +{ + d->mGag.clear(); + + QString msg(""); + msg += i18n("The gag list is now empty."); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Gag the selected user + */ +void KBgChat::slotGag() +{ + d->mGag.append(d->mName[0]); + + QString msg(""); + msg += i18n("You won't hear what %1 says and shouts.").arg(d->mName[0]); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Simple interface to the actual talk slot + */ +void KBgChat::slotTalk() +{ + fibsTalk(d->mName[0]); +} + +/* + * Remove selected user from gag list + */ +void KBgChat::slotUngag() +{ + d->mGag.remove(d->mName[0]); + + QString msg(""); + msg += i18n("You will again hear what %1 says and shouts.").arg(d->mName[0]); + msg += ""; + + addMessage(QString::null, msg); +} + +/* + * Get information on selected user + */ +void KBgChat::slotInquire() +{ + kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInquire: user == null" << endl; + emit fibsCommand("whois " + d->mName[0]); +} + +/* + * Block all shouts from the chat window + */ +void KBgChat::slotSilent() +{ + QString msg; + if (((KToggleAction *)d->mAct[KBgChatPrivate::Silent])->isChecked()) + msg = "" + i18n("You will not hear what people shout.") + ""; + else + msg = "" + i18n("You will hear what people shout.") + ""; + addMessage(QString::null, msg); +} + +/* + * Copy the selected line to the clipboard. Strip the additional HTML + * from the text before copying. + */ +void KBgChat::slotCopy() +{ + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp(""), ""); + d->mText.replace(QRegExp("^.*\">"), ""); + + kapp->clipboard()->setText(d->mText); +} + +/* + * Invite the selected player. + */ +void KBgChat::slotInviteD() +{ + kdDebug(d->mName[0].isNull(), 10500) << "KBgChat::slotInvite: user == null" << endl; + emit fibsRequestInvitation(d->mName[0]); +} +void KBgChat::slotInvite1() { emit fibsCommand("invite " + d->mName[0] + " 1"); } +void KBgChat::slotInvite2() { emit fibsCommand("invite " + d->mName[0] + " 2"); } +void KBgChat::slotInvite3() { emit fibsCommand("invite " + d->mName[0] + " 3"); } +void KBgChat::slotInvite4() { emit fibsCommand("invite " + d->mName[0] + " 4"); } +void KBgChat::slotInvite5() { emit fibsCommand("invite " + d->mName[0] + " 5"); } +void KBgChat::slotInvite6() { emit fibsCommand("invite " + d->mName[0] + " 6"); } +void KBgChat::slotInvite7() { emit fibsCommand("invite " + d->mName[0] + " 7"); } + +void KBgChat::slotInviteU() { emit fibsCommand("invite " + d->mName[0] + " unlimited"); } +void KBgChat::slotInviteR() { emit fibsCommand("invite " + d->mName[0]); } + + +// EOF diff --git a/kbackgammon/engines/fibs/kbgfibschat.h b/kbackgammon/engines/fibs/kbgfibschat.h new file mode 100644 index 00000000..c3a1d670 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibschat.h @@ -0,0 +1,273 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGCHAT_H +#define __KBGCHAT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QString; +class QPoint; +class QListBox; +class QListBoxItem; +class QPopupMenu; + +class KTabCtl; +class KAction; + +class KBgChatPrivate; + +/** + * Class of the FIBS Chat Windows + * + * This class inherits from KChat and represents a widget for a chat + * window. It has rich text entries and supports a powerful context + * menu. + * + * @short An extension of the KGame chat window for the FIBS engine + * @author Jens Hoefkens + * + */ +class KBgChat : public KChat +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgChat(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgChat(); + +public slots: + + /** + * Catch the RMB signal to display a context menu at p. The + * menu shows entries specific to the selected item i. + */ + void contextMenu(QListBoxItem *i, const QPoint &p); + + /** + * Add chat window specific pages to the setup dialog + */ + void getSetupPages(KTabCtl *nb, int space); + + /** + * Save and apply the changes made in the setup dialog + */ + void setupOk(); + + /** + * Do not save any of the changes made in the setup dialog + */ + void setupCancel(); + + /** + * Load default values from the setup dialog + */ + void setupDefault(); + + /** + * Player name has logges out. Remove name from the chat + * window combo box if necessary. + */ + void deletePlayer(const QString &name); + + /** + * Process and append msg to the text. + */ + void handleData(const QString &msg); + + /** + * Restore previously saved setting or provides defaults + */ + void readConfig(); + + /** + * Save current settings + */ + void saveConfig(); + + /** + * Set the opponents name and select whisper + */ + void startGame(const QString &name); + + /** + * Game is over. We won (or not) and have been playing (or not) + */ + void endGame(); + + /** + * Start talking to name + */ + void fibsTalk(const QString &name); + +signals: + + /** + * Emits a string that can be sent to the server + */ + void fibsCommand(const QString &cmd); + + /** + * Request an invitation of player + */ + void fibsRequestInvitation(const QString &player); + + /** + * Text of a personal message + */ + void personalMessage(const QString &msg); + + /** + * Dialog is visible or not + */ + void windowVisible(bool v); + +protected: + + /** + * Catch show events, so the engine's menu can be updated. + */ + virtual void showEvent(QShowEvent *e); + + /** + * Catch hide events, so the engine's menu can be updated. + */ + virtual void hideEvent(QHideEvent *e); + + /** + * Create a custom ListBoxItem that contains a formated string + * for the chat window. + */ + virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text); + +protected slots: + + /** + * Invite the selected player using the dialog + */ + void slotInviteD(); + + /** + * Invite the selected player to resume a match + */ + void slotInviteR(); + + /** + * Invite the selected player to an unlimited match + */ + void slotInviteU(); + + /** + * Invite the selected player to a 1 point match + */ + void slotInvite1(); + + /** + * Invite the selected player to a 2 point match + */ + void slotInvite2(); + + /** + * Invite the selected player to a 3 point match + */ + void slotInvite3(); + + /** + * Invite the selected player to a 4 point match + */ + void slotInvite4(); + + /** + * Invite the selected player to a 5 point match + */ + void slotInvite5(); + + /** + * Invite the selected player to a 6 point match + */ + void slotInvite6(); + + /** + * Invite the selected player to a 7 point match + */ + void slotInvite7(); + + /** + * Request information on the selected player + */ + void slotInquire(); + + /** + * Copy the selected line to the clipboard + */ + void slotCopy(); + + /** + * Talk to the selected player + */ + void slotTalk(); + + /** + * Add the selected player to the gag list + */ + void slotGag(); + + /** + * Remove the selected player from the gag list + */ + void slotUngag(); + + /** + * Clear the gag list + */ + void slotCleargag(); + + /** + * Toggle everybody silent + */ + void slotSilent(); + + /** + * Slot for return pressed. Time to send the text to FIBS. + */ + void handleCommand(int id, const QString& msg); + +private: + + KBgChatPrivate *d; + +}; + +#endif // __KBGCHAT_H diff --git a/kbackgammon/engines/fibs/kbginvite.cpp b/kbackgammon/engines/fibs/kbginvite.cpp new file mode 100644 index 00000000..cb455f0a --- /dev/null +++ b/kbackgammon/engines/fibs/kbginvite.cpp @@ -0,0 +1,185 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbginvite.h" +#include "kbginvite.moc" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +class KBgInvitePrivate { + +public: + + KLineEdit *mLe; + QSpinBox *mSb; + QPushButton *mInvite, *mResume, *mUnlimited, *mCancel, *mClose; + +}; + +/* + * Constructor is quite simple - most positioning is left to + * the toolkit. + */ +KBgInvite::KBgInvite(const char *name) + : KDialog(0, name, false) +{ + setCaption(i18n("Invite Players")); + + d = new KBgInvitePrivate(); + + QLabel *info = new QLabel(this); + + d->mLe = new KLineEdit(this, "invitation dialog"); + d->mSb = new QSpinBox(1, 999, 1, this, "spin box"); + + d->mInvite = new QPushButton(i18n("&Invite"), this); + d->mResume = new QPushButton(i18n("&Resume"), this); + d->mUnlimited = new QPushButton(i18n("&Unlimited"), this); + + d->mClose = new KPushButton(KStdGuiItem::close(), this); + d->mCancel = new KPushButton(KStdGuiItem::clear(), this); + + info->setText(i18n("Type the name of the player you want to invite in the first entry\n" + "field and select the desired match length in the spin box.")); + + QFrame *hLine = new QFrame(this); + hLine->setFrameStyle(QFrame::Sunken|QFrame::HLine); + + /* + * Set up layouts + */ + QBoxLayout *vbox = new QVBoxLayout(this); + + QBoxLayout *hbox_1 = new QHBoxLayout(vbox); + QBoxLayout *hbox_2 = new QHBoxLayout(vbox); + QBoxLayout *hbox_3 = new QHBoxLayout(vbox); + QBoxLayout *hbox_4 = new QHBoxLayout(vbox); + QBoxLayout *hbox_5 = new QHBoxLayout(vbox); + + hbox_1->addWidget(info); + + hbox_2->addWidget(d->mLe); + hbox_2->addWidget(d->mSb); + + hbox_3->addWidget(hLine); + + hbox_4->addWidget(d->mInvite); + hbox_4->addWidget(d->mResume); + hbox_4->addWidget(d->mUnlimited); + + hbox_5->addWidget(d->mClose); + hbox_5->addWidget(d->mCancel); + + /* + * Adjust widget sizes and resize the dialog + */ + KDialog::resizeLayout(this, marginHint(), spacingHint()); + setMinimumSize(childrenRect().size()); + vbox->activate(); + resize(minimumSize()); + + /* + * Set focus and default buttons + */ + d->mInvite->setDefault(true); + d->mInvite->setAutoDefault(true); + d->mLe->setFocus(); + + /* + * Connect the buttons + */ + connect(d->mUnlimited, SIGNAL(clicked()), SLOT(unlimitedClicked())); + connect(d->mResume, SIGNAL(clicked()), SLOT(resumeClicked())); + connect(d->mInvite, SIGNAL(clicked()), SLOT(inviteClicked())); + connect(d->mClose, SIGNAL(clicked()), SLOT(hide())); + connect(d->mCancel, SIGNAL(clicked()), SLOT(cancelClicked())); +} + +/* + * Destructor + */ +KBgInvite::~KBgInvite() +{ + delete d; +} + +/* + * After hiding, we tell our creator that we are ready to die. + */ +void KBgInvite::hide() +{ + emit dialogDone(); +} + +/* + * Set player name + */ +void KBgInvite::setPlayer(const QString &player) +{ + d->mLe->setText(player); +} + +/* + * Invitation with number + */ +void KBgInvite::inviteClicked() +{ + QString tmp; + emit inviteCommand(QString("invite ") + d->mLe->text() + " " + tmp.setNum(d->mSb->value())); +} + +/* + * Invitation for unlimited match + */ +void KBgInvite::unlimitedClicked() +{ + emit inviteCommand(QString("invite ") + d->mLe->text() + " unlimited"); +} + +/* + * Resume a game + */ +void KBgInvite::resumeClicked() +{ + emit inviteCommand(QString("invite ") + d->mLe->text()); +} + +/* + * Slot for Cancel. clear everything to default. + */ +void KBgInvite::cancelClicked() +{ + d->mSb->setValue(1); + d->mLe->clear(); +} + +// EOF diff --git a/kbackgammon/engines/fibs/kbginvite.h b/kbackgammon/engines/fibs/kbginvite.h new file mode 100644 index 00000000..992ee445 --- /dev/null +++ b/kbackgammon/engines/fibs/kbginvite.h @@ -0,0 +1,113 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGINVITE_H +#define __KBGINVITE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class KBgInvitePrivate; + +/** + * + * This class implements a dialog for inviting players for games. It + * is quite simple (but follows the default style guide). The dialog + * offers specific numbers, unlimited and resume as invitation + * options. + * + * @short Simple dialog that allows to invite somebody on FIBS + * @author Jens Hoefkens + * + */ +class KBgInvite : public KDialog +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgInvite(const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgInvite(); + +public slots: + + /** + * After hiding, we tell our creator that we are ready to die. + */ + virtual void hide(); + + /** + * Set the name of the player in the line editor + */ + void setPlayer(const QString &name); + +protected slots: + + /** + * Emits the FIBS invitation command if the Ok button was clicked. + */ + void inviteClicked(); + + /** + * Ask FIBS to resume a match + */ + void resumeClicked(); + + /** + * Ask FIBS for an unlimited match + */ + void unlimitedClicked(); + + /** + * Clear the entry field + */ + void cancelClicked(); + +signals: + + /** + * Emits the text of an invitation + */ + void inviteCommand(const QString &cmd); + + /** + * Delete the dialog after it is closed. + */ + void dialogDone(); + +private: + + KBgInvitePrivate *d; +}; + +#endif // __KBGINVITE_H diff --git a/kbackgammon/engines/fibs/kplayerlist.cpp b/kbackgammon/engines/fibs/kplayerlist.cpp new file mode 100644 index 00000000..102c354d --- /dev/null +++ b/kbackgammon/engines/fibs/kplayerlist.cpp @@ -0,0 +1,902 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kplayerlist.moc" +#include "kplayerlist.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kbgfibs.h" +#include "version.h" + + +/* + * Simple container for information on columns of the list view. + * + * index : the internal index in the list + * width : width of the column in pixel + * show : whether the column is visible + * cb : check box for the setup dialog + */ +class KFibsPlayerListCI { + +public: + + int index, width; + bool show; + QCheckBox *cb; + QString key, name; + +}; + +/* + * Extension of the QListViewItem class that has a custom key function + * that can deal with the different items of the player list. + */ +class KFibsPlayerListLVI : public KListViewItem { + +public: + + /* + * Constructor + */ + KFibsPlayerListLVI(KFibsPlayerList *parent) : KListViewItem(parent) { _plist = parent; } + + /* + * Destructor + */ + virtual ~KFibsPlayerListLVI() {} + + /* + * Overloaded key function for sorting + */ + virtual QString key(int col, bool) const + { + int real_col = _plist->cIndex(col); + + QString s = text(col); + + switch (real_col) { + case KFibsPlayerList::Player: + case KFibsPlayerList::Opponent: + case KFibsPlayerList::Watches: + case KFibsPlayerList::Client: + case KFibsPlayerList::Email: + case KFibsPlayerList::Status: + case KFibsPlayerList::Host: + s = s.lower(); + break; + case KFibsPlayerList::Idle: + case KFibsPlayerList::Experience: + s.sprintf("%08d", s.toInt()); + break; + case KFibsPlayerList::Rating: + s.sprintf("%08d", (int)(1000*s.toDouble())); + break; + case KFibsPlayerList::Time: + s = s.lower(); + break; + default: + kdDebug(10500) << "KFibsPlayerListLVI::key(): illegal column" << endl; + break; + } + return s; + } + +private: + + KFibsPlayerList *_plist; + +}; + +/* + * Private data of the player list + */ +class KFibsPlayerListPrivate { + +public: + + /* + * Named constants for the popup menu actions + */ + enum MenuID {Info, Talk, Mail, InviteD, Invite1, Invite2, Invite3, Invite4, + Invite5, Invite6, Invite7, InviteR, InviteU, + Look, Watch, Unwatch, BlindAct, Update, Reload, Close, ActionEnd}; + + /* + * Various actions for the context menu + */ + KAction *mAct[ActionEnd]; + + /* + * All relevant information on the columns + */ + KFibsPlayerListCI *mCol[KFibsPlayerList::LVEnd]; + + /* + * Context menus for player related commands + */ + QPopupMenu *mPm[2]; + + /* + * ID of the invite menu in the context menu + */ + int mInID; + + /* + * Are we watching? + */ + bool mWatch; + + /* + * count similar clients - KFibs & kbackgammon + */ + int mCount[2]; + + /* + * Short abbreviations for Blind, Ready, and Away. + */ + QString mAbrv[KFibsPlayerList::MaxStatus]; + + /* + * Name of the last selected player - for internal purposes + */ + QString mUser; + + /* + * Our own name + */ + QString mName; + + /* + * Email address of the last selected player - for internal purposes + */ + QString mMail; + +}; + + +// == constructor, destructor and setup ======================================== + +/* + * Construct the playerlist and do some initial setup + */ +KFibsPlayerList::KFibsPlayerList(QWidget *parent, const char *name) + : KListView(parent, name) +{ + d = new KFibsPlayerListPrivate(); + KActionCollection* actions = new KActionCollection(this); + + /* + * Allocate the column information + */ + for (int i = 0; i < LVEnd; i++) + d->mCol[i] = new KFibsPlayerListCI(); + + /* + * Initialize variables + */ + d->mCol[Player]->name = i18n("Player"); + d->mCol[Opponent]->name = i18n("Opponent"); + d->mCol[Watches]->name = i18n("Watches"); + d->mCol[Status]->name = i18n("Status"); + d->mCol[Rating]->name = i18n("Rating"); + d->mCol[Experience]->name = i18n("Exp."); + d->mCol[Idle]->name = i18n("Idle"); + d->mCol[Time]->name = i18n("Time"); + d->mCol[Host]->name = i18n("Host name"); + d->mCol[Client]->name = i18n("Client"); + d->mCol[Email]->name = i18n("Email"); + + // These strings shouldn't be translated!! + d->mCol[Player]->key = "player"; + d->mCol[Opponent]->key = "opponent"; + d->mCol[Watches]->key = "watches"; + d->mCol[Status]->key = "status"; + d->mCol[Rating]->key = "rating"; + d->mCol[Experience]->key = "experience"; + d->mCol[Idle]->key = "idle"; + d->mCol[Time]->key = "time"; + d->mCol[Host]->key = "hostname"; + d->mCol[Client]->key = "client"; + d->mCol[Email]->key = "email"; + + d->mCount[0] = d->mCount[1] = 0; + + d->mAbrv[Blind] = i18n("abreviate blind", "B"); + d->mAbrv[Away ] = i18n("abreviate away", "A"); + d->mAbrv[Ready] = i18n("abreviate ready", "R"); + + d->mName = QString::null; + + d->mWatch = false; + + /* + * Get a sane caption, initialize some eye candy and read the + * configuration - needed for the column information. + */ + updateCaption(); + setIcon(kapp->miniIcon()); + QWhatsThis::add(this, i18n("This window contains the player list. It shows " + "all players that are currently logged into FIBS." + "Use the right mouse button to get a context " + "menu with helpful information and commands.")); + + readColumns(); + + /* + * Put the columns into the list view + */ + for (int i = 0; i < LVEnd; i++) { + if (d->mCol[i]->show) { + d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width); + if (i == Experience || i == Rating || i == Time || i == Idle) + setColumnAlignment(d->mCol[i]->index, AlignRight); + } else { + d->mCol[i]->index = -1; + } + } + setAllColumnsShowFocus(true); + + /* + * Create context menus + */ + d->mPm[0] = new QPopupMenu(); + d->mPm[1] = new QPopupMenu(); + + /* + * Create the whole set of actions + */ + d->mAct[KFibsPlayerListPrivate::Info] = new KAction(i18n("Info"), + QIconSet(kapp->iconLoader()->loadIcon + ("help.xpm", KIcon::Small)), + 0, this, SLOT(slotInfo()), actions); + d->mAct[KFibsPlayerListPrivate::Talk] = new KAction(i18n("Talk"), + QIconSet(kapp->iconLoader()->loadIcon + (PROG_NAME "-chat.png", KIcon::Small)), + 0, this, SLOT(slotTalk()), actions); + + d->mAct[KFibsPlayerListPrivate::Look] = new KAction(i18n("Look"), 0, this, SLOT(slotLook()), actions); + d->mAct[KFibsPlayerListPrivate::Watch] = new KAction(i18n("Watch"), 0, this, SLOT(slotWatch()), actions); + d->mAct[KFibsPlayerListPrivate::Unwatch] = new KAction(i18n("Unwatch"), 0, this, SLOT(slotUnwatch()),actions); + d->mAct[KFibsPlayerListPrivate::BlindAct] = new KAction(i18n("Blind"), 0, this, SLOT(slotBlind()), actions); + d->mAct[KFibsPlayerListPrivate::Update] = new KAction(i18n("Update"), 0, this, SLOT(slotUpdate()), actions); + + d->mAct[KFibsPlayerListPrivate::Reload] = KStdAction::redisplay(this, SLOT(slotReload()), actions); + d->mAct[KFibsPlayerListPrivate::Mail] = KStdAction::mail(this, SLOT(slotMail()), actions); + d->mAct[KFibsPlayerListPrivate::Close] = KStdAction::close(this, SLOT(hide()), actions); + + d->mAct[KFibsPlayerListPrivate::InviteD] = new KAction(i18n("Use Dialog"), 0, this, + SLOT(slotInviteD()), actions); + d->mAct[KFibsPlayerListPrivate::Invite1] = new KAction(i18n("1 Point Match"), 0, this, + SLOT(slotInvite1()), actions); + d->mAct[KFibsPlayerListPrivate::Invite2] = new KAction(i18n("2 Point Match"), 0, this, + SLOT(slotInvite2()), actions); + d->mAct[KFibsPlayerListPrivate::Invite3] = new KAction(i18n("3 Point Match"), 0, this, + SLOT(slotInvite3()), actions); + d->mAct[KFibsPlayerListPrivate::Invite4] = new KAction(i18n("4 Point Match"), 0, this, + SLOT(slotInvite4()), actions); + d->mAct[KFibsPlayerListPrivate::Invite5] = new KAction(i18n("5 Point Match"), 0, this, + SLOT(slotInvite5()), actions); + d->mAct[KFibsPlayerListPrivate::Invite6] = new KAction(i18n("6 Point Match"), 0, this, + SLOT(slotInvite6()), actions); + d->mAct[KFibsPlayerListPrivate::Invite7] = new KAction(i18n("7 Point Match"), 0, this, + SLOT(slotInvite7()), actions); + d->mAct[KFibsPlayerListPrivate::InviteU] = new KAction(i18n("Unlimited"), 0, this, + SLOT(slotInviteU()), actions); + d->mAct[KFibsPlayerListPrivate::InviteR] = new KAction(i18n("Resume"), 0, this, + SLOT(slotInviteR()), actions); + + /* + * Fill normal context menu + */ + d->mAct[KFibsPlayerListPrivate::Info]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Talk]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Mail]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mInID = d->mPm[0]->insertItem(i18n("Invite"), d->mPm[1]); // save ID for later + d->mAct[KFibsPlayerListPrivate::Look ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Watch ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Unwatch ]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::BlindAct]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Update]->plug(d->mPm[0]); + d->mAct[KFibsPlayerListPrivate::Reload]->plug(d->mPm[0]); + d->mPm[0]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Close]->plug(d->mPm[0]); + + /* + * Fill the invitation menu + */ + d->mAct[KFibsPlayerListPrivate::InviteD]->plug(d->mPm[1]); + d->mPm[1]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::Invite1]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite2]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite3]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite4]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite5]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite6]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::Invite7]->plug(d->mPm[1]); + d->mPm[1]->insertSeparator(); + d->mAct[KFibsPlayerListPrivate::InviteU]->plug(d->mPm[1]); + d->mAct[KFibsPlayerListPrivate::InviteR]->plug(d->mPm[1]); + + /* + * Right mouse button gets context menu, double click gets player info + */ + connect(this, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint &)), + this, SLOT(showContextMenu(KListView *, QListViewItem *, const QPoint &))); + connect(this, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)), + this, SLOT(getPlayerInfo(QListViewItem *, const QPoint &, int))); +} + +/* + * Destructor deletes members + */ +KFibsPlayerList::~KFibsPlayerList() +{ + for (int i = 0; i < LVEnd; i++) + delete d->mCol[i]; + delete d->mPm[0]; + delete d->mPm[1]; + delete d; +} + + +// == settings and config ====================================================== + +/* + * Called when the setup dialog is positively closed + */ +void KFibsPlayerList::setupOk() +{ + int i; + bool change = false; + + for (i = 1; i < LVEnd; i++) + change |= (d->mCol[i]->cb->isChecked() != d->mCol[i]->show); + + /* + * Only juggle with the columns if something changed + */ + if (change) { + + /* + * It's important to remove the columns in reverse order + */ + for (i = LVEnd-1; i > 0; i--) + if (d->mCol[i]->show) + removeColumn(d->mCol[i]->index); + + /* + * Now add all columns that are selected + */ + for (i = 1; i < LVEnd; i++) { + if ((d->mCol[i]->show = d->mCol[i]->cb->isChecked())) { + d->mCol[i]->index = addColumn(d->mCol[i]->name, d->mCol[i]->width); + if (i == Experience || i == Rating || i == Time || i == Idle) + setColumnAlignment(d->mCol[i]->index, AlignRight); + } else { + d->mCol[i]->index = -1; + } + } + + /* + * Reload the list + */ + slotReload(); + } + + /* + * store the new settings + */ + saveConfig(); + +} + +/* + * Setup dialog page of the player list - allow the user to select the + * columns to show + */ +void KFibsPlayerList::getSetupPages(KTabCtl *nb, int space) +{ + int i; + + /* + * Main Widget + */ + QWidget *w = new QWidget(nb); + QGridLayout *gl = new QGridLayout(w, 2, 1, space); + + /* + * Label + */ + QGroupBox *gbl = new QGroupBox(w); + gbl->setTitle(i18n("Column Selection")); + + gl->addWidget(gbl, 0, 0); + + /* + * Note that the first column (Player == 0) is always there + */ + QLabel *lb = new QLabel(i18n("Select all the columns that you would\n" + "like to be shown in the player list."), gbl); + + for (i = 1; i < LVEnd; i++) { + d->mCol[i]->cb = new QCheckBox(d->mCol[i]->name, gbl); + d->mCol[i]->cb->setChecked(d->mCol[i]->show); + } + + gl = new QGridLayout(gbl, LVEnd, 2, 20); + gl->addWidget(lb, 0, 0); + + // two column layout.... + for (i = 1; i < LVEnd/2; i++) { + gl->addWidget(d->mCol[2*i-1]->cb, i, 0); + gl->addWidget(d->mCol[2*i ]->cb, i, 1); + } + gl->addWidget(d->mCol[2*i-1]->cb, i, 0); + if (2*i < LVEnd) + gl->addWidget(d->mCol[2*i]->cb, i, 1); + + /* + * put in the page and connect + */ + nb->addTab(w, i18n("&Playerlist")); + + connect(nb, SIGNAL(applyButtonPressed()), this, SLOT(setupOk())); +} + +/* + * Nothing to cancel/undo + */ +void KFibsPlayerList::setupCancel() +{ + // do nothing +} + +/* + * By default all entries are checked + */ +void KFibsPlayerList::setupDefault() +{ + for (int i = 0; i < LVEnd; i++) + d->mCol[i]->cb->setChecked(true); +} + +/* + * Restore the columns + */ +void KFibsPlayerList::readColumns() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + for (int i = 0; i < LVEnd; i++) { + d->mCol[i]->show = config->readBoolEntry("col-" + d->mCol[i]->key, true); + d->mCol[i]->width = config->readNumEntry("col-w-" + d->mCol[i]->key, -1); + } +} + +/* + * Restore the saved settings + */ +void KFibsPlayerList::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + QPoint pos, defpos(10, 10); + pos = config->readPointEntry("ori", &defpos); + setGeometry(pos.x(), pos.y(), config->readNumEntry("wdt",460), + config->readNumEntry("hgt",190)); + + (config->readBoolEntry("vis", false)) ? show() : hide(); + + readColumns(); +} + +/* + * Save current settings + */ +void KFibsPlayerList::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + config->writeEntry("ori", pos()); + config->writeEntry("hgt", height()); + config->writeEntry("wdt", width()); + + config->writeEntry("vis", isVisible()); + + for (int i = 0; i < LVEnd; i++) { + config->writeEntry("col-" + d->mCol[i]->key, d->mCol[i]->show); + config->writeEntry("col-w-" + d->mCol[i]->key, + (d->mCol[i]->show) ? columnWidth(d->mCol[i]->index) : -1); + } +} + + +// == popup menu slots and functions =========================================== + +/* + * Save selected player, update the menu entries and show the popup menu + */ +void KFibsPlayerList::showContextMenu(KListView *, QListViewItem *i, const QPoint &p) +{ + /* + * Get the name of the selected player + */ + d->mUser = (i ? i->text(Player) : QString::null); + + d->mAct[KFibsPlayerListPrivate::Info ]->setText(i18n("Info on %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Talk ]->setText(i18n("Talk to %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Mail ]->setText(i18n("Email to %1").arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Look ]->setText(i18n("Look at %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Watch ]->setText(i18n("Watch %1" ).arg(d->mUser)); + d->mAct[KFibsPlayerListPrivate::Update]->setText(i18n("Update %1" ).arg(d->mUser)); + + d->mAct[KFibsPlayerListPrivate::Info ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Talk ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Mail ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Look ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Watch ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::Update ]->setEnabled(i); + d->mAct[KFibsPlayerListPrivate::BlindAct]->setEnabled(i); + + d->mAct[KFibsPlayerListPrivate::Unwatch]->setEnabled(d->mWatch); + + d->mPm[0]->setItemEnabled(d->mInID, i && d->mName != d->mUser); + d->mPm[0]->changeItem(d->mInID, i18n("Invite %1").arg(d->mUser)); + + d->mMail = (i && d->mCol[Email]->show ? i->text(d->mCol[Email]->index) : QString::null); + d->mAct[KFibsPlayerListPrivate::Mail]->setEnabled(!d->mMail.isEmpty()); + + if (i && d->mCol[Status]->show) + d->mAct[KFibsPlayerListPrivate::BlindAct]->setText + ((i->text(d->mCol[Status]->index).contains(d->mAbrv[Blind])) ? + i18n("Unblind %1").arg(d->mUser) : i18n("Blind %1").arg(d->mUser)); + else + d->mAct[KFibsPlayerListPrivate::BlindAct]->setText(i18n("Blind")); + + // show the menu + d->mPm[0]->popup(p); +} + +/* + * Reload the entire list + */ +void KFibsPlayerList::slotReload() +{ + emit fibsCommand("rawwho"); + clear(); +} + +/* + * Stop watching + */ +void KFibsPlayerList::slotUnwatch() +{ + emit fibsCommand("unwatch"); +} + +/* + * Blind/Unblind user + */ +void KFibsPlayerList::slotBlind() +{ + emit fibsCommand("blind " + d->mUser); +} + +/* + * Start talking to user + */ +void KFibsPlayerList::slotTalk() +{ + emit fibsTalk(d->mUser); +} + +/* + * Request information on user + */ +void KFibsPlayerList::slotInfo() +{ + emit fibsCommand("whois " + d->mUser); +} + +/* + * Look at user + */ +void KFibsPlayerList::slotLook() +{ + emit fibsCommand("look " + d->mUser); +} + +/* + * Send an email to player user at address email + */ +void KFibsPlayerList::slotMail() +{ + kapp->invokeMailer(d->mMail, QString::null); +} + +/* + * Request a new entry for user + */ +void KFibsPlayerList::slotUpdate() +{ + emit fibsCommand("rawwho " + d->mUser); +} + +/* + * Watch user and get an updated board + */ +void KFibsPlayerList::slotWatch() +{ + emit fibsCommand("watch " + d->mUser); + emit fibsCommand("board"); +} + +/* + * Request information about the selected user + */ +void KFibsPlayerList::getPlayerInfo(QListViewItem *i, const QPoint &, int col) +{ + int num = cIndex(col); + if (col < 0 || num < 0 || num > 2 || i->text(num).isEmpty()) + num = 0; + emit fibsCommand("whois " + i->text(num)); +} + +/* + * Invite the selected user. + */ +void KFibsPlayerList::slotInviteD() +{ + emit fibsInvite(d->mUser); +} +void KFibsPlayerList::slotInvite1() { emit fibsCommand("invite " + d->mUser + " 1"); } +void KFibsPlayerList::slotInvite2() { emit fibsCommand("invite " + d->mUser + " 2"); } +void KFibsPlayerList::slotInvite3() { emit fibsCommand("invite " + d->mUser + " 3"); } +void KFibsPlayerList::slotInvite4() { emit fibsCommand("invite " + d->mUser + " 4"); } +void KFibsPlayerList::slotInvite5() { emit fibsCommand("invite " + d->mUser + " 5"); } +void KFibsPlayerList::slotInvite6() { emit fibsCommand("invite " + d->mUser + " 6"); } +void KFibsPlayerList::slotInvite7() { emit fibsCommand("invite " + d->mUser + " 7"); } + +void KFibsPlayerList::slotInviteU() { emit fibsCommand("invite " + d->mUser + " unlimited"); } +void KFibsPlayerList::slotInviteR() { emit fibsCommand("invite " + d->mUser); } + + +// == inserting and updating the list ========================================== + +/* + * Add or change the entry of player with the corresponding string + * from the server - rawwho + */ +void KFibsPlayerList::changePlayer(const QString &line) +{ + char entry[LVEnd][100]; + char ready[2], away[2]; + QListViewItem *i; + QDateTime fromEpoch; + QString str_entry[LVEnd], tmp; + + entry[Status][0] = '\0'; + + // the line comes from FIBS and is 7 bit ASCII + sscanf(line.latin1(), "%99s %99s %99s %1s %1s %99s %99s %99s %99s %99s %99s %99s", entry[Player], entry[Opponent], + entry[Watches], ready, away, entry[Rating], entry[Experience], entry[Idle], entry[Time], + entry[Host], entry[Client], entry[Email]); + + // convert time + tmp = entry[Time]; + fromEpoch.setTime_t(tmp.toUInt()); + strcpy(entry[Time], fromEpoch.toString().latin1()); + + // clear empty strings and copy + for (int j = 0; j < LVEnd; j++) { + if ((str_entry[j] = entry[j]) == "-") + str_entry[j] = ""; + } + str_entry[Status].replace(Ready, 1, ready[0] == '0' ? "-" : d->mAbrv[Ready]); + str_entry[Status].replace(Away, 1, away [0] == '0' ? "-" : d->mAbrv[Away ]); + str_entry[Status].replace(Blind, 1, "-"); + + // disable drawing until the end of update + setUpdatesEnabled(false); + + // try to find the item in the list + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(0) == str_entry[Player]) { + i = it.current(); + goto found; + } + } + + // getting here means we have to create a new entry + i = new KFibsPlayerListLVI(this); + + // count the KFibs and KBackgammon clients + if (str_entry[Client].contains("KFibs")) + d->mCount[0]++; + else if (str_entry[Client].contains(PROG_NAME)) + d->mCount[1]++; + + // new entry requires an update to the player count + updateCaption(); + + goto update; + + found: + + // getting here means the player is in the list - update private status + str_entry[Status].replace(Blind,1,i->text(Status).contains + (d->mAbrv[Blind]) ? d->mAbrv[Blind] : "-"); + + update: + + for (int j = 0; j < LVEnd; j++) { + if (d->mCol[j]->show) + i->setText(d->mCol[j]->index, str_entry[j]); + } + + // find out if we are watching somebody + if (d->mName == str_entry[Player]) + d->mWatch = !str_entry[Watches].isEmpty(); +} + +/* + * Remove player from the list + */ +void KFibsPlayerList::deletePlayer(const QString &player) +{ + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(0) == player) { + if (it.current()->text(Client).contains(PROG_NAME)) + --d->mCount[1]; + else if (it.current()->text(Client).contains("KFibs")) + --d->mCount[0]; + delete it.current(); + updateCaption(); + return; + } + } +} + +/* + * Set/Unset the status stat in the corresponding column of the list + */ +void KFibsPlayerList::changePlayerStatus(const QString &player, int stat, bool flag) +{ + QListViewItem *i = 0; + + /* + * Find the correct line + */ + QListViewItemIterator it(this); + for ( ; it.current(); ++it) { + if (it.current()->text(Player) == player) { + i = it.current(); + break; + } + } + if (!i) return; + + /* + * Update the status flag + */ + i->setText(Status, i->text(Status).replace(stat, 1, (flag) ? d->mAbrv[stat] : "-")); +} + + +// == various slots and functions ============================================== + +/* + * Reverse column to index mapping. Return negative on error. + */ +int KFibsPlayerList::cIndex(int col) +{ + for (int i = 0; i < LVEnd; i++) + if (d->mCol[i]->index == col) + return i; + return -1; +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KFibsPlayerList::showEvent(QShowEvent *e) +{ + KListView::showEvent(e); + emit windowVisible(true); +} + +/* + * Catch hide events, so the engine's menu can be update. + */ +void KFibsPlayerList::hideEvent(QHideEvent *e) +{ + emit windowVisible(false); + KListView::hideEvent(e); +} + +/* + * Called at the end of updates to re-enable the UI + */ +void KFibsPlayerList::stopUpdate() +{ + setUpdatesEnabled(true); + triggerUpdate(); +} + +/* + * Knowing our own name allows us to disable certain menu entries for + * ourselves. + */ +void KFibsPlayerList::setName(const QString &name) +{ + d->mName = name; +} + +/* + * Update the caption of the list by including the current client + * count + */ +void KFibsPlayerList::updateCaption() +{ + setCaption(i18n("Player List - %1 - %2/%3").arg(childCount()).arg(d->mCount[0]).arg(d->mCount[1])); +} + +/* + * Clear the list and reset the client counters + */ +void KFibsPlayerList::clear() +{ + d->mCount[0] = 0; + d->mCount[1] = 0; + QListView::clear(); +} + +// EOF diff --git a/kbackgammon/engines/fibs/kplayerlist.h b/kbackgammon/engines/fibs/kplayerlist.h new file mode 100644 index 00000000..701f9ace --- /dev/null +++ b/kbackgammon/engines/fibs/kplayerlist.h @@ -0,0 +1,298 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KPLAYERLIST_H +#define __KPLAYERLIST_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class KTabCtl; +class KFibsPlayerListPrivate; + +/** + * + * A class that keeps track of players on the server. The server is flooding + * us with user information. At any given time we are able to have an current + * list of all loged-in players and their status. + * + * @short The FIBS backgammon engine player list + * @author Jens Hoefkens + * + */ +class KFibsPlayerList : public KListView +{ + Q_OBJECT + +public: + + /** + * Enumerate player status + */ + enum PStatus {Ready, Away, Blind, MaxStatus}; + + /** + * Enumerate the different columns of the list + */ + enum {Player, Opponent, Watches, Status, Rating, Experience, + Idle, Time, Host, Client, Email, LVEnd}; + + /** + * Constructor + */ + KFibsPlayerList(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KFibsPlayerList(); + + /** + * Clear the list and reset the client counter + */ + virtual void clear(); + +public slots: + + /** + * Remove the player with the name given by the first word + */ + void deletePlayer(const QString &player); + + /** + * Change/Add the entry for the given player + */ + void changePlayer(const QString &line); + + /** + * Enables list redraws after an update + */ + void stopUpdate(); + + /** + * Read the UI settings from disk + */ + void readConfig(); + + /** + * Read the column info from disk + */ + void readColumns(); + + /** + * Restore settings from previously stored settings + */ + void saveConfig(); + + /** + * Change the status of a player + */ + void changePlayerStatus(const QString &player, int stat, bool flag); + + /** + * Fills the playerlist page into the notebook + */ + virtual void getSetupPages(KTabCtl *nb, int space); + + /** + * Save setting changes + */ + void setupOk(); + + /** + * Setup changes have been cancelled + */ + void setupCancel(); + + /** + * Set default settings + */ + void setupDefault(); + + /** + * Set our own name. This allows us to special case the context + * menu. + */ + void setName(const QString &name); + + /** + * Return the column index + */ + int cIndex(int col); + +protected: + + /** + * Catch show events, so the engine's menu can be update. + */ + virtual void showEvent(QShowEvent *e); + + /** + * Catch hide events, so the engine's menu can be update. + */ + virtual void hideEvent(QHideEvent *e); + +protected slots: + + /** + * Double click handler, requests information on a player + */ + void getPlayerInfo(QListViewItem *i, const QPoint &p, int col); + + /** + * Display a popup menu for the current player + */ + void showContextMenu(KListView *, QListViewItem *, const QPoint &); + + /** + * Reload the whole list + */ + void slotReload(); + + /** + * Upate the caption + */ + void updateCaption(); + + /** + * Watch user + */ + void slotWatch(); + + /** + * Update line of user + */ + void slotUpdate(); + + /** + * Request information on user + */ + void slotInfo(); + + /** + * Look at user + */ + void slotLook(); + + /** + * Send an email to user + */ + void slotMail(); + + /** + * Stop watching + */ + void slotUnwatch(); + + /** + * Blind user + */ + void slotBlind(); + + /** + * Talk to user + */ + void slotTalk(); + + /** + * Invite using the dialog + */ + void slotInviteD(); + + /** + * Invite to a 1 point match + */ + void slotInvite1(); + + /** + * Invite to a 2 point match + */ + void slotInvite2(); + + /** + * Invite to a 3 point match + */ + void slotInvite3(); + + /** + * Invite to a 4 point match + */ + void slotInvite4(); + + /** + * Invite to a 5 point match + */ + void slotInvite5(); + + /** + * Invite to a 6 point match + */ + void slotInvite6(); + + /** + * Invite to a 7 point match + */ + void slotInvite7(); + + /** + * Invite to resume a saved match + */ + void slotInviteR(); + + /** + * Invite to an unlimited match + */ + void slotInviteU(); + +signals: + + /** + * Send a command to the server + */ + void fibsCommand(const QString &); + + /** + * Initiate an invitation of a player + */ + void fibsInvite(const QString &); + + /** + * Request talking to player user + */ + void fibsTalk(const QString &); + + /** + * Allow the engine's menu to be updated + */ + void windowVisible(bool); + +private: + + KFibsPlayerListPrivate *d; + +}; + +#endif // __KPLAYERLIST_H diff --git a/kbackgammon/engines/generic/Makefile.am b/kbackgammon/engines/generic/Makefile.am new file mode 100644 index 00000000..da0f2fdb --- /dev/null +++ b/kbackgammon/engines/generic/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libkbggeneric.la + +libkbggeneric_la_SOURCES = kbgengine.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/generic/kbgengine.cpp b/kbackgammon/engines/generic/kbgengine.cpp new file mode 100644 index 00000000..bbe528a6 --- /dev/null +++ b/kbackgammon/engines/generic/kbgengine.cpp @@ -0,0 +1,62 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include +#include + +#include + +#include +#include "kbgengine.h" + + +/* + * Constructor initializes the QObject + */ +KBgEngine::KBgEngine(QWidget *parent, QString *name, QPopupMenu *pmenu) + : QObject(parent, name->local8Bit()) +{ + menu = pmenu; + cl = -1; + ct = new QTimer(this); + connect(ct, SIGNAL(timeout()), this, SLOT(done())); +} + +/* + * Destructor is empty + */ +KBgEngine::~KBgEngine() +{ + // empty +} + +/* + * Set the length of the commit timeout. Negative values disable the + * feature. + */ +void KBgEngine::setCommit(const double com) +{ + cl = int(1000*com); +} + +// EOF diff --git a/kbackgammon/engines/generic/kbgengine.h b/kbackgammon/engines/generic/kbgengine.h new file mode 100644 index 00000000..ee672f40 --- /dev/null +++ b/kbackgammon/engines/generic/kbgengine.h @@ -0,0 +1,298 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGENGINE_H +#define __KBGENGINE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QTimer; +class QPopupMenu; + +class KDialogBase; + +class KBgStatus; + +/** + * + * Abstract class for a generic backgammon engine. Real engines have + * to inherit this and implement the interfaces. + * + * Engines can and will use the following global events described in + * the file eventsrc: + * + * "game over l" + * "game over w" + * + * "roll" + * "roll or double" + * + * "move" + * "invitation" + * + * @short Abstract base class for backgammon engines + * @author Jens Hoefkens + * + */ +class KBgEngine:public QObject +{ + Q_OBJECT public: + + enum Command { Redo, Undo, Roll, Cube, Done, Load }; + + /** + * Constructor + */ + KBgEngine (QWidget * parent = 0, QString * name = 0, QPopupMenu * pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngine (); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages (KDialogBase * nb) = 0; + + /** + * Called after the user clicked ok in the setup dialog. Time + * to save settings. + */ + virtual void setupOk () = 0; + + /** + * The user cancelled the setup + */ + virtual void setupCancel () = 0; + + /** + * Set engine defaults + */ + virtual void setupDefault () = 0; + + /** + * Called when the windows are about to be hidden. Engines + * should hide all their child windows. + * + * The default implementation does nothing. + */ + virtual void hideEvent () + { + } + + /** + * Called when the windows are about to be shown. Engines + * should show all visible child windows. + * + * The default implementation does nothing. + */ + virtual void showEvent () + { + } + + /** + * Start the engine. This is called pretty much right after + * the constructor. While the constructor may not have any + * user interaction, it is possible to display dialogs in + * start. + * + * The default implementation does nothing. + */ + virtual void start () + { + } + + /** + * Check with the engine if we can quit. This may require user + * interaction. + * + * The default implementation returns true. + */ + virtual bool queryClose () + { + return true; + } + + /** + * About to be closed. Let the engine exit properly. + * + * The default implementation returns true. + */ + virtual bool queryExit () + { + return true; + } + + /** + * Set the length of the commit timeout. Negative values + * disable the feature. + */ + void setCommit (const double com = 2.5); + +public slots: + /** + * Read user settings from the config file + */ + virtual void readConfig () = 0; + + /** + * Save user settings to the config file + */ + virtual void saveConfig () = 0; + + /** + * Roll dice for the player w + */ + virtual void rollDice (const int w) = 0; + + /** + * Double the cube of player w + */ + virtual void doubleCube (const int w) = 0; + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove (QString * s) = 0; + + /** + * Undo the last move + */ + virtual void undo () = 0; + + /** + * Redo the last move + */ + virtual void redo () = 0; + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll () = 0; + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube () = 0; + + /** + * Reload the board to the last known sane state + */ + virtual void load () = 0; + + /** + * Commit a move + */ + virtual void done () = 0; + + /** + * Process the string cmd + */ + virtual void handleCommand (const QString & cmd) = 0; + + /** + * Start a new game + */ + virtual void newGame () + { + } + + /** + * Can we start a new game? + */ + virtual bool haveNewGame () + { + return false; + } + +signals: + + /** + * The text identifies the current game status - could be put + * into the main window caption + */ + void statText (const QString & msg); + + /** + * Text that should be displayed in the ongoing message window + */ + void infoText (const QString & msg); + + /** + * Emit the most recent game state + */ + void newState (const KBgStatus &); + + /** + * Tell the board that we need the current state of the board. + */ + void getState (KBgStatus *); + + /** + * Starts/ends the edit mode of the board + */ + void setEditMode (const bool f); + + /** + * Toggle RO/RW flag of the board + */ + void allowMoving (const bool fl); + + /** + * Announce that we will accept/reject the command cmd from + * now on + */ + void allowCommand (int cmd, bool f); + + /** + * Tell the board to undo the last move + */ + void undoMove (); + + /** + * Tell the board to redo the last undone move + */ + void redoMove (); + +protected: + + /** + * Context menu for the board + */ + QPopupMenu * menu; + + /** + * Commit timer + */ + QTimer *ct; + int cl; + +}; + +#endif // __KBGENGINE_H diff --git a/kbackgammon/engines/gnubg/Makefile.am b/kbackgammon/engines/gnubg/Makefile.am new file mode 100644 index 00000000..7c0c9e1e --- /dev/null +++ b/kbackgammon/engines/gnubg/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbggnubg.la + +libkbggnubg_la_SOURCES = kbggnubg.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/gnubg/kbggnubg.cpp b/kbackgammon/engines/gnubg/kbggnubg.cpp new file mode 100644 index 00000000..eaaa4bdf --- /dev/null +++ b/kbackgammon/engines/gnubg/kbggnubg.cpp @@ -0,0 +1,710 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbggnubg.moc" +#include "kbggnubg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kbgstatus.h" +#include "kbgboard.h" +#include "version.h" + + +// == cube ===================================================================== + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineGNU::cube() +{ + handleCommand("double"); +} + +/* + * Double the cube for player w + */ +void KBgEngineGNU::doubleCube(const int w) +{ + dummy = w; // avoid compiler warning + cube(); +} + + + + + +void KBgEngineGNU::handleLine(const QString &l) +{ + if (l.isEmpty()) + return; + + QString line(l); + + /* + * Start of a new game/match + */ + if (line.contains(QRegExp("^gnubg rolls [1-6], .* rolls [1-6]\\."))) { + KRegExp e("^gnubg rolls ([1-6]), .* rolls ([1-6])\\."); + e.match(line.latin1()); + if (int r = strcmp(e.group(1), e.group(2))) + turn = (r < 0) ? uRoll : tRoll; + } + + /* + * Bug fixes for older versions of GNUBG - to be removed + */ + if (line.contains(QRegExp("^.* cannot move\\..+$"))) { + KRegExp e("(^.* cannot move.)(.*$)"); + e.match(line.latin1()); + handleLine(e.group(1)); + handleLine(e.group(2)); + return; + } + if (line.contains(QRegExp("^Are you sure you want to start a new game, and discard the one in progress\\?"))) { + KRegExp e("(^Are you sure you want to start a new game, and discard the one in progress\\? )(.+$)"); + e.match(line.latin1()); + handleLine(e.group(1)); + handleLine(e.group(2)); + return; + } + + /* + * Cube handling + */ + if (line.contains(QRegExp("^gnubg accepts and immediately redoubles to [0-9]+\\.$"))) { + + // redoubles mess up the game counter "turn" + + //KBgStatus st(board); + //st.setCube(32, BOTH); + //emit newState(st); + + } + if (line.contains(QRegExp("^gnubg doubles\\.$"))) { + + // TODO: we need some generic class for this. the class + // can be shared between all engines + +#if 0 + KBgStatus st(board); + + int ret = KMessageBox::warningYesNoCancel + (0, i18n("gnubg doubles the cube to %1.").arg(2*st.cube(THEM)), + i18n("gnubg doubles"), + i18n("&Accept"), i18n("Re&double"), i18n("&Reject"), true); + + switch (ret) { + + case KMessageBox::Yes: + handleCommand("accept"); + break; + + case KMessageBox::No: + handleCommand("redouble"); + break; + + case KMessageBox::Cancel: + handleCommand("reject"); + break; + } +#endif + } + + /* + * Ignore the following messages + */ + if (line.contains(QRegExp("^TTY boards will be given in raw format"))) { + line = " "; + } + + /* + * Board messages + */ + if (line.contains(QRegExp("^board:"))) { + + KBgStatus st(line); + + /* + * Do preliminary analysis of board + */ + if (st.doubled()) { + --turn; + return; + } + if (strcmp(board.latin1(),line.latin1())) + ++turn %= maxTurn; + board = line; + + /* + * Act according to the current state in the move/roll cycle + */ + switch (turn) { + + case uRoll: + + if (st.cube() > 0) { + emit infoText(i18n("Please roll or double.")); + KNotifyClient::event("roll or double"); + } else { + emit infoText(i18n("Please roll.")); + KNotifyClient::event("roll"); + } + + emit allowCommand(Roll, true); + emit allowCommand(Cube, true); + break; + + case uMove: + st.setDice(THEM, 0, 0); + st.setDice(THEM, 1, 0); + emit infoText(i18n("You roll %1 and %2.").arg(st.dice(US, 0)).arg(st.dice(US, 1))); + switch (st.moves()) { + case 0: + // get a message + break; + case 1: + emit infoText(i18n("Please move 1 piece.")); + break; + default: + emit infoText(i18n("Please move %1 pieces.").arg(st.moves())); + break; + } + emit allowCommand(Roll, false); + break; + + case tRoll: + break; + + case tMove: + st.setDice(US, 0, 0); + st.setDice(US, 1, 0); + emit infoText(i18n("gnubg rolls %1 and %2.").arg(st.dice(THEM, 0)).arg(st.dice(THEM, 1))); + if (st.moves() == 0) + emit infoText(i18n("gnubg cannot move.")); + + break; + + } + + /* + * Bookkeeping + */ + undoCounter = 0; + toMove = st.moves(); + emit allowMoving(st.turn() == US); + emit newState(st); + + emit statText(i18n("%1 vs. %2").arg(st.player(US)).arg(st.player(THEM))); + + emit allowCommand(Load, true ); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + emit allowCommand(Done, false); + return; + } + + /* + * Show the line... + */ + line.replace(QRegExp(" "), " "); + if (!line.isEmpty()) + emit infoText(line); +} + + +/* + * Handle textual commands. All commands are passed to gnubg. + */ +void KBgEngineGNU::handleCommand(const QString& cmd) +{ + cmdList += cmd; + nextCommand(); +} + + + +// == start and init games ===================================================== + +/* + * Start a new game. + */ +void KBgEngineGNU::newGame() +{ + /* + * If there is a game running we warn the user first + */ + if (gameRunning && (KMessageBox::warningYesNo((QWidget *)parent(), + i18n("A game is currently in progress. " + "Starting a new one will terminate it."), + QString::null, i18n("Start New Game"), i18n("Continue Old Game")) + == KMessageBox::No)) + return; + + /* + * Start new game + */ + handleCommand("new game"); + if (gameRunning) + handleCommand("yes"); + + gameRunning = true; + + emit infoText(i18n("Starting a new game.")); +} + + + +// == various slots & functions ================================================ + +/* + * Quitting is fine at any time + */ +bool KBgEngineGNU::queryClose() +{ + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineGNU::queryExit() +{ + return true; +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineGNU::load() +{ + handleCommand("show board"); +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineGNU::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + rollingAllowed = f; + return; + case Undo: + undoPossible = f; + return; + case Cube: + doublePossible = f; + return; + case Done: + donePossible = f; + return; + } +} + + + + + + + + + + + + + + + +// == configuration handling =================================================== + +void KBgEngineGNU::setupOk() +{ + // nothing yet +} + +void KBgEngineGNU::setupCancel() +{ + // nothing yet +} + +void KBgEngineGNU::setupDefault() +{ + // nothing yet +} + +void KBgEngineGNU::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *w = nb->addVBoxPage(i18n("GNU Engine"), i18n("Here you can configure the GNU backgammon engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); +} + +/* + * Restore settings + */ +void KBgEngineGNU::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("gnu engine"); + + // nothing yet +} + +/* + * Save the engine specific settings + */ +void KBgEngineGNU::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("gnu engine"); + + // nothing yet +} + + + +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** +// ***************************************************************************** + + + + +// == constructor, destructor and other ======================================== + +/* + * Constructor + */ +KBgEngineGNU::KBgEngineGNU(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + // obsolete + nameUS = "US"; + nameTHEM = "THEM"; + random.setSeed(getpid()*time(NULL)); + + /* + * internal statue variables + */ + rollingAllowed = undoPossible = gameRunning = donePossible = false; + connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool))); + + /* + * Setup of menu + */ + resAction = new KAction(i18n("&Restart GNU Backgammon"), 0, this, SLOT(startGNU()), this); + resAction->setEnabled(false); resAction->plug(menu); + + /* + * Restore last stored settings + */ + readConfig(); +} + +/* + * Destructor. Kill the child process and that's it. + */ +KBgEngineGNU::~KBgEngineGNU() +{ + gnubg.kill(); +} + + +// == start, restart, termination of gnubg ===================================== + +/* + * Start the GNU Backgammon process in the background and set up + * some communication links. + */ +void KBgEngineGNU::start() +{ + /* + * Will be started later + */ + cmdTimer = new QTimer(this); + connect(cmdTimer, SIGNAL(timeout()), SLOT(nextCommand()) ); + + emit infoText(i18n("This is experimental code which currently requires a specially " + "patched version of GNU Backgammon.

")); + + /* + * Initialize variables + */ + partline = board = ""; + + /* + * Start the process - this requires that gnubg is in the PATH + */ + gnubg << "gnubg" << "--tty"; + + connect(&gnubg, SIGNAL(processExited(KProcess *)), this, SLOT(gnubgExit(KProcess *))); + connect(&gnubg, SIGNAL(receivedStderr(KProcess *, char *, int)), + this, SLOT(receiveData(KProcess *, char *, int))); + connect(&gnubg, SIGNAL(receivedStdout(KProcess *, char *, int)), + this, SLOT(receiveData(KProcess *, char *, int))); + connect(&gnubg, SIGNAL(wroteStdin(KProcess *)), this, SLOT(wroteStdin(KProcess *))); + + startGNU(); +} + +/* + * Actually start the background process. + */ +void KBgEngineGNU::startGNU() +{ + + resAction->setEnabled(false); + + if (!gnubg.start(KProcess::NotifyOnExit, KProcess::All)) + KMessageBox::information((QWidget *)parent(), + i18n("Could not start the GNU Backgammon process.\n" + "Make sure the program is in your PATH and is " + "called \"gnubg\".\n" + "Make sure that your copy is at least version 0.10")); + + /* + * Set required gnubg options + */ + handleCommand("set output rawboard on"); +} + +/* + * The gnubg process has died. Stop all user activity and allow a restart. + */ +void KBgEngineGNU::gnubgExit(KProcess *proc) +{ + ct->stop(); + + cmdTimer->stop(); + + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Load, false); + + emit allowMoving(false); + + emit infoText(QString("
") + i18n("The GNU Backgammon process (%1) has exited. ") + .arg(proc->pid()) + "
"); + + resAction->setEnabled(true); +} + + +// == communication callbacks with GNU bg ====================================== + +/* + * Last command has been sent. Try to send pending ones. + */ +void KBgEngineGNU::wroteStdin(KProcess *proc) +{ + if (!proc->isRunning()) + return; + nextCommand(); +} + +/* + * Try to send the next command from the command list to gnubg. + * If it fails, make sure we call ourselves again. + */ +void KBgEngineGNU::nextCommand() +{ + if (!gnubg.isRunning()) + return; + + for (QStringList::Iterator it = cmdList.begin(); it != cmdList.end(); ++it) { + QString s = (*it) + "\n"; + if (!gnubg.writeStdin(s.latin1(), strlen(s.latin1()))) { + cmdTimer->start(250, true); + cmdList.remove(QString::null); + return; + } + (*it) = QString::null; + } + cmdList.remove(QString::null); + cmdTimer->stop(); +} + +/* + * Get data from GNU Backgammon and process them. Note that we may have + * to buffer the last line and wait for the closing newline... + */ +void KBgEngineGNU::receiveData(KProcess *proc, char *buffer, int buflen) +{ + if (!proc->isRunning()) + return; + + char *buf = new char[buflen+1]; + + memcpy(buf, buffer, buflen); + buf[buflen] = '\0'; + + QStringList l(QStringList::split('\n', buf, true)); + + /* + * Restore partial lines from the previous time + */ + l.first() = partline + l.first(); + partline = ""; + if (buf[buflen-1] != '\n') { + partline = l.last(); + l.remove(partline); + } + + delete[] buf; + + /* + * Handle the information from gnubg + */ + for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) + handleLine(*it); +} + + +// == moving =================================================================== + +/* + * Finish the last move - called by the timer and directly by the user + */ +void KBgEngineGNU::done() +{ + ct->stop(); + + emit allowMoving(false); + + emit allowCommand(Done, false); + emit allowCommand(Undo, false); + emit allowCommand(Redo, false); + + // Transform the string to FIBS format + lastmove.replace(0, 2, "move "); + lastmove.replace(QRegExp("\\+"), " "); + lastmove.replace(QRegExp("\\-"), " "); + + // sent it to the server + handleCommand(lastmove); +} + +/* + * Undo the last move + */ +void KBgEngineGNU::undo() +{ + ct->stop(); + + redoPossible = true; + ++undoCounter; + + emit allowMoving(true); + + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + + emit undoMove(); +} + +/* + * Redo the last move + */ +void KBgEngineGNU::redo() +{ + --undoCounter; + emit redoMove(); +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineGNU::handleMove(QString *s) +{ + lastmove = *s; + + int index = 0; + QString t = s->mid(index, s->find(' ', index)); + index += 1 + t.length(); + int moves = t.toInt(); + + /* + * Allow undo and possibly start the commit timer + */ + redoPossible &= ((moves < toMove) && (undoCounter > 0)); + + emit allowCommand(Undo, moves > 0); + emit allowCommand(Redo, redoPossible); + emit allowCommand(Done, moves == toMove); + + if (moves == toMove && cl >= 0) { + emit allowMoving(false); + ct->start(cl, true); + } +} + + +// == dice & rolling =========================================================== + +/* + * Roll random dice for the player whose turn it is. We can ignore the + * value of w, since we have the turn value. + */ +void KBgEngineGNU::roll() +{ + if (turn == uRoll) + handleCommand("roll"); +} +void KBgEngineGNU::rollDice(const int w) +{ + roll(); +} + + + +// EOF diff --git a/kbackgammon/engines/gnubg/kbggnubg.h b/kbackgammon/engines/gnubg/kbggnubg.h new file mode 100644 index 00000000..3240b8b1 --- /dev/null +++ b/kbackgammon/engines/gnubg/kbggnubg.h @@ -0,0 +1,223 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGGNU_H +#define __KBGGNU_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +/** + * + * + */ +class KBgEngineGNU : public KBgEngine +{ + Q_OBJECT + +public: + + /* + * Constructor and destructor + */ + KBgEngineGNU(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + virtual ~KBgEngineGNU(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + virtual void start(); + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /* + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Process the string cmd + */ + virtual void handleCommand(const QString& cmd); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + +protected slots: + + /** + * Store if cmd is allowed or not + */ + void setAllowed(int cmd, bool f); + + void startGNU(); + +private: + + /** + * Use the standard method of obtaining random numbers + */ + KRandomSequence random; + + /** + * Player's names + */ + QString nameUS, nameTHEM; + + /** + * Who did the last roll + */ + int lastRoll; + + /** + * How many checkers to move + */ + int toMove; + + /** + * Various flags, representing the current status of the game + */ + bool rollingAllowed, undoPossible, donePossible; + bool gameRunning, redoPossible, doublePossible; + + /** + * Count the number of available undos + */ + int dummy, undoCounter; + +private: + + enum Turn {uRoll, uMove, tRoll, tMove, maxTurn}; + + KProcess gnubg; + + QStringList cmdList; + + QTimer *cmdTimer; + + QString partline; + + QString board; + + QString lastmove; + + int turn; + + KAction *resAction; + +protected slots: + + void wroteStdin(KProcess *); + + void receiveData(KProcess *, char *buffer, int buflen); + + void handleLine(const QString &l); + + void gnubgExit(KProcess *proc); + + void nextCommand(); + +}; + +#endif // __KBGGNU_H diff --git a/kbackgammon/engines/nextgen/Makefile.am b/kbackgammon/engines/nextgen/Makefile.am new file mode 100644 index 00000000..ed58d2f4 --- /dev/null +++ b/kbackgammon/engines/nextgen/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgnextgen.la + +libkbgnextgen_la_SOURCES = kbgng.cpp kbgplayer.cpp kbggame.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + -I$(top_srcdir)/libkdegames/kgame $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/nextgen/kbggame.cpp b/kbackgammon/engines/nextgen/kbggame.cpp new file mode 100644 index 00000000..6ee709e1 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbggame.cpp @@ -0,0 +1,47 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbggame.moc" +#include "kbggame.h" + +#include + +#include + +/* + * Constructor + */ +KBgGame::KBgGame(int cookie, QObject *parent) + : KGame(cookie, parent) +{ + // do nothing... +} + +bool KBgGame::playerInput(QDataStream &msg,KPlayer *player) +{ + Q_INT32 move; + msg >> move; + cerr << " Player " << player->id() << " moved to " << move << endl; + return true; +} + diff --git a/kbackgammon/engines/nextgen/kbggame.h b/kbackgammon/engines/nextgen/kbggame.h new file mode 100644 index 00000000..fea5f516 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbggame.h @@ -0,0 +1,57 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGGAME_H +#define __KBGGAME_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +class QObject; +class KPlayer; + +/** + * + * + */ +class KDE_EXPORT KBgGame : public KGame +{ + Q_OBJECT + +public: + + enum MsgID {Text, Cmd, MaxMsg}; + + KBgGame(int cookie = 42, QObject *parent = 0); + +protected: + + virtual bool playerInput(QDataStream &msg,KPlayer *player); + +}; + +#endif // __KBGGAME_H + diff --git a/kbackgammon/engines/nextgen/kbgng.cpp b/kbackgammon/engines/nextgen/kbgng.cpp new file mode 100644 index 00000000..6518147c --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgng.cpp @@ -0,0 +1,622 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgng.moc" +#include "kbgng.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + +/* + * Constructor + */ +KBgEngineNg::KBgEngineNg(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + // get a new game + initGame(); + + // create actions and menus + QString label[MaxTypes]; + + label[Local ] = i18n("Local Games"); + label[NetServer] = i18n("Offer Network Games"); + label[NetClient] = i18n("Join Network Games"); + + QStringList list; + for (int i = 0; i < MaxTypes; i++) + list.append(label[i]); + + _gameSelect = new KSelectAction(i18n("&Types"), 0, this, SLOT(setGame()), this); + _gameSelect->setItems(list); + _gameSelect->plug(menu); + + menu->insertSeparator(); + + _connectAction = new KAction(i18n("&Names..."), 0, this, SLOT(changeName()), this); + _connectAction->plug(menu); + + // Restore last settings + readConfig(); + + // initialize to local games + _player[0] = _player[1] = 0; + _currGame = None; + _gameSelect->setCurrentItem(Local); + setGame(); +} + + +/* + * Switch the local game type. This is called by the menu... + * + * TODO: lots of work and testing needed... + */ +void KBgEngineNg::setGame() +{ + // shutdown old game + switch (_currGame) { + + case Local: + // nothing to do... + break; + + case NetServer: + _game->stopServerConnection(); + break; + + case NetClient: + _game->disconnect(); + break; + + default: + // ignore + break; + } + + // reset the game and delete the players + delete _game; + initGame(); + + emit infoText("
"); + + // initialize a new game + bool ret = false; + QString label, port_s, host_s; + Q_UINT16 port; + + switch (_currGame = _gameSelect->currentItem()) { + + case Local: + + _game->addPlayer(createPlayer(0, _name[0])); + _game->addPlayer(createPlayer(1, _name[1])); + break; + + case NetServer: + label = i18n("Type the port number on which you want to listen to " + "connections.\nThe number should be between 1024 and " + "65535."); + port_s.setNum(_port); + do { + port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent()); + if (!ret) + return; + port = port_s.toUShort(&ret); + } while (port_s.isEmpty() && !ret); + + if (_game->offerConnections(port)) + emit infoText(i18n("Now waiting for incoming connections on port %1."). + arg(_port = port)); + else + emit infoText(i18n("Failed to offer connections on port %1.").arg(port)); + + _game->addPlayer(createPlayer(0, _name[0])); + break; + + case NetClient: + label = i18n("Type the name of the server you want to connect to:"); + host_s = _host; + do { + host_s = KLineEditDlg::getText(label, host_s, &ret, (QWidget *)parent()); + if (!ret) + return; + } while (host_s.isEmpty()); + + label = i18n("Type the port number on %1 you want to connect to.\nThe " + "number should be between 1024 and 65535.").arg(host_s); + port_s.setNum(_port); + do { + port_s = KLineEditDlg::getText(label, port_s, &ret, (QWidget *)parent()); + if (!ret) + return; + port = port_s.toUShort(&ret); + } while (port_s.isEmpty() && !ret); + + /* + * Hi Martin: another thing you night want to try is to move this to the + * place marked by (about 10 lines further down. If you do that, the + * players are created properly on the server, but a total of three players + * is created on the client. + */ + _game->addPlayer(createPlayer(0, _name[0])); + + if (_game->connectToServer(host_s, port)) + emit infoText(i18n("Now connected to %1:%2.").arg(_host = host_s). + arg(_port = port)); + else + emit infoText(i18n("Failed to connect to %1:%2.").arg(_host = host_s). + arg(_port = port)); + + // + + break; + + default: + kdDebug(true, PROG_COOKIE) << "setGame parameter invalid: " + << _currGame << endl; + _currGame = None; + return; + } + + // we are still having problems with player creation... + + // FIXME - which status _game->setGameStatus(KGame::End); +} + +void KBgEngineNg::slotPlayerJoinedGame(KPlayer *p) +{ + emit infoText(i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id())); + cerr << i18n("Player %1 (%2) has joined the game.").arg(p->name()).arg(p->id()).latin1() << endl; +} + +void KBgEngineNg::slotCreatePlayer(KPlayer *&p, int rtti, int io, bool v, KGame *g) +{ + Q_UNUSED(rtti) + Q_UNUSED(g) + Q_UNUSED(io) + emit infoText(i18n("creating player. virtual=%1").arg(v)); + p = createPlayer(1); +} + +void KBgEngineNg::slotClientConnected(Q_UINT32) +{ + cerr << "client has joint the game..." << endl; +} + +void KBgEngineNg::slotClientDisconnected(Q_UINT32, bool) +{ + cerr << "KBgEngineNg::slotClientDisconnected" << endl; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// == start and init games ===================================================== + +/* + * Start a new game. ... + */ +void KBgEngineNg::newGame() +{ + // TODO + cerr << "games are not yet working..." << endl; +} + +/* + * Finish the last move - called by the timer and directly by the used + */ +void KBgEngineNg::done() +{ + // empty +} + +/* + * Undo the last move + */ +void KBgEngineNg::undo() +{ + // TODO +} + +/* + * Redo the last move + */ +void KBgEngineNg::redo() +{ + // TODO +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineNg::handleMove(QString *s) +{ + Q_UNUSED(s) + // TODO +} + +/* + * Roll random dice for the player whose turn it is + */ +void KBgEngineNg::roll() +{ + // empty +} + +/* + * If possible, roll random dice for player w + */ +void KBgEngineNg::rollDice(const int w) +{ + Q_UNUSED(w) + // empty +} + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineNg::cube() +{ + // TODO +} + +/* + * Double the cube for player w + */ +void KBgEngineNg::doubleCube(const int) +{ + cube(); +} + +/* + * Put the engine specific details in the setup dialog + */ +void KBgEngineNg::getSetupPages(KDialogBase *) +{ + // FIXME: do nothing... +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineNg::setupOk() +{ + // FIXME: do nothing... +} +void KBgEngineNg::setupDefault() +{ + // FIXME: do nothing... +} +void KBgEngineNg::setupCancel() +{ + // FIXME: do nothing... +} + + +// == various slots & functions ================================================ + +/* + * Check with the user if we should really quit in the middle of a + * game. + */ +bool KBgEngineNg::queryClose() +{ + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineNg::queryExit() +{ + return true; +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineNg::load() +{ + // TODO +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineNg::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + rollingAllowed = f; + return; + case Undo: + undoPossible = f; + return; + case Cube: + doublePossible = f; + return; + case Done: + donePossible = f; + return; + } +} + + +// ******************************************************************************** +// ******************************************************************************** + +// DONE + +// ******************************************************************************** +// ******************************************************************************** + + +/* + * Destructor. + */ +KBgEngineNg::~KBgEngineNg() +{ + saveConfig(); + delete _game; +} + +/* + * Restore settings + */ +void KBgEngineNg::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("next generation engine"); + + _port = config->readNumEntry("port", PROG_COOKIE); + _host = config->readEntry("host", "localhost"); + + _name[0] = config->readEntry("name_0", i18n("one")); + _name[1] = config->readEntry("name_1", i18n("two")); +} + +/* + * Save the engine specific settings + */ +void KBgEngineNg::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("next generation engine"); + + config->writeEntry("port", _port); + config->writeEntry("host", _host); + + config->writeEntry("name_0", _name[0]); + config->writeEntry("name_1", _name[1]); +} + +/* + * Read the users input from the command line and send it to all + * players. Although the message gets the Cmd ID, it is currently + * handled as a regular text message. + */ +void KBgEngineNg::handleCommand(const QString& text) +{ + QByteArray msg; + QTextStream ts(msg, IO_WriteOnly); + ts << text; + if (!_game->sendMessage(msg, KBgGame::Cmd)) + kdDebug(true, PROG_COOKIE) << "couldn't send message: " + << text.latin1() << endl; +} + +/* + * Return a random integer between 1 and 6. Use the KGame random + * number generator. + */ +int KBgEngineNg::getRandom() +{ + return 1+_game->random()->getLong(6); +} + +/* + * A player propert has changed - check if we care + */ +void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KPlayer *me) +{ + int player = (me->id() == _player[1]->id()); + + switch (p->id()) { + + case KGamePropertyBase::IdName: + emit infoText(i18n("Player %1 has changed the name to %2.") + .arg(_name[player]).arg(me->name())); + _name[player] = me->name(); + break; + + default: + kdDebug(true, PROG_COOKIE) << "KBgPlayer (" << me << ") property change (" + << p->id() << ") ignored" << endl; + break; + } +} + +/* + * A game property has changed + */ +void KBgEngineNg::slotPropertyChanged(KGamePropertyBase *p, KGame *me) +{ + Q_UNUSED(me) + switch (p->id()) { + + default: + kdDebug(true, PROG_COOKIE) << "Change in GameProperty " << p->id() + << " has been ignored." << endl; + break; + } +} + +/* + * Change the names of all local players + */ +void KBgEngineNg::changeName() +{ + bool ok = false; + QString name; + + for (int i = 0; i < 2; i++) { + name = QString::null; + while (!_player[i]->isVirtual() && name.isEmpty()) { + if (i == 0) + name = KLineEditDlg::getText(i18n("Type the name of the first player:"), + _name[i], &ok, (QWidget *)parent()); + else + name = KLineEditDlg::getText(i18n("Type the name of the second player:"), + _name[i], &ok, (QWidget *)parent()); + if (!ok) return; + _player[i]->setName(name); + } + } +} + +/* + * Receive data sent via KBgGame::sendMessage(...) + */ +void KBgEngineNg::slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 r, Q_UINT32 s) +{ + Q_UNUSED(r); + Q_UNUSED(s); + switch (msgid) { + + case KBgGame::Cmd: + emit infoText(msg); + emit infoText(i18n("Players are %1 and %2").arg(_player[0]->name()) + .arg(_player[1]->name())); + break; + + default: + kdDebug(true, PROG_COOKIE) << "Ignored message ID: " << msgid << endl; + break; + } +} + +/* + * Create the i-th player + */ +KBgPlayer * KBgEngineNg::createPlayer(int i, QString name) +{ + KBgPlayer *p = new KBgPlayer(); + + if (!name.isNull()) + p->setName(name); + + p->findProperty(KGamePropertyBase::IdName)->setEmittingSignal(true); + + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KPlayer *)), + this, SLOT(slotPropertyChanged(KGamePropertyBase *, KPlayer *))); + + return (_player[i] = p); +} + +/* + * Create and connect the game object + */ +void KBgEngineNg::initGame() +{ + _game = new KBgGame(PROG_COOKIE); + _game->random()->setSeed(getpid()*time(NULL)); + + connect(_game, SIGNAL(signalPlayerJoinedGame(KPlayer *)), + this, SLOT(slotPlayerJoinedGame(KPlayer *))); + connect(_game, SIGNAL(signalCreatePlayer(KPlayer *&, int, int, bool, KGame *)), + this, SLOT(slotCreatePlayer(KPlayer *&, int, int, bool, KGame *))); + + connect(_game, SIGNAL(signalClientConnected(Q_UINT32)), + this, SLOT(slotClientConnected(Q_UINT32))); + connect(_game, SIGNAL(signalClientDisconnected(Q_UINT32, bool)), + this, SLOT(slotClientDisconnected(Q_UINT32, bool))); + + connect(_game, SIGNAL(signalPropertyChanged(KGamePropertyBase *, KGame *)), + this, SLOT(slotPropertyChanged(KGamePropertyBase *, KGame *))); + connect(_game, SIGNAL(signalNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32)), + this, SLOT(slotNetworkData(int,const QByteArray &, Q_UINT32, Q_UINT32))); +} + +// EOF diff --git a/kbackgammon/engines/nextgen/kbgng.h b/kbackgammon/engines/nextgen/kbgng.h new file mode 100644 index 00000000..149f3bf6 --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgng.h @@ -0,0 +1,263 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGNG_H +#define __KBGNG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include + +#include + +#include "kbgboard.h" +#include "kbgstatus.h" +#include "kbgplayer.h" +#include "kbggame.h" + + +/** + * + * The interface of the next generation backgammon engine. + * + */ +class KBgEngineNg : public KBgEngine +{ + Q_OBJECT + +public: + + /* + * Constructor and destructor + */ + KBgEngineNg( QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + virtual ~KBgEngineNg(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + virtual void setupOk(); + virtual void setupDefault(); + virtual void setupCancel(); + + /* + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /** + * Process the string text + */ + virtual void handleCommand(const QString& text); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + + + void slotPlayerJoinedGame(KPlayer *p); + void slotNetworkData(int msgid, const QByteArray &msg, Q_UINT32 receiver, Q_UINT32 sender); + void slotCreatePlayer(KPlayer *&, int, int, bool, KGame *); + + void slotClientDisconnected(Q_UINT32, bool); + void slotClientConnected(Q_UINT32); + + void slotPropertyChanged(KGamePropertyBase *p, KGame *me); + void slotPropertyChanged(KGamePropertyBase *p, KPlayer *me); + +protected slots: + + void initGame(); + + void setGame(); + + void changeName(); + +protected: + + void setAllowed(int cmd, bool f); + +private: + + + /** + * Who did the last roll + */ + int lastRoll; + + /** + * How many checkers to move + */ + int toMove; + + /** + * Various flags, representing the current status of the game + */ + bool rollingAllowed, undoPossible, donePossible; + bool gameRunning, redoPossible, doublePossible; + + /** + * Count the number of available undos + */ + int dummy, undoCounter; + + + + + + + + + + + + + + + enum GameTypes {None = -1, Local, NetServer, NetClient, MaxTypes}; + KSelectAction * _gameSelect; + KAction* _connectAction; + KAction* _nameAction; + int _currGame; + int _nLocalPlayers; + + int _nplayers; + + QString _host; + Q_UINT16 _port; + + // ************************************************************ + // ************************************************************ + + // DONE + + // ************************************************************ + // ************************************************************ + + +protected: + + /** + * Return a random integer between 1 and 6. The random numer + * is based on the @ref KRandomSequence of @ref KGame. Thus, + * the numbers should be synchronized across the network. + */ + int getRandom(); + +private: + + /** + * Create the i-th player. Legal values for i are 0 and 1. The + * name of the player is taken from @ref _name and the parent of + * the player is @ref _player. That means that the players are + * automatically deleted. + */ + KBgPlayer * createPlayer(int i, QString name = QString::null); + +private: + + KBgGame* _game; + + QString _name[2]; + + KBgPlayer* _player[2]; + +}; + +#endif // __KBGNG_H diff --git a/kbackgammon/engines/nextgen/kbgplayer.cpp b/kbackgammon/engines/nextgen/kbgplayer.cpp new file mode 100644 index 00000000..f0b3a7ed --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgplayer.cpp @@ -0,0 +1,62 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgplayer.moc" +#include "kbgplayer.h" + +#include + +#include + + +/* + * Constructors + */ +KBgPlayer::KBgPlayer() + : KPlayer() +{ + // do nothing... +} +KBgPlayer::KBgPlayer(KGame *game) + : KPlayer(game) +{ + // do nothing... +} + +int KBgPlayer::rtti() const +{ + return 10500; +} + +bool KBgPlayer::load(QDataStream &stream) +{ + KPlayer::load(stream); + cerr << "-------- KBgPlayer::load" << endl; + return false; +} +bool KBgPlayer::save(QDataStream &stream) +{ + KPlayer::save(stream); + cerr << "-------- KBgPlayer::save" << endl; + return false; +} diff --git a/kbackgammon/engines/nextgen/kbgplayer.h b/kbackgammon/engines/nextgen/kbgplayer.h new file mode 100644 index 00000000..7c11d83c --- /dev/null +++ b/kbackgammon/engines/nextgen/kbgplayer.h @@ -0,0 +1,58 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGPLAYER_H +#define __KBGPLAYER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class KGame; + + +/** + * + * + */ +class KBgPlayer : public KPlayer +{ + Q_OBJECT + +public: + + KBgPlayer(); + KBgPlayer(KGame* game); + + virtual int rtti() const; + + virtual bool load(QDataStream &stream); + virtual bool save(QDataStream &stream); + +}; + +#endif // __KBGPLAYER_H + diff --git a/kbackgammon/engines/offline/Makefile.am b/kbackgammon/engines/offline/Makefile.am new file mode 100644 index 00000000..82d7a681 --- /dev/null +++ b/kbackgammon/engines/offline/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgoffline.la + +libkbgoffline_la_SOURCES = kbgoffline.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon -I$(top_srcdir)/kbackgammon/engines \ + $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/offline/kbgoffline.cpp b/kbackgammon/engines/offline/kbgoffline.cpp new file mode 100644 index 00000000..920dc741 --- /dev/null +++ b/kbackgammon/engines/offline/kbgoffline.cpp @@ -0,0 +1,810 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbgoffline.moc" +#include "kbgoffline.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" + +class KBgEngineOfflinePrivate +{ +public: + + /* + * Various flags, representing the current status of the game + */ + bool mRollFlag, mUndoFlag, mDoneFlag, mCubeFlag, mGameFlag, mRedoFlag; + + /* + * Store two copies of the game: one backup and a working copy + */ + KBgStatus mGame[2]; + + /* + * Use the standard method of obtaining random numbers + */ + KRandomSequence *mRandom; + + /* + * Game actions + */ + KAction *mNew, *mSwap; + KToggleAction *mEdit; + + /* + * Player's names + */ + QString mName[2]; + + /* + * Who did the last roll + */ + int mRoll; + + /* + * How many checkers to move + */ + int mMove; + + /* + * Count the number of available undos + */ + int mUndo; + + /* + * Entry fields for the names + */ + QLineEdit *mLe[2]; + +}; + + +// == constructor, destructor and other ======================================== + +/* + * Constructor + */ +KBgEngineOffline::KBgEngineOffline(QWidget *parent, QString *name, QPopupMenu *pmenu) + : KBgEngine(parent, name, pmenu) +{ + d = new KBgEngineOfflinePrivate(); + + /* + * get some entropy for the dice + */ + d->mRandom = new KRandomSequence; + d->mRandom->setSeed(0); + + /* + * Create engine specific actions + */ + d->mNew = new KAction(i18n("&New Game..."), 0, this, SLOT(newGame()), this); + d->mSwap = new KAction(i18n("&Swap Colors"), 0, this, SLOT(swapColors()), this); + + d->mEdit = new KToggleAction(i18n("&Edit Mode"), 0, this, + SLOT(toggleEditMode()), this); + d->mEdit->setChecked(false); + + /* + * create & initialize the menu + */ + d->mNew->plug(menu); + d->mEdit->plug(menu); + d->mSwap->plug(menu); + + /* + * get standard board and set it + */ + initGame(); + emit newState(d->mGame[0]); + + /* + * initialize the commit timeout + */ + ct = new QTimer(this); + connect(ct, SIGNAL(timeout()), this, SLOT(done())); + + /* + * internal statue variables + */ + d->mRollFlag = d->mUndoFlag = d->mGameFlag = d->mDoneFlag = false; + connect(this, SIGNAL(allowCommand(int, bool)), this, SLOT(setAllowed(int, bool))); + + /* + * Restore last stored settings + */ + readConfig(); +} + +/* + * Destructor. The only child is the popup menu. + */ +KBgEngineOffline::~KBgEngineOffline() +{ + saveConfig(); + delete d->mRandom; + delete d; +} + + +// == configuration handling =================================================== + +/* + * Put the engine specific details in the setup dialog + */ +void KBgEngineOffline::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *vbp = nb->addVBoxPage(i18n("Offline Engine"), i18n("Use this to configure the Offline engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); + + /* + * Get a multi page work space + */ + KTabCtl *tc = new KTabCtl(vbp, "offline tabs"); + + /* + * Player names + */ + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 2, 1, nb->spacingHint()); + + /* + * Group boxes + */ + QGroupBox *gbn = new QGroupBox(i18n("Names"), w); + + gl->addWidget(gbn, 0, 0); + + gl = new QGridLayout(gbn, 2, 2, 20); + + d->mLe[0] = new QLineEdit(d->mName[0], gbn); + d->mLe[1] = new QLineEdit(d->mName[1], gbn); + + QLabel *lb[2]; + lb[0] = new QLabel(i18n("First player:"), gbn); + lb[1] = new QLabel(i18n("Second player:"), gbn); + + for (int i = 0; i < 2; i++) { + gl->addWidget(lb[i], i, 0); + gl->addWidget(d->mLe[i], i, 1); + } + + QWhatsThis::add(d->mLe[0], i18n("Enter the name of the first player.")); + QWhatsThis::add(d->mLe[1], i18n("Enter the name of the second player.")); + + /* + * Done with the page, put it in + */ + gl->activate(); + tc->addTab(w, i18n("&Player Names")); +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineOffline::setupOk() +{ + d->mName[0] = d->mLe[0]->text(); + d->mName[1] = d->mLe[1]->text(); +} +void KBgEngineOffline::setupDefault() +{ + d->mName[0] = i18n("South"); + d->mName[1] = i18n("North"); +} +void KBgEngineOffline::setupCancel() +{ + // do nothing +} + +/* + * Restore settings + */ +void KBgEngineOffline::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("offline engine"); + + d->mName[0] = config->readEntry("player-one", i18n("South")); // same as above + d->mName[1] = config->readEntry("player-two", i18n("North")); // same as above + cl = config->readNumEntry("timer", 2500); +} + +/* + * Save the engine specific settings + */ +void KBgEngineOffline::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("offline engine"); + + config->writeEntry("player-one", d->mName[0] ); + config->writeEntry("player-two", d->mName[1]); + config->writeEntry("timer", cl); +} + + +// == start and init games ===================================================== + +/* + * Start a new game. + */ +void KBgEngineOffline::newGame() +{ + int u = 0; + int t = 0; + + /* + * If there is a game running we warn the user first + */ + if (d->mGameFlag && (KMessageBox::warningYesNo((QWidget *)parent(), + i18n("A game is currently in progress. " + "Starting a new one will terminate it."), + QString::null, i18n("Start New Game"), + i18n("Continue Old Game")) + == KMessageBox::No)) + return; + + /* + * Separate from the previous game + */ + emit infoText("


"); + + /* + * Get player's names - user can still cancel + */ + if (!queryPlayerName(US) || !queryPlayerName(THEM)) + return; + + /* + * let the games begin + */ + d->mGameFlag = true; + + /* + * Initialize the board + */ + initGame(); + + /* + * Figure out who starts by rolling + */ + while (u == t) { + u = getRandom(); + t = getRandom(); + emit infoText(i18n("%1 rolls %2, %3 rolls %4."). + arg(d->mName[0]).arg(u).arg(d->mName[1]).arg(t)); + } + + if (u > t) { + emit infoText(i18n("%1 makes the first move.").arg(d->mName[0])); + d->mRoll = US; + } else { + emit infoText(i18n("%1 makes the first move.").arg(d->mName[1])); + d->mRoll = THEM; + int n = u; u = t; t = n; + } + + /* + * set the dice and tell the board + */ + rollDiceBackend(d->mRoll, u, t); + + /* + * tell the user + */ + emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1])); +} + +/* + * Initialize the state descriptors mGame[0|1] + */ +void KBgEngineOffline::initGame() +{ + /* + * nobody rolled yet + */ + d->mRoll = -1; + + /* + * set up a standard game + */ + d->mGame[0].setCube(1, true, true); + d->mGame[0].setDirection(+1); + d->mGame[0].setColor(+1); + for (int i = 1; i < 25; i++) + d->mGame[0].setBoard(i, US, 0); + d->mGame[0].setBoard( 1, US, 2); d->mGame[0].setBoard( 6, THEM, 5); + d->mGame[0].setBoard( 8, THEM, 3); d->mGame[0].setBoard(12, US, 5); + d->mGame[0].setBoard(13, THEM, 5); d->mGame[0].setBoard(17, US, 3); + d->mGame[0].setBoard(19, US, 5); d->mGame[0].setBoard(24, THEM, 2); + d->mGame[0].setHome(US, 0); d->mGame[0].setHome(THEM, 0); + d->mGame[0].setBar(US, 0); d->mGame[0].setBar(THEM, 0); + d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0); + d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0); + + /* + * save backup of the game state + */ + d->mGame[1] = d->mGame[0]; + + emit allowCommand(Load, true); +} + +/* + * Open a dialog to query for the name of player w. Return true unless + * the dialog was canceled. + */ +bool KBgEngineOffline::queryPlayerName(int w) +{ + bool ret = false; + QString *name; + QString text; + + if (w == US) { + name = &d->mName[0]; + text = i18n("Please enter the nickname of the player whose home\n" + "is in the lower half of the board:"); + } else { + name = &d->mName[1]; + text = i18n("Please enter the nickname of the player whose home\n" + "is in the upper half of the board:"); + } + + do { + *name = KLineEditDlg::getText(text, *name, &ret, (QWidget *)parent()); + if (!ret) break; + + } while (name->isEmpty()); + + return ret; +} + + +// == moving =================================================================== + +/* + * Finish the last move - called by the timer and directly by the used + */ +void KBgEngineOffline::done() +{ + ct->stop(); + + emit allowMoving(false); + emit allowCommand(Done, false); + emit allowCommand(Undo, false); + + if (abs(d->mGame[0].home(d->mRoll)) == 15) { + + emit infoText(i18n("%1 wins the game. Congratulations!"). + arg((d->mRoll == US) ? d->mName[0] : d->mName[1])); + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + + } else { + + emit allowCommand(Roll, true); + if (d->mGame[0].cube((d->mRoll == US ? THEM : US)) > 0) { + + d->mGame[0].setDice(US , 0, 0); d->mGame[0].setDice(US , 1, 0); + d->mGame[0].setDice(THEM, 0, 0); d->mGame[0].setDice(THEM, 1, 0); + + emit newState(d->mGame[0]); + emit getState(&d->mGame[0]); + + d->mGame[1] = d->mGame[0]; + + emit infoText(i18n("%1, please roll or double."). + arg((d->mRoll == THEM) ? d->mName[0] : d->mName[1])); + emit allowCommand(Cube, true); + + } else { + + roll(); + emit allowCommand(Cube, false); + } + } +} + +/* + * Undo the last move + */ +void KBgEngineOffline::undo() +{ + ct->stop(); + + d->mRedoFlag = true; + ++d->mUndo; + + emit allowMoving(true); + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + emit undoMove(); +} + +/* + * Redo the last move + */ +void KBgEngineOffline::redo() +{ + --d->mUndo; + emit redoMove(); +} + +/* + * Take the move string and make the changes on the working copy + * of the state. + */ +void KBgEngineOffline::handleMove(QString *s) +{ + int index = 0; + QString t = s->mid(index, s->find(' ', index)); + index += 1 + t.length(); + int moves = t.toInt(); + + /* + * Allow undo and possibly start the commit timer + */ + d->mRedoFlag &= ((moves < d->mMove) && (d->mUndo > 0)); + emit allowCommand(Undo, moves > 0); + emit allowCommand(Redo, d->mRedoFlag); + emit allowCommand(Done, moves == d->mMove); + if (moves == d->mMove && cl) { + emit allowMoving(false); + ct->start(cl, true); + } + + /* + * Apply moves to d->mGame[1] and store results in d->mGame[0] + */ + d->mGame[0] = d->mGame[1]; + + /* + * process each individual move + */ + for (int i = 0; i < moves; i++) { + bool kick = false; + t = s->mid(index, s->find(' ', index) - index); + index += 1 + t.length(); + char c = '-'; + if (t.contains('+')) { + c = '+'; + kick = true; + } + QString r = t.left(t.find(c)); + if (r.contains("bar")) { + d->mGame[0].setBar(d->mRoll, abs(d->mGame[0].bar(d->mRoll)) - 1); + } else { + int from = r.toInt(); + d->mGame[0].setBoard(from, d->mRoll, abs(d->mGame[0].board(from)) - 1); + } + t.remove(0, 1 + r.length()); + if (t.contains("off")) { + d->mGame[0].setHome(d->mRoll, abs(d->mGame[0].home(d->mRoll)) + 1); + } else { + int to = t.toInt(); + if (kick) { + d->mGame[0].setBoard(to, d->mRoll, 0); + int el = ((d->mRoll == US) ? THEM : US); + d->mGame[0].setBar(el, abs(d->mGame[0].bar(el)) + 1); + } + d->mGame[0].setBoard(to, d->mRoll, abs(d->mGame[0].board(to)) + 1); + } + } +} + + +// == dice & rolling =========================================================== + +/* + * Roll random dice for the player whose turn it is + */ +void KBgEngineOffline::roll() +{ + rollDice((d->mRoll == US) ? THEM : US); +} + +/* + * If possible, roll random dice for player w + */ +void KBgEngineOffline::rollDice(const int w) +{ + if ((d->mRoll != w) && d->mRollFlag) { + rollDiceBackend(w, getRandom(), getRandom()); + return; + } + emit infoText(i18n("It's not your turn to roll!")); +} + +/* + * Return a random integer between 1 and 6. According to the man + * page of rand(), this is the way to go... + */ +int KBgEngineOffline::getRandom() +{ + return 1+d->mRandom->getLong(6); +} + +/* + * Set the dice for player w to a and b. Reload the board and determine the + * maximum number of moves + */ +void KBgEngineOffline::rollDiceBackend(const int w, const int a, const int b) +{ + /* + * This is a special case that stems from leaving the edit + * mode. + */ + if (a == 0) + return; + + /* + * Set the dice and tel the board about the new state + */ + d->mGame[0].setDice(w, 0, a); + d->mGame[0].setDice(w, 1, b); + d->mGame[0].setDice((w == US) ? THEM : US, 0, 0); + d->mGame[0].setDice((w == US) ? THEM : US, 1, 0); + d->mGame[0].setTurn(w); + + d->mGame[1] = d->mGame[0]; + + d->mRoll = w; + emit newState(d->mGame[0]); + + /* + * No more roling until Done and no Undo yet + */ + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + d->mRedoFlag = false; + d->mUndo = 0; + + /* + * Tell the players how many checkers to move + */ + switch (d->mMove = d->mGame[0].moves()) { + case -1: + emit infoText(i18n("Game over!")); + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + emit allowMoving(false); + break; + case 0: + emit infoText(i18n("%1, you cannot move."). + arg((w == US) ? d->mName[0] : d->mName[1])); + if (cl) + ct->start(cl, true); + emit allowMoving(false); + break; +// case 1: + default: + emit infoText(QString((w == US) ? d->mName[0] : d->mName[1]) + + i18n(", please move 1 piece.",", please move %n pieces.",d->mMove)); + emit allowMoving(true); + break; + } +} + + +// == cube ===================================================================== + +/* + * Double the cube for the player that can double - asks player + */ +void KBgEngineOffline::cube() +{ + int w = ((d->mRoll == US) ? THEM : US); + + if (d->mRollFlag && d->mGame[0].cube(w) > 0) { + emit allowCommand(Cube, false); + if (KMessageBox::questionYesNo((QWidget *)parent(), + i18n("%1 has doubled. %2, do you accept the double?"). + arg((w == THEM) ? d->mName[1] : d->mName[0]). + arg((w == US) ? d->mName[1] : d->mName[0]), + i18n("Doubling"), i18n("Accept"), i18n("Reject")) != KMessageBox::Yes) { + d->mGameFlag = false; + emit allowCommand(Roll, false); + emit allowCommand(Cube, false); + emit infoText(i18n("%1 wins the game. Congratulations!"). + arg((w == US) ? d->mName[0] : d->mName[1])); + return; + } + + emit infoText(i18n("%1 has accepted the double. The game continues."). + arg((w == THEM) ? d->mName[0] : d->mName[1])); + + if (d->mGame[0].cube(US)*d->mGame[0].cube(THEM) > 0) + d->mGame[0].setCube(2, w == THEM, w == US); + else + d->mGame[0].setCube(2*d->mGame[0].cube(w), w == THEM, w == US); + + emit newState(d->mGame[0]); + emit getState(&d->mGame[0]); + + d->mGame[1] = d->mGame[0]; + + roll(); + } +} + +/* + * Double the cube for player w + */ +void KBgEngineOffline::doubleCube(const int) +{ + cube(); +} + + +// == various slots & functions ================================================ + +/* + * Check with the user if we should really quit in the middle of a + * game. + */ +bool KBgEngineOffline::queryClose() +{ + if (!d->mGameFlag) + return true; + + switch (KMessageBox::warningContinueCancel((QWidget *)parent(), + i18n("In the middle of a game. " + "Really quit?"), QString::null, KStdGuiItem::quit())) { + case KMessageBox::Continue : + return TRUE; + case KMessageBox::Cancel : + return FALSE; + default: // cancel + return FALSE; + } + return true; +} + +/* + * Quitting is fine at any time + */ +bool KBgEngineOffline::queryExit() +{ + return true; +} + +/* + * Handle textual commands. Right now, all commands are ignored + */ +void KBgEngineOffline::handleCommand(const QString& cmd) +{ + emit infoText(i18n("Text commands are not yet working. " + "The command '%1' has been ignored.").arg(cmd)); +} + +/* + * Load the last known sane state of the board + */ +void KBgEngineOffline::load() +{ + if (d->mEdit->isChecked()) + emit newState(d->mGame[1]); + else { + // undo up to four moves + undo(); + undo(); + undo(); + undo(); + } +} + +/* + * Store if cmd is allowed or not + */ +void KBgEngineOffline::setAllowed(int cmd, bool f) +{ + switch (cmd) { + case Roll: + d->mRollFlag = f; + return; + case Undo: + d->mUndoFlag = f; + return; + case Cube: + d->mCubeFlag = f; + return; + case Done: + d->mDoneFlag = f; + return; + } +} + +/* + * Swaps the used colors on the board + */ +void KBgEngineOffline::swapColors() +{ + d->mGame[1].setDice(US, 0, d->mGame[0].dice(US, 0)); + d->mGame[1].setDice(US, 1, d->mGame[0].dice(US, 1)); + d->mGame[1].setDice(THEM, 0, d->mGame[0].dice(THEM, 0)); + d->mGame[1].setDice(THEM, 1, d->mGame[0].dice(THEM, 1)); + d->mGame[1].setColor(d->mGame[1].color(THEM), US); + emit newState(d->mGame[1]); + emit getState(&d->mGame[1]); + d->mGame[0] = d->mGame[1]; +} + +/* + * Switch back and forth between edit and play mode + */ +void KBgEngineOffline::toggleEditMode() +{ + emit setEditMode(d->mEdit->isChecked()); + if (d->mEdit->isChecked()) { + ct->stop(); + d->mNew->setEnabled(false); + d->mSwap->setEnabled(false); + emit allowCommand(Undo, false); + emit allowCommand(Roll, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit statText(i18n("%1 vs. %2 - Edit Mode").arg(d->mName[0]).arg(d->mName[1])); + } else { + d->mNew->setEnabled(true); + d->mSwap->setEnabled(true); + emit statText(i18n("%1 vs. %2").arg(d->mName[0]).arg(d->mName[1])); + emit getState(&d->mGame[1]); + d->mGame[0] = d->mGame[1]; + emit allowCommand(Done, d->mDoneFlag); + emit allowCommand(Cube, d->mCubeFlag); + emit allowCommand(Undo, d->mUndoFlag); + emit allowCommand(Roll, d->mRollFlag); + int w =((d->mGame[0].dice(US, 0) && d->mGame[0].dice(US, 1)) ? US : THEM); + rollDiceBackend(w, d->mGame[0].dice(w, 0), d->mGame[0].dice(w, 1)); + } +} + +// EOF diff --git a/kbackgammon/engines/offline/kbgoffline.h b/kbackgammon/engines/offline/kbgoffline.h new file mode 100644 index 00000000..db2bdc03 --- /dev/null +++ b/kbackgammon/engines/offline/kbgoffline.h @@ -0,0 +1,213 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBGOFFLINE_H +#define __KBGOFFLINE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "kbgboard.h" +#include "kbgstatus.h" + +class KBgEngineOfflinePrivate; + +/** + * + * The interface of an offline backgammon engine. The engine is inherently + * stupid and doesn't play - it just manages the games betweeen two humans + * sitting at the same computer. Network enabled games will be part of the + * next generation engine (KBgNg). + * + * @short The offline backgammon engine + * @author Jens Hoefkens + * + */ +class KBgEngineOffline : public KBgEngine +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgEngineOffline(QWidget *parent = 0, QString *name = 0, QPopupMenu *pmenu = 0); + + /** + * Destructor + */ + virtual ~KBgEngineOffline(); + + /** + * Fills the engine-specific page into the notebook + */ + virtual void getSetupPages(KDialogBase *nb); + + /** + * Save new steup + */ + virtual void setupOk(); + + /** + * Load default setup + */ + virtual void setupDefault(); + + /** + * Cancel the changes to the setup + */ + virtual void setupCancel(); + + /** + * Check with the engine if we can quit. This may require user + * interaction. + */ + virtual bool queryClose(); + + /** + * About to be closed. Let the engine exit properly. + */ + virtual bool queryExit(); + +public slots: + + /** + * Read user settings from the config file + */ + virtual void readConfig(); + + /** + * Save user settings to the config file + */ + virtual void saveConfig(); + + /** + * Roll dice for the player w + */ + virtual void rollDice(const int w); + + /** + * Double the cube of player w + */ + virtual void doubleCube(const int w); + + /** + * A move has been made on the board - see the board class + * for the format of the string s + */ + virtual void handleMove(QString *s); + + /** + * Undo the last move + */ + virtual void undo(); + + /** + * Redo the last move + */ + virtual void redo(); + + /** + * Roll dice for whoevers turn it is + */ + virtual void roll(); + + /** + * Double the cube for whoevers can double right now + */ + virtual void cube(); + + /** + * Reload the board to the last known sane state + */ + virtual void load(); + + /** + * Commit a move + */ + virtual void done(); + + /** + * Process the string cmd + */ + virtual void handleCommand(const QString& cmd); + + /** + * Start a new game. + */ + virtual void newGame(); + virtual bool haveNewGame() {return true;} + + +protected slots: + + /** + * Initialize the state descriptors game[0] and game[1] + */ + void initGame(); + + /** + * Switch back and forth between edit and play mode + */ + void toggleEditMode(); + + /** + * Store if cmd is allowed or not + */ + void setAllowed(int cmd, bool f); + + /** + * Swaps the used colors on the board + */ + void swapColors(); + +protected: + + /** + * Returns a random integer between 1 and 6 + */ + int getRandom(); + + /** + * Set the dice for player w to a and b. Reload the board and determine the + * maximum number of moves + */ + void rollDiceBackend(const int w, const int a, const int b); + + /** + * Open a dialog to query for the name of player w. Return true unless + * the dialog was canceled. + */ + bool queryPlayerName(int w); + +private: + + KBgEngineOfflinePrivate *d; + +}; + +#endif // __KBGOFFLINE_H diff --git a/kbackgammon/eventsrc b/kbackgammon/eventsrc new file mode 100644 index 00000000..de6f5f90 --- /dev/null +++ b/kbackgammon/eventsrc @@ -0,0 +1,802 @@ +[!Global!] +IconName=kbackgammon +Comment=KBackgammon +Comment[af]=Kbackgammon +Comment[ar]=لعبة النرد/الطاولة (KBackgammon) +Comment[be]=Ðарды +Comment[bn]=কে-বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ +Comment[cs]=Vrchcáby +Comment[eo]=Bakgamono +Comment[hi]=के-बैकगेमॉन +Comment[ne]=केडीई बà¥à¤¯à¤¾à¤•à¤—ामोन +Comment[pt_BR]=KGamão +Comment[ro]=Joc de table +Comment[sv]=Kbackgammon +Comment[ta]=கேபேகà¯à®•à®®à®¾à®©à¯ +Comment[tg]=KÐардбозӣ +Comment[tr]=KTavla +Comment[zh_TW]=KBackgammon 西洋雙陸棋 + + +[game over w] +Name=Game over, you won +Name[af]=Speletjie bo, jy wen +Name[ar]=اللعبة انتهت، لقد Ùزت +Name[az]=Oyun Qurtardı, siz uddunuz +Name[be]=Канец гульні, вы выйгралі +Name[bg]=Спечелихте +Name[bn]=খেল খতম, আপনি জিতেছেন +Name[br]=Echu an abadenn, aet out ar maout +Name[bs]=Igra zavrÅ¡ena, vi ste pobjednik +Name[ca]=Final del joc, heu guanyat +Name[cs]=Konec hry, vyhrál(a) jste +Name[cy]=Gêm drosodd, ennill wnaethoch chi +Name[da]=Spillet forbi, du vandt +Name[de]=Spiel beendet, Sie haben gewonnen +Name[el]=Τέλος παιχνιδιοÏ, νικήσατε +Name[eo]=Ludo finita, vi gajnis +Name[es]=Fin de la partida, usted ganó +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa amaitu da, irabazi duzu +Name[fa]=بازی تمام شد، شما بردید +Name[fi]=Peli loppu, voitit +Name[fr]=Fin de la partie, vous avez gagné +Name[gl]=Fin do xogo, vostede gaña +Name[he]=המשחק הסתיי×, ניצחת +Name[hi]=खेल ख़तà¥à¤®, आप जीते +Name[hr]=Igra je zavrÅ¡ena. Pobijedili ste! +Name[hu]=Vége a játéknak, Ön nyert +Name[is]=Leik lokið, þú vannst +Name[it]=Partita finita, hai vinto +Name[ja]=ゲームオーãƒãƒ¼ã€ã‚ãªãŸã®å‹ã¡ +Name[km]=ល្បែង​ចប់, អ្នក​ឈ្នះ​ហើយ +Name[lt]=Žaidimas baigtas, jÅ«s laimÄ—jote +Name[lv]=SpÄ“les beigas, jÅ«s uzvarÄ“jÄt +Name[mk]=Играта заврши, вие победивте +Name[mt]=Logħba spiÄ‹Ä‹at - int irbaħt +Name[nb]=Spillet er slutt, du vant +Name[nds]=Speel vörbi, Du hest wunnen +Name[ne]=खेल समापà¥à¤¤, तपाईà¤à¤²à¥‡ जितà¥à¤¨à¥ भयो +Name[nl]=Spel is afgelopen, u hebt gewonnen. +Name[nn]=Spelet er slutt, du vann +Name[nso]=Papadi e fedile, o fentse +Name[pa]=ਖੇਡ ਖਤਮ, ਤà©à¨¸à©€à¨‚ ਜਿੱਤ ਗਠ+Name[pl]=Koniec gry, wygraÅ‚eÅ› +Name[pt]=Fim do jogo, ganhou +Name[pt_BR]=Fim do jogo; você ganhou +Name[ro]=Joc terminat. Ai cîştigat. +Name[ru]=Конец игры, вы выиграли +Name[se]=Speallu nogai, don vuitet +Name[sk]=Koniec hry, vyhrali ste +Name[sl]=Konec igre, zmagali ste +Name[sr]=Крај игре, победили Ñте +Name[sr@Latn]=Kraj igre, pobedili ste +Name[sv]=Spelet är slut, du vann +Name[ta]=விளையாடà¯à®Ÿà¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ வெனà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Name[tg]=Бозӣ ба итмом раÑид, шумо ғолиб омадед +Name[th]=จบเà¸à¸¡ - คุณชนะ +Name[tr]=Oyun bitti, sen kazandın +Name[uk]=Гру завершено, ви виграли +Name[uz]=OÊ»yin tugadi, siz gÊ»alaba qozondingiz +Name[uz@cyrillic]=Ўйин тугади, Ñиз ғалаба қозондингиз +Name[ven]=Mutambo wo fhela, no wina +Name[vi]=Trò chÆ¡i kết thúc, bạn thắng +Name[wa]=Li djeu est houte, vos avoz wangnî +Name[xh]=Umdlalo uphelile, uphumelele +Name[zh_CN]=游æˆç»“æŸï¼Œæ‚¨èµ¢äº† +Name[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨è´äº† +Name[zu]=Umdlalo uphelile, uphemelele +Comment=You have won the current game of backgammon +Comment[af]=Jy het wen die huidige speletjie van backgammon +Comment[ar]=لقد Ùزت اللعبة الحالية من لعبة النرد/الطاولة +Comment[az]=Hazırkı nÉ™rdtaxta oyununu uddunuz +Comment[be]=Ð’Ñ‹ выйгралі партыю Ñž нарды +Comment[bg]=Спечелихте +Comment[bn]=à¦à¦‡ বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলাটি আপনি জিতেছেন +Comment[bs]=Pobjedili ste u trenutnoj backgammon igri +Comment[ca]=Heu guanyat aquesta partida de backgamon +Comment[cs]=Tuto hru ve vrchcáby jste vyhráli +Comment[cy]=Rydych wedi ennill y gêm gyfredol o dawlbwrdd +Comment[da]=Du har vundet dette spil backgammon +Comment[de]=Sie haben die Backgammon-Partie gewonnen! +Comment[el]=ΚεÏδίσατε αυτή την παÏτίδα backgammon +Comment[eo]=Vi gajnis la nunan bakgamonludon +Comment[es]=Usted ha ganado la partida actual de backgammon +Comment[et]=Sa võitsid selle mängu +Comment[eu]=Uneko Backgammon jokoa irabazi duzu +Comment[fa]=شما بازی جاری تخته نرد را بردید +Comment[fi]=Olet voittanut backgammon pelin +Comment[fr]=Vous avez gagné cette partie de backgammon +Comment[gl]=Vostede gañou esta partida de backgammon +Comment[he]=ניצחת במשחק השש־בש הנוכחי +Comment[hi]=आप बैकगेमॉन का हालिया खेल जीत गठ+Comment[hr]=Pobijedili ste u ovoj partiji backgammona +Comment[hu]=Ön megnyerte ezt a backgammon játékot +Comment[is]=Þú vannst þennan Backgammon leik +Comment[it]=Hai vinto questa partita di backgammon +Comment[ja]=ç¾åœ¨ã®backgammonゲームã«å‹ã¡ã¾ã—㟠+Comment[km]=អ្នក​បានឈ្មះ​ល្បែង​បច្ចុប្បន្ន​នៃ backgammon +Comment[lt]=JÅ«s laimÄ—jote šį backgammon žaidimÄ… +Comment[lv]=JÅ«s uzvarÄ“jÄt tekoÅ¡ajÄ bekgemona spÄ“lÄ“ +Comment[mk]=Ја добивте тековната игра на табла +Comment[mt]=Int irbaħt il-logħba preżenti tal-backgammon +Comment[nb]=Du vant det gjeldende backgammon-spillet +Comment[nds]=Du hest den Backgammon-Törn wunnen +Comment[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोनको हालको खेल तपाईà¤à¤²à¥‡ जितà¥à¤¨à¥ भयो +Comment[nl]=U hebt het huidige Backgammon-spel gewonnen. +Comment[nn]=Du har vunne denne backgammon-runden +Comment[nso]=O fentse papadi ya bjale ya backgammon +Comment[pa]=ਤà©à¨¸à©€à¨‚ ਮੌਜੂਦਾ ਬੈਕਗਮੋਮ ਦੀ ਮੌਜੂਦਾ ਖੇਡ ਜਿੱਤ ਗਠ+Comment[pl]=WygraÅ‚eÅ› bieżącÄ… grÄ™ backgammon +Comment[pt]=Ganhou o jogo de gamão +Comment[pt_BR]=Você ganhou o jogo atual de gamão +Comment[ro]=AÅ£i cîştigat jocul de table curent +Comment[ru]=Ð’Ñ‹ выиграли партию в нарды +Comment[se]=Don leat vuoitán dán backgammon-vuoru +Comment[sk]=Vyhrali ste aktuálnu hru v backgammone +Comment[sl]=Zmagali ste trenutno igro backgammona +Comment[sr]=Победили Ñте у овој игри бекгемона +Comment[sr@Latn]=Pobedili ste u ovoj igri bekgemona +Comment[sv]=Du har vunnit det aktuella spelet av backgammon +Comment[ta]=பேகà¯à®•à®¾à®®à¯à®®à¯‹à®©à®¾à®©à®¿à®©à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ விளையாடà¯à®Ÿà¯ˆ நீஙà¯à®•à®³à¯ வெனà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[tg]=Шумо дар нардбозии ҷорӣ ғолиб омадед +Comment[tr]=Åžu anki tavla oyununu kazandınız +Comment[uk]=Ви виграли поточну гру backgammon +Comment[ven]=No khunda kha mutambo wa backgammon +Comment[vi]=Bạn thắng trong trò chÆ¡i backgammon này +Comment[wa]=Vos avoz wangnî l' djeu d' backgammon +Comment[xh]=Uphumelele emdlalweni wangoku we backgammon +Comment[zh_CN]=您赢了这盘åŒé™†æ£‹æ¸¸æˆ +Comment[zh_TW]=您è´äº†é€™ä¸€ç›¤è¥¿æ´‹é›™é™¸æ£‹ +Comment[zu]=Uphumelele emdlalweni wamanje we-backgammon +default_sound=kbackgammon-won.wav +default_presentation=1 + +[game over l] +Name=Gamo over, you lost +Name[af]=Speletjie bo, jy verloor +Name[ar]=اللعبة انتهت، لقد خسرت +Name[az]=Oyun Qurtardı, siz uduzdunuz +Name[be]=Канец гульні, вы прайгралі +Name[bg]=Загубихте +Name[bn]=খেল খতম, আপনি হেরে গিয়েছেন +Name[br]=Echu eo an abadenn, kollet out +Name[bs]=Igra zavrÅ¡ena, izgubili ste +Name[ca]=Final del joc, heu perdut +Name[cs]=Konec hry, prohrál(a) jste +Name[cy]=Gêm drosodd, colli wnaethoch chi +Name[da]=Spillet forbi, du tabte +Name[de]=Spiel beendet, Sie haben verloren +Name[el]=Τέλος παιχνιδιοÏ, χάσατε +Name[en_GB]=Game over, you lost +Name[eo]=Ludo finita, vi malgajnis +Name[es]=Fin de la partida, usted perdió +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa amaitu da, galdu duzu +Name[fa]=بازی تمام شد، شما باختید +Name[fi]=Peli loppu, hävisit +Name[fr]=Fin de la partie, vous avez perdu +Name[gl]=Fin do xogo, vostede perde +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल ख़तà¥à¤®, आप हारे +Name[hr]=Igra je zavrÅ¡ena. Izgubili ste. +Name[hu]=Vége a játéknak, Ön vesztett +Name[is]=Leik lokið, þú tapaðir +Name[it]=Partita finita, hai perso +Name[ja]=ゲームオーãƒãƒ¼ã€ã‚ãªãŸã®è² ã‘ +Name[km]=ល្បែងចប់​, អ្នក​ចាញ់​ហើយ +Name[lt]=Žaidimas baigtas, jÅ«s pralaimÄ—jote +Name[lv]=SpÄ“les beigas, jÅ«s zaudÄ“jÄt +Name[mk]=Играта заврши, вие изгубивте +Name[mt]=Logħba spiÄ‹Ä‹at - int tlift +Name[nb]=Spillet er slutt, du tapte +Name[nds]=Speel vörbi, Du hest verloren +Name[ne]=खेल समापà¥à¤¤, तपाईठहारà¥à¤¨à¥ भयो +Name[nl]=Spel is afgelopen, u hebt verloren. +Name[nn]=Spelet er slutt, du tapte +Name[nso]=Papadi e fedile, o paletswe +Name[pa]=ਖੇਡ ਖਤਮ, ਤà©à¨¹à¨¾à¨¡à©€ ਵਾਰੀ ਖਤਮ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Fim do jogo, perdeu +Name[pt_BR]=Fim do jogo; você perdeu +Name[ro]=Joc terminat. Ai pierdut. +Name[ru]=Конец игры, вы проиграли +Name[se]=Speallu nogai, don vuoittehallet +Name[sk]=Koniec hry, prehrali ste +Name[sl]=Konec igre, izgubili ste +Name[sr]=Крај игре, изгубили Ñте +Name[sr@Latn]=Kraj igre, izgubili ste +Name[sv]=Spelet är slut, du förlorade +Name[ta]=விளையாடà¯à®Ÿà¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ தோறà¯à®±à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Name[tg]=Бозӣ ба итмом раÑид, шумо мағлуб шудед +Name[th]=จบเà¸à¸¡ - คุณà¹à¸žà¹‰ +Name[tr]=Oyun bitti, sen kaybettin +Name[uk]=Гру завершено, ви програли +Name[uz]=OÊ»yin tugadi, siz yutqazdingiz +Name[uz@cyrillic]=Ўйин тугади, Ñиз ютқаздингиз +Name[ven]=Mutambo wo fhela, no liwa +Name[vi]=Trò chÆ¡i kết thúc, bạn thua +Name[wa]=Li djeu est houte, vos avoz pierdou +Name[xh]=Umdlalo uphelile, wohluliwe +Name[zh_CN]=游æˆç»“æŸï¼Œæ‚¨è¾“了 +Name[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨è¼¸äº† +Name[zu]=Umdlalo uphellile, uhluliwe +Comment=You have lost the current game of backgammon +Comment[af]=Jy het verloor die huidige speletjie van backgammon +Comment[ar]=لقد خسرت اللعبة الحالية من لعبة النرد/الطاولة +Comment[az]=Hazırkı nÉ™rdtaxta oyununu uduzdunuz +Comment[be]=Ð’Ñ‹ прайгралі партыю Ñž нарды +Comment[bg]=Загубихте +Comment[bn]=à¦à¦‡ বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলাটি আপনি হেরেছেন +Comment[bs]=Izgubili ste u trenutnoj backgammon igri +Comment[ca]=Heu perdut aquesta partida de backgamon +Comment[cs]=Tuto hru ve vrchcáby jste prohráli +Comment[cy]=Rydych wedi colli y gêm gyfredol o dawlbwrdd +Comment[da]=Du har tabt dette spil backgammon +Comment[de]=Sie haben die Backgammon-Partie verloren! +Comment[el]=Χάσατε αυτή την παÏτίδα backgammon +Comment[eo]=Vi malgajnis la nunan bakgamonludon +Comment[es]=Usted ha perdido la partida actual de backgammon +Comment[et]=Sa kaotasid selle mängu +Comment[eu]=Uneko Backgammon jokoa galdu duzu +Comment[fa]=شما بازی جاری تخته نرد را باختید +Comment[fi]=Olet hävinnyt backgammon pelin +Comment[fr]=Vous avez perdu cette partie de backgammon +Comment[gl]=Vostede perdeu esta partida de backgammon +Comment[he]=הפסדת במשחק השש־בש הנוכחי +Comment[hi]=आप बैकगेमॉन का हालिया खेल हार गठ+Comment[hr]=Izgubili ste u ovoj partiji backgammona +Comment[hu]=Ön elvesztette ezt a backgammon játékot +Comment[is]=Þú tapaðir þessum Backgammon leik +Comment[it]=Hai perso questa partita di backgammon +Comment[ja]=ç¾åœ¨ã®backgammonゲームã«è² ã‘ã¾ã—㟠+Comment[km]=អ្នក​បានចាញ់​ល្បែង​បច្ចុប្បន្ន​នៃ backgammon +Comment[lt]=JÅ«s pralaimÄ—jote šį backgammon žaidimÄ… +Comment[lv]=JÅ«s zaudÄ“jÄt tekoÅ¡ajÄ bekgemona spÄ“lÄ“ +Comment[mk]=Ја изгубивте тековната игра на табла +Comment[mt]=Int tlift il-logħba preżenti tal-backgammon +Comment[nb]=Du tapte det gjeldende backgammon-spillet +Comment[nds]=Du hest den Backgammon-Törn verloren +Comment[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोनको हालको खेल तपाईà¤à¤²à¥‡ हारà¥à¤¨à¥ भयो +Comment[nl]=U hebt het huidige Backgammon-spel verloren. +Comment[nn]=Du har tapt denne backgammon-runden +Comment[nso]=O paletswe ke papadi ya bjale ya backgammon +Comment[pl]=PrzegraÅ‚eÅ› bieżącÄ… grÄ™ backgammon +Comment[pt]=Perdeu o jogo de gamão +Comment[pt_BR]=Infelizmente você perdeu o jogo atual de gamão +Comment[ro]=AÅ£i pierdut jocul de table curent +Comment[ru]=Ð’Ñ‹ проиграли партию в нарды +Comment[se]=Don leat vuoittehallan dán backgammon-vuoru +Comment[sk]=Prehrali ste aktuálnu hru v backgammone +Comment[sl]=Izgubili ste trenutno igro backgammona +Comment[sr]=Изгубили Ñте у овој игри бекгемона +Comment[sr@Latn]=Izgubili ste u ovoj igri bekgemona +Comment[sv]=Du har förlorat det aktuella spelet av backgammon +Comment[ta]=பேகà¯à®•à®¾à®®à¯à®®à¯‹à®©à®¾à®©à®¿à®©à¯ தறà¯à®ªà¯‹à®¤à¯ˆà®¯ விளையாடà¯à®Ÿà®¿à®²à¯ நீஙà¯à®•à®³à¯ தோலà¯à®µà®¿ அடைநà¯à®¤à¯à®³à¯à®³à¯€à®°à¯à®•à®³à¯. +Comment[tg]=Шумо дар нардбозии ҷорӣ мағлуб шудед +Comment[tr]=Åžu anki tavla oyununu kaybettiniz +Comment[uk]=Ви програли поточну гру backgammon +Comment[ven]=No kunda kha mutambo wa backgammon +Comment[vi]=Bạn thua trong trò chÆ¡i backgammon này +Comment[wa]=Vos avoz pierdou l' djeu d' backgammon +Comment[xh]=Wahlulekile emdlalweni wangoku we backgammon +Comment[zh_CN]=您输了这盘åŒé™†æ£‹æ¸¸æˆ +Comment[zh_TW]=您輸了這一盤西洋雙陸棋 +Comment[zu]=Uhluliwe emdlallweni wamanje we-backgammon +default_sound=kbackgammon-lost.wav +default_presentation=1 + +[roll or double] +Name=Roll or double +Name[af]=Rol of dubbel +Name[az]=At vÉ™ ya CütlÉ™ +Name[be]=Кінуць коÑці або падвоіць +Name[bg]=ХвърлÑне или удвоÑване +Name[bn]=গড়ান অথবা দà§à¦¬à¦¿à¦—à§à¦¨ করà§à¦¨ +Name[bs]=Ponovo ili duplo +Name[ca]=Tirar o doblar +Name[cs]=HoÄte nebo double +Name[cy]=Taflu neu dwbl +Name[da]=Kast eller fordobl +Name[de]=Würfeln oder verdoppeln +Name[el]=Ρίξτε το ζάÏι ή διπλασιάστε +Name[eo]=Rulu aÅ­ duobligu +Name[es]=Tirar o doblar +Name[et]=Veereta või duubelda +Name[eu]=Jaurti edo bikoiztu +Name[fa]=غلتاندن یا دو برابر کردن +Name[fi]=Heitä tai tuplaa +Name[fr]=Jeter les dés ou doubler +Name[gl]=Botar ou dobrar +Name[he]=הטלה ×ו הכפלה +Name[hi]=पाà¤à¤¸à¤¾ फेंकें या दोगà¥à¤¨à¤¾ करें +Name[hr]=Bacaj ili dvostruko +Name[hu]=Dobás vagy duplázás +Name[is]=Kastaðu eða tvöfaldaðu +Name[it]=Lancia o raddoppia +Name[ja]=振るã‹ãƒ€ãƒ–ル +Name[km]=ក្រឡុក ឬ ទ្វ០+Name[lt]=Ridenti ar dvigubinti +Name[lv]=Mest vai dubultot +Name[mk]=Фрлете или удвојте +Name[mt]=Waddab damem jew Irdoppja +Name[nb]=Kast eller doble +Name[nds]=Wörpeln oder verdubbeln +Name[ne]=घà¥à¤®à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ वा दोबà¥à¤¬à¤° पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Werpen of verdubbelen +Name[nn]=Rull eller dobla +Name[nso]=Tokolosa goba gabedi +Name[pa]=ਰੋਲ ਜਾਂ ਦà©à¨¹à¨°à¨¾ +Name[pl]=Rzucaj lub podwój +Name[pt]=Lançar ou duplicar +Name[pt_BR]=Jogar ou dobrar +Name[ro]=Aruncă sau dublează +Name[ru]=БроÑить коÑти или удвоить +Name[se]=ÄŒaskke dahje duppalastte +Name[sk]=HodiÅ¥ alebo zdvojiÅ¥ +Name[sl]=MeÄi ali podvoji +Name[sr]=Баците коцкице или удвоÑтручите +Name[sr@Latn]=Bacite kockice ili udvostruÄite +Name[sv]=SlÃ¥ eller dubblera +Name[ta]=சà¯à®±à¯à®±à¯ அலà¯à®²à®¤à¯ இரடà¯à®Ÿà®¿à®ªà¯à®ªà®¾à®•à¯à®•à¯ +Name[tg]=Партофтани мӯҳр Ñ‘ дубора партофтан +Name[th]=ทอดหรือได้ทอดใหม่ +Name[tr]=At ya da çift +Name[uk]=Кинути коÑÑ‚Ñ– або подвоїти +Name[ven]=Kungulusani kha ni kou pada +Name[vi]=Cuá»™n hay gấp +Name[xh]=Jikeleza okanye phindaphinda +Name[zh_CN]=掷骰å­æˆ–åŠ å€ +Name[zh_TW]=擲骰å­æˆ–åŠ å€ +Name[zu]=Gingqa noma phinda kabili +Comment=It's your turn to roll the dice or double the cube +Comment[af]=Dit is jou skakel na rol die dobbelsteen of dubbel die kubus +Comment[az]=ZÉ™rlÉ™ri atmaqda ya da kubları cütlÉ™mÉ™kdÉ™ sizin sıranız gÉ™ldi +Comment[be]=Ваша чарга кідаць коÑці або падвойваць +Comment[bg]=ХвърлÑне или удвоÑване +Comment[bn]=à¦à¦–ন আপনার পাশা গড়ানোর বা কিউব দà§à¦¬à¦¿à¦—à§à¦¨ করার পালা +Comment[bs]=Na vas je red da bacite kocke ili poduplate cube +Comment[ca]=És el vostre torn per a tirar els daus o doblar el cub +Comment[cs]=Jste na tahu, buÄ hoÄte kostkou nebo double +Comment[cy]=Eich tro chi yw hi i daflu'r dîs neu ddwblu'r ciwb +Comment[da]=Det er din tur til at kaste terningerne eller fordoble kuben +Comment[de]=Sie sind dran, entweder zu würfeln oder zu verdoppeln +Comment[el]=Είναι η σειÏά σας να Ïίξετε τα ζάÏια +Comment[eo]=Estas via vico ĵeti la du kubojn aÅ­ duobligi la kubon +Comment[es]=Es su turno para tirar los dados o doblar el cubo +Comment[et]=Sinu kord veeretada täringut või kahekordistada panust +Comment[eu]=Kuboa jaurti edo bikoizteko zure txanda da +Comment[fa]=نوبت شماست Ú©Ù‡ طاس را بغلتانید، یا مکعب را دو برابر کنید +Comment[fi]=On sinun vuoro heittää noppaa tai tuplata +Comment[fr]=C'est à votre tour de jeter les dés ou de doubler le videau. +Comment[gl]=É a súa quenda de botar o dado ou dobrar o cubo +Comment[he]=תורך להטיל ×ת הקוביות ×ו להכפיל ×ת הקובייה +Comment[hi]=पाà¤à¤¸à¤¾ फेंकने की या कà¥à¤¯à¥‚ब को दोगà¥à¤¨à¤¾ करने की यह आपकी बारी है +Comment[hr]=Vi ste na redu da bacite kocku ili je udvostruÄite +Comment[hu]=Dobjon a kockával vagy duplázzon +Comment[is]=Þú átt leik. Kastaðu teningnum eða tvöfaldaðu +Comment[it]=È il tuo turno di lanciare i dadi o di raddoppiare il cubo +Comment[ja]=ã‚ãªãŸãŒã•ã„ã“ã‚を振るã‹ãƒ€ãƒ–ルã™ã‚‹ç•ªã§ã™ +Comment[km]=វា​គឺ​ជា​វáŸáž“​របស់​អ្នក ដើម្បី​ក្រឡុក​គ្រាប់​ឡុកឡាក់ ឬ ដើម្បី​ទ្វáŸâ€‹áž‚ូប +Comment[lt]=JÅ«sų eilÄ— ridenti kauliukÄ… ar dvigubinti kubÄ… +Comment[lv]=Å is ir jÅ«su gÄjiens, lai mestu kauliņu vai dubultotu +Comment[mk]=Вие Ñте на ред да ги фрлите коцките или да ја удвоите коцката +Comment[mt]=Imiss lilek twaddab id-damem jew tirdoppja l-kubu +Comment[nb]=Det er din tur til Ã¥ kaste terningene eller doble kuben +Comment[nds]=Du büst mit Wörpeln oder Verdubbeln an de Reeg +Comment[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥‡ वा घन दोबà¥à¤¬à¤° पारà¥à¤¨à¥‡ तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt om de dobbelsteen te werpen of te verdubbelen. +Comment[nn]=Det er din tur til Ã¥ rulla terningen eller dobla kuben +Comment[nso]=Ke nako ya gago yago tokolosa mataese goba wa double cube +Comment[pl]=Twój ruch, by rzucić kostkÄ… lub podwoić szeÅ›cian +Comment[pt]=É a sua vez de lançar os dados ou duplicar o cubo +Comment[pt_BR]=É a sua vez de jogar os dados ou dobrar o cubo +Comment[ro]=Este rîndul dumneavoastră să aruncaÅ£i zarurile sau să dublaÅ£i cubul +Comment[ru]=Ваша очередь броÑать коÑти или удваивать +Comment[se]=Lea du vuorru birccu Äaskit dahje duppalastit kuba +Comment[sk]=Ste na Å¥ahu, buÄ hoÄte kockou alebo zdvojte +Comment[sl]=Na vrsti ste za met kock ali podvojitev vrednosti +Comment[sr]=Ваш је ред да баците коцкице или да дуплирате коцку +Comment[sr@Latn]=VaÅ¡ je red da bacite kockice ili da duplirate kocku +Comment[sv]=Det är din tur att slÃ¥ tärningen eller dubblera kuben +Comment[ta]=நீஙà¯à®•à®³à¯ தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®®à¯ அலà¯à®²à®¤à¯ படà¯à®Ÿà®•à®¤à¯à®¤à¯ˆ இரடà¯à®Ÿà®¿à®•à¯à®•à¯à®®à¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ðавбати шумо барои партофтани мӯҳр Ñ‘ партофтани дубора +Comment[tr]=Zar atma sırası sizde +Comment[uk]=Ваша черга кидати коÑÑ‚Ñ– +Comment[ven]=Ndi tshifhinga tshavho tsha u kungulusa dice kana pada cube +Comment[vi]=Äến lượt bạn gieo xúc sắc hay double cube +Comment[xh]=Lithuba lakho lokujikelezisa idayisi okanye phinda kabini ityhubhu +Comment[zh_CN]=该您掷骰å­æˆ–åŠ å€ +Comment[zh_TW]=該您擲骰å­æˆ–åŠ å€ +Comment[zu]=Ithuba lakho lokugingqa idayisi noma uphinde kabili iqhuzu +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[roll] +Name=Roll the dice +Name[af]=Rol die dobbelsteen +Name[ar]=ارمي النرد +Name[az]=ZÉ™rlÉ™ri at +Name[be]=Кінуць коÑці +Name[bg]=ХвърлÑне на заровете +Name[bn]=পাশা গড়ান +Name[bs]=Baci kocke +Name[ca]=Tirar els daus +Name[cs]=Hodit kostkou +Name[cy]=Taflwch y dîs +Name[da]=Kast terningerne +Name[de]=Würfeln +Name[el]=Ρίξτε το ζάÏι +Name[eo]=Rulu aÅ­ ĵetu kubojn +Name[es]=Tirar los dados +Name[et]=Veereta täringut +Name[eu]=Jaurti dadoa +Name[fa]=غلتاندن طاس +Name[fi]=Heitä noppaa +Name[fr]=Lancer les dés +Name[gl]=Botar o dado +Name[he]=הטלת הקוביות +Name[hi]=पाà¤à¤¸à¤¾ फेंकें +Name[hr]=Baci kocke +Name[hu]=Dobás a kockával +Name[is]=Kastaðu teningnum +Name[it]=Lancia i dadi +Name[ja]=サイコロを振ã£ã¦ãã ã•ã„ +Name[km]=ក្រឡុក​គ្រាប់​ឡុកឡាក់ +Name[lt]=Ridenti kauliukÄ… +Name[lv]=Mest kauliņu +Name[mk]=Фрлете ги коцките +Name[mt]=Waddab id-damem +Name[nb]=Kast terningene +Name[nds]=Wörpeln +Name[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Dobbelsteen werpen +Name[nn]=Rull terningen +Name[nso]=Tokolosa mataese +Name[pa]=ਗੋਟੀ ਘà©à©°à¨®à¨¾à¨“ +Name[pl]=Rzuć kostkÄ… +Name[pt]=Lançar os dados +Name[pt_BR]=Jogar os dados +Name[ro]=Aruncă zarurile +Name[ru]=БроÑить коÑти +Name[se]=ÄŒaskke birccu +Name[sk]=HodiÅ¥ kocku +Name[sl]=Meci kocke +Name[sr]=Баците коцкице +Name[sr@Latn]=Bacite kockice +Name[sv]=Kasta tärningen +Name[ta]=தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®• +Name[tg]=Партофтани мӯҳр +Name[th]=ทอดเต๋า +Name[tr]=Zarı at +Name[uk]=Кинути коÑÑ‚Ñ– +Name[uz]=Toshni otish +Name[uz@cyrillic]=Тошни отиш +Name[ven]=Kungulusani dice +Name[vi]=Cuá»™n hay xắc +Name[wa]=Taper les dés +Name[xh]= Jikelezisa idayisi +Name[zh_CN]=æŽ·éª°å­ +Name[zh_TW]=æ“²éª°å­ +Name[zu]=Gingqa idayisi +Comment=It's your turn to roll the dice +Comment[af]=Dit is jou skakel na rol die dobbelsteen +Comment[ar]=لقد حان دورك لرمي النرد +Comment[az]=ZÉ™rlÉ™ri atmaqda sizin sıranız gÉ™ldi +Comment[be]=Ваша чарга кідаць коÑці +Comment[bg]=ХвърлÑне на заровете +Comment[bn]=à¦à¦–ন আপনার পাশা গড়ানোর পালা +Comment[bs]=Na vas je red da bacate kocke +Comment[ca]=És el vostre torn per a tirar els daus +Comment[cs]=Jste na tahu, hoÄte kostkou +Comment[cy]=Eich tro chi yw hi i daflu'r dîs +Comment[da]=Det er din tur til at kaste terningerne +Comment[de]=Sie sind dran mit Würfeln! +Comment[el]=Είναι η σειÏά σας να Ïίξετε τα ζάÏια +Comment[eo]=Estas via vico ĵeti la kubojn +Comment[es]=Es su turno para tirar los dados +Comment[et]=Sinu kord täringut veeretada +Comment[eu]=Dadoa jaurtizeko zure txanda da +Comment[fa]=نوبت شماست Ú©Ù‡ طاس را بغلتانید +Comment[fi]=Sinun vuoro heittää noppaa +Comment[fr]=C'est à votre tour de jeter les dés +Comment[gl]=É a súa quenda de botar o dado +Comment[he]=תורך להטיל ×ת הקוביות +Comment[hi]=पाà¤à¤¸à¤¾ फेंकने की यह आपकी बारी है +Comment[hr]=Vi ste na redu da bacite kocku +Comment[hu]=Dobjon a kockával +Comment[is]=Þú átt að kasta +Comment[it]=È il tuo turno di lanciare i dadi +Comment[ja]=ã‚ãªãŸãŒã‚µã‚¤ã‚³ãƒ­ã‚’振る番ã§ã™ +Comment[km]=វា​ជា​វáŸáž“​របស់​អ្នក​ដើម្បី​ប្រមៀលឡុក​គ្រាប់​ឡុក​ឡាក់ +Comment[lt]=JÅ«sų eilÄ— ridenti kauliukÄ… +Comment[lv]=Å is ir jÅ«su gÄjiens mest kauliņu +Comment[mk]=Вие Ñте на ред да ги фрлите коцките +Comment[mt]=Imiss lilek twaddab id-damem +Comment[nb]=Det er din tur til Ã¥ kaste terningene +Comment[nds]=Du büst mit Wörpeln an de Reeg +Comment[ne]=पासा घà¥à¤®à¤¾à¤‰à¤¨à¥‡ तपाईà¤à¤•à¥‹ पालो हो +Comment[nl]=U bent aan de beurt om de dobbelsteen te werpen. +Comment[nn]=Det er din tur til Ã¥ rulla terningen +Comment[nso]=Ke nako ya gago yago tokolosa mataese +Comment[pa]=ਗੋਟੀ ਘà©à©°à¨®à¨¾à¨‰à¨£ ਦੀ ਵਾਰੀ ਤà©à¨¹à¨¾à¨¡à©€ ਹੈ +Comment[pl]=Twój ruch do rzutu kostkÄ… +Comment[pt]=É a sua vez de lançar os dados +Comment[pt_BR]=É a sua vez de jogar os dados +Comment[ro]=Este rîndul dumneavoastră să aruncaÅ£i zarurile +Comment[ru]=Ваша очередь броÑать коÑти +Comment[se]=Lea du vuorru birccu Äaskit +Comment[sk]=Ste na Å¥ahu, hoÄte kockou +Comment[sl]=Na vrsti ste za met kock +Comment[sr]=Ваш је ред да баците коцкице +Comment[sr@Latn]=VaÅ¡ je red da bacite kockice +Comment[sv]=Det är din tur att kasta tärningen +Comment[ta]=நீஙà¯à®•à®³à¯ இபà¯à®ªà¯‹à®¤à¯ தாயதà¯à®¤à¯ˆ உரà¯à®Ÿà¯à®Ÿà¯à®®à¯ à®®à¯à®±à¯ˆ +Comment[tg]=Ðавбати шумо барои партофтани мӯҳрҳо +Comment[tr]=Zar atma sırası sizde +Comment[uk]=Ваша черга кидати коÑÑ‚Ñ– +Comment[ven]=Ndi tshifhinga tshanu tsha u tamba daisi +Comment[vi]=Äến lượt bạn reo xúc sắc +Comment[wa]=C' est a vos d' taper les dés asteure +Comment[zh_CN]=该您掷骰å­äº† +Comment[zh_TW]=該您擲骰å­äº† +Comment[zu]=Ithuba lakho lokugingqa idayisi +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[move] +Name=Move checkers +Name[af]=Beweeg skuifstukke +Name[az]=DaÅŸları HÉ™rÉ™kÉ™t etdir +Name[be]=ПераÑунуць фішку +Name[bg]=ПремеÑтване на пулове +Name[bn]=চেকারà§à¦¸ চালà§à¦¨ +Name[ca]=Moure fitxes +Name[cs]=PÅ™esunout kameny +Name[cy]=Symud drafftiau +Name[da]=Flyt brikkerne +Name[de]=Steine ziehen +Name[el]=Μετακινήστε ποÏλια +Name[eo]=Movu pecojn +Name[es]=Mover fichas +Name[et]=Liiguta nuppe +Name[eu]=Mugitu fitxak +Name[fa]=حرکت بازبینها +Name[fi]=Siirrä tammea +Name[fr]=Déplacer des pions +Name[gl]=Mover fichas +Name[he]=הזזת ×—×œ×§×™× +Name[hi]=चेकरà¥à¤¸ खिसकाà¤à¤ +Name[hr]=Pomakni figure +Name[hu]=Lépés +Name[is]=Færðu +Name[it]=Sposta le pedine +Name[ja]=ãƒã‚§ãƒƒã‚«ãƒ¼ã®ç§»å‹• +Name[km]=ផ្លាស់​ទី​អ្នក​ពិនិážáŸ’áž™ +Name[lt]=Eiti Å¡aÅ¡kÄ—mis +Name[lv]=PÄrvietot kauliņus +Name[mk]=ПремеÑтете ги пуловите +Name[mt]=Mexxi checkers +Name[nb]=Flytt brikker +Name[nds]=Steen trecken +Name[ne]=चाल परिकà¥à¤·à¤• +Name[nl]=Stukken verplaatsen +Name[nn]=Flytt brikker +Name[nso]=Sutisa checkers +Name[pl]=Rusz pionkiem +Name[pt]=Mover as peças +Name[pt_BR]=Mover peças +Name[ro]=MutaÅ£i piesele +Name[ru]=Передвинуть фишку +Name[se]=Sirdde bihtáid +Name[sk]=Presunúť kamene +Name[sl]=Premakni figure +Name[sr]=Померите чекере +Name[sr@Latn]=Pomerite Äekere +Name[sv]=Flytta brickor +Name[ta]=கடà¯à®Ÿà®®à¯ கடà¯à®Ÿà®®à®¾à®• நகறà¯à®±à¯à®• +Name[tg]=Ҷойивазкунии домнаҳо +Name[th]=ย้ายตัวหมาภ+Name[tr]=Pulu oynat +Name[uk]=ПереÑунути шашки +Name[ven]=Tshimbidzani checkers +Name[vi]=Di chuyển checkers +Name[xh]= Hambisa icheckers +Name[zh_CN]=ç§»åŠ¨æ£‹å­ +Name[zh_TW]=ç§»å‹•æ£‹å­ +Name[zu]=Nyakazisa i-checkers +Comment=The dice have been rolled and it's your turn to move checkers +Comment[af]=Die dobbelsteen het al gerol en dit is jou skakel na beweeg skuifstukke +Comment[az]=ZÉ™ri atdınız vÉ™ daÅŸları oynatmaq vaxtı gÉ™ldi +Comment[be]=Ð’Ñ‹ ўжо кінулі коÑці, Ñ– зараз ваш ход +Comment[bg]=ПремеÑтване на пулове +Comment[bn]=পাশা গড়ানো হয়েছে à¦à¦¬à¦‚ à¦à¦–ন আপনার চেকারà§à¦¸ চালার পালা +Comment[ca]=S'han llançat els daus i és el vostre torn per a moure les fitxes +Comment[cs]=Kostka byla hozená a teÄ máte pÅ™esunout kameny +Comment[cy]=Mae'r dîs wedi eu taflu ac eich tro chi yw hi i symud y drafftiau +Comment[da]=Terningerne er kastet og det er din tur til at flytte brikkerne +Comment[de]=Die Würfel sind gefallen, Sie müssen ziehen. +Comment[el]=Τα ζάÏια Ïίχτηκαν και είναι η σειÏά σας να μετακινήσετε τα ποÏλια +Comment[eo]=La kuboj estas ĵetitaj kaj estas via vico movi la pecojn +Comment[es]=Se han lanzado los dados y es su turno para mover fichas +Comment[et]=Täring sai visatud, liiguta nüüd nuppe +Comment[eu]=Dadoak jaurti dira eta fitxak mugitzeko zure txanda da +Comment[fa]=طاس غلتانیده شد، Ùˆ نوبت شماست Ú©Ù‡ بازبینها را حرکت بدهید +Comment[fi]=Noppaa on heitetty ja on sinun vuoro siirtää tammea +Comment[fr]=Les dés ont été jetés et c'est à votre tour de déplacer des pions +Comment[gl]=O dado xa rodou e é a súa quenda de mover as fichas +Comment[he]=הקוביות הוטלו וכעת תורך להזיז ×ת ×בני המשחק +Comment[hi]=पाà¤à¤¸à¤¾ फेंक दिया गया है और यह चेकरà¥à¤¸ को चलने की आपकी बारी है +Comment[hr]=Kocke su baÄene i vaÅ¡ je red da pomaknete figure +Comment[hu]=A dobás megtörtént, most Ön lép +Comment[is]=Það er búið að kasta og þú átt að færa +Comment[it]=I dadi sono stati lanciati e tocca a te muovere le pedine +Comment[ja]=サイã¯æŠ•ã’られã¾ã—ãŸã€ãƒã‚§ãƒƒã‚«ãƒ¼ã‚’å‹•ã‹ã™ç•ªã§ã™ +Comment[km]=គ្រាប់​ឡុក​ឡាក់​ážáŸ’រូវ​បាន​ប្រមៀល ហើយ​វា​ជា​វáŸáž“​របស់​អ្នក​ដើម្បី​ផ្លាស់​ទីអ្នក​ពិនិážáŸ’áž™ +Comment[lt]=Kauliukas nuridentas ir dabar jÅ«sų eilÄ— stumti Å¡aÅ¡kes +Comment[lv]=Kauliņi ir mesti un ir jÅ«su kÄrta izdarÄ«t gÄjienu +Comment[mk]=Коцките Ñе фрлени и вие Ñте на ред да ги премеÑтите пуловите +Comment[mt]=Id-damem twaddbu u jmiss lilek tmexxi Ä‹-checkers +Comment[nb]=Terningene er kastet og det er din tur til Ã¥ flytte +Comment[nds]=Du hest wörpelt un muttst nu trecken +Comment[ne]=पासा घà¥à¤®à¤¾à¤‡à¤à¤•à¥‹ छ र परीकà¥à¤·à¤• सारà¥à¤¨à¥‡ अब तपाईà¤à¤•à¥‹ पालो छ । +Comment[nl]=De dobbelsteen is geworpen, en u bent aan de beurt om de stukken te verplaatsen. +Comment[nn]=Terningen er rulla, og det er din tur til Ã¥ flytta brikker +Comment[nso]=Mataese a tokolositswe gomme ke nako ya gago yago sutisa checkers +Comment[pl]=Kość zostaÅ‚a rzucona, Twój ruch do ruchu pionkiem +Comment[pt]=Os dados foram lançados e é a sua vez de mexer as peças +Comment[pt_BR]=Os dados foram jogados e é a sua vez de mover as peças +Comment[ro]=Zarurile au fost aruncate ÅŸi e rîndul dumneavoastră să mutaÅ£i piesele +Comment[ru]=Ð’Ñ‹ уже броÑили коÑти, и теперь ваша очередь ходить +Comment[se]=Bircu lea Äaskon, ja dál lea du vuorru bihtáid sirdit +Comment[sk]=Kocka bola hodená a teraz máte presunúť kamene +Comment[sl]=Kocke so vržene in zdaj ste na vrsti za premik figur +Comment[sr]=Коцкица је бачена и ваш је ред да померите чекере +Comment[sr@Latn]=Kockica je baÄena i vaÅ¡ je red da pomerite Äekere +Comment[sv]=Tärningen har slagits och det är din tur att flytta brickor +Comment[ta]=தாயம௠உரà¯à®Ÿà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.இபà¯à®ªà¯‹à®¤à¯ நீஙà¯à®•à®³à¯ கடà¯à®Ÿà®¤à¯à®¤à¯ˆ நகரà¯à®¤à¯à®¤à®²à®¾à®®à¯. +Comment[tg]=Мӯҳрҳо партофта шудаанд ва ҳоло навбати шумо барои ҷойивазкунии дамнаҳо +Comment[tr]=Zar atıldı ve ÅŸimdi pulları oynatma sırası sizde +Comment[uk]=КоÑÑ‚Ñ– кинуто; ваша черга переÑунути шашки +Comment[ven]=Dice lo kunguluswa zwino ndi tshifhinga tshavho tsha u tshimbidza checkers +Comment[vi]=xúc sắc đã được gieo và đến lượt bạn di chuyển checkers +Comment[xh]=Amadayisi aqengqiwe lithuba lakho lukuhambisa iicheckersi +Comment[zh_CN]=å·²ç»æŽ·äº†éª°å­ï¼Œè¯¥æ‚¨ç§»åŠ¨äº† +Comment[zh_TW]=已經擲了骰å­ï¼Œè©²æ‚¨ç§»å‹•äº† +Comment[zu]=Idayisi ligingqiwe, ithuba lakho lokunyakazisa i-checkers +default_sound=kbackgammon-roll.wav +default_presentation=1 + +[invitation] +Name=Game invitation +Name[af]=Speletjie uitnodiging +Name[ar]=دعوة إلى لعبة +Name[az]=Oyuna DÉ™vÉ™t +Name[be]=ЗапрашÑнне да гульні +Name[bg]=Покана за игра +Name[bn]=খেলার আমনà§à¦¤à§à¦°à¦£ +Name[bs]=Poziv u igru +Name[ca]=Invitació a una partida +Name[cs]=Výzva ke hÅ™e +Name[cy]=Gwahoddiad gêm +Name[da]=Spilinvitation +Name[de]=Einladung zum Spiel +Name[el]=ΠÏόσκληση για παιχνίδι +Name[eo]=Ludinvito +Name[es]=Invitación al juego +Name[et]=Mängu tervitus +Name[eu]=Jokora gonbidapena +Name[fa]=دعوت به بازی +Name[fi]=Pelikutsu +Name[fr]=Invitation à une partie +Name[gl]=Invitación ao xogo +Name[he]=הזמנה למשחק +Name[hi]=खेल निमंतà¥à¤°à¤£ +Name[hr]=Poziv za igru +Name[hu]=Játék kezdeményezése (meghívás) +Name[is]=Býð þér í nýjan leik +Name[it]=Invito a giocare +Name[ja]=ゲームã«æ‹›å¾… +Name[km]=សំបុážáŸ’រ​អញ្ជើញ​ល្បែង +Name[lt]=Kvietimas į žaidimÄ… +Name[lv]=IelÅ«gums uz spÄ“li +Name[mk]=Покана за игра +Name[mt]=Stedina għal logħba +Name[nb]=Spillinvitasjon +Name[nds]=Speelinladen +Name[ne]=खेल निमनà¥à¤¤à¥à¤°à¤£à¤¾ +Name[nl]=Speluitnodiging +Name[nn]=Spelinvitasjon +Name[nso]=Memo ya papadi +Name[pa]=ਖੇਡ ਸੱਦਾ +Name[pl]=Zaproszenie do gry +Name[pt]=Convite para jogo +Name[pt_BR]=Convite para jogar +Name[ro]=InvitaÅ£ie joc +Name[ru]=Приглашение в игру +Name[se]=Speallanbovdehus +Name[sk]=Výzva na hru +Name[sl]=Povabilo k igri +Name[sr]=Позив у игру +Name[sr@Latn]=Poziv u igru +Name[sv]=Spelinbjudan +Name[ta]= விளையாடà¯à®Ÿà¯ அழைபà¯à®ªà®¿à®¤à®´à¯ +Name[tg]=Ташрифот ба бозӣ +Name[th]=เชิà¸à¹ƒà¸«à¹‰à¹€à¸¥à¹ˆà¸™à¹€à¸à¸¡à¸”้วย +Name[tr]=Oyuna davet +Name[uk]=Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð´Ð¾ гри +Name[uz]=OÊ»yinga taklif qilish +Name[uz@cyrillic]=Ўйинга таклиф қилиш +Name[ven]=U rambiwa ha mutambo +Name[vi]=Má»i chÆ¡i game +Name[xh]=Isimemo somdlalo +Name[zh_CN]=游æˆé‚€è¯· +Name[zh_TW]=邀請別人加入éŠæˆ² +Name[zu]=Isimemo somdlalo +Comment=Somebody has invited you to a match +Comment[af]=Iemand het uitgenooi jy na 'n ooreenstem +Comment[ar]=لقد دعاك أحد لمباراة +Comment[az]=Biri sizi oyuna dÉ™vÉ™t etdi +Comment[be]=Ð’Ð°Ñ Ð·Ð°Ð¿Ñ€Ð°ÑˆÐ°ÑŽÑ†ÑŒ паўдзельнічаць у гульні +Comment[bg]=Покана за игра +Comment[bn]=কেউ à¦à¦•à¦œà¦¨ আপনাকে à¦à¦•à¦Ÿà¦¿ মà§à¦¯à¦¾à¦šà§‡ আমনà§à¦¤à§à¦°à¦£ জানিয়েছে +Comment[bs]=Neko vas je pozvao u meÄ +Comment[ca]=Algú us ha invitat a una partida +Comment[cs]=NÄ›kdo vás vyzval na zápas +Comment[cy]=Mae rhywun wedi eich gwahodd i gêm +Comment[da]=Der er nogen som har inviteret dig til et spil +Comment[de]=Jemand hat Sie zu einer Partie eingeladen. +Comment[el]=Κάποιος σας κάλεσε για ένα παιχνίδι +Comment[eo]=Iu invitis vin al ludo +Comment[es]=Alguien le ha invitado a una partida +Comment[et]=Keegi kutsus sind duellile +Comment[eu]=Norbaitek joko batera gonbidatu zaitu +Comment[fa]=شخصی شما را به یک مسابقه دعوت کرده است +Comment[fi]=Joku on kutsunut sinut otteluun +Comment[fr]=Quelqu'un vient de vous proposer une partie +Comment[gl]=Alguén convidouno a botar unha partida +Comment[he]=מישהו הזמין ×ותך למשחק +Comment[hi]=किसी ने आपको पà¥à¤°à¤¤à¤¿à¤¯à¥‹à¤—िता के लिठनà¥à¤¯à¥Œà¤¤à¤¾ दिया है +Comment[hr]=Netko vas je pozvao na igru +Comment[hu]=Valaki kihívta Önt egy játszmára +Comment[is]=Það bauð þér einhver í leik +Comment[it]=Qualcuno ti ha invitato ad una partita +Comment[ja]=誰ã‹ãŒã‚ãªãŸã‚’マッãƒã«æ‹›å¾…ã—ã¾ã—㟠+Comment[km]=មាន​មនុស្ស​អញ្ជើញ​អ្នក​ទៅ​កាន់​ការ​ប្រកួហ+Comment[lt]=Kažkas pakvietÄ— jus maÄui +Comment[lv]=KÄds jÅ«s ir uzaicinÄjis uz spÄ“li +Comment[mk]=Ðекој ве покани на натпревар +Comment[mt]=Xi ħadd stiednek għal logħba +Comment[nb]=Noen har invitert deg til et spill +Comment[nds]=Een hett Di to en Törn inlaadt +Comment[ne]=कसैले तपाईà¤à¤²à¤¾à¤ˆ खेलका लागि निमनà¥à¤¤à¥à¤°à¤£à¤¾ गरेकोछ । +Comment[nl]=Iemand heeft u voor een spel uitgenodigd. +Comment[nn]=Nokon har invitert deg til ein runde +Comment[nso]=Motho o mongwe ogo laleditse papading +Comment[pa]=ਤà©à¨¹à¨¾à¨¨à©‚à©° ਕਿਸੇ ਨੇ ਮੈਚ ਖੇਡਣ ਲਈ ਸੱਦਿਆ ਹੈ +Comment[pl]=KtoÅ› zaproponowaÅ‚ pojedynek +Comment[pt]=Alguém o convidou para um jogo +Comment[pt_BR]=Alguém o convidou para uma partida +Comment[ro]=Cineva va invitat la o partidă +Comment[ru]=Ð’Ð°Ñ Ð¿Ñ€Ð¸Ð³Ð»Ð°ÑˆÐ°ÑŽÑ‚ начать игру +Comment[se]=Giinu lea bovden du speallat +Comment[sk]=Niekto vás vyzval na zápas +Comment[sl]=Nekdo vas je povabil k igri +Comment[sr]=Ðеко Ð²Ð°Ñ Ñ˜Ðµ позвао у игру +Comment[sr@Latn]=Neko vas je pozvao u igru +Comment[sv]=NÃ¥gon har bjudit in dig till en match +Comment[ta]=எவரோ à®’à®°à¯à®µà®°à¯ உஙà¯à®•à®³à¯ˆ ஆடà¯à®Ÿà®¤à¯à®¤à®¿à®±à¯à®•à¯ அழைகà¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=КаÑе шуморо ба муÑобиқа даъват мекунад +Comment[tr]=Birileri sizi maça davet etti +Comment[uk]=ХтоÑÑŒ запрошує Ð²Ð°Ñ Ð½Ð° матч +Comment[uz]=Kimdir sizni oÊ»yinga taklif qildi +Comment[uz@cyrillic]=Кимдир Ñизни ўйинга таклиф қилди +Comment[ven]=Munwe muthu o ni ramba uri ni tambe naye +Comment[vi]=Có ngÆ°á»i má»i bạn chÆ¡i +Comment[xh]=Ukhona umntu okumemele emdlalweni +Comment[zh_CN]=æœ‰äººé‚€è¯·æ‚¨è¿›è¡Œæ¸¸æˆ +Comment[zh_TW]=有人邀請您進行éŠæˆ² +Comment[zu]=Kukhona okumemele emdlalweni +default_sound=kbackgammon-move.wav +default_presentation=1 + diff --git a/kbackgammon/icons/Makefile.am b/kbackgammon/icons/Makefile.am new file mode 100644 index 00000000..1980b970 --- /dev/null +++ b/kbackgammon/icons/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON = kbackgammon kbackgammon_engine + diff --git a/kbackgammon/icons/hi128-app-kbackgammon.png b/kbackgammon/icons/hi128-app-kbackgammon.png new file mode 100644 index 0000000000000000000000000000000000000000..794c94e0002f12c413ef955df3fa15e6ae115f2e GIT binary patch literal 7621 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 TR`@{000000NkvXXu0mjfcb0-t literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi16-app-kbackgammon_engine.png b/kbackgammon/icons/hi16-app-kbackgammon_engine.png new file mode 100644 index 0000000000000000000000000000000000000000..bd2ba6a1e87bd4f5cdd3d805c127ab7971eb1d9b GIT binary patch literal 1131 zcmV-x1eE)UP)KLZ*U+ zMZqD61UKR0ATHv^)-h>I5gck1NwPJ`rHVGW++068`4T>agNvh}Z%}a%oO}c~K^z<$ zB4lt7!84uz8UFmBvEp_7ZetEcxivx0mthpknMbB+0>B_Gwb$=&1LQ@n_`&-#0^n)Y z>-b&%#%!vM1wMhzOqD5!bs#&l+Jd+Q+~i1G5LbX}_JTHuJHQ!MJ^`Gh@)y7{VHAUS z1>9j6#n1q7lgeGdwd&jgvH7ey+o)$(IW?)qYRQsyk zp4P`o20fquC5&QO+1m~R5DSyK)ubop^jn2^`2DVbjPJnEQ_@xV|$`ith~00004XF*Lt007wQ^&Awc0000WV@Og>004R=004l4008tQ004y1 z003=Y008K0002CT000|UgXaf$0007}NklKbZ{Mq^TJY^+MF)rMdzkjv$2tJOC9u(o-i zyLZ_Cvb}elq8#q*?A(&gcmx0x7G6G=l48o1tHSaX3MFKcZ2$n7Op2iomUnaB&;6yP z7b^iQtry?{07X#&I(>4!kS_u_0^{TK8pAAbZudNNQAx=jFJl6*{5J=D070h;E}fT1 zgJJ>{N<3|D9Jx_`A$@Y%DK({;<7s*devaq&xMqeJ#x?PW1Ck^oN#%~bZ{In919tBU z{Q_W29~JJDO2m-KCE&mhV_7zF!$TMh30I#q+qjO`6i1CRoWsE|V`JX0eSI|7_KM<; zpSaSs5tj!561hU(`n+L|FCd^)NFm^ff~8WKFjyUg&W;z0i}Q>WKyY>SgG>NogTWyB zEd>CCP$=AARB~q^LK{lx4f}Xrx5q#Faf)~UZdFNErb(8bkzu0i z6=&DGQIcff82~W{Q}g;YVS8@_I8s(t)!y9F?PTVC+$!tmR#`u18>W}96_?)wAlhiP zJph6Lpt!iAZk%4?8k@Sqc8cK&P8Zb!AOsr-Oag#f9hw#$mzp;HVSVTy z=y|)~oqfaqM@0yRac?IBRFRQ7y=r;Irx1z7gniiRX>F;kVON*>iLDe(qtRYhr~-AS xLpjF=-*@%8W@qd!Cw&Y54?!XT`LB~70{}!C2~cQJQ4atB002ovPDHLkV1g}p>>vOD literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi22-app-kbackgammon.png b/kbackgammon/icons/hi22-app-kbackgammon.png new file mode 100644 index 0000000000000000000000000000000000000000..882d0f9b4892949ba311774b2aae35da4208877a GIT binary patch literal 3914 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 N002ovPDHLkV1fhFciR8} literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi32-app-kbackgammon_engine.png b/kbackgammon/icons/hi32-app-kbackgammon_engine.png new file mode 100644 index 0000000000000000000000000000000000000000..871ef577666bdef6e1807a2ec310602dcc348b68 GIT binary patch literal 2192 zcmV;B2ygd^P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ^21!IgRCwC7R(VuZ*B$<5-n{qby;*>n0Rd4GcM&AG z0HG31ZIjrz5ZsQ|J=LT3M32=|v@JcRX%bHsEv+^791}~TmPl2kF2qJOBnFilR2Gqu z9R`@02h9FvdGGcQqcI*eV)Xm#p1Yj;-S2+CMTv(*PR^EJOOjNPl$10Mj|qejDDShT z&(Gh#ZN`kOYygOfiQ&`JC#_T{6caKtpP%>b9!+ua$-gdN{^}w;ZltEB&XHxAbaq

QU1EXOw z=t)znO<}%nwk=)!QZj(few*;@+5a{a78do{Y)(Z*MKy*GjR&Qo5ekN&QYxX>YG5#g zL(6mDDnTGiKyyncT3W4&#)ejWxF_!-0I@$`aN7e8hok81na{EZ#13Dq(P}``3}l(W z>k-l0=RsXvb)ZKO0x2mIv|$l?7!6@iD3lOI5m%}k5EPxh@~=PN450Us2pEk2mt{= zQ7R0GjDX1$;lJaIXV9RykeX$nQc=)oIjA`Xk|d#{(~8E%=0P+~8~YQ68j7OQqN4}B zvV3LE!XZNw4JwL2r{$s3a$s2&g3x;7%<1DR)6&M*S5{VUm^*LPCa2SdU`T??<$=@V zgQL%lT|58y?*{YLy#P)F@b@bW0pJ(Y7ZxsDvVIP&B;a`#VR{XCUX3uF7HTyk`Tb&u zWqDQ*dJqhT;B(he4;H5{Z1#xjtPt2VBv-I8`0cuVKhM_1k2K37!{NX1#~(Nq9{VeaMal3$xHg132ze^F*;?nMmIQ4 zunVx+`(Q9=z%Vq3bkh%aGbe<=V9kgM}g;fh++uAVCbe~N(vlDBO-#wjiwHK_W3#N%imt; z4|s)$a03C*1ppZtnaA7O+Ij%Y13;}_z1p~Y_ipRm1gP&`B{lX4U57={bIj7wBo2;G zez~Ko7aENkk&$|Yg>m3DG}Iggm&c3pi`P(ou@+yH9=&|6rhIzp#ED#S(cv7HRlBE5 znL6va=cX<)MMe$*fj2vQ_RQDT{pP@d{hyq2yWN&scW&Lf^?TKj+;Xnb7{zOJ;i#;< zB%VBR#5*b>*$^8W16rkm+wDbFRi)V7+2Kr^^gO_0H6-Ltl?WD;sjs@bl?}g!`;Eg|TJ~j2l8JR6j&7dhIBpDzQ(AHe_0f2%) zzz2uWJjv$`X4}2J@Or#}f?;1<|MmgDxN!@BZ9j&9BLAN|FLrlZM~qK!X2-+Fid zeuY8_SqeeT&=`|240?^qE&)tuSD(J_dILm%5Gsa+xxS`%=Z@c|0=RMCuDCxcWMwZ| z04Sl=u&@bkxZN(4S6qgc*O(+(hSTAKPxOQ5)!@_&qN3s>5)#uF)>fbY(~tH50BvJK zlhIU1bNbXaKZXbyP2?x+VQqJAPC z05x>j*qpWN-dHsz;VFcNY2b3XArk_g zP%4$Jsig%42X@!RKQ+XZm^8u2a=hZpGbI+Q&FV`W`|MCzmg)gC;|U>6&1<&+7y)4X zg!GpymaTq^0MrZsDGbAJ0x;n2vi`)tFx*_N-t-Ot?Snd^CkYAw^FuA!{|5l*U9KeF SxH~-n0000~V? literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi48-app-kbackgammon.png b/kbackgammon/icons/hi48-app-kbackgammon.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b5f1540067b13b015df679e1086b16db7887db GIT binary patch literal 3359 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 literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi48-app-kbackgammon_engine.png b/kbackgammon/icons/hi48-app-kbackgammon_engine.png new file mode 100644 index 0000000000000000000000000000000000000000..8291e82aa871e3ecd0c8b8ce45d6d40dbdb33a95 GIT binary patch literal 4046 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 AI{*Lx literal 0 HcmV?d00001 diff --git a/kbackgammon/icons/hi64-app-kbackgammon.png b/kbackgammon/icons/hi64-app-kbackgammon.png new file mode 100644 index 0000000000000000000000000000000000000000..f76e41581d92f28297286898ce39778b7fa95174 GIT binary patch literal 3704 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 literal 0 HcmV?d00001 diff --git a/kbackgammon/kbackgammon.desktop b/kbackgammon/kbackgammon.desktop new file mode 100644 index 00000000..f81fd922 --- /dev/null +++ b/kbackgammon/kbackgammon.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Exec=kbackgammon %i %m -caption "%c" +Name=KBackgammon +Name[af]=Kbackgammon +Name[ar]=لعبة النرد/الطاولة (KBackgammon) +Name[be]=Ðарды +Name[bn]=কে-বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ +Name[cs]=Vrchcáby +Name[eo]=Bakgamono +Name[hi]=के-बैकगेमॉन +Name[is]=Kotra +Name[ja]=ãƒãƒƒã‚¯ã‚®ãƒ£ãƒ¢ãƒ³ +Name[ne]=केडीई बà¥à¤¯à¤¾à¤•à¤—ामोन +Name[pt_BR]=KGamão +Name[ro]=Joc de table +Name[sv]=Kbackgammon +Name[ta]=கேபேகà¯à®•à®®à®¾à®©à¯ +Name[tg]=KÐардбозӣ +Name[tr]=Tavla +Name[zh_TW]=KBackgammon 西洋雙陸棋 +Type=Application +DocPath=kbackgammon/index.html +GenericName=Backgammon Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž нарды +GenericName[bg]=Табла +GenericName[bn]=বà§à¦¯à¦¾à¦•à¦—à§à¦¯à¦¾à¦®à§‹à¦¨ খেলা +GenericName[bs]=Igra tavle (Backgammon) +GenericName[ca]=Joc de Backgammon +GenericName[cs]=Backgammon hra +GenericName[cy]=Gêm Dawlbwrdd +GenericName[da]=Backgammon-spil +GenericName[de]=Backgammon Spiel +GenericName[el]=Παιχνίδι τάβλι +GenericName[eo]=Triktrakludo +GenericName[es]=Juego de Backgammon +GenericName[et]=Backgammoni mäng +GenericName[eu]=Backgammon jokoa +GenericName[fa]=بازی Backgammon +GenericName[fi]=Backgammon lautapeli +GenericName[fr]=Jeu de Backgammon +GenericName[he]=משחק שש־בש +GenericName[hr]=Backgammon +GenericName[hu]=Backgammon +GenericName[is]=Kotruleikur +GenericName[it]=Gioco del Backgammon +GenericName[ja]=ãƒãƒƒã‚¯ã‚®ãƒ£ãƒ¢ãƒ³ +GenericName[km]=ល្បែង Backgammon +GenericName[lt]=Backgammon žaidimas +GenericName[lv]=Backgammon spÄ“le +GenericName[mk]=Игра на табла +GenericName[nb]=Backgammon-spill +GenericName[nds]=Backgammon-Speel +GenericName[ne]=बà¥à¤¯à¤¾à¤•à¤—ामोन खेल +GenericName[nl]=Backgammonspel +GenericName[nn]=Backgammon-spel +GenericName[pa]=ਬੈਕਗਾਮੋਨ ਖੇਡ +GenericName[pl]=Backgammon +GenericName[pt]=Jogo de Gamão +GenericName[pt_BR]=Jogo de Gamão +GenericName[ru]=Ðарды +GenericName[se]=Backgammon-speallu +GenericName[sk]=Backgammon hra +GenericName[sl]=Igra backgammona +GenericName[sr]=Игра бекгемона +GenericName[sr@Latn]=Igra bekgemona +GenericName[sv]=Backgammonspel +GenericName[ta]=பாகà¯à®•à®¾à®®à®¾à®©à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в нарди +GenericName[wa]=Djeu d' backgammon +GenericName[zh_TW]=Backgammon 西洋雙陸棋éŠæˆ² +Terminal=false +Icon=kbackgammon +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kbackgammon/kbackgammonui.rc b/kbackgammon/kbackgammonui.rc new file mode 100644 index 00000000..24791882 --- /dev/null +++ b/kbackgammon/kbackgammonui.rc @@ -0,0 +1,51 @@ + + + + +

&Move + + + + + &Command + + &Settings + + + &Help + + + + +Main Toolbar + + + + + + + + + + + +Command Toolbar + + + + + + + + + + + + + + + + + + + diff --git a/kbackgammon/kbg.cpp b/kbackgammon/kbg.cpp new file mode 100644 index 00000000..55aef32a --- /dev/null +++ b/kbackgammon/kbg.cpp @@ -0,0 +1,830 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#include "kbg.h" +#include "kbg.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbgtextview.h" +#include "offline/kbgoffline.h" +#include "fibs/kbgfibs.h" +#include "gnubg/kbggnubg.h" +#include "nextgen/kbgng.h" +#include "version.h" + + +// == setup ==================================================================== + +/* + * Constructor creates user interface, actions and first engine. + */ +KBg::KBg() +{ + /* + * Initialize menu strings + */ + engineString[Offline] = i18n("Open Board"); + engineString[FIBS ] = i18n("FIBS"); + engineString[GNUbg ] = i18n("GNU Backgammon (Experimental)"); + engineString[NextGen] = i18n("Next Generation (Experimental)"); + + helpTopic[FIBSHome][0] = i18n("FIBS Home"); + helpTopic[FIBSHome][1] = "http://www.fibs.com/"; + + helpTopic[RuleHome][0] = i18n("Backgammon Rules"); + helpTopic[RuleHome][1] = "http://www.bkgm.com/rules.html"; + + /* + * The main view is shared between the board and a small text window + */ + panner = new QSplitter(Vertical, this, "panner"); + board = new KBgBoardSetup(panner, "board"); + status = new KBgTextView(panner, "status"); + setCentralWidget(panner); + + /* + * Create all actions needed by the application + */ + newAction = KStdGameAction::gameNew(this, SLOT(openNew()), actionCollection()); + newAction->setEnabled(false); + KStdGameAction::print(this, SLOT(print()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + QStringList list; + for (int i = 0; i < MaxEngine; i++) + list.append(engineString[i]); + engineSet = new KSelectAction(i18n("&Engine"), 0, this, SLOT(setupEngine()), actionCollection(), + "move_engine"); + engineSet->setItems(list); + + // AB: what the heck has this to do with redisplay? perhaps use reload instead? + loadAction = KStdAction::redisplay(this, SLOT(load()), actionCollection(), "move_load"); + loadAction->setEnabled(false); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + undoAction->setEnabled(false); + redoAction = KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + redoAction->setEnabled(false); + + rollAction = KStdGameAction::roll(this, SLOT(roll()), actionCollection()); + rollAction->setEnabled(false); + endAction = KStdGameAction::endTurn(this, SLOT(done()), actionCollection()); + endAction->setEnabled(false); + cubeAction = new KAction(i18n("Double Cube"), QIconSet(kapp->iconLoader()->loadIcon + (PROG_NAME "-double.xpm", KIcon::Toolbar)), + 0, this, SLOT(cube()), actionCollection(), "move_cube"); + cubeAction->setEnabled(false); + + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + KStdAction::preferences(this, SLOT(setupDlg()), actionCollection()); + KStdAction::saveOptions(this, SLOT(saveConfig()), actionCollection()); + + KPopupMenu *p = (new KActionMenu(i18n("&Backgammon on the Web"), + actionCollection(), "help_www"))->popupMenu(); + + (new KAction(helpTopic[FIBSHome][0], 0, this, SLOT(wwwFIBS()), + actionCollection(), "help_www_fibs"))->plug(p); + (new KAction(helpTopic[RuleHome][0], 0, this, SLOT(wwwRule()), + actionCollection(), "help_www_rule"))->plug(p); + + /* + * Set up the command line - using actions, otherwise recreating the GUI will delete them + * (e.g. using KEditToolbar) + */ + cmdLabel = new QLabel(i18n("Command: "), this); + new KWidgetAction( cmdLabel, cmdLabel->text(), 0, 0, 0, actionCollection(), "command_label"); + cmdLine = new KLineEdit(this, "commandline"); + KWidgetAction* actionCmdLine = new KWidgetAction( cmdLine, QString::null, 0, 0, 0, actionCollection(), "command_lineedit"); + actionCmdLine->setAutoSized(true); + + cmdLine->completionObject()->setOrder(KCompletion::Weighted); + connect(cmdLine, SIGNAL(returnPressed(const QString &)), this, SLOT(handleCmd(const QString &))); + /* + * Done with the actions, create the XML-defined parts of the + * user interface + */ + statusBar(); + setupGUI(); + + cmdLine->setFocus(); + + /* + * Initialize the engine to the default (offline). If the user + * prefers a different engine, it will be started later + */ + for (int i = 0; i < MaxEngine; i++) + engine[i] = 0; + currEngine = None; + engineSet->setCurrentItem(Offline); + setupEngine(); + + /* + * Set up configuration handling. + * FIXME: support session management + */ + connect(this, SIGNAL(readSettings()), board, SLOT(readConfig())); + connect(this, SIGNAL(saveSettings()), board, SLOT(saveConfig())); + + /* + * Set up some whatis messages for the online help + */ + QWhatsThis::add(status, i18n("This area contains the status messages for the game. " + "Most of these messages are sent to you from the current " + "engine.")); + QWhatsThis::add(toolBar("cmdToolBar"), + i18n("This is the command line. You can type special " + "commands related to the current engine in here. " + "Most relevant commands are also available " + "through the menus.")); + QWhatsThis::add(toolBar("mainToolBar"), + i18n("This is the button bar tool bar. It gives " + "you easy access to game related commands. " + "You can drag the bar to a different location " + "within the window.")); + QWhatsThis::add(statusBar(), + i18n("This is the status bar. It shows you the currently " + "selected engine in the left corner.")); + + /* + * Create and initialize the context menu + */ + QPopupMenu* menu = (QPopupMenu*)factory()->container("popup", this); + board->setContextMenu(menu); +} + +/* + * Destructor is empty + */ +KBg::~KBg() {} + + +// == engine handling ========================================================== + +/* + * Set the engine according to the currently selected item in the + * engineSet action. Additional engines have to be added to the switch + * statement (and only there). + */ +void KBg::setupEngine() +{ + /* + * Get new engine type + */ + int type = engineSet->currentItem(); + + /* + * Engine doesn't need to be changed? + */ + if (engine[type]) return; + + /* + * Check with the engine if it can be terminated + */ + if (currEngine != None && engine[currEngine] && !engine[currEngine]->queryClose()) { + engineSet->setCurrentItem(currEngine); + return; + } + + /* + * Remove the old engine, create a new one, and hook up menu and slots/signals + */ + QPopupMenu *commandMenu = (QPopupMenu *)factory()->container("command_menu", this); + QString s = PROG_NAME; + commandMenu->clear(); + + if (currEngine != None) { + delete engine[currEngine]; + engine[currEngine] = 0; + } + + switch (currEngine = type) { + case Offline: + engine[currEngine] = new KBgEngineOffline(this, &s, commandMenu); + break; + case FIBS: + engine[currEngine] = new KBgEngineFIBS(this, &s, commandMenu); + break; + case GNUbg: + engine[currEngine] = new KBgEngineGNU(this, &s, commandMenu); + break; + case NextGen: + engine[currEngine] = new KBgEngineNg(this, &s, commandMenu); + break; + default: // FIXME: we need some kind of catch here... + currEngine = Offline; + engine[currEngine] = new KBgEngineOffline(this, &s, commandMenu); + break; + } + + statusBar()->message(engineString[currEngine]); + KConfig* config = kapp->config(); + config->setGroup("global settings"); + if (config->readBoolEntry("enable timeout", true)) + engine[currEngine]->setCommit(config->readDoubleNumEntry("timeout", 2.5)); + newAction->setEnabled(engine[currEngine]->haveNewGame()); + + // engine -> this + connect(engine[currEngine], SIGNAL(statText(const QString &)), this, SLOT(updateCaption(const QString &))); + connect(engine[currEngine], SIGNAL(infoText(const QString &)), status, SLOT(write(const QString &))); + connect(engine[currEngine], SIGNAL(allowCommand(int, bool)), this, SLOT(allowCommand(int, bool))); + + // this -> engine + connect(this, SIGNAL(readSettings()), engine[currEngine], SLOT(readConfig())); + connect(this, SIGNAL(saveSettings()), engine[currEngine], SLOT(saveConfig())); + + // board -> engine + connect(board, SIGNAL(rollDice(const int)), engine[currEngine], SLOT(rollDice(const int))); + connect(board, SIGNAL(doubleCube(const int)), engine[currEngine], SLOT(doubleCube(const int))); + connect(board, SIGNAL(currentMove(QString *)), engine[currEngine], SLOT(handleMove(QString *))); + + // engine -> board + connect(engine[currEngine], SIGNAL(undoMove()), board, SLOT(undoMove())); + connect(engine[currEngine], SIGNAL(redoMove()), board, SLOT(redoMove())); + connect(engine[currEngine], SIGNAL(setEditMode(const bool)), board, SLOT(setEditMode(const bool))); + connect(engine[currEngine], SIGNAL(allowMoving(const bool)), board, SLOT(allowMoving(const bool))); + connect(engine[currEngine], SIGNAL(getState(KBgStatus *)), board, SLOT(getState(KBgStatus *))); + connect(engine[currEngine], SIGNAL(newState(const KBgStatus &)), board, SLOT(setState(const KBgStatus &))); + + // now that all signals are connected, start the engine + engine[currEngine]->start(); +} + + +// == configuration handing ==================================================== + +/* + * Save all settings that should be saved for the next start. + */ +void KBg::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Save the main window options unless the user has asked not + * to do so. + */ + if (config->readBoolEntry("autosave on exit", true)) { + + config->setGroup("main window"); + + config->writeEntry("origin", pos()); + + config->writeEntry("font", status->font()); + config->writeEntry("panner", (double)board->height()/(double)panner->height()); + + saveMainWindowSettings(config, "main window"); + } + + /* + * Save the history + */ + config->setGroup("command line"); + config->writeEntry("history", cmdLine->completionObject()->items()); + + /* + * Save current engine + */ + config->setGroup("engine settings"); + config->writeEntry("last engine", currEngine); + + /* + * Tell other objects to save their settings, too. + */ + emit saveSettings(); + + config->sync(); +} + +/* + * Read the stored configuration and apply it + */ +void KBg::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Restore the main window settings unless the user has asked + * not to do so. + */ + if (config->readBoolEntry("autosave on exit", true)) { + + config->setGroup("main window"); + + QPoint pos, defpos(10, 10); + QFont kappFont = kapp->font(); + + pos = config->readPointEntry("origin", &defpos); + + status->setFont(config->readFontEntry("font", &kappFont)); + + QValueList l; + l.append(qRound( config->readDoubleNumEntry("panner", 0.75) *panner->height())); + l.append(qRound((1-config->readDoubleNumEntry("panner", 0.75))*panner->height())); + panner->setSizes(l); + + applyMainWindowSettings(config, "main window"); + } + + /* + * Restore the history + */ + config->setGroup("command line"); + cmdLine->completionObject()->setItems(config->readListEntry("history")); + + /* + * Tell other objects to read their configurations + */ + emit readSettings(); + + /* + * Restore last engine + */ + config->setGroup("engine settings"); + engineSet->setCurrentItem((Engines)config->readNumEntry("last engine", Offline)); + setupEngine(); +} + + +// == configuration ============================================================ + +/* + * Connected to the setup dialog applyButtonPressed signal. Make sure + * that all changes are saved. + */ +void KBg::setupOk() +{ + // global settings + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + config->writeEntry("enable timeout", cbt->isChecked()); + config->writeEntry("timeout", sbt->value()); + config->writeEntry("autosave on exit", cbs->isChecked()); + + // tell engine about commit timer + engine[currEngine]->setCommit(cbt->isChecked() ? sbt->value() : -1); + + // one time requests + if (cbm->isChecked()) + KMessageBox::enableAllMessages(); + + // tell children to read their changes + board->setupOk(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupOk(); + + // save it all + saveConfig(); +} + +/* + * Load default values for the user settings + */ +void KBg::setupDefault() +{ + // timeout + cbt->setChecked(true); + sbt->setValue(2.5); + + // messages + cbm->setChecked(false); + + // auto save + cbs->setChecked(true); + + // board + board->setupDefault(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupDefault(); +} + +/* + * Connected to the setup dialog cancelButtonPressed signal. There + * isn't much to do. We tell the board to undo the changes. + */ +void KBg::setupCancel() +{ + // board + board->setupCancel(); + + // engines + for (int i = 0; i < MaxEngine; i++) + engine[i]->setupCancel(); +} + +/* + * Setup dialog is ready to be deleted. Do it later... + */ +void KBg::setupDone() +{ + nb->delayedDestruct(); + for (int i = 0; i < MaxEngine; i++) + if (i != currEngine) engine[i] = 0; +} + +// FIXME make more general... + +void KBg::startKCM(const QString &url) +{ + KRun::runCommand(url); +} + +/* + * Initialize and display the setup dialog + */ +void KBg::setupDlg() +{ + /* + * Get a new notebook in which all other members can put their + * config pages + */ + nb = new KDialogBase(KDialogBase::IconList, i18n("Configuration"), + KDialogBase::Ok|KDialogBase::Cancel|KDialogBase::Default| + KDialogBase::Apply|KDialogBase::Help, + KDialogBase::Ok, this, "setup", true, true); + + KConfig* config = kapp->config(); + config->setGroup("global settings"); + + /* + * Main Widget + */ + QVBox *w = nb->addVBoxPage(i18n("General"), i18n("Here you can configure general settings of %1"). + arg(kapp->aboutData()->programName()), + kapp->iconLoader()->loadIcon("go", KIcon::Desktop)); + + /* + * Group boxes + */ + QGroupBox *gbm = new QGroupBox(i18n("Messages"), w); + QGroupBox *gbt = new QGroupBox(i18n("Timer"), w); + QGroupBox *gbs = new QGroupBox(i18n("Autosave"), w); + QGroupBox *gbe = new QGroupBox(i18n("Events"), w); + + /* + * Timer box + */ + QWhatsThis::add(gbt, i18n("After you finished your moves, they have to be sent to the engine. " + "You can either do that manually (in which case you should not enable " + "this feature), or you can specify an amount of time that has to pass " + "before the move is committed. If you undo a move during the timeout, the " + "timeout will be reset and restarted once you finish the move. This is " + "very useful if you would like to review the result of your move.")); + + cbt = new QCheckBox(i18n("Enable timeout"), gbt); + cbt->setChecked(config->readBoolEntry("enable timeout", true)); + + sbt = new KDoubleNumInput(gbt); + sbt->setRange(0.0, 60.0, 0.5); + sbt->setLabel(i18n("Move timeout in seconds:")); + sbt->setValue(config->readDoubleNumEntry("timeout", 2.5)); + + connect(cbt, SIGNAL(toggled(bool)), sbt, SLOT(setEnabled(bool))); + sbt->setEnabled(cbt->isChecked()); + + QGridLayout *gl = new QGridLayout(gbt, 2, 1, 20); + gl->addWidget(cbt, 0, 0); + gl->addWidget(sbt, 1, 0); + + /* + * Enable messages + */ + QWhatsThis::add(gbm, i18n("Check the box to enable all the messages that you have previously " + "disabled by choosing the \"Don't show this message again\" option.")); + + QGridLayout *glm = new QGridLayout(gbm, 1, 1, nb->spacingHint()); + cbm = new QCheckBox(i18n("Reenable all messages"), gbm); + glm->addWidget(cbm, 0, 0); + + /* + * Save options on exit ? + */ + QWhatsThis::add(gbm, i18n("Check the box to automatically save all window positions on program " + "exit. They will be restored at next start.")); + + QGridLayout *gls = new QGridLayout(gbs, 1, 1, nb->spacingHint()); + cbs = new QCheckBox(i18n("Save settings on exit"), gbs); + cbs->setChecked(config->readBoolEntry("autosave on exit", true)); + gls->addWidget(cbs, 0, 0); + + /* + * Event vonfiguration + */ + QWhatsThis::add(gbe, i18n("Event notification of %1 is configured as part of the " + "system-wide notification process. Click here, and you " + "will be able to configure system sounds, etc."). + arg(kapp->aboutData()->programName())); + + QGridLayout *gle = new QGridLayout(gbe, 1, 1, nb->spacingHint()); + KURLLabel *lab = new KURLLabel("kcmshell kcmnotify", + i18n("Klick here to configure the event notification"), gbe); + lab->setMaximumSize(lab->sizeHint()); + + gle->addWidget(lab, 0, 0); + connect(lab, SIGNAL(leftClickedURL(const QString &)), SLOT(startKCM(const QString &))); + + /* + * Board settings + */ + board->getSetupPages(nb); + + /* + * Hack alert: this little trick makes sure that ALL engines + * have their settings available in the dialog. + */ + QPopupMenu *dummyPopup = new QPopupMenu(nb); + QString s = PROG_NAME; + for (int i = 0; i < MaxEngine; i++) { + if (currEngine != i) { + switch (i) { + case Offline: + engine[i] = new KBgEngineOffline(nb, &s, dummyPopup); + break; + case FIBS: + engine[i] = new KBgEngineFIBS(nb, &s, dummyPopup); + break; + case GNUbg: + engine[i] = new KBgEngineGNU(nb, &s, dummyPopup); + break; + case NextGen: + engine[i] = new KBgEngineNg(nb, &s, dummyPopup); + break; + } + connect(this, SIGNAL(saveSettings()), engine[i], SLOT(saveConfig())); + } + engine[i]->getSetupPages(nb); + } + + /* + * Connect the signals of nb + */ + connect(nb, SIGNAL(okClicked()), this, SLOT(setupOk())); + connect(nb, SIGNAL(applyClicked()), this, SLOT(setupOk())); + connect(nb, SIGNAL(cancelClicked()), this, SLOT(setupCancel())); + connect(nb, SIGNAL(defaultClicked()),this, SLOT(setupDefault())); + + connect(nb, SIGNAL(finished()), this, SLOT(setupDone())); + + nb->resize(nb->minimumSize()); + nb->show(); +} + + +// == action slots ============================================================= + +/* + * Tell the board to print itself - restore and save user settings for + * the print dialog. + */ +void KBg::print() +{ + KPrinter *prt = new KPrinter(); + + KConfig* config = kapp->config(); + config->setGroup("printing"); + + prt->setNumCopies(config->readNumEntry("numcopies", 1)); + prt->setOutputFileName(config->readPathEntry("outputfile")); + prt->setOutputToFile(config->readBoolEntry("tofile", false)); + prt->setPageSize((KPrinter::PageSize) config->readNumEntry("pagesize", KPrinter::A4)); + prt->setOrientation((KPrinter::Orientation)config->readNumEntry("orientation", KPrinter::Landscape)); + + if (prt->setup(this, i18n("Print %1").arg(baseCaption))) { + QPainter p; + p.begin(prt); + board->print(&p); + p.end(); + config->writeEntry("tofile", prt->outputToFile()); + config->writePathEntry("outputfile", prt->outputFileName()); + config->writeEntry("pagesize", (int)prt->pageSize()); + config->writeEntry("orientation", (int)prt->orientation()); + config->writeEntry("numcopies", prt->numCopies()); + } + delete prt; +} + +/* + * Toggle visibility of the menubar - be careful that the menu doesn't + * get lost + */ +void KBg::toggleMenubar() +{ + if (menuBar()->isVisible()) { + + KMessageBox::information(this, i18n("You can enable the menubar again with the " + "right mouse button menu of the board."), + i18n("Information"), "conf_menubar_information"); + menuBar()->hide(); + + } else { + + menuBar()->show(); + } +} + +/* + * Display a standard dialog for the toolbar content + */ +void KBg::configureToolbars() +{ + saveMainWindowSettings(KGlobal::config(), "kedittoolbar settings"); // temp group + KEditToolbar dlg(actionCollection(), xmlFile(), true, this); + connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(newToolbarConfig())); + dlg.exec(); + KGlobal::config()->deleteGroup( "kedittoolbar settings" ); // delete temp group +} + +/* + * Called when clicking OK or Apply in the toolbar editor + */ +void KBg::newToolbarConfig() +{ + createGUI(); + applyMainWindowSettings(KGlobal::config(), "kedittoolbar settings"); +} + +/* + * Help slots + */ +void KBg::wwwFIBS() {showWWW(FIBSHome);} +void KBg::wwwRule() {showWWW(RuleHome);} + +void KBg::showWWW(int t) +{ + kapp->invokeBrowser(helpTopic[t][1]); +} + +/* + * Edit slots + */ +void KBg::undo() {engine[currEngine]->undo();} +void KBg::redo() {engine[currEngine]->redo();} +void KBg::roll() {engine[currEngine]->roll();} +void KBg::cube() {engine[currEngine]->cube();} +void KBg::done() {engine[currEngine]->done();} +void KBg::load() {engine[currEngine]->load();} + +/* + * Start a new game with the current engine + */ +void KBg::openNew() +{ + engine[currEngine]->newGame(); +} + + +// == various slots - not for actions ========================================== + +/* + * Check with the engine if it is okay to close the window. + * If so, save settings. + */ +bool KBg::queryClose() +{ + bool ret = engine[currEngine]->queryClose(); + if ( ret ) + saveConfig(); + return ret; +} + +/* + * Set the caption of the main window. If the user has requested pip + * counts, they are appended, too. + */ +void KBg::updateCaption(const QString &s) +{ + baseCaption = s; + QString msg; + if (!s.isEmpty()) { + msg = s; + if (board->getPipCount(US) >= 0) { + QString tmp; + tmp.setNum(board->getPipCount(US )); + msg += " - " + tmp; + tmp.setNum(board->getPipCount(THEM)); + msg += "-" + tmp; + } + } + setCaption(msg, false); +} + +/* + * Take the string from the commandline, give it to the engine, append + * to the history and clear the buffer. + */ +void KBg::handleCmd(const QString &s) +{ + if (!s.stripWhiteSpace().isEmpty()) { + engine[currEngine]->handleCommand(s); + cmdLine->completionObject()->addItem(s); + } + cmdLine->clear(); + cmdLine->completionBox()->close(); +} + +/* + * Reflect the availability of commands in the button bar. + */ +void KBg::allowCommand(int cmd, bool f) +{ + switch (cmd) { + case KBgEngine::Undo: + undoAction->setEnabled(f); + break; + case KBgEngine::Redo: + redoAction->setEnabled(f); + break; + case KBgEngine::Roll: + rollAction->setEnabled(f); + break; + case KBgEngine::Cube: + cubeAction->setEnabled(f); + break; + case KBgEngine::Done: + endAction->setEnabled(f); + break; + case KBgEngine::Load: + loadAction->setEnabled(f); + break; + } +} + +/* + * Catch the hide envents. That way, the current engine can close its + * child windows. + */ +void KBg::hideEvent(QHideEvent *e) +{ + KMainWindow::hideEvent(e); + engine[currEngine]->hideEvent(); +} + +/* + * Catch the show envents. That way, the current engine can open any + * previously hidden windows. + */ +void KBg::showEvent(QShowEvent *e) +{ + KMainWindow::showEvent(e); + engine[currEngine]->showEvent(); +} + +// EOF + diff --git a/kbackgammon/kbg.h b/kbackgammon/kbg.h new file mode 100644 index 00000000..d191c8a3 --- /dev/null +++ b/kbackgammon/kbg.h @@ -0,0 +1,228 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + hoefkens@pilot.msu.edu + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef __KBG_H +#define __KBG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +class QSplitter; +class QCheckBox; +class QPopupMenu; +class QLabel; +class KAction; +class KSelectAction; +class KLineEdit; +class KDialogBase; +class KDoubleNumInput; + +class KBgEngine; +class KBgTextView; +class KBgBoardSetup; + + +class KBg : public KMainWindow +{ + Q_OBJECT + +public: + + /** + * Constructor creates the full main window + */ + KBg(); + + /** + * Destructor + */ + virtual ~KBg(); + + /** + * Read various settings from the configuration files or + * set some reasonable defaults + */ + void readConfig(); + +public slots: + + /** + * Set the caption to KFIBS_NAME + string + pipcount (if requested by + * the user) + */ + void updateCaption(const QString &s); + + /** + * Slot to be called by the engine - it enables/disables buttons + * in the button bar + */ + void allowCommand(int cmd, bool f); + + /** + * Sets the backgammon engine to type + */ + void setupEngine(); + + void startKCM(const QString &); + +signals: + + /** + * Tell all listeners to write their settings to disk + */ + void saveSettings(); + + /** + * Tell all listeners to restore their settings or use reasonable + * defaults + */ + void readSettings(); + +protected: + + /* + * Windows are to be hidden + */ + virtual void hideEvent(QHideEvent *); + + /* + * Redisplay the windows + */ + virtual void showEvent(QShowEvent *); + + /* + * Called before the window is closed. Check with the engine + * if that is okay. + */ + virtual bool queryClose(); + +protected slots: + + /** + * Show the button bar - or not - depending on the corresponding action + */ + void toggleMenubar(); + + void configureToolbars(); + void newToolbarConfig(); + + /** + * Starts the print dialog and asks the board to print itself + */ + void print(); + + void openNew(); + + /** + * Takes text from the commandline and hands it over to the + * current engine + */ + void handleCmd(const QString &); + + /** + * Saves the user settings to disk + */ + void saveConfig(); + + /** + * Slots for the respective actions - called by the button bar + * and some global key shortcuts + */ + void undo(); + void redo(); + void roll(); + void cube(); + void load(); + void done(); + + /** + * Opens and displays the respective home pages + */ + void showWWW(int t); + + void wwwFIBS(); + void wwwRule(); + + /** + * Show the big setup dialog + */ + void setupDlg(); + + /** + * Save the settings + */ + void setupOk(); + + /** + * Delete the setup dialog + */ + void setupDone(); + + /** + * Load default values for user settings + */ + void setupDefault(); + + /** + * Undo the settings + */ + void setupCancel(); + +private: + KAction *newAction, *undoAction, *redoAction, *rollAction, *cubeAction, *endAction, *loadAction; + + /* + * Each engine has its own identifier. + */ + enum Engines {None = -1, Offline, FIBS, GNUbg, NextGen, MaxEngine}; + QString engineString[MaxEngine]; + KBgEngine *engine[MaxEngine]; + int currEngine; + + QPopupMenu *dummyPopup; + enum HelpTopics {FIBSHome, RuleHome, MaxHelpTopic}; + QString helpTopic[MaxHelpTopic][2]; + KSelectAction *engineSet; + + /** + * Notebook for the setup + */ + KDialogBase *nb; + KDoubleNumInput *sbt; + QCheckBox *cbt, *cbs, *cbm; + + /* + * UI elements + */ + QSplitter *panner; + KBgBoardSetup *board; + KBgTextView *status; + KLineEdit *cmdLine; + QLabel *cmdLabel; + QString baseCaption; // for user friendly printing, we keep it around +}; + +#endif // __KBG_H diff --git a/kbackgammon/kbgboard.cpp b/kbackgammon/kbgboard.cpp new file mode 100644 index 00000000..8b961a45 --- /dev/null +++ b/kbackgammon/kbgboard.cpp @@ -0,0 +1,2918 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +/* + + This file contains the implementation of the KBgBoard class and + all related utility classes. + + Effort has been made to keep this class general. Please comment on that + if you want to use it in your own project. + +*/ + +#include + +#include "kbgboard.h" +#include "kbgboard.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" + + +const int CUBE_UPPER = 3; +const int CUBE_LOWER = 4; + +static const int MINIMUM_CHECKER_SIZE = 10; + +/* + * Set the default settings in all user configurations + */ +void KBgBoardSetup::setupDefault() +{ + // default background color + setBackgroundColor(QColor(200, 200, 166)); + pbc_1->setPalette(QPalette(backgroundColor())); + + // checker colors + baseColors[0] = black; + baseColors[1] = white; + pbc_2->setPalette(QPalette(baseColors[0])); + pbc_3->setPalette(QPalette(baseColors[1])); + + // default font + setFont(QFont("Serif", 18, QFont::Normal)); + kf->setFont(getFont()); + + // short moves + setShortMoveMode(SHORT_MOVE_DOUBLE); + for (int i = 0; i < 3; i++) + rbMove[i]->setChecked(i == SHORT_MOVE_DOUBLE); + + // pip count + cbp->setChecked(computePipCount = true); +} + +/* + * User committed the changes. Save them. + */ +void KBgBoardSetup::setupOk() +{ + // font selection + setFont(kf->font()); + + // move strategy + for (int i = 0; i < 3; i++) + if (rbMove[i]->isChecked()) setShortMoveMode(i); + + // pipcount + computePipCount = cbp->isChecked(); +} + +/* + * User cancelled the changes. Undo the color changes that become + * visible right away. + */ +void KBgBoardSetup::setupCancel() +{ + // undo background color change + setBackgroundColor(saveBackgroundColor); + + // undo checker color changes + baseColors[0] = saveBaseColors[0]; + baseColors[1] = saveBaseColors[1]; + + for (int i = 0; i < 30; i++) + cells[i]->update(); +} + +/* + * Fills configuration page in the dialog nb + */ +void KBgBoardSetup::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + * =========== + */ + QVBox *vbp = nb->addVBoxPage(i18n("Board"), i18n("Here you can configure the backgammon board"), + kapp->iconLoader()->loadIcon(PROG_NAME, KIcon::Desktop)); + + /* + * Need more than one page + */ + KTabCtl *tc = new KTabCtl(vbp, "board tabs"); + + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + /* + * Group boxes + * =========== + */ + QGroupBox *ga = new QGroupBox(w); + QButtonGroup *gm = new QButtonGroup(w); + QGroupBox *go = new QGroupBox(w); + + ga->setTitle(i18n("Colors")); + gm->setTitle(i18n("Short Moves")); + go->setTitle(i18n("Options")); + + gl->addWidget(ga, 0, 0); + gl->addWidget(gm, 1, 0); + gl->addWidget(go, 2, 0); + + /* + * Appearance group + * ---------------- + */ + QGridLayout *blc = new QGridLayout(ga, 2, 2, 20); + + pbc_1 = new QPushButton(i18n("Background"), ga); + pbc_1->setPalette(QPalette(backgroundColor())); + + pbc_2 = new QPushButton(i18n("Color 1"), ga); + pbc_2->setPalette(QPalette(baseColors[0])); + + pbc_3 = new QPushButton(i18n("Color 2"), ga); + pbc_3->setPalette(QPalette(baseColors[1])); + + blc->addWidget(pbc_2, 0, 0); + blc->addWidget(pbc_3, 0, 1); + blc->addMultiCellWidget(pbc_1, 1, 1, 0, 1); + + connect(pbc_1, SIGNAL(clicked()), this, SLOT(selectBackgroundColor())); + connect(pbc_2, SIGNAL(clicked()), this, SLOT(selectBaseColorOne())); + connect(pbc_3, SIGNAL(clicked()), this, SLOT(selectBaseColorTwo())); + + /* + * Moving style + * ------------ + */ + QBoxLayout *blm = new QVBoxLayout(gm, nb->spacingHint()); + + blm->addSpacing(gm->fontMetrics().height()); + + for (int i = 0; i < 3; i++) + rbMove[i] = new QRadioButton(gm); + + rbMove[SHORT_MOVE_NONE]->setText(i18n("&Disable short moves. Only drag and drop will move.")); + rbMove[SHORT_MOVE_SINGLE]->setText(i18n("&Single clicks with the left mouse button will\n" + "move a checker the shortest possible distance.")); + rbMove[SHORT_MOVE_DOUBLE]->setText(i18n("D&ouble clicks with the left mouse button will\n" + "move a checker the shortest possible distance.")); + + for (int i = 0; i < 3; i++) { + rbMove[i]->setMinimumSize(rbMove[i]->sizeHint()); + blm->addWidget(rbMove[i]); + rbMove[i]->setChecked(i == getShortMoveMode()); + } + + /* + * Other options + * ------------- + */ + QGridLayout *glo = new QGridLayout(go, 1, 1, 20); + + cbp = new QCheckBox(i18n("Show pip count in title bar"), go); + cbp->setChecked(computePipCount); + cbp->adjustSize(); + cbp->setMinimumSize(cbp->size()); + + glo->addRowSpacing(0, cbp->height()); + glo->addWidget(cbp, 0, 0); + + gl->activate(); + + w->adjustSize(); + w->setMinimumSize(w->size()); + + tc->addTab(w, i18n("&Board")); + + /* + * Save current settings + * --------------------- + */ + saveBackgroundColor = backgroundColor(); + saveBaseColors[0] = baseColors[0]; + saveBaseColors[1] = baseColors[1]; + + /* + * Font selection page + * =================== + */ + w = new QWidget(tc); + kf = new KFontChooser(w); + kf->setFont(getFont()); + gl = new QGridLayout(w, 1, 1, nb->spacingHint()); + gl->addWidget(kf, 0, 0); + gl->activate(); + w->adjustSize(); + w->setMinimumSize(w->size()); + tc->addTab(w, i18n("&Font")); +} + +/* + * Empty constructor calls the board constructor + */ +KBgBoardSetup::KBgBoardSetup(QWidget *parent, const char *name, QPopupMenu *menu) + : KBgBoard(parent, name, menu) +{ + // empty +} + +/* + * User changed first checker color + */ +void KBgBoardSetup::selectBaseColorOne() +{ + KColorDialog *c = new KColorDialog(this, "base-col-1", true); + c->setColor(baseColors[0]); + if (c->exec()) { + baseColors[0] = c->color(); + pbc_2->setPalette(QPalette(baseColors[0])); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * User changed second checker color + */ +void KBgBoardSetup::selectBaseColorTwo() +{ + KColorDialog *c = new KColorDialog(this, "base-col-2", true); + c->setColor(baseColors[1]); + if (c->exec()) { + baseColors[1] = c->color(); + pbc_3->setPalette(QPalette(baseColors[1])); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * User changed background color + */ +void KBgBoardSetup::selectBackgroundColor() +{ + KColorDialog *c = new KColorDialog(this, "bg-col", true); + c->setColor(backgroundColor()); + if (c->exec()) { + setBackgroundColor(c->color()); + pbc_1->setPalette(QPalette(backgroundColor())); + for (int i = 0; i < 30; i++) + cells[i]->update(); + } + delete c; +} + +/* + * Saves the persistent settings of the board + */ +void KBgBoard::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + config->writeEntry("bgcolor", backgroundColor()); + config->writeEntry("color-1", baseColors[0]); + config->writeEntry("color-2", baseColors[1]); + config->writeEntry("font", getFont()); + config->writeEntry("move", getShortMoveMode()); + config->writeEntry("pip", computePipCount); +} + +/* + * Restore the settings or use reasonable defaults + */ +void KBgBoard::readConfig() +{ + QColor col(200, 200, 166); + QFont fon("Serif", 18, QFont::Normal); + + KConfig* config = kapp->config(); + config->setGroup(name()); + + setBackgroundColor(config->readColorEntry("bgcolor", &col)); + baseColors[0] = config->readColorEntry("color-1", &black); + baseColors[1] = config->readColorEntry("color-2", &white); + setFont(config->readFontEntry("font", &fon)); + setShortMoveMode(config->readNumEntry("move", SHORT_MOVE_DOUBLE)); + computePipCount = config->readBoolEntry("pip", true); +} + +/* + * Get the font the board cells should use for the display of + * numbers and cube value. + */ +QFont KBgBoard::getFont() const +{ + return boardFont; +} + +/* + * Allows the users of the board classe to set the font to be used + * on the board. Note that the fontsize is dynamically set + */ +void KBgBoard::setFont(const QFont& f) +{ + boardFont = f; +} + +/* + * Ask the user for an updated cube value + */ +void KBgBoard::queryCube() +{ + KBgStatus *st = new KBgStatus(); + getState(st); + KBgBoardQCube *dlg = + new KBgBoardQCube(abs(st->cube()), (st->cube(US) > 0), (st->cube(THEM) > 0)); + if (dlg->exec()) { + bool u = ((dlg->getCubeValue() == 0) || (dlg->getCubeOwner() == US )); + bool t = ((dlg->getCubeValue() == 0) || (dlg->getCubeOwner() == THEM)); + st->setCube((int)rint(pow(2.0, dlg->getCubeValue())), u, t); + setState(*st); // JENS + } + delete dlg; + delete st; +} + +/* + * Constructor, creates the dialog but does not show nor execute it. + */ +KBgBoardQCube::KBgBoardQCube(int val, bool us, bool them) + : QDialog(0, 0, true) +{ + setCaption(i18n("Set Cube Values")); + + QBoxLayout *vbox = new QVBoxLayout(this, 17); + + QLabel *info = new QLabel(this); + + cb[0] = new QComboBox(this, "first sb"); + cb[1] = new QComboBox(this, "second sb"); + ok = new KPushButton(KStdGuiItem::ok(), this); + cancel = new KPushButton(KStdGuiItem::cancel(), this); + + info->setText(i18n("Set the face value of the cube and select who should be able to\n" + "double. Note that a face value of 1 automatically allows both\n" + "players to double.")); + + info->setMinimumSize(info->sizeHint()); + + vbox->addWidget(info, 0); + + QBoxLayout *hbox_1 = new QHBoxLayout(); + QBoxLayout *hbox_2 = new QHBoxLayout(); + + vbox->addLayout(hbox_1); + vbox->addLayout(hbox_2); + + hbox_1->addWidget(cb[1]); + hbox_1->addWidget(cb[0]); + + hbox_2->addWidget(ok); + hbox_2->addWidget(cancel); + + cb[0]->insertItem(" 1", 0); + cb[0]->insertItem(" 2", 1); + cb[0]->insertItem(" 4", 2); + cb[0]->insertItem(" 8", 3); + cb[0]->insertItem("16", 4); + cb[0]->insertItem("32", 5); + cb[0]->insertItem("64", 6); + + switch(val) { + case 1: + cb[0]->setCurrentItem(0); + break; + case 2: + cb[0]->setCurrentItem(1); + break; + case 4: + cb[0]->setCurrentItem(2); + break; + case 8: + cb[0]->setCurrentItem(3); + break; + case 16: + cb[0]->setCurrentItem(4); + break; + case 32: + cb[0]->setCurrentItem(5); + break; + case 64: + cb[0]->setCurrentItem(6); + break; + } + + cb[1]->insertItem(i18n("Lower Player"), US); + cb[1]->insertItem(i18n("Upper Player"), THEM); + cb[1]->insertItem(i18n("Open Cube"), BOTH); + + if (us && them) + cb[1]->setCurrentItem(BOTH); + else if (us) + cb[1]->setCurrentItem(US); + else if (them) + cb[1]->setCurrentItem(THEM); + + cb[0]->setMinimumSize(cb[0]->sizeHint()); + cb[1]->setMinimumSize(cb[1]->sizeHint()); + + ok->setMinimumSize(ok->sizeHint()); + cancel->setMinimumSize(cancel->sizeHint()); + + setMinimumSize(childrenRect().size()); + + vbox->activate(); + + resize(minimumSize()); + + ok->setAutoDefault (true); + ok->setDefault(true); + + cb[0]->setFocus(); + + connect(ok, SIGNAL(clicked()), SLOT(accept())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + + connect(cb[0], SIGNAL(activated(int)), SLOT(changePlayer(int))); + connect(cb[1], SIGNAL(activated(int)), SLOT(changeValue (int))); +} + +/* + * Deconstructor, empty. + */ +KBgBoardQCube::~KBgBoardQCube() +{ + // nothing +} + +/* + * Get the face value of the cube + */ +int KBgBoardQCube::getCubeValue() +{ + return cb[0]->currentItem(); +} + +/* + * Get the owner of the cube + */ +int KBgBoardQCube::getCubeOwner() +{ + return cb[1]->currentItem(); +} + +/* + * If the cube is open, the value can only be 1 + */ +void KBgBoardQCube::changeValue(int player) +{ + if (player == BOTH) + cb[0]->setCurrentItem(0); + +} + +/* + * If the value is 1, the cube has to be open; and if the value + * becomes bigger than 1, the player cannot stay open. + */ +void KBgBoardQCube::changePlayer(int val) +{ + if (val == 0) + cb[1]->setCurrentItem(BOTH); + else if (cb[1]->currentItem() == BOTH) + cb[1]->setCurrentItem(US); +} + +/* + * Constructor, creates the dialog but does not show nor execute it. + */ +KBgBoardQDice::KBgBoardQDice(const char *name) + : QDialog(0, name, true) +{ + setCaption(i18n("Set Dice Values")); + + QBoxLayout *vbox = new QVBoxLayout(this, 17); + + QLabel *info = new QLabel(this); + + sb[0] = new QSpinBox(this, "first sb"); + sb[1] = new QSpinBox(this, "second sb"); + ok = new KPushButton(KStdGuiItem::ok(), this); + cancel = new KPushButton(KStdGuiItem::cancel(), this); + + info->setText(i18n("Set the face values of the selected dice. The other player's\n" + "dice will be cleared and it will be the dice's owner's turn.")); + + info->setMinimumSize(info->sizeHint()); + + vbox->addWidget(info, 0); + + QBoxLayout *hbox_1 = new QHBoxLayout(); + QBoxLayout *hbox_2 = new QHBoxLayout(); + + vbox->addLayout(hbox_1); + vbox->addLayout(hbox_2); + + hbox_1->addWidget(sb[0]); + hbox_1->addWidget(sb[1]); + + hbox_2->addWidget(ok); + hbox_2->addWidget(cancel); + + sb[0]->setMinimumSize(sb[0]->sizeHint()); + sb[1]->setMinimumSize(sb[1]->sizeHint()); + + ok->setMinimumSize(ok->sizeHint()); + cancel->setMinimumSize(cancel->sizeHint()); + + setMinimumSize(childrenRect().size()); + + vbox->activate(); + + resize(minimumSize()); + + ok->setAutoDefault (true); + ok->setDefault(true); + + sb[0]->setFocus(); + + connect(ok, SIGNAL(clicked()), SLOT(accept())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + + sb[0]->setValue(1); + sb[1]->setValue(1); + + sb[0]->setRange(1, 6); + sb[1]->setRange(1, 6); +} + +/* + * Deconstructor, empty. + */ +KBgBoardQDice::~KBgBoardQDice() +{ + // nothing +} + +/* + * Get the face value of the dice + */ +int KBgBoardQDice::getDice(int n) +{ + return sb[n]->value(); +} + +/* + * Allows for overriding the current turn color in edit mode. + */ +void KBgBoard::storeTurn(const int pcs) +{ + storedTurn = ((pcs > 0) ? +1 : -1); +} + +/* + * Switch edit mode on/off + */ +void KBgBoard::setEditMode(const bool m) +{ + editMode = m; +} + +/* + * Retrurns the current edit mode status. + */ +bool KBgBoard::getEditMode() const +{ + return editMode; +} + +/* + * This function takes a KBgStatus object and fills it with the current + * board status. + */ +KBgStatus* KBgBoard::getState(KBgStatus *st) const +{ + st->setColor(color); + st->setDirection(direction); + + st->setCube(cube, maydouble[US], maydouble[THEM]); + + st->setBar(US, onbar[US]); st->setBar(THEM, onbar[THEM]); + st->setHome(US, onhome[US]); st->setHome(THEM, onhome[THEM]); + + st->setDice(US, 0, dice[US][0]); + st->setDice(US, 1, dice[US][1]); + + st->setDice(THEM, 0, dice[THEM][0]); + st->setDice(THEM, 1, dice[THEM][1]); + + for (int i = 1; i < 25; ++i) + st->setBoard(i, ((color*board[i] < 0) ? THEM : US), abs(board[i])); + + return st; +} + +/* + * This function lets external users change the context menu + */ +void KBgBoard::setContextMenu(QPopupMenu *menu) +{ + contextMenu = menu; +} + +/* + * This function prints all moves up to now in the extended FIBS command + * notation (that is moves that involved kicking have a "+" instead of "-". + */ +void KBgBoard::sendMove() +{ + if (getEditMode()) + return; + + QString s, t; + + s.setNum(moveHistory.count()); + s += " "; + + QPtrListIterator it(moveHistory); + for (; it.current(); ++it) { + KBgBoardMove *move = it.current(); + if (move->source() == BAR_US || move->source() == BAR_THEM ) { + s += "bar"; + } else { + t.setNum(move->source()); + s += t; + } + if (move->wasKicked()) + s += "+"; + else + s += "-"; + + if ((move->destination() != HOME_THEM_LEFT) && (move->destination() != HOME_THEM_RIGHT) && + (move->destination() != HOME_US_LEFT ) && (move->destination() != HOME_US_RIGHT )) { + t.setNum(move->destination()); + s += t; + } else { + s += "off"; + } + s += " "; + } + emit currentMove(&s); +} + +/* + * This is overloaded from QWidget, since it has to pass the new + * background color to the child widgets (the cells). + */ +void KBgBoard::setBackgroundColor(const QColor &col) +{ + if (col != backgroundColor()) { + QWidget::setBackgroundColor(col); + for( int i = 0; i < 30; ++i) + cells[i]->setBackgroundColor(col); + } +} + +/* + * Overloaded from QWidget since we have to resize all cells + */ +void KBgBoard::resizeEvent(QResizeEvent *) +{ + int xo0 = 0; + int xo1, w; + int hu = height()/2; + int hl = height() - hu; + + checkerDiam = (int)((width()/15-2)<(height()/10.0-2) ? + (width()/15-2) : (height()/10.0-2)); + + if (checkerDiam < MINIMUM_CHECKER_SIZE) + checkerDiam = MINIMUM_CHECKER_SIZE; + + for (int i = 0; i < 14; ++i) { + xo1 = int((i+1)*width()/15.0); + w = xo1 - xo0; + cells[i ]->setGeometry(xo0, 0, w, hu); + cells[i+15]->setGeometry(xo0, hu, w, hl); + xo0 = xo1; + } + cells[14]->setGeometry(xo0, 0, width() - xo0, hu); + cells[29]->setGeometry(xo0, hu, width() - xo0, hl); +} + +/* + * This function draws the whole board in black and white on the + * painter *p. It is very well suited for printing on paper. + * It scales the output according to the width of the widget. + * I.e. if the widget is insanely long (y-direction) this will look + * shitty. The upper 20% of the painter are not used. So the caller + * can print whatever she/he wants above the 0.2*p->viewport().height() + * margin (like game status information). + */ +void KBgBoard::print(QPainter *p) +{ + double sf = 0.8*p->viewport().width()/width(); + int xo = int((p->viewport().width() - sf*width())/2); + int yo = int(0.2*p->viewport().height()); + int hu = height()/2; + + int xo0 = 0; + for (int i = 0; i < 15; ++i) { + cells[i ]->paintCell(p, xo+sf*xo0, yo , sf); + cells[i+15]->paintCell(p, xo+sf*xo0, yo+sf*(hu-1), sf); + xo0 = int((i+1)*width()/15.0); + } +} + +/* + * This function returns the selected drawing color for a checker + * of the given sign(!). I.e. we distinguish checkers by whether + * they are negative or positive. + */ +QColor KBgBoard::getCheckerColor(int p) const +{ + return ((p < 0) ? baseColors[0] : baseColors[1]); +} + +/* + * This small utility function returns the y-coordinate base + * of a checker. This is the offset in the y-coordinate at + * which we have toposition the upper corner of the first + * checker so that it is fully in the cell. + */ +int KBgBoardField::numberBase() const +{ + return (cellID < 13) ? 0 : height()-20; +} + +/* + * This function computes the proper diameter for checkers on this cell. + * It tries to stay within the horizontal boundaries and adjusts the + * diameter in such a way that 5 checkers fit on top of each other and + * there is still some room for stacked checkers. + */ +int KBgBoardCell::getCheckerDiameter() const +{ + return board->checkerDiam; +} + +/* + * Draws the cells content using the painter p. + * Reimplemented from QLabel. + */ +void KBgBoardCell::drawContents(QPainter *) +{ + QRect cr(0, 0, width(), height()); + cr.moveBottomLeft(rect().bottomLeft()); + QPixmap pix(cr.size()); + QPainter tmp; + pix.fill(this, cr.topLeft()); + tmp.begin(&pix); + paintCell(&tmp); + tmp.end(); + bitBlt(this, 0, 0, &pix); + /* + * New state is now current. + * This avoids unnecessary redrawings. + */ + stateChanged = false; +} + +/* + * This does the absolute bare minimum of painting a cell. It draws a small + * horizontal black line that marks the outer boundary of the cell and all + * overloaded paintCell() member are supposed to call this one after(!) they + * have painted themselves. + */ +void KBgBoardCell::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + int x1 = xo; int x2 = xo; + int y1 = yo; int y2 = yo; + + if ((cellID==HOME_THEM_LEFT || cellID==BAR_THEM) || + (cellID<13 && cellID>0)) { + x2 += int(sf*width()); + } else if ((cellID==HOME_US_LEFT || cellID==BAR_US) || + (cellID<25 && cellID>12)) { + x2 += int(sf*width()); + y1 = y2 += int(sf*(height()-1)); + } else if (cellID == HOME_THEM_RIGHT) { + x2 += int(sf*(width()-1)); + } else if (cellID == HOME_US_RIGHT) { + x2 += int(sf*(width()-1)); + y1 = y2 += int(sf*(height()-1)); + } else { + return; // do nothing if the cellID is wrong + } + + // draw line in black + p->setBrush( black ); + p->setPen( black ); + p->drawLine(x1, y1, x2, y2); +} + +/* + * This function draws vertical boundaries around a cell. This is used + * for bars and homes to get them separated from the rest of the board. + */ +void +KBgBoardCell::drawVertBorder(QPainter *p, int xo, int yo, double sf) const +{ + p->setBrush(black); + p->setPen(black); + p->drawLine(xo, yo, xo, yo+sf*(height()-1)); + p->drawLine(xo+sf*(width()-1), yo, xo+sf*(width()-1), yo+sf*(height()-1)); +} + +/* + * This function draws the content of the homes on the painter *p. It + * starts at the upper left corner (xo, yo) and uses the scaling factor + * sf. + */ +void KBgBoardHome::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + /* + * Only these homes contain checkers. The other ones contains dice and cube. + */ + if (((cellID == HOME_THEM_LEFT ) && (direction > 0)) || + ((cellID == HOME_THEM_RIGHT) && (direction < 0)) || + ((cellID == HOME_US_LEFT ) && (direction > 0)) || + ((cellID == HOME_US_RIGHT ) && (direction < 0))) { + + drawOverlappingCheckers(p, xo, yo, sf); + + } else { + + drawDiceAndCube(p, ((cellID == HOME_THEM_LEFT || + cellID == HOME_THEM_RIGHT) ? + THEM : US), xo, yo, sf); + + } + + /* + * Finally draw the boundaries + */ + drawVertBorder(p, xo, yo, sf); + KBgBoardCell::paintCell(p, xo, yo, sf); +} + +/* + * This function draws the content of the bar cells. Bars may contain + * checkers and the cube. Please read the comments in the code on how + * and why the checkers and (especially) the cube is printed. + */ +void KBgBoardBar::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + /* + * Put in the checkers. + */ + drawOverlappingCheckers(p, xo, yo, sf); + + /* + * Now comes a slightly tricky part: the cube belongs in the center + * of the board if nobody has doubled yet. In the way we do the board + * the center belongs to two(!) fields - both bars. + * + * If we are not printing on paper we use the fact that + * Qt will clip the drawing for us. So we print the upper + * half of the cube and the lower half on different cells. + * + * Since there is no such thing as clipping when we print + * on paper we can only print one cube. It turns out that + * the lower one is sufficiently centered. + */ + if (board->canDouble(US) && + board->canDouble(THEM) && + !(abs(xo)+abs(yo) > 0 && cellID == BAR_THEM)) { + + drawCube(p, cellID == BAR_THEM ? CUBE_UPPER : + CUBE_LOWER, xo, yo, sf); + + } + + /* + * Finally draw the boundaries + */ + drawVertBorder(p, xo, yo, sf); + KBgBoardCell::paintCell(p, xo, yo, sf); +} + + +/* + * This function draws a cube on the painetr p. The cube will be drawn in + * the coundaries given by cubeRect(...). The other parameters are like + * in the other functions. + */ +void KBgBoardCell::drawCube(QPainter *p, int who, int xo, int yo, + double sf) const +{ + QRect r = cubeRect(who, true, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + p->setBrush(black); + p->setPen(black); + p->drawRoundRect(r, 20, 20); + + r = cubeRect(who, false, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + p->setBrush(white); + p->setPen(white); + p->drawRoundRect(r, 20, 20); + + p->setBrush(black); + p->setPen(black); + + QString cubeNum; + int v = board->getCube(); + /* + * Ensure that the cube shows 64 initially + */ + if (v == 1) v = 64; + cubeNum.setNum(v); + + /* + * Adjust the font size + */ + QFont f = board->getFont(); + f.setPointSizeFloat(0.75*f.pointSizeFloat()); + p->setFont(f); + p->drawText(r, AlignCenter, cubeNum); +} + +/* + * This function returns a boundary rectangle for the dice. It does so for both + * dice (i is either 0 or 1). It can return big and small rectangles and everything + * is scaled with a default value of 1.0. The scale parameter determines the the + * size of the dice relative to the checker diameter. + */ +QRect KBgBoardCell::diceRect(int i, bool big, double sf, double scale) const +{ + int d = int(scale*getCheckerDiameter()); + int l = (1+width())%2; + int k = (big ? 0 : 1); + return(QRect(sf*(width()/2-d+k), + sf*(height()/2-2*d-3+2*i*(d+3)-1+k), + sf*(2*(d-k)+1-l), + sf*(2*(d-k)+1-l))); +} + +/* + * This function returns a bounding rectangle for the cube. This rectangle + * is moved to the correct place and scaled correctly. The cube is slightly + * smaller than the dice. + */ +QRect KBgBoardCell::cubeRect(int who, bool big, double sf) const +{ + QRect r = diceRect(0, big, sf, 0.40); + + int d = int(0.40*getCheckerDiameter()); + int h = r.height(); + int k = (big ? 1 : 0); + + switch (who) { + case US: + r.setTop(sf*(height() - 3*d) - k); + break; + case THEM: + r.setTop(sf*d - k); + break; + case CUBE_UPPER: + r.setTop(height()-d*sf - k); + break; + case CUBE_LOWER: + r.setTop( -d*sf - k); + break; + default: + return(QRect(0,0,0,0)); + } + r.setHeight(h); + return r; +} + +/* + * This function draws the face value on a given dice painter. + * If the painting of dice should be saved this is the place + * to modify. + */ +void KBgBoardHome::drawDiceFace(QPainter *p, int col, int num, int who, + int xo, int yo, double sf) const +{ + p->setBrush(board->getCheckerColor(col)); + p->setPen(board->getCheckerColor(col)); + + QRect r = diceRect(num, false, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + + int cx = r.width() /2; + int cy = r.height()/2; + int cx2 = cx/2; + int cy2 = cy/2; + int cx7 = int(0.7*cx); + int cy7 = int(0.7*cy); + + switch (board->getDice(who, num)) { + case 5: + p->drawEllipse(r.x()+cx-cx7 , r.y()+cy+cy7-1, 2, 2); + p->drawEllipse(r.x()+cx+cx7-1, r.y()+cy-cy7 , 2, 2); + case 3: // fall through + p->drawEllipse(r.x()+cx-cx7 , r.y()+cy-cy7 , 2, 2); + p->drawEllipse(r.x()+cx+cx7-1, r.y()+cy+cy7-1, 2, 2); + case 1: // fall through + p->drawEllipse(r.x()+cx , r.y()+cy , 2, 2); + break; + case 4: + p->drawEllipse(r.x()+cx-cx2, r.y()+cy+cy2-1, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy-cy2, 2, 2); + case 2: // fall through + p->drawEllipse(r.x()+cx-cx2, r.y()+cy-cy2, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy+cy2-1, 2, 2); + break; + case 6: + p->drawEllipse(r.x()+cx-cx2, r.y()+cy-cy7, 2, 2); + p->drawEllipse(r.x()+cx-cx2, r.y()+cy, 2, 2); + p->drawEllipse(r.x()+cx-cx2, r.y()+cy+cy7, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy-cy7, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy, 2, 2); + p->drawEllipse(r.x()+cx+cx2-1, r.y()+cy+cy7, 2, 2); + break; + default: // nothing + break; + } +} + +/* + * This function draws a nice little square on the painter p. + * The square is suited to contain a a face value as printed + * by drawDiceFace(...). + */ +void KBgBoardHome::drawDiceFrame(QPainter *p, int col, int num, + int xo, int yo, bool big, double sf) const +{ + p->setBrush(board->getCheckerColor(col)); + p->setPen(board->getCheckerColor(col)); + QRect r = diceRect(num, big, sf); + r.moveTopLeft(QPoint(xo+r.left(), yo+r.top())); + p->drawRoundRect(r, 20, 20); +} + +/* + * If the event is left button we just store that. If the event is right + * button we ask the board to possibly display the popup menu. + */ +void KBgBoardCell::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == RightButton) + board->showContextMenu(); + else + mouseButton = e->button(); +} + +/* + * This function sets the short move mode of the board. + */ +void KBgBoard::setShortMoveMode(int m) +{ + switch (m) { + case SHORT_MOVE_NONE: + case SHORT_MOVE_SINGLE: + shortMoveMode = m; + break; + case SHORT_MOVE_DOUBLE: + default: + shortMoveMode = SHORT_MOVE_DOUBLE; + } +} + +/* + * This function returns the currently selected short move mode. + */ +int KBgBoard::getShortMoveMode() +{ + return shortMoveMode; +} + +/* + * This function checks if (a) the mouse event was a left button, + * (b) the parameter m equals the currently selected short move + * mode and (c) t a short move from this field is possible. If all + * tests are ok, the shortest possible move away from here is + * made. + */ +void KBgBoardCell::checkAndMakeShortMove(QMouseEvent *e, int m) +{ + if ((e->button() == LeftButton) && + (board->getShortMoveMode() == m) && + (dragPossible()) && + (!board->getEditMode())) + makeShortMove(); +} + +/* + * This functions reacts on a double click. + */ +void KBgBoardCell::mouseDoubleClickEvent(QMouseEvent *e) +{ + checkAndMakeShortMove(e, SHORT_MOVE_DOUBLE); +} + +/* + * This function reacts on a double click. Note that the bar knows + * about two different double clicks: double the cube and make a + * short move. + */ +void KBgBoardBar::mouseDoubleClickEvent(QMouseEvent *e) +{ + QRect r = cubeRect(cellID == BAR_THEM ? CUBE_UPPER : CUBE_LOWER, true); + if (board->canDouble(US) && + board->canDouble(THEM) && r.contains(e->pos())) { + if (board->getEditMode()) + board->queryCube(); + else + board->getDoubleCube(US); + return; + } + checkAndMakeShortMove(e, SHORT_MOVE_DOUBLE); +} + +/* + * This is the destructor of the backgammon board. It frees + * all resources previously allocated. + */ +KBgBoard::~KBgBoard() +{ + restoreCursor(); +} + +/* + * This function draws dice and cube on the painter for a home cell. + * who may be either US or THEM. + */ +void KBgBoardHome::drawDiceAndCube(QPainter *p, int who, int xo, int yo, + double sf) const +{ + int col = ((who == THEM) ? -color : color); + + /* + * draw the empty squares and then put the face value in there + */ + for (int i = 0; i < 2; i++) { + drawDiceFrame(p,-col, i, xo, yo, true, sf); + drawDiceFrame(p, col, i, xo, yo, false, sf); + drawDiceFace(p,-col, i, who, xo, yo, sf); + } + /* + * if necessary draw the cube + */ + if (board->canDouble(who) && + !(board->canDouble(US) && board->canDouble(THEM))) + drawCube(p, who, xo, yo, sf); +} + +/* + * This function determines whether a drag off this home is possible. + * This is only possible if there are checkers and edit mode is on. + */ +bool KBgBoardHome::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + return false; +} + +/* + * This function determines whether a drag off this bar is possible. + * It checks in the follwoing order: (1) owner of this bar is the one + * whose turn it is now, (2) does the board allow moving right now is + * it in read-only mode? + */ +bool KBgBoardBar::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + + switch(board->getTurn()) { + case US: + if (pcs*color <= 0) return false; + break; + case THEM: + if (pcs*color >= 0) return false; + break; + default: + return false; + } + return board->movingAllowed(); +} + +/* + * This function checks whether a checker can be moved away from + * this field. It first checks whether the owner of this field is + * the one whose turn to move it it, then it is checked whether + * the players bar is empty and finally it is checked if the board + * is in read-only mode. + */ +bool KBgBoardField::dragPossible() const +{ + if (board->getEditMode()) + return (pcs != 0); + + switch(board->getTurn()) { + case US: + if (pcs*color <= 0) return false; + break; + case THEM: + if (pcs*color >= 0) return false; + break; + default: + return false; + } + if (board->getOnBar(board->getTurn())) + return false; + return board->movingAllowed(); +} + +/* + * This function returns the current read-write flag of the board. + * If this returns true the board doesn't accept user input. If + * allowmoving is true we will accept user events. + */ +bool KBgBoard::movingAllowed() const +{ + return allowmoving; +} + +/* + * This function sets the read-write or read-only flag of the + * board. See also movingAllowed(). + */ +void KBgBoard::allowMoving(const bool fl) +{ + allowmoving = fl; +} + +/* + * This function returns the current pip count of the player w. + */ +int KBgBoard::getPipCount(const int& w) const +{ + if (!computePipCount || (w != US && w != THEM)) + return -1; + int pip = 25*abs(onbar[w]); + int d = ((w == US) ? 1 : -1); + for (int i = 1; i < 25; i++) { + if (d*board[i]*color > 0) + pip += ((d*direction < 0) ? + i*abs(board[i]) : + (25 - i)*abs(board[i])); + } + return pip; +} + +/* + * This function handles double clicks on homes. It will ignore + * double clicks on the real home and only handle the ones on the + * "other" home - the one with the dice. It will propagate the event + * only if the the click happened within the boundaries of a + * dice or the cube. + */ +void KBgBoardHome::mouseDoubleClickEvent(QMouseEvent * e) +{ + if (e->button() != LeftButton) + return; + /* + * Check whether this is the bookkeeping home... + */ + if ((cellID == HOME_US_LEFT && direction < 0) || + (cellID == HOME_US_RIGHT && direction > 0) || + (cellID == HOME_THEM_LEFT && direction < 0) || + (cellID == HOME_THEM_RIGHT && direction > 0)) { + + int w = ((cellID == HOME_US_LEFT || cellID == HOME_US_RIGHT) ? + US : THEM); + for (int i = 0; i < 2; ++i) { + QRect r = diceRect(i, true); + if (r.contains(e->pos())) { + if (board->getEditMode()) { + + KBgBoardQDice *dlg = new KBgBoardQDice(); + if (dlg->exec()) { + KBgStatus *st = new KBgStatus(); + board->getState(st); + st->setDice(w, 0, dlg->getDice(0)); + st->setDice(w, 1, dlg->getDice(1)); + st->setDice(((w == US) ? THEM : US), 0, 0); + st->setDice(((w == US) ? THEM : US), 1, 0); + board->setState(*st); // JENS + delete st; + } + delete dlg; + + } else + board->getRollDice(w); + return; + } + } + if (board->canDouble(w) && + !(board->canDouble(US) && board->canDouble(THEM))) { + QRect r = cubeRect(w, true); + if (r.contains(e->pos())) + if (board->getEditMode()) + board->queryCube(); + else + board->getDoubleCube(w); + } + } +} + +/* + * This function determines if a checker can be dropped on this field. + * It checks whether the field is already owned, empty or contains + * only one opponents piece. Then the dice are checked. + */ +bool KBgBoardField::dropPossible(int fromCellID, int newColor) +{ + if ((newColor*pcs > 0) || (pcs == 0) || (abs(pcs) == 1)) + // editMode is checked in diceAllowMove(...) + return board->diceAllowMove(fromCellID, cellID); + return false; +} + +/* + * This function determines if a checker can be dropped on this field. + * Drops on the bar are never possible. + */ +bool KBgBoardBar::dropPossible(int fromCellID, int newColor) +{ + if (!board->getEditMode()) + return false; + + if (newColor*pcs > 0) + return true; + if ((cellID == BAR_US) && (board->getTurn() == US)) + return true; + if ((cellID == BAR_THEM) && (board->getTurn() == THEM)) + return true; + + return (fromCellID == -12345); // always false +} + +/* + * This function checks if the current player can move a checker off. + * Check if we can move a piece off. This obviously only works if there + * are no pieces on the bar and all remaining pieces are in the home + * board. This does not check the dice and it doesn't work for multiple + * moves that start outside the home. + */ +bool KBgBoard::moveOffPossible() const +{ + if (getEditMode()) + return true; + + int w = getTurn(); + int d = ((w == THEM) ? -1 : 1); + if (onbar[w] == 0 && d*direction > 0) { + for (int i = 1; i < 19; ++i) { + if (d*color*board[i] > 0) return false; + } + return true; + + } else if (onbar[w] == 0 && d*direction < 0) { + for (int i = 24; i > 6; --i) { + if (d*color*board[i] > 0) return false; + } + return true; + } + return false; +} + +/* + * This function tries to determine the field cell under the point p. + * The point needs to be in board coordinates and the function returns + * a pointer to the cell or NULL if there is no cell under the point. + */ + +KBgBoardCell* KBgBoard::getCellByPos(const QPoint& p) const +{ + for (int i = 0; i < 30; ++i) { + if (cells[i]->rect().contains(cells[i]->mapFromParent(p))) + return cells[i]; + } + return NULL; +} + +/* + * This function takes a board number (1 to 24 and 0 or 25 depending on the + * direction) or a cell ID and returns a pointer to the corresponding cell. + * If the cell cannot be found it returns NULL. + */ +KBgBoardCell* KBgBoard::getCell(int num) +{ + switch (num) { + case BAR_US: + return (KBgBoardCell *)cells[22]; + case BAR_THEM: + return (KBgBoardCell *)cells[ 7]; + case HOME_THEM_LEFT: + return (KBgBoardCell *)cells[ 0]; + case HOME_THEM_RIGHT: + return (KBgBoardCell *)cells[14]; + case HOME_US_LEFT: + return (KBgBoardCell *)cells[15]; + case HOME_US_RIGHT: + return (KBgBoardCell *)cells[29]; + default: + int cell; + if (num < 0 || num > 25) + return NULL; + else if (num < 7) + cell = ((direction > 0) ? num : 29 - num); + else if (num < 13) + cell = ((direction > 0) ? num + 1 : 28 - num); + else if (num < 19) + cell = ((direction > 0) ? 41 - num : num - 12); + else + cell = ((direction > 0) ? 40 - num : num - 11); + return (KBgBoardCell *)cells[cell]; + } +} + +/* + * This function translates a field ID to the field number or just + * returns the ID for bars and homes. + */ +int KBgBoard::IDtoNum(const int ID) const +{ + if (ID > 0 && ID < 25) { + if (ID < 13) + return ((direction > 0) ? ID : 12 + ID); + else + return ((direction > 0) ? 37 - ID : 25 - ID); + } + return ID; +} + +/* + * This function takes a checker from the cell if possible. It also + * updates the bookkeeping of the board and redraws itself. + */ +bool KBgBoardCell::getPiece() +{ + if (pcs != 0) { + ((pcs > 0) ? --pcs : ++pcs); + stateChanged = true; + refresh(); + board->updateField(getNumber(), pcs); + return true; + } + return false; +} + +/* + * This function stores the current cursor and replaces it with the + * supplied one c. + */ +void KBgBoard::replaceCursor(const QCursor& c) +{ + if (savedCursor) + delete savedCursor; + savedCursor = new QCursor(cursor()); + setCursor(c); +} + +/* + * This function restores the previously set cursor to the stored one. + */ +void KBgBoard::restoreCursor() +{ + if (savedCursor) { + setCursor(*savedCursor); + delete savedCursor; + savedCursor = NULL; + } +} + +/* + * This function puts a checker of color newColor on the cell. It handles + * all necessary updates including the kicking. It will however not properly + * handle illegal moves! + */ +void KBgBoardCell::putPiece(int newColor) +{ + if (newColor*pcs > 0) { + pcs > 0 ? ++pcs : --pcs; + } else if (pcs == 0) { + newColor > 0 ? pcs = 1 : pcs = -1; + } else if (newColor*pcs < 0) { + board->kickedPiece(); + newColor > 0 ? pcs = 1 : pcs = -1; + } + stateChanged = true; + refresh(); + board->updateField(getNumber(), pcs); + board->sendMove(); +} + +/* + * This function handles mouse release events. It is important to know that + * the cell where the first mousePressEvent occurred receives the release event. + * The release event marks the end of a drag or a single click short move. + */ +void KBgBoardCell::mouseReleaseEvent(QMouseEvent *e) +{ + if (dragInProgress) { + + KBgBoardCell *dest = board->getCellByPos + (mapToParent(e->pos())); + board->restoreCursor(); + if ((dest != NULL) && (dest->dropPossible(cellID, ((board->getTurn() == US) ? + color : -color)))) { + if (!board->getEditMode()) + board->makeMove(getNumber(), dest->getNumber()); + dest->putPiece(((board->getTurn() == US) ? color : -color)); + } else { + putPiece(((board->getTurn() == US) ? color : -color)); + } + dragInProgress = false; + + } else { + + checkAndMakeShortMove(e, SHORT_MOVE_SINGLE); + + } +} + +/* + * This is the destructor of the home cells. It doesn't do anything. + */ +KBgBoardHome::~KBgBoardHome() +{ + // nothing +} + +/* + * This is the destructor of the bar cells. It doesn't do anything. + */ +KBgBoardBar::~KBgBoardBar() +{ + // nothing +} + +/* + * This is the destructor of regular fields. It doesn't do anything. + */ +KBgBoardField::~KBgBoardField() +{ + // nothing +} + +/* + * This is the constructor of the bars. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardBar::KBgBoardBar(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This is the bar of the backgammon board.\n\n" + "Checkers that have been kicked from the board are put " + "on the bar and remain there until they can be put back " + "on the board. Checkers can be moved by dragging them to " + "their destination or by using the 'short move' feature.\n\n" + "If the cube hasn't been doubled yet and if it can be used, " + "its face shows 64 and if the cube can be doubled, double " + "clicking it will do so.")); +} + +/* + * This is the constructor of regular fields. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardField::KBgBoardField(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This is a regular field of the backgammon board.\n\n" + "Checkers can be placed on this field and if the current state " + "of the game and the dice permit this, they can be moved by " + "dragging them to their destination or by using the 'short " + "move' feature.")); +} + +/* + * This is the constructor of the homes. It calls the base class' constructor + * and defines the QWhatsThis string. + */ +KBgBoardHome::KBgBoardHome(QWidget * parent, int numID) + : KBgBoardCell(parent, numID) +{ + QWhatsThis::add(this, i18n("This part of the backgammon board is the home.\n\n" + "Depending on the direction of the game, one of the homes " + "contains the dice and the other one contains checkers that " + "have been moved off the board. Checkers can never be moved " + "away from the home. If this home contains the dice and the " + "current state of the game permits this, double clicking on " + "the dice will roll them. Moreover, the cube might be placed " + "on the home bar and if it can be doubled, double clicking it " + "will do so.")); + savedDice[0] = -1; + savedDice[1] = -1; +} + +/* + * This function updates the number of checkers on the bar and also updates + * the cell if the cube has changed (this is more often than necessary...) + */ +void KBgBoardBar::cellUpdate(const int p, const bool cubechanged) +{ + stateChanged = (cubechanged || colorChanged); + if (pcs != p) { + stateChanged = true; + pcs = p; + } +} + +/* + * This function updates the number of checkers on the field. + */ +void KBgBoardField::cellUpdate(const int p, const bool cubechanged) +{ + if (p != pcs) { + pcs = p; + stateChanged = true; + } + bool f = stateChanged; // useless, avoids compiler warning + stateChanged = cubechanged; + stateChanged = (f || colorChanged); +} + +/* + * This function updates the number of checkers on the home if it + * actually contains checkers. It will also redraw if the cube or dice + * have changed. + */ +void KBgBoardHome::cellUpdate(const int p, const bool cubechanged) +{ + if ((cellID == HOME_THEM_LEFT && direction > 0) || + (cellID == HOME_THEM_RIGHT && direction < 0) || + (cellID == HOME_US_LEFT && direction > 0) || + (cellID == HOME_US_RIGHT && direction < 0)) { + + if (pcs != p) { + pcs = p; + stateChanged = true; + } + + } else { + + int who = ((cellID == HOME_THEM_LEFT || cellID == HOME_THEM_RIGHT) ? THEM : US); + + stateChanged = ((savedDice[0] != board->getDice(who, 0)) || + (savedDice[1] != board->getDice(who, 1))); + + savedDice[0] = board->getDice(who, 0); + savedDice[1] = board->getDice(who, 1); + + stateChanged = (stateChanged || cubechanged || colorChanged || directionChanged); + } +} + +/* + * This function returns whose players turn it is. + */ +int KBgBoard::getTurn() const +{ + if (getEditMode()) + return ((storedTurn*color > 0) ? US : THEM); + + if (getDice(US , 0) != 0 && getDice(US , 1) != 0) + return US; + if (getDice(THEM, 0) != 0 && getDice(THEM, 1) != 0) + return THEM; + return -1; +} + +/* + * This is the constructor of the basic cells. It initializes the cell + * to a sane state and connects a signal to the board. + */ +KBgBoardCell::KBgBoardCell(QWidget * parent, int numID) + : QLabel(parent) +{ + board = (KBgBoard *)parent; + + direction = +1; + color = -1; + pcs = 0; + cellID = numID; + stateChanged = false; + colorChanged = false; + directionChanged = false; + mouseButton = NoButton; + dragInProgress = false; + + connect(parent, SIGNAL(finishedUpdate()), this, SLOT(refresh())); +} + +/* + * This is the destructor of the cells. It doesn't do anything. + */ +KBgBoardCell::~KBgBoardCell() +{ + // nothing +} + +/* + * This function returns the color of the checkers on this cell. + */ +int KBgBoardCell::getCellColor() +{ + return ((pcs < 0) ? -1 : +1); +} + +/* + * This function updates the basic board settings color and direction + * and signals a redraw if necessary. + */ +void KBgBoardCell::statusUpdate(int dir, int col) +{ + if (direction != dir || color != col) { + colorChanged = (color != col); + directionChanged = (direction != dir); + color = col; + direction = dir; + stateChanged = true; + } +} + +/* + * This function refreshes the content of the cell if necessary. + */ +void KBgBoardCell::refresh() +{ + if (stateChanged) { + update(); + stateChanged = false; + colorChanged = false; + directionChanged = false; + } +} + +/* + * This function returns the board number of this cell as given by the board. + */ +int KBgBoardCell::getNumber() const +{ + return board->IDtoNum(cellID); +} + +/* + * This function returns the number of checkers of player who on the bar. + */ +int KBgBoard::getOnBar(int who) const +{ + return ((who == US || who == THEM) ? onbar[who] : 0); +} + +/* + * This function returns the face value of the n-th dice of player w + */ +int KBgBoard::getDice( int w, int n ) const +{ + return (((w == US || w == THEM) && (n == 0 || n == 1)) ? dice[w][n] : 0); +} + +/* + * This function returns the current cube value. + */ +int KBgBoard::getCube() const +{ + return cube; +} + +/* + * This function updates the stored number of pieces on field f to v. + */ +void KBgBoard::updateField(int f, int v) +{ + switch (f) { + case BAR_US: + case BAR_THEM: + onbar[((f == BAR_US) ? US : THEM)] = v; + break; + case HOME_US_RIGHT: + case HOME_US_LEFT: + onhome[US] = v; + break; + case HOME_THEM_RIGHT: + case HOME_THEM_LEFT: + onhome[THEM] = v; + break; + default: + if (0 < f && f < 25) + board[f] = v; + break; + } +} + +/* + * This function displays the context menu our parent may have given us + */ +void KBgBoard::showContextMenu() +{ + if (contextMenu) contextMenu->popup(QCursor::pos()); +} + +/* + * This function determines if the player who can double. + */ +bool KBgBoard::canDouble(int who) const +{ + return ((who == US || who == THEM) ? maydouble[who] : false); +} + +/* + * This function is a simple utility for makeMove. It takes care + * of all the bookeeeping needed for a move. + */ +int KBgBoard::makeMoveHelper(int si, int sf, int delta) +{ + moveHistory.append(new KBgBoardMove(si, sf, abs(delta))); + --possMoves[abs(delta)]; + return delta; +} + +/* + * This function makes a move from src to dest for the current player. + * It can handle illegal moves but the move should have been checked. + */ +void KBgBoard::makeMove(int src, int dest) +{ + int m[4]; + int l; + + int d = direction*((getTurn() == US) ? +1 : -1); + + if (src == BAR_US || src == BAR_THEM ) { + + int start = ((d > 0) ? 0 : 25); + l = checkMultiMove(start, dest, m); + moveHistory.append(new KBgBoardMove(src, start+d*m[0], m[0])); + src = start+d*m[0]; + --possMoves[m[0]]; + for (int i = 1; i < l; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + } else if (0 < src && src < 25 && 0 < dest && dest < 25) { + + l = checkMultiMove(src, dest, m); + for (int i = 0; i < l; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + } else { + + int s = src; + int final = ((d > 0) ? 25 : 0); + while (((l = checkMultiMove(s, final, m)) == 0) && (0 < s && s < 25)) + s -= d; + + for (int i = 0; i < l-1; i++) + src += makeMoveHelper(src, src+d*m[i], d*m[i]); + + moveHistory.append(new KBgBoardMove(src, dest, ((d > 0) ? 25 - src : src))); + --possMoves[m[l-1]]; + + } +} + +/* + * This function checks if there is any possibility (based on the dice) + * to move from src to dest. It takes the ownership of the intermediate + * fields into account. The function returns the number of steps necessary + * to perform the move (or 0 if the move is not possible) and the actual + * dice values used for the steps are returned in the array m. + * + * The values src and dest are expected to be in board coordinates and the + * homes and/or bars should already be mapped to the corresponding values + * 0 and 25 (based onb direction and whose turn it is). + */ +int KBgBoard::checkMultiMove(int src, int dest, int m[4]) +{ + m[0] = 0; m[1] = 0; m[2] = 0; m[3] = 0; + + int mcolor = ((getTurn() == US) ? color : -color); + int d = ((src > dest) ? -1 : 1); + + /* + * These are very easy special cases: move length is 0 or + * player cannot move to the destination field. + */ + if ((src == dest) || (mcolor*board[dest] < -1)) return 0; + + int diceToUse[4]; + int dice = 0; + /* + * Get the available step sizes for this move + */ + for (int i = 1; i < 7; i++) { + for (int j = 0; j < possMoves[i]; j++) { + diceToUse[dice++] = i; + /* + * If this happens there is something wrong + */ + if (dice > 4) return 0; + } + } + /* + * And start all possible combination of dices. + */ + switch (dice) { + case 4: if (src+4*d*diceToUse[0] == dest) { + if ((mcolor*board[src+1*d*diceToUse[0]] >= 0) && + (mcolor*board[src+2*d*diceToUse[0]] >= 0) && + (mcolor*board[src+3*d*diceToUse[0]] >= 0)) { + m[0] = m[1] = m[2] = m[3] = diceToUse[0]; + return 4; + } + } + case 3: if (src+3*d*diceToUse[0] == dest) { + if ((mcolor*board[src+1*d*diceToUse[0]] >= 0) && + (mcolor*board[src+2*d*diceToUse[0]] >= 0)) { + m[0] = m[1] = m[2] = diceToUse[0]; + return 3; + } + } + case 2: if ((src+d*(diceToUse[0]+diceToUse[1])) == dest) { + if (mcolor*board[src+d*diceToUse[0]] >= 0) { + m[0] = diceToUse[0]; + m[1] = diceToUse[1]; + return 2; + } + if (mcolor*board[src+d*diceToUse[1]] >= 0) { + m[0] = diceToUse[1]; + m[1] = diceToUse[0]; + return 2; + } + } + case 1: if (abs(src-dest) < 7 && possMoves[abs(src-dest)] > 0) { + m[0] = abs(src-dest); + return 1; + } + default: return 0; + } +} + +/* + * This function determines if a checker can be dropped on this home field. + * It first checks whether this is the proper of the four home fields (belongs + * to the player and not the one with dice and cube). Then we check if the move + * itself is possible. + */ +bool KBgBoardHome::dropPossible(int fromCellID, int newColor) +{ + if ((cellID==HOME_US_LEFT && board->getTurn() == US && direction > 0) || + (cellID==HOME_THEM_LEFT && board->getTurn() == THEM && direction > 0) || + (cellID==HOME_US_RIGHT && board->getTurn() == US && direction < 0) || + (cellID==HOME_THEM_RIGHT && board->getTurn() == THEM && direction < 0)) + return (board->moveOffPossible() && + board->diceAllowMove(fromCellID, cellID)); + return (newColor == -12345); // always false +} + +/* + * This function is a simple boolean interface to checkMultiMove. + * It takes car of directions and bar/home mappings. If necessary + * it also handles the case of bearing off. + */ +bool KBgBoard::diceAllowMove(int src, int dest) +{ + int m[4]; + int w = getTurn(); + int k = ((w == US) ? +1 : -1); + int t = ((k*direction > 0) ? 25 : 0); + int d = ((k*direction > 0) ? +1 : -1); + + if (getEditMode()) + return true; + + if ((w == US && src == BAR_US) || (w == THEM && src == BAR_THEM)) { + /* + * Move comes from a bar. Hence it has to end on a field + * and not on bars or homes. If there are checkers left + * on the bar we don't accept multi moves. + */ + if (0 < dest && dest < 25) { + int r = checkMultiMove((k*direction > 0) ? 0 : 25, + IDtoNum(dest), m); + return((abs(onbar[w]) == 0) ? (r != 0) : (r == 1)); + } else { + return false; + } + } else if (0 < dest && dest < 25 && 0 < src && src < 25) { + /* + * Move from a field to a field + */ + if (direction*k*(IDtoNum(dest)-IDtoNum(src)) > 0) { + return(checkMultiMove(IDtoNum(src), IDtoNum(dest), m)); + } else { + return false; + } + } else { + /* + * Move from a field on the home. First we try exact dice. + */ + if (checkMultiMove(IDtoNum(src), t, m) > 0) return true; + + /* + * Then maybe we could bear the checker off ? + */ + int i = IDtoNum(src); + while (0 < i && i < 25) { + i -= d; + if (k*color*board[i] > 0) return false; + } + + /* + * Indeed we are bearing off. So find the highest dice and use it. + * Start from all the way back to catch double 6 from the start. + */ + int j = 24; + while (checkMultiMove(t-d*j, t, m) == 0 && j > 0) {--j;} + return (j >= t-d*IDtoNum(src)); + } + return false; +} + +/* + * This is the most important of all members of the board class. It takes + * a single board status object and initializes the internal status. + */ +void KBgBoard::setState(const KBgStatus &st) +{ + color = st.color(); + direction = st.direction(); + + cubechanged = (cube != abs(st.cube())); + cube = abs(st.cube()); + maydouble[US ] = (st.cube(US ) > 0); + maydouble[THEM] = (st.cube(THEM) > 0); + + for (int i = 0; i < 30; i++) + cells[i]->statusUpdate(direction, color); + + for (int i = 1; i < 25; ++i) + board[i] = st.board(i); + + onbar[US ] = st.bar(US ); + onbar[THEM] = st.bar(THEM); + + onhome[US] = st.home(US ); + onhome[THEM] = st.home(THEM); + + dice[US ][0] = st.dice(US , 0); + dice[US ][1] = st.dice(US , 1); + dice[THEM][0] = st.dice(THEM, 0); + dice[THEM][1] = st.dice(THEM, 1); + + for (int i = 0; i < 7; ++i) + possMoves[i] = 0; + + int w = getTurn(); + if (getEditMode()) + w = ((dice[US][0] && dice[US][1]) ? US : THEM); + + if (w == US || w == THEM) { + ++possMoves[dice[w][0]]; + ++possMoves[dice[w][1]]; + if (dice[w][0] == dice[w][1]) + possMoves[dice[w][0]] *= 2; + } + + board[ 0] = 0; + board[25] = 0; + for (int i=1; i<25; ++i) + (getCell(i))->cellUpdate(board[i]); + + (getCell(BAR_US ))->cellUpdate(st.bar(US ), cubechanged); + (getCell(BAR_THEM))->cellUpdate(st.bar(THEM), cubechanged); + + (getCell(HOME_US_LEFT ))->cellUpdate(st.home(US ), cubechanged); + (getCell(HOME_US_RIGHT ))->cellUpdate(st.home(US ), cubechanged); + (getCell(HOME_THEM_LEFT ))->cellUpdate(st.home(THEM), cubechanged); + (getCell(HOME_THEM_RIGHT))->cellUpdate(st.home(THEM), cubechanged); + + moveHistory.clear(); + redoHistory.clear(); + + emit finishedUpdate(); +} + +/* + * This function starts a drag from this cell if possible. It asks the board to + * change the mouse pointer and takes a checker away from this cell. + */ +void KBgBoardCell::mouseMoveEvent(QMouseEvent *) +{ + if ((mouseButton == LeftButton) && dragPossible()) { + dragInProgress = true; + QRect cr(0, 0, 1+getCheckerDiameter(), 1+getCheckerDiameter()); + cr.moveBottomLeft(rect().bottomLeft()); + QPixmap pix(cr.size()); + QPainter tmp; + pix.fill(this, cr.topLeft()); + tmp.begin(&pix); + board->drawSimpleChecker(&tmp, 0, 0, pcs, getCheckerDiameter()); + tmp.end(); + pix.setMask(pix.createHeuristicMask()); + QBitmap mask = *(pix.mask()); + QBitmap newCursor; + newCursor = pix; + board->replaceCursor(QCursor(newCursor, mask)); + if (board->getEditMode()) + board->storeTurn(pcs); + getPiece(); + } + mouseButton = NoButton; +} + +/* + * This function draws a checker on the painter p. It is painted + * in the ractangle with the upper left corner (x,y) and has a + * maximum diameter of diam. This checker has only two colors and + * as such it is suited for the mouse cursor and printing. + */ +void KBgBoard::drawSimpleChecker(QPainter *p, int x, int y, int pcs, + int diam) const +{ + p->setBrush(getCheckerColor(pcs)); + p->setPen(getCheckerColor(pcs)); + p->drawEllipse(x+1, y+0, diam-0, diam-0); + p->setBrush(getCheckerColor(-pcs)); + p->setPen(getCheckerColor(-pcs)); + p->drawEllipse(x+2, y+1, diam-2, diam-2); + p->setBrush(getCheckerColor(pcs)); + p->setPen(getCheckerColor(pcs)); + p->drawEllipse(x+3, y+2, diam-4, diam-4); +} + +/* + * This function draws an anti-aliased checker on the painter p. It + * is painted in the ractangle with the upper left corner (x,y) and + * has a diameter of diam. col indicates the color of the cell this + * checker is painted on. Special values for col are 0 and 100 that + * indicate that the checker is stacked (bars and homes) or stacked + * on a field respectively. upper indicates whether the checker is + * in the upper half of the board or not. + */ +void KBgBoard::drawChecker(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper) const +{ + drawCircle(p, x, y, pcs, diam , col, upper, true ); + drawCircle(p, x+1, y+1,-pcs, diam-2, col, upper, false); + drawCircle(p, x+2, y+2, pcs, diam-4, col, upper, false); +} + +/* + * This function draws checkers on the painter *p. They overlap so that + * up to fifteen checkers fit on the cell. This is used by homes and + * bars. + */ +void KBgBoardCell::drawOverlappingCheckers(QPainter *p, int xo, int yo, + double sf) const +{ + int d = getCheckerDiameter(); + bool upper = + cellID == HOME_THEM_LEFT || + cellID == HOME_THEM_RIGHT || + cellID == BAR_THEM; + double xp = xo + sf*((width()-d-1)/2); + double ra = sf*d; + for (int i = 0; i < abs(pcs); ++i) { + double yp = yo + (upper ? 1+i*sf*height()/25.0 : + sf*(height()-d-i*height()/25.0)); + board->drawChecker(p, xp, yp, pcs, ra, 0, upper); + } +} + +/* + * This function paints the content of a regular cell on the painter p. + * It does so by first drawing a triangle (depending on whether we draw + * on the screen or not this will be antialiased). Then on top of that + * we draw the field number in inverse color. Finally we draw all the + * checkers in such a way that always five are in one level and the next + * level is slightly shifted. + */ +void KBgBoardField::paintCell(QPainter *p, int xo, int yo, double sf) const +{ + QColor color, alphaColor, background = backgroundColor(); + bool printing = abs(xo)+abs(yo) > 0; + + if (printing) { + /* + * This is the code for black and white printing on + * paper. This justs draws a triangle and surrounds + * it by a black triangle. Easy but works. + */ + QPointArray pa(3); + + color = (getNumber()%2 ? white : black); + + if (cellID < 13) { + pa.setPoint( 0, xo , yo ); + pa.setPoint( 1, xo + sf*width()/2, yo + 0.9*sf*height()); + pa.setPoint( 2, xo + sf*width() , yo ); + } else { + pa.setPoint( 0, xo , yo + sf*(height()-1)); + pa.setPoint( 1, xo + sf*width()/2, yo + 0.1*sf*height()); + pa.setPoint( 2, xo + sf*width() , yo + sf*(height()-1)); + } + + p->setBrush(color); + p->setPen(color); + p->drawPolygon(pa); + + p->setBrush(black); + p->setPen(black); + p->drawPolyline(pa); + + } else { + /* + * This is the code for antialiased triangles. This code has + * been written by Bo Thorsen. + */ + color = board->getCheckerColor(getNumber()%2-1); + + int topX, topY, bottomX1, bottomX2, bottomY, incrY; + topX = xo + (int)(sf*width()/2.0); + bottomX1 = xo; + bottomX2 = xo + (int)(sf*width()); + if (cellID < 13) { + topY = yo + (int)(0.9*sf*height()); + bottomY = yo; + incrY = 1; + } else { + topY = yo + (int)(0.1*sf*height()); + bottomY = yo + (int)(sf*height()); + incrY = -1; + } + + float x1 = bottomX1, x2 = bottomX2; + float dx1 = (float)(topX-bottomX1) / (topY-bottomY); + float dx2 = (float)(topX-bottomX2) / (topY-bottomY); + if (dx1 < 0) dx1 = -dx1; + if (dx2 < 0) dx2 = -dx2; + + p->setPen( color ); + p->drawLine(bottomX1, bottomY, bottomX2, bottomY); + x1 += dx1; + x2 -= dx2; + + /* + * The scaling factor (0.99) cuts off the top op the points + */ + for (int y=bottomY; x1 < x2*0.99; y+=incrY) { + int ix1 = (int)x1, ix2 = (int)x2; + float a1 = x1 - ix1, a2 = x2 - ix2; + + /* + * This is a simple linear interpolation between + * the two colors + */ + int red1 = (int) + ((1-a1)*color.red() + a1*background.red()); + int green1 = (int) + ((1-a1)*color.green() + a1*background.green()); + int blue1 = (int) + ((1-a1)*color.blue() + a1*background.blue()); + int red2 = (int) + (a2*color.red() + (1-a2)*background.red()); + int green2 = (int) + (a2*color.green() + (1-a2)*background.green()); + int blue2 = (int) + (a2*color.blue() + (1-a2)*background.blue()); + + /* + * Draw the antialiasing pixels + */ + alphaColor.setRgb(red1, green1, blue1); + p->setPen(alphaColor); + p->drawPoint(ix1, y); + alphaColor.setRgb(red2, green2, blue2); + p->setPen(alphaColor); + p->drawPoint(ix2, y); + + ix1++; + ix2--; + x1 += dx1; + x2 -= dx2; + + if (ix1 <= ix2 && x1 < x2*0.99) { + /* + * Draw the line + */ + p->setPen(color); + p->drawLine(ix1, y, ix2, y); + } + } + } + + /* + * Print the field number in inverted color + */ + color = board->getCheckerColor((1+getNumber())%2-1); + + p->setBrush(color); + p->setPen(color); + + QString t; + t.setNum(getNumber()); + + p->setFont(board->getFont()); + int textHeight = QFontMetrics(p->font()).height(); + p->drawText(xo, yo+((cellID < 13) ? 5 : height()-5-textHeight), + width()*sf, textHeight, AlignCenter, t); + + /* + * Put the checkers on the field. + */ + int d = getCheckerDiameter(); + double yp, xp = xo + sf*((width()-d-1)/2); + double ra = sf*d; + bool upper = cellID < 13; + int col = (getNumber()%2) ? 1 : -1; + + for (int i = 0; i < abs(pcs); ++i) { + /* + * There is hard work in these formulas. Unless you have + * tried _ALL_ possible windowsizes: don't touch! + */ + yp = yo + (upper ? sf*((i%5)+(i/5)/4.0)*(d-1) : + sf*(height()-((1+i%5)*d)-int(i/5)*0.25*d)-1); + if (printing) { + board->drawSimpleChecker(p, xp, yp, pcs, ra); + } else { + board->drawChecker(p, xp, yp, pcs, ra, + ((i < 5) ? col : 100), upper); + } + } + + /* + * Finally draw the horizontal boundaries + */ + KBgBoardCell::paintCell(p, xo, yo, sf); +} + +/* + * This function draws an anti-aliased circle on the painter p. It is painted + * in the ractangle with the upper left corner (x,y) and has a maximum diameter + * of diam. col and upper are as in drawChecker(). outer indicates if this + * circle blends with the background. Note that this function needs knowledge + * about the triangles on the cells. This is long but it is just a big if + * construct. + */ +void KBgBoard::drawCircle(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper, bool outer) const +{ + QColor fColor = getCheckerColor(pcs); + QColor alphaColor; + QColor bColor; + + int red, green, blue; + int rad = diam/2; + int xoff = 0; + + float sn = 4; + float rs = 0.25*diam*diam; + float cf, a; + + for (int ys = rad; ys >= 0; ys--) { + for (int xs = xoff; cf = 0, xs < rad; xs++) { + + /* + * perform super-sample this pixel + */ + for (int s1 = 0; s1 < sn; s1++) + for (int s2 = 0; s2 < sn; s2++) + if ((rad-xs+s1/sn)*(rad-xs+s1/sn)+ + (rad-ys+s2/sn)*(rad-ys+s2/sn) < rs) + cf += 1; + a = cf/sn/sn; + + if (outer && (col == 0 || col == 100)) { + + if (col == 0) + bColor = backgroundColor(); + else + bColor = fColor; + + red = (int) + ((1-a)*bColor.red()+a*fColor.red()); + green = (int) + ((1-a)*bColor.green()+a*fColor.green()); + blue = (int) + ((1-a)*bColor.blue()+a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (upper) { + + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + p->setBrush(fColor); + p->setPen(fColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + + } else { + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + + p->setBrush(fColor); + p->setPen(fColor); + + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + } + + } else if (outer) { + + if (upper) { + + bColor = getCheckerColor(col); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + bColor = backgroundColor(); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (x+xs < rad*(y+ys)/(0.45*height())) { + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + } + if (x+xsdrawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + } + + } else { + + bColor = getCheckerColor(col); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + bColor = backgroundColor(); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + if (x+xsdrawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + } + if (x+xs < rad*(0.5-(y+diam-ys)/ + (1.0*height()))/0.45) { + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + } + } + + } else { + + bColor = getCheckerColor(-pcs); + + red = (int)((1-a)*bColor.red()+ + a*fColor.red()); + green = (int)((1-a)*bColor.green()+ + a*fColor.green()); + blue = (int)((1-a)*bColor.blue()+ + a*fColor.blue()); + + alphaColor.setRgb(red, green, blue); + p->setBrush(alphaColor); + p->setPen(alphaColor); + + p->drawPoint(x+xs, y+ys); + p->drawPoint(x+diam-xs, y+ys); + p->drawPoint(x+xs, y+diam-ys); + p->drawPoint(x+diam-xs, y+diam-ys); + + } + + if (fabs(cf-sn*sn) < 0.0001) { + + p->moveTo(x+xs, y+ys); + p->lineTo(x+diam-xs, y+ys); + p->moveTo(x+xs, y+diam-ys); + p->lineTo(x+diam-xs, y+diam-ys); + + xoff = xs; + break; + + } + } + } +} + +/* + * This function redoes a previously undone move + */ +void KBgBoard::redoMove() +{ + if (getEditMode()) + return; + + int w = getTurn(); + int mcolor = ((w == US) ? color : -color); + KBgBoardMove *move = redoHistory.last(); + if (move && (w == US || w == THEM)) { + /* + * Make changes at source + */ + if (move->source() == BAR_US || move->source() == BAR_THEM) { + onbar[w] -= mcolor; + (getCell(move->source()))->cellUpdate(onbar[w], false); + } else { + board[move->source()] -= mcolor; + (getCell(move->source()))->cellUpdate(board[move->source()]); + } + /* + * Make changes at the destination + */ + if ((move->destination() == HOME_THEM_LEFT ) || (move->destination() == HOME_THEM_RIGHT) || + (move->destination() == HOME_US_LEFT ) || (move->destination() == HOME_US_RIGHT )) { + onhome[w] += mcolor; + (getCell(move->destination()))->cellUpdate(onhome[w], false); + } else { + board[move->destination()] += mcolor; + if (move->wasKicked()) { + board[move->destination()] = mcolor; + onbar[((w == US) ? THEM : US)] -= mcolor; + (getCell(((w == US) ? BAR_THEM : BAR_US)))->cellUpdate + (onbar[((w == US) ? THEM : US)], false); + } + (getCell(move->destination()))->cellUpdate(board[move->destination()]); + } + makeMove(move->source(), move->destination()); + redoHistory.remove(); + emit finishedUpdate(); + } + sendMove(); +} + +/* + * This function performs and undo for the last move and updates the parent + * of the board by calling sendMove() after the undo. + */ +void KBgBoard::undoMove() +{ + if (getEditMode()) + return; + + int w = getTurn(); + int mcolor = ((w == US) ? color : -color); + KBgBoardMove *move = moveHistory.last(); + if (move && (w == US || w == THEM)) { + /* + * Undo changes at source + */ + if (move->source() == BAR_US || move->source() == BAR_THEM) { + onbar[w] += mcolor; + (getCell(move->source()))->cellUpdate(onbar[w], false); + } else { + board[move->source()] += mcolor; + (getCell(move->source()))->cellUpdate + (board[move->source()]); + } + /* + * Undo changes at the destination + */ + if ( (move->destination() == HOME_THEM_LEFT ) || + (move->destination() == HOME_THEM_RIGHT) || + (move->destination() == HOME_US_LEFT ) || + (move->destination() == HOME_US_RIGHT )) { + onhome[w] -= mcolor; + (getCell(move->destination()))->cellUpdate + (onhome[w], false); + } else { + board[move->destination()] -= mcolor; + if (move->wasKicked()) { + board[move->destination()] = -mcolor; + onbar[((w == US) ? THEM : US)] += mcolor; + (getCell(((w == US) ? + BAR_THEM : BAR_US)))->cellUpdate + (onbar[((w == US) ? THEM : US)], false); + } + (getCell(move->destination()))->cellUpdate + (board[move->destination()]); + } + ++possMoves[move->length()]; + redoHistory.append(new KBgBoardMove(*move)); + moveHistory.remove(); + emit finishedUpdate(); + } + sendMove(); +} + +/* + * While putting a piece on a cell the cell has noticed that it changed + * ownership and hence needs a piece to be kicked. Since cells don't + * know where the opponents bar is we handle this here. + */ +void KBgBoard::kickedPiece() +{ + int w = ((getTurn()) == US ? THEM : US); + + if (w == US) { + onbar[w] += color; + (getCell(BAR_US ))->cellUpdate(onbar[w], false); + } else { + onbar[w] -= color; + (getCell(BAR_THEM))->cellUpdate(onbar[w], false); + } + if (!getEditMode()) { + KBgBoardMove *move = moveHistory.last(); + move->setKicked(true); + } + emit finishedUpdate(); +} + +/* + * This is a very short utility function for makeShortMove(). + */ +void KBgBoardCell::makeShortMoveHelper(int s, int d) +{ + if (getPiece()) { + board->makeMove(s, d); + KBgBoardCell *dest = board->getCell(d); + dest->putPiece(((board->getTurn() == US) ? color : -color)); + } +} + +/* + * This function makes the shortes possible move from this cell. It + * uses only one dice and and it will kick opponent checkers. + */ +void KBgBoardCell::makeShortMove() +{ + int m[4]; + + int dir = ((board->getTurn() == US) ? direction : -direction); + int src = board->IDtoNum(cellID); + + if (src == BAR_US || src == BAR_THEM) { + + int s = (dir > 0) ? 0 : 25; + for (int i = 1; i < 7; i++) { + int d = (dir > 0) ? i : 25 - i; + if (board->checkMultiMove(s, d, m) == 1) { + makeShortMoveHelper(src, d); + break; + } + } + + } else { + + for (int i = 1; i < 7; i++) { + int d = src + dir*i; + if (d > 25) d = 25; + if (d < 0) d = 0; + if (0 < d && d < 25) { + + if (board->checkMultiMove(src, d, m) == 1) { + makeShortMoveHelper(src, d); + break; + } + + } else { + + if (board->moveOffPossible()) { + int whichHome; + if (board->getTurn() == US) + whichHome = ((direction > 0) ? + HOME_US_LEFT : + HOME_US_RIGHT); + else + whichHome = ((direction > 0) ? + HOME_THEM_LEFT : + HOME_THEM_RIGHT); + + if (board->diceAllowMove + (cellID, whichHome)) { + makeShortMoveHelper(src, whichHome); + break; + } + } + } + } + } +} + +/* + * Ask the current backgammon engine for a doubled cube. + */ +void KBgBoard::getDoubleCube(const int w) +{ + emit doubleCube(w); +} + +/* + * Ask the current backgammon engine rolling the dice. + */ +void KBgBoard::getRollDice(const int w) +{ + emit rollDice(w); +} + +/* + * This is the constructor of the KBgBoard class. It creates + * a backgammon board with an initial distribution of checkers, empty + * dice and a cube with face value 1. The initial board is not usable! + * You have to change the status by passing a KBgStatus + * object to setState(...) before you can play! + */ +KBgBoard::KBgBoard(QWidget *parent, const char *name, QPopupMenu *menu) + : QWidget(parent, name) +{ + /* + * The following lines set up internal bookkeeping data. + */ + moveHistory.setAutoDelete(true); + redoHistory.setAutoDelete(true); + cube = 1; + allowMoving(true); + setEditMode(false); + savedCursor = NULL; + checkerDiam = MINIMUM_CHECKER_SIZE; + + /* + * We may be initialized with a popup menu by our parent. + */ + contextMenu = menu; + + baseColors[0] = black; + baseColors[1] = white; + + /* + * Get the 30 cells that constitute the board and initialize + * them properly. + */ + cells[ 0] = new KBgBoardHome(this, HOME_THEM_LEFT); + cells[14] = new KBgBoardHome(this, HOME_THEM_RIGHT); + cells[15] = new KBgBoardHome(this, HOME_US_LEFT); + cells[29] = new KBgBoardHome(this, HOME_US_RIGHT); + + cells[ 7] = new KBgBoardBar(this, BAR_THEM); + cells[22] = new KBgBoardBar(this, BAR_US); + + for (int i=1; i<7; ++i) { + cells[ i] = new KBgBoardField(this, i); + cells[ 7+i] = new KBgBoardField(this, 6+i); + cells[15+i] = new KBgBoardField(this, 12+i); + cells[22+i] = new KBgBoardField(this, 18+i); + } + + /* + * Get the default seeting of the board and initialize the + * state of it. + */ + KBgStatus *st = new KBgStatus(); + + st->setCube(1, true, true); + st->setDirection(+1); + st->setColor(+1); + + st->setBoard( 1, US, 2); st->setBoard( 6, THEM, 5); + st->setBoard( 8, THEM, 3); st->setBoard(12, US, 5); + st->setBoard(13, THEM, 5); st->setBoard(17, US, 3); + st->setBoard(19, US, 5); st->setBoard(24, THEM, 2); + + st->setHome(US, 0); + + st->setDice(US , 0, 0); st->setDice(US , 1, 0); + st->setDice(THEM, 0, 0); st->setDice(THEM, 1, 0); + + setState(*st); + + delete st; + + /* + * This line simplifies the checkMultiMove(...) function a lot. + */ + board[0] = board[25] = 0; + + /* + * User interface design settings come here. These may be + * overwritten by the user. + */ + shortMoveMode = SHORT_MOVE_DOUBLE; + setBackgroundColor(QColor(200, 200, 166)); + computePipCount = true; + + /* + * Set initial font + */ + setFont(QApplication::font()); +} + +QSize KBgBoard::minimumSizeHint() const +{ + return QSize(MINIMUM_CHECKER_SIZE * 15, MINIMUM_CHECKER_SIZE * 11); +} + +QSize KBgBoard::sizeHint() const { + return QSize(MINIMUM_CHECKER_SIZE *15*4,MINIMUM_CHECKER_SIZE*11*2); +} + diff --git a/kbackgammon/kbgboard.h b/kbackgammon/kbgboard.h new file mode 100644 index 00000000..e2f35f68 --- /dev/null +++ b/kbackgammon/kbgboard.h @@ -0,0 +1,967 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +/* + + This file contains the declaration and definition of a couple of + classes related to the KBgBoard class. + + Effort has been made to keep this class general. Please comment on that + if you want to use it in your own project. Most of the stuff is private + and/or in utility classes that shouldn't be used directly. All public + interfaces are in teh beginning of the file. + +*/ + +#ifndef KBGBOARD_H +#define KBGBOARD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbgstatus.h" + +/* + * Just some internal constants and classes + */ +const int US = KBgStatus::US; +const int THEM = KBgStatus::THEM; +const int BOTH = KBgStatus::BOTH; + +const int HOME_US_LEFT = 101; +const int HOME_US_RIGHT = 102; +const int HOME_THEM_LEFT = 103; +const int HOME_THEM_RIGHT = 104; +const int BAR_US = 105; +const int BAR_THEM = 106; +const int HOME_US = 107; +const int HOME_THEM = 108; + +const int SHORT_MOVE_NONE = 0; +const int SHORT_MOVE_SINGLE = 1; +const int SHORT_MOVE_DOUBLE = 2; + +class KBgBoard; +class KBgBoardCell; +class KBgBoardMove; +class KBgBoardHome; +class KBgBoardBar; +class KBgBoardField; +class KBgBoardQDice; + + +/** + * The KBgBoard class. + * + * This class handles all game operations of the client. + */ +class KBgBoard : public QWidget +{ + Q_OBJECT + + friend class KBgBoardCell; + friend class KBgBoardHome; + friend class KBgBoardBar; + friend class KBgBoardField; + + public: + + /** + * Constructor and destructor. Parameter as usual. + */ + KBgBoard(QWidget *parent = 0, const char *name = 0, + QPopupMenu *menu = 0); + virtual ~KBgBoard(); + + /** + * Returns the current read/write status of the board. + */ + bool movingAllowed() const; + + /** + * Returns the current up-to-the-second pip count (including + * the moves that have been done. + */ + int getPipCount(const int& w) const; + + /** + * Sets the momentary short move mode. The parameter should + * be one of the constanst SHORT_MOVE_NONE, SHORT_MOVE_SINGLE + * or SHORT_MOVE_DOUBLE. This the automatic moving of a checker + * with the shortest possible move away from the current field. + */ + void setShortMoveMode(int m); + + /** + * Returns the current short move mode. + */ + int getShortMoveMode(); + + /** + * Sets the background color and passes the info to the + * child widgets + */ + virtual void setBackgroundColor(const QColor &col); + + /** + * Prints the baord along with some basic info onto the + * painetr p. It is assumed that this painter is a postscript + * printer. Hence the plot is black/white only. + */ + void print(QPainter *p); + + /** + * Get whose turn it is - US, THEM or 0 + */ + int getTurn() const; + + /** + * Allows for overriding the current turn color in edit mode. + */ + void storeTurn(const int pcs); + + /** + * Retrurns the current edit mode status. + */ + bool getEditMode() const; + + /** + * Get a new value for the cube from the user - this opens a dialog + */ + void queryCube(); + + /** + * Get the font the board cells should use for the display of + * numbers and cube value. + */ + QFont getFont() const; + + /** + * This function has to be reimplemented to provide a minimum size for + * the playing area. + */ + QSize minimumSizeHint() const; +public slots: + + /** + * This allows the users of this widget to dis- and enable + * mouse events. In effect this triggers the read/write + * flag of the board. + */ + void allowMoving(const bool fl); + + /** + * Overwritten resize event handler. + * + * We overwrite the handler to make sure that all the cells are + * repainted as well. + */ + virtual void resizeEvent(QResizeEvent *); + + /** + * Undo the last move. + */ + void undoMove(); + + /** + * Redo a previously undone move + */ + void redoMove(); + + /** + * This is the most important public member. It takes + * a board status - s. the KBgBoardStatus class - + * and sets the board accordingly. + */ + void setState(const KBgStatus &); + + /** + * Set the context menu + */ + void setContextMenu(QPopupMenu *menu); + + /** + * Get the current state of the board. + */ + KBgStatus *getState(KBgStatus *st) const; + + /** + * Sets the edit mode of the board. In that mode the board can be + * modified arbitrarily. + */ + void setEditMode(const bool m); + + /** + * Allows the users of the board classe to set the font to be used + * on the board. Note that the fontsize is dynamically set + */ + void setFont(const QFont& f); + + /** + * Write the current configuration to the application's data base + */ + void saveConfig(); + + /** + * Restore the stored configuration or start with reasonable defaults + */ + void readConfig(); + + signals: + + /** + * The text identifies the current game status - could be put + * into the main window caption + */ + void statText(const QString &msg); + + /** + * The cells connect to this signal and it tells them that it is + * time to update their content now if necessary. + */ + void finishedUpdate(); + + /** + * The user has requested the dice to be rolled. Emit the + * request to somebody who knows how to do that. + */ + void rollDice(const int w); + + /** + * Ask the server to double + */ + void doubleCube(const int w); + + /** + * Once the moves are all made, build a server command and send + * them out. + */ + void currentMove(QString *s); + + /* ************************************************** */ + /* ************************************************** */ + + /* Everything below this line is private and it */ + /* shouldn't be used by users of this widget. */ + + /* This means the whole file! All following */ + /* classes and members are private. */ + + /* ************************************************** */ + /* ************************************************** */ + +protected: + virtual QSize sizeHint() const; + + QColor baseColors[2]; + QFont boardFont; + KBgBoardCell* cells[30]; + bool computePipCount; + + private: + + /** + * Emits a currentMove string to whomever cares. + */ + void sendMove(); + + /** + * Emit a request for doubling. + */ + void getDoubleCube(const int w); + + /** + * Get pieces on who's bar - US or THEM + */ + int getOnBar( int who ) const; + + /** + * Get who's dice num - who = US or THEM, num = 0 or 1 + */ + int getDice( int w, int n ) const; + + /** + * Get the number on the cube + */ + int getCube() const; + + /** + * Ask the server for rolling + */ + void getRollDice(const int w); + + /** + * Tell the board that we kicked a piece off and store + * the information + */ + void kickedPiece(); + + /** + * Check whether a move off is possible by checking that all pieces + * are either in the home board or already off + */ + bool moveOffPossible() const; + + /** + * Check whether who (US or THEM) can double + */ + bool canDouble( int who ) const; + + /** + * The cells have to tell us if the change the number of pieces, since + * we check that sometimes + */ + void updateField( int f, int v ); + + /** + * Convert an ID from to the board numbers + */ + int IDtoNum(const int ID ) const; + + /** + * Checks if there is a possibility to get from src to dest. + */ + int checkMultiMove( int src, int dest, int m[4] ); + + /** + * Checks whether the dice allow a move from src to dest (ID's) + */ + bool diceAllowMove( int src, int dest ); + + /** + * Make a move from src to dest. The numbers are cellID's. + */ + void makeMove( int src, int dest ); + + /** + * Translates a field number to a pointer to the cell. + */ + KBgBoardCell* getCell(int num); + + /** + * Draws a piece on the painter p, with the upper left corner + * of the enclosing rectangle being (x,y) + */ + void drawCircle(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper, bool outer) const; + + /** + * Draws an anti-aliased checker on the painter p. + */ + void drawChecker(QPainter *p, int x, int y, int pcs, int diam, + int col, bool upper) const; + + /** + * Draws a simple 2-color checker on the painter p. This is intended + * for printing. + */ + void drawSimpleChecker(QPainter *p, int x, int y, int pcs, + int diam) const; + + /** + * Given a position on the board, return the cell under the mouse pointer + */ + KBgBoardCell* getCellByPos(const QPoint& p) const; + + /** + * Name says it all, doesn't it? + */ + void showContextMenu(); + + /** + * Temporary replace the cursor, saves the old one + */ + void replaceCursor(const QCursor& c); + + /** + * Restore the previously stored cursor. + */ + void restoreCursor(); + + /** + * Given the sign of p, return the current base color + */ + QColor getCheckerColor(int p) const; + + /** + * Small utility function for makeMove - just for readability + */ + int makeMoveHelper(int si, int sf, int delta); + + /** + * Private data members - no description needed + */ + QPopupMenu *contextMenu; + QPtrList moveHistory; + QPtrList redoHistory; + int direction, color; + int hasmoved; + bool allowmoving, editMode; + int storedTurn; + int onbar[2]; + int onhome[2]; + int board[26]; + int dice[2][2]; + int possMoves[7]; + int cube; + int checkerDiam; + bool cubechanged; + bool maydouble[2]; + int shortMoveMode; + QCursor *savedCursor; +}; + +/** + * Base class for the cells on the board + * + * This base class provides all the necessary functions of a cell + * on a backgammon board. It has a bunch of virtual functions that + * are overloaded in the derived classes. + */ +class KBgBoardCell : public QLabel +{ + Q_OBJECT + + public: + + /** + * Constructor and destructor + */ + KBgBoardCell(QWidget * parent, int numID); + virtual ~KBgBoardCell(); + + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false) = 0; + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Updates all the status variables at once + */ + virtual void statusUpdate(int dir, int col); + +protected: + + /** + * Draw vertical lines around the board. + */ + void drawVertBorder(QPainter *p, int xo, int yo, double sf = 1.0) const; + void drawOverlappingCheckers(QPainter *p, int xo, int yo, + double sf = 1.0) const; + void drawCube(QPainter *p, int who, int xo, int yo, double sf = 1.0) const; + + /** + * Puts a piece of color on a field + */ + void putPiece( int newColor ); + + /** + * Removes a piece from a field. Returns true if success or false else + * (i.e. there is no piece on this field. + */ + bool getPiece(); + + /** + * Return the number of this cell + */ + virtual int getNumber() const; + + /** + * Return the suggested diameter of a piece + */ + int getCheckerDiameter() const; + int getCellColor(); + + /** + * Do we allow a drop of the DragEvent ? This checks the payload and + * reacts on it. + */ + virtual bool dropPossible(int fromCellID, int newColor) = 0; + + protected: + + /** + * Overwrite how a cell draws itself + */ + virtual void drawContents(QPainter *); + + /** + * Status numbers that store the current board status. + */ + int mouseButton; + int direction; + int color; + + /** + * How many pieces are we currently holding ? + */ + int pcs; + + /** + * Our own ID + */ + int cellID; + + /** + * Indicates whether this cell needs to repaint itself after + * the board has been processed. + */ + bool stateChanged; + bool colorChanged, directionChanged; + + /** + * the board and the pieces are one unit (none makes + * sense without the other). So the pieces know and access their parent. + */ + KBgBoard *board; + void checkAndMakeShortMove(QMouseEvent *e, int m); + + /** + * Returns the bounding rectangle of the cube on this cell + */ + QRect cubeRect( int who, bool big, double sf = 1.0 ) const; + + /** + * Returns the bounding rectangle of the dice i on this cell + */ + QRect diceRect(int i, bool big, double sf = 1.0, double scale = 0.45) const; + bool dragInProgress; + + protected slots: + /** + * Refreshes the widget. This is essentially a call to update(). + */ + virtual void refresh(); + + /** + * Can we currently drag from this field ? + */ + virtual bool dragPossible() const = 0; + + /** + * Possibly initiate a drag. + */ + virtual void mouseMoveEvent( QMouseEvent * ); + virtual void mousePressEvent(QMouseEvent *e); + + /** + * Make the shortes possible move away from this cell + */ + void makeShortMove(); + void makeShortMoveHelper(int s, int d); + + /** + * Catch a single left click and perhapes make a move. + */ + virtual void mouseReleaseEvent( QMouseEvent *e ); + + /** + * Catch a double left click and perhapes make a move. + */ + virtual void mouseDoubleClickEvent( QMouseEvent *e ); +}; + +/** + * The homes are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardHome : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /* + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Constructor and destructor + */ + KBgBoardHome( QWidget * parent, int numID); + virtual ~KBgBoardHome(); + + /** + * Check whether a drop on the home cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the home is possible. + */ + virtual bool dragPossible() const; + + /** + * Get the double clicks + */ + virtual void mouseDoubleClickEvent( QMouseEvent *e ); + + /** + * The homes contain dice and cube. This draws them. + */ + void drawDiceAndCube(QPainter *p, int who, int xo, int yo, + double sf) const; + + void drawDiceFrame(QPainter *p, int col, int num, int xo, int yo, + bool big, double sf) const; + void drawDiceFace(QPainter *p, int col, int num, int who, int xo, + int yo, double sf) const; + + private: + /** + * Save old dice to avoid repainting + */ + int savedDice[2]; + +}; + +/** + * The bars are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardBar : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Constructor + */ + KBgBoardBar( QWidget * parent, int numID ); + + /** + * Destructor + */ + virtual ~KBgBoardBar(); + + /** + * Check whether a drop on the bar cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the bar is possible. + */ + virtual bool dragPossible() const; + /** + * Get the double clicks + */ + virtual void mouseDoubleClickEvent(QMouseEvent *e); +}; + +/** + * The fields are derived from the regular cells. They just overwrite + * some members. + */ +class KBgBoardField : public KBgBoardCell +{ + Q_OBJECT + + public: + /** + * Constructor and destructor + */ + KBgBoardField( QWidget * parent, int numID); + virtual ~KBgBoardField(); + + /** + * sets the number and color of checkers on this cell + * takes care of repainting + */ + virtual void cellUpdate(const int p, const bool cubechanged = false); + + /** + * Draws the content of the cell on the painter *p + */ + virtual void paintCell(QPainter *p, int xo = 0, int yo = 0, + double sf = 1.0) const; + + /** + * Check whether a drop on the field cell is possible. + */ + virtual bool dropPossible(int fromCellID, int newColor); + + protected: + /** + * Determine whether a drag from the field is possible. + */ + virtual bool dragPossible() const; + + /** + * Return the y-coordinate of the number of the field. + */ + int numberBase() const; +}; + +/** + * Internal class for storing a move in the undo history buffer. + */ +class KBgBoardMove +{ + public: + /** + * Accepts source, destination and the dice that made this move + * possible. Set the kicked flag to false. + */ + KBgBoardMove(int src, int dest, int delta) + {s = src; d = dest; l = delta; k = false;} + + /** + * Set this move to be a kick + */ + void setKicked(bool kicked) {k = kicked;} + + /** + * Look up the source + */ + int source() const {return s;} + + /** + * Look up the destination + */ + int destination() const {return d;} + + /** + * Look up the dice that made this move + */ + int length() const {return l;} + + /** + * Check whether the move kicked a piece + */ + bool wasKicked() const {return k;} + + private: + /** + * Source, destination, dice + */ + int s, d, l; + + /** + * Kicked move ? + */ + bool k; +}; + +/** + * Simple dialog that allows to query the user for dice values. + * + * A very simple dialog with two SpinBoxes and two buttons. + */ +class KBgBoardQDice : public QDialog +{ + Q_OBJECT + +public: + + /** + * Constructor and destructor + */ + KBgBoardQDice(const char *name = 0); + virtual ~KBgBoardQDice(); + +protected: + + /** + * Spin boxes and buttons are children + */ + QSpinBox *sb[2]; + QPushButton *ok; + QPushButton *cancel; + +public slots: + + /** + * Get the face values. + */ + int getDice(int n); +}; + + +/** + * Simple dialog that allows to query the user for the cube value. + */ +class KBgBoardQCube : public QDialog +{ + Q_OBJECT + +public: + + /** + * Constructor and destructor + */ + KBgBoardQCube(int val, bool us, bool them); + virtual ~KBgBoardQCube(); + +protected: + + /** + * Spin boxes and buttons are children + */ + QComboBox *cb[2]; + QPushButton *ok; + QPushButton *cancel; + +public slots: + + /** + * Get the face values. + */ + int getCubeValue(); + int getCubeOwner(); + +protected slots: + + /** + * These slots are needed to get consistent relations + * between the two combo boxes. + */ + void changePlayer(int val); + void changeValue(int player); + +}; + + +/** + * Extension of the KBgBoard class that can add itself + * to a QTabDialog for configuration. + */ +class KBgBoardSetup : public KBgBoard +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgBoardSetup(QWidget *parent = 0, const char *name = 0, + QPopupMenu *menu = 0); + + /** + * Lets the board put its setup pages into the notebook nb + */ + void getSetupPages(KDialogBase *nb); + +public slots: + + /** + * Setup changes are confirmed. Store them. + */ + void setupOk(); + + /** + * Setup has been cancelled. Undo the color changes + */ + void setupCancel(); + + /** + * Load default values for user settings + */ + void setupDefault(); + +protected slots: + + /** + * Open a color dialog for the background color + */ + void selectBackgroundColor(); + + /** + * Open a color dialog for the first checker color + */ + void selectBaseColorOne(); + + /** + * Open a color dialog for the second checker color + */ + void selectBaseColorTwo(); + +private: + + /** + * Save settings before the user changed them + */ + KFontChooser *kf; + + QRadioButton *rbMove[3]; + + QColor saveBackgroundColor; + QColor saveBaseColors[2]; + + /** + * Need these to change their colors + */ + QPushButton *pbc_1, *pbc_2, *pbc_3; + QCheckBox *cbp; +}; + +#endif // KBGBOARD_H diff --git a/kbackgammon/kbgstatus.cpp b/kbackgammon/kbgstatus.cpp new file mode 100644 index 00000000..1215324e --- /dev/null +++ b/kbackgammon/kbgstatus.cpp @@ -0,0 +1,544 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kbgstatus.h" +#include "kbgstatus.moc" + +#include +#include + + +/* + * Parse a rawboard description from FIBS and initialize members. + */ +KBgStatus::KBgStatus(const QString &rawboard) +{ + /* + * This is the format string from hell... + */ + const char *format = ("%*[^:]%*[:]%99[^:]%*[:]%99[^:]%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]%i%*[:]" + "%i%*[:]%i%*[:]"); + + char opponent[100], player[100]; + + QString cap; + + int board[26], ldice[2][2], maydouble[2], scratch[4], onhome[2], onbar[2]; + int points[2]; + int tomove, lturn, color, cube, direction, redoubles, bar, home, length; + + // split the incoming line at colons - latin1() is fine, since the string comes from FIBS. + sscanf (rawboard.latin1(), format, + player, opponent, + &length, + &points[0], &points[1], + &board[ 0], &board[ 1], &board[ 2], &board[ 3], &board[ 4], &board[ 5], + &board[ 6], &board[ 7], &board[ 8], &board[ 9], &board[10], &board[11], + &board[12], &board[13], &board[14], &board[15], &board[16], &board[17], + &board[18], &board[19], &board[20], &board[21], &board[22], &board[23], + &board[24], &board[25], + <urn, + &ldice[US ][0], &ldice[US ][1], &ldice[THEM][0], &ldice[THEM][1], + &cube, + &maydouble[US], &maydouble[THEM], + &doubled_, + &color, + &direction, + &home, &bar, + &onhome[US], &onhome[THEM], // on home + &onbar[US], &onbar[THEM], // on bar + &tomove, + &scratch[2], &scratch[3], // forced move & did crawford + &redoubles); + + player_[US] = player; + player_[THEM] = opponent; + + setCube(cube, maydouble[US], maydouble[THEM]); + setDirection(direction); + setColor(color); + for (int i = 1; i < 25; i++) { + if (board[i] == 0 || color == board[i]/abs(board[i])) + setBoard(i, US, abs(board[i])); + else + setBoard(i, THEM, abs(board[i])); + } + setDice(US , 0, ldice[US ][0]); + setDice(US , 1, ldice[US ][1]); + setDice(THEM, 0, ldice[THEM][0]); + setDice(THEM, 1, ldice[THEM][1]); + + setHome(US, onhome[US ]); + setHome(THEM, onhome[THEM]); + + setBar(US, board[ bar]); + setBar(THEM, board[25-bar]); + + setPoints(US, points[0]); + setPoints(THEM, points[1]); + + if (lturn == 0) + setLength(-1); + else + setLength(length); + + int t = lturn*color; + if (t > 0) setTurn(US); + if (t < 0) setTurn(THEM); + if (t == 0) setTurn(NONE); +} + +/* + * Constructor initializes the status to an empty board with cube one + * and empty dice. + */ +KBgStatus::KBgStatus() + : QObject() +{ + /* + * Initialize members + */ + for (int i = 0; i < 26; ++i) + setBoard(i, US, 0); + + for (int i = US; i <= THEM; i++) { + setDice (i, 0, 0); + setDice (i, 1, 0); + setHome (i, 0); + setBar (i, 0); + setPoints(i, -1); + setPlayer(i, QString::null); + } + setColor(White, US); + setCube(1, BOTH); // also initializes maydouble + setDirection(1); + setLength(-1); + setTurn(NONE); + + // initialize members without assignment functions + doubled_ = 0; +} + +/* + * Copy constructor calls private utility function. + */ +KBgStatus::KBgStatus(const KBgStatus &rhs) + : QObject() +{ + copy(rhs); +} + +/* + * Destructor + */ +KBgStatus::~KBgStatus() +{ + // nothing to do +} + +/* + * Assignment operator shares a lot of code with the copy + * constructor. + */ +KBgStatus& KBgStatus::operator=(const KBgStatus &rhs) +{ + if (this == &rhs) return *this; + copy(rhs); + return *this; +} + +void KBgStatus::copy(const KBgStatus &rhs) +{ + for (int i = 0; i < 26; i++) + board_[i] = rhs.board_[i]; + + for (int i = US; i <= THEM; i++) { + + home_[i] = rhs.home_[i]; + bar_ [i] = rhs.bar_ [i]; + dice_[i][0] = rhs.dice_[i][0]; + dice_[i][1] = rhs.dice_[i][1]; + + maydouble_[i] = rhs.maydouble_[i]; + player_ [i] = rhs.player_ [i]; + points_ [i] = rhs.points_ [i]; + } + + cube_ = rhs.cube_; + direction_ = rhs.direction_; + color_ = rhs.color_; + turn_ = rhs.turn_; + doubled_ = rhs.doubled_; +} + + +/* + * Access functions + */ +int KBgStatus::board(const int &i) const +{ + return ((0 < i && i < 25) ? color_*board_[i] : 0); +} + +int KBgStatus::home(const int &w) const +{ + return ((w == US || w == THEM) ? color_*home_[w] : 0); +} + +int KBgStatus::bar(const int &w) const +{ + return ((w == US || w == THEM) ? color_*bar_[w] : 0); +} + +int KBgStatus::color(const int &w) const +{ + return ((w == THEM) ? -color_ : color_); +} + +int KBgStatus::direction() const +{ + return direction_; +} + +int KBgStatus::dice(const int &w, const int &n) const +{ + if ((w == US || w == THEM) && (n == 0 || n == 1)) + return dice_[w][n]; + else + return 0; +} + +int KBgStatus::cube(const int &w) const +{ + if (w == US || w == THEM) + return ((maydouble_[w]) ? cube_ : -cube_); + return 0; +} + +int KBgStatus::points(const int& w) const +{ + return ((w == US || w == THEM) ? points_[w] : -1); +} + +QString KBgStatus::player(const int &w) const +{ + return ((w == US || w == THEM) ? player_[w] : QString::null); +} + +int KBgStatus::length() const +{ + return length_; +} + +int KBgStatus::turn() const +{ + return turn_; +} + +bool KBgStatus::doubled() const +{ + return doubled_; +} + + +/* + * Assignment functions + */ +void KBgStatus::setBoard(const int &i, const int &w, const int &v) +{ + if (0 < i && i < 25) { + if (w == US) + board_[i] = abs(v); + else if (w == THEM) + board_[i] = -abs(v); + } +} + +void KBgStatus::setHome(const int &w, const int &v) +{ + if (w == US) + home_[w] = abs(v); + else if (w == THEM) + home_[w] = -abs(v); +} + +void KBgStatus::setBar(const int& w, const int& v) +{ + if (w == US) + bar_[w] = abs(v); + else if (w == THEM) + bar_[w] = -abs(v); +} + +void KBgStatus::setColor(const int &c, const int &w) +{ + if (w == US) + color_ = ((c < 0) ? Black : White); + else if (w == THEM) + color_ = ((c < 0) ? White : Black); +} + +void KBgStatus::setDirection(const int &dir) +{ + direction_ = ((dir < 0) ? -1 : +1); +} + +void KBgStatus::setDice(const int &w, const int &n, const int &v) +{ + if ((w == US || w == THEM) && (n == 0 || n == 1)) { + if (0 <= v && v <= 6) + dice_[w][n] = v; + else + dice_[w][n] = 0; + } +} + +void KBgStatus::setCube(const int &c, const bool &us, const bool &them) +{ + int w = NONE; + if (us) w = US; + if (them) w = THEM; + if (us && them) w = BOTH; + setCube(c, w); +} + +void KBgStatus::setCube(const int &c, const int &w) +{ + // assume that int has at least 32 bits... + for (int i = 0; i < 31; i++) { + if (1< 0)) { + start = 25; + dir = -1; + } else { + start = 0; + dir = 1; + } + + /* + * Get the current dice transferred into the move[] array. The + * final zero is a marker + */ + int move[5] = {0, 0, 0, 0, 0}; + move[0] = dice(turn(), 0); + move[1] = dice(turn(), 1); + if (move[0] == move[1]) { + move[3] = move[2] = move[0]; + // saves some work further down + if (move[0] == 0) + return 0; + + } + + bool doubledice = (move[3] != 0); + int count = 4; + + /* + * Get a copy of ourselves. That way we can mess around with + * the internals of the game. + */ + KBgStatus sc(*this); + + /* + * Start with getting all checkers off the bar + */ + while (count > 0 && sc.bar(turn()) != 0) { + if (move[--count] != 0) { + if (color(turn())*sc.board(start+dir*move[count]) >= -1) { + sc.setBar(turn(), abs(sc.bar(turn()))-1); + sc.setBoard(start + dir*move[count], turn(), 1); + move[count] = 0; + } + } + } + + /* + * Collect remaining moves in the beginning of the move array + */ + int j = 0; + for (int i = 0; i < 4; i++) { + if ((move[j] = move[i])) + ++j; + if (i > j) move[i] = 0; + } + + + /* + * Find number of remaining moves + */ + int moves = 0; + move[4] = 0; + while (move[moves]) + ++moves; + + /* + * Done or no more moves because the bar is not empty + */ + if (sc.bar(turn()) != 0 || move[0] == 0) + return (move[3] ? 4 - moves : 2 - moves); + + /* + * Try to find possible moves on the board + */ + if (moves == 1 || move[0] == move[1]) { + + /* + * Order doesn't matter, all moves are equal + */ + while (--moves >= 0 && movePossible(sc, move[moves], start, dir)); + moves++; + return (doubledice ? 4 - moves : 2 - moves); + + } else { + + /* + * Order does matter; try both ways. + */ + moves = 0; + for (int i = 0; i < 25; i++) { + if (movePossible(sc, move[0], start + i*dir, dir)) { + moves = 1; + if (movePossible(sc, move[1], start, dir)) { + return 2; + } + } + // Restore scratch copy... + sc = *this; + } + for (int i = 0; i < 25; i++) { + + if (movePossible(sc, move[1], start + i*dir, dir)) { + moves = 1; + if (movePossible(sc, move[0], start, dir)) { + return 2; + } + } + // Restore scratch copy... + sc = *this; + } + return moves; + } +} + +bool KBgStatus::movePossible(KBgStatus &sc, int a, int start, int dir) const +{ + /* + * Determine where the first checker in moving direction is + * located + */ + int first = (dir > 0) ? 1 : 24; + int last = (dir > 0) ? 25 : 0; + while (first != last && color(turn())*sc.board(first) <= 0) + first += dir; + + /* + * Are we moving off ? + */ + bool off = false; + if ((dir > 0 && first > 18) || (dir < 0 && first < 7)) + off = true; + + /* + * Find a move by exhaustive search. + */ + while (true) { + + start += dir; + int final = start+dir*a; + + /* + * Make absolutely sure that the loop terminates eventually + */ + if (start <= 0 || start >= 25) + return false; + + if (color(turn())*sc.board(start) > 0) { + + if (0 < final && final < 25 && color(turn())*sc.board(final) >= -1) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setBoard(final, turn(), abs(sc.board(final)) + 1); + return true; + } else if (off && (final == 0 || final == 25)) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setHome(turn(), abs(sc.home(turn())) + 1); + return true; + } else if (off && first == start && (final > 24 || final < 1)) { + sc.setBoard(start, turn(), abs(sc.board(start)) - 1); + sc.setHome(turn(), abs(sc.home(turn())) + 1); + return true; + } + } + } +} + +// EOF diff --git a/kbackgammon/kbgstatus.h b/kbackgammon/kbgstatus.h new file mode 100644 index 00000000..5543e1ca --- /dev/null +++ b/kbackgammon/kbgstatus.h @@ -0,0 +1,310 @@ +/* + Copyright (C) 2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +#ifndef KBGSTATUS_H +#define KBGSTATUS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + + +/** + * This is a the fundamental class of the KBg* hierarchy. It represents + * the state of backgammon games. + * + * The game states can be initialized in a variety of information and + * are meant to be passed to the board. In addition to that, the class + * has several utility functions that are helpful for engines that + * maintain a local state. + * + * @short The backgammon status object + * @author Jens Hoefkens + * @version $Id$ + */ +class KBgStatus : public QObject +{ + + Q_OBJECT + + public: + + /** + * These numbers are used to distinguish the two players. The + * notion of US and THEM is a leftover from ancient times. + */ + enum {NONE = -1, US = 0, THEM = 1, BOTH = 2}; + + /** + * The names are just to distinguish the two different colors. + */ + enum {Black = -1, White = +1}; + + /** + * The default constructor initializes the status to an "empty" + * state. The board and dice are empty and the cube shows 1. + */ + KBgStatus(); + + /** + * Constructor from a FIBS rawboard string + */ + KBgStatus(const QString &rawboard); + + /** + * Copy constructor + */ + KBgStatus(const KBgStatus &rhs); + + /** + * Assignment operator + */ + KBgStatus& operator=(const KBgStatus &rhs); + + /** + * Destructor + */ + virtual ~KBgStatus(); + + + /* + * The absolute value of the return value is the number of + * checkers on the i-th field (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + */ + int board(const int &i) const; + + /* + * The absolute value of the return value is the number of + * checkers on the home of player w (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + * + * The encoding of the color is slighly redundant. See also board(...). + */ + int home(const int &w = US) const; + + /* + * The absolute value of the return value is the number of + * checkers on the bar of player w (or zero if i is out of + * bounds). If the return value has the same sign as the + * current color of US, it belongs to US, otherwise it belongs + * to THEM. + * + * The encoding of the color is slighly redundant. See also board(...). + */ + int bar(const int &w = US) const; + + /* + * Return the current color of player w. If w is invalid, the + * color of US is returned. The return value will be either + * Black or White. + */ + int color(const int& w = US) const; + + /* + * Returns the current direction of the game. It is -1 if US + * plays from 24 to 1 and +1 if US plays from 1 to 24. + */ + int direction() const; + + /* + * Returns the n-th dice of player w. If w is invalid or if n + * is out of bounds, return zero. Also, if the dice haven't + * been set, zero is returned. + */ + int dice(const int &w, const int &n) const; + + /* + * Returns the value of the cube. If w can double, the return + * value is positive, if w may not double, the negative value + * of the cube is returned. If w is not legal, zero is + * returned. + */ + int cube(const int &w = US) const; + + /* + * Return the points of w in th ecurrent game. Negative values + * indicate that either w was not a legal player ID or that + * the engine doesn't have any information on points. + */ + int points(const int &w) const; + + /* + * Return the name of player w. If w is out of bounds or if + * the player names have not been set, QString::null is + * returned. + */ + QString player(const int &w = US) const; + + /* + * Return the length of the game. Negative values should be used to + * indicate that the game is over. Zero indicates that the game is + * unlimited. + */ + int length() const; + + /* + * Return whose turn it is. The possible return values are US, + * THEM, and NONE. + */ + int turn() const; + + /* + * Return true if the cube has just been offered. If this + * information is not available or if this is not the case, + * return false. + */ + bool doubled() const; + + /* + * Set the number of checkers of player w on the i-th field to + * the absolute value of v. If either i or w are out of bound, + * nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. + */ + void setBoard(const int &i, const int &w, const int &v); + + /* + * Set the number of checkers on the home of player w to the + * absolute value of v. If w is out of bound, nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. See also + * setBoard(...). + */ + void setHome(const int &w = US, const int &v = 0); + + /* + * Set the number of checkers on the bar of player w to the + * absolute value of v. If w is out of bound, nothing is done. + * + * Internally, positive numbers are stored for US and negative + * ones for THEM. While this coding is redundant, it is + * consistent with the storing of board positions. See also + * setBoard(...). + */ + void setBar(const int &w = US, const int &v = 0); + + /* + * This function sets the color of player w to c. Negative + * values of c translate to Black for US (and White for + * THEM). Non-negative values for c translate to White for US + * and Black for THEM. + */ + void setColor(const int& col, const int& w = US); + + /* + * Set the direction of the game. If dir is negative, US plays + * from 24 to 1. If dir is positive, US plays from 1 to 24. + */ + void setDirection(const int &dir); + + /* + * Set the n-th dice of player w to v. Nothing is done if w is + * invalid or if n is out of bounds. If v is invalid, the + * value zero is assigned (i.e., the dice is invalidated). + */ + void setDice(const int &w, const int &n, const int &v); + + /* + * Set the cube to c us indicates if US can double, them + * indicates if THEM can double. + * + * This function is depreciated. + */ + void setCube(const int &c, const bool &us, const bool &them); + + /* + * Set the cube to c, which must be a legal value (i.e., a + * power of 2). w indicates who can double. Legal values are + * NONE, US, THEM, and BOTH. + */ + void setCube(const int &c, const int &w); + + /* + * Set the points of w in the current game to p. Nothing is + * done if w is illegal. + */ + void setPoints(const int &w, const int &p); + + /* + * Set the name of player w to name. If w is out of bound, + * nothing is done. + */ + void setPlayer(const int &w, const QString &name); + + /* + * Set the length of the game. Negative values should be used to + * indicate that the game is over. Zero indicates that the game + * is unlimited. + */ + void setLength(const int &l); + + /* + * Set the turn to w. Legal values for w are US, THEM, and + * NONE (which should indicate that the game is over). + */ + void setTurn(const int &w); + + /* + * Return the number of possible moves basesd on the current + * dice, checkers, etc. + */ + int moves() const; + + private: + + /* + * Determine if there is any possibility to move a steps + * anywhere starting from start or later into direction + * dir in the game given by sc. + */ + bool movePossible(KBgStatus &sc, int a, int start, int dir) const; + + /* + * Copy constr. and assignment share a lot of code. + */ + void copy(const KBgStatus &rhs); + + /* + * Private variables with self-expalanatory names. + */ + QString player_[2]; + + int board_[26], home_[2], bar_[2], dice_[2][2], points_[2]; + int color_, direction_, cube_, length_, turn_; + int doubled_; + + bool maydouble_[2]; +}; + +#endif // KBGSTATUS_H diff --git a/kbackgammon/kbgtextview.cpp b/kbackgammon/kbgtextview.cpp new file mode 100644 index 00000000..b99a2f27 --- /dev/null +++ b/kbackgammon/kbgtextview.cpp @@ -0,0 +1,104 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999,2000 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include "kbgtextview.moc" +#include "kbgtextview.h" + +#include +#include +#include +#include +#include +#include + +#include + +// == advanced text control ==================================================== + +/* + * Constructor + */ +KBgTextView::KBgTextView(QWidget *parent, const char *name) + : KTextBrowser(parent, name) +{ + clear(); + setLinkUnderline(true); +} + +/* + * Destructor + */ +KBgTextView::~KBgTextView() +{ + // empty +} + +/* + * Write the string l to the TextView and put the cursor at the end of + * the current text + */ +void KBgTextView::write(const QString &l) +{ + append("" + l + "
\n"); + scrollToBottom(); +} + +/* + * Clears the view by overwriting the text with an empty string. + */ +void KBgTextView::clear() +{ + setText(""); +} + +/* + * Open a font-selection dialog. + */ +void KBgTextView::selectFont() +{ + QFont f = font(); + KFontDialog::getFont(f, false, this, true); + setFont(f); +} + +/* + * Restore the previously stored settings + */ +void KBgTextView::readConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + // nothing to restore +} + +/* + * Save the current settings to disk + */ +void KBgTextView::saveConfig() +{ + KConfig* config = kapp->config(); + config->setGroup(name()); + + // nothing to save +} + +// EOF diff --git a/kbackgammon/kbgtextview.h b/kbackgammon/kbgtextview.h new file mode 100644 index 00000000..887136f4 --- /dev/null +++ b/kbackgammon/kbgtextview.h @@ -0,0 +1,81 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999,2000 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef __KBGTEXTVIEW_H +#define __KBGTEXTVIEW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + + +/** + * A small extension to the QTextView control. + */ +class KBgTextView : public KTextBrowser +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + KBgTextView(QWidget *parent = 0, const char *name = 0); + + /** + * Destructor + */ + virtual ~KBgTextView(); + +public slots: + + /** + * Restore previously saved setting or provides defaults + */ + void readConfig(); + + /** + * Save current settings + */ + void saveConfig(); + + /** + * Simple interface to the non-slot function selectFont() + */ + void selectFont(); + + /** + * Clears the view by setting the text to "" + */ + void clear(); + + /** + * Write the string at the end of the buffer and scroll to + * the end + */ + void write(const QString &); +}; + +#endif // __KBGTEXTVIEW_H diff --git a/kbackgammon/main.cpp b/kbackgammon/main.cpp new file mode 100644 index 00000000..fe20cc7c --- /dev/null +++ b/kbackgammon/main.cpp @@ -0,0 +1,67 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include + +#include "kbg.h" +#include "version.h" + +static const char description[] = I18N_NOOP("A Backgammon program for KDE"); +static const char notice[] = I18N_NOOP("This is a graphical backgammon program. It supports " + "backgammon games\nwith other players, games against " + "computer engines like GNU bg and even\n" + "on-line games on the 'First Internet Backgammon Server'."); + +/* + * Main program doesn't do much - more or less standard stuff. Right + * after creating a KBg object, control is passed to it. + */ +int main(int argc, char *argv[]) +{ + KAboutData about(PROG_NAME, I18N_NOOP("KBackgammon"), PROG_VERSION, description, + KAboutData::License_GPL, "(C) 1999-2001 Jens Hoefkens", notice); + + about.addAuthor ("Jens Hoefkens", I18N_NOOP("Author & maintainer"), + "jens@hoefkens.com"); + + about.addCredit ("Bo Thorsen", I18N_NOOP("Initial anti-aliasing of the board"), + "gobo@imada.sdu.dk"); + + KCmdLineArgs::init(argc, argv, &about); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (app.isRestored()) + RESTORE(KBg) + else { + KBg* widget = new KBg; + app.setMainWidget(widget); + widget->readConfig(); + widget->show(); + } + return app.exec(); +} + diff --git a/kbackgammon/pics/Makefile.am b/kbackgammon/pics/Makefile.am new file mode 100644 index 00000000..faf4aad5 --- /dev/null +++ b/kbackgammon/pics/Makefile.am @@ -0,0 +1,6 @@ +pics_DATA = kbackgammon-double.xpm kbackgammon-chat.png + +picsdir = $(kde_datadir)/kbackgammon/pics + +EXTRA_DIST = $(pics_DATA) + diff --git a/kbackgammon/pics/kbackgammon-chat.png b/kbackgammon/pics/kbackgammon-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..65369f6de7b6668c248952779dda053bfc87e589 GIT binary patch literal 292 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Ξ*MQ zD1rrM6_jjiyms>X&0$#8DcQTHJ;QSp$pxbF#B^zAU(y?DjTRJ{z!Rc5G-kT>qzC6j zR+Us*f?d(vO2Oe-(U_w(GJzMFRrKpW|Mp?`;Eb$lESZQ?`Cn~pXPqmHf~q6WdR^K zd25uiQ&?WbivE&D&`OcIGr(AJ#nz@ePft^lXe zJduD12VK1svo0-ZR~`9-t9S2@LJNm@II+}>;MXIiG(aTzO>H!;!2p{G8_&L#pIEI@mD*YPG zxr7;!)^>gO>0iIR*Mla3g5rtMWO1vS;zep!m6!s|2z~eWpZ@iWyTm#T@ZCYk+DV@OHlGe=0&OX z3U%wk*kG)@q370TpM3FC*YJ#~#@U~fdE$d~Cv0e)5_UE5|b-#zPtgcX!c_kA_uvC%`_>;ULI7sg< z5ZqKye2^_vnqwzZu2&N5935|a=k(><^OP18%&zXi7K-3-3zgi|FfUtt8>!B74vHVY zLR}osYK2254v2HEyak1PnI|HQi+J&Af-V$vgCprK$B8m~GWg6Zv9Zipw1`6umZPbQ zqv`v{WYdYb2PGUWF5h+~&H)Ar(&Ov5r*TaWZT)VM=AYq^4kl7^W(o@PU|CR%^_s)5 z>&I{C*Vi{Zr%>=0YHE)+)2!;tU4LsVK`}3RkLh%#D4G{0ipcf$ zWc~QX3s7A@(NB+~d=@87eq%a~2qUth6zV8roxFGs39;+$ci-M^2gNli2biFCP}wkR zboy1wvW}n^y5~TBcdv!Q_f7G~tp>`Z(dsD6+B$xIU3boO-u?d8{dV3X*L#3`0X})9 zu~!Y7G&&t+S%RuLX1nj-zWuNRlp5nl@)(&M{ypv7wL0ypi}c&G%gW|#k5q4UDyR*g zT(+6K`1c``PX7UQK~ALi5mi!M>_Yr#Uo^p3gxn86=~s1>8C4Ij&kDAgUMic~C|t(S zVJuMDd<}X_(6nmMqa0MF$}wk|?xC`=9g^;}O7Y2gqND=K0Z;@2L0uieaqXv2r=zQ9 zXnIM{oy=faN(&(l8dp~-7sK>EM|^W;pzR^k>K(z17E~Nm;~1)0ybA@ii?drRGOJJ=g(t0)rI` z8P6Rljy#4AIu%y)n%3lna&=ho>SiP{hie5jRbvvm1ECdiwM9SV1D%N+V{^acrYgR> z6GIxW78Gty6jPL{;Zne1H|j0ojWsiNJS*%ysl_KwDyAr?tz%F@m&0V(pu()#ND1A6 z+}$8KQ9+5r7thv@FAj=959EVQc(KqOEUKwN#sWo>UnhQ!f;!*N2fc0!AA5{>BZU zOMGA0cIW=C$LwWwQ98hO#!PR6?HP!x{3EQTk zE|YF)a(HME0*};DNP)B66Q#+^?pCc<&VY(1(s7Sv71j-)L8-}@!88CB$<}w9GM0i$ zrW0PPfyxvDFv3z8pH@(OHub&TZ5b<`h;t$&=-0hGGb)c1zJ@G?2?Hf7if;9KC4;?8 zkS^}C8kXVH$d808!&smw+o+-FR4 zZtz4!l)_ zj5XY^px8USZukJa?){^^t!hPH5vX*`xlRBH(0`8weS+7$ceGb8vnc{l665K(%cz+b zlv3BkS0r19EX@AN!B)AHZJ{y=w^1R8d_?g?rPgit_@G`aWFx)+S&dAJ0Mb0s6`shR zkg-5XIp7IujrmO211y%!hzQLX!5ltF($o}ZN^MH4xO;L~M+z+HCg2AOc^N^m5(wn* zd7y%zpiYmr$Q}lq#F=6_nozJ=`}+o!gF@(25EhdCC)ZaV6=P@;_@JKj-1CTL@73FJC#8j|g`XEqEQgEmX zYFFJ*?=3m9KKso&T_^Q6mnnW&xpbhF_7Aa-CTfrr8PJS2tUWIYVL&| zmX)zufs9BZC+Bb4=LEIkrJoao@Fn^QbrGW+hvpGnkrr$ADz)Pdu zPoDCQ#wo-l6aZy2iAKnwg3{|y>fJs$DsB=(di1!9D;d@x7otM5sO&T3QtZKD3xMyc4B@zbh4<5Al^iu^OVFf@KU=1kE z&|oy$4OCDrsDWM>czO?`36XD>%Nrq%klhfFld3k(M`f%U9;<#?OYMKYaRJe2ZTRtzx9@fgjLqvIAqz^< zX(R)kdX0Y75-ROo5|7diJiK%3ZYPq}aI9e9a}gkwT`eUXn7*sl2n5?_dyy3~O`wD~ zOx+_nBv6TbnLAyhSzWWoYlqj@&7i91-mUNNbqxy&J`f-dTu_k>)?sZkQ9r!8-1aSY z-Mw||ez%Cc^L`RIa}ngg%ek<1Wkqiw>_Y)|-Gl1v_qQJOj=`XYH73)7%}^+3QHw&b z$%SIYGy1kqxBWn)guZcz$|w>MHxX_@u8ziAT1|}ERIKwG2 zpfVtwDwfi|^<`Ag0_e5foMem$J$xqxOHhm4#F-e1Fnh6>^cX-{LeensD7o5?+wo(^ z$m}u^Q5NC@ioP0zSJ!Rf>cPd$epK80(|6zf)HyZ>{hKHm*$WC!lEr+&vql-}Mil;& z)Qa`ql?H<23xtL!3xJ~!9UxuI$6c!$gV7$}I=Z~t@h^7LX#IZg%XtG zD-#q49Q8`Vo+y=40V3N*Yp}F?a@Gi~^xhM_j%iR(6oVnEJN%(^sgU#;Mdl}5-aS6s z4yb$Y-TC1$H53L?2-MaDy&i8cT`Gd&CvFG|_SRDO-8-ml&vI5$Fc(C#%u6X!sSx)d zv}bj~EIdB0B3vRujze>TBIgqHyD_)3WnK`{er87kA&%p+bH4LF$}%h$@pe$uV#}gd z3UT)uHGs|}DEdH5xBWx`-WZlD5=a=aevgOCm9ePRH(Q*E>hAGj!9Lx7w}moNf9DGX zywnCpa^-x?wMN}5W9=R7BiBRwH3U_b`1)n`LB`^8#Z+~GGRs(?Y*T{j9FuwB00gDR zh~!d*jblK|%!LTb@xCG>^wWdxaY222BCoM5D8>S{d$gNE`sdzH4|_NUsR3qHTQ9qS z+3Q?iH-lom_K^Mo^`M)OHM3`4%Iw)*jI~A?Iw53a_fjUty894RYhKE46P`iS9fH)IcR>C!09`D&=Cbz6Sj;PATQ~8 zW*sT6M;}pGWC2(=p=7a`4nTtCiRmh+=-N2lJK6^&&CFQrFZqNLdCoXNF;=;8z*rOA zPan4pOwOzM_(+p0wTPT?8G0u$mV%<-WxB5&QUMfRNu{w1IZHwb)}C$+M%F%AZX6;{ zGSlDj^aBb*C_wdN3ngQ*_P8?#jg)m(Q2mnsqRLzf8-qd4apj5Y>qciB@gK~Wg`qA` z;|H|qteRtRsJ&Bk>*r{~UAdQ&rt$iwJ_C;;jxCh-TTtVv7A z-D$Cy*6D6i+}_SOwR14^1w|paf$PIZNn)}{NYrB^2t!AbY;7y&F^J6J=;WkQ5f9Ot znQ_(Ds~O1A<~52*h`&>5ff8j4MH&G{Ge;d^@+}Bw4unTdPIOPv%|{}0f?AA8QC#?N zq+o5Mw;OfYHxy$#Pqx{nqa(%G)D*!LnHYL$LhdGxnG{J;Bg~RarA)DcYGxcw4@IH^ zS3%AcbW^uzwc&IiDT<_CiCnp1nJzDrXxNHchcNUREMBZvvq6`0!(?>_6GdX?Yby&= z<2(^91rZ&iL|sa=>qw(}-3|+B#27IV&$?!D3fHlP;(K!{Mb|Y2<%H!<&15m-GijHm z1%(TP7@E|aNRel4iw15U7aA4hs0&%{F7+(0aT=L~MJNv(7os&W+UNnl+Ya4ZDl!~vTGjOAG{Y^h6pY2hxUg!ph33fKR8Z(#8<{?+ zX$sav@l6zfxUed<0?MO42PaV^olpBL#7>aWR8V{(@+0Nt)o8_sj))W@By%aBWfgr@ zQ0T-hs>QM8C9&P>B^Z#%xQM)9Hs!Y%1T{M|BMbHcC3_D6_@Lj3iUIUdP_kfb3R#L) z-3AMmLSI}#ArBLb<0ab|Yl=-VMR~grC|b)()IE!pS+N$MIXK`9E@uZlCD3XGbc`=qq4#~W> z>k5i$9L56m-%ms{3ylC-34Y_2$o+d97AggxCnsG-nb%ZnQ%LQTd>^3L`98PZ#GZ}{ zYF)E1N!)=ik}RPVl(bN_Ttq>k5|l3@T*4v1_tBD;5v~u08sBX#8}=Zw!MDa0op2gx zQX}83h{lNXj=d9xM_tQ=P*(*NNET^;`4Odj-C})!k~1I^bURReMLr3>jEg=z{~Svx zhfz?KbilP~HBy|wNs&M%7x7{C&fF5xA99{3Pu!}c{7z0C(r3!4BXY-rLcEB?9;_!y zdeaqYDYUs^TE}Y)7;Qchh@vtpC{}{IPY6NCLCCy{xv0x( zF`4XvWO-*h=h4lK^z{-ZN7Mr*nMXlEp3g;{R&Hf_c9qGmyEJslri4|A!LrHGa1b+~ zn3cQFWO9VFwdQtuQ$0@Ylc3O+;_Uc{%7pBo%*t)Mm9#C=(5xGj1RnYLu&yM2mQMw^ zZB1r3KXDfnWFJ8hTotJtL2=3miYv`zCE>BN2h(6~s2dR!u4D<+ip9;m61jZRXH`&4 zUib*~8tUjCmOw3Ic|?E$vniWP_$(NGR$sc@py0qXi`~&P!a|@mqL{4WT?xu+G@EVy zG}Z|kepCHj9lfIxaW{+6Cw68amQF`KW~0So4`r*Qr-G{}(YE)D5)WNhrp4qje~Gxe zP3vaMCOyx0_fWxvso35_UGHKX0_?Y-j?1q9VHe+3nB8SyDLBh-z6S{VV=r=a9K<|bgQBxAXhd11{p zn|b##jk4Q%2_NZTuA$d~?T+BNJkgBp?ur%a&7G`MMW>|pz6mPRgx}`#dEF>uD`S}{ zOGIC8Yj-EJxj5L_*3myH0;Xan_!U$FBD)I#sSP(}5WAG*FVIhk?&eZ_#|_};iTH-e zOp5%o=vGFEDXvD!$wbJ8nkrSe2Ug&p!l zZ>qB0tj8@geI0G2`CDhD*YESVQ8-W0Rc8~)D%e<+?FQnHQ}h+5AWJFHlMe_K8bJz* z+c}V_?vP1cpX`46l*S2+g@58>S}ruz(^-1nn$WikQ1sG-Ix``q$Ro%v%IS=v3ce1+ zx=cKPVw9k&J1ET~@5{@krbRJJKz*23oR0(1)*#8^Mk$~l|A69*;EA9po{VM~i{p92 z5z5zgb_!mtvL%SzNu~xv)aOQJR-TCQONk>Jr?HcFuMEoz!s!zmfwPNBUl}VM`+#CB zzJE2feC@o(7Ro2<&6yK>O*%F=YP+< zL6Od%qxT!AmfhI;I207y-m-zPRJp!Wjhbf%I^~HndsbD}3r&&~bvRCReA)KJ+IA&k zma+bclF!kD9jlSio zSjrRmgg}v{l(8T-%n$W&LgVL1X(}tx+R!-;6J$JFPprnQ3p9zOFNf42lT-Lt6s5%I zgO&J=3W}UN`e6TxO654dL=w~!FVuFbaT{Ynpj1%E^)Z&Rs&dFFD9rAiT4G~yq`#Xd zw#Je@;bHALm|7^ej8&&PW3<1ACn{CcBueCvW4&@2W^aKo8_89+(Kq8p>+Kz$nq#wa zDR?=@eN^=4GBI`zIgu6$5pxRlKce7&bE;AxPK6B_5kX}uTlF+3npX}Y)Vy%|-uFld*k{fNl zb+08i_Q zB^pg`*pVd!RmGCSz$EJW1_>uU?ieF0{ zBNKAFoHmHBEFQ9i%Zt;zY^1&z%Y-M*Sd>Ura}f_M7RV}5QUq!R)NBjIT_m2gg2Geq zuw>9<(=?j^H6tj+VWlyOI05X?Y9Y>PKxUEsO=}~AMm5XE5frW{7Zi^S6B7{>t|;`_ zM4Ct>B62u6f+D-cmB{o%X+dG8I3)HY$zR5r!@-=x$6Ul=Rh)NFl|llO$0^kVaq{zP zv<1MWou~E~ubf+$!H6|ia9EMF=jHs+5NI=ND8o9(JtjHG)#c;+qt~V6;{9g6nenc&ziqev3lr9*Im4z@CCZvy`PG}Rh^ujD^{<20yEFjgVGbnwL(Hj7YdYJrJ{uNBcPlngM#`n7NLF7X$`0Iq_@+lsOW}8 zV_ZrGoeFIj`2l$Npm;|Z6f#yi9pe|uST6ILPCZW>AqB-!AVX>Q@U2oyjD;c^Q4xeT z6x7GD)O6Hnq4E$;ve^U!iN=-7vaVON2V0L+(MJL$V=DIk1tnws|53^lDNO!2mY}Tv z`&jZs#-bFOa}$vq*d$>$DAn97e=>b}A}E|x1(gK_Z4?y!d}Llo^L#*&?3Z~-g|;$# zI4A!c3lv^C={QB#7L+nC(KACz>VJ76AtX?QH^Ne)O(8n#6+xma^uM7*PE;D;sCG&@D;9y?klX&7`HuE2uQihp3Aw>qS6>(+d^uvBM>lRw`#{ zh)1U-(V0kw?CVgixI`f-5S|eEYYFjX%TQ%PAs_6Ipn_VH_z|;bED0AXK}F)}kYIvA zFG?qn@lp_UK*?`p?bWzyaxJj*S?`H1jgTx8P$iQP>SIBi895w%pJOa*dfxMG{+Eht3E1gI_{ z4){+LLtzaSEBP4L5FLmDl$iW0YGF}o*sv%h(MNs@8-9bNF`N4%iY+a#QDQ!LJIc~W zJT51e0&&wqMk0@Ms8R}wK$?vQDk$)NytsiNMPR$pE(BHEymR1nezew=^+=_{cHPq41YPG52cu;L-mikJ!0&bTd8B7lHWQ>W?EgtfRWVR$o<{<_R?ZdEiwK$x=A&)_RkATfevWW8BItI@ zx`Dw!@m$UW;ahsvRkWTTnSom*VRYO)l)h187h zv14t?x9Qq+@FQ3;wwr4e!Lfd!Gf|-okt`8(Zx~l4GVQ`7CsPWSD3u7JphXmUY+h@Y zHj$WlJ_jmEgr?bR%3wmuk4Y0X#Jj0I-;|bye?o=SDX4rV>a`oSe4jSFJ{LDPcK4P<{H!3*n`;Eh8! zJ4UDZ2^9)`Nd#uNg*J^EP7Z?2(7kvjW+`25q1X0w4bEt72mlp@l>n0;-N(6Er1zVww6^P{>y%Wn8KSx8YR!sQN zvd6zRtjkFa_X;C7=|@?-zoT86>7qGT+pZVlUaNLd+;FH!d@cL{{C*Ef?4CB{YSvsh zhO{>ZMw+$-+we!^tq^K(YNv+K+ZtIik<}(jNahEn_jzf?VL)AgXVQojN;ORq2Wy@R5MwM$_yU>DJJlKcHOfD1m zuFZ|~c61EP8JwgkcXpekVyxPEn2sEQR<8ofreiMsEW!M)VOoX~$Z761$_WpwdeQ_m z3fTyt!HJ#+exi1IxVx=mc)=*~vEBV9Fw^p!jD}noZJ#M88lfyt}tdfGLrve3z4 z3s@lvjGkY-1ek@0v&MPx5j$NtI-jt*W=3OfJk-TEOYj?*o$HMHM=WfIW}0}nyaWgc zwZ?9vlu+Ik3^X}FBniMzjk_qD=pq=rX!22c)!c8AIJfXh)p`zcIw_C?71Jd}hr5BBQWkX^q-;B?IZok5A37K&+~VRpFdX z{Ix%N(l@0=U4lMDr@N&XRAa(0;3({sbZ@bk@<_SK!-t)tDqDzFUKf`~^^|*UiOK2U z9$Lj@%@hjqQ-`4DbPO-@9%}pNSEoB!pGl1|x=MA98B3F+AfE}DXy)=5&d>~PajX01 zHy3-wuuX%h223YRDTd1*e?_OSYFYu z>N(^jydFk2EaB9Q_q9E^-!`-8?(r%?B(xx?%#8-clciv(N_M$%DteQwAS8 zyqC{zPU;DG5PVzO6)IGTrpk{^xwRAh9S#=k#-T__!X0b9m=n;$5D-Y@#1n%`g-uK%JfK~VHOaOj93=O`kvf_FsAp)ueiBB zt|vWay>4yYpogl9HziszX?EvG&*KMeg9{cqs~ummc%`V5LMfwhO>n63V+d#HfIT^g z@+%+g23+mK^9!nCLYqbo)^&qUgY^QhgmWUYK?|PFCr>$p?UY;`oSq+VW9M7VLa$kc zNKBwsD99ljeLZ{H$rouiqh!*KP7b$oQE1*6)dU|%!bUVAv=ll!KZlJp5R zETrQ0-r>OxAuKX9E{ENuXSMZk)`MX`>W-uRQU=2IwSBp+s^0oOr8(IRPeL}C+Z<1Bt+B-VjDW&Ob z>-RWq>(rg2!DDgx{f@Q8sZsPW&}O!hDQ0DkcWGcB3DdWB)1)P|wkQ?%ewTS=c4CAM zq{v1)!|C$Q!SVi93CWBY29a62NKl4?HMh^RvAQ@tJ_L)O2M1wJ?;RcNRPov45x?7} zU&5b9Lf+v;eFfSLTD4>JUnfviX&xQ#Z&#${I;ImRGfq8<@K0Y|53K0#i7@8tAgr$U?wVJ;>$CB!MJYT@)4 zF!Kjd(3n95kj(MX*~$KPwS@eb&q0@#F&Iy36K0~vhQmgiR}4w?5t3BwpB^^1>y-jP zyMBJ0GOAe}2A+>6&mm99FvQF_V5=-5^BA92VKE|X zes*zoc7DdD$Z^+FJJQiTxps}F=DEy#2cN|6&;+PpPgTvpYpwgHcJO%|M(IqAPH%0HAeRrimNPcJSn&-tV|4@P_2pLUPKzgRb~((84K zh&uHhlq6Fjb##1od3kZV4{Of4G)meR?L62V^psf^S`cP~D2z~{c659S@a%A>5VkIj z!0qfDqe$Meu};6*NlF9}+n@R$?1!Dm)mGK1_yndcDj~(gsQtWbBlx?d%<#Ufn!ru(_MddFv}^I*g!iNs55Qd<7zFi?&! zZ(h85ez8ww&rF|mH5q}&qSLC?D@#htD_YC2&B{A_N0-lDzIky)#!$5gnNXxsxzpnY1Awb0SBSdu`A%l9-Uo1 zd-3Axcq_@h+4bP32VL}gQ|YzKnw2F9+ykU`8^gG{e|q)&<+F=}lJe~P588*Q;6poA zwG5ETY4VTWD3#kgyT=#T&#uwgiL#QN2)GXLj#pM&&n6@){0}0E^~V0m)w7o`uTOWV zXP@YPc=ur!eb|=d*&3CE%9mFOsnDi(XZP^p=H;vB=lgW|n(IfQwQYzpH@!g%kP~no zk}5BUrEDCWKY#W1joiI1%~;2Meh&F2wQfcE-8@?|h{}+A$5$_2zmYW$tk3j5zV`qD ze5zD&HuyP<7*I6*adLw1?48`adiV3&XD79&ZE^7FPd`5B8Wv9&LqS7ofS-fA$+wLG zdH($M`}eP|_VT{f$*y~MXh}MZn~O7~MKhf`^>yAiG}P_gqpO#1e}41qv=*_<^(iF) zl!YN(rqQi{lh~}J%;13>UcP+y>o2b^n`y`L@Y5ftPGHFS72Y>j36~De80vFb?xWM2 zS8w0FyxcGN4O3nBZr^#-%jvX?`N3~r1}_(FM4o+k@$A*R*Eh61wX24n{CN9bC$!Zi zR+9b3!!C}A}d-d+^i_6`NOEc2;e`S{23EO<{{afGP z?HnGRUS7qP=Is#Y=Is!&Vkr**{`Sjj)PiqHB?~o^<2Z3J8vsL_&<$+F?q%!~b=Jn6NzJGbK9hYZ+|Lu<* zFwoRGmXfbRUL!QU5Gojr{j+DU-@kit*(8@e^hDYNQOCR}Wm!y;4+t}D)Bj=3U%mhN z6+%f~-B`P{8GwV$np2Ly46wC*3{+GAUPbxv>*fHwdhv!sr~%zz4ArCiom2uZ&=TRpuYsz8@s3B|tDFCi@`ajY3?Wm&7uU#@Htm zEJ59JrM9zwd~tEISBY(EXNI~Ft%7$AaCSjLKAZtk1DDE|$`!evPLFmg36#;K-o7EN%jI1reg@9t5T^{-D(~l9}snM(QaWF`*vZT@Ht6>SxAL?Vxg#?39wkMZSNiI zZkLl@6PD`;It=q*_(j+&Bwwk!sr0_65_Dehh(pGcz! zLR@HHQ@bfarmYI(&b4LcwzxDuJv})logWwI;3=F0WNp#yL}sJ9y;UovNu+7W-K*4# zv(uAG^U4Lz3d+UNyxsbJG*j=n^1a7#c>|0>aLed-Ch^xipb(o5?^}UQ!36gIL9r3vh!uDRL|h zzK;~hkReUQC_E->foHlhJ2r$^*dNtQN}egkM5ZSia9W{{!q5;=Wd23O@7geGW=D_^q$!E0$`SXHNqII* z2F_`k45!GG^V^6yBb7Kp+4k5p8fZ$frPNV6twze%cS27@pi2i=?pFX|40eMqoh+wnlIA= zP)OyXZb+I7kJ)ae@<%uoZxZ->d)Os--6hS)9Gdmemd8E*-yb$bK6SP5Ola}WSN(K}t|HX&FTaToQOa5eX z0+tnNO&)Hor?pT#RwgD$Gk^TU!XKnU(pu0O$yTy@9=C9^lpiWs&=h{Htw~v$uy2G( zMe0_N&^^hFu?YMzMP3&Rsr(@Nb<6MIg$OrJAv%6oL1lG54vDX-%u{(C3iC}GB0_j< zy;Yg5@)G|)rrs*J(KOo@e4Mxs6B98Jb7xM(iMZX>RS9NhW?PnIfn~{-nVFfHnL!50 zwu~~P%*+g#&dkbcI_KP>hnbj%d6_e7|H|rfGdlXg^?&+kf3)}7YayhES)>8b{3^wc zH7&php|v#P`MJ3G;U9J*e9JVT5{iz@m-OxLKZ-~X^t3sLmLPa z2S5-gv$0T03h^mjPjSD=1f>c>@AQv`hxxRgr1qdVrdcbncZhA`zC>#SL37L_V~N0U z5r(b>^eW7^*j@8Lx`+0oFrb0^CU8F``<)_6pxoLrn$t?2P1i1hat|p*)A$ z+UiO|>lFa70=xi1XS8Q`(rPa)#HVqH$CFwQ@ko_HfC-a<1f3q) z(9qyOUnj_UDVqn|CvozEixM6~pzD!zmQNYB*uxOv86Cyx)(y%HO6j3baYaD}b6rbY zGdA?n0^D~Ac?{fzVtiaNBI{|T=os8M5)gtS%@kBq&DgMDNhNt%IF^ug4hQ`tj%&Ev z0MEjK4Ns;Bv=!h_s&2;ShMtudWP)vuh;e)6lhac;u>lz<0@;Vu7n<&Ypx4Z=Zbi_o z0p?MVfdoQef#9f`ou3084AQRQ1w@bvBoI;@3xzdpJ%BpbR+blLf+P`Sr(oQlU4#>8 z40+r|>G5Iye3mCi90DIvWlIm}*U9;qg)kcSdvFEKE-x>t;J##}odft_RNw=8o>S3A zz?|l~n(~}vfI$jNYuZ3dyS}T+ng&xI4LUB;g5JWi9(ZDnyrOhhaYAN3GJ-rT{fe{#61IwwBV zkL_Y-@97&FpH}Y3YdZ@WDA&Bp8YX&XN;Dp@jF3@x$vQt@<7ABU2C1#gF zyJYwL@?>ufpBBUha9wTiDK{uSr>bXyxLtr-N)8tKf?%zAUh3m& zWuRx_6%dzG-3tjI&_>r3rH1g}E>Fy_>sRevKipj&tWI>7;vwN=uCHzCE{V&llg(|a zm&Q7)3lam^o?QRvtg2qs!QJcg?a|s)Z+U{m+rdm%%g9v{pI<*XzqO4|EAtZkS?=EA zsLbj<)&A|v%iYPwbbob9AlHdLb@7YIts78n!7tEPSCAxSQ;K?8MbGTs&CBcK#rFJA zU3##OlbOzYJqKY_PHq3(_TI+KKz(6ifR_t$@l(osRQvZ|e*N-rv@EYn4e+wo`}vPr zw*08vhLP3dldai-y8JjlH^}Nq6U+M+Pe1+TZ-0I{T^p@W4e+$md-o?@C%^cjwy}-V z^WFIY__lC+$etSM%QPidQds^$i`^CUTl33a?p*}s`Rt?nVMhjdW z-FP8swZm(dU;g@c`c?YW!$RlhpAB67lgrVE%gg=6q59ltKI!^NlPibTuD<@uzy1B~ zVjZ$F{_bWU{_saV7ys0%zWJlO+v61wwMTHFf9oBPR6VkJ`|H2{umAGpa&xpcS?p?x zPxW2=Q!4T4?e*a@xWvP`P7ZE9LFoTG9zus%DC18;&bJg0SX_u<#S{r$Jc!^MHJc#(_oho9b?vx3vA`WKEa(XIZPOmJg3 zd-+D>woUCleE-|uzCRq!_Y_A991VZ@!+R@kL=J}ai_a5T?b)ILCe_Y>G}T3Xlrr2ue*~Q$3MQTf9dq~`=7tRoUTHy zFo_%w zpKi~|>as%7D|cQ{YK?s3^7C(h{&v5w>Mn{FIvKtD!!PFEp_#R^rDHU83Qt$5my?qR zFF3PFxqb8L*YBUNwx*D}&vP((|C5$2FEXcYaOv=Le`Or94t}7$MX&PPr*`i?|N868 z+4@*xMlc(_`bo!55S7;`Up+eBo*!;5gv6qg8(SPx)UDdTd3pPMhsmx<4P;sA{PND& zO_Bgo%k={+&Az&va36O!FMd#J_0aOsEe!i?W3riOZ85{#;&iNg<<9&-Ws1}b6Yz_% zi(ecVKiBql*Qdc`6UKGH0EpvCd*}A9o?f5M)zjU@v3{_?pFUWyf}z1YrQX|Gkhhj5 zfz`y>!v_;Ev~+m=@OX2uEN{#T=R28vd~e_=h=eS%O1-r*)mN8~#p2}Zt*QU?_VM9- zYqq~KS<153d-vYhMHG`;KeV`|#sj`4D}>|XOru)dJ+pm&_w;yy`r(uEu+aYbgPBJ_ zB1Uy}Ykf|RkZgdLvx}!sKw_nAasTrE>HcJ0*~|LNv8^c9Wj_O@*-{Pd5)Xn%Xk9c6fPr zcebS(s7ejy*c*NLVC*7{%&Hlf-`rS4Q?tYQ9`0UT|M=prne8+5>P$V;Tb>-~ZKMBQ z%fdqvo8Kf~T3-Qgzh+k9z(}kZnBTj&d%Qo{nCdJ>uPk-m>)Nn`lAtoXvbj1t)KZ!# z@%Hdw@q<&V2N(7(?w_!=j<*)XiQUb$KNvdrgr=5vPpz#jO!PMu#rd&3yg0rQIn85h zM^_JzSNlsN_1TdEC*zM=rmntGnN|I&b@-%v>f!xkd3yUuVv9N^H;%6E@6gn~O7zOk z@S~2Urz95gy9;Z}vvNdDp=(a+Txr!ZXoy_jUmvc5Z9EQ5g`$leJ20`RZG0KQ1zAf; zVt|h~hbNB6Zd5Gqo!{PH?k*0)XY1=?s;y<>EC^4p?3rF!nHxujd@R_vIf8)Lg7%5^ z!%M1vwjXY7o}-D5o)y(!H#oPtJgexe&I$uV6lgFK%X?=wk1lVo4pzoHN|J&&c1C)} zj=YfM;#MfDgVesYA~lG|^5*%4rPn~E?C9e9d}mS7ToC8avN6&(a}|cCm31nqsRPX= ziBcZNM<9vJZWvzJIk~zy+nO7!&y5kgTS0cl!!Hs--_uKr1YpRI5%ah_-@tgt$v`OP z;(Q;cAi^U;R|_LkCmsMB4MVD>`N^TSs?0DU*9Y1c>DB!+>-#6?C)*41=7NMkwu2dI zvj92M3i^H3cwbXlY6$Y(MA8`Sp9|Z^7gr~0)lfrjtb}D}Vr=c{7nxqx0Yb)^(Vm8) z1gU^05Cz5;wvIwJ=JN6g=Uq*9wAj-I%ED|QXsY|B7Uri$KtvQR=J^Q3AxTg&MpG{@ z_E(ghWvO8T7fWMvXKHFAnmRWjYpKWxLx@`_35Pf@Bz(^BYVPC{l#pB74}v`9U|SWa+yw%Vucue_O|9*nUY_qRk9U-& zgb7@&%&gpmp~;1yUBa!euem%u3^}Es+fmir zlpI)-Bv6X_NUeb#`n3+sWF-jUSvq>|xV{DXPvx9vryt0E6jPE|a5Qy)f2U zkr^#!kp!}DXkuPnpK@wK0gwVH`Jq?~3hLUvi6!;^(cYR$-d36(C1yFfY&P`;Dd8fDNeCb(i2tA%9RZ(4Nk%NBR->Ri-3~gejqR=F8Gv!}l0to1uC8n` zuGi(QGWiJM?Fv#NLqRl|oL>V?`K5LB#=_JvGK>|bhPs?)?BjFW}&nRi=8&fTtKUtOJ_l=r}W85iv50~JBU!2ra7qp72zx|DEYaS5s5bU_NXYH@L994=9O z8iJK6gi0_e9svJ_2L=!cfIcUtpNC*EUegrIMi%G9gdBri1KLo4R0BgO zB_DAkJk{YpE6z;?=AQllko?G)a(uWSk4!|)@hRM$1jq%V7c^dYWg`!a$*TiVgKSt1 zTf?vb>;klHcsN>$7dh~{1j;RjSB@V3psfJJeMqhtLhflTaHqf(17eMcW&yZz@p7)j zGd~L<5K5IK`6|4#``~qjlQE0@$6zZ!3=lL=4AQqSF9V;Fivan$aOon+qq!CU+Kd#S zRFF4IDe;sz4+GW zFJKDF0ZI|Begm&he43pd&2To7 z`veF!CdfuENAhd|fem_RN}vLmt{IZ%Z7m@9f_FME2Z$}S3oa>0?ZV4aRt)DC=%kQP z0_-69kecy6hgT?{T#)3DV*Fv`EyIt(tHpfDz?R_^g5v?c@jUvHev_PhRq$ZK$AYHf zN2!mLPzGK)IHllf%FW5e|0u1FTzurMgCh@4J;qZD!6@)j)mJkvP`K{kkfYie?D+be`OcWG3Wt*8G*q|Nf=MXH1q%Eya++NSWr7W0-0pZ!=SmW<;1T$$74+ z3EgM9#8?>q4^5?yf3%44K~tm37!NW1mFYh7wZ?h;-?+B_(W?LGK7B}zR2sfwW}1F? z$}>NznI>kQXyH&zG*%jiIl1QPZ%qBn2r|tgH?rp659c*pz>FVX(si|Tlg~#!0-R4w`c-0EWej#s>fgOB{|Q3e5&~pP5AL=O{0F) zqWMW?RPnjyH#PmjZ!m2~E%fh4@5v}NhE59_!&=3R4D&I5lKJ#UqsUhAkCqAz7xSx3 z7)Qmqf0lzOQW~k7_-+QVVQl-7i`FA3^N*;^4=bUH=s3d>2bepBNGRVG+cFdJ zq24m1gXy55((FKVQZNynZ2E`T935G-m1+V?no5}chA|JCTulK@Xa`XkCN~;sZjDIZ zMUu_LEuq8QxQmEsZ$Ov78S`gT!;4^}@2oTWrN{fPt z9iomrgiR4m4+{kuJ<=ACkJZxIJ1{sj1Zidjvngf?0w2;F4-Y#D5X(m-(!Ku2=HZtdWYnq630UYwg6 z1C$zi;tt}&Tfec$p34`?@h)g7+ zwG@<20dQEv<8q*P830^aQ7zGN0;I|WMg{;B*F?Q>7h!G#=sM{BkebBfu*oA58kbo@ z2@*iDY;LSB04m#BTMnfVpwu-+U5n5NXx_r5LN3dbJVSxeX+`yTnJsT_ZL8N8fpEo} ztr#nm!hcxEgf~Z+DLx`lKElAsR}3o*a}F3%J6K83>0xaU0vKfxuW1$JRrgFz6OZmu6hNs7wss4J2aLX(Lf;7 z2~$ap4f7XxySuo$vv~gDh(|R8slBS+{zoN%EMOX`1j4SMu!pikOejKKnj#RD$^b?C zD3F7@dpnyes&QEd=m+z2@tQyuHg4IC;06R&zf{O_aj>&@a>oNUKD(lAXnF-mv10%? zRinsFD9y(m9JLU419}fgaV!!$Je?gJoLpIWbEg*8K^JFz_weX&5AR_VVa6B$EFxPX z`$7@Sqc7Xt#o5K(8-z`GPf3XSg-JE1(!Erq_4MQ!-dDR^wKsoLm9PDka zs*rX7%LjIYR8TYv7|3j7it#+1?CtCv-8p`t6dfZqtcPP-GsvBI4(4T}`%G>!MH+!bqB5>j#*0r= zS>7U>T-@Ac8a6kMtUhE+A|eaa3N{K<1Dt$Z7#ikmZ)@k^3K(!)W@*y^*vWSf4iDf; znHuSBLs$^8VGyHWn?-KwkKm~X`r_d1!G+uw91JvMyO>La3^8U{l+=HwVVD*y7H|am z3s}zfcJ_|0-a@DzVU$PbH};NCPJm}zoWd~ImK6c*LXZ|h0znoe23`qICp#2zBj-qJ zK~0xt`j7UvSLc*N|Lh^PQxj=O`Eg{8H<8&@2L{x{1Yg?D^@etxotU@l_-VDkg`2*)fQ=}7-T z){&dNrJ1>voeSF!!C=fk6*)UQg=NkuVa#xGfC2%@M|iH+l;%0{!h(P~4M;Et_&o3{}k9StF zG0~8LPX#)6Of6`bRN(DmV`^k#j{bvZn)(k3x5LwOtQl+>8fzx95TfdDtcLX?N0H-h zZ((YRmU&QUJ-fVhWN!2D?DF#BNBxI{{AKP9Aw`*}NC8|!?~GO}{^_6tjZP6aH6>=OGj zx<8>9q&%($FnTH}EvFQcv~aN6Tbk$_nAte9e1oH*m|fd5`41x_-(pvDEz$UbT5-6y zxdMF=v7D?;^|f`3ZCsEUL22d?z;7cr#Z#xXe#zhhZuY@m;uy_KoHj-IJ4)dE8O zvPPMv`^Nw#W57@e@j4EJ-+mOhjm($;jx*Zz@x6|zy$4T<#P*^p2z&q&ez?C2Y{fKk zzd%0(vM30iPYiX|6{bXp0klBxbq%eYSukW=5{v74X^%NLpi>#yU9xTvA-51s_!ua; z3R5E`92Z*)BVBEMb9+~;N~qyhw2sVg9-N$>Q2N4@0=K?~S}5lz=NG5t-L?7f;yYWJ z=xhD*Uf&AqnE3olYOuuA2iTPNu_%#3*4xnp#4iOV6=h^*}}wNYgtB=*xSk4%t-g6wh8qflI%zhADCX-q0<6q zvJ$t^&eq1p4k!SxFDm;R5eV^du{JZ-*ZOE+jV%e40KeOa9cF8950?c@BvRXeKyB`V zOMYp3q_a8)zz}raMDOEALmPA+Sr<7))g5C{&OAOsX2Jr73%BI@`WEP#uP;x-FP{-1 z^0c!+CGWM(99cq$zyn6v1UtrE0Y~x*tVIScAxK?7nQm=vw5KjFPKtRnH=;^xJ$NK% z3x95h0)mZ)2gsTb)GtcHw9$LaeA)LQ%?|;vo|-^*T#ah z!#)X%M3N6$y@9Lf#tMP~UyUWA-_S9v z+CUCaZ+(88zc-p?s7tem{R@f~NF<=k7&QyiK0>%-q?dQe=heF#Gf?_YM$C+w_3@*= zwFgZi(z$Behi6tcx76zZj*kw3fr=DTMpT=-n=0VQfdF#0Ff!27)rnSW39Bhpnj*D$y}>!X`oN{}qkUOkY>4*H@74NYma1 z>H$b=?`$tBIw~_GeBG^03{i=WsUuq)1|U*m7r>S^ z^o%cVFg-^<2B{y77%b>$t|+@}b7L^;X2u4(SiJTusspoLPqV&-Da0;>)s87_fuP^g z*7h2|kMtEi6cWo1uo9#cYJSL^*p zL+VEYsF*67`X-juyE|%RnJE=yy;R32^?VE}KM_KA^aDk7OdKenhS7x_Hx@gUK&#}` zs@9hFzOh9$5=2_dGs1=L*2V_txvr^ZJ^)E&)@ToFl=k z1$#CU`ZiY66v`mSRYGjS|2nYv~Ee}Xo99=w7nb=dN*q$ zT^((0U1KP(lvwWg7-K*r-aV8y~Be& zY$mimkkH)GBcH<(oW%TLZ0)cF(G3GDHy%_wz?53nDqq6kez>!-$P5)&(t2dd<(=Kt z(YDgmP`)c}9D2GqsBJy?Q1D1h%d6^AuIymv-Cjc#09=CQtY;YeAgj%eLSWql)2*eYXW)p9-JVP=^u^i9hzQ0INSu9EG>lVY)Lky zrDJ4^$v~cEQf5gDj%eiLVR6h-H=qkSpqSe}K3E) zVr*so{X^r6yC+9$ipIQXUpE_56wuc*Fn3^~3Y17KX@fvI-hb!?Qm?5|{lio1M`wGg z-iowfpcIS^jf@QRHC3QQLQd_#?8Y7r!Hp%=^u*YRY@mN|d~xsWcy**MJIdG1+SJg{ z*vQb>(upk!gtTUOA_M(6ru*W;>=f=ly*+&+GwS2h?I|E(q}~qbwvn-sk(oWoj6sJl zE)Phfonz*GF)=EqUJs1Ss?TpumSxo$p&SP@eO-McBV!8((h>^`2?|dx?VMQMr5EWU z(!$a2LD{f!<>2CUU4cwEfs2)?v5A?PshPElw{JjDpfor(uUWCYizgfoe=H1|ihj9j z=koec)m574?}?qzz}UpZ#ER)ea9~hmW-TC~NGgQUPASLaWFLc5>Wkac6Ou!o;?y{>(9|Zy!v6L3{tUoXA{ProeIrz1Ve1MV}mJv=4igVBtb$Or=h921s*P3ad2pGKtNb(Wxq;IZCb@TVwPU_z~tt| z{l)rFRZ5_jEvC-Y!qU>lne7)G4iX+bQS2y=U9 z7jimDqygZp2~2BHZk^xWp6xDAqYogB?CO;-9zVTaEOzEcaBU5BbPO%+ot)j^r3w!A z2h~tY?dZnI)#b_d5{ZJs-Q3$ZGI#j&cDK=A7|FFU(AGAvKoxE*fix^USS*x87j@5~ z6Xyr(D&?p`fl3BP=lAYjuGS#qEU-7y(KfVnbaiw0@_}BkKgfF`3cBa_ukNmaP@0gV zNj(sNob8i3Q=Hg&|PN^6{1^s=4lj@td z=Y!FTSfQh_jt-tDF0QU{0tJNz12Gkx**b%XxIEH~E7A!1ho`sizI{HKsE!jj7;1gg zHM4htGn4Nh5*{Mq`vj!WFx_0AY%k9!DNC*o;?L_>Z*Lc>rWCP@365+NTW42ScW*4Z zATggONvs{)1|aWzhZ!cg*!u>Rt7o6STrahy`@0xxeb6?tc6M|3@TRQ>B=-SHwc}eC z_xBfj%abGMKwmE;p*JqyzTK^McWd1_!CscQ2O8P9vAn(6e7}GI_>6gBh5ak14^MZen{(r|fO>le zMi-A?etq5@E{XE7(f{}Xy>|8R^yWjxQ!McDjV>KrJH5TT*k7F)lVh$2WrJf&C$GP~ z?v0d0a;)`ni7 zjBQ-KSYGf%W2yygZeVuT(%IANqyESx;N z07R-9#m?7{aUE9eKYjmtKHHEaaxm0}G1)T(pnQ!cvBfFPGy6A>cc)v6Q`r2FHqt*l zvwipF)5#Q+7oALS%30XEy1T&#EfR}(Y)(K%n`-}xS=G~H@?n%1RLt!^{`&cNyfTIl z`=*D!i<_Gp3(}i@0uEQ2(W*K?BEZ@9BB}2p?Gp>);Q6;NrxVq&JX^i@A9PLa-Oy>a z&_581lqXGZp5DE@yFJ^%TXz)8dSrN1wR`vWaxjKY+tMgmIC*-ryx4q^AL0pY|Ma#w zssYwFi4}yL$YJFYR?zFgXgQi>tfy~e;ppz^i2{CNUoOjAoZ6x~ynVbo*_>0N3+Tk~ z*uwt9x6j9uwF$m7N(NMeHw!h0_}-o@NqYO-;m!T^(fSN_ff3lBT)BAoOcO^C*70H+nAfV-z( zDvi?p_3`FBgqUI7L&M_>2an%Ap-G9pju=)03r7zYI?ba3ZtlKGO-v1B-q4*MLV?lw z{m1WL&Sn~t{TvMNTx0s+&E`=b+&u*GbrU-mw>PxUpa$6A$ms0$&F9zC*{0M0H**6$ z+<-jL2Ns9V)WD0W8dIO&-JGh?Bst`jhXzNcHm^RvT`sm~1-hH*>KK^YyOVu$g(3ml z!;KSOIibZmrq{5-f#5gNZn1*X>$ip4p%b~2)M8UvU`>eX&1muqtipf!}7_s^S3|0 z?hX}(vMls;&<8ILm(9!^kA-nfYe!nd!6^Y7>NZ2HM#0e@r#m zh8Ihe)G)boesg)aHZwLN9~~PXRgBN=+ppdC}F% z-K%?;>!NZPHDK~ar#G*@e7#<5O_R8oFiQ@dV0&|ge!e^;mWP*&Y@XkdZ78v*VH`sv z6RYQ+zdvmau6zjb!T(fERIkt#yEKgWc93`KRf|%J_Z?eq<13^eD(D8x6g;8 zrQzNd+V8b-+lA%vctSrvgp53diH)-dHxIX1a}#t^92k(vXLjy?{dzgqm`vuUZw#jw zmy4P3^%HVDsS^|XH&2h3I}77O0~A;t8kyQaCvKM8GXrt->KNdjNS)vae1#kjH}Bx0 z;jQcEXByZMRDcpA6Dy~mzQ61a6@{_@GuOs`%=SSL5^cgvcu6w4R?Z)v?~bvChKA&{ zt`##|w_je5C#qtw`QgfL?aJnHy)jNA0f%Oyd5$)}(@oVFeqBKYrq(Y$eZH7$N)+1Z zz5i%n<;oH8xiE2|0G;rOC?DHHa?s7e%H%MveXw!)#LC&HZ}%JBSplvlA3xA*h|BZg z_z3+(Ja12SP~O1C)zi!U$tJc-Oa;t|>BK8`@K7&vt#?|6)FvMehvy56_41aawJ)AM z(*8O>j*}f0E+40za>F`{PDWHmcD6sB3KF#^LjQd{Bzz z=E@E!8ri*neZJaT93SlM?H>f2!ps`2qWzJ=Aa~P`SVdN@T%oUkFMv6DxOs|F+n29C zeR)3Hm>L2BJQ_7THn(&4?d#=iJ&n`54>+#9u^#wB5iL7+UUbdW$;+2d*Sia24E=mB zuI;O5pMQJV>d%$Bo6*X)alS&IQ z9?Zksi4AKN`ubrjV0N2{!@DQC5sb-t@aFFu7@l0ec>Dgi-jyzLggxQt^2SCZ;G-v4 z6r7NP!R`CcpY9GWZ$~f>FAMDtSa)tdzEnU&HqH*n>{`41{Q2Q{ zZE6^_2A!Q?K$ueBe*f#!YFiS|TKnhsI9b@fetsf9i9Z_UEr=|W?>>Bb!TzL_bpsUI z-9J3BeE9g~X=gA$(496kQ(F&#BtYWlhXWfjvyzO?_4{wXzFuz44tK$WMgo#kTX(u<%%%z^v}I+qbvJll7^=E@%L>wsrQ8W0(DUyWE;AwAcIP9lS3bKdCgpUm^xz zz>6PQK6?1_?aST4^7ud}Lds3e9kQ8Sx)qF-g<+uH<2vWg$3w#3-`@|n0`Gu~&ehv5 zU!Tv_DtQmN!=U1+SUP$6dcW3{A$Bb#-Y8&&v=#ndRaAhKk5=i%+s-O<{ltQ{i4 z4K1C6)7S&w&L%4(SeDwqd@!(b7Y2p`h2qcLR744N)2HY+wzLTuJhLQlCY#y0`;4K@ zlhQ&W6BGpmN+qa6!14A8%IVp-|MvaU<<^X>tqy#`kV6?;!Pwodw5OuqKmV+4>c|ZU z2@MVmKsVUl!sznR{pYV=kRvwHj|dTjO4uYds%4x@(~UmL11VY6%m2I z(l4QY`t0quulI+`V^H6NGE;Nc@XXfL>(joxB*fGF<1bq90g6My!-4~){vtkyCrxXa zzkK`k?e1Vv(S?2!p*-ri`ShUf&&AIC;hhd#VPI%N6;gj+A2vTEw`YyU_H1)zxT~oS zWZH-lEF3<3zF+H16WSZRV@iMwC_FSINFsz_Z)kr1=KYs1nDFT#5D`L{qibMnVek6> zWTrlW?_l)dBUT0|o5E;t{RBQdacsGK2b&Z!-X=*|l1lU|R9hFfC$o)-co)&O=Hw#{ zkASZOMTA2C*s|fRyVsZ7qxD&NH(*CKprapB?cF_{&D6#D*db%f(9(s6mta^(ND#Q^ z1d`bDk?p%rugF)OmG>Y`8ub0`gR{FgPZx8|FhhNqA*KU$M4(Kd1RS`rr2}i1&#!b8 z%h7X?jW=}+&ZsXR&KFu!MUMKIKog{;L`0%QXs}c)@{cO$Svq@s#$#$_0$Prcw}L(= z{0om4i|y%t&W7)Pflrnl0M}MjR0Oa@Lcj3b&V|#5XXJXVP7Wc+RfAY!>+tgV%LA?; zfo>-6e*S1=>+K&FL5iLcA>h0VOlg=pdcrn>99>yQQyl^ACT5 z>3aBqD=jWA1`sDu)y0&Lz)(LwB1=l%)d+3E(yC^e>fqt!Y^FYrYxVJ`U$jkKgrT6j zONfgG4uLBO&Fxva{Pg7$?g&#d%6lfowlVeXr`y%8EQyo;pZ@qx-MDwe2WCv9^#aa0nhsQ@7hMAExBxp5X-$Ct;`jj8@7WE22#)HJws{^`@P zvOLt)@K5N8EjJ_qyfP5M0T(Wt7gjX5b^rDI=i7rN1<3me+EGoz_4$6iJHyvb=ck{L zz$XTe8W>!YV}eCoZ(&@G^5F67*XN7vx#4zXH~B4=cL6U((la;&g)sd z`uy$f?r?dus~(dAI=)u<>cz{wdLUQgp!?HLA55G@;PcWH@dIIONPZs``FwY@GS*pB zl%0{8SKf-=zdfq^vi$6sB1jBR2UTlsT3iUyesJ3Mt=+tR!_+S-pk0}V-dD7ZZr*%; zRQG23+GzjrCoOXi|2S~Np-4=S5Gm(DdA(~lU%q4KT9UU{gRndcwTy1wzCGb4Lk;`m z2U9mmYz8D7^U`CHG33DxA}fCT{^kCViWCECS6I;^U%Py{-|WwkIO_cgMcn=4vyd^s z)B>JAX=dBp>FclGo=#TByJ|~vLAwMqUOsue-x8T|YURQ6H=IFh6TQru#CqwT+P%kKQ5Qq^Y8?|tJ}f3HD=R%A zT;k*D&Iv7&ZQgzT_Hw>4HPFH^S`<|>llicT$+Xo$$lZn|jRONNs=>^#+&p}f8fT7Q zzJ8`7zNevrV0|U^eY5-bPsfwhQ7k&*O`QbcM6#WenHVnN;++!&o4J1bhC3bg1_(vi zOv~`n>EoSRmM?WN`tV-Yf@}swvXNUQ;&|fDUo*bC0EF%@t)| zb2VUhIR#Z+)B6vP2jdkHcqM5@4Vo5N*%&qI4m+f_wP?fii`#+*#Z1>)uhrpcVV zyo|UY0n3f-W|SF%OZBu2Lr_4QdB@oL#lzV`TbiGP{`>cO*4{x0M0unsDeXCqg(fHw<{fKcvs@0pxMx}^YXJ20FrTW<;T@34_UDF#}8ir@|Q0+dvsZ(w&ho} zD{)i1Ug^s8v(x?MPg>@l0i=itBHW}9pjTbtsv6yU{_W3SaC4Z3c6>orW=?4FPLiOK^e~H^|@!zTF%s2y!)k|MNRTM`1Wc zn#)VFfaP&<@(RxHUAz75U;p}cy)!oiUUDMgsqLFPetOtblt*})y#MKEJv)AQ60kv) z<+-4M1w~{4*6GE!fBE}Y+_L5ERRv&pFQ|dR-QDkxSI4j|KK|)vU0a{9BqF4*05Xa1 z<>n>9q@I5L*M9{=$D+IqdKT%K`Bm6>Zy%1P>*F!0zx<+W!wqE=Ov>|zr-5F_QQ8f(BZP8 z157`(=o*Jt&+ktc+B3wChIj#3(8UALe$d@SNw}U~!ni8=*2DL|e!IsB-(3gEsI-jy znm#yU&z8G$1M$TFpo3E}3WHEqS(XEaMQ@fcx@2JO`rBW=Jsho!cO%;rRH}s-xWk+C zm7d%{R})~7P$n9@Nu?+gk1}5T@WS5Z%P*k*I9yfsBAFGuv$>U>Q@d9e>x0GN-j;g6 zRXO{@5OOit`AH$TNby3l+h&elaAMPSt+BiSsg^lqt)uIwXB)%i(LS_-j2-!*48>eU zA-Ed3EUq-YQHl5Duee95hM?7%1>X9?hN0!-^KE5al87QVwCbSGoL^Q^k`XQCvpJHa z8pYP#mv1kZJ9ER#Zk3ik#tR*c5$v+#K)g1ysG_ncJxVI{;RmJF$v3ZGKRul7ER1&5m4Nd)H4n3j z4k;T_C9Y<0HClW5M}jP>vZf*z;t)KZB%!w|GXO&x4 zT~(AC38@KwaAxE9&K*1rXIrx}NG_%&CZrbBg9_$&Ltc~Q=VYpgrM` zarkn1fvL6f&6~F`Pv^LY)4(OfC1zLkEgYWjPBv%284pyVjdws42BE4JW(#z@b=(p?l5o03wTNl+4tdD^0CcY{A{~biOr%dm?Dw<5Th*v)5-t? z=LL=Pl!D6IirfTjSl+%d#XXB>FDP@wSVB%pd}3xfjN|%vp{IxjPFLT`6HKb0ny)O+ z174ToEsQGcS~!3Gf->v4URGi`BxhH2&mCPKFZLEkacqrrbTM#2L{EJbNQ;Z4)OS|4^{*S3!0lLINv|D(IZU1NPJX zIqeCRxhe4p8D(9nqnneZ{*q`Pdkpq}lp&BapDT*W?|@~$eR>1|4n!+pafwWs+q0E{ z(r6z$BVgj3d5}`bD6XmmWI2Gx5k@d&KEn~QIWyQ^U4Vr9%(AZ8!)vsyMAIKL7ett& z(~By~3jjbC_=qC&+Gh{%!5(z6tpfW+K}HH~BQtwv`>L+OD4wIKo}QWW?`85+qoH~& zi7D(<9p1gX++X0thww%^+J-SWINzV`q%vmu1{N+p;=st%qRNVbw3t8$62%qw%pcu9 zKi;0AZCDMUZ^u~A?w{^VcN9eO987feEnNBj!O7u4hlguyS0ElKr2bU(&Fvj+DqFzg&NpMU?5z3^-hWH1Br&ja0Z@!(gjP%Zy1~dp62*qa}G2$bji*uYm&{bf?$cT zh`{Sw5)2Bk{u1PPL5nLmJh7lb2Ivt$TAOg>)Rp9BX5^GM56-SGPxb+^>+kJk!(d~G zy*8iWMvP4aBOZb9)SGMbNIODi7iBlJ4NfhnHZ>PC0nn9;f+$9L z8=*?on=7-5UZnSsKcl97a8d_-{U8i1(~%=fYp1%mvL-GF)txI3$$U(`D|*h5Dp2nG=YkOULV z4(~H){$v7;O0u^UDn{x+PdgI(ke}KJ5@%Uo8Nz9DIeT>XW;*o5kTuV}PBgMET zq!DGM#z(_L8Wt4?(hN{`lI|^&YzY-OhWru~oTRLcd`*U-fie`6!NLp<4x(hd0Z|FD5LYtl>^76!`1 zWO~4~ipju7HYX*4;twE(U>I*94*+ISbOrK&n(TEX$ZCF_AtFEtO5JCQ5laKZG=Lm_ zO5vqeQL;XmBXYpg4nZ9#4KbV!Orj~Jzk=SCG=~}aNTyzDK9eR(l_0wmBb1$*7#E8% z1wRTz)*D-yG#|+PWnBKv7aP}VSTPa z8f#y7JJ>FXJQ^IRpfB%4L>O^DAZ$T8%1}Chbpt;3CS(&pc3ud2qab4S%mEEO5t)LU zl*A7~XTWfuBPvcd$`l680VS28fi>At{z4uFeEk{G#PWvrE+m~GiyMi#u>R&&(wYP2 zih+L<83ox7VsILUZb6^ZS=KC-bf8Vkd)N{`k>K2qebo z*Z<@H`M>`DsFv5HO4*cp!)W=fBCXER2=I1@qhg9{?k7h`y^Bju3f!-f7u(Y z2zS={U;g|5;h#* z|LH&f^9O5TTI=k=?c>GzaAlm2#ZUkFfBxB0n9;rY_J9BTzyH7g^+`Qk5$}gMDla6x zPJwG2d{FD-?FFG;z~Y1T-jm$5R0$56rx3xZQrqZ;=sgtk&^?&=@>*>mHT_*88 z1;*qyDOQiKE)Q2H`|1mmLj4dI^MG0wREQufUYwm24QC=q=wos~z;}3cdwB@RKg2ab z2$P&i$#4+zLY4*b1c6Y8nPf5XrowrbnVJYT9BCj#cFJ3^4Xc6dpBe3Ife!-SE9eG* zWe5cAq)=Co0qsV_5PU^`0U>}cXJA<-#m7VfqMDdlQr|6~np;?yMIID!kTYc85Sv#_ zOiwF^K*9=UZZYDkJb|AS*G|$zBf=WeH%`LEwWSZ$0C@^rJy7xuu#IL&9fC9n@_s$_ zg{e_Ui1lW90~`eiG@LrQP-CHO1WZWal!Zvu^z7_3p>KP;JAvwM1#>$EZ51Q^EhTij z^>TA{XA1*D(d;bvco_y9Fdijm6jU@ZQdlZf4(u9G{YYMfY+*n6=Ep|5tMN+Wc{n*Z z0}B}jn(~4Y2-;?+AzhrHyjjI{(9>BUN?0T_K~frf0|e{4WngBSn*xVfw1n+!XX^xv zH05ts5SML6Lb%k=*H0RWT-ctm`PKEcADP1-1_nfC;-a}T+3>}`})G4pU*}1%Zbh5uP)`1KbzPlp|gQP31uoB`^dGPMB zUG32pNnBy8e12nldt-5GSaXP$*L4D}4w;NCI6W&cUVLvaFSY;)f)Hn-dqE_U?Oef= z8amf{47hn10$r7PZAVB9>tJlk7Di1Z(!#WRop|6+p`_;xn@U-`2tCJdd(1msE_BHQ{AO; zV!#1>p$1w9emlrDHRgnRnZExCl%e9(dd1rD_4UcdL}zJIkiZArlKHKZK(4?)KGu>Q z;sYqAUwCQ>F0;#v6TMZb5*MTQ@APcAQN?%zT-;n8E|cDjgvSNdWLfXx>C@B2)^ty4 ztS{(4p`@5uh18MN1!(awXiZ%UcWGubhVkzHa%ZLo`ZIiQ2yo#PQC~m7RXz;vVHVg( z1;I&0Eh7t?$gFA3C4Y^!u>(K0T($^;RATt8&kBX^68L5DKs$PPWXM&c_--yxK>-J+ zOog-&5Mw3?;LC=i2URN7faRTSD%%R9eLY>>IMS5r5%ulMJ?M_>(*nHRJbVITva7n4 zE8FS?c~h3u1u!E$Tc7B%eh`h`VA2O`GlIG9u3o~({0`OeCR($CxgH*D@MTtX zjU$a_VWc5lY_IdjpR}zw(dgyD)!o(p{6KZ8)EnwE;`nme`puWO+r2rwyagVhz{T)1 z56`Q&S0}oP!f7rMPz%oMoZY>+yE$1?H04GJJX}5ap;=9n2agC@FOSrw1h754d;(%~ zYLH5)UY{MTP4#m$_<(?yUrHSr!Oi*BR98tX*l?XZMRAD1U%x(IZclgShx1USAS9VG zHg-W9-BS|DGW+<8j+J*rN&n*E&BM*%!a!xB*xlaNi5;BPHV2H!~%@aL8D>vZcvYLmO9*cgJhvjNNI5r*mvc z4XW(+0`fxY7~At=Fe7*-o^8+eRVMqhT%0`wk$D|+Cr`j;srpJ|FuU%YfVjK{MM_h5BI&UrB_IP=L}$t*GJ34bs2%)PN0$w$z+BHMlf8RD1x?+#8{N5od%4(D!g?U7My7zJE6`6$ElK`BctnKOM=X(14j<49r) zJK#Edxjk4Ms!f%0JaHF`&cfychTF-$suXg)!htABt{Gg|ImRJ7+6sPJB8&{ns2yI% z=5w|&(GKeYXEUs)xMg^DZA~@Ylp9J2Pa_9@Olj}z*3s$VI`*DKe{VO8MM7!s!s+AF z<@QWZ8Jh3z#as%|R-9^WMer&c>LQdAme;0S+dn=4CpC5u9}n!OafR(udw0)h>u`N~ zF!*fw(%2lLvY@tBW7jf;3&mZMR5hU5Br<1NJ%p-&e-uSz){SV|s_ZC?60$tmzF-$^ z#-)*IYZ!~n&Vd(I*fFubf3&}<>;PXc{Q3N#4c7hiO%8}k(URH_Q@tB$ga)FTZ@47 zHZe4F;z#DUjj!w;>=KJ@auCl84T~>opWMB9gxSez(}H}^d}tik_9~Y*SEu@F(gQu< zk+Ee-Q>zD58@s#fGhi?e7kEJ{FCwdcWc>m}%WGq8`H>hNj$Z`Ys#rkM<8X6cINaiT zrcV5*!uD|-}8(!R%Jn7f0Jm;)D(u9vd%d zTJ7N6=I+iaH081)L>#sNn!jx{JU}&!V!erg=n{k1w{3Wq4xnnZ)k0t2+*ue?OsQA< zVEb;z$;D@L{lYV92A6RF!FqZt5+$_V`Nw2ecaP02P7gK}P%4SOi9I(or)6{nIa}b* zugQvpgp_Yca%mT$M9;VT^MkeNK^%8?*afN_nO%YsVO5$G!((FW6_5h!f%?GqA_hAl z5Qn9IG|0hMPVeuIiQE|{3$c?Y9A>nln|VR8#ig0kUFYJ^_5(Lhv{o-MqNFK3o}VD~R^P9a0D>@J89x;=HmSr2QPQ zDBHLRqYK-SN4$&l=B^5eIS3#DpH_)b$|abgXJxe+VInX)2Slag{xUTSuH}kUsi&=_ zl_M)4xw2}nnNeAg5XPinW-{Oem(c6tTQ}AhMms8PL{(&bdT~Sduts;)7bJ$BIdSYbUKCU4rxxk+Jl@;L z1Yw58WR*74y=h~Uj#B+irCEtgF^f;lt8D4+9~|gzF3*e&4L%igCOo;IwsS=DRxUBE zy@+^Uq8cWTFRZSw2@9KptdVy`wFC-#yIZUCQ>dK_KBv|_jol+tGc!}eHqwP|y+t+c zgO?VGjLwefjUX+_2EtaVD-(gJqV)Jkn+?s(koLY&N=|5SQeVltECtLh1XULnD5$n} zz;t(G2x+PMU$r!9I&dttnc+-stYFya=!BZ7kmN#tSwd1Y*qBq1BfWMiiIXuaP7N7o z+uB%Dl9Qrg&tbgh6xX)*4v!HDWz<(qIdA9{4efp7)2h0nuMrQu*%^%Qp#cJMK_=1U zrY9%FM#seKy;32w(J`v~8PHNiAiBPVIex09q9F_kZH#?%b1`YeSyfh;od#rVd~#-f zd3_thz-Y0{Tr~iRoZ66i2S~+=c1u%jRQ=Kr>2?qbakzS8wQynO`;@sOm*+o zpt2!`KzA?;jc&X2{8P)ns;Vjt`XXq;JvW_U>}oVct09Ku&|i*jr<%h?^WFg>gLFS8 z*jQLVr(Ra1Z69;HiPdPlP!>abiO@3Ht%m@jU2EwlW3$n0?liexM5WbbkPfCa+tm=V zq=a}%wuuQgfUKGbG2BS)k%$~#>|sXUkY)`v5_SbyMi8pophlE~Yoc)FrJfc?{6&J6 zU`v-EI?(a|P!~U11t~m9Ku57!7NwOh{9(z2ByVI<599jiXw90n0&Hfkn?l$C($x&Y zGM|xDCsO1}Y?I6cq>CRd1r#S8qiz$*YDwmx5&iZ@#S!i$L`)tMl&vTiB)oJSY3S%3 z8kP4)6dZ`I9%ydd32GBMz!2^XR9WlbGlH4i9c!x$u)dIYMu6VRUq)c6NgApGZ z1gcFo&9Adc>N**_yRyDUo4Plm!eyWscN1r0#6ukv=&-I)X`#ik8*Xlow<&bo&C3*2+thGjDFK z&W-gnSMUZ!hnp@YP|=yOlNbn7*dxP<{%C+wZB2Q;9zM>W4_B+Ho+%}W(IvH#|lz?-TJ0!gvUhDb*3WJ7G|b%Kn` zO0t+Bs1S8xb_qd#TFTMnFzh^S7Y&l3wGZy!yuuq;WnNk`SsD#p%nh9yAMR=>$&8N( z2|ml)VOB9`wyBohS}~zm7rN&+57NzpZV-2_GQ*-OH<=(~Au9XQ9Odi-ZI!t+WM@_a zrY}!}$7p_)!5FR(8aEcvziX8>AKu%#%)4j_5Ag^``yj!7X1aqu@$^$cCkQWP7FE>N zLn70&5wYsxN5>aBpFO_4KBJAMFvD7l5B-sF z)bG|qIu6`fpB-$i$W4lgVd56Xm5~4wLl>)aNpgF4-#%veB<3=qv6c3N(zb~y+4K;j z4UU1TRZB%qVsvzTI{O#s_~1nvJcTLVOVBAc10~tiB(sy1mYM+*=tV=g@!^Bp8w;bI zwFRkhn8|!5auMvs8qtGEeXjQGdFS|r_^cvEDbnv!r@LSpFg^9*PFZcAHR^0T~gE3LOZ)+I<03FR5bTbi^5MH-P>By!+l1g zf@D=4BQwk&r6+$0Gq>J->-E=p8imMZbuDy{uR_&lldVMjZsYbNb^p4yINDL2m&`a( zoQd|4xh1pz)@$jFS6+Ez-|^7of+|L_F^v*!53FK;f_?h<-qzwsdu2{yWLQL8dP&p3 z%+fLw2HGn#qfXJQ=;haTAHs{TZ0_po>tZ6Qy4IK0T^uDM{E&_wH&-vw9)(ez;n7LC zRfKS_&=sq!HDJO_j@FvOrD82>vE3GyE6^muDqJhI6Vaa3(kD z896||r1J;gc>X)jz4XSrXX5ghOfWJ^C|dqNdTv<*??rT&Sv!l zS{-g+p#1S;r^0Dbv3&o-4=F<}>UCsZ8(FEp z_|<>^n_vFyBidbX*FW$s(fX>vwI@IP;RiQo+Veu*{@QQ-#=rXAufKXYx};lei#EB4 z@$$cS&%u!F_Qgj(`PJY5<3Ie5zx?#Zczs6b(f#`l1t*m=Xp0cTdvoo%A#Z){-~Nl= z_?@r6b~L6`*QNW66Jkix(S3UkoKue9SAYGF|M&m={eS+9_pY?_hjzccKPbBJ;sR;R z-@ALcyC`D+w|?he`p|Tid3cvFSZ4U~clNwnx5x zb8ye@17}kk=N|m{mw)%a{{FxI*@v4WnkToP7m}#lW(}D7=-zUFS?rOQ|M0he^Y^~F zD~Rh1y~l1cSG|#W@{G4S5mnf`{?X5V^|ycbH-GWNJF^`{ac3wtJa#^@qqqWYcElfMbUx6ExeH}%?vi?MpJljD2O29ePeR?=I7a~`l(emcC72V*E?WRiph25uPxjAu-85!tmDocwv zeS(=(NyY8cw?1Zm8*dJagPP;WwS`W~RjvJ#i|dBsL$x@|7(#7F@f!*k7G?;ppyHpZ_jVz?6dO zR<2TD-&mR!!RCV_7!x&r`{U2Qq{RH{R9AIwqKyotOK~gjj~iR7V~trf40`oN22zEj z7S*&ejFhg0^+hRRr>O4^=ly-|_QzlRDQSjR^+uh_Ls(S2Zixev3#(UG#+$MuPV9T* zpQf_x6o%|1rsd)d4l}!FeU_t5 z2dr0LeEIDYG1;Yc+{$pin<{eRL(g#YPb_E}S$p`&k3ReU6P3bX#bcwQ;+XuyG@F^_ zjg8Cw6-jDD_~MIi9*N8XoJ$MtaCLrS=;@OhyanwZD$VVK+e!_djh^(?Fwy^RF<`vZzX%=gvV=C!`NL8^Pen1(E)v&n^3O`S}Aq2DPkLJ%RWP@>dJ)SCPmg|62wktUUFa-*9)hla+2 z(=--&`vtnPz4UHaw(^U(x%5hIE}RNF6%xzP#bw*>rqldfSCC69 z*YDhzrAflRm(&37KtwLM>lQN5SznYy9%4{%R93^-#xrV1KmX|N(onO-D5AJt6xVZP zH}2funy5}VxtF$4&%JgeCf~aj40P1w#`0)Mj5?!!eCwm1{Nm?7`Q+YWPgQn8L})0^ ztD+Z|w;sIzbaSA9eBf_>?d#9IbtbK{nd{Wl$Ip`nNhQI5`~}q#8{@5I z*~xJpeBuX9zWU~C=5OLx{5(|ok%Qg=-^7m{P~vh^l(J}R7iZ$#rZp* ze)V&Te72_A%Q9&k&)ctt#!f8XeEi`fDoD@nd+8g0^oSd;c2!3J$*ZZ94qzI+;?@+A}u0I6u)% zo5WKG-^CsvI3mL|{7$bMyT+{2Uwrw|d-L6u**J3C3$y68jR*hu{fAdNv(LTr z>bJkaYt^ab$~K_cct>SM_;H@IPlO~_l0^H(&wuf!-@CUwP>Z7B;z}w+6Fro#-+%gO zgCzY!uYa5RZ=TmmTgjZbJU`x9l@)nH$sXr;BVK*-<)8n>Pd~Z0GAQVXNTy?zH}xT` z_Z~mo7%GcChO~e4yRRRPDroE(omrS2>#WX+ab%0Pd89x4`A@!o|H^P91Iog~qtzvw zT!$^7N%4*k+C!z)bFUwY%xmc4hBgDsa$-;On0xeWY#|)~ zltA4+G`Du+?t^>l!#r)OW#0?0zjH3Nl$hA)NN;mlX7rgOYK#+|-!@Gnm7jjWE8l2q zMSg~6O%>O649~68wDi5D-jY~#A$G7EIii_C4A+M-yP(wfDg zmWo0qLaBvs|K#P38@J!vH069~_nWk~4hl~rm%jaCdt-TSV(2NuJO<3So-);uxv11* zkoK3)80x->McU=xxw$&gQV@IQ(4MzwOm;ddy||hg1`U-3X(}T6+;l1z5by8WcFxa3) z?Z6o(F+MM}Z}QBAfsZ3l&M-&f8#MSWFDuT=h!0mA0^T!7s~DbLQQp-AiKD9d@9G<# zoLgGmxPD_}VYs!D?&x83MhGV$oJ8_dAz7ZPAwJ6c0^=eH5X>zuFv6{e=^IQg9~`?h zcX@?j4p#}i*Oufm(~NTScs`;}S=q#dqQiojND&g9R#Z#0Ym#8dU|$b2Kl^+m&ZHnNGBz)MjyG1Ny(|H$&BQP3=KJdAtD}|p^0dtfRaht(>E|YPS|YW^3uxc z>cU7{C7{eqPh(1mhNmYbCnqIPSI;)WbOswbERxvQqpz=@7@yW9_iSmhv$`OY$YCxx?CSNKn{!;Gk09F2?|%1W zbZ&j$G_%Q9h(Faby@qjd?R{hOYd2{q_}*MwVG?WL%%+#rw-V3oyU5JVGlys$|HeC~ znMTbs*Ujsz)BR25`B_=H#nr9-IOY%D|M=Opk=o4YV2ZQO#^%;`^b!gi>#0sXe+b9n zJ3Of8G8mQQFts1WyGkU>P3`1wFvRu~n(^@_#}MWd5t&sT17nwFr$^h1VovUT>Dzz& z+}o#9>xOx0d3=vNmx}DfnCLi*O0D0elg|h1{S_&p$M)?#5}H=oF*q^1xHvsfn;ybQ z?5}_Gl_Rm`%IA6d@cMjjb#{C>-f%hzZmaj`ZSei;qqP~~C#Z8H_pE~n@GDmqcn1t; zlJD2Q@zTMllAeW|51%}|vB(_K*bBkuc${h(UAg`K4}SE~^|AWwXtFzwgrtkehl^M}oTtekmuH60TN1uPRIaHm-h|N7FAZaAAUyL#%@N)9^SL}XlQDgAeS!B z4Af>Ynf$qLy|DLOX5$3iMW5Z@&`i@I@!PxhpNh?I=$%+x zUA;Wc8|yLH`K{NElSQ=t;De8z+=881TzAfdlG8eV_3;mX^5wJDe&!b++P&xS`Q#F^ z(3aL#7lvvxF3>aP8{gd%oWb;Bx`p0dpJ*#hjXWP55|dfci8=YkV;pG1KDafXrKe%TfJSkO;C-ZoeP_3jg(n`e5ob>YM={ZM@%G0*{>i7e=Robufqe%~ktcJJe96tV>CUnw zT8w;ys>kT!F7@twa(j803B6%w^om>CPfhlhpFi6e12vAAVtMXS@A7DKLCo-rHb;ZEie~d}if#Ow+6JV^ZeYib%29PYw*3fjqtHvAQDV?7o+ue`PNz zg?*P%2M=$pjJKDkM}>rh$3xl6x8MKp(H6#m;0$dFPxH*(*f%*pH`Y~~8+CNo%de0W zPE<(^eeYev+?En1jfhPvX`9%1_|XRxC3hhPwDvlFHY%;8d4SZ(;fvLop$Ff1<+VM> zqw-tHOcPJ$hj=IECK{hne(}<^NAEvYyXaCL^O;O^J~p$wb#Q8WyuY;^lI?oq?E_~L zN;_#)&I7|LdoLghH6|%ruhsV-kx4t$To`wbcVf#zZRsClK5AoeVlboA4+Mp0RQK>6 za+7i1<0R{5U}m#QNORk|{T{*5uG*Zq&|s326$u85#c3Y83km*H{zG7E z{o2Or+z5U;Z#SV~(Y!d+b@TyAY$fUphXQrm4>0N=tw6?_mhp;^n7QJniG;}JM z(O8-ud6p4h=VLN)Er|H6EzOPbI@sgjYEUE4iz`RcWeD{~{H zJESJWC1#W~57Dpv2K5hI+u}mcVoBrD2?z`!c`X&0krZ6>cBCa4pv+itirpB0j7vgQ z2zgbvwyrR~y)Y>t>dc5o>pe4As|c^9Z&adJoUSG zws;As%EWe`CbuD{vUPCs5_3)}a5U&I=R|g4U6bVP*@P#Tq&$XzZ*qUSN-|5nBKBu4 z#4=l&S10ncnKmALnEX@n`5FdD>c76RJk{F({kivL@o=$5lk)4!V{N4b5cPbQT2$XP zIy1{`k?_8>M}|2-y&Itk@wAo@#jt)J)xB5CKT5^Tq}$} z{SHM5$HFqI`)1c}7!@0eQez?_HLQDl_3pFx?_Hgu2YdMGDJQ^pWIyNXQl(y^+6$V*}`>h){0w>?SB1@eL+#V zjYAZp+}KzgyJ$~YF)8_Vee>5Je?;Ce)qeX|1pEJ=*;`y0(-gs@-!E?#f z9bDPEA&!#*&tz}jz&a+^?tSp_Q?k3NbbL>oji6lU;?U&uWPejp()okC=w z?w2n)!03cbW|S}8eDcu;x|^dhPGQji>>Qe$nI7q=%#1v-@69*&1x4pJ49=}>ZlI%x z6DCISfK|YB_09Vqd`OyS8}r>yDYgSc1}0|brf9vNaPHvSZ|YuL(K)$%b#o21Se2Wk z0oFV$jV|4M^z6~~*)9gNW6(lkNu}={o1UBTCamykaW0Yk);Uy&33^5X2|*beGDf3y zXG6JExVuQ_5EFw&E>>m7oH@8#H-((qo=N$)l-?3Ah>anjs`${&2T$&AOm&oJ#DoNg zM5Pqe5(sAubti95=a1~$z5jSP%?07g1JV|9^aT_2z^}Gc@>3L zxAqK?N}(L23&-~9g(abgjHAoTmr<~_#aX-=YkE0jw{UMsIBv*K3@0KVmub=Kk)a;m zI3rK<V7Cbc6+g3@x6Hev9 zlaZNKonyGgdbzAErSy=j8yC8~cXxBTvmyicpGWnaaw}ZxX{}Nc4bZiB>(zH_FJ=bfKY{ zRqk$g*2Y?jQZQYiOiiTTjaX@S6Ym(bfZ6-*iSYD_j!|A{=O-zGEy~WI(Mgr+WUk$Q zaDQv23k4g&C5K#!W)e&KyIU%=W6vLxQ*$n%5U+S{es*$@q>KVGRCrD8!hYVo_u#$d zp~ixg7@iuFvcXk#n^+MFZVoHATkawc%5&4B-Az@NXT^wih6@ST_3_q{v{>Fan2T7$ zgzX-ld9}$S?~rXD26>p;v@ko--$rwaeEP^Vb`EP6;Qf2ZYk7JM32bB>5Q}F7dmGjD z5vLIz$v~lGU}+I`1g=!2k^wU~O_9sJ`|m9e)NoHx&RZSvv$E#?6vg;OG>q_YNY&6`FBRvGAE0FHSZI26@60-BjLVZfjU=!Y5DIfOT8|#yTI?=Kz*z}8 zACu1GIb$4~s6h@teR%)w-TO~O=F|_&U%het>LNA!<;+{JP=y@QbN~LG&8Zfiu}+^l z3uTL|m}SY(Kq5pz%s<@24JS_;_WIWP0nPqFkX2M2HD2r=CxoSOG;8CHInif>NN^X(d@G2QpOf20*VRJ2vp_c{TTm}VJRb*OaGIKSI1el;x7ZSKo zGCS(VCKDmssRyfXCTVM8dSUqrQZ><0qKgW}sOkA73~VpVA;T{wBJ@11QMtq_tM=L! z=}SYM%?lXclpZN>dM@3Q$F_%IkMS65~L&9i-g3G;4^2=a{sL-?!3VWq)S7cbf{#^ zJ@t*32$zBGRCg8L^Enco(sBza30GCM7NxD{+4DsB+D8|#LmFagEjQabdxu6RXXY6l zJ2TQ&nihTGd=`-nq{X?S@)1**N^OT#H z$iNA5!7_7b`j>;NOJ*T28xhL3U{L222IcbH#840Qm=t|?^PWRzWpbDXTFXdb3JZ^> z7Zb&Hvm$xF02CMU?A%Hk=hrsUqON*_r^{zmbjP+@kM1S_WS0!cM`Cy9gfYo=Ve zM^@2Z7_-SREG=PtF(E~zi$+GqF=(-!r=B^c-H`s+#Qb4q8TV*a3i(I-S}Mp$Wu&nB z36wF7nTpr!3<9W(1<^}2^^CJ~QH1mlkE)smDME z!$vtZs?|p%XtY1v+e%lJlq4!B^T{%#uANoMicC>zEE&P}kUKI&{8Q*k_S@kN-NJd@ zWj-R(PDU-~w1)iF3NlE^xPnY^ypmOrPf6CL3dyY^T(#=Tl_fWpq@U_yx(6wllBvXu zxvwlQCE<##^1!ArF?p#xUTf4brS?_I8YMSVtQjy{hkd;OY13k@= z@!O;7b*uSQ9lGH=#OApn(7P%}-2{}uN%cI5kreDhq&U4Km)5polPM!+?lX1F#B;@( zfnj#4Bt@`eQCc^UiOEMwF41Z|<%r6mN}$&}xjUvRu_T&JuwuHHVs1Z3bggc}iivhI=#ejxGds zZlkc2dq3?P)o*}ypfp@i?y)jmGr8_1QE|=iSs-m@x2A%t#2pY(+Km#Y?RjLJHZ=Ht<$` zC76V#8QxS&+oH;?CB2sf;=J6P98E_rL3)j&yj^8xllZFs1_5++U_e(iBXe5qC6u(x zPHFl;rYlkg`q`O+)If9oCXNJShB@DyQiF6k*UGTgKz2w(b~arYY|U)VHs=5dlF{k( zuxO`81y#pBq;js|AB)oY1L~q0IN_R$o|BWKUVW|~hUHiH5U8%XQU z*BE#)Mh_K?s4f5ludPIzUN*ESnl}L0%<17cD<8Z-*pezumNiMnPBtC7NdGSE$GF%jkE9xoAL#0P`G9;Jx7pr&yINt6Uj z@QgaJU3u@cYh~2E2|nLQJD9vmLHNkoiMj1`$XHSZ%J-J36sZ7 zw3Vzb4d!;~`n6C?PMUjGdKw=Z7XhN%samVRA98}5+SMCoXlRJcdbMB>b-LKI<`P@s zLt1J|s@2IuAIO6t%*;ahqJQuzMo@Bk&SRG%P8VmK=$P_BGkMEPOiD?Wrp7NqdR+Ad zjdI$$>C&LFFvNQ<%7D_;Mf4(+SC674b)C8c9s;vCinwu>q>lzItf(ia3AaE~anKuR z1FHJbKz(i^a1oI(x&b66lcX<5UQCrhpOP*;tNIQGP^cdOr3f@6H=7N$I$P>63)YeX zz$~@rEaO-z=@^|?L^jQUEy=7Y3JL&hYNK#NI@!SM@`>^qe5n7ir@H;*xIyw%EjdOzhsJ#UBJ0ysZS<^p0Gr!0LHwp^qdQe+S z{RJ(4$a?RmmrynZFOj_K;?_vLW5mFMq#Sxps4QT10Z1BZP?;fZN!i2@qY?yJoJ9h6 zWK=A50fL0IGJm)^N;A-!wB2B?sT3=HJOnv_RT%E`A*1D)6qZ(0;v~o_$j)P|TlOn)=-`YLwPZmGo&h*VmtgPEl^kQG7XfLqhi-RX6xhPx z7D5y0JX$k%QA$sKpXx=DRS1(=P-*23OzuMX&~#=MsM(={Mztk01xu$F1t-OXPjApU zc6?MoKra?5lSb+1gZ5AhB54TEl(y7W6ydN7J#{t;C>q;g3ibaSrl zleYKlH`CzB*_Nh7330B`oxlndr>wMwA{r`+%BHdMRnOcvnZGt!KSAZn<=SoqgpcVFbwpKYDAlGM!ECI$Pj*@l&1`1d#4d%&<3OZ5^q6>%czYuu7X$g$0Ew5J2`RrUIFb z&aFnu9kR5`(l%vv%}q+%_Hq}4XD!Wepxa5kx?p~-dGa@4_GGcQA5 zM|(?y{4ZH{2WLEy=pv5hL{Sq6apbe9E0`~~pl^kJYEp)`htr1Dl}*fquxKXiC)Qog zg!X8lnB5-rF+-3{uJN;v*nXMjfpv z*S!|^Mv#($EZH?~LqfF)@6sA=Q9p?cNbZawwn;Hk<$sm(sL00BX{A{WsZ;bZ)2e8% zdhHVNk+X_lf-kha$qI_<5Io5~+DuN=_9sGdO6Y#&GOL7#caV*B;Dyyv4tq3pLk%uGh+-S!~Oc+MYq}9`0a!cgsxe`Y?e~vny z?5OdYlO6naboBNSp^i{X7O76rA(I(|2W>ff@hSEY>nmU_J6^lP=1GZ)1>7&LsrmB+d+=y1jCm(IB|{cD5>A6v5&{@}P9H@y;ZCVVm;V zNTwPvudT|-j;?(!4v??tP-=RXCchIOamb35S`Z_IbyJVVkBYum59a69!B&D`yeB0`IPfn#}nRB`f)jXBR z;$P;L)V6lJGFO>BU1~72y{@#D^moNq@V(8vJKgG)$l@4~q)tD;;lVyVwW#PSS6E@B{;n2t*odXb1>%ZPAgN1Eh>NA@EvK{|$m!V` z=F@B1ylxp5MjE5Wpu4TEG*@0CqnOpT($LK`bQ9z|HFr}&IWx_4*lrjKzD6NAiPY&5 zM@qAr;S6q@+yJqanQBg%MDTzt(w<@YdbX@%nM6D|>55ao91FQ#Du)Dq7;5#4t1 z{>q@P+-B}Jjxj>m4vGr4kv;_6E=As=<^UP1bicuG9$^HyyHCjjlJCTo7lnYuI75_CQq(P<67#IfE{)dAuF(9HJFBjq#tr#=GWX`u$h0VqK*)-_ zW(Mh}k_k+V;;`_SLUWuF3exRh%C|*5NS8@tckm}!7C)e*aEZUD?)XLJM87(TP~OnwRXRUPMifOTI$^U}mF}a)9tAI)e~VCId$(65 zAfWJ~T|3Ndn``SgF2zdkfHZGNjX#@I?vFatPG!3BYap*dxNAwe<(vHZBjL>j0=*TPO$#aFBZ@cagOO&1cJN12_?cdbN(Xt^>h^ zbe~5hk46Ek1&JRa$1$*qi8`G1tF zTd=$%|LS1`+C~r}1XA}0AJn8D zNZfGyd)}HR2xTI)+8jHHF;SBQ>CcixWCFT!XE3?bE+u*H)9-+sB-CWoZXWecWX)%m zzX)y;0*@m)GA0ueJF!mWfILoQo#-R%0yJFhSV|+f18#?e6=8_v3|Kpk4ueKXk2Z-@ zBq;!6@3Uj+ol0Z6U_Fk4E~_JTzS7gWeEvZbmFr|j+?poeTGznz)F z0@a$Hes;UmMjf^w3%0gTPrEu?CA_|77ddtOoqE+(%l3%U@Q*RJKq}pivTagA=00fv zDRpN_Bymo_Bs|*|C?NKxKa8m02yJXlyEfZ#@JJ%M*e7l}mhd>m36TMi6o@#c5_CGy zu7VTmwPHXYa1rHc&(4F1oxfhdh&U$tIzX07=2GWT%GFafk{L77ZkxI@aY@FY%Rp*h z+Gqe)TEIKJI?s|92=_ChiWoL!Gf4D?YEVJ#DU3v-@(`Bn_T_C&< zKodPPB*?6T1zewP)H4P`@t=Kwn&7clyCI9ZEGnJ|TAqRJ6uJQE=!@*x@3go}u%~PS zQYVJmx?C(Rm?T_%0z)PP*19I|zQwY$;&I2t7fh{-zNSXpadQOs32eH8{3xFmRI7=jnmf-Yp3B^6f8|OxYY#c!zuLRuWCTQrmCG< z5ovdR{Mj9GF!oNJ?bfL!sWDk3Sv@l+25lSFd6({lz&jAOWo@%ahe3)Te72@GD*4di zbJFk8$Y}@xn{t6QSYqcyp^0=lwMi!v{LwsWGcN9w+dd7}{1o$OyaVS>==P>Dz0SON zCmS?9T`H%_?2_VqzIqYr`cTJ?F-U`%2h;HZBAGPoBy3=xa=(lzX6DV#J9RJ2S$n6p zXB|nA37}MqgGCeqw%Ril5?^^WMk2|b$&c3Gao)_wkZ)jG(9u@fMQ7YRanaKN^*`c@ z^o*UhfE4*+>$u{GYtn0o0^}JAlY1u&P5_-5_Q9nod{b&v`t^mK?sej4dzE7n5QTt6 z6chMN3_#bNHJwt4u8z!(%Y!%rU*#g_^z6G-VnO6j$=>#K` z6L{~i1=jSns5v(P>8e8b+767(+zIj=26Mea9|!Y;C9hg+b&Au*KlWnkD*%9?z`;Lj@!lXeYoo{DEY zVZYdEq;+~zzJ^^KO}V=RjzL;%$@A)%&EoBm;OhK0BNZ^t6`)bsnd&a$9;NL$XH}RdV7Z z+DdV`FF+r2IPHA7RlOy5h^vmY_j$@v&w1J+VE(72?Y4{IG~&o$YnMJGpdLreWi#=S z(X~|#6kNp+&*wBp%uJH}O~*@*tkUX$NnK-5Bm=%4`1K_vx-PaOC1U!=&^5RKPn;uS z>~Qu$!pn;OJxQ3_f=(47M{Y@9OTvA5C8os8trnn19zlYrr^5`=!hG)9f_OWB#+?^K zR)5(Ek7I{Swxr&Rnz@jSI2j* zsdtn$3+5D#Tniu_)~2U@P#4v41mZOmRPBU0A6`r9bKEfo$t5=T$-Ca06XXeyw@o=R z_K7xu(`kBWhtuY)AN19VGJs|@J&9u}x0>%;cm~%www!F!@8%vz@0r<<<*{uBBwvDO zUYBm_y3)yCbIQ-5>ImyuEm6p%$0QF>bJtK*I?c{OFrA4qFqViX9#cGT#*lTY))(7t z;c59!&T7;;cBQlTz7cj-hwDC9T$I}cD^J8zPj>(y>lY3K%d7GJ0;GP z2~xKk+agfr1-VT+DR91%x1Qq&KO^fd+v#M_&X%WVrBGx~oai}Uz>_?)D^3|0VPz+6 zqdJ#nS-33dQ0vprD=ejJj?4oOSUS@_N92EqorIR1O)}-q(G)+#7AFV+3I#N+_Gq84 zNOX&n@={F7S3ds#dOKxR#}Dn%q}F#XBnIYv+ltaZJ6Qs<@KXP$WFfc9z>VH^scpJl z1`McfVkQFsxz85`EowJTk;QhY-(aUEM8;2dZIspWoHS3l>r|Tp@=m5vNF3}-= zfT)ArA?{-5&TR&Cfvk|pfyCBSkUL+V-juXESvO_~U$>r!yF<5&jozZ3rZwFjxHSG6 zJDt$V4q3cIW>113ap$uG2ChyDOxSn+L&*B-#}fm^w(WC{jMKBg=%n~G(jP<~d6rtq zjIA)1%Jej}r=T5(+pnEBIbQxLahnvFVe7PkC~G|2iKzQ`ThrU96IuOJ*#0wQ-Psn( z9?rH;AKL(otac*7a@ zE+)vS9WbdOLHM5s=_wH-uP$H?&Il{yfN~m4t4TSSCv4=|R$8Wf%#z3;IBOs%f0GW@ zmsb-Tk36P@dH@ zRp$}N&W2MAY{9nbVnL#-M^%Jg1cDpr{`En}W{RAWm5Y`y)_~9T; z3bdk0sszqNz@w0D)8C~z>>eKh_M||kL`p!{3p<~NEy%KFfVQSQd_H?=P2IZDDi8VE10_3f0zK>WU)n&&-GV8-ig8;{hjwPl996xj{ zfBjrKS@%GJL}EQi^p5RS{4|M`_!vX9qB|wVMIpR>yiCg(PYlrQ(wHe^?Nqu0<|M$# z<1NZ^-kMs{$m98Hb~Zg&Z>Nqv-cF^}#Tj1~9BG{ZdHf78)yV77NX9^)+o|nS>c=B3 z=r%xALGD1-2;}6(J1hQy_C$}ryZw3WbRuM?-NloiD@6VOKDVzjnfN~S~+;0?_-OQEv-NL z6xx23M|w=)4AvlhP=}rw9sxUIZ>v1Xt1T+Q@x{qj_5S7#ra~krWD3aS&eL;F2EE_$ z1VQw4Yxx8cpf5mTh9^JX*)*Q8wO{)@((XW31Ql1j&(X_x5+I80*DbG2n;j(MPxf$9#yoCftd_A+87~s98Sa4$J9HP9)2D-_!Gak zeKH++yp{C_A6;o0pNAf{re?uds>BW`%Xzp07T~O}FP-GrnK5A(9Dq_^(qB$ROYkJL z?9}^gQD5xbq4pGzzfmpf9X5ANq~E+x+w^E_ykxEI&ld!7#-}cDMqo!?BadwnL=97u zI41*+IM7`GO$dA1OQ62E3RQ=vC*Y>K99l{~34vYPDp8}xB>7iWJ4}E~4QOND_VhnGCce%yvicxymx49XGcDkRzyktGNc!t}sM>mNHEqSz zhmq1qN{l7W7na5|@|Xs3yfkn0*BnwuAxV&Tv-K68Pgw!7MT8DGfjZB?B*1A8t%>>MLSA#in|NV^kQUv+`)4&NKrI&4iCoGLL%Hyh^BR}9q=^Y;mb1m~Ls2g<6p zs5@VdK4K^z6yCY0QP%wz{nMSZojmbukgSQpx^>-2^9bZgk=v?wLJeD?s;xRANf;bw z`7@wS<~g9>aBNDp*}*3&65Ai;U-O0l~o-67Eygbv`6LK8km zRS!?^FBrP+w1}4ho_NQ_sV^tQV}iefmW8mJ^^|-W)T16(VZ-D@yWY`W&cn#6woVB9b(vK^px7Trqdr6ChDe zo7A$~pU+AUVQ-^O478@u^*;T8JQ7KQThmh}_TRO<w!f?`>H+I7 ziAuIzsTBR2Q<^)D=r*Kn!O5Q?D?zZ&LGe@mif*SoyaBwuO(Cllx24Zy&lfF+skcYl zrMH%cry*)6dh2O5k1j?SVQM&vTBfAEwInutwl{~ROmjoGc1ZD_}8;+_1v|T!6-A1)r z{iJs+{pA30KV^i1NYY&X-Om6V8g+&^!eeUITndl<;9&?jLvJpL^s`{usO>>Z2jXPj zG102pf++2l^=Pz>w4v*Xfvq8=`Ltba)p3*`H@QRF-8r+heK60z49K66$}Q+lh-n}9 y8~%Zj*S6zqd5C(O4B+a-PTTY`uwA`Az@7fAy>X^zZ-l|GxS!(|_}S{pwf0`d9dWAO7R7^#A|Wum1I~UjFCl zf1cn>x0>vh^{RPynmx|yeLrs}%l`JM>GqS4)rZO3+ne*3-Ob0{hi&up;;&y-^FKb! zPm@1S-aS75>&xTo#m)RO{rTCaL-pOwdD{P`T~7YT7oVE_x10U#hyUmD?D-GReyscU zX;**vryu`t_u-2_ub$=~m;K-W-A}(>wJ#5|Pq%-0ef<9BufJ`d_sjXmFaLIXIWNDR zo__c6d~^Br-=2Ru>>j%FSKs{8e)a0pmmiyt?Nc}T@!x*@YWnS0pC<2DKW!F2{JZb} zP_3qy`NtRkwR-sY>mUBm-K`d@Z@&ERFTedT`S{2F#lzQK|NOuG@zbIIaNmCU;g9F) zix~Tf9}3MO&6b@{jchDolI|-E5lb@6Uf;>^@FT zFZ$=F`_(tE|2{wMcl%lY+mBy2Z=d~KeY?Ii-P3PB{I=cQJ}o{p-_MW7SKt16JFWKX z%l!Yl|E|6L^vC1Z)qHon|I5w4?5fK@pI&Xg+O3-(p8woDobIE?&}wEj??w!i=8cgNN3!|MIw)8y&={Nt~?d9|&M%bzD7w##>4H(zh> z59{gAw|`wVkCVf*_RC9s|J}2n`@?ayp6!2oe%-vk{m^{ezMn2nzxnw4^vgr9AAfsUG=IMTwm-MK>FoUF!^`UZ`dxRfKhEmypWeM(*Ujnj zH2q<6?7uv{XsV_Nx@Ucc(@>Q}qjZTosRJD=KbmQSnZep0%**kBe&hvVV14v~Rl) z&1t@y-B-^KlS9)#?cYz&lXd-U^J>59cm0R@>AtCE_09VJT%V4cZ|BF!ay6-6_Oqvb z`*HQIT2GhN-R9+SeY!M{>$msKWZJ$sOit(hrM^^$yJ_`mciT1R^Xa^Nnr^2z>)U>M z+FYLMcXy}dbor{CAFA`=_+fdPH}l!_c0YYrAD`OyQw*8;O>=XpkH_u%_Bd^)E1)&I z?2d>2Y5Dfmx_Z5Rd8jXk{bT)p+TKr_+s*XczB_bJ%gcRrk2)RiKOBzl7Eg=re!9Hd zPtMKdvU^*dX6@~LeS5jb6n$Sm+#e^4`OE$Eygr=|Ps`(E1GH{W%cth_uzkAQ-&Tn7 z?y)}i-TUf%S5Ifv?SB5WJs#V4^Zl#kV$$85S5L?7<;`WfzMa;$JuE*Q+lR&R_459{ zo1ErPr^97&nRK^ztJ~xByt!Pqm+9fInoOGM!~Ah~dRLuqyXk!S`Y?Z7A1}M7d3Re+ zVe3*|`olx}aNFHftDAoE*q)f-@ur$iH@An?Q-3;PIP|Y()nqq$Sie8Em-%^u{G0p3 z>}hv?Se<9P+vRk+yMJ6hKI|Unhx-;$-r?Bp{J1?#dw9CtO)u+*{pqqiz1q(1n_D2; zzCE{(cim*UoYeD&`a^eosvhp@d(fw!erO*a)=!J}uA0s_cjx-)ygxOt{BlvaBKOEm4>Jx0;FQ=H2>-VSKWztVp(@8Zyzj^A;m-;Ymrm)q`->)yH z-MQ*sZ*CXs*AMm6{&3pD^37^7+0HNB;|}BR_^O%AkzxI`yX?Wc?)7XrZDV47o7>fF ziA~l%BI8wiTTiOx^3c5Rz`TCCc~#Gr+vUUdU4Q87-EDnaRm%?fKkYg^QEsaH)#eTJ zKkb&?q`qA)tA2IaJ{>xa{_3_`ug+`mp`)=OA)3REhH}AF&eY3q= zzpk-{>Zk2v*Y{O(yPB*JW&5E!cWrmSzNzOc`0d{9_8s!C@2a{vY~J;kZnH)HHKu%X zX+P`^-ExaKs;W6{p1Q~Vww-U_X|>s0+INa$0^7?q+NgWL3ZJcS*V9$AxpeQgnAGhp zwq9?}-TUse#j5~AY1!=XjC$Ag+r{Q}HL05xwssF~vzawFtNEsH zFI#Ba1v0#=>&`>j?X5O~DxBJH}w5a~1dDHcK#Ifnt829z8Yf!(Z z?zmpBrq!fb_I(dqZC6zlPH!7<@PdWkR2Vbs#dg;}>>f7#YKa<6H|zcJaM`|XnhH}I z+AWslWwWa>W~yn6x7+^Gp6X3CTTYsJ*Y=nFL)))b&`vh>Zohx%Pj$OoAdY#r-JRgK zhhNxg>OHi(w@q8km-nk#*B+qV9;?-I3T?IT4~S~Btrm;vdfqkY!H4!xH*?IL4RSbK z`cs1(787VUhvVS^cN^Z_torWwus=6DbmVyGFCyy=(Kd|FT28h0@q;TPKVa@QWuu-q?K^ZRPnuJ--$VT-61 z3!Gl`o8t-E4%+Dw=p1&ie8S!392MTRhvWX-^6qRo-K-86e$civ=8q_k@YL>?^=!77 zBLD7qg5T|YF`I!MYur7x_|ln%7HA#U?QA*2Q0~^q;j~5j&*t;#xo{qq7yQpR}pi>|B#}l;8bTOaROUz09!3J+zY1ap=tZu*B%%Fwk_Ix}7 z_hmJmEf=(WI`xOuhNoAwj5yj3IUr?31?~P&Z!qoWt7X4E9q>Hpt=+5-J9?^H99^uc zU3)&9PGAS`E;sdIcgEeOg=J_v#Edw0RWn~K7VUZu?eVatcFwz}!}-`T|K(z_t`8j^ zDM#L&FEDmD*dwQ1U#$_xqM;U0x7&Gz=TNnU_I&936&BiJxvux!+vD-jcG#N>Xq(gi z;n?>zo{qC+E$s<+k%RB<5oM1GBT8z|jDz_vt7VJl-rK`rkGmk^s_u7hi*`{p&8dGl z?)S|Gb*SpqZu|C#$3lk>fof5;)SeD~gS&_l+SB1Jw8{Z@@lkm=rnc_3Amw4#0Xsyw z2C~S%ZxGcKiv`&A=Y6+*Q-ciEdfh`h?lzm5v@Ny$u3O=8hz!?#hqu9wQQa?=^=1R@ zq3>!~o-zkyc-prO&{@t`ZF_*NzOC12q~&Ve?{K%@;_ehJ-^lVocYy~+xV7NUa$Yyh zasP1GcMWQGU!}I&U^pPk4ZakR!*;z^{!Q1RvHGs9oCEy6Jsfupw7^!D8Ar7Op;ryG zKxd~Yt7X02qAGpAt=~-XI<7bS{_XzMZPthqcUxF~i-m^qk8x0Mb{M75D#JzHVo1M* zv^g&a7T<_dxd0u4~$=HW-~~_#L$1@T#U|*lKWhwp=#Q?$5g&umk_= zy2T*bAGU}RewR%v%l)Q?7Wr>`aODW?Qhup@y9+HWQ+w*5MMjHtyTi!p_nQ@l^sHXB z&;lLbU930s+wYMQw5tlsk=iZR&V0F8H#@}9_nS3(bhcWx-5xn~9lkn|!@B7X;L09a z=D*r>y|i_;#2l*e>lffZqvVpb=SWcinnL?RvGviwwwa zusTqcP1_&ow_edN?(T?A+pbcJD0jQ{YKb`nEr#;p*tILXIWdNK7a2Bi4DAYcF_gB@ zF2K8{#kc*b@1e!r)oKeZ2HX}o%&O&PgQqXG$PgLTTdW<#K`qc}Ha)AfhZgTw+$B1= zyQ;8;mg`2Al_9#JYC9}A=D@qq?vx>xGWHI%Tg42m@?Y~I(PLkl(T){FUE8DfH2 zOIB`rESA0pg%Kt0!ZNjQP&8`y(C(o{kn;-cptggS`8Q1u%f!xiw^(S_jteW zdqjzpfZY};ptFeGLU$3zzEKXajCOGT$bs1P$D{5t2cQEe52y+($*fl^0iJ7_UU@u0!>LuVn%B`qJJMOVTW?gIA{RtL0Pf8ZT9 zHlxI_0J5-*)5H$zPFm#8;mEt-9b#sbOZcrg{;b8_(Bd$)^o#Mw)wxxL@m;Y)3scJ+ z*wJ_k&@virzFY}+z(I7dlz=Q=mbeSc(5`p*O#wt$o-qz$7nbR1Yc20Ge{0$Az^+;& zLy#6d3wA(c{#+~=ow$pNB8NQ}3u-82Nu>?OAGOR7Q3+XqrMm|XczQzq$YBo=8KH!m z9V{PqiM!E(7T8IP{IPa0;N$KdB!w1H0!rKk?ih~DU)U+b6+8hujz4JGmC#~1qGk{S zJ1{G?hzj$L=wNja*$Q`&q3#m92HytAA9v9m)Pm92;IzD0tk#%W(vp?PAET4`Z#e!? z23bA;JE4Qc!V$K^>hN6*IAO;q=~q!g3znL*Xb0p^ZO!3`MuP|~7YptJofu&)Ei(s= zuzJJs=er9GIE+r9LMv7{r zC3aXHMi!gY=qO6!o_94IzVW6S4&Y$Q2cTpH|0QbVJ-)8mRja7EumDDq;TBDlMN$G4X(RtQi!A>>c3XdV6^Wte@t{XxVgjp_rhJ4 zG5$F^c$ZXymgC=|@+YHfU?+5tQZP#cj@V(q0e94&?cnjp`j@+SSU_ZhGbN!D`79 zVmZ<>Mc^(deHR_A;h^zHEm?^wHOc{_lPlX9LK~dzkiUz>(X*T@{W$ zu>-6m4bhd!EZsHk8jgjPB$}2ZktKIFD|QFII+)>=7BO?)5qDRGREj3ma`ZGb@3{Ur zI&oM7jw1|*F<7%n$65>r4k>C0mZ&tkOHwb4q^{zzCf#M+5TDzXTj716O4a8#Oi zgI3r@lXAf6Fjof$ToR3Im!h;rN64zd#XCjGyIMQUkayu%jTY#Dv&4=>3(Ia&qQh?z zws4>$g7dDi!$N0OED}32t6;~P86DG*({O}4!!WWcgG9VDOj52L90m=IJ6Y)wW*Sl( zRq^QbfFsb!JJl@Eu@-$NpbPG9G>9CI?r4rMTh?&kkg{K~lIxG7Q$%CJoOc{yT00!! zB&E>ZO#%MwxM~)ph?3(hd^^FYjm1uLe}Uc z?!3#Ptb~!mXIi1d{53lGa^x0PRR-P#IwZ?hUbx5nFY-TZEYW?cYySbEY%DYl2?Fl8}W!%S;@QF;03#)Ee(zj$G{H18H>YQO6DCGI!MZiMpNo{ykH=5 z3sXC4@L1V-IC;l@2RdWXKnL##CEk@IWc`9s!nn(MPgZ6bSR^*7XJsZV-g%;7q3dnr z0mq&l&{2k3EJ#7f?uo2%XOpVIvkf^}{oHZ>6#3jy8`6|Ur<=5882~i9(ztsIaaUv+ zxbF3(=({8A3Oae13thv}PjvMTCyRD;bTm!jsieDnlmO{iEaHw23t6*6KyzhN)HYy( zM2GVZrquqQ4LB_&L%9ruWnIE)4+>GcUkb*Gi0TBcZEBeqbwHj zeFPT#YQHn+0Xqb3bdrW58ZC==I;^O?l%)-zO{Kz0wo(>J&rD9GF)Q&`x8$sOmr!D{ zX#1DPpr9NcLj_r)qiG-td){ef2crwnNm9JJ6D)=$ICxjwX-2G!R|vZyNgBu|E934U zDhG;6Slr3yy&Q2klEU;0twfUr%wkIUV%PT!mV@^@c;}8LI_hXG<-$AMMF}C|E2}Vz z+L&nl-b0yUY!ZGcX_GJqYRh8bVJ{X@Nl~R1hjCiSCOdFAkWK6)=3RV}2ONp4n-pPi zb!0~?Dlm)h8&=fFVsx5l+5QNc$Oa9wqvH`SI9yS#jw{~ffHUp{no;7gXC+gjWw*aR zGvHXiHW%OXFl&~k{LG}N_Z{wH{1vTCNwiiUKiUSE_kMK)*@V*Hkum<@xHLGi!>Z(9 z65;@oJ|=p}gW9 z@>eU-l%dpj1wQU~#_-S#9tH%gq7ogbw*G&JM_WuJabrM&n`p1$4|=aED?pC4m*Q0z)~$3P#Hj?h+0? z_q>J1qL)J;OR^Yuk3X*tB`mP=aPkf%j17*zfzw?>={JfdiI7D*e7>WC?`UCIz5C=qC3dBwgJ^t{MuV$n1dDI-foKp(C5;ZC!{4ff zI2w)$V`%>?5pb||pmj=RDSK9?2si{*%t{{y9Y{3(HZw*iqNLwIC$Ym89x@EA<~{wY z{fE!Wf}K|uL}KTvfZ+T)7S+rP{i>sbm7Eny33uJ3Bq=i-_3UVL`f$+_ju9&poe|K| z#^}thv?Dz;@%>7^vq_DtXN7skyGGVs$(r$QlGIR|N(CJx981}E)ug%5#XGN##9egI zLeKpkhqwupybB6LRE8#L3^?=7Q3_~qmdpyE$;uF2hJ5afco!7b z*mZ-8{)7W1)vN@S*d1t6{3|rUN!BMcoDvbk7%w+sjNojI#U0kSZTXpSq+W*Kr;2x#2Z=m4|$CY33Vj+hl>7V;;v zJRIGm7}*f{HxPI#+A`2U94KkUdV{mEIw5KbNd&ZFgZ~x*tjxoa0}s;?enY_z35c9C z{=D&FRMeKOY*yg+WpS zt-GZvF%3#pe7Da6$J!~IB#{+%!8`RVgJzI|BhVSfAGni_lrRUJ4&%uZDY1n~C4TLa zh88umh^(rT?BJ}3-|mo=$-4ln5G}S4pkx__1}E=^o+S+dmcOz!{@u}KN5%N#=MB-} zRFY-RtRhCw#;oM3RL~l5;(HF(MAq|un1&u<Un2V%XA83c7IGN#j;xGl z<_Jmzr8&#;lXs|tcXW(!ih3IY%JQHgg-4?JPWqEsf^yep3l}(`YpZ z=ps?1iv|b&;PKTKiOvT#qHjt@n?DXMSUOy<3z+%t?XWg^O zO82{qgfb}qV5KOmdizUp#M!7yKq-PJl)w%pEyx;~)_t8zbum7dnM#@oA`v3d#}&3$B%|j&}Q- zXu?jv>lV=N=tzkT9*@CbrZmOeqtK%7vq3Kj}lOccj<^)?-^-x?Yb+WC0Jfc1MdP3YXP*e zQ#Ww!m`bi0pDYDgnfgpDhxrSOysOcv#o~vQfXd62IJpHn@Sio#15W@wW|yh zyJ78sJ4RMiN|FYJSrz=65zHf4|7)qS>6beyyz2BoN5hTl?pqYOM%Nk*ChG~!^EE?QTR56Rc3L`7Ic8Og; zNxl!fR>3Ur*XkkxzE3F|N)l2Lzh*6673 zg2E$He(uG`kAWWD&6lGp%7v~B(P$j-stOOYq{C8T3u{;8VQO(${jNqskO_zHg3$;P zL6dj*tV%7JrO!Lv6?cZ=Xez;$EMr!Z9eEdFDXI~6$U*4PlV(v??0z>P^lt%3ac5^A3*56$G84rol){RuOo@kMqUFj0XN8^k4=pE)*NzUG;A9qG?EIyROc^Y* zlIVb>L1D2{dsY*TP;zu?NHJ(rDQ6am=EZ6_@K>xFfAM+a?x1CHho8nAj%EBYXqeJn zKP)UVqXGw5X?AotOpL49_*k8NQib?~r63HwjEX!_x{HI7{ zmZ@YdO&KimUD_n6nzXDx0ZrfmR*e5#tRZTWs?l5qk4~-~`c4sX4q#5>-_VeyW1zy=Sn00&G9 z_o59`!j6ui)#@NRj01O7|16SPm}Ou}rlErN;bD_F;Q?73M&FU)Si58;gC=&^;1co9 zh&?OI;e_7_tVoETT4{4Ky zhoi#g4pSPileU;DaOf@@oEfGgYRO9BE>>#oa)i6yL&3{%7+48q!HypxrV{Uhvjmz{ zqR8Pe;!aXHcn2{EF28DUA}iiy9CD;a6QkYl+Tc#9)l*SqVH}0g%D+pJy3vxAxo67V zK*vFc7QCy?yU>oX<0CxbkRv9XKnsr1;Ze<4QiH|AZtzl3ltC39&=DO55nBo9M9ti@ zqZU?_*VrN7Y;bFh4*BkWPm-3qC>o2TnvKbVvLImut-E{XpBeISp;A~@{onYoIE$r3 zHmJeN-N+v)nRg(mu@n78C4eQM^`K#t={Kmv0(1N!EQ7_UwBH3Zf)vpBU)n;;(O6)m zO^wL1V;KZiF-5TuZ8v6C99FZ)lt{{oCbIZ|iRGZ0dF`Z@-*dVfth@#qDaE{F3TmY^ z1u3E|rWo{_6-7=GLJ89lRTZ_8MC7ZL1+5NCjFh4(JiLb5b2ety8$06N$U#M-c2mPK zhJ!gPEyNsup7$CKI=o)3@XSydU@Eoo`os5}$Rbms!y6t}{wg@coU;InG?aFfk~z!6 z$iMc=CU&EQuSFsY-W8H^Ija6qLf>^o%^g$8lavG}vlJGqBEJ#VZWyfD?>ZbqN{&!c zRS8rYlk|#)YzNSPXki$RAk|94VZHA{Bw4U_yB$uG9M`N2-mzKm_XKE`paak#?;@%~ zmN&K8!3+iEfGmLyb_B8smZ5~4u0J%t2eAL!%&el9E!+Ra{W{5sl>a+J=j4} zLCaLs?E0>FCn|*_j}AU)Or>FxDt}_fMvGw)Ij}jhXAyKU<-?&h97dKTRV(#7QJ5JO z%g6y?u}EqTw(uK?tRbR$6MaW?fIDhPt!8$JZY9=CG*lZBN{M0?f(-6RBpp;>gvFUf zcEFLtLRkcKjxdPjE-7p(;mNKHak}IWQ&3XOyF*3CtTeL z1rDu_Z7AS!$IMYj(xTm1rn9OtfFHQYjHEHJY{o zVh%WfU9@4z-%&{4yG*}IW zvRIkeAuRhXQ3g7OGDsTO#pq1zP$gg|%Z@TDs=FLCSIBxuNB;DCjVuyo&qfK8cVWsD z9sD(L{$vbg2h$b;{*Hq$g9FSF5QEzirmi<%| zg-b=F3@We~Eh@R*rAdbd2OI|4Guruc`{!@~Ji}ukSy^9ov~VYOYEmwx7_7vdEnNIs zR7c}*cBT4V9Zfj6qsH^jk0DlxUs<~A_7CjvpjBAz2BE|Lw%;7#!S}0YsJ{YJm% zVbkGyR?;%b!Qrq`!W20x@cVk?z?x~X6t%&zg$ReI zunx1qfn9kyxw9yiS?Ou0;h@EWu&8Cs+0o7c+F=?Z1>%k%iLB81jHF3J@(z3-wh)8l zM~JC}HuU>eIq<_t?^%=($TA16JJ*c)>#!G^reV$sO~dj{MmQMO(4;E3!;m9slMU=y zn3hN_#X2qXeM?iL!+ad*A)y?GyP%y8*GSOKq2Hq@W|lCtj>;L*vJ>`ji19~cgISD2 z1y+!37Qq2J5O>?kyO2rnaukSebq z1Z}MfOmak&$rUkLeW$LB8U`CkLsZ3ZFpO(f2GB{vpc3H-?s!!O4LybZau$^wWD-pU z-n+AyzwU|-g_VfI`;7$#u}l_FNj3YN{vJcxQSLkSteAz%4w76^M;i{czo-<*2D5xP zUSZ^l=d7X>@7TfS9VS}L8KDycj?+&SPJ+V|gD#VVxQm7gQd)em!4|6`qJ@^822&Ug z8Z7#}bHe5hhm>fT^(HH^I;16b1JJ>DaYr$G?Zk_h2&J&2HW+P^ z5)Ou8R#K@jEEeokQdthI$`Fo5K*Ln7-3~uQ19fozg@(z>E$6JzVTDm7g~7w}%u0zvlWKH~nH4#d?T2+IPf>=Fzc;uXYtFhkRI_lzf~RE%Il^5eRnUeqs1#7T z3?LdR8VjA+>0Ko{xWI`H&|;B@JGEtG57@B{#SWrlu4t3!uF*C|0#s6yeKvv8GD+4UUQTQGS>zNI7vJ89-QdIs# zM-|P(&LL|i!XqPvkv&_0cF;8~W9^!xT04Oq!x~Kz5#Hrz!2t)49s!E{40*B?uU&yKq97BRbDVn4%uyHTsaE7kgh_fmKb70LhXUS;aWm)Xk>~Otx zSbUGW@xFuUz+D*kaS>WK4^%nfF>(hLeJ>P(DWzoq>VW%%PIp|$@yMp z#fXNq)wFCqcid^YZtN|2`mIzco*2Q{&WnHDKNu8mhXA7 z(rX8nD5|ptD0x+PxzNLuhBQoRb$DjuH@{bRwMmRdbS0Y<6_({;C~un>VQiK#Rho3m z!`jin4nLf;=uuIcWT9maIDF4Z8{A%QA56M*cZEbeIXc3~EwMlBfzAJb>PzN%?Xlg+)?!lB=SMDhz_M?-J!S{hX8p?rP6e%AxU>2d%U}e3- zGN3HmA5p4i16WNgxEz1lQ4utWCBCcgoImML4!hr@D*<$9^&2VvmPO6s@khcBqca^v z3B#26j`}l)=p?~0kTlnw&`~=?8KNuQv(OG($WLWvC<;3)wvrOm-!KbGWt6|ZEnsmP zWY6MizN^f4^Ns^AIV-Zz5s1bqIr79fx>WgiR ztQZa!64_WC#M0BidnQQApreFJ&?0K4%Z}1SBUt5d(7{FWtQ7s3F!^s}N4z7@;``{A zY;m|CtGibVJqK$csYfS^rqOAj$6EX!&Ej4~vzUfNE6~v4F=qqld@3gHgu}B!Upt&E zDq&OwzeNX<16bh0U|EOREKw!TojlB-dD&XALqID?ilV~+G@;b^GnLdzM%MX9g;^C5 zT8D#Kv6Q&8SO!`~RmAAPojCxUfHH!PDmnkej$yEM)b9$CH7ii+Fux-_;2d*2l~e~M zOv{4O*oC8@AuT6%$WV?oOon!{L0X6*?vhtD>Tr4!SEk;I*Tg<-D_H6U`(=&GeEm-{qHw**c6Frs6K*;MkF0 zV2Kno9Ia)7^c06n-#IFpDwEV;iSWZ+au`6TUuh*~9PrDcsiV!ELQ;XnGO)rzR{4*C zhN-w~zbGm5Rfc4iZBaxN$WT-oOmP~N61r%@oe8@PD*sS3TGl8XEHnS0QZYql*>Y%= z0}Jl?uCYTAVJS#NVNbNB!>&IqLzGsB3$jjWj2*A~ZU#Npvfw5>Q&2?>2T6*Noqy#3 zEpZ=smxuAN_?${H-~_Y>EAGmXfQ}UCm?=pt%OONX#c5zIAMhC#O{os7aA~ZeqHr|u z-BFSbvLz-B<!-Mg|JzD~(nIVufakoCM59q6fG=cqIdMsGuK zHoqJcRaAv=IAQ;*JOg$nOOzp@aZR)V zS!2f_#aS*#G5ksd(5fg7oBuA~8ft3wmkJ6?OSwR4WRpsKNQI{?p%bQGPzR<)yo2n7mA}^xsfEMQq(KVg0cW@-r=Tz#$}-c zX#g7Z$I9{u7dnF0L{kaf4Q%klT~(oJ54gN5(Uz5Of@SWgl_F+`1vrobnG`J?Q4PQw z9hH=D=y`{SrNS!cAY;ysXf$Rh?C|$OxTu87V&(5^0TvrRM<=lhWO0}olEU#l$CxQ8 zVdbCmzQEyMj8il@3Y2^dkcTmq=vSZeo|>zyDW2SNJUiX6Y`%;NNXAV{|m`lELI1O|!oKfHMtQhR_0^a0FB0tVN9>%O0$S!m#CE zl#WTNZ*q8Il(q~lMc2$^aYdodgk6TP>{e1nvaFgRh0?-IA~RC{q=QT)?qUZodm7>{ z9A~eC6G>s7lB%Xc&iuw8%nR*6MKHbr@uH+!fHDk;R@w zG|i|?N%aPUBMz4zQeiYInx=vr1K$(bIP4V4UBbap64QWC!j|cPmt!UcjYM>d}v{s9y&Vs?!!?Pf;C1quyaKVl>Ck+4fRVmp+j;+N0WwbH0&8d8GO$M z_ub@^r%$P&nMDcO{(LenCe z(V@2R4ujPHqDz`8hj}-CBd>iYWW#b|$7vAnjDn6A8bxYN()??K2qPKoFkFT-MS^D= z$`nQZ+FavrfP;S#T=-tX;J#Pi#hx*v8Dw&nV8w5OgV6-qxT{wJXm}z!l*6#2OOb@E zfp(M{J6R=Rhr0oE;;w?TR}>`QMsi$O$*O=W#!e+fCxJ>D2MJbmv|?tX<*abO!xj_N z;W8^S3JPn!Iq1bV7YfV4roeo#1ztDvT}soFowK#LmM%hJ3|YPzbjM*!ojm}r0FLSj?MST z5D8wvk;q=5qv1HpFz9dIiSK%Z29zUYY1-HoWK}|tGW*W?2b8Kx&>wuqWu~Cc62W0B zY9`)|?+!#0vJmNsjh8JP^Ue!B20YQBqaZEO$lu6u=Cs%8t!P? zQdRgdloO4IY0IAE$hh+`%P^{$(15JN_^iq~lT;#BCYl)SumF$fz{4X);`@jlaTjIr z7n@UZR4PAW)>LK=&d_M_^UjvthW^Vu9Tvk2qxCJA6HH_g9C8$J1hU@u{9RB!m16C1 zpz&8BA%GHd3E7QhD`R8j>kX1Q6ELktI&6f*uh2C+24vSkyTNZM~3t~uidLMWP;YhbZDJnV8`}Pu-KGEEDK%b&ms*88(G{X08S|e8f)g|SkSRZbbL6r zkcNZM(G+qVy6Y+d9j*?5L%#wmw4Tb!Us~o6bCx-%E1@0MF0} zR>3t%5ysVFev{G>!txkKkO8H+;-C|q979TpqzqQ_Gee@ly5hT$4LXdAB+KR< zC!wS*b<@k$G_WEq$t?o!NI5!i87jH3+hWYEF)S%Bnehv z2k;{QEI-6@xP(Ph9HejzV`PK#zj8!|5BbwExsz2%k{X@hziqJ%xf^g9Bqh<}E<6c4 zTQ)08hJ~bIS#*$gG{VTr>;_L#MKxron2lZQC%RKel8XLOQbZ|Rnpv_O`8vpiGH_=} z1y&$y>~dCQ!2t*JWzmp-84kl;YGcqw`Gt;lqb*0l^X_M5K;MZ`F&R9BE1ki|_%5+pYOwjvA$ZQP;F zf}5mF-%*w_Bs%FxomEUhtD3R>(-a*s=F&!$*qUmu!TK;3~0%$M8 z0tX!%%J>GyWhmpYZH>4i(^LjoSW+GqE1eVDPFlqQW%QZ*U6#>x+*vKE#(vL|(iSBx zWtEhkw06>#%TGr*Oh+>1j2$!gJEBa>8p@2a999i8EFLD8d>4VFwnPVr8aYwsRf#|=qOuk; zPWvu7ED>SEVRl2rtX0CpOkWw&7EHrwRLOIf#+X0x(CI=Yhn479tGgnP02t2-J2sR& zOqo1nzF|3lh8C1{RB%f*GfryxutaH%G{ds!!%DF18CVw6beAc{j-n@7E?T}Td{-F> zj|e&vR*@7l%M$sk4!D`v84iq!zz)cY{`QMifhch{ER%PX9b_~IgLJ<~l?Lp1b^wbt zOjF>jm_@KW;OK~8IYY0GxNF=6hqMkOs$yoj{L+$H6r+_`LW;Bnz9*w8vSx^xXBh_v z59h3HiQiGP%z+7pDV{Z0vdof3`gu5Nm?DnDH0F@}=3pUqxEhx;eVS$$4aS~V$4Vpve9r38AoKqglU@jvu26|f1tYx3l#;> z&Lc%F@+Kfu?16LcT;JYZj(spp8>fkmfKAGswYlK&iMg=F~<;*;>P8ffYd# zt1%4)C8=X%GbK7Ec>>AIX!QDF3sVgyfu!W2X4F75#TZnArKo7IC5RQA6*}Twtd62i z>=?5+%Ys@NPW?2Mu;s#$Ry>AQtV4pk{SnhAOuhmFN2}m?Div}>8B~4;%@!_dafg0Kmc<<~ zDdBL@5EX+I9Y|6J?VA0PhSWMEeX$i&8XbjMN;vt>vP4v4>@uwDm`8`@iE$7+I%2{M zQ^<<`G|c=XeQL9HbXb1vh<785Y$e{+Mo(K;QSvbIk7kJ~4cLXczyVuW*bdS`qx=$G z6>>*(5NBc8hY?e$84}dT5W~=E@;!sL<(vk_U2qTpT8^slZdOI6!_hzoI+g^mj21)^ zEn7*p=vP{rX3QljO_CvK+)du$XvV?2b|lNXT=I9(l+m@;VOY&_+c67HY*Kq|*>S~N z*kKV5U)4m17?(>*G94w9X{HZLZWiJ$S|*e*7BMSmNW2T)B`YN+N%=Vgt+?VOj3hU@ z9H*$bcvyaD3;%49o5W?}F!XS=n%R5L&{mq#=Y9FE(nd!Xsih@?se*d2#8(%&;zFcj zxmCjUq^OiHn+2x>J7`&8SO(3KI&u`~q~QcB4qF^LI!YF{7^YrI4nvP{#zAAu0Y4T^ z4~bHe|M+2si4G)+yv!&GwCWofm3n8;p;k)BY2+x|FoVpL$V>Pob#&T64O9v>!=h&u zP#@09;rgzi%p3+si7ZW(Hl!(jS2;tqKdVv`J6AXjQFJ1Qpqq&$fZoIIEI2d z@}#R|sVJR(iL`%j1idCHF58jONZgAp!^1FC2Ll~q(me(Z5@fwetPENi@HA8cOXJN& z(_xMwX?1s~8T&tDR8%GAxlR>Bl!-ISi!H6PA$s-W4?>4G*xooUP1jK3s^@HS{NEZ zNt~%RmiT8m3ZN0Cm4$Z*WOSHeIYFUA%ZiFQz!J+6H6xX%l{)fuh8gpScgaec#*a9v zKoT6+khY+PBgP!R`ow8zi(e?6go{RXgyA49`iVkMN{K~cN;E8VhN`fHB@U+)hlL%{ zDPt&rX3yHRw6>*~5xyNMXlsXr2Q9-;9D;*H%a=o-<2rbC7!I5q4o6u84&V1y`mQa< zVXym)N{)(S9HePRSu79ejI>Z=?Fqa{i7fP8AnOr^KN-`3u=B9jE*#sk-_avlv-oZx%d$im%)et?kkRnJ%)cZUH7ao^W5N-pStJN4`uojmcU)2NUvOmp zBkW38#40R{qy&rT&{9w*#!xnkv~t88c$)P~?BZ@t;RrJ4Ox)%8<5j*o!joD_bx``I zY{5<$%T|U(0Enc(@%T$qs!BL^{sN1KDLQ67jVx+bWEiRTVMd7zRc{8Zuz-%oosFrW z1gmLznNg^tRpOKbO9D$Z4y!Fg4AZ!um&aP(WgMoRE|Yoq1B^!eT$#!g)3S$j`jsPH zMjYyfFs-O)T81^F$>|UYjwEY!myQqv8d`CMh_XOSe)B4srK*JCASn(ozvLa=@Nhgr zg^kFO8duYlV}vOmre#D0L2*c=5`Pfom*3p1F~QT3WA;X8kdP4-jY%?RHNjNx zNP$Hg()Y`mckS2c*imkDuvQ5NcuJ|jL03Yfq-3myoa8rR7Iu8U8hGL!VbL*uREFR% zqf|=HD1k4gblT9d&!YUYtPE+|JS?pwOL3Tv%{1NBVNogQfU{3`Ws2tEF2rzOqjP3 z2bmKYGKHcQ?uJ881p~{&3{u+h;e1CIQ%oT(M=L=Tc6QAAI^QrIeHSGxU^%Doiwuoj zh9Q~Qsb_f>dN_9av@Bp9w7H_w%w8gc9BE;RB7z7kX-iL-M2D8+({Q+3g4ErhlA{=p zAqMFYCUlfPharKFD9UVfP&loOBPvcb*3^d~89T`s^~B?v{u*&te6I3 z%SamqmT4j@axlXYI21K!c{oan7)2$8g%}+q)af^Rg>e8Lo0ebPbX3eREE^omC>SFD z3t0Zo~96m^S2}JyU5pJFiInvQCehzMix*RJJ`Z$R)Mx82px%K zlSs`nL`s3XGK7wGP|!-BqH4$lol*p?sIL7&!jDoKVTX%xH!Kq@nv%${AnT@Jr*tJM ze>xgf1;-smn4(NL3TF$m;3=48aNwA>L=O3tR%kh4J0cpoio=N|akpq~z=3eHgl^&F z-QZaH>Z)>R@NWe`sW^m|(6Csr8gS>}2*0c^vEy0OL84;Jg41-yoKz~v29;D`*cvht zc7<84p%WC8Y9$6j%i#(CEJtzJr=4H~nK_ukzN|8YWtY^45wyjqw?#6Chr8n#LfE-)`f;QNyqNglR=Fm@@A4 zYcU_!Ifp33ZD5ijvxeD zR?re5WzdmtI!-lHqG`pXyOEJPr(CApu*C}y$DCyaqr%kUV3I{(n3S>=S9Mvgl>$#o z(p7(DTMJA2We;nry&_NXUMbOxFdZABQithvj>Aqr{U9XTR9D2FZlW05yVI|GX}sq|@(W6fu$o&a(b0S_T4n*$7@j7y_DpK|5kjU>7Ts-{9blj{O0_KUuqjnge&a5) z3nlLIvUZaC-w7%u>e94s3R$_5;i4v35mjnU*(fR0I;${sxQGf7+AJCzkAP;N6pNPf zaF@tZhMImEw3QS~&2mN=4IJQhG&nvTS7lkI5<0dAQxIDA>x|M-u(Vh%ejO#FNT8X5 z{W3!tqhbD7a=~G0D1$N@CCmhISZE*xi3XaE^)sMDk=P_SDJu-0{l#HInKfgOyzBgj zCC4PRUv&oPOgj;s~61H-jvGiCvghQQ%jyO$}k`E<~Ls z@0^nl6KT@{;AG5pOh+Z?h>~FpmZ=@4&~VscM9}gB(^n;hXw#0FQAG}{87zeyL8d&! ztkd)p>z?^Vg8%4nq@Sjh!w{wJQVi&Z3=sxj-8SugHn4-x1!omCV-wg!d1z@-ypI)q7tIwujq!mgf<+*Hyuf>7+D7?!xJ;&A237wz}A&kml>9| zw2GfB68>>~nWkyVYJoKz=6l9`mn~}L$`YewkZDww%2*qeWzj-AQu{Pbg)PPb z(>@HX3`fQ#N}ff6+#~V?QAL5ouzZ=P^}|66`yQ;&W=(iDJ!OWYyCp$h4o9iRJzlmL zewc!pGS1(4l(P~uMl%UI{A9w88iv&k)~Wpbb!{l=mUktaXe@suOiQFr&O6#7I^wKC z9*7lUSRR%`I#L{F5)-Cl{HVgCnk4|CWm)j!ibl-C-I3{HYWT~`&rJsa#qtRD2#cNu zbx4APceT-m!QurcIH*Mz6X=M8GXC*1?4%X8a^t&BFb@at)&+XgFbz?E zAjimq5>MkCGgcURImMJ2hH%$eCnVDWR%o4*4i{u)3i{6ymSZC=Wn{z{35SM9gk`q}M>-u>bCMVih7qReWr)gQrBa7STKyt5gNmp#3T81fmCX{4U@^2# zkQtkInXuFmlr0&oQJ30QT8bQCh*!ynl2I_4vQH1V%Z<1r$Bc@nuN+6rnf-u{91)Ll zml<0sHKeJ3Kr~wuS_+FdWmyRp?85XFg-gbAi}Ok%!wLX!BcnpLHqAdE&_+i&#??qz z36Z1&b|R_d;&!AJp-cXO$LDZJPSe-OrYGV)#5iQch?$_(W&24)Jb|q4iv0p_utl** zdV?ql0>Ys1SV+r`ImL01Je*NP7%;-~x=XO|&-`H+X?lp+ot9_E*zx=oc0?mzp;E%J zQV754sfhCYSqjMpXNVxJ&mi^7Px-leSJ_cP3;%$G;%ZnbDH)xiEMYq0kana@$BCVw z${=luu8{99v9r1)m|`9dQw0vnIxI_mvQSn-D-ONU1+Bv>rg@cY;25Hw@_^kh4=X`C z#-%uoduZs8}S?ss0spJWB2{Jf|b^D;g84;-^aX^nkcOB7yFSb)(9j^70C9en6t>jRg;u7dR?KoW z+@)4OB}g)uDJiVPwu4lHvSs=*R6OhK z{=wler3@Ui?cxul@@1(hi#rS~OvPdRkN5+g#7~MmN>R4ba#0d^YN;M9>+nE_JRR_a zpX7sK{~S93?U5Cp9Y$Ps9g1O#86}Zb%sw2-lHf1ovE`DXyt;(eCjeKbr5doJPC*Zz zQSJg>oKDmJ7P{7Elvf!M*{i$23^L9J9Qa4-AqI6Ug_`H0HK;CUHHXZ?P%|>AWmkvU z;LhO6w1)-lj>52IhJhcB*^G*Eam1^z6^E662@9Xqqc)`Ryed;+Ixq9E^-%C^o@V;^ zu`=Sg{$gj;c~@scio?+I4`2~L&<|Q4hRkIU%45g{iK|xq|G3#9E~hF9;y}JMXNBPDkHW11~zG_Bs_#A)A)0GDvlw=lZC$Q zS9h6S$w*hzQd~~xWDEzKK{5Ty%Ol5w>Bx|ts1LN_azt6eh{L)49hIV{L2F%>B1TGs ztSs_yyrZ3wct>!QKhemHeca@rI)nBwv`jdh0o9BrfYs>&7EJjMbQV`F$*t3;aVm$h z$Qb^}yc|rWC7#Al+7euua9|fT3)9$Vq4(LM_2~hfR5K?1FckfRXsCoWNGKWrt10E-F_pF~IZQz-q|y_uFnhP1921RDQ_G*L%hr~$RJ1rZ+_WVIGBQmm zEl1Q>mW!zo07`-R4`DDO{){c#5v@U?!x=QiaY_<=942s7N}bkK>cd*f3>yb*h)m*X zEA`h#QidPhWx?`v9EMSml1!sIe<+TwDh>z5^H8NHMWxfaN+ndGwdoQSPlR81qEd!< dSeLD(WBf=7!@O-R{tyEGq==RWb^4{{{{x#B{&N5T literal 0 HcmV?d00001 diff --git a/kbackgammon/sounds/kbackgammon-roll.wav b/kbackgammon/sounds/kbackgammon-roll.wav new file mode 100644 index 0000000000000000000000000000000000000000..29744c927bbeae558701d7791aec5d1eb5b126a9 GIT binary patch literal 7522 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 Otn!AOn)4!YfB!$|JM<3# literal 0 HcmV?d00001 diff --git a/kbackgammon/version.h b/kbackgammon/version.h new file mode 100644 index 00000000..11d5dbec --- /dev/null +++ b/kbackgammon/version.h @@ -0,0 +1,32 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + + +#ifndef __KBG_VERSION_H +#define __KBG_VERSION_H + + +#define PROG_NAME "kbackgammon" +#define PROG_VERSION "2.6.0" + +#define PROG_COOKIE 10500 // see also kdebug.areas + +#endif // __KBG_VERSION_H diff --git a/kbattleship/AUTHORS b/kbattleship/AUTHORS new file mode 100644 index 00000000..cfa61d99 --- /dev/null +++ b/kbattleship/AUTHORS @@ -0,0 +1,4 @@ +Nikolas Zimmermann +Daniel Molkentin +Kevin Krammer +Albert Astals Cid diff --git a/kbattleship/CLIENTS b/kbattleship/CLIENTS new file mode 100644 index 00000000..393dda40 --- /dev/null +++ b/kbattleship/CLIENTS @@ -0,0 +1,13 @@ +Information about other KBattleship-compatible clients: + +Client OS Status Homepage/Screenshots + +KBattleship Linux/KDE 100% http://games.kde.org/kbattleship +Mac KBattleship Mac OS + Mac OS X 100% http://www.sebek.de/ + +Wishes: +gBattleship +WinBattleship + +Nikolas Zimmermann + diff --git a/kbattleship/COPYING b/kbattleship/COPYING new file mode 100644 index 00000000..54754ab4 --- /dev/null +++ b/kbattleship/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kbattleship/ChangeLog b/kbattleship/ChangeLog new file mode 100644 index 00000000..39adbf93 --- /dev/null +++ b/kbattleship/ChangeLog @@ -0,0 +1 @@ +See NEWS diff --git a/kbattleship/INSTALL b/kbattleship/INSTALL new file mode 100644 index 00000000..02a4a074 --- /dev/null +++ b/kbattleship/INSTALL @@ -0,0 +1,167 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes a while. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install the programs and any data files and + documentation. + + 4. You can remove the program binaries and object files from the + source code directory by typing `make clean'. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kbattleship/Makefile.am b/kbattleship/Makefile.am new file mode 100644 index 00000000..409a1b7d --- /dev/null +++ b/kbattleship/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = kbattleship diff --git a/kbattleship/NEWS b/kbattleship/NEWS new file mode 100644 index 00000000..579e06db --- /dev/null +++ b/kbattleship/NEWS @@ -0,0 +1,45 @@ +Sun Jul 29 14:17:55 CEST 2001 + o 0.9 -> 1.0 version (WildFox) + (AI enhancements) + +Mon Apr 30 10:09:31 CEST 2001 + o 0.8 -> 0.9 version (WildFox) + (AlphaBlending added + bigger battlefield) + +Tue Apr 17 23:34:45 CEST 2001 + o 0.7 -> 0.8 version (WildFox) + (Rewrote network handling) + +Sat Apr 14 20:41:42 CEST 2001 + o 0.6 -> 0.7 version (WildFox) + (AI working + added) + +Sat Apr 14 10:56:20 CEST 2001 + o 0.5 -> 0.6 version (WildFox) + (ship-placing preview working) + +Tue Mar 26 22:57:08 CEST 2001 + o Moved into kdegames (WildFox) + (paaarty :) + +Sat Mar 24 01:57:31 CET 2001 + o 0.4 -> 0.5 version (WildFox) + (much has been changed) + +Fri Mar 16 23:55:16 CET 2001 + o Beta2 -> 0.4 version (WildFox) + (yiippie...beta state is over) + +Wed Dec 27 00:22:55 CET 2000 + o Beta -> Beta2 version (WildFox) + (only 2 really important TODO things left) + +Sun Dec 24 01:50:47 CET 2000 + o Alpha -> Beta version (WildFox) + +Sat Nov 4 18:23:27 UTC 2000 + o Checked in into KDE CVS (WildFox) + (module: kdenonbeta) + +Fri Nov 01 16:06:52 CET 2000 + o Initial creation (WildFox) diff --git a/kbattleship/README b/kbattleship/README new file mode 100644 index 00000000..e2e0ad30 --- /dev/null +++ b/kbattleship/README @@ -0,0 +1,23 @@ +KBattleship 1.0 +----------------------------------- + +KBatteship is a KDE implentation of the +popular game "Battleship". + +In the current version, you can play it +with two computers in a TCP/IP based +network and (as of version 0.7) with the +computer. + +KBattleship uses an XML based protocol, +meaning you can write a compatible application +in almost every programming or scripting +language (Would be nice to hear of a +GNOME port) + +If you have ideas or want to get involved, +simple contanct the authors (see AUTHORS). + +Have a lot of fun! + +The KBattleship Team diff --git a/kbattleship/TODO b/kbattleship/TODO new file mode 100644 index 00000000..6b14a6b4 --- /dev/null +++ b/kbattleship/TODO @@ -0,0 +1,22 @@ +KBattleship TODO List: + +p = partially +x = ready +a = assigned task +? = really necessary + +KB Team: + READY :) + +User ideas: + READY :) + +Competiton: + http://batnav.sourceforge.net/batnav-en.html (tronical) + +as discussed with danimo: +- there should be at least 2 sets of ships. Like 2 different navys +- one should be able to choose between different sets of maps: + - maps with or without land/island or whatever + - maps of differnt size (bigger maps with more ships) + diff --git a/kbattleship/VERSION b/kbattleship/VERSION new file mode 100644 index 00000000..66c8f8d2 --- /dev/null +++ b/kbattleship/VERSION @@ -0,0 +1 @@ +KBattleship 1.0 \ No newline at end of file diff --git a/kbattleship/configure.in.in b/kbattleship/configure.in.in new file mode 100644 index 00000000..35f58a6e --- /dev/null +++ b/kbattleship/configure.in.in @@ -0,0 +1,2 @@ + +AC_CHECK_HEADERS(sys/filio.h stropts.h) diff --git a/kbattleship/kbattleship/Makefile.am b/kbattleship/kbattleship/Makefile.am new file mode 100644 index 00000000..6c59ddd5 --- /dev/null +++ b/kbattleship/kbattleship/Makefile.am @@ -0,0 +1,39 @@ +SUBDIRS = dialogs . pictures sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kbattleship +kbattleship_SOURCES = kbaiplayer.cpp kbstrategy.cpp kbverticalstepstrategy.cpp\ + kbdestroyshipstrategy.cpp kbhorizontalstepstrategy.cpp \ + kbrandomshotstrategy.cpp kbdiagonalwrapstrategy.cpp \ + kmessage.cpp kbattleshipserver.cpp kbattleshipclient.cpp \ + kbattleshipview.cpp kgridwidget.cpp kbattlefield.cpp \ + kchatwidget.cpp kserverdialog.cpp kclientdialog.cpp \ + kstatdialog.cpp kbdiagonalshotstrategy.cpp \ + konnectionhandling.cpp kship.cpp kshiplist.cpp \ + kbchooserstrategy.cpp kbattleship.cpp main.cpp +kbattleship_LDADD = $(LIB_KDEGAMES) ./dialogs/libkbattleshipdialogs.la $(LIB_KFILE) $(LIB_KDNSSD) +kbattleship_COMPILE_FIRST = dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kbattleship_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kbattleship_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +services_DATA = _kbattleship._tcp +servicesdir = $(kde_datadir)/zeroconf + +xdg_apps_DATA = kbattleship.desktop + +rcdir = $(kde_datadir)/kbattleship +rc_DATA = kbattleshipui.rc eventsrc + +messages: rc.cpp + $(EXTRACTRC) */*.ui >> rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kbattleship.pot + +kbattleship.o: dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kbattleshipview.o: dialogs/infoDlg.h dialogs/chatDlg.h dialogs/connectDlg.h dialogs/serverDlg.h dialogs/statDlg.h +kchatwidget.o: dialogs/chatDlg.h +kclientdialog.o: dialogs/connectDlg.h +kserverdialog.o: dialogs/serverDlg.h +kstatdialog.o: dialogs/statDlg.h + diff --git a/kbattleship/kbattleship/_kbattleship._tcp b/kbattleship/kbattleship/_kbattleship._tcp new file mode 100644 index 00000000..7044f950 --- /dev/null +++ b/kbattleship/kbattleship/_kbattleship._tcp @@ -0,0 +1,4 @@ +Name=KBattleship games +Type=_kbattleship._tcp +Exec=kbattleship %u +Icon=kbattleship diff --git a/kbattleship/kbattleship/dialogs/Makefile.am b/kbattleship/kbattleship/dialogs/Makefile.am new file mode 100644 index 00000000..6f7fa1ab --- /dev/null +++ b/kbattleship/kbattleship/dialogs/Makefile.am @@ -0,0 +1,14 @@ +INCLUDES = $(all_includes) +METASOURCES = AUTO + +noinst_LTLIBRARIES = libkbattleshipdialogs.la +libkbattleshipdialogs_la_SOURCES = dummy.cpp connectDlg.ui serverDlg.ui \ + chatDlg.ui statDlg.ui infoDlg.ui +libkbattleshipdialogs_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) +libkbattleshipdialogs_la_LIBADD = $(LIB_KDEUI) + +dummy.cpp: + echo > dummy.cpp + +DISTCLEANFILES = dummy.cpp + diff --git a/kbattleship/kbattleship/dialogs/chatDlg.ui b/kbattleship/kbattleship/dialogs/chatDlg.ui new file mode 100644 index 00000000..40ffdbf3 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/chatDlg.ui @@ -0,0 +1,91 @@ + +chatDlg + + + chatDlg + + + + 0 + 0 + 252 + 236 + + + + Chat Widget + + + + unnamed + + + 5 + + + 3 + + + + layouter + + + + unnamed + + + 0 + + + 6 + + + + commentEdit + + + Enter a message here + + + + + sendBtn + + + &Send + + + true + + + Press here to send the message + + + + + + + + chatView + + + WidgetWidth + + + true + + + + + chatLabel + + + Chat dialog: + + + + + + + + diff --git a/kbattleship/kbattleship/dialogs/connectDlg.ui b/kbattleship/kbattleship/dialogs/connectDlg.ui new file mode 100644 index 00000000..1a5b53de --- /dev/null +++ b/kbattleship/kbattleship/dialogs/connectDlg.ui @@ -0,0 +1,183 @@ + +clientConnectDlg + + + clientConnectDlg + + + + 0 + 0 + 332 + 148 + + + + Connect to Server + + + + + + + + unnamed + + + + nicknameLabel + + + + 5 + 0 + 0 + 0 + + + + &Nick name: + + + AlignVCenter|AlignLeft + + + nicknameEdit + + + + + + + nicknameEdit + + + 10 + + + Enter a name that identifies you in the game + + + + + serverLabel + + + + 5 + 0 + 0 + 0 + + + + &Server: + + + AlignVCenter|AlignLeft + + + serverEdit + + + + + + + lanLabel + + + LAN games: + + + lanBox + + + + + Layout4 + + + + unnamed + + + 0 + + + 6 + + + + serverEdit + + + + 3 + 5 + 0 + 0 + + + + + + portLabel + + + + 0 + 0 + 0 + 0 + + + + &Port: + + + AlignVCenter|AlignLeft + + + portEdit + + + + + + + + + portEdit + + + 65000 + + + 54321 + + + Choose a port to connect to + + + + + + + lanBox + + + + + + + + kcombobox.h + + + + kcombobox.h + klineedit.h + kcombobox.h + + diff --git a/kbattleship/kbattleship/dialogs/infoDlg.ui b/kbattleship/kbattleship/dialogs/infoDlg.ui new file mode 100644 index 00000000..3c66d854 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/infoDlg.ui @@ -0,0 +1,248 @@ + +KInfoDialog + + + KInfoDialog + + + + 0 + 0 + 254 + 197 + + + + Enemy Client Information + + + + unnamed + + + 11 + + + 6 + + + + Frame8 + + + StyledPanel + + + Raised + + + + unnamed + + + 11 + + + 6 + + + + Layout6 + + + + unnamed + + + 0 + + + 6 + + + + lbl_clientIdentfierLabel + + + + 1 + + + + Client identifier: + + + + + lbl_ClientInformationLabel + + + + 1 + + + + MShape + + + MShadow + + + Client information: + + + + + lbl_clientVersion + + + + 3 + 1 + 0 + 0 + + + + CV + + + + + lbl_clientVersionLabel + + + + 1 + + + + Client version: + + + + + lbl_ProtocolVersionLabel + + + + 1 + + + + Protocol version: + + + + + lbl_ProtocolVersion + + + + 3 + 1 + 0 + 0 + + + + PV + + + + + lbl_ClientInformation + + + + 3 + 1 + 0 + 0 + + + + CI + + + + + lbl_clientIdentfier + + + + 3 + 1 + 0 + 0 + + + + CI + + + + + + + + + Layout7 + + + + unnamed + + + 0 + + + 6 + + + + Spacer13_2 + + + Horizontal + + + Expanding + + + + + PushButton7 + + + &OK + + + true + + + + + Spacer13 + + + Horizontal + + + Expanding + + + + + + + + + PushButton7 + clicked() + KInfoDialog + accept() + + + + diff --git a/kbattleship/kbattleship/dialogs/serverDlg.ui b/kbattleship/kbattleship/dialogs/serverDlg.ui new file mode 100644 index 00000000..f16216b5 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/serverDlg.ui @@ -0,0 +1,132 @@ + +serverStartDlg + + + serverStartDlg + + + + 0 + 0 + 247 + 138 + + + + Start Server + + + + + + + + unnamed + + + + gamenameLabel + + + &Game name: + + + gamenameEdit + + + + + portEdit + + + 65000 + + + 54321 + + + Choose a port where the server listens on + + + + + portLabel_2 + + + + 5 + 0 + 0 + 0 + + + + &Port: + + + AlignVCenter|AlignLeft + + + portEdit + + + + + + + + + nicknameLabel + + + + 5 + 0 + 0 + 0 + + + + MShape + + + MShadow + + + &Nick name: + + + AlignVCenter|AlignLeft + + + nicknameEdit + + + + + + + gamenameEdit + + + 100 + + + Enter a name that identifies you in the game + + + + + nicknameEdit + + + 100 + + + Enter a name that identifies you in the game + + + + + + diff --git a/kbattleship/kbattleship/dialogs/statDlg.ui b/kbattleship/kbattleship/dialogs/statDlg.ui new file mode 100644 index 00000000..7ff2c202 --- /dev/null +++ b/kbattleship/kbattleship/dialogs/statDlg.ui @@ -0,0 +1,465 @@ + +statDlg + + + statDlg + + + + 0 + 0 + 144 + 338 + + + + + unnamed + + + 5 + + + 3 + + + + Spacer20_3 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + Layout8 + + + + unnamed + + + 0 + + + 6 + + + + Spacer18_2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + OwnLabel + + + + 19 + 1 + + + + 0 + + + + + TextLabel1 + + + + 19 + 1 + + + + : + + + AlignCenter + + + + + + + EnemyLabel + + + + 19 + 1 + + + + 0 + + + AlignVCenter|AlignRight + + + + + + + Spacer18 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + + + Spacer20 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + gbShots + + + Box + + + Sunken + + + Shots + + + + unnamed + + + 11 + + + 6 + + + + Layout17 + + + + unnamed + + + 0 + + + 6 + + + + pShots_2 + + + + 0 + 0 + 0 + 0 + + + + image0 + + + true + + + + + Spacer9_3 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + ShotLCD + + + NoFrame + + + false + + + 3 + + + Flat + + + Shows all shots + + + + + + + + + gbHits + + + Hits + + + + unnamed + + + 11 + + + 6 + + + + Layout15 + + + + unnamed + + + 0 + + + 6 + + + + pHits_2 + + + + 0 + 0 + 0 + 0 + + + + image1 + + + true + + + + + Spacer9_2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + HitLCD + + + NoFrame + + + 3 + + + Flat + + + Shows all hit ships + + + + + + + + + gbWater + + + Water + + + + unnamed + + + 11 + + + 6 + + + + Layout18 + + + + unnamed + + + 0 + + + 6 + + + + pWater_2 + + + + 0 + 0 + 0 + 0 + + + + image2 + + + true + + + + + Spacer9 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + WaterLCD + + + NoFrame + + + 3 + + + Flat + + + Shows all water shots + + + + + + + + + Spacer20_2 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + + + 89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000030649444154789ced9531681c5710863f8535ccc209de820dbb2081b67071450a77c1b8ba2a3e3b019d9322526564bb31a9845358c685e154048e5422453029020a84dc059c7855283e55569360a7307663d840047b60831edcc10e64e1523ce94987e390a45173af5916dece7cf3cf3fb3338f771f7392e79d13cd3e0598024c01a60040f06f2eb5efb6c7c76f96554924115ae9e4bb2a6bf7d666fe0bc0ccf155dc59ef8cb55287551d254be753ecc82281608716131a08404b855360c460d562c49017396110faf2d66eff339007e8ac77c62631e848d152915382a2482000482034de6f604243efc71e5460871609052a10910970dc67d8c2a2958bb37a7bf50d180fd05e6f8fcdac61e993251f48878a558b20c8acd05cfa88ecbbefa184ece70c0055a758e34203803889e9fdd0f3eda1c2c7b3430b15acdd3d52c59b5070156c7ebbc9d35f9e32f863407a36e5dcbbe7a8d7ebf477faf47b3f615f596456687dd072df89d0b8d0209d4f8993983008b9f9e94da79c82d4c4ab129f89899398f67a7bfc0680a2fe62fe7b8e56cac6571b68a544a79de1545d558362e0ed7bdc88d76e5cc3c40680d6620b630c12887b8a38a8c081b4ef3908ef6d4126fabd902ef0e4b727d417eabe872272d4bc03d8e474e22b7f983de4ca8757e83ee892cea584b5100984642ea1ac4a4242f647fb586b7d984905c499a779b1891d5aeedcbae393fbe900e2f91802884c84d4846c3bc3c40641e83ee872a97989ce971d072c4e1d1da94b3eb2e479cecaf59599490f0482202c2f2e43004994f0fce5735e3c7bc1eeafbb2c2f2e73f9e265d2f9142ad8d9def1b28612b2f9f526f95e8e20b43e6ed1dfea3b238f14fbdaa223a5d82b28f60a563f3b9a06df02ad94b016d2ddeabaa572e8890307975af28847945549b69511d522440455259d4b290605d976e6a56d2e36b9ffcd7dcebf77de8df49f3a91f8f04c2ea2cf3b631339c378e9554917d2c9193f50ec703911b84ab552f65fef3bc396ce9c7f97f4ad006f3b1b5f6c8ca5e6608e8f1538a8c384e68c61e5eacaff5fc527714efc6f380598024c01a6007f0170086155964fc7580000000049454e44ae426082 + + + 789c4dd9577332bb9606e0fbfd2bbedaba3b35a5436ce89a9a0b8213c9c6018ca7e6425a92b04d70001c989aff3e42eb5d3aa7becdc68f5ba99752abfdcf7ffc79bc19fff9c73fffdaedcdfe85fed0b3f9fcf30f77d86c7efffb7ffeeb7ffffabb5efb13ff6bd66a7f6a7fffc75f7f87a73ff447d5ab756ad59307c965a3021fb26df2add834922bd994dc871bc8dfc9e6fc4331d2dfc16d5cef9ddca8e6fcdd6cbede12a3feaf6c2eaf80db9cdff7b239ff4ecce9cd354c4d73b2ab8839bdcbd7dba93c7b64372b458adfd2271b297f19c45cbefb81519ebf1773796e22b6e97edc96dd44bc692846fa1b31a73753b8c9eda19698d3878f6cbe9f8d98f387f76c97daafe092d3077372b326fde1155bdae367b0d4f799cdf9dfc45c3e6de112f15c64f3fd8ec4dc3edfcf4ef9cd0dec8ad45ff6498cfacec49cdea7f1d53cc59febfb654b7acbf7638a2af26fc488c7524ccd54ff1c6e72fd612b46fe5f71c9fdf30ae37ebd3eb9c8f7474db6d46fbb30fad35e65737d5e8cf4468cf65dc2258f6723e5957cff6e2de6fc6622e6f6aa05ecd09f293e8591f16e1cbb28e16636d7f70d3baecfccb2b9bd5a8cfa3762c4eb458cfe2676abcaf9555dcced33a36c2ebf23e6f8aa463697b71553b26dc34d943f15a3fc75365faf88d1de9598e31f72798e7d0f97f0f8e4563daf577b18ebc5d288313f576c99efae849b886f9f2de3c33fc125e23d80d11ff682ddaa36aa29ff39dce4f1a2e662ce6f0a18e3d7b4c4656aafaa89793d26b4af85fe3646ccf9d5319bafe7f41c4f3ab0dbe85ffacce6f985f6b69b885f1a2f2d2bf10bcf62ac1f1b9878bc869118ebf3af18f391efd74abcad65cbf8f40f62f44f939dc7570d96f1f403a33fd4af18f15cc2aec1f17f10235e3d31c7dba27d121fa3b2b9bfbfc598cfefd9293f4d608c6fda67737fdc89915fca6f72ffaa2d2cebc78398d3878518fd81fa4aec372e5d6fd7637ff07af62a463c976c89bfafc0586fc2373bc77f9dcdfbcb869dfbe30946fc4d1596f568c89678aa7d365f6fc0129f67b8e4fed22b31ae1fc5581feec5bc3e2823763ec5a30b633ed0bb98f3eb7c9df37b6297e8df7001973e95e7f9feac3c0f84c09678f82d8cf5caf561c487be6089cf588cfd9ac43c9ec28f98cb23cd9678299427f1501f625e7fd5272ce5bf66a7fcfa458c7834c43c1e75919ddaa33bec1c1f9bcdeb5536a7571b31dab3869b9cdebf89f9ba47fb4bac7f24d7713f01ed8bdd9bfa233cb08dc43bc5b36c349bcdd43fbe0e3b5e7fed0fbb25fb1fd2b7f1fcb1b4d9dcde6b18e3cfae60895f9b5dcafcd5d91cafb118eb9d83f1bca05662c42f5f47fc8762ceaf6bd9a97d7a0663bca89198f3fb39db54d92ecdd7385970bef0255c72bcec1bbbc07e45b7b03c0fb6d8323ecd153baf971d18f76f2b62dcff038c78aa4731ce0b6f623c5fa0bd6df457388a79bf27d45fcaf34280cb7a99aebf8ab9bdba22e6f1a3af6194afd11e89971a6673fe252cf737870dd6e34b76dcc0783d49cf3be6341ef9f9a1c52e106fb563b7509e5f8811af4b18cf7fdec3e88f70c56e5779bdb31f30f61fdd8425be3d18f3393831d6cf6e363fbf6dd832df6d15c6f80b62e78b94bf604bfc6805637ed8001bb48fc4886f4f8ce78506ec71dd8b313e70ffb686f39a860bf692c421a4f6d660c3a6b4df19cacfdb962dfde1de615cb777629ebfc4fd17c73ff6df4e36af2f6d31c793c438af995b31d6f70b589edfced825ca3703318f2f7b80d15e7523467caa621e1f3485e57c7493cded7b8631bf692cc6fec6e325ce078c97168cfa0dda23fda32fb3793ca17c5be3fd4fefc548bf1373f9fa072e38bf0ab081cf611fd2f85b72ff13d530ffd278b68d461bfbb566cbfeed9beca2caf39114ecf8bafa664b7fd2078cfe536f62c4a32346ffec60399f7e8ad1ff0d31f6af2a8cf9e87ad929bd437dd21f74cd36689f6d8a715e1d8ad19e1296f68fc548ff9ecdd72f61999f67ecdc3f688f2d38bdbe12737dea0e36bc1eab6731d6db0f31ea7b813dda83f8510de9bfc48877339bdf273818f3dda5f1695ddedf385eae85fe371531d6f32d5b9ee74d1d96e7438e8f2b25be8f30e2e58eb03c7fddc298cf6a26c67a3165e7fefa81d11ffe1c46bcec9718eb33da97e7574dccd7cdaf18fddf675b5f4bfde1d2f8a6661eff812deb9dabb3db553c3f8d608c5f7560cb7aa408c67aa38fb0c4e315c6fe6d2ed806e76d752fe6fdc27c89110f03e3fef4488cfd1ce55b8c073511737d7a91cde9f375debff4209bafcf618c6fd517e37d036573fa3b36a17fc20ddb798cc7b4fe9393f3968765bdf1b76c79dfe2348cf70f96dbebe4f9df217d89fdd69ec34d9e6fc6c3384fd947b6ac076691cded2f61192f2f6c5bc3fcaf8a119fba98d75f5ac3888f6e8911dfa76c3e4f59d820fdbfccd7cf600fa7f5d235653efb6b7651e5fbf37b18f7eb6aec16c697dfc178fea13376bb8af8dec3d81fed275bf64bd5661b8c6f8bfae27ce47879b695e7956731daffc8268c3f3510e3f9c967737c038cf891a437ec308531bec29cedf17ce3d3f398f3395e1dd8f1f8b09cdf174dc4eb15c6fb12ba64cbfe6567ec529e57ea6c996f760de37eed924d059e0ff6628c0707e37ef46f36c7a7643b8caf700dcbfda5fef54581f39e1bb25b309db3e5fc402fecbc3e9cb149d68f4f31d7a7fbd9a93e75c176e81fb51423fd7b764aaf0f7081ebdfb0c17ce1f6fb16c69f53b0f40f97e7e57c4f5c9fcfe795299ba47c939deab77c7f317ef5f43e529f8bd1be0b58d2f760c3fb835e8b91fe06463c96cfec80eb36ad47a190f7e36ec596f5cbdfc0f2fef2832df1359f6c295fa33c8ff686193b884b589ee7d3fa11421ecf23b8e4f7b1ee0cc6f8b6dc9ed0c2fee7d762fc3de908233ffd8831de5e6087f176c76e233f1931febe24d7b13ed12f2cf3cd89f1f7262fc6fbc43ebb94f1f22846f97358ce9b1762de2f7c80e57e2a62b4b72ac6fb81158cfb73cf62ec5713b6c1fb58ba873de2d1cbe6f69eb36d0dfe12637c2ce102fd31860dde473c89393d9118f737803de2331363bd477f12ea73056cf0f748f41779dcdf6536bf8f7c60cbfc719f7081fd6524c6fedc800de2f721c679e0578c781f60d4e7afc4188f3f628c870edbd7709ede8ad1be265ce0feac18e3e5418cf74d4a8cf984f879c3eb8fbbcfe6f3f18518e7fd6e365f1f88b15e6dc458bfd1ff7185e3f1f328c6fb1aa90feb81c77c0b35d4f790cdef4f5ec578bedec3781fe09662fc3de9518c7821de41da1fc4382f2fc4389f1dc4d89fead9dcde211c50bfcbe6f3f4b318e5edb2f93c4162bcef9a67f3f3c4b798f74fe7b3f9fdf7a598db37dd6707b656f8186d3569a7bd0e7aa99ff58b7ed52bbdd69b7f4bf3ef9fede93bf5d19b7ed71ffa53efe267af0ffa4b7feb1ffd1b4b39ea8eeeea9eeeebb378ed439fc7b417fa32faf4f355f2792a63a0877aa4c77aa2aff58d9eea5b7da7eff543fc79a6e7fa512fe2f5275dd1555dd375ddd04d5d440f754bb775a974da8b9532ca2a524e7915d4523deb837a89edf951af6aa58f6aad366aabded4bbfa509f6aa7f6eaa0bed4b7fa51bfeaa83aa98caeeaa9be2675a6ced585ba8c255ca9811a6aab466aac576aa2aed58d9aaa5b75a7eed5839aa9b97a540bf5a42aaaaaf89d415d35e2bd7faba65eab22b6a3a5dab194d2e818dd9551f12c1d8fbbaa6ee253b60966699ecd8b79352bb38edf1bb34dfbe09b9a9a77f3613ecdceeccdc17c996ff3a397e6d71c4dc7744dcff4cd99daeba6393717e6d25cc5dc03f56e866664c66692cab83637666a6ecd9db9370ff1be8f6666e6e6518dccc23c998aa99a9a9a9b7aece96e2cf5f48eae302dd336a58db76b797fb46a61e9745eb4c12eedb37db1afe6ccaeecda6eecd6bed977fb613fedceee63845fd5c81ef48b9ae88efdb2dff6c7fea6328eb663bbb667fbf62c7eceed85bdb4577660877664c77662afed8d9dda5b7b67efed839dd9798cd7ca3eea9969d8857d4a6554d4d15663b9b5f8a9db868d272edbb26d5b92563352f1bc6589c891a7404b7aa6177a55135ad13a2e541b4a31556b7aa377fa88390bfaa45dfcece34f3b3ad0177dd30ffdd2913ad4a51ef5e98ccee9822ee98a062af60c0d6994e6df9826f1b3a7ebf87d63148dd5034d632937744b77744f0f34a3393dd2829ea812cbac528dead4a0753c1316d44afd62629d13f5aa67d48e43a1a09276a7f79d7470da2967e2a31d39179f71835bba67f7e25edd4a3f9a7bb7761bb7a5616ac7944af716e35d4b751fa227b14def3476efeec37dba9ddb4ff7ee904ad9b92fba73df7109fe71bff14e0a2e23c561e28e31578c83ebd044af5cd7f55cdf9db973b77717b1043a7ddca5bb7203377423377613774d43b77537a98c669c51e3d85bb53837c66a6b9bb18c69fcdcba3b771fefc44ff7fffae71edccccddda35bb827f74385aba4b9df7155dac77562651a3449ad6ad397abb9ba6bc493cddceda67b9ae3b388ad896771d7726d577aed15c7d49b382e9a6a150f1ee4de62ff8ce309d3c587fae097319697ff6a837f7697fec5bffa955ffb0d1576e1b73cc674c7bfe9957ff71f3e96e03fd536c6a5e1777eef0ffe2bc6c0f96f174ebdeb76fec7ffc6128ebee3bbbe1763daf2fd148fafd3d857237f16579bb1ebd2752cf1dcdef90b7fe92efc55cc35f0c358dec88ffdc45ffb1b3ff5b7fe2e9e87eefd839ff13855af7eee1fe3aa77f414e7a6f10bffe42bbeea6bbeee1bbee90bdff26d5f061d94bf0b26f6498586fededdc4ffcfb85f820d71258ceb45195c88bb27b54efd1e96ae129ee3eef4e27ec2abbb8ebe0e2b77ed1fe2b5754cb3f133dad845da3bb66eeb676113b67641adf046f13b46ab1f530d63eaf7f8f9b08b106797fd4dbf1f86cf98eefd942bd6b24bfbe13e1cc257fcec7d2fec7c4fc65ff8c6671d6bf8b64fa7391a7ec26f389e72864eccd73da54f65c49f422fe63f7df7e3b54e2cb1ef4fbf49bf4fdffd78b51fcec279b80897e12ade5d2f0cc2f0f4fb54c6574c358a9faf980e6d09e358d2e97b7ffa8ea94f759ed21dc3245c879b30b54fe1f6d4f67097ca18a43abed2f780db70726a413f95d065876e8cc37d7808b3300f8f6111cb7f0a69acc73a631da7fb43abf97b80b645a756e3fe634caaf1a4530ff5e93e3442330c5219456a4327a66ec5cf21b76590ee8ebf0b6e5bfcf9f4af1de20c08e574bfd4cbb467a7bac72945d7fd22b2e353ce531fc53286a92da7b6759626d6510983a55d12cfa1a5433c3a5cf7d2fbde32a43e487d13bac912af4e90b6c4b62d97a7b62c9fa7fbbfffef3ffffa7fb85958cf + + + 789c3d98d9522b460e86efcf53504777a7a614e3dd959a0bc060035e31669b9a0bb51730c678df989a77cfef96d421247ca8b5fc92ba7d92bffe9cbd749a677ffefab5dec86632381b7cc8eaeccf703b9b1dfff3df7fffefd7ef5ce60c7f670bd9b3ecef7ffdfa1d32678333cae0af522ef29d71def8d2b86cfc9038447e331e987d9d58ed7de7b2dadbcae719b5d3dcd9ecf7c679655a189795799f38faf393f140996e94b319b31f8df3957c8cff9c7810b96a3c50e60be55cc6ec1abfec7ab8a79cea6767ab6f636cf5f3dad8ea0fb789355e30b6fef020b1da0fcaae275c39ab1e1e1bbbbe4262d533342eab9dae13abfdc3d8f487aeb2ebe76767d1f87963ef5fd6d9ece7c665657e3cf1799a273f18fb3cb789a3beeec6d8f5af945dbf8871deec7d67ebc7c2d8f4f22cb1eaf9311e987d9258ed2367ab7fa99cfaf1eeac7609c6a69fbb89b53f7563ef4727b1da73c69eef32b1da5f94f31965a6c8691f588c4dbfd48c7dbf76ce66a7c4311e358dad1f524dacf637e59ced1bb5136b3d5367ad9f5ac6b61f5475b6fd38248e76c9185b7fe822b1eaef197b7f3e136b7cd3effd09afce41cf5f19fb3c5ace6aa7983f9bf6452ac6de8f5162cdf7a29cde8799b3d5ff6cecf7efdbd9ec2563dfc7b9b3f587126bbeacb1e9a75e62ed9fc71fa81ea92be7bd9e65628d977336fd0563eb0f579dad7f2de3b29d7f30f67c711fb215d7136ace763fae8cbd7f4767d37b9358ed3fca5e3f7f2556bd0767cb3f36f67d1a385bfd569fd72f6f8987d1ee6c7ac23e71b493cea352f07d7a491ced12fb933bcff9fbfa6a6c7a65e66cf36828bbbeb04dacfd5b199b1eba74b6fedf1bfb7e1c9cadfea5b1f7b3e36cfef9c4aaef56b9e0f98fcea6b796389e0f4de382d96f126b3fe27ee5d2fce4d6d8eb3d37f6fdbd76b6fa878935de93b2d7270fcea6a79858cf4f8dbdbe4662d53b741e14e2f90be38a9dbf4eacfb5171b6f31de3a1e5ff4eacf9e3e7511ef3b2f96f8c5d6f4139f5bbe16cfbb932b6fa696fecf5349db59e3032f67a2689f57cddd9ce4f958be776be9658ebbf71b6f38fc6de2fcd2f5e5fb830b6fae4ced8ea916c62dd9f0f678b1f94bd1ef94aacf3da3a5bfefbc46a777fab8fc689d5de35f6f9c5fd2f640b05dbcf86b2e70b3fce16af6c6c7ae53db19eff74363d8bc4da0f67eb0fad9cf53ce7126bbcb9b1efd3d1d9cebf26567b4fb9e4fd89f32a88d71b06ce66ff34f6793d3b5bfdef89d5be36f67a76ce564f39b1da4bce43eddf9d72c9f76be26cfeb789d53e7436ff91b1f733be87c5acdbb9686c76ce38ab5d96c6a68f7689751e5fce966f6d6c7ac2d859ed21def762aa3fec8cbdbfe7c63edfbbc45affd6d9f23d195b3fe923b19eef3adbf94fe5b2efffccd9f4c679947229de5439d98bc6deaf92b3d95f138fa2ffc0d8ea0df1fd2b8574fec7d8f55c258efef2616c7ae8d1d9e2c5fee38f7b43cbf7ae5cf1f91612477bb8761e15238bb1d5132689357ffcefb372a8b8de7b67b3bf386b3cda185bfdd47756bbec8dadde10cf57f26ea78ab1e9e37367b57336b1fab3b2b8def83e550662fa649e58eba91b8bdabb1b26160e3ce0218f78cceffcc113fee429fef9c5337c7ff39c17bc846582af39af78cd1bde763731df8677bce7031ff9872ff892afb8cad77cc335aef32dbeeef89e1bdce4167ed386adc35d9c7ee01e3f44ff47eec3ff899f91b3c52ffcca6f9ce173ce728ef35ce02297f05319bf7be50ae2bee0dc1bef88cd7f83cafba8eb8a88845b14b844031ad288c6f44e1f34e137fa44ee6b6ed094be6846df88d2e715cdd59f167ca4254edcd08ad6b4a12ded28d09e0e74a41fe86dd305f7e91267eafc4a57885425a12a8fa3ff135dd30db886c8759aa1ca3addd21ddd53951ad4a4166a5b529bdac8dfa13d7297a94b0fd4e369f43ff57dc22fa8f791fa34e25b7aa2677aa1577aa30c9df39cb298d152274039cad3150dd1c9776a47ff211f68498553975073910aa0364fa94465aa0843fb2b1d857823c27d0950b34505797451f52ff92003be95217ed7a4aa8c507755c6f22e1ff42113f99429ede44b66f48c492fe59b6b72ea89f02afaa343b482e62f78f7a014ded496057dd2a32c29c80adf4b59d34a3694e505f5309b2ff4d0f2a3331df438275bc4cc4125b64b76b2e73b643dc8024adb3493a3fcc8858ce15fe51bb9c446d4e839eef79554e51a14d09b3636b3cf5f31f717a69fc50cdb98213443c10dcfa52675e8bcc5e97b5e46ff5bb9433fa6728fcaaad8a6be4c658f0dda4a439ad2a21769c3a3814daed1443ab8050dd94a97afa51af52fe8809343587bf07d40b706d2931d8d642d8fd297963c207f40bc23b7e409353c23ff0a3bacf39fa3b217799537c9a0febe9c43f71e9dcf626a39c11f74a881c91631a1a21cb14fd82129a19b2b91e83f96326ac3ecf95b3252917a60da7109678b98c4919e0261b773320912023ab20c03dc8a3cf2af63ff73d4912e5d85217fa303cf61848c35dc8470ea12badf44e426faf1888a3e70977a50b285da0bf5c75635b077537c8454b14b1bec721ffbbfc2366fd1f11aedc3587ab486f7845af09a8577dcc9a2d47911ebefe35e35691d3ee880fdeaf3166fd14c1ec204a74af0df05fc1bb3dc63b3dbb8b53dd83ee50691ecff4fe1a56ae34e8c30a34fd4b0a6676cef17b47e4a8f9bf2c1af612a0ff406db842e90ad717a63907f1ffd2788dae67af8a229269b0933d480b72f7ccb2eccd1c53db41c51f951821cb81d16e8de90af4fbb12fd0f889943059f88104e5b80aeb730d53195b16b3dec47239ceeffe95435ac30bd4beae2055986b5ea87822a5ebe06ee652e6c4eb700b358e06b0eef1dfcaaa77b2b6fd4099f611b7698247a110eda7fbc9c630c768f19d730f52ee2bf862366dec6bd20285923f712513be107fe1738d5c01b7d7a95a27fb80c57f0afe2b737785f6fa1af18f05606dc16fe0eb530c44f5fb0e28e40770df716d5867ab8d5fe87bb704f0bbc8105bc22d7786df0e663f7bba7379f0ea181cc43dc864e68483634d1834bbe0c2da87e228efeb7618daf76b8c21b56c31d29a136dde82ddefc1c174307113ba78d42f62a7ad8c50eddd33ce8fbbf0e0fb408f7bc878a1eb6fa0dd3bcc6e7c07778c4cf3778d1d7127f4fe8c9e9f303afd03ef4a13bfa87a7987f8d78cfdcc1fb38c73754e27daba2ceabd00a2fd0fb125e4f1cead073dddd40f53af463fe157e7a0b99b0c64b7efaf43cbde647be4435e7211bb2e8d765c885163e217a218fd8b81fe13eb45173e1f7fffffef50feb776dab + + + + diff --git a/kbattleship/kbattleship/eventsrc b/kbattleship/kbattleship/eventsrc new file mode 100644 index 00000000..c2c8fe7e --- /dev/null +++ b/kbattleship/kbattleship/eventsrc @@ -0,0 +1,475 @@ +[!Global!] +IconName=kbattleship +Comment=KBattleship +Comment[ar]=لعبة سÙينة الحرب (KBattleship) +Comment[be]=МарÑкі бой +Comment[bn]=কে-বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª +Comment[cs]=Souboj lodí +Comment[de]=Schiffe versenken (KBattleship) +Comment[eo]=BatalÅipo +Comment[hi]=के-बैटलशिप +Comment[hr]=KPotapanje brodova +Comment[is]=KSjóorrusta +Comment[nds]=Scheep versenken (KBattleship) +Comment[ne]=केडीई बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª +Comment[pa]=ਕੇ-ਜੰਗੀ ਜਹਾਜ਼ +Comment[ro]=Bătălie navală +Comment[sl]=KBojnaLadja +Comment[sv]=Sänka fartyg +Comment[ta]=கேபோரà¯à®•à¯à®•à®ªà¯à®ªà®²à¯ +Comment[tg]=KҶанги Киштиҳо +Comment[tr]=Amiral Battı +Comment[zh_TW]=KBattleship 戰艦 + +[shoot_water] +Name=Shot at water +Name[ar]=إصابة ÙÙŠ الماء +Name[be]=СтрÑл у ваду +Name[bg]=ПропуÑк (Ñтрелба във водата) +Name[bn]=পানিতে কামান চালান +Name[bs]=Pucanj u vodu +Name[ca]=Tir a l'aigua +Name[cs]=StÅ™ela do vody +Name[cy]=Saethu i'r dŵr +Name[da]=Skudt pÃ¥ vand +Name[de]=Schuss ins Wasser +Name[el]=ΠυÏοβολισμός στο νεÏÏŒ +Name[eo]=Pafo al akvo +Name[es]=Disparo al agua +Name[et]=Lask vette +Name[eu]=Tiroa uretara +Name[fa]=شلیک کردن در آب +Name[fi]=Ammus veteen +Name[fr]=Tirer dans l'eau +Name[gl]=Disparo á auga +Name[he]=ירייה ×‘×ž×™× +Name[hi]=पानी पर गोली चलाà¤à¤ +Name[hr]=Pucanj u vodu +Name[hu]=Mellé ment lövés +Name[is]=Skot í sjóinn +Name[it]=Colpo in acqua +Name[ja]=æ°´é¢ã‚’撃㤠+Name[km]=បាញ់​ážáŸ’រូវ​ទឹក +Name[lt]=Å Å«vis į vandenį +Name[lv]=TrÄpÄ«ts Å«denÄ« +Name[mk]=ИÑтрел во вода +Name[nb]=Skutt til sjøs +Name[nds]=Schööt in't Water +Name[ne]=पानीमा गोली +Name[nl]=Schot in het water +Name[nn]=Skot i sjøen +Name[pa]=ਪਾਣੀ ਉੱਤੇ ਗੋਲਾਬਾਰੀ +Name[pl]=PudÅ‚o +Name[pt]=Disparo na água +Name[pt_BR]=Tiro na água +Name[ro]=A tras în apă +Name[ru]=Промах +Name[se]=Bávkkáhus mearas +Name[sk]=Zásah vody +Name[sl]=Ustreli proti vodi +Name[sr]=Пуцањ у воду +Name[sr@Latn]=Pucanj u vodu +Name[sv]=Skott i vattnet +Name[ta]=நீரில௠எரி +Name[tg]=Тирпарронӣ дар Об +Name[tr]=Yara aldı +Name[uk]=ПоÑтріл у воду +Name[wa]=Côp dins l' aiwe +Name[zh_CN]=å‘水中射击 +Name[zh_TW]=æ°´é¢å°„æ“Š +Comment=Someone has shot at the water +Comment[ar]=لقد رمى أحدهم على الماء +Comment[be]=ХтоÑьці зрабіў ÑÑ‚Ñ€Ñл у ваду +Comment[bg]=ПропуÑк (Ñтрелба във водата) +Comment[bn]=কোনো à¦à¦•à¦œà¦¨ পানিতে গোলা ছà§à§œà§‡à¦›à§‡ +Comment[bs]=Neko je pucao u vodu +Comment[ca]=Algú dispara a l'aigua +Comment[cs]=NÄ›kdo vystÅ™elil do vody +Comment[cy]=Mae rhywun wedi saethu i'r dŵr +Comment[da]=Nogen har skudt pÃ¥ vandet +Comment[de]=Da hat jemand ins Wasser geschossen +Comment[el]=Κάποιος πυÏοβόλησε στο νεÏÏŒ +Comment[eo]=Iu pafis al la akvo +Comment[es]=Alguien ha disparado al agua +Comment[et]=Keegi tulistas vette +Comment[eu]=Norbaitek urari tiro egin dio +Comment[fa]=شخصی در آب شلیک کرده است +Comment[fi]=Joku on ampunut veteen +Comment[fr]=Quelqu'un a tiré dans l'eau +Comment[gl]=Alguén disparou á auga +Comment[he]=מישהו ירה ×‘×ž×™× +Comment[hi]=किसी ने पानी पर गोली चलाई +Comment[hr]=Neko je pucao u vodu +Comment[hu]=Az egyik játékos lövése nem talált +Comment[is]=Einhver skaut í vatnið +Comment[it]=Qualcuno ha sparato nell'acqua +Comment[ja]=誰ã‹ãŒæ°´é¢ã‚’æ’ƒã£ãŸ +Comment[km]=មាន​មនុស្ស​បាន​បាញ់​ážáŸ’រូវ​ទឹក +Comment[lt]=Kažkas Å¡ovÄ— į vandenį +Comment[lv]=KÄds trÄpija Å«denÄ« +Comment[mk]=Ðекој Ñтрелаше во вода +Comment[nb]=Noen har skutt pÃ¥ vannet +Comment[nds]=Een hett in't Water schaten +Comment[ne]=केसैले पानीमा गोली हानेको छ +Comment[nl]=Iemand heeft in het water geschoten +Comment[nn]=Nokon har skote pÃ¥ vatnet +Comment[pa]=ਕਿਸੇ ਨੇ ਪਾਣੀ ਉੱਤੇ ਗੋਲਾਬਾਰੀ ਕੀਤੀ +Comment[pl]=KtoÅ› spudÅ‚owaÅ‚ +Comment[pt]=Alguém disparou para a água +Comment[pt_BR]=Alguém atirou na água +Comment[ro]=Cineva a tras în apă +Comment[ru]=Кто-то промазал +Comment[se]=Giinu lea bávkkihan bombba +Comment[sk]=Niekto trafil vodu +Comment[sl]=Nekdo je ustrelil v vodo +Comment[sr]=Ðеко је пуцао у воду +Comment[sr@Latn]=Neko je pucao u vodu +Comment[sv]=NÃ¥gon har skjutit i vattnet +Comment[ta]=யாரோ தணà¯à®£à¯€à®°à¯ˆ நோகà¯à®•à®¿ சà¯à®Ÿà¯à®Ÿà¯à®µà®¿à®Ÿà¯à®Ÿà®¾à®°à¯ +Comment[tg]=КаÑе дар об тир паронд +Comment[tr]=Birileri biri suya ateÅŸ etti +Comment[uk]=ХтоÑÑŒ попав у воду +Comment[zh_CN]=有人å‘水中射击 +Comment[zh_TW]=æŸäººå·²é€²è¡Œæ°´é¢å°„æ“Š +default_sound=ship-player-shoot-water.ogg +default_presentation=1 + +[shoot_hit_1] +Name=Player 1 shot +Name[ar]=رمية اللاعب 1 +Name[be]=СтрÑл першага гульнёўцы +Name[bg]=ИзÑтрел на играч 1 +Name[bn]=পà§à¦°à¦¥à¦® খেলোয়াড় গোলা ছà§à§œà§‡à¦›à§‡ +Name[bs]=Pucanj igraÄa 1 +Name[ca]=Tir del jugador 1 +Name[cs]=HrÃ¡Ä 1 vystÅ™elil +Name[cy]=Saethu Chwaraewr 1 +Name[da]=1. spillers skud +Name[de]=Schuss von Spieler 1 +Name[el]=Παίκτης 1 πυÏοβόλησε +Name[eo]=Ludanto 1 pafis +Name[es]=Disparo del jugador 1 +Name[et]=Mängija 1 lask +Name[eu]=1. jokalariak tiro egin du +Name[fa]=شلیک بازیکن Û± +Name[fi]=Pelaaja 1 ampui +Name[fr]=Le joueur 1 a tiré +Name[gl]=Disparo do xogador 1 +Name[he]=יריית שחקן 1 +Name[hi]=खिलाड़ी 1 ने गोली चलाई +Name[hr]=Puca igraÄ 1 +Name[hu]=Az 1. játékos egyik hajója találatot kapott +Name[is]=Leikmaður 1 skaut +Name[it]=Spara il giocatore n. 1 +Name[ja]=プレイヤー 1 発射 +Name[km]=អ្នក​លáŸáž„ ១ បាន​បាញ់ +Name[lt]=I ŽaidÄ—jo Å¡Å«vis +Name[lv]=PirmÄ spÄ“lÄ“tÄja Å¡Äviens +Name[mk]=ИÑтрел на играчот 1 +Name[nb]=Spiller 1 skutt +Name[nds]=Schööt vun Speler 1 +Name[ne]=खेलाडी १ गोली +Name[nl]=Schot van speler 1 +Name[nn]=Spelar 1 skote +Name[pa]=ਖਿਡਾਰੀ 1 ਗੋਲੀ ਚਲਾਈ +Name[pl]=Strzela Gracz 1 +Name[pt]=Jogador 1 disparou +Name[pt_BR]=Jogador 1 atira +Name[ro]=Jucătorul 1 a tras +Name[ru]=Ход первого игрока +Name[se]=Speallár 1 lea bávkkihan bombba +Name[sk]=HrÃ¡Ä 1 vystrelil +Name[sl]=Strel igralca 1 +Name[sr]=Пуца играч 1 +Name[sr@Latn]=Puca igraÄ 1 +Name[sv]=Skott frÃ¥n första spelaren +Name[ta]=விளையாடà¯à®ªà®µà®°à¯ 1 எரிதல௠+Name[tg]=Тирпарронии бозингари 1 +Name[tr]=Oyunu 1 atış +Name[uk]=ПоÑтріл першого Ð³Ñ€Ð°Ð²Ñ†Ñ +Name[zh_CN]=Player 1 射击 +Name[zh_TW]=玩家 1 å°„æ“Š +Comment=Player 1 takes a shot +Comment[ar]=اللاعب 1 ÙŠ رم +Comment[be]=Першы гульнёўца ÑтралÑе +Comment[bg]=ИзÑтрел на играч 1 +Comment[bn]=পà§à¦°à¦¥à¦® খেলোয়াড় গোলা ছà§à§œà¦›à§‡ +Comment[bs]=IgraÄ 1 je na potezu +Comment[ca]=El jugador 1 dispara +Comment[cs]=HrÃ¡Ä 1 vystÅ™elil +Comment[cy]=Chwaraewr 1 yn saethu +Comment[da]=Spiller 1 skyder +Comment[de]=Spieler 1 schießt +Comment[el]=Ο παίκτης 1 πυÏοβολεί +Comment[eo]=Ludanto 1 pafas +Comment[es]=El jugador 1 hace un disparo +Comment[et]=Mängija 1 tulistas +Comment[eu]=1. jokalariak tiro egin du +Comment[fa]=بازیکن ۱، یک گلوله می‌خورد +Comment[fi]=Pelaaja 1 ampuu +Comment[fr]=Le joueur 1 a subi un tir +Comment[gl]=O xogador 1 fai un disparo +Comment[he]=שחקן 1 יורה +Comment[hi]=खिलाड़ी 1 ने गोली चलाना लिया +Comment[hr]=IgraÄ 1 upravo puca +Comment[hu]=Az 1. játékos lÅ‘ +Comment[is]=Leikmaður 1 skýtur +Comment[it]=Il giocatore 1 prende un colpo +Comment[ja]=プレイヤー 1 ãŒç™ºå°„ +Comment[km]=អ្នក​លáŸáž„​១ បាន​បាញ់ +Comment[lt]=I ŽaidÄ—jas Å¡auna +Comment[lv]=Å auj pirmais spÄ“lÄ“tÄjs +Comment[mk]=Играчот 1 иÑтрела +Comment[nb]=Spiller 1 er truffet +Comment[nds]=Speler 1 schütt +Comment[ne]=खेलाडी १ ले गोली लियो +Comment[nl]=Speler 1 schiet raak +Comment[nn]=Spelar 1 er truffen +Comment[pl]=Gracz 1 zostaÅ‚ trafiony +Comment[pt]=Jogador 1 dispara +Comment[pt_BR]=Jogador 1 leva um tiro +Comment[ro]=Jucătorul 1 a primit o lovitură +Comment[ru]=Ход первого игрока +Comment[se]=Speallár 1 lea deaivvahallan +Comment[sk]=HrÃ¡Ä 1 zasiahnutý +Comment[sl]=Igralec 1 je dobil strel +Comment[sr]=Играч 1 управо пуца +Comment[sr@Latn]=IgraÄ 1 upravo puca +Comment[sv]=Första spelaren skjuter +Comment[ta]=விளையாடà¯à®Ÿà®¾à®³à®°à¯ 1 விளையாட தà¯à®µà®™à¯à®•à¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари 1 тир паронд +Comment[tr]=Oyuncu 1 atış attı +Comment[uk]=Перший гравець ÑтрілÑÑ” +Comment[zh_CN]=Player 1 射击 +Comment[zh_TW]=玩家 1 進行了一次射擊 +default_sound=ship-player1-shoot.ogg +default_presentation=1 + +[shoot_hit_2] +Name=Player 2 shot +Name[ar]=رمية اللاعب 2 +Name[be]=СтрÑл другога гульнёўцы +Name[bg]=ИзÑтрел на играч 2 +Name[bn]=দà§à¦¬à¦¿à¦¤à§€à§Ÿ খেলোয়াড় গোলা ছà§à§œà§‡à¦›à§‡ +Name[bs]=Pucanj igraÄa 2 +Name[ca]=Tir del jugador 2 +Name[cs]=HrÃ¡Ä 2 vystÅ™elil +Name[cy]=Saethu Chwaraewr 2 +Name[da]=2. spillers skud +Name[de]=Schuss von Spieler 2 +Name[el]=Παίκτης 2 πυÏοβόλησε +Name[eo]=Ludanto 2 pafis +Name[es]=Disparo del jugador 2 +Name[et]=Mängija 2 lask +Name[eu]=2. jokalariak tiro egin du +Name[fa]=شلیک بازیکن Û² +Name[fi]=Pelaaja 2 ampui +Name[fr]=Le joueur 2 a tiré +Name[gl]=Disparo do xogador 2 +Name[he]=יריית שחקן 2 +Name[hi]=खिलाड़ी 2 ने गोली चलाई +Name[hr]=Puca igraÄ 2 +Name[hu]=A 2. játékos egyik hajója találatot kapott +Name[is]=Leikmaður 2 skaut +Name[it]=Spara il giocatore n. 2 +Name[ja]=プレイヤー 2 発射 +Name[km]=អ្នក​លáŸáž„ ២ បាន​បាញ់ +Name[lt]=II ŽaidÄ—jo Å¡Å«vis +Name[lv]=OtrÄ spÄ“lÄ“tÄja Å¡Äviens +Name[mk]=ИÑтрел на играчот 2 +Name[nb]=Spiller 2 skutt +Name[nds]=Schööt vun Speler 2 +Name[ne]=खेलाडी २ गोली +Name[nl]=Schot van speler 2 +Name[nn]=Spelar 2 skote +Name[pa]=ਖਿਡਾਰੀ 2 ਨੇ ਗੋਲੀ ਚਲਾਈ +Name[pl]=Strzela Gracz 2 +Name[pt]=Jogador 2 disparou +Name[pt_BR]=Jogador 2 atira +Name[ro]=Jucătorul 2 a tras +Name[ru]=Ход второго игрока +Name[se]=Speallár 2 lea deaivvahallan +Name[sk]=HrÃ¡Ä 2 vystrelil +Name[sl]=Strel igralca 2 +Name[sr]=Пуца играч 2 +Name[sr@Latn]=Puca igraÄ 2 +Name[sv]=Skott frÃ¥n andra spelaren +Name[ta]=விளையாடà¯à®ªà®µà®°à¯ 2 எரிதல௠+Name[tg]=Тирпарронии бозингари 2 +Name[tr]=Oyuncu 2 atış +Name[uk]=ПоÑтріл другого Ð³Ñ€Ð°Ð²Ñ†Ñ +Name[zh_CN]=Player 2 射击 +Name[zh_TW]=玩家 2 å°„æ“Š +Comment=Player 2 takes a shot +Comment[ar]=اللاعب 2 يرمى +Comment[be]=Другі гульнёўца ÑтарлÑе +Comment[bg]=ИзÑтрел на играч 2 +Comment[bn]=দà§à¦¬à¦¿à¦¤à§€à§Ÿ খেলোয়াড় গোলা ছà§à§œà¦›à§‡ +Comment[bs]=IgraÄ 2 je na potezu +Comment[ca]=El jugador 2 dispara +Comment[cs]=HrÃ¡Ä 2 vystÅ™elil +Comment[cy]=Chwaraewr 2 yn saethu +Comment[da]=Spiller 2 skyder +Comment[de]=Spieler 2 schießt +Comment[el]=Ο παίκτης 2 πυÏοβολεί +Comment[eo]=Ludanto 2 pafas +Comment[es]=El jugador 2 hace un disparo +Comment[et]=Mängija 2 tulistas +Comment[eu]=2. jokalariak tiro egin du +Comment[fa]=بازیکن ۲، یک گلوله می‌خورد +Comment[fi]=Pelaaja 2 ampuu +Comment[fr]=Le joueur 2 a subi un tir +Comment[gl]=O xogador 2 fai un disparo +Comment[he]=שחקן 2 יורה +Comment[hi]=खिलाड़ी 2 ने गोली चलाना लिया +Comment[hr]=IgraÄ 2 upravo puca +Comment[hu]=A 2. játékos lÅ‘ +Comment[is]=Leikmaður 2 skýtur +Comment[it]=Il giocatore 2 prende un colpo +Comment[ja]=プレイヤー 2 ãŒç™ºå°„ +Comment[km]=អ្នក​លáŸáž„ ២ បាន​បាញ់ +Comment[lt]=II ŽaidÄ—jas Å¡auna +Comment[lv]=Å auj otrais spÄ“lÄ“tÄjs +Comment[mk]=Играчот 2 иÑтрела +Comment[nb]=Spiller 2 er truffet +Comment[nds]=Speler 2 schütt +Comment[ne]=खेलाडी २ ले गोली लियो +Comment[nl]=Speler 2 schiet raak +Comment[nn]=Spelar 2 er truffen +Comment[pl]=Gracz 2 zostaÅ‚ trafiony +Comment[pt]=Jogador 2 dispara +Comment[pt_BR]=Jogador 2 leva um tiro +Comment[ro]=Jucătorul 2 a primit o lovitură +Comment[ru]=Ход второго игрока +Comment[se]=Speallár 2 lea deaivvahallan +Comment[sk]=HrÃ¡Ä 2 zasiahnutý +Comment[sl]=Igralec 2 je dobil strel +Comment[sr]=Играч 2 управо пуца +Comment[sr@Latn]=IgraÄ 2 upravo puca +Comment[sv]=Andra spelaren skjuter +Comment[ta]=விளையாடà¯à®Ÿà®¾à®³à®°à¯ 2 விளையாட தà¯à®µà®™à¯à®•à¯à®•à®¿à®±à®¾à®°à¯ +Comment[tg]=Бозингари 2 тир паронд +Comment[tr]=Oyuncu 2 atış attı +Comment[uk]=Другий гравець ÑтрілÑÑ” +Comment[zh_CN]=Player 2 射击 +Comment[zh_TW]=玩家 2 進行了一次射擊 +default_sound=ship-player2-shoot.ogg +default_presentation=1 + +[shoot_sink] +Name=Sunk ship +Name[ar]=سÙينة مغرقة +Name[be]=Карабель пайшоў на дно +Name[bg]=Потопен е кораб +Name[bn]=ডà§à¦¬à§‡ যাওয়া জাহাজ +Name[br]=Lestr kaset d'ar strad +Name[bs]=Potopljen brod +Name[ca]=Vaixell enfonsat +Name[cs]=LoÄ potopena +Name[cy]=Suddo llong +Name[da]=Sunket skib +Name[de]=Schiff versenkt +Name[el]=Βουλιαγμένο πλοίο +Name[eo]=Sinkita Åipo +Name[es]=Barco hundido +Name[et]=Laev uputatud +Name[eu]=Urperatutako ontzia +Name[fa]=کشتی غرق‌‌‌شده +Name[fi]=Laiva upposi +Name[fr]=Bateau coulé +Name[ga]=Long curtha chun tóin farraige +Name[gl]=Barco afundido +Name[he]=ספינה טבועה +Name[hi]=जहाज डà¥à¤¬à¤¾à¤à¤ +Name[hr]=Brod je potopljen +Name[hu]=Elsüllyedt egy hajó +Name[is]=Sökkti skipi +Name[it]=Nave affondata +Name[ja]=沈んã èˆ¹ +Name[km]=នាវា​លិច +Name[lt]=Nuskendes laivas +Name[lv]=Nogrimis kuÄ£is +Name[mk]=Потопи брод +Name[nb]=Senket skip +Name[nds]=Schipp afbuddelt +Name[ne]=पानीजहाज डà¥à¤¬à¥à¤¯à¥‹ +Name[nl]=Gezonken schip +Name[nn]=Senka skip +Name[pa]=ਡà©à©±à¨¬à¨¦à¨¾ ਜਹਾਜ਼ +Name[pl]=Zatopiony okrÄ™t +Name[pt]=Navio afundado +Name[pt_BR]=Navio afundado +Name[ro]=Nava se scunfundă +Name[ru]=Корабль потоплен +Name[se]=Skiipa vuojui +Name[sk]=Potopenie lode +Name[sl]=Potopljena ladja +Name[sr]=Брод је потопљен +Name[sr@Latn]=Brod je potopljen +Name[sv]=Sänkt skepp +Name[ta]=மூழà¯à®•à¯à®®à¯ கபà¯à®ªà®²à¯ +Name[tg]=Киштии ғарқшуда +Name[tr]=Batık gemi +Name[uk]=Потопив корабель +Name[wa]=Batea coulé +Name[zh_CN]=击沉军舰 +Name[zh_TW]= 沉船 +Comment=A ship has been sunk +Comment[ar]=لقد أغرقت سÙينة +Comment[be]=Карабель пайшоў на дно +Comment[bg]=Потопен е кораб +Comment[bn]=à¦à¦•à¦Ÿà¦¿ জাহাজ ডà§à¦¬à§‡ গিয়েছে +Comment[br]=Kaset d'ar strad eo ul lestr +Comment[bs]=Brod je potopljen +Comment[ca]=S'ha enfonsat un vaixell +Comment[cs]=LoÄ byla potopena +Comment[cy]=Mae llong wedi ei suddo +Comment[da]=Et skib er blevet sænket +Comment[de]=Ein Schiff wurde versenkt +Comment[el]=Ένα πλοίο βυθίστηκε +Comment[es]=Se ha hundido un barco +Comment[et]=Laev uputati +Comment[eu]=Ontzi bat urperatu dute +Comment[fa]=یک کشتی غرق شده است +Comment[fi]=Laiva on upotettu +Comment[fr]=Un bateau a été coulé +Comment[gl]=Afundiuse un barco +Comment[he]=ספינה הוטבעה +Comment[hi]=à¤à¤• जहाज डूबा +Comment[hr]=Brod je upravo potopljen +Comment[hu]=Elsüllyedt egy hajó +Comment[is]=Skipi hefur verið sökkt +Comment[it]=Una nave è stata affondata +Comment[ja]=船ã¯æ²ˆæ²¡ã—ã¾ã—㟠+Comment[km]=នាវា​ážáŸ’រូវ​បាន​លិច +Comment[lt]=Laivas nuskandintas +Comment[lv]=Kugis ir nogrimis +Comment[mk]=Потопен е брод +Comment[nb]=Et skip er senket +Comment[nds]=En Schipp is afbuddelt +Comment[ne]=पानीजहाज डà¥à¤¬à¥‡à¤•à¥‹ छ +Comment[nl]=Er is een schip gezonken +Comment[nn]=Eit skip er senka +Comment[pa]=ਇੱਕ ਜਹਾਜ਼ ਡà©à©±à¨¬ ਗਿਆ +Comment[pl]=OkrÄ™t zostaÅ‚ zatopiony +Comment[pt]=Foi afundado um navio +Comment[pt_BR]=Um navio acabou de afundar +Comment[ro]=O navă s-a scufundat +Comment[ru]=Корабль потоплен +Comment[se]=Skiipa vuojui +Comment[sk]=Potopenie lode +Comment[sl]=Ladja je bila potopljena +Comment[sr]=Брод је управо потопљен +Comment[sr@Latn]=Brod je upravo potopljen +Comment[sv]=Ett skepp har sänkts +Comment[ta]=கபà¯à®ªà®²à¯ மூழà¯à®•à®¿à®µà®¿à®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Киштӣ ғарқ гардид +Comment[tr]=Bir gemi batırıldı +Comment[uk]=Корабель було потоплено +Comment[uz]=Kema choÊ»ktirildi +Comment[uz@cyrillic]=Кема чўктирилди +Comment[wa]=On batea a stî coulé +Comment[zh_CN]=击沉了一艘军舰 +Comment[zh_TW]= 有一艘傳沉沒了 +default_sound=ship-sink.ogg +default_presentation=1 diff --git a/kbattleship/kbattleship/kbaiplayer.cpp b/kbattleship/kbattleship/kbaiplayer.cpp new file mode 100644 index 00000000..ca95c2da --- /dev/null +++ b/kbattleship/kbattleship/kbaiplayer.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + kbaiplayer.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include "kbchooserstrategy.h" + +#include "kbaiplayer.moc" + +#define MAX_SHIP_LEN 4 + +KBAIPlayer::KBAIPlayer() +{ + m_ownShipList = 0; + m_battleField = 0; + m_masterStrategy = 0; + m_randomSeq = new KRandomSequence(KApplication::random()); +} + +KBAIPlayer::~KBAIPlayer() +{ + delete m_masterStrategy; + delete m_randomSeq; +} + +void KBAIPlayer::init(KBattleField *battle_field, KShipList *ai_shiplist) +{ + m_battleField = battle_field; + m_ownShipList = ai_shiplist; + + if(m_battleField != 0) + { + QRect rect = m_battleField->enemyRect(); + int grid = m_battleField->gridSize(); + m_fieldRect = QRect(0, 0, (rect.width() / grid), (rect.height() / grid)); + } +} + +void KBAIPlayer::slotRestart() +{ + if(m_randomSeq == 0 || m_ownShipList == 0 || m_battleField == 0) + return; + + addShips(); + chooseStrategy(); + emit sigReady(); +} + +void KBAIPlayer::addShips() +{ + m_ownShipList->clear(); + + for(int shiplen = MAX_SHIP_LEN; shiplen >= 1; shiplen--) + { + int x, y; + bool vertical; + + do + { + x = (int) m_randomSeq->getLong(m_fieldRect.width()); + y = (int) m_randomSeq->getLong(m_fieldRect.height()); + vertical = m_randomSeq->getBool(); + } + while(!shipPlaced(shiplen, x, y, vertical)); + } +} + +void KBAIPlayer::chooseStrategy() +{ + delete m_masterStrategy; + + m_masterStrategy = new KBChooserStrategy(); + m_masterStrategy->init(m_battleField, m_fieldRect); +} + +bool KBAIPlayer::slotRequestShot() +{ + if(m_masterStrategy != 0 && m_masterStrategy->hasMoreShots()) + { + QPoint pos = m_masterStrategy->nextShot(); + emit sigShootAt(pos); + m_masterStrategy->shotAt(pos); + return true; + } + return false; +} + +bool KBAIPlayer::shipPlaced(int shiplen, int x, int y, bool vertical) +{ + QRect ship = vertical ? QRect(x, y, 1, shiplen) : QRect(x, y, shiplen, 1); + return m_ownShipList->addNewShip(vertical, ship.x(), ship.y()); +} diff --git a/kbattleship/kbattleship/kbaiplayer.h b/kbattleship/kbattleship/kbaiplayer.h new file mode 100644 index 00000000..aebb149d --- /dev/null +++ b/kbattleship/kbattleship/kbaiplayer.h @@ -0,0 +1,59 @@ +/*************************************************************************** + kbaiplayer.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBAIPLAYER_H +#define KBAIPLAYER_H + +#include + +#include + +#include "kbstrategy.h" +#include "kbattlefield.h" +#include "kshiplist.h" + +class KBAIPlayer : public QObject +{ + Q_OBJECT +public: + KBAIPlayer(); + ~KBAIPlayer(); + + void init(KBattleField *battle_field, KShipList *ai_shiplist); + +public slots: + void slotRestart(); + bool slotRequestShot(); + bool shipPlaced(int shiplen, int x, int y, bool vertical); + +signals: + void sigShootAt(const QPoint pos); + void sigReady(); + +private: + void chooseStrategy(); + void addShips(); + + KBStrategy *m_masterStrategy; + KShipList *m_ownShipList; + KBattleField *m_battleField; + KRandomSequence *m_randomSeq; + + QRect m_fieldRect; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattlefield.cpp b/kbattleship/kbattleship/kbattlefield.cpp new file mode 100644 index 00000000..0467ae28 --- /dev/null +++ b/kbattleship/kbattleship/kbattlefield.cpp @@ -0,0 +1,233 @@ +/*************************************************************************** + kbattlefield.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbattleship.h" +#include "kship.h" + +#include "kbattlefield.h" + +KBattleField::KBattleField(QWidget *parent, bool grid) : KGridWidget(parent, grid) +{ + m_parent = parent; + m_width = parent->width(); + m_canDraw = true; + + m_ownfieldx = 10; + m_ownfieldy = 10; + m_enemyfieldx = 10; + m_enemyfieldy = 10; + + clearOwnField(); + clearEnemyField(); + clearPreviewField(); + drawField(); +} + +void KBattleField::clearOwnField() +{ + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + m_ownfield[i][j] = KBattleField::FREE; + } + } +} + +void KBattleField::clearEnemyField() +{ + for(int i = 0; i != m_enemyfieldx; i++) + { + for(int j = 0; j != m_enemyfieldy; j++) + { + m_enemyfield[i][j] = KBattleField::FREE; + } + } +} + +void KBattleField::clearPreviewField() +{ + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + m_newfield[i][j] = KBattleField::FREE; + m_newdata[i][j] = false; + } + } +} + +void KBattleField::setPreviewState(int fieldx, int fieldy, int type, bool rotate) +{ + m_newfield[fieldx][fieldy] = type; + m_newdata[fieldx][fieldy] = true; + m_rotatedata[fieldx][fieldy] = rotate; +} + +void KBattleField::drawField() +{ + drawOwnField(); + drawEnemyField(); + clearPreviewField(); + finished(); +} + +void KBattleField::drawOwnField() +{ + if(!m_canDraw) + return; + + KBattleshipWindow *window = static_cast(m_parent->parent()->parent()); + KShip *ship = 0; + int data; + + for(int i = 0; i != m_ownfieldx; i++) + { + for(int j = 0; j != m_ownfieldy; j++) + { + setValues(((i * gridSize()) + ownXPosition()), ((j * gridSize()) + ownYPosition()), gridSize()); + if(!m_newdata[i][j]) + data = m_ownfield[i][j]; + else + data = m_newfield[i][j]; + switch(data) + { + case KBattleField::FREE: + drawSquare(); + break; + + case KBattleField::WATER: + drawSquare(); + drawWaterIcon(); + break; + + case KBattleField::HIT: + drawSquare(); + ship = window->shipAt(i, j); + if(ship->placedLeft()) + drawShipIcon((ship->shiptype() + 1), (ship->shipxstop() - i + 1), true, true); + else + drawShipIcon((ship->shiptype() + 1), (j - ship->shipystart() + 1), false, true); + break; + + case KBattleField::DEATH: + drawSquare(); + drawDeathIcon(); + break; + + default: + ship = window->shipAt(i, j); + if(ship) + { + drawSquare(); + if(m_newdata[i][j]) + data = m_ownfield[i][j]; + drawShipIcon(data, ship->placedLeft()); + } + else if(!ship) + drawShipIcon(data, !m_rotatedata[i][j], false, true); + break; + } + } + } +} + +void KBattleField::drawEnemyField() +{ + if(!m_canDraw) + return; + + KBattleshipWindow *window = static_cast(m_parent->parent()->parent()); + + for(int i = 0; i != m_enemyfieldx; i++) + { + for(int j = 0; j != m_enemyfieldy; j++) + { + setValues(((i * gridSize()) + enemyXPosition()), ((j * gridSize()) + enemyYPosition()), gridSize()); + switch(m_enemyfield[i][j]) + { + case KBattleField::FREE: + drawSquare(); + break; + + case KBattleField::WATER: + drawSquare(); + drawWaterIcon(); + break; + + case KBattleField::HIT: + drawSquare(); + drawHitIcon(); + break; + + case KBattleField::BORDER: + drawSquare(); + drawDeathBorder(); + break; + + case KBattleField::DEATH: + drawSquare(); + drawDeathIcon(); + break; + + default: + drawSquare(); + KShip *ship = window->enemyShipAt(i, j); + if(ship->placedLeft()) + drawShipIcon(m_enemyfield[i][j], true); + else + drawShipIcon(m_enemyfield[i][j]); + break; + } + } + } +} + +int KBattleField::ownXPosition() +{ + return 10; +} + +int KBattleField::ownYPosition() +{ + return 10; +} + +int KBattleField::enemyXPosition() +{ + return (m_width / 2) + 10; +} + +int KBattleField::enemyYPosition() +{ + return 10; +} + +int KBattleField::rectX() +{ + return 10; +} + +QRect KBattleField::ownRect() +{ + return QRect(ownXPosition(), ownYPosition(), m_ownfieldx * gridSize(), m_ownfieldy * gridSize()); +} + +QRect KBattleField::enemyRect() +{ + return QRect(enemyXPosition(), enemyYPosition(), m_enemyfieldx * gridSize(), m_enemyfieldy * gridSize()); +} diff --git a/kbattleship/kbattleship/kbattlefield.h b/kbattleship/kbattleship/kbattlefield.h new file mode 100644 index 00000000..d1e62b3f --- /dev/null +++ b/kbattleship/kbattleship/kbattlefield.h @@ -0,0 +1,82 @@ +/*************************************************************************** + kbattlefield.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLEFIELD_H +#define KBATTLEFIELD_H + +#include +#include + +#include "kgridwidget.h" + +class KBattleField : public KGridWidget +{ +public: + enum{FREE, WATER, HIT, DEATH, BORDER, SHIP1P1, SHIP2P1, SHIP2P2, SHIP3P1, SHIP3P2, SHIP3P3, SHIP4P1, SHIP4P2, SHIP4P3, SHIP4P4}; + KBattleField(QWidget *parent, bool grid); + + void clearOwnField(); + void clearEnemyField(); + void clearPreviewField(); + + void drawField(); + void setDrawField(bool draw) { m_canDraw = draw; } + + void setOwnState(int fieldx, int fieldy, int type) { m_ownfield[fieldx][fieldy] = type; } + int ownState(int fieldx, int fieldy) { return m_ownfield[fieldx][fieldy]; } + + void setEnemyState(int fieldx, int fieldy, int type) { m_enemyfield[fieldx][fieldy] = type; } + int enemyState(int fieldx, int fieldy) { return m_enemyfield[fieldx][fieldy]; } + + void setPreviewState(int fieldx, int fieldy, int type, bool rotate); + + QRect ownRect(); + QRect enemyRect(); + + int gridSize() { return 32; } + +private: + void drawOwnField(); + void drawEnemyField(); + + int ownXPosition(); + int ownYPosition(); + int enemyXPosition(); + int enemyYPosition(); + + int rectX(); + + int m_ownfield[15][15]; + int m_enemyfield[15][15]; + + int m_newfield[15][15]; + bool m_newdata[15][15]; + bool m_rotatedata[15][15]; + + int m_ownfieldx; + int m_ownfieldy; + int m_enemyfieldx; + int m_enemyfieldy; + + int m_width; + + bool m_canDraw; + + QWidget *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattleship.cpp b/kbattleship/kbattleship/kbattleship.cpp new file mode 100644 index 00000000..b7d38891 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.cpp @@ -0,0 +1,1352 @@ +/*************************************************************************** + kbattleship.cpp + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include "kbattleship.moc" + +extern const char *clientVersion; + +KBattleshipWindow::KBattleshipWindow() : KMainWindow() +{ + shift = false; + m_connection = 0; + m_lost = 0; + m_config = 0; + m_client = 0; + m_server = 0; + m_aiPlayer = 0; + m_aiHits = 0; + + init(); +} + +KBattleshipWindow::~KBattleshipWindow() +{ + if(m_config != 0) + saveOptions(); + delete m_aiPlayer; + delete m_ownshiplist; + delete m_enemyshiplist; +} + +void KBattleshipWindow::init() +{ + m_aiPlaying = false; + m_placeable = false; + m_shootable = false; + m_serverHasClient = false; + m_config = kapp->config(); + initStatusBar(); + initActions(); + readOptions(); + initView(); + initChat(); + initShipPlacing(); + parseCommandLine(); +} + +void KBattleshipWindow::slotConfigureNotifications() +{ + KNotifyDialog::configure(this); +} + +void KBattleshipWindow::initStatusBar() +{ + m_ownNickname = "-"; + m_enemyNickname = "-"; + statusBar()->insertItem(i18n(" Player 1: %1 ").arg(m_ownNickname), ID_PLAYER_OWN, 0, true); + statusBar()->insertItem(i18n(" Player 2: %1 ").arg(m_enemyNickname), ID_PLAYER_ENEMY, 0, true); + statusBar()->insertItem(i18n("Ready"), ID_STATUS_MSG, 1); + statusBar()->setItemAlignment(ID_STATUS_MSG, AlignLeft); +} + +void KBattleshipWindow::initActions() +{ + KStdAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection()); + m_gameServerConnect = new KAction(i18n("&Connect to Server..."), "connect_no", Key_F2, this, SLOT(slotServerConnect()), actionCollection(), "game_serverconnect"); + m_gameNewServer = new KAction(i18n("&Start Server..."), "network", Key_F3, this, SLOT(slotNewServer()), actionCollection(), "game_newserver"); + m_gameSingle = new KAction(i18n("S&ingle Player..."), "gear", Key_F4, this, SLOT(slotSinglePlayer()), actionCollection(), "game_singleplayer"); + m_gameQuit = KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::highscores(this, SLOT(slotHighscore()), actionCollection()); + m_gameEnemyInfo = new KAction(i18n("&Enemy Info"), "view_text", Key_F11, this, SLOT(slotEnemyClientInfo()), actionCollection(), "game_enemyinfo"); + + m_configSound = new KToggleAction(i18n("&Play Sounds"), 0, actionCollection(), "options_configure_sound"); + m_configGrid = new KToggleAction(i18n("&Show Grid"), 0, this, SLOT(slotShowGrid()), actionCollection(), "options_show_grid"); + m_configGrid->setCheckedState(i18n("Hide Grid")); + + m_gameEnemyInfo->setEnabled(false); + + setupGUI( KMainWindow::Save | StatusBar | Keys | Create ); +} + +void KBattleshipWindow::initChat() +{ + connect(m_chat, SIGNAL(sigSendMessage(const QString &)), this, SLOT(slotSendChatMessage(const QString &))); + connect(m_chat, SIGNAL(sigChangeEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_chat, SIGNAL(sigChangeOwnNickname(const QString &)), this, SLOT(slotChangedNickCommand(const QString &))); +} + +void KBattleshipWindow::changeShipPlacementDirection(){ + shift = !shift; +} + +void KBattleshipWindow::initShipPlacing() +{ + connect(m_ownshiplist, SIGNAL(sigOwnFieldDataChanged(int, int, int)), this, SLOT(slotChangeOwnFieldData(int, int, int))); + connect(m_ownshiplist, SIGNAL(sigLastShipAdded()), this, SLOT(slotShipsReady())); +} + +void KBattleshipWindow::initView() +{ + QWidget *dummy = new QWidget(this, "dummy"); + setCentralWidget(dummy); + + QGridLayout *topLayout = new QGridLayout(dummy, 2, 2, 0, -1, "topLayout"); + + m_chat = new KChatWidget(dummy); + m_view = new KBattleshipView(dummy, "", m_configGrid->isChecked()); + m_stat = new KStatDialog(dummy); + topLayout->setColStretch(1, 10); + topLayout->setRowStretch(1, 10); + topLayout->addWidget(m_view, 0, 0); + topLayout->addWidget(m_stat, 0, 1); + topLayout->addMultiCellWidget(m_chat, 1, 1, 0, 1); + + m_ownshiplist = new KShipList(); + m_enemyshiplist = new KShipList(); + + m_view->startDrawing(); + setFocusProxy(m_view); + + connect(m_view, SIGNAL(sigEnemyFieldClicked(int, int)), this, SLOT(slotEnemyFieldClick(int, int))); + connect(m_view, SIGNAL(sigOwnFieldClicked(int, int)), this, SLOT(slotPlaceShip(int, int))); + connect(m_view, SIGNAL(sigMouseOverField(int, int)), this, SLOT(slotPlaceShipPreview(int, int))); + connect(m_view, SIGNAL(changeShipPlacementDirection()), this, SLOT(changeShipPlacementDirection())); +} + +void KBattleshipWindow::slotDeleteAI() +{ + m_aiHits = 0; + delete m_aiPlayer; + m_aiPlayer = 0; +} + +void KBattleshipWindow::slotRestartAI() +{ + m_aiHits = 0; + slotStartBattleshipGame(false); +} + +void KBattleshipWindow::slotEnemyFieldClick(int fieldx, int fieldy) +{ + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(!m_serverHasClient && m_connection != 0) + return; + + if(!m_shootable) + return; + + if(m_view->enemyFieldState(fieldx, fieldy) == KBattleField::FREE) + { + if(!m_aiPlaying && !m_lost) + { + slotStatusMsg(i18n("Sending Message...")); + KMessage *msg = new KMessage(KMessage::SHOOT); + msg->addField("fieldx", QString::number(fieldx)); + msg->addField("fieldy", QString::number(fieldy)); + slotSendMessage(msg); + } + + if(m_stat->hit() != 10 && m_aiPlaying) + { + m_stat->setShot(); + + int showstate; + + if(m_enemyshiplist->shipTypeAt(fieldx, fieldy) == 99) + { + m_stat->setWater(); + showstate = KBattleField::WATER; + } + else + { + m_stat->setHit(); + showstate = KBattleField::HIT; + } + + slotChangeEnemyFieldData(fieldx, fieldy, showstate); + + if(showstate == KBattleField::HIT) + { + KShip *ship = m_enemyshiplist->shipAt(fieldx, fieldy); + typedef QValueList DeathValueList; + DeathValueList deathList; + bool xokay = true, yokay = true; + int tempy = 0, tempx = 0; + + if(ship->placedLeft()) + { + for(tempx = ship->shipxstart(); tempx <= ship->shipxstop(); tempx++) + { + if(m_view->enemyFieldState(tempx, fieldy) == KBattleField::HIT) + { + deathList.append(tempx); + xokay = true; + yokay = false; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + else + { + for(tempy = ship->shipystart(); tempy <= ship->shipystop(); tempy++) + { + if(m_view->enemyFieldState(fieldx, tempy) == KBattleField::HIT) + { + deathList.append(tempy); + xokay = false; + yokay = true; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + + if(xokay) + { + DeathValueList::Iterator it; + for(it = deathList.begin(); it != deathList.end(); ++it) + { + if(fieldy+1 < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(*it, fieldy+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(*it, fieldy, KBattleField::DEATH); + if(fieldy > 0) m_view->changeEnemyFieldData(*it, fieldy-1, KBattleField::BORDER); + } + if(ship->shipxstart() > 0) + { + if (fieldy > 0) m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(ship->shipxstart()-1, fieldy+1, KBattleField::BORDER); + } + if(ship->shipxstop() < m_enemyshiplist->m_fieldx) + { + if (fieldy>0) m_view->changeEnemyFieldData(ship->shipxstop()+1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(ship->shipxstop()+1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy)m_view->changeEnemyFieldData(ship->shipxstop()+1,fieldy+1, KBattleField::BORDER); + } + } + else if(yokay) + { + DeathValueList::Iterator it; + for(it = deathList.begin(); it != deathList.end(); ++it) + { + if (fieldx>0) m_view->changeEnemyFieldData(fieldx-1, *it, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, *it, KBattleField::DEATH); + if(fieldxm_fieldx) m_view->changeEnemyFieldData(fieldx+1, *it, KBattleField::BORDER); + } + if(ship->shipystart()>0) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ship->shipystart()-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ship->shipystart()-1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ship->shipystart()-1, KBattleField::BORDER); + } + if(ship->shipystop()m_fieldy) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ship->shipystop()+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ship->shipystop()+1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ship->shipystop()+1, KBattleField::BORDER); + } + } + } + } + + if(m_stat->hit() == 10 && m_aiPlaying) + { + m_aiPlaying = false; + m_shootable = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("You won the game :)")); + m_stat->slotAddOwnWon(); + slotUpdateHighscore(); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"),QString::null,i18n("Restart"),i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + QTimer::singleShot(0, this, SLOT(slotRestartAI())); + break; + + case KMessageBox::No: + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + break; + } + return; + } + else if(m_aiPlayer != 0 && m_aiPlaying) + m_aiPlayer->slotRequestShot(); + } + } +} + +void KBattleshipWindow::slotReceivedEnemyFieldData(int fieldx, int fieldy, int enemystate, int xstart, int xstop, int ystart, int ystop, bool death) +{ + m_stat->setShot(); + + int showstate; + + if(enemystate == 99) + { + m_stat->setWater(); + showstate = KBattleField::WATER; + } + else + { + m_stat->setHit(); + showstate = KBattleField::HIT; + } + + slotChangeEnemyFieldData(fieldx, fieldy, showstate); + + if(death) + { + if(xstart == xstop) + { + for(int i = ystart; i <= ystop; i++) + { + if (fieldx>0) m_view->changeEnemyFieldData(fieldx-1, i, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, i, KBattleField::DEATH); + if(fieldxm_fieldx) m_view->changeEnemyFieldData(fieldx+1, i, KBattleField::BORDER); + } + if(ystart>0) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ystart-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ystart-1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ystart-1, KBattleField::BORDER); + } + if(ystopm_fieldy) + { + if (fieldx>0)m_view->changeEnemyFieldData(fieldx-1, ystop+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(fieldx, ystop+1, KBattleField::BORDER); + if (fieldxm_fieldx)m_view->changeEnemyFieldData(fieldx+1, ystop+1, KBattleField::BORDER); + } + } + else if(ystart == ystop) + { + for(int i = xstart; i <= xstop; i++) + { + if(fieldy+1 < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(i, fieldy+1, KBattleField::BORDER); + m_view->changeEnemyFieldData(i, fieldy, KBattleField::DEATH); + if(fieldy > 0) m_view->changeEnemyFieldData(i, fieldy-1, KBattleField::BORDER); + } + if(xstart > 0) + { + if (fieldy > 0) m_view->changeEnemyFieldData(xstart-1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(xstart-1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy) m_view->changeEnemyFieldData(xstart-1, fieldy+1, KBattleField::BORDER); + } + if(xstop < m_enemyshiplist->m_fieldx) + { + if (fieldy>0) m_view->changeEnemyFieldData(xstop+1, fieldy-1, KBattleField::BORDER); + m_view->changeEnemyFieldData(xstop+1, fieldy, KBattleField::BORDER); + if (fieldy < m_enemyshiplist->m_fieldy)m_view->changeEnemyFieldData(xstop+1,fieldy+1, KBattleField::BORDER); + } + } + } + + if(m_stat->hit() != 10) + slotStatusMsg(i18n("Waiting for enemy to shoot..")); + else + { + KMessage *msg = m_view->getAliveShips(m_ownshiplist); // let's show ai player ships + slotSendMessage(msg); + slotStatusMsg(i18n("You won the game :)")); + m_stat->slotAddOwnWon(); + slotUpdateHighscore(); + if(m_connection->type() == KonnectionHandling::SERVER) + slotServerReplay(); + else + slotClientReplay(); + } +} + +void KBattleshipWindow::slotClientLost() +{ + slotAbortNetworkGame(); + slotStatusMsg(i18n("Enemy disconnected.")); +} + +void KBattleshipWindow::slotServerLost() +{ + slotAbortNetworkGame(); + slotStatusMsg(i18n("Enemy disconnected.")); +} + +void KBattleshipWindow::slotAbortNetworkGame() +{ + slotStatusMsg(i18n("Ready")); + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + + m_gameServerConnect->setText(i18n("&Connect to server")); + m_gameNewServer->setText(i18n("&Start server")); + m_gameSingle->setText(i18n("S&ingle game")); + m_gameServerConnect->setEnabled(true); + m_gameNewServer->setEnabled(true); + m_gameSingle->setEnabled(true); + m_gameEnemyInfo->setEnabled(false); + + m_chat->clear(); + + m_aiPlaying = false; + m_shootable = false; + m_placeable = false; + m_serverHasClient = false; + + if (m_connection) + { + if(m_connection->type() == KonnectionHandling::SERVER) + { + delete m_kbserver; + m_kbserver = 0; + } + else + { + delete m_kbclient; + m_kbclient = 0; + } + delete m_connection; + m_connection = 0; + } +} + +void KBattleshipWindow::slotReplay() +{ + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_lost = false; + if(m_connection->type() == KonnectionHandling::SERVER) + m_placeable = true; + else + m_placeable = false; + m_stat->clear(); +} + +void KBattleshipWindow::slotPlaceShipPreview(int fieldx, int fieldy) +{ + int xadd = 0, yadd = 0; + + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(m_connection != 0 && !m_aiPlaying && !m_serverHasClient) + return; + + if((m_placeable && m_ownshiplist->canAddShips()) || m_aiPlaying) + { + switch(m_ownshiplist->shipCount()) + { + case 4: + for(int i = 0; i <= 3; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP4P1 + i, shift); + } + break; + + case 3: + for(int i = 0; i <= 2; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP3P1 + i, shift); + } + break; + + case 2: + for(int i = 0; i <= 1; i++) + { + if(!shift) + xadd = i; + else + yadd = i; + m_view->previewShip(fieldx + xadd, fieldy + yadd, KBattleField::SHIP2P1 + i, shift); + } + break; + + case 1: + m_view->previewShip(fieldx, fieldy, KBattleField::SHIP1P1, shift); + break; + } + + m_view->field()->drawField(); + } + } +} + +void KBattleshipWindow::slotPlaceShip(int fieldx, int fieldy) +{ + if(m_connection != 0 || m_aiPlaying) + { + if(!m_aiPlaying && m_connection == 0) + return; + + if(m_connection != 0 && !m_aiPlaying && !m_serverHasClient) + return; + + if(m_placeable && m_ownshiplist->canAddShips()) + m_ownshiplist->addNewShip(shift, fieldx, fieldy); + } +} + +void KBattleshipWindow::slotShipsReady() +{ + if(m_aiPlaying) + { + slotStatusMsg(i18n("Waiting for computer player to start the match...")); + m_placeable = false; + m_aiPlayer->slotRequestShot(); + return; + } + + KMessage *msg = new KMessage(KMessage::SHIPSREADY); + slotSendMessage(msg); + + if(m_connection->type() == KonnectionHandling::SERVER) + slotStatusMsg(i18n("Waiting for other player to place their ships...")); + else + slotStatusMsg(i18n("Waiting for other player to start the match...")); + + m_placeable = false; +} + +void KBattleshipWindow::slotSendMessage(int fieldx, int fieldy, int state) +{ + if(m_connection != 0) + { + KMessage *msg = new KMessage(KMessage::ANSWER_SHOOT); + msg->addField(QString("fieldx"), QString::number(fieldx)); + msg->addField(QString("fieldy"), QString::number(fieldy)); + msg->addField(QString("fieldstate"), QString::number(state)); + + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); + } +} + +void KBattleshipWindow::slotSendMessage(KMessage *msg) +{ + if(m_connection != 0) + { + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); + } +} + +void KBattleshipWindow::slotSendChatMessage(const QString &text) +{ + if(m_connection != 0 && m_serverHasClient) + { + KMessage *msg = new KMessage(KMessage::CHAT); + msg->chatMessage(m_ownNickname, text); + slotSendMessage(msg); + } +} + +void KBattleshipWindow::slotChangedNickCommand(const QString &text) +{ + m_ownNickname = text; + slotChangeOwnPlayer(m_ownNickname); + m_chat->setNickname(m_ownNickname); +} + +KShip *KBattleshipWindow::shipAt(int fieldx, int fieldy) +{ + return m_ownshiplist->shipAt(fieldx, fieldy); +} + +KShip *KBattleshipWindow::enemyShipAt(int fieldx, int fieldy) +{ + return m_enemyshiplist->shipAt(fieldx, fieldy); +} + +void KBattleshipWindow::slotUpdateHighscore() +{ + // Balancing factors + // a = shot-balance + // b = water-balance + double a = 3; + double b = 0.5; + double score = (a * m_stat->hit() - b * m_stat->water()) / (m_stat->shot() + m_stat->water()) * 1000; + if(score == 0) score = 1; + + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Custom1 | KScoreDialog::Custom2 | KScoreDialog::Custom3, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Shots"), "shots"); + scoreDialog->addField(KScoreDialog::Custom2, i18n("Hits"), "hits"); + scoreDialog->addField(KScoreDialog::Custom3, i18n("Water"), "water"); + + KScoreDialog::FieldInfo info; + info[KScoreDialog::Name] = m_ownNickname; + info[KScoreDialog::Custom1] = QString::number(m_stat->shot()); + info[KScoreDialog::Custom2] = QString::number(m_stat->hit()); + info[KScoreDialog::Custom3] = QString::number(m_stat->water()); + + scoreDialog->addScore((int)score, info, false, false); +} + +void KBattleshipWindow::saveOptions() +{ + m_config->setGroup("General"); + m_config->writeEntry("PlaySounds", m_configSound->isChecked()); + m_config->writeEntry("ShowGrid", m_configGrid->isChecked()); + m_config->sync(); +} + +void KBattleshipWindow::readOptions() +{ + m_config->setGroup("General"); + m_configSound->setChecked(m_config->readBoolEntry("PlaySounds", true)); + m_configGrid->setChecked(m_config->readBoolEntry("ShowGrid", false)); +} + +void KBattleshipWindow::slotHighscore() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Custom1 | KScoreDialog::Custom2 | KScoreDialog::Custom3, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Shots"), "shots"); + scoreDialog->addField(KScoreDialog::Custom2, i18n("Hits"), "hits"); + scoreDialog->addField(KScoreDialog::Custom3, i18n("Water"), "water"); + scoreDialog->show(); +} + +void KBattleshipWindow::slotEnemyClientInfo() +{ + KInfoDialog *m_info = new KInfoDialog(this); + + m_info->lbl_clientIdentfier->setText(m_enemyClient); + m_info->lbl_clientVersion->setText(m_enemyClientVersion); + m_info->lbl_ClientInformation->setText(m_enemyClientDescription); + m_info->lbl_ProtocolVersion->setText(m_enemyProtocolVersion); + + m_info->show(); +} + +void KBattleshipWindow::slotServerConnect() +{ + if(m_connection == 0) + { + if(m_client != 0) { + m_client->show(); + return; + } + + slotStatusMsg(i18n("Loading Connect-Server dialog...")); + + m_client = new KClientDialog(this); + connect(m_client, SIGNAL(sigConnectServer()), this, SLOT(slotConnectToBattleshipServer())); + connect(m_client, SIGNAL(sigCancelConnect()), this, SLOT(slotDeleteConnectDialog())); + m_client->show(); + + slotStatusMsg(i18n("Ready")); + } + else + slotAbortNetworkGame(); +} + +void KBattleshipWindow::slotDeleteConnectDialog() +{ + delete m_client; + m_client = 0; +} + +void KBattleshipWindow::slotReplayRequest() +{ + switch(KMessageBox::questionYesNo(this, i18n("The client is asking to restart the game. Do you accept?"),QString::null,i18n("Accept Restart"), i18n("Deny Restart"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the client could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + } + else slotAbortNetworkGame(); + break; + + case KMessageBox::No: + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::slotServerReplay() +{ + KMessage *msg = new KMessage(KMessage::REPLAY); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"), QString::null, i18n("Restart"), i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the client could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + slotSendMessage(msg); + } + else + { + delete msg; + slotAbortNetworkGame(); + } + break; + + case KMessageBox::No: + delete msg; + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::slotClientReplay() +{ + KMessage *msg = new KMessage(KMessage::REPLAY); + switch(KMessageBox::questionYesNo(this, i18n("Do you want to ask the server restarting the game?"), QString::null, i18n("Ask to Restart"), i18n("Do Not Ask"))) + { + case KMessageBox::Yes: + if (m_connection) + { // the server could have closed while the user was thinking if he wanted to replay + slotReplay(); + slotStatusMsg(i18n("Waiting for an answer...")); + slotSendMessage(msg); + } + else + { + delete msg; + slotAbortNetworkGame(); + } + break; + + case KMessageBox::No: + delete msg; + slotAbortNetworkGame(); + break; + } +} + +void KBattleshipWindow::cleanup(bool placechange) +{ + if(placechange) + m_placeable = false; + m_view->field()->setDrawField(false); + m_ownshiplist->clear(); + m_enemyshiplist->clear(); + m_view->clearField(); + m_view->field()->setDrawField(true); + m_view->field()->drawField(); +} + +void KBattleshipWindow::slotNewServer() +{ + if(m_connection == 0) + { + if(m_server != 0) + return; + + slotStatusMsg(i18n("Loading Start-Server dialog...")); + + m_server = new KServerDialog(this); + connect(m_server, SIGNAL(okClicked()), this, SLOT(slotStartBattleshipServer())); + connect(m_server, SIGNAL(cancelClicked()), this, SLOT(slotDeleteServerDialog())); + m_server->show(); + + slotStatusMsg(i18n("Ready")); + } + else + slotAbortNetworkGame(); +} + +void KBattleshipWindow::slotDeleteServerDialog() +{ + delete m_server; + m_server = 0; +} + +void KBattleshipWindow::slotSendVersion() +{ + KMessage *msg = new KMessage(KMessage::GETVERSION); + msg->versionMessage(); + slotSendMessage(msg); + + QTimer::singleShot(150, this, SLOT(slotSendGreet())); +} + +void KBattleshipWindow::slotSendGreet() +{ + m_serverHasClient = true; + m_chat->slotAcceptMsg(true); + + KMessage *msg = new KMessage(KMessage::GREET); + msg->addField(QString("nickname"), m_ownNickname); + slotSendMessage(msg); +} + +void KBattleshipWindow::slotStartBattleshipServer() +{ + m_gameNewServer->setText(i18n("&Stop server")); + m_gameServerConnect->setEnabled(false); + m_gameSingle->setEnabled(false); + slotStatusMsg(i18n("Waiting for a player...")); + m_kbserver = new KBattleshipServer((m_server->port()).toInt(),m_server->gamename()); + m_ownNickname = m_server->nickname(); + m_chat->setNickname(m_ownNickname); + slotChangeOwnPlayer(m_ownNickname); + delete m_server; + m_server = 0; + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_placeable = true; + m_stat->clear(); + m_stat->clearWon(); + if(m_connection == 0) + { + m_connection = new KonnectionHandling(this, m_kbserver); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + { + if(m_connection->type() == KonnectionHandling::CLIENT) + { + disconnect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + disconnect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + disconnect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + disconnect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + disconnect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + disconnect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + disconnect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + disconnect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + disconnect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + disconnect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + disconnect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + disconnect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + m_connection->updateInternal(m_kbserver); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + m_connection->updateInternal(m_kbserver); + } + m_kbserver->init(); +} + +void KBattleshipWindow::slotLost(KMessage *msg) +{ + m_stat->slotAddEnemyWon(); + if (!msg->field("ship0").isNull()) m_view->drawEnemyShipsHuman(msg, m_enemyshiplist); + m_lost = true; +} + +void KBattleshipWindow::slotSendEnemyFieldState(int fieldx, int fieldy) +{ + int data, showstate; + bool xokay = false, yokay = false, is_kill = false; + typedef QValueList DeathValueList; + DeathValueList deathList; + + data = m_ownshiplist->shipTypeAt(fieldx, fieldy); + if(data == 99) + showstate = KBattleField::WATER; + else + showstate = KBattleField::HIT; + + slotChangeOwnFieldData(fieldx, fieldy, showstate); + + KMessage *msg = new KMessage(KMessage::ANSWER_SHOOT); + + if(showstate == KBattleField::HIT) + { + if(m_ownshiplist->shipTypeAt(fieldx, fieldy) != 0 && m_ownshiplist->shipTypeAt(fieldx, fieldy) != 99) + { + KShip *ship = m_ownshiplist->shipAt(fieldx, fieldy); + int tempy = 0, tempx = 0; + + if(ship->shipystart() == ship->shipystop() && ship->shipxstart() != ship->shipxstop()) + { + for(tempx = ship->shipxstart(); tempx <= ship->shipxstop(); tempx++) + { + if(m_view->ownFieldState(tempx, fieldy) == KBattleField::HIT) + { + deathList.append(tempx); + xokay = true; + yokay = false; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + else if(ship->shipystart() != ship->shipystop() && ship->shipxstart() == ship->shipxstop()) + { + for(tempy = ship->shipystart(); tempy <= ship->shipystop(); tempy++) + { + if(m_view->ownFieldState(fieldx, tempy) == KBattleField::HIT) + { + deathList.append(tempy); + xokay = false; + yokay = true; + } + else + { + xokay = false; + yokay = false; + break; + } + } + } + } + else if(m_ownshiplist->shipTypeAt(fieldx, fieldy) == 0) + { + msg->addField(QString("xstart"), QString::number(fieldx)); + msg->addField(QString("xstop"), QString::number(fieldx)); + msg->addField(QString("ystart"), QString::number(fieldy)); + msg->addField(QString("ystop"), QString::number(fieldy)); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + } + + msg->addField(QString("fieldx"), QString::number(fieldx)); + msg->addField(QString("fieldy"), QString::number(fieldy)); + + if(xokay) + { + msg->addField(QString("xstart"), QString::number(deathList.first())); + msg->addField(QString("xstop"), QString::number(deathList.last())); + msg->addField(QString("ystart"), QString::number(fieldy)); + msg->addField(QString("ystop"), QString::number(fieldy)); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + else if(yokay) + { + msg->addField(QString("xstart"), QString::number(fieldx)); + msg->addField(QString("xstop"), QString::number(fieldx)); + msg->addField(QString("ystart"), QString::number(deathList.first())); + msg->addField(QString("ystop"), QString::number(deathList.last())); + msg->addField(QString("death"), QString("true")); + is_kill = true; + } + + if(is_kill) + // If sunk, reveal ship type + msg->addField(QString("fieldstate"), QString::number(data)); + else if(showstate == KBattleField::HIT) + // On non-fatal hit, keep ship type secret + msg->addField(QString("fieldstate"), QString::number(1)); + else /* showstate == KBattleField::WATER */ + // Miss + msg->addField(QString("fieldstate"), QString::number(99)); + + if(m_connection->type() == KonnectionHandling::SERVER) + m_kbserver->sendMessage(msg); + else + m_kbclient->sendMessage(msg); +} + +void KBattleshipWindow::slotChangeOwnFieldData(int fieldx, int fieldy, int type) +{ + m_view->changeOwnFieldData(fieldx, fieldy, type); + playSound(true, type); +} + +void KBattleshipWindow::playSound(bool enemy, int fieldstate) +{ + if (m_configSound->isChecked()) + { + switch(fieldstate) + { + case KBattleField::WATER: + KNotifyClient::event(winId(), "shoot_water"); + break; + + case KBattleField::HIT: + if(enemy) + KNotifyClient::event(winId(), "shoot_hit_1"); + else + KNotifyClient::event(winId(), "shoot_hit_2"); + break; + + case KBattleField::DEATH: + KNotifyClient::event(winId(), "shoot_sink"); + break; + } + } +} + +void KBattleshipWindow::slotChangeEnemyFieldData(int fieldx, int fieldy, int type) +{ + m_view->changeEnemyFieldData(fieldx, fieldy, type); + playSound(false, type); +} + +void KBattleshipWindow::parseCommandLine() { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if ( args->count() > 0 ) + { + KURL u( args->url(0)); + if(u.protocol().isEmpty()) + u.setProtocol("kbattleship"); + if( !u.isValid()) { + KMessageBox::sorry(this, + i18n("The URL passed to KDE Battleship '%1' is not a valid url") + .arg(args->arg(0))); + return; + } + if( u.protocol() != "kbattleship" ) { + KMessageBox::sorry(this, + i18n("The URL passed to KDE Battleship '%1' is not recognised " + "as a Battleship game.") + .arg(args->arg(0))); + return; + } + + slotConnectToBattleshipServer(u.host(), u.port(), u.user()); + + } + +} + +void KBattleshipWindow::slotConnectToBattleshipServer() +{ + QString host = m_client->host(); + int port = m_client->port().toInt(); + QString nickname = m_client->nickname(); + delete m_client; + m_client = 0; + slotConnectToBattleshipServer(host, port, nickname); +} +void KBattleshipWindow::slotConnectToBattleshipServer(const QString &host, int port, const QString &nickname) +{ + m_kbclient = new KBattleshipClient(host, port); + nickname.isEmpty() ? m_ownNickname = "TestUser" : m_ownNickname = nickname; + m_chat->setNickname(m_ownNickname); + slotChangeOwnPlayer(m_ownNickname); + cleanup(true); + m_aiPlaying = false; + m_shootable = false; + m_placeable = false; + m_stat->clear(); + m_stat->clearWon(); + m_gameServerConnect->setText(i18n("Dis&connect from server")); + m_gameNewServer->setEnabled(false); + m_gameSingle->setEnabled(false); + if(m_connection == 0) + { + m_connection = new KonnectionHandling(this, m_kbclient); + connect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + { + if(m_connection->type() == KonnectionHandling::SERVER) + { + disconnect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + disconnect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + disconnect(m_connection, SIGNAL(sigSendNickname()), this, SLOT(slotSendGreet())); + disconnect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + disconnect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + disconnect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + disconnect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + disconnect(m_connection, SIGNAL(sigClientLost()), this, SLOT(slotClientLost())); + disconnect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + disconnect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplayRequest())); + disconnect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + disconnect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + m_connection->updateInternal(m_kbclient); + connect(m_kbclient, SIGNAL(sigConnected()), this, SLOT(slotSendVersion())); + connect(m_connection, SIGNAL(sigAbortNetworkGame()), this, SLOT(slotAbortNetworkGame())); + connect(m_connection, SIGNAL(sigStatusBar(const QString &)), this, SLOT(slotStatusMsg(const QString &))); + connect(m_connection, SIGNAL(sigEnemyNickname(const QString &)), this, SLOT(slotChangeEnemyPlayer(const QString &))); + connect(m_connection, SIGNAL(sigSendFieldState(int, int)), this, SLOT(slotSendEnemyFieldState(int, int))); + connect(m_connection, SIGNAL(sigEnemyFieldData(int, int, int, int, int, int, int, bool)), this, SLOT(slotReceivedEnemyFieldData(int, int, int, int, int, int, int, bool))); + connect(m_connection, SIGNAL(sigShootable(bool)), this, SLOT(slotSetShootable(bool))); + connect(m_connection, SIGNAL(sigPlaceShips(bool)), this, SLOT(slotSetPlaceable(bool))); + connect(m_connection, SIGNAL(sigServerLost()), this, SLOT(slotServerLost())); + connect(m_connection, SIGNAL(sigReplay()), this, SLOT(slotReplay())); + connect(m_connection, SIGNAL(sigChatMessage(const QString &, const QString &, bool)), m_chat, SLOT(slotReceivedMessage(const QString &, const QString &, bool))); + m_kbclient->init(); + connect(m_connection, SIGNAL(sigClientInformation(const QString &, const QString &, const QString &, const QString &)), this, SLOT(slotReceivedClientInformation(const QString &, const QString &, const QString &, const QString &))); + connect(m_connection, SIGNAL(sigLost(KMessage *)), this, SLOT(slotLost(KMessage *))); + } + else + m_connection->updateInternal(m_kbclient); + } + m_kbclient->init(); +} + +void KBattleshipWindow::slotSetPlaceable(bool place) +{ + m_placeable = place; +} + +void KBattleshipWindow::slotSetShootable(bool shoot) +{ + m_shootable = shoot; +} + +void KBattleshipWindow::slotShowGrid() +{ + if(!m_configGrid->isChecked()) + m_view->field()->disableGrid(); + else + m_view->field()->enableGrid(); +} + +void KBattleshipWindow::slotStatusMsg(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(text, ID_STATUS_MSG); +} + +void KBattleshipWindow::slotChangeOwnPlayer(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(i18n(" Player 1: %1 ").arg(text), ID_PLAYER_OWN); +} + +void KBattleshipWindow::slotChangeEnemyPlayer(const QString &text) +{ + statusBar()->clear(); + statusBar()->changeItem(i18n(" Player 2: %1 ").arg(text), ID_PLAYER_ENEMY); +} + +void KBattleshipWindow::slotSinglePlayer() +{ + bool ok; + if(!m_aiPlaying) + { + KUser u; + m_ownNickname = KInputDialog::getText(i18n("Start Game"), i18n("Nick name:"), + u.loginName(), &ok, this); + if (ok) + { + slotStatusMsg(i18n("Ready")); + slotStartBattleshipGame(); + } + } + else + { + if(m_aiPlayer != 0) + { + m_aiPlaying = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("Ready")); + m_stat->clear(); + m_chat->clear(); + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + cleanup(false); + } + } +} + +void KBattleshipWindow::slotStartBattleshipGame() +{ + slotStartBattleshipGame(true); +} + +void KBattleshipWindow::slotStartBattleshipGame(bool clearstat) +{ + m_gameSingle->setText(i18n("&Stop game")); + m_gameNewServer->setEnabled(false); + m_gameServerConnect->setEnabled(false); + slotStatusMsg(i18n("Waiting for the AI player to place the ships...")); + slotChangeOwnPlayer(m_ownNickname); + slotChangeEnemyPlayer(KGameMisc::randomName()); + cleanup(true); + if(m_connection != 0) + { + delete m_connection; + m_connection = 0; + } + m_aiPlaying = true; + m_shootable = false; + m_stat->clear(); + if(clearstat) + m_stat->clearWon(); + + if(m_aiPlayer == 0) + { + m_aiPlayer = new KBAIPlayer(); + m_aiPlayer->init(m_view->field(), m_enemyshiplist); + connect(m_aiPlayer, SIGNAL(sigReady()), this, SLOT(slotAIReady())); + connect(m_aiPlayer, SIGNAL(sigShootAt(const QPoint)), this, SLOT(slotAIShootsAt(const QPoint))); + } + m_aiPlayer->slotRestart(); +} + +void KBattleshipWindow::slotAIReady() +{ + slotStatusMsg(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + m_placeable = true; +} + +void KBattleshipWindow::slotAIShootsAt(const QPoint pos) +{ + if(!m_shootable) + m_shootable = true; + + int showstate = m_view->ownFieldState(pos.x(), pos.y()); + + if(showstate == KBattleField::HIT) + { + m_aiPlayer->slotRequestShot(); + return; + } + else if(showstate == KBattleField::FREE) + showstate = KBattleField::WATER; + else if(showstate >= KBattleField::SHIP1P1) + { + showstate = KBattleField::HIT; + m_aiHits++; + } + + slotStatusMsg(i18n("Enemy has shot. Shoot now.")); + slotChangeOwnFieldData(pos.x(), pos.y(), showstate); + + if(m_aiHits == 10 && m_stat->hit() != 10) + { + m_aiPlaying = false; + m_shootable = false; + slotChangeOwnPlayer("-"); + slotChangeEnemyPlayer("-"); + m_gameSingle->setText(i18n("S&ingle Player")); + m_gameNewServer->setEnabled(true); + m_gameServerConnect->setEnabled(true); + slotStatusMsg(i18n("You lost the game. :(")); + m_stat->slotAddEnemyWon(); + slotUpdateHighscore(); + m_view->drawEnemyShipsAI(m_enemyshiplist); // let's show ai player ships + switch(KMessageBox::questionYesNo(this, i18n("Do you want to restart the game?"), QString::null, i18n("Restart"), i18n("Do Not Restart"))) + { + case KMessageBox::Yes: + QTimer::singleShot(0, this, SLOT(slotRestartAI())); + break; + + case KMessageBox::No: + QTimer::singleShot(0, this, SLOT(slotDeleteAI())); + break; + } + } + else + { + if(!m_shootable) + m_shootable = true; + } +} + +void KBattleshipWindow::slotReceivedClientInformation(const QString &clientName, const QString &clientVersion, const QString &clientDescription, const QString &protocolVersion) +{ + m_enemyClient = clientName; + m_enemyClientVersion = clientVersion; + m_enemyClientDescription = clientDescription; + m_enemyProtocolVersion = protocolVersion; + m_gameEnemyInfo->setEnabled(true); + + if(m_connection->type() == KonnectionHandling::SERVER) + { + KMessage *msg = new KMessage(KMessage::GETVERSION); + msg->versionMessage(); + slotSendMessage(msg); + } +} diff --git a/kbattleship/kbattleship/kbattleship.desktop b/kbattleship/kbattleship/kbattleship.desktop new file mode 100644 index 00000000..48cd8dd1 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.desktop @@ -0,0 +1,76 @@ +[Desktop Entry] +Name=KBattleship +Name[af]=Kbattleship +Name[ar]=لعبة سÙينة الحرب (KBattleship) +Name[be]=МарÑкі бой +Name[bn]=কে-বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª +Name[cs]=LodÄ› +Name[de]=Schiffe versenken +Name[hi]=के-बैटलशिप +Name[hr]=KPotapanje brodova +Name[is]=KSjóorrusta +Name[nds]=Scheep versenken +Name[ne]=केडीई बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª +Name[pa]=ਕੇ-ਜੰਗੀ ਜਹਾਜ਼ +Name[ro]=Bătălie navală +Name[sl]=KBojnaLadja +Name[sv]=Kbattleship +Name[ta]=கேபாடà¯à®Ÿà®¿à®²à¯à®·à®¿à®ªà¯ +Name[tg]=KҶанги Киштиҳо +Name[th]=เรือรบ - K +Name[tr]=Amiral Battı +Name[zh_TW]=KBattleship 戰艦 +Exec=kbattleship %i %m -caption "%c" +Icon=kbattleship +Type=Application +DocPath=kbattleship/index.html +GenericName=Battleship Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž марÑкі бой +GenericName[bg]=МорÑки шах +GenericName[bn]=বà§à¦¯à¦¾à¦Ÿà§‡à¦²à¦¶à§€à¦ª খেলা +GenericName[bs]=Igra podmornica +GenericName[ca]=Joc d'enfonsar la flota +GenericName[cs]=Bitva lodí +GenericName[cy]=Gêm Longau Rhyfel +GenericName[da]=Krigsskibe-spil +GenericName[de]=Schiffe versenken +GenericName[el]=Παιχνίδι Battleship +GenericName[eo]=BatalÅipa ludo +GenericName[es]=Juego de la batalla de naves +GenericName[et]=Laevade pommitamise mäng +GenericName[eu]=Ontzi-guda jokoa +GenericName[fa]=بازی Battleship +GenericName[fi]=Meritaistelupeli +GenericName[fr]=Jeu de bataille navale +GenericName[he]=משחק צוללות +GenericName[hr]=Igra potapanja brodova +GenericName[hu]=Torpedós +GenericName[is]=Sjóorustuleikur +GenericName[it]=Gioco a battaglia navale +GenericName[ja]=戦艦ゲーム +GenericName[km]=ល្បែង​នាវា​ចម្បាំង +GenericName[ko]=전함 게임 +GenericName[lt]=Laivų mÅ«Å¡io žaidimas +GenericName[lv]=JÅ«ras kaujas spÄ“le +GenericName[mk]=Игра Ñо потопување бродови +GenericName[nb]=Slagskip +GenericName[nds]=Scheep versenken +GenericName[ne]=बà¥à¤¯à¤¾à¤Ÿà¤²à¤¸à¥€à¤ª खेल +GenericName[nl]=Zeeslagspel +GenericName[nn]=Slagskip +GenericName[pa]=ਜੰਗੀ ਜਹਾਜ਼ ਖੇਡ +GenericName[pl]=Gra w statki +GenericName[pt]=Jogo de Batalha Naval +GenericName[pt_BR]=Jogo de Batalha Naval +GenericName[ru]=МорÑкой бой +GenericName[sk]=Bojová loÄ hra +GenericName[sl]=Igra potapljanja ladjic +GenericName[sr]=Игра подморница +GenericName[sr@Latn]=Igra podmornica +GenericName[sv]=Sänka fartyg spel +GenericName[ta]=போரà¯à®¤à¯à®¤à®³ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в морÑький бій +GenericName[zh_TW]=戰艦éŠæˆ² +Terminal=false +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kbattleship/kbattleship/kbattleship.h b/kbattleship/kbattleship/kbattleship.h new file mode 100644 index 00000000..7e606140 --- /dev/null +++ b/kbattleship/kbattleship/kbattleship.h @@ -0,0 +1,162 @@ +/*************************************************************************** + kbattleship.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIP_H +#define KBATTLESHIP_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dialogs/infoDlg.h" + +#include "kbaiplayer.h" +#include "kbattleshipclient.h" +#include "kbattleshipserver.h" +#include "kbattleshipview.h" +#include "kchatwidget.h" +#include "kclientdialog.h" +#include "konnectionhandling.h" +#include "kserverdialog.h" +#include "kship.h" +#include "kshiplist.h" +#include "kstatdialog.h" + +class KBattleshipWindow : public KMainWindow +{ + Q_OBJECT +public: + enum{ID_STATUS_MSG, ID_PLAYER_OWN, ID_PLAYER_ENEMY}; + KBattleshipWindow(); + ~KBattleshipWindow(); + + void init(); + + KShip *shipAt(int fieldx, int fieldy); + KShip *enemyShipAt(int fieldx, int fieldy); + +private slots: + void changeShipPlacementDirection(); + void slotConfigureNotifications(); + void slotLost(KMessage *msg); + void slotStatusMsg(const QString &text); + void slotReceivedEnemyFieldData(int fieldx, int fieldx1, int enemystate, int xstart, int xstop, int ystart, int ystop, bool death); + void slotSendEnemyFieldState(int, int); + void slotChangeOwnPlayer(const QString &text); + void slotChangeEnemyPlayer(const QString &text); + void slotSendVersion(); + void slotSendGreet(); + void slotShipsReady(); + void slotSetPlaceable(bool place); + void slotSetShootable(bool shoot); + void slotEnemyFieldClick(int fieldx, int fieldy); + void slotSendMessage(int fieldx, int fieldy, int state); + void slotSendMessage(KMessage *msg); + void slotClientLost(); + void slotServerLost(); + void slotServerReplay(); + void slotClientReplay(); + void slotAIReady(); + void slotAIShootsAt(const QPoint pos); + void slotDeleteAI(); + void slotRestartAI(); + void slotSinglePlayer(); + void slotServerConnect(); + void slotDeleteConnectDialog(); + void slotNewServer(); + void slotDeleteServerDialog(); + void slotHighscore(); + void slotShowGrid(); + void slotStartBattleshipGame(); + void slotStartBattleshipGame(bool clearstat); + void slotStartBattleshipServer(); + /** + * Get server to connect to from "Connect to server" dialog. + */ + void slotConnectToBattleshipServer(); + void slotConnectToBattleshipServer(const QString &host, int port, const QString &nickname); + void slotPlaceShipPreview(int fieldx, int fieldy); + void slotPlaceShip(int fieldx, int fieldy); + void slotChangeOwnFieldData(int fieldx, int fieldy, int type); + void slotChangeEnemyFieldData(int fieldx, int fieldy, int type); + void slotUpdateHighscore(); + void slotAbortNetworkGame(); + void slotReplay(); + void slotReplayRequest(); + void slotChangedNickCommand(const QString &text); + void slotSendChatMessage(const QString &text); + void slotEnemyClientInfo(); + void slotReceivedClientInformation(const QString &client, const QString &clientVersion, const QString &clientInformation, const QString &protocolVersion); + +private: + bool shift; + void initActions(); + void initStatusBar(); + void initView(); + void initChat(); + void initShipPlacing(); + void saveOptions(); + void readOptions(); + + void cleanup(bool placechange = true); + void playSound(bool enemy, int fieldstate); + void parseCommandLine(); + + bool m_placeable; + bool m_shootable; + bool m_aiPlaying; + bool m_serverHasClient; + bool m_lost; + int m_aiHits; + + QString m_enemyClient; + QString m_enemyClientVersion; + QString m_enemyClientDescription; + QString m_enemyProtocolVersion; + + KConfig *m_config; + KBAIPlayer *m_aiPlayer; + KonnectionHandling *m_connection; + KBattleshipServer *m_kbserver; + KBattleshipClient *m_kbclient; + KChatWidget *m_chat; + KStatDialog *m_stat; + KBattleshipView *m_view; + KAction *m_gameServerConnect; + KAction *m_gameNewServer; + KAction *m_gameQuit; + KAction *m_gameEnemyInfo; + KAction *m_gameSingle; + KToggleAction *m_configSound; + KToggleAction *m_configGrid; + KClientDialog *m_client; + KServerDialog *m_server; + KShipList *m_ownshiplist; + KShipList *m_enemyshiplist; + QString m_ownNickname; + QString m_enemyNickname; + +}; +#endif diff --git a/kbattleship/kbattleship/kbattleshipclient.cpp b/kbattleship/kbattleship/kbattleshipclient.cpp new file mode 100644 index 00000000..d8ae2a75 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipclient.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + kbattleshipclient.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#ifdef HAVE_STROPTS_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#include +#include "kmessage.h" +#include "kbattleshipclient.moc" + +KBattleshipClient::KBattleshipClient(const QString &host, int port) : KExtendedSocket(host, port, inetSocket) +{ +} + +void KBattleshipClient::init() +{ + if(connect()) + { + emit sigSocketFailure(status()); + return; + } + + m_readNotifier = new QSocketNotifier(fd(), QSocketNotifier::Read, this); + QObject::connect(m_readNotifier, SIGNAL(activated(int)), SLOT(slotReadData())); + emit sigConnected(); +} + +void KBattleshipClient::sendMessage(KMessage *msg) +{ + QCString post = msg->sendStream().utf8(); + writeBlock(post.data(), post.length()); + emit sigMessageSent(msg); +} + +void KBattleshipClient::slotReadData() +{ + int len; + ioctl(fd(), FIONREAD, &len); + if(!len) + { + delete m_readNotifier; + m_readNotifier = 0; + emit sigEndConnect(); + return; + } + + char *buf = new char[len + 1]; + readBlock(buf, len); + buf[len] = 0; + m_readBuffer += QString::fromUtf8(buf); + delete []buf; + int pos; + while ((pos = m_readBuffer.find("")) >= 0) + { + pos += 11; // Length of "" + KMessage *msg = new KMessage(); + msg->setDataStream(m_readBuffer.left(pos)); + m_readBuffer.remove(0, pos); + emit sigNewMessage(msg); + } +} diff --git a/kbattleship/kbattleship/kbattleshipclient.h b/kbattleship/kbattleship/kbattleshipclient.h new file mode 100644 index 00000000..d69ec7f9 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipclient.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kbattleshipclient.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPCLIENT_H +#define KBATTLESHIPCLIENT_H + +#include +#include +#include "kmessage.h" + +class KBattleshipClient : public KExtendedSocket +{ + Q_OBJECT +public: + KBattleshipClient(const QString &host, int port); + + void init(); + void sendMessage(KMessage *msg); + +private slots: + void slotReadData(); + +signals: + void sigConnected(); + void sigEndConnect(); + void sigSocketFailure(int); + void sigNewMessage(KMessage *); + void sigMessageSent(KMessage *); + +private: + QSocketNotifier *m_readNotifier; + QString m_readBuffer; +}; + +#endif diff --git a/kbattleship/kbattleship/kbattleshipserver.cpp b/kbattleship/kbattleship/kbattleshipserver.cpp new file mode 100644 index 00000000..d03bd213 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipserver.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + kbattleshipserver.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include +#ifdef HAVE_STROPTS_H +#include +#endif +#ifdef HAVE_SYS_FILIO_H +#include +#endif +#include +#include +#include +#include +#include "kbattleshipserver.moc" + +KBattleshipServer::KBattleshipServer(int port, const QString& name) + : KExtendedSocket(QString::null, port, inetSocket | passiveSocket), m_name(name) +{ + m_port = port; + m_serverSocket = 0; +} + +void KBattleshipServer::init() +{ + if(listen()) + { + KMessageBox::error(0L, i18n("Failed to bind to local port \"%1\"\n\nPlease check if another KBattleship server instance\nis running or another application uses this port.").arg(m_port)); + emit sigServerFailure(); + return; + } + m_service.setServiceName(m_name); + m_service.setType(BATTLESHIP_SERVICE); + m_service.setPort(m_port); + m_service.publishAsync(); + m_connectNotifier = new QSocketNotifier(fd(), QSocketNotifier::Read, this); + QObject::connect(m_connectNotifier, SIGNAL(activated(int)), SLOT(slotNewConnection())); +} + +void KBattleshipServer::slotNewConnection() +{ + KExtendedSocket *sock; + accept(sock); + if(sock && m_serverSocket == 0) + { + m_service.stop(); + m_serverSocket = sock; + m_readNotifier = new QSocketNotifier(sock->fd(), QSocketNotifier::Read, this); + QObject::connect(m_readNotifier, SIGNAL(activated(int)), this, SLOT(slotReadClient())); + emit sigNewConnect(); + } + else + delete sock; +} + +void KBattleshipServer::slotReadClient() +{ + int len; + ioctl(m_serverSocket->fd(), FIONREAD, &len); + if(!len) + { + slotDiscardClient(i18n("The connection broke down!"), false, true); + return; + } + + char *buf = new char[len + 1]; + m_serverSocket->readBlock(buf, len); + buf[len] = 0; + m_readBuffer += QString::fromUtf8(buf); + delete []buf; + int pos; + while ((pos = m_readBuffer.find("")) >= 0) + { + pos += 11; // Length of "" + KMessage *msg = new KMessage(); + msg->setDataStream(m_readBuffer.left(pos)); + m_readBuffer.remove(0, pos); + emit sigNewMessage(msg); + } +} + +void KBattleshipServer::sendMessage(KMessage *msg) +{ + QCString post = msg->sendStream().utf8(); + m_serverSocket->writeBlock(post.data(), post.length()); + emit sigMessageSent(msg); +} + +void KBattleshipServer::slotDiscardClient(const QString &reason, bool kmversion, bool bemit) +{ + KMessage *msg = new KMessage(KMessage::DISCARD); + msg->addField("reason", reason); + if(kmversion) + msg->addField("kmversion", "true"); + else + msg->addField("kmversion", "false"); + QCString post = msg->sendStream().utf8(); + m_serverSocket->writeBlock(post.data(), post.length()); + delete msg; + + delete m_readNotifier; + m_readNotifier = 0; + delete m_serverSocket; + m_serverSocket = 0; + + if(bemit) + emit sigEndConnect(); +} diff --git a/kbattleship/kbattleship/kbattleshipserver.h b/kbattleship/kbattleship/kbattleshipserver.h new file mode 100644 index 00000000..46f815e0 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipserver.h @@ -0,0 +1,60 @@ +/*************************************************************************** + kbattleshipserver.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPSERVER_H +#define KBATTLESHIPSERVER_H + +#include +#include +#include +#include "kmessage.h" + +class KBattleshipServer : public KExtendedSocket +{ + Q_OBJECT +public: + KBattleshipServer(int port, const QString& name); + void init(); + void sendMessage(KMessage *msg); + +public slots: + void slotDiscardClient(const QString &reason, bool kmversion, bool bemit); + +private slots: + void slotNewConnection(); + void slotReadClient(); + +signals: + void sigServerFailure(); + void sigNewConnect(); + void sigEndConnect(); + void sigNewMessage(KMessage *); + void sigMessageSent(KMessage *); + +private: + QSocketNotifier *m_connectNotifier; + QSocketNotifier *m_readNotifier; + KExtendedSocket *m_serverSocket; + QString m_readBuffer; + DNSSD::PublicService m_service; + int m_port; + QString m_name; +}; + +#define BATTLESHIP_SERVICE "_kbattleship._tcp" + +#endif diff --git a/kbattleship/kbattleship/kbattleshipui.rc b/kbattleship/kbattleship/kbattleshipui.rc new file mode 100644 index 00000000..491fbb03 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipui.rc @@ -0,0 +1,17 @@ + + + + + &Game + + + + + + &Settings + + + + + + diff --git a/kbattleship/kbattleship/kbattleshipview.cpp b/kbattleship/kbattleship/kbattleshipview.cpp new file mode 100644 index 00000000..86e8cbf6 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipview.cpp @@ -0,0 +1,295 @@ +/*************************************************************************** + kbattleshipview.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include +#include +#include "kbattleship.h" +#include "kbattleshipview.moc" + +KBattleshipView::KBattleshipView(QWidget *parent, const char *name, bool draw) + : QWidget(parent, name, WResizeNoErase), m_drawGrid(draw) +{ + setFixedSize(20 * 32 + 30, 10 * 32 + 20); + setBackgroundMode(NoBackground); + setMouseTracking(true); + installEventFilter(this); + + m_decide = false; + m_lastX = 0; + m_lastY = 0; + m_battlefield = 0; +} + +KBattleshipView::~KBattleshipView() +{ + delete m_battlefield; +} + +void KBattleshipView::startDrawing() +{ + m_battlefield = new KBattleField(this, m_drawGrid); +} + +void KBattleshipView::clearField() +{ + m_battlefield->clearOwnField(); + m_battlefield->clearEnemyField(); +} + +int KBattleshipView::ownFieldState(int fieldx, int fieldy) +{ + return m_battlefield->ownState(fieldx, fieldy); +} + +int KBattleshipView::enemyFieldState(int &fieldx, int &fieldy) +{ + return m_battlefield->enemyState(fieldx, fieldy); +} + +void KBattleshipView::previewShip(int fieldx, int fieldy, int type, bool rotate) +{ + m_battlefield->setPreviewState(fieldx, fieldy, type, rotate); +} + +void KBattleshipView::changeOwnFieldData(int fieldx, int fieldy, int type) +{ + m_battlefield->setOwnState(fieldx, fieldy, type); + m_battlefield->drawField(); +} + +void KBattleshipView::changeEnemyFieldData(int fieldx, int fieldy, int type) +{ + m_battlefield->setEnemyState(fieldx, fieldy, type); + m_battlefield->drawField(); +} + +void KBattleshipView::drawEnemyShipsAI(KShipList *list) +{ + KShip *ship; + int state; + int grid = m_battlefield->gridSize(); + int width = m_battlefield->enemyRect().width() / grid; + int height = m_battlefield->enemyRect().height() / grid; + + for(int i = 0; i < width; i++) + { + for(int j = 0; j < height; j++) + { + ship = list->shipAt(i, j); + state = enemyFieldState(i, j); + if (ship && state != KBattleField::HIT && state != KBattleField::DEATH) + { + changeEnemyFieldData(i, j, ship->shipTypeEnum(i, j)); + } + } + } +} + +void KBattleshipView::drawEnemyShipsHuman(KMessage *msg, KShipList *list) +{ + int posx, posy, placedLeft; + bool left; + int i = 3; + while (!msg->field(QString("ship%1").arg(i)).isNull()) + { + posx = msg->field(QString("ship%1").arg(i)).section(" ", 0, 0).toInt(); + posy = msg->field(QString("ship%1").arg(i)).section(" ", 1, 1).toInt(); + placedLeft = msg->field(QString("ship%1").arg(i)).section(" ", 2, 2).toInt(); + if (placedLeft == 0) left = false; + else left = true; + list->addNewShip(!left, posx, posy); + i--; + } + drawEnemyShipsAI(list); +} + +KMessage *KBattleshipView::getAliveShips(KShipList *list) +{ + KShip *ship; + QString shipPos, shipNum; + int shipType; + int grid = m_battlefield->gridSize(); + int width = m_battlefield->enemyRect().width() / grid; + int height = m_battlefield->enemyRect().height() / grid; + KMessage *msg = new KMessage(KMessage::WON); + bool shipsFound[4] = {false, false, false, false}; + + for(int i = 0; i < width; i++) + { + for(int j = 0; j < height; j++) + { + ship = list->shipAt(i, j); + shipType = list->shipTypeAt(i, j); + if (ship && !shipsFound[shipType]) + { + shipPos.sprintf("%d %d %d", i, j, ship->placedLeft()); + shipsFound[shipType] = true; + shipNum.sprintf("ship%d",shipType); + msg->addField(shipNum, shipPos); + } + } + } + return msg; +} + +bool KBattleshipView::eventFilter(QObject *object, QEvent *event) +{ + if(event->type() == QEvent::KeyPress && m_decide) + { + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->key() == Key_Shift){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + } + } + else if(event->type() == QEvent::KeyRelease && m_decide) + { + QKeyEvent *keyEvent = static_cast(event); + if(keyEvent->key() == Key_Shift){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + } + } + else if(event->type() == QEvent::MouseButtonRelease) + { + m_decide = false; + + QMouseEvent *mouseEvent = static_cast(event); + + if(mouseEvent->button() == RightButton){ + emit sigMouseOverField(m_lastX, m_lastY); + emit changeShipPlacementDirection(); + return true; + } + + if(mouseEvent->button() != LeftButton) + return false; + + QPoint point(mouseEvent->x(), mouseEvent->y()); + QRect ownRect = m_battlefield->ownRect(); + QRect enemyRect = m_battlefield->enemyRect(); + + QRect newRect; + + int fieldx = 0; + int fieldy = 0; + + if(ownRect.contains(point)) + newRect = ownRect; + else if(enemyRect.contains(point)) + newRect = enemyRect; + else + return false; + + int j = -1; + + for(int i = newRect.left(); i <= newRect.right(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(i, newRect.top(), m_battlefield->gridSize(), newRect.bottom() - newRect.top()); + + if(tempRect.contains(point)) + { + fieldx = j; + break; + } + } + + j = -1; + + for(int i = newRect.top(); i <= newRect.bottom(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(newRect.left(), i, newRect.right() - newRect.left(), m_battlefield->gridSize()); + + if(tempRect.contains(point)) + { + fieldy = j; + break; + } + } + + if( newRect == ownRect) + emit sigOwnFieldClicked(fieldx, fieldy); + else if(newRect == enemyRect) + emit sigEnemyFieldClicked(fieldx, fieldy); + + return true; + } + else if(event->type() == QEvent::MouseMove) + { + setFocus(); + m_decide = true; + + QMouseEvent *mouseEvent = static_cast(event); + + QPoint point(mouseEvent->x(), mouseEvent->y()); + QRect ownRect = m_battlefield->ownRect(); + + int fieldx = 0; + int fieldy = 0; + + if(ownRect.contains(point)) + { + int j = -1; + + for(int i = ownRect.left(); i <= ownRect.right(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(i, ownRect.top(), m_battlefield->gridSize(), ownRect.bottom() - ownRect.top()); + + if(tempRect.contains(point)) + { + fieldx = j; + break; + } + } + + j = -1; + + for(int i = ownRect.top(); i <= ownRect.bottom(); i += m_battlefield->gridSize()) + { + j++; + QRect tempRect(ownRect.left(), i, ownRect.right() - ownRect.left(), m_battlefield->gridSize()); + + if(tempRect.contains(point)) + { + fieldy = j; + break; + } + } + + m_lastX = fieldx; + m_lastY = fieldy; + + emit sigMouseOverField(fieldx, fieldy); + } + else + m_battlefield->drawField(); + + return true; + } + else if(event->type() == QEvent::Paint) + { + m_battlefield->drawField(); + return true; + } + + return QWidget::eventFilter(object, event); +} diff --git a/kbattleship/kbattleship/kbattleshipview.h b/kbattleship/kbattleship/kbattleshipview.h new file mode 100644 index 00000000..f7f0a953 --- /dev/null +++ b/kbattleship/kbattleship/kbattleshipview.h @@ -0,0 +1,72 @@ +/*************************************************************************** + kbattleshipview.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBATTLESHIPVIEW_H +#define KBATTLESHIPVIEW_H + +#include + +#include +#include +#include +#include + +#include "kbattlefield.h" +#include "kmessage.h" +#include "kship.h" +#include "kshiplist.h" + +class KBattleshipView : public QWidget +{ + Q_OBJECT +public: + KBattleshipView(QWidget *parent = 0, const char *name = 0, bool draw = false); + ~KBattleshipView(); + + KBattleField *field() { return m_battlefield; } + + void startDrawing(); + void clearField(); + void changeOwnFieldData(int fieldx, int fieldy, int type); + void changeEnemyFieldData(int fieldx, int fieldy, int type); + + void previewShip(int fieldx, int fieldy, int type, bool rotate); + + int ownFieldState(int fieldx, int fieldy); + int enemyFieldState(int &fieldx, int &fieldy); + + void drawEnemyShipsAI(KShipList *list); + void drawEnemyShipsHuman(KMessage *msg, KShipList *list); + KMessage *getAliveShips(KShipList *list); + +signals: + void sigEnemyFieldClicked(int, int); + void sigOwnFieldClicked(int, int); + void sigMouseOverField(int, int); + void changeShipPlacementDirection(); + +private: + bool eventFilter(QObject *object, QEvent *event); + + KBattleField *m_battlefield; + bool m_drawGrid; + bool m_decide; + int m_lastX; + int m_lastY; +}; + +#endif diff --git a/kbattleship/kbattleship/kbchooserstrategy.cpp b/kbattleship/kbattleship/kbchooserstrategy.cpp new file mode 100644 index 00000000..f365c6d1 --- /dev/null +++ b/kbattleship/kbattleship/kbchooserstrategy.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + kbchooserstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbchooserstrategy.h" + +#include + +#include "kbrandomshotstrategy.h" +#include "kbdiagonalwrapstrategy.h" +#include "kbhorizontalstepstrategy.h" +#include "kbverticalstepstrategy.h" + +#define MAX_CHILD_NUM 4 + +KBChooserStrategy::KBChooserStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + + m_child = 0; + + m_random = new KRandomSequence(KApplication::random()); +} + +KBChooserStrategy::~KBChooserStrategy() +{ + delete m_destroyer; + delete m_child; + delete m_random; +} + +void KBChooserStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); + + advance(); +} + +const QPoint KBChooserStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(advance()) + return m_child->nextShot(); + } + + return QPoint(0, 0); +} + +bool KBChooserStrategy::advance() +{ + if(!m_destroying && m_prevShots.count() % 5 == 0) + { + delete m_child; + + switch(m_random->getLong(MAX_CHILD_NUM)) + { + case 0: + m_child = new KBVerticalStepStrategy(this); + break; + + case 1: + m_child = new KBHorizontalStepStrategy(this); + break; + + case 2: + m_child = new KBDiagonalWrapStrategy(this); + break; + + default: + m_child = new KBRandomShotStrategy(this); + break; + } + + m_child->init(m_battleField, m_fieldRect); + } + + return true; +} + +bool KBChooserStrategy::hasMoreShots() +{ + if(m_parent == 0) + { + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + for(int row = 0; row < m_fieldRect.height(); row++) + { + for(int col = 0; col < m_fieldRect.width(); col++) + { + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + return true; + } + } + + return false; +} + +void KBChooserStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbchooserstrategy.h b/kbattleship/kbattleship/kbchooserstrategy.h new file mode 100644 index 00000000..298cbe68 --- /dev/null +++ b/kbattleship/kbattleship/kbchooserstrategy.h @@ -0,0 +1,46 @@ +/*************************************************************************** + kbchooserstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBCHOOSERSTRATEGY_H +#define KBCHOOSERSTRATEGY_H + +#include + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBChooserStrategy : public KBStrategy +{ +public: + KBChooserStrategy(KBStrategy *parent = 0); + virtual ~KBChooserStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + + KBStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + KRandomSequence *m_random; + + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbdestroyshipstrategy.cpp b/kbattleship/kbattleship/kbdestroyshipstrategy.cpp new file mode 100644 index 00000000..714bdd7c --- /dev/null +++ b/kbattleship/kbattleship/kbdestroyshipstrategy.cpp @@ -0,0 +1,390 @@ +/*************************************************************************** + kbdestroyshipstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbdestroyshipstrategy.h" + +KBDestroyShipStrategy::KBDestroyShipStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_working = false; +} + +void KBDestroyShipStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + m_working = false; +} + +const QPoint KBDestroyShipStrategy::nextShot() +{ + if(hasMoreShots()) + return QPoint(m_column, m_row); + else + return m_start; +} + +bool KBDestroyShipStrategy::hasMoreShots() +{ + if(!m_working) + return false; + + if(shipDestroyed()) + { + m_working = false; + markBorderingFields(); + return false; + } + + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + + // last shot was no success :( + if(m_battleField->ownState(m_column, m_row) == KBattleField::WATER) + { + m_column = m_start.x(); + m_row = m_start.y(); + } + + switch(m_direction) + { + case VERTICAL: + if(searchUp() || searchDown()) + return true; + else + { + //kdDebug << "KBDestroyShipStrategy: failed vertical search!" << endl; + m_working = false; + } + break; + + case HORIZONTAL: + if(searchLeft() || searchRight()) + return true; + else + { + //kdDebug << "KBDestroyShipStrategy: failed horizontal search!" << endl; + m_working = false; + } + break; + + default: + int up = m_row > 0 ? m_battleField->ownState(m_column, m_row - 1) : -1; + int down = m_row < (m_fieldRect.height() - 1) ? m_battleField->ownState(m_column, m_row + 1) : -1; + int left = m_column > 0 ? m_battleField->ownState(m_column - 1, m_row) : -1; + int right = m_column < (m_fieldRect.width() - 1) ? m_battleField->ownState(m_column + 1, m_row) : -1; + + if((up != -1 && up == KBattleField::HIT) || (down != -1 && down == KBattleField::HIT)) + { + m_direction = VERTICAL; + return hasMoreShots(); + } + + if((left != -1 && left == KBattleField::HIT) || (right != -1 && right == KBattleField::HIT)) + { + m_direction = HORIZONTAL; + return hasMoreShots(); + } + + if(searchUp() || searchDown() || searchLeft() || searchRight()) + return true; + else + { + //kdDebug << "KBDestroyStrategy: ship unsinkable?" << endl; + m_working = false; + } + break; + } + return false; +} + +void KBDestroyShipStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBDestroyShipStrategy::destroyShipAt(const QPoint pos) +{ + if(enemyFieldStateAt(pos.x(), pos.y()) == FREE || m_battleField->ownState(pos.x(), pos.y()) == KBattleField::DEATH || m_battleField->ownState(pos.x(), pos.y()) == KBattleField::WATER) + m_working = false; + else + { + m_working = true; + m_start = pos; + m_column = pos.x(); + m_row = pos.y(); + m_direction = NODIR; + } +} + +bool KBDestroyShipStrategy::searchUp() +{ + int row = m_row; + int prevCol = m_column - 1; + int nextCol = m_column + 1; + + while(row >= 0 && (m_row - row) < 4 && enemyFieldStateAt(m_column, row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(m_column, row) == KBattleField::WATER) + return false; + + row--; + + bool leftOK = true; + bool rightOK = true; + if(prevCol >= 0) + leftOK = (enemyFieldStateAt(prevCol, row) == FREE) || (m_battleField->ownState(prevCol, row) == KBattleField::WATER); + + if(nextCol < m_fieldRect.width()) + rightOK = (enemyFieldStateAt(nextCol, row) == FREE) || (m_battleField->ownState(nextCol, row) == KBattleField::WATER); + + if(!(rightOK && leftOK)) + return false; + } + + if(row < 0 || (m_row - row) >= 4) + return false; + + m_row = row; + return true; +} + +bool KBDestroyShipStrategy::searchDown() +{ + int row = m_row; + int prevCol = m_column - 1; + int nextCol = m_column + 1; + + while(row < m_fieldRect.height() && (row - m_row) < 4 && enemyFieldStateAt(m_column, row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(m_column, row) == KBattleField::WATER) + return false; + + row++; + + bool leftOK = true; + bool rightOK = true; + if(prevCol >= 0) + leftOK = (enemyFieldStateAt(prevCol, row) == FREE) || (m_battleField->ownState(prevCol, row) == KBattleField::WATER); + + if(nextCol < m_fieldRect.width()) + rightOK = (enemyFieldStateAt(nextCol, row) == FREE) || (m_battleField->ownState(nextCol, row) == KBattleField::WATER); + + if(!(rightOK && leftOK)) + return false; + } + + if(row >= m_fieldRect.height() || (row - m_row) >= 4) + return false; + + m_row = row; + return true; +} + +bool KBDestroyShipStrategy::searchLeft() +{ + int col = m_column; + int prevRow = m_row - 1; + int nextRow = m_row + 1; + + while(col >= 0 && (m_column - col) < 4 && enemyFieldStateAt(col, m_row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(col, m_row) == KBattleField::WATER) + return false; + + col--; + + bool upOK = true; + bool downOK = true; + if(prevRow >= 0) + upOK = (enemyFieldStateAt(col, prevRow) == FREE) || (m_battleField->ownState(col, prevRow) == KBattleField::WATER); + + if(nextRow < m_fieldRect.height()) + downOK = (enemyFieldStateAt(col, nextRow) == FREE) || (m_battleField->ownState(col, nextRow) == KBattleField::WATER); + + if(!(upOK && downOK)) + return false; + } + + if(col < 0 || (m_column - col) >= 4) + return false; + + m_column = col; + return true; +} + +bool KBDestroyShipStrategy::searchRight() +{ + int col = m_column; + int prevRow = m_row - 1; + int nextRow = m_row + 1; + + while(col < m_fieldRect.width() && (col - m_column) < 4 && enemyFieldStateAt(col, m_row) == KBStrategy::SHOT) + { + if(m_battleField->ownState(col, m_row) == KBattleField::WATER) + return false; + + col++; + + bool upOK = true; + bool downOK = true; + if(prevRow >= 0) + upOK = (enemyFieldStateAt(col, prevRow) == FREE) || (m_battleField->ownState(col, prevRow) == KBattleField::WATER); + + if(nextRow < m_fieldRect.height()) + downOK = (enemyFieldStateAt(col, nextRow) == FREE) || (m_battleField->ownState(col, nextRow) == KBattleField::WATER); + + if(!(upOK && downOK)) + return false; + } + + if(col >= m_fieldRect.width() || (col - m_column) >= 4) + return false; + + m_column = col; + return true; +} + +bool KBDestroyShipStrategy::shipDestroyed() +{ + int col = m_start.x(); + int row = m_start.y(); + int state = m_battleField->ownState(col, row); + + while(m_direction != HORIZONTAL && row >= 0 && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + row--; + if(row >= 0) + state = m_battleField->ownState(col, row); + } + + row = m_start.y(); + state = m_battleField->ownState(col, row); + while(m_direction != HORIZONTAL && row < m_fieldRect.height() && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + row++; + if(row < m_fieldRect.height()) + state = m_battleField->ownState(col, row); + } + + row = m_start.y(); + state = m_battleField->ownState(col, row); + while(m_direction != VERTICAL && col >= 0 && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + col--; + if(col >= 0) + state = m_battleField->ownState(col, row); + } + + col = m_start.x(); + state = m_battleField->ownState(col, row); + while(m_direction != VERTICAL && col < m_fieldRect.width() && state != KBattleField::FREE && state != KBattleField::WATER) + { + if(enemyFieldStateAt(col, row) == SHIP) + return false; + + col++; + if(col < m_fieldRect.width()) + state = m_battleField->ownState(col, row); + } + + return true; +} + +void KBDestroyShipStrategy::markBorderingFields() +{ + int col = m_start.x(); + int row = m_start.y(); + int i,j; + + if (m_direction == VERTICAL) + { + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + row--; + } + if (row >= 0) + { // above the ship + setViablePos(col, row, false); + } + row++; + i = col+1; // right of the ship + j = col-1; // left of the ship + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + if (m_fieldRect.contains(i, row)) + setViablePos(i, row, false); + if (m_fieldRect.contains(j, row)) + setViablePos(j, row, false); + setViablePos(col, row, false); + row++; + } + if (m_fieldRect.contains(col, row)) + { // below the ship + setViablePos(col, row, false); + } + } + else if (m_direction == HORIZONTAL) + { + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + col--; + } + if (col >= 0) + { // left of the ship + setViablePos(col, row, false); + } + col++; + i = row+1; // below the ship + j = row-1; // above the ship + while (m_fieldRect.contains(col, row) && + m_battleField->ownState(col, row) == KBattleField::HIT) + { + if (m_fieldRect.contains(col, i)) + setViablePos(col, i, false); + if (m_fieldRect.contains(col, j)) + setViablePos(col, j, false); + setViablePos(col, row, false); + col++; + } + if (m_fieldRect.contains(col, row)) + { // right of the ship + setViablePos(col, row, false); + } + } + else + { + if (row > 0) + setViablePos(col, (row-1), false); + if (row < (m_fieldRect.height()-1)) + setViablePos(col, (row+1), false); + if (col > 0) + setViablePos((col-1), row, false); + if (col < (m_fieldRect.width()-1)) + setViablePos((col+1), row, false); + } +} diff --git a/kbattleship/kbattleship/kbdestroyshipstrategy.h b/kbattleship/kbattleship/kbdestroyshipstrategy.h new file mode 100644 index 00000000..0fb6ca92 --- /dev/null +++ b/kbattleship/kbattleship/kbdestroyshipstrategy.h @@ -0,0 +1,55 @@ +/*************************************************************************** + kbdestroyshipstratgey.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDESTROYSHIPSTRATEGY_H +#define KBDESTROYSHIPSTRATEGY_H + +#include "kbstrategy.h" + +class KBDestroyShipStrategy : public KBStrategy +{ +public: + KBDestroyShipStrategy(KBStrategy *parent = 0); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + + virtual void destroyShipAt(const QPoint pos); + +private: + enum {NODIR, VERTICAL, HORIZONTAL}; + + bool m_working; + QPoint m_start; + + int m_column; + int m_row; + + int m_direction; + + virtual bool searchUp(); + virtual bool searchDown(); + virtual bool searchLeft(); + virtual bool searchRight(); + + virtual bool shipDestroyed(); + virtual void markBorderingFields(); +}; + +#endif diff --git a/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp b/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp new file mode 100644 index 00000000..f0e0d6c3 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalshotstrategy.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + kbdiagonalshotstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbdiagonalshotstrategy.h" + +KBDiagonalShotStrategy::KBDiagonalShotStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_column = 0; + m_row = 0; + m_vertical = 0; + m_horizontal = 0; +} + +const QPoint KBDiagonalShotStrategy::nextShot() +{ + if(hasMoreShots()) + return QPoint(m_column, m_row); + + return QPoint(0,0); +} + +bool KBDiagonalShotStrategy::advance() +{ + while (m_fieldRect.contains(m_column, m_row)) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + m_column += m_horizontal; + m_row += m_vertical; + } + + return false; +} + +bool KBDiagonalShotStrategy::hasMoreShots() +{ + return advance(); +} + +void KBDiagonalShotStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBDiagonalShotStrategy::startAt(int col, int row, Direction dir) +{ + m_column = col; + m_row = row; + + switch(dir) + { + case LEFTUP: + m_vertical = -1; + m_horizontal = -1; + break; + + case LEFTDOWN: + m_vertical = 1; + m_horizontal = -1; + break; + + case RIGHTUP: + m_vertical = -1; + m_horizontal = 1; + break; + + case RIGHTDOWN: + m_vertical = 1; + m_horizontal = 1; + break; + + default: + m_vertical = 0; + m_horizontal = 0; + break; + } +} + +QPoint KBDiagonalShotStrategy::endPoint() +{ + int row = m_row; + int col = m_column; + + if(m_vertical == 0 || m_horizontal == 0) + return QPoint(col, row); + + while(m_fieldRect.contains(col, row)) + { + row += m_vertical; + col += m_horizontal; + } + + row -= m_vertical; + col -= m_horizontal; + + return QPoint(col, row); +} diff --git a/kbattleship/kbattleship/kbdiagonalshotstrategy.h b/kbattleship/kbattleship/kbdiagonalshotstrategy.h new file mode 100644 index 00000000..b7c9b086 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalshotstrategy.h @@ -0,0 +1,43 @@ +/*************************************************************************** + kbdiagonalshotstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDIAGONALSHOTSTRATEGY_H +#define KBDIAGONALSHOTSTRATEGY_H + +#include "kbstrategy.h" + +class KBDiagonalShotStrategy : public KBStrategy +{ +public: + enum Direction {LEFTUP, LEFTDOWN, RIGHTUP, RIGHTDOWN}; + KBDiagonalShotStrategy(KBStrategy *parent = 0); + + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + virtual void startAt(int col, int row, Direction dir); + virtual QPoint endPoint(); + +private: + bool advance(); + + int m_row; + int m_column; + int m_vertical; + int m_horizontal; +}; + +#endif diff --git a/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp b/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp new file mode 100644 index 00000000..7f88fb54 --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalwrapstrategy.cpp @@ -0,0 +1,320 @@ +/*************************************************************************** + kbdiagonalwrapstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbdiagonalwrapstrategy.h" + +KBDiagonalWrapStrategy::KBDiagonalWrapStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = new KBDiagonalShotStrategy(this); + + if(parent == 0) + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBDiagonalWrapStrategy::~KBDiagonalWrapStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBDiagonalWrapStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + + switch(rand.getLong(4)) + { + case 0: + m_direction = KBDiagonalShotStrategy::RIGHTDOWN; + m_row = 0; + break; + + case 1: + m_direction = KBDiagonalShotStrategy::RIGHTUP; + m_column = 0; + break; + + case 2: + m_direction = KBDiagonalShotStrategy::LEFTDOWN; + m_column = m_fieldRect.width() - 1; + break; + + default: + m_direction = KBDiagonalShotStrategy::LEFTUP; + m_row = m_fieldRect.height() - 1; + break; + } + + m_child->init(field, field_rect); + m_child->startAt(m_column, m_row, m_direction); + + m_start = QPoint(m_column, m_row); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBDiagonalWrapStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_child != 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBDiagonalWrapStrategy::advance() +{ + switch(m_direction) + { + case KBDiagonalShotStrategy::RIGHTDOWN: + if(!advanceRightDown()) + return false; + break; + + case KBDiagonalShotStrategy::RIGHTUP: + if(!advanceRightUp()) + return false; + break; + + case KBDiagonalShotStrategy::LEFTDOWN: + if(!advanceLeftDown()) + return false; + break; + + case KBDiagonalShotStrategy::LEFTUP: + if(!advanceLeftUp()) + return false; + break; + + default: + break; + } + + return true; +} + +bool KBDiagonalWrapStrategy::hasMoreShots() +{ + if(m_parent == 0 && !m_destroying && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + + if(!m_child->hasMoreShots()) + return advance(); + + return true; +} + +void KBDiagonalWrapStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + + if(m_child != 0) + m_child->shotAt(pos); +} + +bool KBDiagonalWrapStrategy::advanceRightDown() +{ + int col; + + if(m_column == 0 && m_row != 0) + { + // start next + m_row = 0; + m_column = (m_start.x() + 3) % m_fieldRect.width(); + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + col = 0; + m_child->startAt(col, m_row, m_direction); + while(!m_child->hasMoreShots()) + { + col++; + if(col >= m_fieldRect.width()) + return false; + + m_child->startAt(col, m_row, m_direction); + } + m_column = col; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_column = 0; + m_row = m_child->endPoint().y(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceRightUp() +{ + int row; + + if(m_row == (m_fieldRect.height() - 1) && m_column != 0) + { + // start next + m_column = 0; + m_row = m_start.y() - 3; + m_row = m_row < 0 ? m_row + m_fieldRect.height() : m_row; + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + row = m_fieldRect.height() - 1; + m_child->startAt(m_column, row, m_direction); + while(!m_child->hasMoreShots()) + { + row--; + if(row < 0) + return false; + + m_child->startAt(m_column, row, m_direction); + } + m_row = row; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_row = m_fieldRect.height() - 1; + m_column = m_child->endPoint().x(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceLeftDown() +{ + int row; + + if(m_row == 0 && m_column != (m_fieldRect.width()-1)) + { + // start next + m_column = m_fieldRect.width() - 1; + m_row = (m_start.y() + 3) % m_fieldRect.height(); + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + row = 0; + m_child->startAt(m_column, row, m_direction); + while(!m_child->hasMoreShots()) + { + row++; + if(row >= m_fieldRect.height()) + return false; + + m_child->startAt(m_column, row, m_direction); + } + m_row = row; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_row = 0; + m_column = m_child->endPoint().x(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} + +bool KBDiagonalWrapStrategy::advanceLeftUp() +{ + int col; + + if(m_column == (m_fieldRect.width()-1) && m_row != (m_fieldRect.height()-1)) + { + // start next + m_row = m_fieldRect.height() - 1; + m_column = m_start.x() - 3; + m_column = m_column < 0 ? m_column + m_fieldRect.width() : m_column; + + m_child->startAt(m_column, m_row, m_direction); + + if(!m_child->hasMoreShots()) + { + col = m_fieldRect.width() - 1; + m_child->startAt(col, m_row, m_direction); + while(!m_child->hasMoreShots()) + { + col--; + if(col < 0) + return false; + + m_child->startAt(col, m_row, m_direction); + } + m_column = col; + } + m_start = QPoint(m_column, m_row); + } + else + { + //wrap; + m_column = m_fieldRect.width() - 1; + m_row = m_child->endPoint().y(); + + m_child->startAt(m_column, m_row, m_direction); + } + + return true; +} diff --git a/kbattleship/kbattleship/kbdiagonalwrapstrategy.h b/kbattleship/kbattleship/kbdiagonalwrapstrategy.h new file mode 100644 index 00000000..06ff8f6a --- /dev/null +++ b/kbattleship/kbattleship/kbdiagonalwrapstrategy.h @@ -0,0 +1,53 @@ +/*************************************************************************** + kbdiagonalwrapstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBDIAGONALWRAPSTRATEGY_H +#define KBDIAGONALWRAPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" +#include "kbdiagonalshotstrategy.h" + +class KBDiagonalWrapStrategy : public KBStrategy +{ +public: + KBDiagonalWrapStrategy(KBStrategy *parent = 0); + virtual ~KBDiagonalWrapStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + bool advanceRightDown(); + bool advanceRightUp(); + bool advanceLeftDown(); + bool advanceLeftUp(); + + int m_row; + int m_column; + + QPoint m_start; + KBDiagonalShotStrategy *m_child; + KBDiagonalShotStrategy::Direction m_direction; + + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp b/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp new file mode 100644 index 00000000..080bc08e --- /dev/null +++ b/kbattleship/kbattleship/kbhorizontalstepstrategy.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + kbhorizontalstepstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbhorizontalstepstrategy.h" + +KBHorizontalStepStrategy::KBHorizontalStepStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = 0; + if(parent == 0) // only the master destroys ships + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBHorizontalStepStrategy::~KBHorizontalStepStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBHorizontalStepStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + m_start = QPoint(m_column, m_row); + m_passes = 0; + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBHorizontalStepStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_passes == 0) + return QPoint(m_column, m_row); + else if(m_parent == 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBHorizontalStepStrategy::advance() +{ + int col, row; + + col = m_column; + row = m_row; + + while(enemyFieldStateAt(col, row) == KBStrategy::SHOT) + { + col += 2; + if(col >= m_fieldRect.width()) + { + col = m_column % 2; + row = (row + 2) % m_fieldRect.height(); + } + + if(col == m_start.x() && row == m_start.y()) + { + col = (col + 1) % m_fieldRect.width(); + row = (row + 1) % m_fieldRect.height(); + } + } + + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + { + m_column = col; + m_row = row; + return true; + } + + return false; +} + +void KBHorizontalStepStrategy::setStart(int col, int row) +{ + m_start = QPoint(col, row); + m_column = col; + m_row = row; +} + +bool KBHorizontalStepStrategy::hasMoreShots() +{ + if(m_parent != 0) + { + // Child Strategy + if(m_passes == 0) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if(advance()) + return true; + else + { + m_passes++; + return false; + } + } + else + return false; + } + else + { + // Parent Strategy + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + int x, y; + switch(m_passes) + { + case 0: + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT || advance()) + return true; + + m_passes++; + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 1: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = m_start.y(); + m_child->setStart(x, y); + + case 2: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBHorizontalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 2) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 3: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + + default: + return false; + } +} + +void KBHorizontalStepStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + if(m_child != 0) + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbhorizontalstepstrategy.h b/kbattleship/kbattleship/kbhorizontalstepstrategy.h new file mode 100644 index 00000000..4e68e4e7 --- /dev/null +++ b/kbattleship/kbattleship/kbhorizontalstepstrategy.h @@ -0,0 +1,48 @@ +/*************************************************************************** + kbhorizontalstepstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBHORIZONTALSTEPSTRATEGY_H +#define KBHORIZONTALSTEPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBHorizontalStepStrategy : public KBStrategy +{ +public: + KBHorizontalStepStrategy(KBStrategy *parent = 0); + virtual ~KBHorizontalStepStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + void setStart(int col, int row); + + int m_row; + int m_column; + int m_passes; + + QPoint m_start; + KBHorizontalStepStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbrandomshotstrategy.cpp b/kbattleship/kbattleship/kbrandomshotstrategy.cpp new file mode 100644 index 00000000..a3748a69 --- /dev/null +++ b/kbattleship/kbattleship/kbrandomshotstrategy.cpp @@ -0,0 +1,102 @@ +/*************************************************************************** + kbrandomshotstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbrandomshotstrategy.h" + +KBRandomShotStrategy::KBRandomShotStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; +} + +KBRandomShotStrategy::~KBRandomShotStrategy() +{ + delete m_destroyer; +} + +void KBRandomShotStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBRandomShotStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(advance()) + return QPoint(m_column, m_row); + } + + return QPoint(0, 0); +} + +bool KBRandomShotStrategy::advance() +{ + while(enemyFieldStateAt(m_column, m_row) == KBStrategy::SHOT) + { + m_column = m_randomSeq.getLong(m_fieldRect.width()); + m_row = m_randomSeq.getLong(m_fieldRect.height()); + } + return true; +} + +bool KBRandomShotStrategy::hasMoreShots() +{ + if(m_parent == 0) + { + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + } + + for(int row = 0; row < m_fieldRect.height(); row++) + { + for(int col = 0; col < m_fieldRect.width(); col++) + { + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + return true; + } + } + + return false; +} + +void KBRandomShotStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} diff --git a/kbattleship/kbattleship/kbrandomshotstrategy.h b/kbattleship/kbattleship/kbrandomshotstrategy.h new file mode 100644 index 00000000..1e93cefa --- /dev/null +++ b/kbattleship/kbattleship/kbrandomshotstrategy.h @@ -0,0 +1,47 @@ +/*************************************************************************** + kbrandomshotstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBRANDOMSHOTSTRATEGY_H +#define KBRANDOMSHOTSTRATEGY_H + +#include + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBRandomShotStrategy : public KBStrategy +{ +public: + KBRandomShotStrategy(KBStrategy *parent = 0); + virtual ~KBRandomShotStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + + int m_row; + int m_column; + + KRandomSequence m_randomSeq; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kbstrategy.cpp b/kbattleship/kbattleship/kbstrategy.cpp new file mode 100644 index 00000000..f8183cfd --- /dev/null +++ b/kbattleship/kbattleship/kbstrategy.cpp @@ -0,0 +1,108 @@ +/*************************************************************************** + kbstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kbstrategy.h" + +KBStrategy::KBStrategy(KBStrategy *parent) +{ + m_parent = parent; + m_viableShots = 0; +} + +KBStrategy::~KBStrategy() +{ + while ( !m_prevShots.empty() ) + { + m_prevShots.remove( m_prevShots.last() ); + } + if (m_parent == 0 && m_viableShots != 0) + { + delete[] m_viableShots; + } +} + +/* Returns the master strategy's shot list. */ +QValueList KBStrategy::masterShotList() +{ + return (!m_parent) ? m_prevShots : m_parent->masterShotList(); +} + +/* the AI player decided to shoot at pos */ +void KBStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); +} + +void KBStrategy::init(KBattleField *field, const QRect &field_rect) +{ + m_battleField = field; + m_fieldRect = field_rect; + if (!m_parent) + { + if (m_viableShots == 0) + { + m_viableShots = new bool[(field_rect.width()*field_rect.height())]; + } + for (int x = 0; x < field_rect.width(); ++x) + { + for (int y = 0; y < field_rect.height(); ++y) + { + //m_viableShots[x, y] = true; + setViablePos(x, y, true); + } + } + } + else + { + m_viableShots = m_parent->getViableShots(); + } +} + +/* Returns the field type of position (x, y) on the user player's field */ +int KBStrategy::enemyFieldStateAt(int x, int y) +{ + if (!isViablePos(x, y)) + return SHOT; // faking SHOT if position is not possible ship position + + switch(m_battleField->ownState(x, y)) + { + case KBattleField::FREE: + return KBStrategy::FREE; + case KBattleField::WATER: + case KBattleField::HIT: + case KBattleField::DEATH: + return KBStrategy::SHOT; + default: + return KBStrategy::SHIP; + } +} + +bool* KBStrategy::getViableShots() +{ + return m_viableShots; +} + +bool KBStrategy::isViablePos(int x, int y) +{ + return m_viableShots[(m_fieldRect.width()*y + x)]; +} + +void KBStrategy::setViablePos(int x, int y, bool viable) +{ + m_viableShots[(m_fieldRect.width()*y + x)] = viable; +} + diff --git a/kbattleship/kbattleship/kbstrategy.h b/kbattleship/kbattleship/kbstrategy.h new file mode 100644 index 00000000..55707a17 --- /dev/null +++ b/kbattleship/kbattleship/kbstrategy.h @@ -0,0 +1,52 @@ +/*************************************************************************** + kbstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBSTRATEGY_H +#define KBSTRATEGY_H + +#include +#include +#include "kbattlefield.h" + +class KBStrategy +{ +public: + enum{FREE, SHOT, SHIP}; + KBStrategy(KBStrategy *parent = 0); + virtual ~KBStrategy(); + + virtual const QPoint nextShot() = 0; + virtual void shotAt(const QPoint &pos); + virtual void init(KBattleField *field, const QRect &field_rect); + virtual bool hasMoreShots() = 0; + +protected: + QValueList masterShotList(); + int enemyFieldStateAt(int x, int y); + bool* getViableShots(); + + QRect m_fieldRect; + bool* m_viableShots; + bool isViablePos(int x, int y); + void setViablePos(int x, int y, bool viable); + QValueList m_prevShots; + + KBattleField *m_battleField; + KBStrategy *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kbverticalstepstrategy.cpp b/kbattleship/kbattleship/kbverticalstepstrategy.cpp new file mode 100644 index 00000000..736e9ac8 --- /dev/null +++ b/kbattleship/kbattleship/kbverticalstepstrategy.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + kbverticalstepstrategy.cpp + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kbverticalstepstrategy.h" + +KBVerticalStepStrategy::KBVerticalStepStrategy(KBStrategy *parent) : KBStrategy(parent) +{ + m_child = 0; + + if(parent == 0) // only the master destroys ships + { + m_destroyer = new KBDestroyShipStrategy(this); + m_destroying = false; + } + else + { + m_destroyer = 0; + m_destroying = false; + } +} + +KBVerticalStepStrategy::~KBVerticalStepStrategy() +{ + delete m_child; + delete m_destroyer; +} + +void KBVerticalStepStrategy::init(KBattleField *field, const QRect &field_rect) +{ + KBStrategy::init(field, field_rect); + KRandomSequence rand; + m_column = (int) rand.getLong(m_fieldRect.width()); + m_row = (int) rand.getLong(m_fieldRect.height()); + m_start = QPoint(m_column, m_row); + m_passes = 0; + + if(m_destroyer != 0) + m_destroyer->init(field, field_rect); +} + +const QPoint KBVerticalStepStrategy::nextShot() +{ + if(hasMoreShots()) + { + if(m_destroying) + return m_destroyer->nextShot(); + else if(m_passes == 0) + return QPoint(m_column, m_row); + else if(m_parent == 0) + return m_child->nextShot(); + } + + return m_start; +} + +bool KBVerticalStepStrategy::advance() +{ + int col, row; + + col = m_column; + row = m_row; + + while(enemyFieldStateAt(col, row) == KBStrategy::SHOT) + { + row += 2; + if(row >= m_fieldRect.height()) + { + row = m_row % 2; + col = (col + 2) % m_fieldRect.width(); + } + + if(col == m_start.x() && row == m_start.y()) + { + col = (col + 1) % m_fieldRect.width(); + row = (row + 1) % m_fieldRect.height(); + } + } + + if(enemyFieldStateAt(col, row) != KBStrategy::SHOT) + { + m_column = col; + m_row = row; + return true; + } + + return false; +} + +void KBVerticalStepStrategy::setStart(int col, int row) +{ + m_start = QPoint(col, row); + m_column = col; + m_row = row; +} + +bool KBVerticalStepStrategy::hasMoreShots() +{ + if(m_parent != 0) + { + // Child Strategy + if(m_passes == 0) + { + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if(advance()) + return true; + else + { + m_passes++; + return false; + } + } + else + return false; + } + else + { + // Parent Strategy + if((!m_destroying) && m_prevShots.count() > 0) + { + QPoint pos = m_prevShots.last(); + int state = m_battleField->ownState(pos.x(), pos.y()); + if(state == KBattleField::HIT) + { + m_destroying = true; + m_destroyer->destroyShipAt(pos); + } + } + if(m_destroying) + { + if(m_destroyer->hasMoreShots()) + return true; + else + m_destroying = false; + } + + int x, y; + switch(m_passes) + { + case 0: + if(enemyFieldStateAt(m_column, m_row) != KBStrategy::SHOT) + return true; + else if (advance()) + return true; + + m_passes++; + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 1: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = m_start.x(); + y = (m_start.y() + 1) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 2: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + delete m_child; + + m_child = new KBVerticalStepStrategy(this); + m_child->init(m_battleField, m_fieldRect); + + x = (m_start.x() + 1) % m_fieldRect.width(); + y = (m_start.y() + 2) % m_fieldRect.height(); + m_child->setStart(x, y); + + case 3: + if(m_child->hasMoreShots()) + return true; + + m_passes++; + + default: + return false; + } + } +} + +void KBVerticalStepStrategy::shotAt(const QPoint &pos) +{ + m_prevShots.append(pos); + if(m_child != 0) + m_child->shotAt(pos); +} diff --git a/kbattleship/kbattleship/kbverticalstepstrategy.h b/kbattleship/kbattleship/kbverticalstepstrategy.h new file mode 100644 index 00000000..904fa68e --- /dev/null +++ b/kbattleship/kbattleship/kbverticalstepstrategy.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kbverticalstepstrategy.h + ---------- + Developers: (c) 2001 Kevin Krammer + (c) 2001 Nikolas Zimmermann + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KBVERTICALSTEPSTRATEGY_H +#define KBVERTICALSTEPSTRATEGY_H + +#include "kbstrategy.h" +#include "kbdestroyshipstrategy.h" + +class KBVerticalStepStrategy : public KBStrategy +{ +public: + KBVerticalStepStrategy(KBStrategy *parent = 0); + virtual ~KBVerticalStepStrategy(); + + virtual void init(KBattleField *field, const QRect &field_rect); + virtual const QPoint nextShot(); + virtual bool hasMoreShots(); + virtual void shotAt(const QPoint &pos); + +private: + bool advance(); + void setStart(int col, int row); + + int m_row; + int m_column; + int m_passes; + + QPoint m_start; + KBVerticalStepStrategy *m_child; + KBDestroyShipStrategy *m_destroyer; + bool m_destroying; +}; + +#endif diff --git a/kbattleship/kbattleship/kchatwidget.cpp b/kbattleship/kbattleship/kchatwidget.cpp new file mode 100644 index 00000000..6c4755c6 --- /dev/null +++ b/kbattleship/kbattleship/kchatwidget.cpp @@ -0,0 +1,83 @@ +/*************************************************************************** + kchatwidget.cpp + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "kchatwidget.moc" + +KChatWidget::KChatWidget(QWidget *parent, const char *name) : chatDlg(parent, name) +{ + connect(sendBtn, SIGNAL(clicked()), this, SLOT(slotComputeMessage())); + connect(commentEdit, SIGNAL(returnPressed()), this, SLOT(slotComputeMessage())); + chatView->setFocusProxy(commentEdit); + chatView->setMinimumSize(0, 50); + commentEdit->installEventFilter(this); + + m_currentNickname = QString::null; + slotAcceptMsg(false); +} + +void KChatWidget::clear() +{ + m_currentNickname = QString::null; + slotAcceptMsg(false); + chatView->clear(); + commentEdit->clear(); +} + +void KChatWidget::slotAcceptMsg(bool value) +{ + m_acceptMsgs = value; +} + +void KChatWidget::slotReceivedMessage(const QString &nickname, const QString &msg, bool fromenemy) +{ + // Niko Z: + // IRC roxxx :) + if(msg.startsWith("/me ")) + chatView->append(QString(" * ") + nickname + QString(" ") + msg.mid(4)); + else if(msg.startsWith("/nick ")) + if(fromenemy) + emit sigChangeEnemyNickname(msg.mid(6)); + else + emit sigChangeOwnNickname(msg.mid(6)); + else + chatView->append(nickname + QString(": ") + msg); + chatView->setCursorPosition(chatView->numLines(), 0); +} + +bool KChatWidget::eventFilter(QObject *obj, QEvent *e) +{ + if(obj == commentEdit && e->type() == QEvent::Wheel) + { + kapp->notify(chatView, e); + return true; + } + return chatDlg::eventFilter(obj, e); +} + +void KChatWidget::slotComputeMessage() +{ + if(!commentEdit->text().stripWhiteSpace().isEmpty() && m_acceptMsgs) + { + slotReceivedMessage(m_currentNickname, commentEdit->text(), false); + emit sigSendMessage(commentEdit->text()); + commentEdit->setText(""); + } + else if(commentEdit->text().stripWhiteSpace().isEmpty() && m_acceptMsgs) + commentEdit->setText(""); + commentEdit->setFocus(); +} diff --git a/kbattleship/kbattleship/kchatwidget.h b/kbattleship/kbattleship/kchatwidget.h new file mode 100644 index 00000000..e9a756a7 --- /dev/null +++ b/kbattleship/kbattleship/kchatwidget.h @@ -0,0 +1,53 @@ +/*************************************************************************** + kchatwidget.h + --------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KCHATWIDGET_H +#define KCHATWIDGET_H + +#include +#include +#include +#include "dialogs/chatDlg.h" +#include "kmessage.h" + +class KChatWidget : public chatDlg +{ + Q_OBJECT +public: + KChatWidget(QWidget *parent = 0, const char *name = 0); + + void clear(); + void setNickname(const QString &nickname) { m_currentNickname = nickname; } + +public slots: + void slotAcceptMsg(bool value); + void slotComputeMessage(); + void slotReceivedMessage(const QString &nickname, const QString &msg, bool fromenemy = true); + +signals: + void sigSendMessage(const QString &); + void sigChangeEnemyNickname(const QString &); + void sigChangeOwnNickname(const QString &); + +private: + virtual bool eventFilter(QObject *, QEvent *); + + QString m_currentNickname; + bool m_acceptMsgs; +}; + +#endif diff --git a/kbattleship/kbattleship/kclientdialog.cpp b/kbattleship/kbattleship/kclientdialog.cpp new file mode 100644 index 00000000..78fc04de --- /dev/null +++ b/kbattleship/kbattleship/kclientdialog.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + kclientdialog.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "kbattleshipserver.h" // for BATTLESHIP_SERVICE +#include "kclientdialog.moc" + +KClientDialog::KClientDialog(QWidget *parent, const char *name) + : KDialogBase(Plain, i18n("Connect to Server"), Ok|Cancel, Ok, parent, name, true, false, KGuiItem(i18n("&Connect"))) +{ + QFrame* page = plainPage(); + QGridLayout* pageLayout = new QGridLayout(page, 1, 1, 0, 0); + m_mainWidget = new clientConnectDlg(page); + pageLayout->addWidget(m_mainWidget, 0, 0); + + enableButtonOK(false); + m_config = kapp->config(); + KUser u; + m_mainWidget->nicknameEdit->setText(u.loginName()); + + connect(m_mainWidget->serverEdit, SIGNAL(returnPressed(const QString &)), this, SLOT(slotReturnPressed(const QString &))); + connect(m_mainWidget->serverEdit, SIGNAL(textChanged(const QString &)), this, SLOT(slotCheckEnableOk())); + + m_config->setGroup("History"); + m_browser = new DNSSD::ServiceBrowser(QString::fromLatin1(BATTLESHIP_SERVICE)); + connect(m_browser,SIGNAL(finished()),SLOT(nextBatch())); + m_browser->startBrowse(); + connect(m_mainWidget->lanBox,SIGNAL(activated(int)),SLOT(gameSelected(int))); + m_mainWidget->serverEdit->completionObject()->setItems(m_config->readListEntry("CompletionList")); + + m_mainWidget->serverEdit->setMaxCount(5); + m_mainWidget->serverEdit->setHistoryItems(m_config->readListEntry("HistoryList")); + + m_mainWidget->serverEdit->setCurrentItem(m_config->readNumEntry("Index", -1)); +} + +KClientDialog::~KClientDialog() +{ + m_config->setGroup("History"); + m_config->writeEntry("CompletionList", m_mainWidget->serverEdit->completionObject()->items()); + m_config->writeEntry("HistoryList", m_mainWidget->serverEdit->historyItems()); + m_config->writeEntry("Index", m_mainWidget->serverEdit->currentItem()); + m_config->sync(); +} + +void KClientDialog::slotCheckEnableOk() +{ + enableButtonOK(!m_mainWidget->serverEdit->currentText().stripWhiteSpace().isEmpty()); +} + +void KClientDialog::slotOk() +{ + QString server = m_mainWidget->serverEdit->currentText().stripWhiteSpace(); + if(!server.isEmpty()) + { + hide(); + m_mainWidget->serverEdit->addToHistory(server); + emit sigConnectServer(); + } + else + m_mainWidget->serverEdit->clearEdit(); +} + +void KClientDialog::slotReturnPressed(const QString &hostname) +{ + if(!hostname.stripWhiteSpace().isEmpty()) + m_mainWidget->serverEdit->addToHistory(hostname); + else + m_mainWidget->serverEdit->clearEdit(); +} + +void KClientDialog::slotCancel() +{ + hide(); + emit sigCancelConnect(); +} + +QString KClientDialog::port() const +{ + return QString::number(m_mainWidget->portEdit->value()); +} + +QString KClientDialog::host() const +{ + return m_mainWidget->serverEdit->currentText(); +} + +QString KClientDialog::nickname() const +{ + return m_mainWidget->nicknameEdit->text(); +} + +void KClientDialog::nextBatch() +{ + bool autoselect=false; + if (!m_mainWidget->lanBox->count()) autoselect=true; + m_mainWidget->lanBox->clear(); + QStringList names; + QValueList::ConstIterator itEnd = m_browser->services().end(); + for (QValueList::ConstIterator it = m_browser->services().begin(); + it!=itEnd; ++it) names << (*it)->serviceName(); + m_mainWidget->lanBox->insertStringList(names); + if (autoselect && m_mainWidget->lanBox->count()) gameSelected(0); +} + +void KClientDialog::gameSelected(int i) +{ + Q_ASSERT(i < m_browser->services().count()); if( i >= m_browser->services().count()) { slotCheckEnableOk(); return; } + + DNSSD::RemoteService::Ptr srv = m_browser->services()[i]; + + Q_ASSERT(srv); if(!srv) { slotCheckEnableOk(); return; } + + if (!srv->isResolved() && !srv->resolve()) return; + m_mainWidget->serverEdit->setCurrentItem(srv->hostName(),true); + m_mainWidget->portEdit->setValue(srv->port()); + slotCheckEnableOk(); +} + + diff --git a/kbattleship/kbattleship/kclientdialog.h b/kbattleship/kbattleship/kclientdialog.h new file mode 100644 index 00000000..e3dbf341 --- /dev/null +++ b/kbattleship/kbattleship/kclientdialog.h @@ -0,0 +1,61 @@ +/*************************************************************************** + kclientdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KCLIENTDIALOG_H +#define KCLIENTDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dialogs/connectDlg.h" + +class KClientDialog : public KDialogBase +{ + Q_OBJECT +public: + KClientDialog(QWidget *parent = 0, const char *name = 0); + ~KClientDialog(); + + QString port() const; + QString host() const; + QString nickname() const; + +public slots: + virtual void slotOk(); + virtual void slotCancel(); + void slotReturnPressed(const QString &hostname); + void nextBatch(); + void gameSelected(int); + void slotCheckEnableOk(); + +signals: + void sigConnectServer(); + void sigCancelConnect(); + +private: + KConfig *m_config; + DNSSD::ServiceBrowser *m_browser; + clientConnectDlg *m_mainWidget; +}; + +#endif diff --git a/kbattleship/kbattleship/kgridwidget.cpp b/kbattleship/kbattleship/kgridwidget.cpp new file mode 100644 index 00000000..49a6d106 --- /dev/null +++ b/kbattleship/kbattleship/kgridwidget.cpp @@ -0,0 +1,416 @@ +/*************************************************************************** + kgridwidget.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include +#include +#include + +#include "kbattlefield.h" +#include "kgridwidget.h" + +KGridWidget::KGridWidget(QWidget *parent, bool draw) : m_drawGrid(draw) +{ + m_doubleBuffer = new QPixmap(parent->width(), parent->height()); + m_parent = parent; + + cleanBuffer(); + cacheImages(); +} + +KGridWidget::~KGridWidget() +{ + delete m_doubleBuffer; +} + +void KGridWidget::cacheImages() +{ + seaPng = QPixmap(findIcon("sea.png")); + waterPng = QPixmap(findIcon("water.png")); + hitPng = QPixmap(findIcon("hit.png")); + borderPng = QPixmap(findIcon("border.png")); + deathPng = QPixmap(findIcon("death.png")); + ship1p1Png = QPixmap(findIcon("ship1-1.png")); + ship1p1rPng = QPixmap(findIcon("ship1-1-r.png")); + ship2p1Png = QPixmap(findIcon("ship2-1.png")); + ship2p1rPng = QPixmap(findIcon("ship2-1-r.png")); + ship2p2Png = QPixmap(findIcon("ship2-2.png")); + ship2p2rPng = QPixmap(findIcon("ship2-2-r.png")); + ship3p1Png = QPixmap(findIcon("ship3-1.png")); + ship3p1rPng = QPixmap(findIcon("ship3-1-r.png")); + ship3p2Png = QPixmap(findIcon("ship3-2.png")); + ship3p2rPng = QPixmap(findIcon("ship3-2-r.png")); + ship3p3Png = QPixmap(findIcon("ship3-3.png")); + ship3p3rPng = QPixmap(findIcon("ship3-3-r.png")); + ship4p1Png = QPixmap(findIcon("ship4-1.png")); + ship4p1rPng = QPixmap(findIcon("ship4-1-r.png")); + ship4p2Png = QPixmap(findIcon("ship4-2.png")); + ship4p2rPng = QPixmap(findIcon("ship4-2-r.png")); + ship4p3Png = QPixmap(findIcon("ship4-3.png")); + ship4p3rPng = QPixmap(findIcon("ship4-3-r.png")); + ship4p4Png = QPixmap(findIcon("ship4-4.png")); + ship4p4rPng = QPixmap(findIcon("ship4-4-r.png")); +} + +void KGridWidget::setValues(int x, int y, int size) +{ + m_x = x; + m_y = y; + m_size = size; +} + +void KGridWidget::drawSquare() +{ + drawIcon(seaPng); +} + +void KGridWidget::drawWaterIcon() +{ + drawIcon(waterPng); +} + +void KGridWidget::drawHitIcon() +{ + drawIcon(hitPng); +} + +void KGridWidget::drawDeathBorder() +{ + drawIcon(borderPng); +} +void KGridWidget::drawDeathIcon() +{ + drawIcon(deathPng); +} + +void KGridWidget::drawShipIcon(int type, bool rotate, bool hit, bool water) +{ + int ship = 0; + int part = 0; + + switch(type) + { + case KBattleField::SHIP1P1: + ship = 1; + part = 1; + break; + + case KBattleField::SHIP2P1: + ship = 2; + if(!rotate) + part = 1; + else + part = 2; + break; + + case KBattleField::SHIP2P2: + ship = 2; + if(!rotate) + part = 2; + else + part = 1; + break; + + case KBattleField::SHIP3P1: + ship = 3; + if(!rotate) + part = 1; + else + part = 3; + break; + + case KBattleField::SHIP3P2: + ship = 3; + if(!rotate) + part = 2; + else + part = 2; + break; + + case KBattleField::SHIP3P3: + ship = 3; + if(!rotate) + part = 3; + else + part = 1; + break; + + case KBattleField::SHIP4P1: + ship = 4; + if(!rotate) + part = 1; + else + part = 4; + break; + + case KBattleField::SHIP4P2: + ship = 4; + if(!rotate) + part = 2; + else + part = 3; + break; + + case KBattleField::SHIP4P3: + ship = 4; + if(!rotate) + part = 3; + else + part = 2; + break; + + case KBattleField::SHIP4P4: + ship = 4; + if(!rotate) + part = 4; + else + part = 1; + break; + } + + switch(ship) + { + case 1: + if(!rotate) + drawIcon(ship1p1Png, hit, water); + else + drawIcon(ship1p1rPng, hit, water, rotate); + break; + + case 2: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship2p1Png, hit, water); + else + drawIcon(ship2p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship2p2Png, hit, water); + else + drawIcon(ship2p2rPng, hit, water, rotate); + break; + } + break; + + case 3: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship3p1Png, hit, water); + else + drawIcon(ship3p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship3p2Png, hit, water); + else + drawIcon(ship3p2rPng, hit, water, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship3p3Png, hit, water); + else + drawIcon(ship3p3rPng, hit, water, rotate); + break; + } + break; + + case 4: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship4p1Png, hit, water); + else + drawIcon(ship4p1rPng, hit, water, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship4p2Png, hit, water); + else + drawIcon(ship4p2rPng, hit, water, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship4p3Png, hit, water); + else + drawIcon(ship4p3rPng, hit, water, rotate); + break; + + case 4: + if(!rotate) + drawIcon(ship4p4Png, hit, water); + else + drawIcon(ship4p4rPng, hit, water, rotate); + break; + } + break; + } +} + +void KGridWidget::drawShipIcon(int ship, int part, bool rotate, bool hit) +{ + switch(ship) + { + case 1: + if(!rotate) + drawIcon(ship1p1Png, hit); + else + drawIcon(ship1p1rPng, hit, false, rotate); + break; + + case 2: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship2p1Png, hit); + else + drawIcon(ship2p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship2p2Png, hit); + else + drawIcon(ship2p2rPng, hit, false, rotate); + break; + } + break; + + case 3: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship3p1Png, hit); + else + drawIcon(ship3p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship3p2Png, hit); + else + drawIcon(ship3p2rPng, hit, false, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship3p3Png, hit); + else + drawIcon(ship3p3rPng, hit, false, rotate); + break; + } + break; + + case 4: + switch(part) + { + case 1: + if(!rotate) + drawIcon(ship4p1Png, hit); + else + drawIcon(ship4p1rPng, hit, false, rotate); + break; + + case 2: + if(!rotate) + drawIcon(ship4p2Png, hit); + else + drawIcon(ship4p2rPng, hit, false, rotate); + break; + + case 3: + if(!rotate) + drawIcon(ship4p3Png, hit); + else + drawIcon(ship4p3rPng, hit, false, rotate); + break; + + case 4: + if(!rotate) + drawIcon(ship4p4Png, hit); + else + drawIcon(ship4p4rPng, hit, false, rotate); + break; + } + break; + } +} + +void KGridWidget::drawIcon(const QPixmap &icon, bool hitBlend, bool waterBlend, bool rotate) +{ + QPainter painter; + painter.begin(m_doubleBuffer); + if(!hitBlend && waterBlend) + { + QImage first = icon.convertToImage(); + QImage second = seaPng.convertToImage(); + painter.drawPixmap(m_x, m_y, seaPng); + if(rotate) + painter.drawImage(m_x, m_y, KImageEffect::blend(first, second, KImageEffect::VerticalGradient, 30, 30)); + else + painter.drawImage(m_x, m_y, KImageEffect::blend(first, second, KImageEffect::HorizontalGradient, 30, 30)); + + } + else if(hitBlend && !waterBlend) + { + painter.drawPixmap(m_x, m_y, icon); + painter.drawPixmap(m_x, m_y, hitPng); + } + else + painter.drawPixmap(m_x, m_y, icon); + + if(!m_drawGrid) + painter.end(); + else + { + painter.setBrush(Qt::NoBrush); + painter.setPen(Qt::black); + painter.drawRect(m_x, m_y, m_size, m_size); + painter.end(); + } +} + +QString KGridWidget::findIcon(const QString &name) const +{ + return locate("data", "kbattleship/pictures/" + name); +} + +void KGridWidget::finished() +{ + bitBlt(m_parent, 0, 0, m_doubleBuffer); + cleanBuffer(); +} + +void KGridWidget::cleanBuffer() +{ + m_doubleBuffer->fill(QApplication::palette().color(QPalette::Normal, QColorGroup::Background)); +} diff --git a/kbattleship/kbattleship/kgridwidget.h b/kbattleship/kbattleship/kgridwidget.h new file mode 100644 index 00000000..91f48eb0 --- /dev/null +++ b/kbattleship/kbattleship/kgridwidget.h @@ -0,0 +1,66 @@ +/*************************************************************************** + kgridwidget.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRIDWIDGET_H +#define KGRIDWIDGET_H + +#include + +class KGridWidget +{ +public: + KGridWidget(QWidget *parent, bool draw); + ~KGridWidget(); + + void enableGrid() { m_drawGrid = true; } + void disableGrid() { m_drawGrid = false; } + +protected: + void cleanBuffer(); + void finished(); + void setValues(int x, int y, int size); + void drawSquare(); + void drawWaterIcon(); + void drawDeathBorder(); + void drawDeathIcon(); + void drawHitIcon(); + void drawShipIcon(int type, bool rotate = false, bool hit = false, bool water = false); + void drawShipIcon(int ship, int part, bool rotate = false, bool hit = false); + +private: + void cacheImages(); + void drawIcon(const QPixmap &icon, bool hitBlend = false, bool waterBlend = false, bool rotate = false); + QString findIcon(const QString &name) const; + + bool m_drawGrid; + int m_x, m_y, m_size; + QPixmap *m_doubleBuffer; + QPixmap seaPng, waterPng, hitPng, borderPng,deathPng; + QPixmap ship1p1Png, ship1p1rPng; + QPixmap ship2p1Png, ship2p1rPng; + QPixmap ship2p2Png, ship2p2rPng; + QPixmap ship3p1Png, ship3p1rPng; + QPixmap ship3p2Png, ship3p2rPng; + QPixmap ship3p3Png, ship3p3rPng; + QPixmap ship4p1Png, ship4p1rPng; + QPixmap ship4p2Png, ship4p2rPng; + QPixmap ship4p3Png, ship4p3rPng; + QPixmap ship4p4Png, ship4p4rPng; + QWidget *m_parent; +}; + +#endif diff --git a/kbattleship/kbattleship/kmessage.cpp b/kbattleship/kbattleship/kmessage.cpp new file mode 100644 index 00000000..5eccacd2 --- /dev/null +++ b/kbattleship/kbattleship/kmessage.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + kmessage.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +//#define XMLDUMP + +#include + +#ifdef XMLDUMP +#include +#endif + +#include "kmessage.h" + +const char *clientName = I18N_NOOP("KBattleship"); +const char *clientVersion = "1.1"; +const char *clientDescription = I18N_NOOP("The KDE Battleship clone"); +const char *protocolVersion = "0.1.0"; + +KMessage::KMessage(int type) +{ + m_xmlDocument = QDomDocument("kmessage"); + m_xmlDocument.appendChild(m_xmlDocument.createElement("kmessage")); + m_messageType = type; + addField("msgtype", QString::number(type)); +} + +KMessage::KMessage(KMessage *msg) +{ + m_xmlDocument.setContent(msg->m_xmlDocument.toString(), false); + m_messageType = msg->type(); +} + +KMessage::KMessage() +{ + m_xmlDocument = QDomDocument("kmessage"); +} + +void KMessage::addField(const QString &name, const QString &content) +{ + QDomElement xmlElement = m_xmlDocument.createElement(name); + QDomText xmlText = m_xmlDocument.createTextNode(content); + xmlElement.appendChild(xmlText); + m_xmlDocument.documentElement().appendChild(xmlElement); +} + +void KMessage::setDataStream(const QString &stream) +{ + m_xmlDocument.setContent(stream); +#ifdef XMLDUMP + kdDebug() << "*** XML-IN ***" << endl << stream << endl << "*** END ***" << endl; +#endif +} + +QString KMessage::sendStream() const +{ +#ifdef XMLDUMP + kdDebug() << "*** XML OUT ***" << endl << m_xmlDocument.toString() << endl << "*** END ***" << endl; +#endif + return m_xmlDocument.toString(); +} + +QString KMessage::field(const QString &name) const +{ + QDomNode xmlNode = m_xmlDocument.documentElement().namedItem(name); + if(!xmlNode.isNull()) + return (xmlNode.toElement()).text(); + return QString::null; +} + +int KMessage::type() +{ + return field("msgtype").toInt(); +} + +void KMessage::chatMessage(const QString &nickname, const QString &message) +{ + addField("nickname", nickname); + addField("chat", message); +} + +void KMessage::versionMessage() +{ + addField("protocolVersion", protocolVersion); + addField("clientName", clientName); + addField("clientVersion", clientVersion); + addField("clientDescription", clientDescription); +} diff --git a/kbattleship/kbattleship/kmessage.h b/kbattleship/kbattleship/kmessage.h new file mode 100644 index 00000000..f1d6a0b5 --- /dev/null +++ b/kbattleship/kbattleship/kmessage.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kmessage.h + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KMESSAGE_H +#define KMESSAGE_H + +#include +#include + +class KMessage +{ +public: + enum{GETVERSION, DISCARD, GREET, SHIPSREADY, SHOOT, ANSWER_SHOOT, WON, REPLAY, CHAT}; + + KMessage(int type); + KMessage(KMessage *msg); + KMessage(); + + int type(); + + void addField(const QString &name, const QString &content); + QString field(const QString &name) const; + + void setDataStream(const QString &stream); + QString sendStream() const; + + void chatMessage(const QString &nickname, const QString &message); + void versionMessage(); + +private: + QDomDocument m_xmlDocument; + int m_messageType; +}; + +#endif diff --git a/kbattleship/kbattleship/konnectionhandling.cpp b/kbattleship/kbattleship/konnectionhandling.cpp new file mode 100644 index 00000000..bcfefe04 --- /dev/null +++ b/kbattleship/kbattleship/konnectionhandling.cpp @@ -0,0 +1,246 @@ +/**************************************MM************************************* + konnectionhandling.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "konnectionhandling.moc" + +extern const char *protocolVersion; + +KonnectionHandling::KonnectionHandling(QWidget *parent, KBattleshipServer *server) : QObject(parent) +{ + m_kbserver = server; + m_kbclient = 0; + m_type = KonnectionHandling::SERVER; + connect(server, SIGNAL(sigServerFailure()), this, SIGNAL(sigAbortNetworkGame())); + connect(server, SIGNAL(sigNewConnect()), this, SLOT(slotNewClient())); + connect(server, SIGNAL(sigEndConnect()), this, SLOT(slotLostClient())); + connect(server, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(server, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +KonnectionHandling::KonnectionHandling(QWidget *parent, KBattleshipClient *client) : QObject(parent) +{ + m_kbclient = client; + m_kbserver = 0; + m_type = KonnectionHandling::CLIENT; + connect(client, SIGNAL(sigEndConnect()), this, SLOT(slotLostServer())); + connect(client, SIGNAL(sigSocketFailure(int)), this, SLOT(slotSocketError(int))); + connect(client, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(client, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::updateInternal(KBattleshipServer *server) +{ + m_kbserver = server; + m_kbclient = 0; + m_type = KonnectionHandling::SERVER; + connect(server, SIGNAL(sigServerFailure()), this, SIGNAL(sigAbortNetworkGame())); + connect(server, SIGNAL(sigNewConnect()), this, SLOT(slotNewClient())); + connect(server, SIGNAL(sigEndConnect()), this, SLOT(slotLostClient())); + connect(server, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(server, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::updateInternal(KBattleshipClient *client) +{ + m_kbclient = client; + m_kbserver = 0; + m_type = KonnectionHandling::CLIENT; + connect(client, SIGNAL(sigEndConnect()), this, SLOT(slotLostServer())); + connect(client, SIGNAL(sigSocketFailure(int)), this, SLOT(slotSocketError(int))); + connect(client, SIGNAL(sigNewMessage(KMessage *)), this, SLOT(slotNewMessage(KMessage *))); + connect(client, SIGNAL(sigMessageSent(KMessage *)), this, SLOT(slotMessageSent(KMessage *))); +} + +void KonnectionHandling::slotNewClient() +{ +} + +void KonnectionHandling::slotLostClient() +{ + KMessageBox::error(0L, i18n("Connection to client lost. Aborting the game.")); + emit sigClientLost(); +} + +void KonnectionHandling::slotMessageSent(KMessage *msg) +{ + if(msg->type() == KMessage::SHOOT) + emit sigShootable(false); + + delete msg; +} + +void KonnectionHandling::slotNewMessage(KMessage *msg) +{ + KMessage *copy; + if(type() == KonnectionHandling::CLIENT) + { + switch(msg->type()) + { + // First possible message + case KMessage::DISCARD: + if(msg->field("kmversion") == QString("true")) + { + KMessageBox::error(0L, i18n("Connection dropped by enemy. The client's protocol implementation (%1) is not compatible with our (%2) version.").arg(msg->field("reason")).arg(protocolVersion)); + emit sigAbortNetworkGame(); + } + else + KMessageBox::error(0L, i18n(msg->field("reason").latin1())); + break; + + // Got some informations + case KMessage::GETVERSION: + emit sigClientInformation(msg->field("clientName"), msg->field("clientVersion"), msg->field("clientDescription"), msg->field("protocolVersion")); + break; + + // Got the enemy's nickname + case KMessage::GREET: + emit sigEnemyNickname(msg->field("nickname")); + emit sigStatusBar(i18n("Waiting for other player to place their ships...")); + break; + + // The server wants ous to place the ships + case KMessage::SHIPSREADY: + emit sigPlaceShips(true); + emit sigStatusBar(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + break; + + // The server shot and wants the field state + case KMessage::SHOOT: + emit sigSendFieldState(msg->field("fieldx").toInt(), msg->field("fieldy").toInt()); + emit sigStatusBar(i18n("Enemy has shot. Shoot now.")); + emit sigShootable(true); + break; + + // The server gave us the field data of our last shot + case KMessage::ANSWER_SHOOT: + emit sigShootable(false); + emit sigEnemyFieldData(msg->field("fieldx").toInt(), msg->field("fieldy").toInt(), msg->field("fieldstate").toInt(), msg->field("xstart").toInt(), msg->field("xstop").toInt(), msg->field("ystart").toInt(), msg->field("ystop").toInt(), (msg->field("death") == QString("true"))); + break; + + // The server starts a new game + case KMessage::REPLAY: + emit sigStatusBar(i18n("Waiting for other player to place their ships...")); + emit sigReplay(); + break; + + // We lost the game + case KMessage::WON: + emit sigStatusBar(i18n("You lost the game :(")); + copy = new KMessage(msg); + emit sigLost(copy); + break; + + // We got a chat message + case KMessage::CHAT: + emit sigChatMessage(msg->field("nickname"), msg->field("chat"), true); + break; + } + } + else + { + switch(msg->type()) + { + // First message....got client information + case KMessage::GETVERSION: + if(msg->field("protocolVersion") != QString::fromLatin1(protocolVersion)) + { + m_kbserver->slotDiscardClient(protocolVersion, true, false); + KMessageBox::error(0L, i18n("Connection to client dropped. The client's protocol implementation (%1) is not compatible with our (%2) version.").arg(msg->field("protocolVersion")).arg(protocolVersion)); + } + else + emit sigClientInformation(msg->field("clientName"), msg->field("clientVersion"), msg->field("clientDescription"), msg->field("protocolVersion")); + break; + + // Got the enemy's nickname + case KMessage::GREET: + KMessageBox::information(0L, i18n("We have a player. Let's start...")); + emit sigStatusBar(i18n("Please place your ships. Use the \"Shift\" key to place the ships vertically.")); + emit sigEnemyNickname(msg->field("nickname")); + emit sigSendNickname(); + emit sigPlaceShips(true); + break; + + // The client placed his ships...we can shoot now + case KMessage::SHIPSREADY: + emit sigShootable(true); + emit sigStatusBar(i18n("You can shoot now.")); + break; + + // The client gave us the field data of our last shot + case KMessage::ANSWER_SHOOT: + emit sigShootable(false); + emit sigEnemyFieldData(msg->field("fieldx").toInt(), msg->field("fieldy").toInt(), msg->field("fieldstate").toInt(), msg->field("xstart").toInt(), msg->field("xstop").toInt(), msg->field("ystart").toInt(), msg->field("ystop").toInt(), (msg->field("death") == QString("true"))); + break; + + // The client shot and wants the field state + case KMessage::SHOOT: + emit sigSendFieldState(msg->field("fieldx").toInt(), msg->field("fieldy").toInt()); + emit sigStatusBar(i18n("Enemy has shot. Shoot now.")); + emit sigShootable(true); + break; + + // The client asks for a replay + case KMessage::REPLAY: + emit sigReplay(); + break; + + // We lost the game + case KMessage::WON: + emit sigStatusBar(i18n("You lost the game :(")); + copy = new KMessage(msg); + emit sigLost(copy); + break; + + // We got a chat message + case KMessage::CHAT: + emit sigChatMessage(msg->field("nickname"), msg->field("chat"), true); + break; + } + } + + delete msg; +} + +void KonnectionHandling::slotSocketError(int error) +{ + switch(error) + { + case IO_ConnectError: + KMessageBox::error(0L, i18n("Connection refused by other host.")); + break; + + case IO_LookupError: + KMessageBox::error(0L, i18n("Couldn't lookup host.")); + break; + + case IO_ReadError: + KMessageBox::error(0L, i18n("Couldn't connect to server.")); + break; + + default: + KMessageBox::error(0L, i18n("Unknown error; No: %1").arg(error)); + break; + } + + emit sigAbortNetworkGame(); +} + +void KonnectionHandling::slotLostServer() +{ + KMessageBox::error(0L, i18n("Connection to server lost. Aborting the game.")); + emit sigServerLost(); +} diff --git a/kbattleship/kbattleship/konnectionhandling.h b/kbattleship/kbattleship/konnectionhandling.h new file mode 100644 index 00000000..baa823df --- /dev/null +++ b/kbattleship/kbattleship/konnectionhandling.h @@ -0,0 +1,73 @@ +/*************************************************************************** + konnectionhandling.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KONNECTIONHANDLING_H +#define KONNECTIONHANDLING_H + +#include +#include + +#include + +#include "kbattleshipclient.h" +#include "kbattleshipserver.h" +#include "kmessage.h" + +class KonnectionHandling : public QObject +{ + Q_OBJECT +public: + enum{SERVER, CLIENT}; + KonnectionHandling(QWidget *parent, KBattleshipServer *server); + KonnectionHandling(QWidget *parent, KBattleshipClient *client); + + int type() { return m_type; } + + void updateInternal(KBattleshipServer *server); + void updateInternal(KBattleshipClient *client); + +public slots: + void slotNewMessage(KMessage *msg); + void slotMessageSent(KMessage *msg); + void slotNewClient(); + void slotLostClient(); + void slotLostServer(); + void slotSocketError(int error); + +signals: + void sigStatusBar(const QString &); + void sigEnemyNickname(const QString &); + void sigEnemyFieldData(int, int, int, int, int, int, int, bool); + void sigClientInformation(const QString &, const QString &, const QString &, const QString &); + void sigSendNickname(); + void sigSendFieldState(int, int); + void sigPlaceShips(bool); + void sigShootable(bool); + void sigClientLost(); + void sigServerLost(); + void sigReplay(); + void sigLost(KMessage *); + void sigAbortNetworkGame(); + void sigChatMessage(const QString &, const QString &, bool); + +private: + KBattleshipServer *m_kbserver; + KBattleshipClient *m_kbclient; + int m_type; +}; + +#endif diff --git a/kbattleship/kbattleship/kserverdialog.cpp b/kbattleship/kbattleship/kserverdialog.cpp new file mode 100644 index 00000000..3936bebf --- /dev/null +++ b/kbattleship/kbattleship/kserverdialog.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + kserverdialog.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include + +#include "kserverdialog.h" + +KServerDialog::KServerDialog(QWidget *parent, const char *name) : + KDialogBase(Plain, i18n("Start Server"), Ok|Cancel, Ok, parent, name, true, false, KGuiItem(i18n("&Start"))) +{ + QFrame* page = plainPage(); + QGridLayout* pageLayout = new QGridLayout(page, 1, 1, 0, 0); + m_mainWidget = new serverStartDlg(page); + pageLayout->addWidget(m_mainWidget, 0, 0); + + KUser u; + m_mainWidget->nicknameEdit->setText(u.loginName()); + + QString gamename = u.fullName(); + if(gamename.isEmpty()) gamename = u.loginName(); + m_mainWidget->gamenameEdit->setText(gamename); +} + +void KServerDialog::slotOk() +{ + hide(); + emit okClicked(); +} + +void KServerDialog::slotCancel() +{ + hide(); + emit cancelClicked(); +} + +QString KServerDialog::port() const +{ + return QString::number(m_mainWidget->portEdit->value()); +} + +QString KServerDialog::nickname() const +{ + return m_mainWidget->nicknameEdit->text(); +} + +QString KServerDialog::gamename() const +{ + return m_mainWidget->gamenameEdit->text(); +} + +#include "kserverdialog.moc" diff --git a/kbattleship/kbattleship/kserverdialog.h b/kbattleship/kbattleship/kserverdialog.h new file mode 100644 index 00000000..e6655c1a --- /dev/null +++ b/kbattleship/kbattleship/kserverdialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + kserverdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSERVERDIALOG_H +#define KSERVERDIALOG_H + +#include +#include +#include +#include + +#include + +#include "dialogs/serverDlg.h" + + +class KServerDialog : public KDialogBase +{ + Q_OBJECT +public: + KServerDialog(QWidget *parent = 0, const char *name = 0); + + QString port() const; + QString nickname() const; + QString gamename() const; + +public slots: + virtual void slotOk(); + virtual void slotCancel(); + +private: + serverStartDlg *m_mainWidget; +}; + +#endif diff --git a/kbattleship/kbattleship/kship.cpp b/kbattleship/kbattleship/kship.cpp new file mode 100644 index 00000000..a9fbb558 --- /dev/null +++ b/kbattleship/kbattleship/kship.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + kship.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kship.h" + +#include "kbattlefield.h" + +KShip::KShip(int _shipxstart, int _shipxstop, int _shipystart, int _shipystop, int _shiplength, bool _placedLeft) +{ + m_shipxstart = _shipxstart; + m_shipxstop = _shipxstop; + m_shipystart = _shipystart; + m_shipystop = _shipystop; + m_shiptype = _shiplength; + m_placedLeft = _placedLeft; +} + +int KShip::shipxstart() +{ + return m_shipxstart; +} + +int KShip::shipxstop() +{ + return m_shipxstop; +} + +int KShip::shipystart() +{ + return m_shipystart; +} + +int KShip::shipystop() +{ + return m_shipystop; +} + +int KShip::shiptype() +{ + return m_shiptype; +} + +bool KShip::placedLeft() +{ + return m_placedLeft; +} + +bool KShip::contains(int x, int y) +{ + return (x >= m_shipxstart && x <= m_shipxstop) && (y >= m_shipystart && y <= m_shipystop); +} + +int KShip::shipTypeEnum(int x, int y) +{ + int ret; + ret = KBattleField::WATER; + if (contains(x, y)) + { + switch(m_shiptype) + { + case 0: + ret = KBattleField::SHIP1P1; + break; + case 1: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP2P1; + else ret = KBattleField::SHIP2P2; + break; + case 2: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP3P1; + else if (x == m_shipxstop && y == m_shipystop) ret = KBattleField::SHIP3P3; + else ret = KBattleField::SHIP3P2; + break; + case 3: + if (x == m_shipxstart && y == m_shipystart) ret = KBattleField::SHIP4P1; + else if (x == m_shipxstop && y == m_shipystop) ret = KBattleField::SHIP4P4; + else if (placedLeft()) + { + if (x == m_shipxstart + 1 && y == m_shipystart) ret = KBattleField::SHIP4P2; + else ret = KBattleField::SHIP4P3; + } + else + { + if (x == m_shipxstart && y == m_shipystart + 1) ret = KBattleField::SHIP4P2; + else ret = KBattleField::SHIP4P3; + } + break; + } + } + return ret; +} diff --git a/kbattleship/kbattleship/kship.h b/kbattleship/kbattleship/kship.h new file mode 100644 index 00000000..df436c7a --- /dev/null +++ b/kbattleship/kbattleship/kship.h @@ -0,0 +1,46 @@ +/*************************************************************************** + kship.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSHIP_H +#define KSHIP_H + +class KShip +{ +public: + KShip(int, int, int, int, int, bool = false); + + int shipxstart(); + int shipxstop(); + int shipystart(); + int shipystop(); + int shiptype(); + int shipTypeEnum(int x, int y); + + bool placedLeft(); + bool contains(int x, int y); + +private: + int m_shipxstart; + int m_shipxstop; + int m_shipystart; + int m_shipystop; + int m_shiptype; + bool m_placedLeft; +}; + +#endif diff --git a/kbattleship/kbattleship/kshiplist.cpp b/kbattleship/kbattleship/kshiplist.cpp new file mode 100644 index 00000000..8a17ad37 --- /dev/null +++ b/kbattleship/kbattleship/kshiplist.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + kshiplist.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kshiplist.moc" +#include + +KShipList::KShipList() : QObject() +{ + m_shiplist.setAutoDelete(true); + m_shipsadded = 4; + + m_fieldx = 10; + m_fieldy = 10; +} + +void KShipList::clear() +{ + m_shipsadded = 4; + m_shiplist.clear(); +} + +int KShipList::shipTypeAt(int x, int y) +{ + int tempx, tempy; + KShip *shipiterator; + for(shipiterator = m_shiplist.first(); shipiterator != 0; shipiterator = m_shiplist.next()) + { + if(shipiterator->shipystart() != shipiterator->shipystop()) + { + for(tempy = shipiterator->shipystart(); tempy <= shipiterator->shipystop(); tempy++) + { + if(tempy == y) + { + if(shipiterator->shipxstart() != shipiterator->shipxstop()) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator->shiptype(); + } + } + else + { + tempx = shipiterator->shipxstart(); + if(tempx == x) + return shipiterator->shiptype(); + } + } + } + } + else + { + tempy = shipiterator->shipystart(); + if(tempy == y) + { + if(shipiterator->shipxstart() != shipiterator->shipxstop()) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator->shiptype(); + } + } + else + { + tempx = shipiterator->shipxstart(); + if(tempx == x) + return shipiterator->shiptype(); + } + } + } + } + return 99; +} + +KShip *KShipList::shipAt(int x, int y) +{ + int tempx, tempy; + KShip *shipiterator; + for(shipiterator = m_shiplist.first(); shipiterator != 0; shipiterator = m_shiplist.next()) + { + for(tempy = shipiterator->shipystart(); tempy <= shipiterator->shipystop(); tempy++) + { + if(tempy == y) + { + for(tempx = shipiterator->shipxstart(); tempx <= shipiterator->shipxstop(); tempx++) + { + if(tempx == x) + return shipiterator; + } + } + } + } + return 0; +} + +bool KShipList::canAddShips() +{ + if(m_shipsadded >= 1) + return true; + return false; +} + +void KShipList::addNewShip(int button, int fieldx, int fieldy) +{ + if(!addNewShip(!(button == LeftButton), fieldx, fieldy)) + KMessageBox::information(0L, i18n("You cannot place the ship here.")); +} + +bool KShipList::addNewShip(bool vertical, int fieldx, int fieldy) +{ + QRect ship = vertical ? QRect(fieldx, fieldy, 1, m_shipsadded) : QRect(fieldx, fieldy, m_shipsadded, 1); + QRect field = QRect(0, 0, m_fieldx, m_fieldy); + if(!field.contains(ship)) + return false; + + for(KShip *placedShip = m_shiplist.first(); placedShip != 0; placedShip = m_shiplist.next()) + { + for(int i = fieldx-1; i < (fieldx + ship.width()+1); i++) + { + if(placedShip->contains(i, fieldy - 1) || placedShip->contains(i, fieldy + ship.height())) + return false; + } + + for(int i = fieldy-1; i < (fieldy + ship.height()+1); i++) + { + if(placedShip->contains(fieldx - 1, i) || placedShip->contains(fieldx + ship.width(), i)) + return false; + } + } + + m_shipsadded--; + + if(!vertical) + m_shiplist.append(new KShip(fieldx, fieldx + shipCount(), fieldy, fieldy, shipCount(), true)); + else + m_shiplist.append(new KShip(fieldx, fieldx, fieldy, fieldy + shipCount(), shipCount(), false)); + + for(int i = 0; i < shipCount() + 1; i++) + { + int start = 0; + if(shipCount() == 3) + start = KBattleField::SHIP4P1; + else if(shipCount() == 2) + start = KBattleField::SHIP3P1; + else if(shipCount() == 1) + start = KBattleField::SHIP2P1; + else if(shipCount() == 0) + start = KBattleField::SHIP1P1; + + if(!vertical) + emit sigOwnFieldDataChanged(fieldx + i, fieldy, start + i); + else + emit sigOwnFieldDataChanged(fieldx, fieldy + i, start + i); + } + + if(m_shipsadded == 0) + emit sigLastShipAdded(); + return true; +} diff --git a/kbattleship/kbattleship/kshiplist.h b/kbattleship/kbattleship/kshiplist.h new file mode 100644 index 00000000..59915a5b --- /dev/null +++ b/kbattleship/kbattleship/kshiplist.h @@ -0,0 +1,58 @@ +/*************************************************************************** + kshiplist.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + (c) 2001 Kevin Krammer + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSHIPLIST_H +#define KSHIPLIST_H + +#include +#include +#include +#include "kbattlefield.h" +#include "kship.h" + +class KShipList : public QObject +{ + Q_OBJECT +public: + KShipList(); + + KShip *shipAt(int x, int y); + int shipTypeAt(int x, int y); + + void clear(); + + void addNewShip(int button, int fieldx, int fieldy); + bool addNewShip(bool vertical, int fieldx, int fieldy); + + bool canAddShips(); + + int shipCount() { return m_shipsadded; } + + int m_fieldx; + int m_fieldy; + +signals: + void sigLastShipAdded(); + void sigOwnFieldDataChanged(int, int, int); + +private: + QPtrList m_shiplist; + int m_shipsadded; +}; + +#endif diff --git a/kbattleship/kbattleship/kstatdialog.cpp b/kbattleship/kbattleship/kstatdialog.cpp new file mode 100644 index 00000000..9db2afe4 --- /dev/null +++ b/kbattleship/kbattleship/kstatdialog.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + kstatdlg.cpp + ------------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "kstatdialog.moc" + +KStatDialog::KStatDialog(QWidget *parent, const char *name) : statDlg(parent, name) +{ +} + +void KStatDialog::slotAddOwnWon() +{ + OwnLabel->setText(QString::number(OwnLabel->text().toInt() + 1)); +} + +void KStatDialog::slotAddEnemyWon() +{ + EnemyLabel->setText(QString::number(EnemyLabel->text().toInt() + 1)); +} + +void KStatDialog::setShot() +{ + setShot(shot() + 1); +} + +void KStatDialog::setShot(int shot) +{ + ShotLCD->display(shot); +} + +void KStatDialog::setHit() +{ + setHit(hit() + 1); +} + +void KStatDialog::setHit(int hit) +{ + HitLCD->display(hit); +} + +void KStatDialog::setWater() +{ + setWater(water() + 1); +} + +void KStatDialog::setWater(int water) +{ + WaterLCD->display(water); +} + +void KStatDialog::clear() +{ + ShotLCD->display(0); + HitLCD->display(0); + WaterLCD->display(0); +} + +void KStatDialog::clearWon() +{ + OwnLabel->setText(QString::number(0)); + EnemyLabel->setText(QString::number(0)); +} + +int KStatDialog::shot() +{ + return ShotLCD->intValue(); +} + +int KStatDialog::hit() +{ + return HitLCD->intValue(); +} + +int KStatDialog::water() +{ + return WaterLCD->intValue(); +} diff --git a/kbattleship/kbattleship/kstatdialog.h b/kbattleship/kbattleship/kstatdialog.h new file mode 100644 index 00000000..fd9b3dd7 --- /dev/null +++ b/kbattleship/kbattleship/kstatdialog.h @@ -0,0 +1,48 @@ +/*************************************************************************** + kstatdialog.h + ----------------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KSTATDIALOG_H +#define KSTATDIALOG_H + +#include "dialogs/statDlg.h" + +class KStatDialog : public statDlg +{ + Q_OBJECT +public: + KStatDialog(QWidget *parent = 0, const char *name = 0); + + void setShot(); + void setShot(int shot); + void setHit(); + void setHit(int hit); + void setWater(); + void setWater(int water); + + void clear(); + void clearWon(); + + int shot(); + int hit(); + int water(); + +public slots: + void slotAddOwnWon(); + void slotAddEnemyWon(); +}; + +#endif diff --git a/kbattleship/kbattleship/main.cpp b/kbattleship/kbattleship/main.cpp new file mode 100644 index 00000000..2fdba01e --- /dev/null +++ b/kbattleship/kbattleship/main.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + main.cpp + ---------- + Developers: (c) 2000-2001 Nikolas Zimmermann + (c) 2000-2001 Daniel Molkentin + + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include "kbattleship.h" + +extern const char *clientName, *clientVersion, *clientDescription; + +static KCmdLineOptions options[] = +{ + {"!+[URL]", I18N_NOOP("URL of server to attach to. In the form kbattleship://host:port/ or host:port"), 0}, + KCmdLineLastOption +}; + +int main(int argc, char *argv[]) +{ + KAboutData aboutData("kbattleship", clientName, clientVersion, clientDescription, KAboutData::License_GPL, "(c) 2000-2005 Nikolas Zimmermann, Daniel Molkentin"); + + aboutData.addAuthor("Nikolas Zimmermann", I18N_NOOP("Project Founder, GUI Handling, Client/Server"), "wildfox@kde.org"); + aboutData.addAuthor("Daniel Molkentin", I18N_NOOP("Dialog Stuff, Client/Server"), "molkentin@kde.org"); + aboutData.addAuthor("Kevin Krammer", I18N_NOOP("Computer Player"), "kevin.krammer@gmx.at"); + aboutData.addCredit("Benjamin Adler", I18N_NOOP("Icon"), "benadler@bigfoot.de"); + aboutData.addCredit("Nils Trzebin", I18N_NOOP("Sounds"), "nils.trzebin@stud.uni-hannover.de"); + aboutData.addCredit("Elmar Hoefner", I18N_NOOP("GFX"), "elmar.hoefner@uibk.ac.at"); + aboutData.addCredit("Lukas Tinkl", I18N_NOOP("Non-Latin1 Support"), "lukas@kde.org"); + aboutData.addCredit("Malte Starostik", I18N_NOOP("Various improvements"), "malte.starostik@t-online.de"); + aboutData.addCredit("Albert Astals Cid", I18N_NOOP("Various improvements and bugfixes"), "tsdgeos@terra.es"); + aboutData.addCredit("John Tapsell", I18N_NOOP("Various improvements and bugfixes"), "john@geola.co.uk"); + aboutData.addCredit("Inge Wallin", I18N_NOOP("Bugfixes and refactoring"), "inge@lysator.liu.se"); + aboutData.addCredit("Jakub Stachowski", I18N_NOOP("DNS-SD discovery"), "qbast@go2.pl"); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + QString picDirCheck = locate("data", "kbattleship/pictures/"); + QString onePicCheck = locate("data", "kbattleship/pictures/hit.png"); + if(picDirCheck.isEmpty() || onePicCheck.isEmpty()) + { + KMessageBox::error(0, i18n("You don't have KBattleship pictures installed. The game cannot run without them!")); + return 1; + } + + if( app.isRestored() ) + RESTORE(KBattleshipWindow) + else { + KBattleshipWindow *mainwin = new KBattleshipWindow; + mainwin->show(); + } + return app.exec(); +} + diff --git a/kbattleship/kbattleship/pictures/Makefile.am b/kbattleship/kbattleship/pictures/Makefile.am new file mode 100644 index 00000000..5bb50353 --- /dev/null +++ b/kbattleship/kbattleship/pictures/Makefile.am @@ -0,0 +1,30 @@ +picsdir = $(kde_datadir)/kbattleship/pictures +pics_DATA = sea.png \ + water.png \ + hit.png \ + death.png \ + border.png \ + ship1-1.png \ + ship2-1.png \ + ship2-2.png \ + ship3-1.png \ + ship3-2.png \ + ship3-3.png \ + ship4-1.png \ + ship4-2.png \ + ship4-3.png \ + ship4-4.png \ + ship1-1-r.png \ + ship2-1-r.png \ + ship2-2-r.png \ + ship3-1-r.png \ + ship3-2-r.png \ + ship3-3-r.png \ + ship4-1-r.png \ + ship4-2-r.png \ + ship4-3-r.png \ + ship4-4-r.png + +KDE_ICON = kbattleship + +EXTRA_DIST = $(pics_DATA) diff --git a/kbattleship/kbattleship/pictures/border.png b/kbattleship/kbattleship/pictures/border.png new file mode 100644 index 0000000000000000000000000000000000000000..575b7d6e670ed505298d259baf96605d64df082e GIT binary patch literal 735 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/hi128-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi128-app-kbattleship.png new file mode 100644 index 0000000000000000000000000000000000000000..93db2a3a4233bf5cf64929339e07644a38cb7a77 GIT binary patch literal 10105 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/hi16-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi16-app-kbattleship.png new file mode 100644 index 0000000000000000000000000000000000000000..b7971c966e1533efc2b8ff377bbcab7280451851 GIT binary patch literal 515 zcmV+e0{s1nP)6I5%9_s18I=x46|9|Y?b_N6QRdmB6!kMW^dNfl z3%KEFcQ4)wveVWLnQSu0*m1~kA*TwFj*_UIDH6LTuLl)mq^>^49}n+8UWFd0s(P? zZ~|x_Rp9i_)*FSRo+7#(@2B6+gfiLUp@66-J2H4u#WP17mqsdX0mTER&9Gtd<1mIDD*002ovPDHLk FV1m}v^t%86 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/hi22-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi22-app-kbattleship.png new file mode 100644 index 0000000000000000000000000000000000000000..27b8816d9b6e8e14e2773162801b886a3cf6e1d7 GIT binary patch literal 3489 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/hi48-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi48-app-kbattleship.png new file mode 100644 index 0000000000000000000000000000000000000000..2f501a4117555be08ea349d69c875d34b01b2274 GIT binary patch literal 2591 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 B`78hc literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/hi64-app-kbattleship.png b/kbattleship/kbattleship/pictures/hi64-app-kbattleship.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4ac1e180e00679c1ac37862271da251e5afa6f GIT binary patch literal 3848 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 IM6N<$f@&A|M*si- literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/sea.png b/kbattleship/kbattleship/pictures/sea.png new file mode 100644 index 0000000000000000000000000000000000000000..ab14e95dbf4caaa93ef2c20ec50ed1672b7adb20 GIT binary patch literal 1695 zcmV;Q24MM#P)%Bbh*Yp0Iv${27KFnq09fz zR`1jK(AsP*vYCI!&Jd$dX{%|dY4>0T?lL9v(H(2`|ACLQD@13WL_UGd!Sojogb__!$ZP{@#f&> z>I%}lAWg4OaS#&0VqvlcbaS-Xg_lPQbJ_#))A3V?aLG1qo_Ip%Ii@FGF`x~h78@(@ z)Equ=5Cf>!?2{a?HU~lQ;3RQ)C1=iIG=mFMyuc=XkUTb+V+ZKgk~1pljO_C>IhRA^ zMN(vCtttl~SbVaWh~6A=w4I6*EW9MUzu_;h;BcvBY908CK^HF?R&tEj4#48<6}wAW z@2hoA6nl&XSBqHJaE^nvMz;ZrA4D5ODB3Vd$i-`pv*c#?}Bx@a-;9yc2fM8dz zk{MF5jK#;f;e5hQ7ypJi__GvyIAc6=_Ax39!Hxk7TprJPrPbCX+rJ%9v5i^|aF4t0 z>=AqdPdM-c`;bPwB=$2O$9?7dA~mWKS%s5e1E$hILRyEAr4Td`jDKSKLEh)`l))x2 z3t+M3;}!D<`3lj(%HAI-9T(`6zb--`kE1Jos*iQ!ySUg#t>by#1R)1&y{DoZetF?B zvb->z@_8H|A;hui1(WHgoc~KYwweHJ17aj6yI%m>eBk_RZ5}HjMl;5?pExgS1;#i2lXQnTv*>kKGVUK zP$iy$QKn{)1dQ)gKp_f=bg!cesx^r-%=OK&PJGbJxr6qW1MhKwWFnf>@Qgy* zBreO3!BhX#DuHw<^Nh8&{k2qUtEEoWO-)XaM4D2(@3Z%Io(RM)vU4B~ftwGs%X>&q z$hCo*9jg0o!1mTp>dA-e@9y1%#_A%GTS>?{!mizF>+7pMZ)tm^ZG%VP$_g1BDm`FY zx=UWf>a9PrKyC<5HxBrZG>;b904n!APL3nI-sO1~1;?oz=@g@_ihnasK?dS%_mC z@qAZ>uQH=a@kgAgyW>+VFYN)0A{~DV3t55oAqJDy9ygu~&Eqh$j0GNx{448necu z)}U#1*TQoi+n&B1W#El`w1LG4r zqz>TD9}Fu7r5qrND3}!0qv<&CIngy_R>G_^2RaBc@+*XR!u>%pIXI~`K_qG53nwk| z3@4BCJ|X?D&#k5|{Gb;VmN8qx*GsK~biXjd>l4 psIG{r(~_G^A`a5*Q+NNb*T3A?%e|Gd>f-k`IZk^R?RoADdCp}Lr;w~;OIyelmS?wm|5JB+u z^(7vUM+pJqnT!*OgsyE+LFn7toBlWZxKOXxRRGVNeRz0KA;g@YpIg4a3K$p|kgcsP zvnQ2GMKdRpU@$0&?QQvae5?Ymudh{vDZ|pzlAeo1B9hPNrPXTbKE#}7luD&PT(OE) zS67{(p&p5daCX*4!AxWiD5(`0EGmYdQMDCC~hfB|F}aj*e<`(&gqGf>XI%PUh$56$25cr>B-r3EDp$FE%$f9n&y;3&@Vf zi^XDUy}3>thRA%JC`Y+mR&g{aahzj4J3H&(FvMp4=cWltr_&DB0*XMgMgR@T;F^&k qt{fa3d<)ZHyQ}Mf>wxQk>%ec_Uw+bWBL>0%0000JD~(v&@z>DU5Ur?C zf|_h$qp>idqO>Bu5>0#%5z)K+&So~d&1UBAHk&rAmX1vYK zP47?E5cK=<+}xZuHZ~@)SWGgRjBgUY2+YpTdLttv(%aiBgM)*TN~L6Vb=4P%Edc7|F{r&R#`YP>q+c^zRDZqU3cwA7S(P$_jpl@$)s>b>G zxhyO!IHkdf04v6DNZr@hNBPP6`no(mK1!ibkmcoNRmVjJrxXCribJ80Y;SKX8Nf=V zlB}$($j8Trro)ARlL7$nU0Pa_tE($5#dJC?`}_N{v9Y1VP{}2%2~1B1}`J9}b z=!%UJJ3Bj?dn$z1?9vacO~}enfD+{T^z>9EXES17K%u+4JN+9(ZNnDSS!GU0|NtE1z*ZV6cB@&{{QSHgHVn9_3eyidrz7V%&D{Uz_zgBiX=kKg^RfT{002ovPDHLk FV1mh^oPYoT literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship1-view.png b/kbattleship/kbattleship/pictures/ship1-view.png new file mode 100644 index 0000000000000000000000000000000000000000..eb96026c0422196a873b685eb17c53ffe3d49bfe GIT binary patch literal 812 zcmV+{1JnG8P)k`IZk^R?RoADdCp}Lr;w~;OIyelmS?wm|5JB+u z^(7vUM+pJqnT!*OgsyE+LFn7toBlWZxKOXxRRGVNeRz0KA;g@YpIg4a3K$p|kgcsP zvnQ2GMKdRpU@$0&?QQvae5?Ymudh{vDZ|pzlAeo1B9hPNrPXTbKE#}7luD&PT(OE) zS67{(p&p5daCX*4!AxWiD5(`0EGmYdQMDCC~hfB|F}aj*e<`(&gqGf>XI%PUh$56$25cr>B-r3EDp$FE%$f9n&y;3&@Vf zi^XDUy}3>thRA%JC`Y+mR&g{aahzj4J3H&(FvMp4=cWltr_&DB0*XMgMgR@T;F^&k qt{fa3d<)ZHyQ}Mf>wxQk>%ec_Uw+bWBL>0%0000w3tu;TB}{AA$H#{(m&?Zxi9|xh z@oHDAl^%5g@!lan9*=?>*w3`_c>F;K8q!yGnk(iEIG!1R6@ld7LV$%^C=^r`0Dzm# zMux+ozFS(?q0*6pOnM^eI`wa|4kbfsO za_eJt_J-?ryU$-izu(vX_8FQok~HM9pdod;zGDo>E9GmlK1}?laMS7Zqi*)_x$(=j zKOzHIQ`^5<`3G$_n@^Lz!n%va;%Q1qu#3oKGV=cZ{-Z{(2)w-Kfaiecfakz}@4zQ< WR5z${{4US{0000QvuzAf?f0RMSe<05JxL#bO%4-QAsD!wBH;@KCQX7z`BT{{G$!fRY6Q0qJx) z$}O&8lt>PjN+o%Jf0yZWs{D7~18xoW8?o-Ip$=360AmtibPkCm`^{$49DvTEjO}*Y zoC6jB`e^m9ZIjy>9mw4$lZm@#!aD?deSLKYaB*>=tqcH(GzK_3J5weCKr^|Dpja$w z4wy~G0EI$9OGqC8U?gjJ=L1yU6#zXz4tPF4Ka~%TGwn~aXpDDqfOitPGXN}P4x&cP zqF%2j$otL!yBOD`GS>60;JXG;sZ{i4Y#cx47>!~^B@zi?Szz4264$zAY7hjO!rArp zwUUD%(7YcBz|u*m4awNZxIL0*yBU3#QTI?Nq%DH8@p!CSgVqwjKliu)HR{jJBaw*q zCIQf91c2lk&u&vA12~Syc|b6)!I@ zzvm6DNEY_afz#7deHb-#0eTG=2!Kqu-llgkS`hZrhOJghOE8U#Kz@#JX{Nq`#*D{~a)u|Q0USe$V6`~`rIuFK7ugu(y-002ovPDHLkV1hBX BcTNBR literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship2-2-r.png b/kbattleship/kbattleship/pictures/ship2-2-r.png new file mode 100644 index 0000000000000000000000000000000000000000..2020e6371257979189bdccd309f8a6510dbb6a6f GIT binary patch literal 732 zcmV<20wev2P)g(%E$4;kHb_yt$%hr587foyAJ2*oTPqL?Xdd zR;!gPmrH%S-EQ%CJbw#-{Pp!!VzHPU92^Kj_V)IKg3r%S1qF;b>mMHrf4@{CLjk8#VF6`^U`XyBoGKlCX>;$#bP1NW>d-ie!s3kikQJ* zAp86KN`P2S{POa$79I=+6&F!B!CVEDN+pW|$dk$B`edy6d_DyQ?BU@-&d$yxl}gF` z`@6aUF+#8#a@s--`P1oC=f}s#f;)&OuXKGa-OJ10tuUuQJw4S0NC846MY&u~Rl8g+ z$!4=MnM{=M=;%nnu+!WN#+=N=)6^5gtmO1~UNq&ff@AiU`tA_(j! zP|cpnBL_j$5VVW2=`V8v0&02K!z~U++}zygnr6~_5{PsD;IEEd&H?f{k(Z*OmM zcXy{_h!A3kd7C*2e14FJLLn6i4z4~wKkKE8Mx)zbL8Vf$dcB^UoSf)s3x$GoIvu&V zxRCq%dj+Rex3{;t5819ThhdM~j}ru9xXk80aDc6^;OgqiQLELg@pvqUhlg6kr85AV zz?)-D-1spqpe>O|M0X%ZOuOAy$4pyx{t-1A4eRmoaosX3CK!0d&? zVg1u#j&SanxgR?Pn8JVYuGi~Uzu(`2LWFs?HtdP*fbD?ofbGC{IPeo2kBK@pX=42V O0000zCKP1^Ilwe!_sy z=abXZQ<qtgOh~+?*P=J_antEe8h&vcA49M@L820X9QVPmf}7 zc6KIvdwbTNu>wB#dcBfLrJNan!QkMa`fz)DTN-NSD-e>ctu48~zn5yYDs>fP2V}+A z*qBPZwY4>wn3&KrV4$O;LolZG5e{^_-Ew_>tyo-LUCHL=rhOyb-QCjO-Y(x-Ta|%a zE~gg-7(kukJ1Du&8!hz%f8MwQ)~`U~AlaEr#+d;=hBi6i$N&x`goKYmYUmeVf%6l(SS%*VWKzEt*b#vy z7hYdqWpZ*-&j4q>xw%oAoge)V{vg& zWxpZU|AYV`!1aUx0>qz&L+z7zJex{@`zC#+SS(7pTvks?@a5%2-rnBS&{lmNQ2!(u z|08#Ick=l7sBX{vn;`ZK$9Fh8zyqiuCAhk}DwRq_b(;70cR4vZQ8!S>jl<F2=tD zl>iYWnd=XPAk)+`1e79el3XAm*_bd;ArNZn=Cv?-HK(`y{rq3>9iCsoxyZ;$#{d8T M07*qoM6N<$f(Y8RJ^%m! literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship2-view.png b/kbattleship/kbattleship/pictures/ship2-view.png new file mode 100644 index 0000000000000000000000000000000000000000..7d175815e6200133fb694f81de968f37fe9e3efb GIT binary patch literal 1171 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship3-2.png b/kbattleship/kbattleship/pictures/ship3-2.png new file mode 100644 index 0000000000000000000000000000000000000000..416c972de9707eafa12db0cc9e952fe8b0a1312a GIT binary patch literal 963 zcmV;!13dhRP)UKYuC4uF z*=*L;>vgxL#RtUu@c8)XPESw2*l}=h;Cj8@=ZEw2bBA~zDwT@6y1M%OaCdh%Z9)2X|-xS0P@mZ8HFgoPVGVN(d6wXv~b&J2e`+r)ba;q2_pcpM%ch7oplcASXr z@9&24fvc)CN0cJB9RCq_~U^b z9UWOta=DzjetCK6$oo=7KfvbZrYS)Oi{tUw-cL?Wq5&XO*lad)Z*OmwAs8%lKDfyB z_V%_ZV0|)~*b@ek0I&`K^*~NgBtI(wkdo``YopIn^TCG;I^x+DXNNs!zAb(NPgg_Zk#pYWEoleIg zD9QI`vuV8$37}f7Iwo1UTsEtK5rfb`T(+opnh5`2US8bm>#MmG8GwPq;FE&p9YKr) zaC>`e7S;Ro0s)Q8U@#aM{o~_fuO3!%fk8sh2xPb0wRo(=fGki%stHMC0F(h?v$}C5 z0D6I0ASHF;N&p(7C?b&yiMpqS=|WP7GEemazlV;s;39oYBl53#Ab?0N1eXpop2cF( z;=?wvl!QR`C%KQg$80EwWJDNve|?k28JPfy;&$1qQRXYbzLUbOE2YGIgT24jHf zbkKpPd8NN8diJJLDT~0b2`B>OHJwgd*akMkp`rl)NdNOU|10R0A|Xu}RrUY) z@Ch!p845i^Bpdpm~xPogm$4US&#xHlFFf zMKmIL@*yvRcO;N_*J!4{f2Lw*a54*+F6cg|Pt~dV>#sVe%_4~yh#2_a8HjyH!2JAt zb!TTsy5;5N>iheFMdQhldBN0}##}0La3^f&pT9c-RI825e+x#LDHeKs-G?$r;omy}iBG z*4Czg2L}gYLYwzkxGP)ml4WKaFw z84T_2?n=krxw$#(>grNpf?-ealamu$TwJstOG{q>*f2CSB!tYvAovEv=H{l2j*g1@ zySqD2&TMAv0GbPv)s|q7XUh(v)$L%hh9Mm<{-2fyeJXD z3H18h68O zJS^V8`}_L>iO%aE2Elp_0B4uFVo)lT(AoL z@W$s? z^>rBM7Xz%Wudm0T_4f8w$A=oj2w=v>#^eZRu;u>-^Z=o3*t=kd(1-|jJgkSGItFAJ zy!=kUkWDz}%7se6F!BK8&y3s$BQ%qBBl#8p8yo~Rquw+pwDS#1!L?oU3(c&%0J@RT xPu{4FEdwuMApdgQC}^aM7>F2%82B3lKLMo)@U+?7kWc^s002ovPDHLkV1k77xmy4L literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship3-3.png b/kbattleship/kbattleship/pictures/ship3-3.png new file mode 100644 index 0000000000000000000000000000000000000000..ab1ada4bc3190f34ab0b212ae53a316281006d2f GIT binary patch literal 922 zcmV;L17-Y)P)fjTuI3L&4Uj`Ox3;$2=Z;B0 z&D?>_%}v*Cw@rWOfmkeNwxa(_3CU#AHbX(;ZRYXu@w6K?DwT?D;G|luHh+LkX1!i_ zm>YV4n?yU*4;uM=-fe7bn51|-ZWFc4P|0Beu=wWY#x&mE-nwqLYXU;uGdl`}g4^ES zcD-KDh>6RDz)JuKm>ecB&(F^`2ZMp10D^UJa9}13heJb=&1TJ5D_nzs*t{rdXqs@3XLB6$fYm&@j~ zU#;znmjJy}ObH>JUa34Qgi99}7yjC3h2S`B|G8QaHqZ_)c6N4*Sa9Ow zmXj1?K>6nQ`!v2OMK@xgDVLkvL z9-+`1!NUtD0K^w>sP!RDshyDlz)F--Q>pa*et+tz;2V7L)JpwV{S6z$7sd1{qtVFX zti~@m0fQ&7p2`X6NNTwHT`W_>D7Fg-ztv1DBgBVq17Ikg5-uQ8u9B=G<=udM2MDBt wlqkmC{7r@|Me*#Ag=7Kg$G=`bqIenOHx+KOi$x#NdH?_b07*qoM6N<$f;6C@J^%m! literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship3-view.png b/kbattleship/kbattleship/pictures/ship3-view.png new file mode 100644 index 0000000000000000000000000000000000000000..19bcb13629b08c4270693539b98243fd205b3926 GIT binary patch literal 879 zcmV-#1CacQP)Z`tF(<4`m5@%?wCbnd3m|HzrP6{AP(>E@8y7fGDHZFz4P;Po1LB2Ij;@@GfyNE(#JVKc>!lWK0dB*Z*NcF zi;IhPa&lsikB?TZR&8KlKn}=aYHCUu1CC?%5P;|A<^*tYabf9n+LFnns?guxZx0U- zCg5nM03$0aD+c)J=%}SqDH|IbvwFQQ@TaFIIibYJ;NYNjc6JKr`1n{AD3wYAMBmrf zr@*0wszF2Gpa41;&H&2ga<;p>tA?V4WW-3e@as;f$icyZ_Bgw=v}C=#y#fRTdqB_5 z&TMsc)z;S6P3<}|GNOn$6a{Au_|DFbO-xLv#Fa_~wdQ1Vd3mY%%*>3&g@px;WO;jg zt2vG5H$!K`UjR3h4xWgPjs|Rdd)tb|B6eCcCvt26MBt8wK^U*DuH*-L@Cy}G#+whj z&CN~ApPpJk$8tA^hlkM%3=jvGGW{7>mJgzMCVyLI9AMWZf=Zg2CvRD^Qd@Z9vvMiIQE?1C^-6ny9aM<)M_;UvY3Wb8ohjVvEc5oV%!^1<%WHRyvB~*z@F!DAKJR*X49^r&ehHsj|UniQ!Uoc9? z9=0@&zi`pU;TmxbX;S>1hJ0dIE|;V6wYqw1Yb!?i?(Xh%4d9Rp5X9uiFBu zJ^?@v_Ab~VG#G*%ANuI`?XC7ZjRD3u{~e)*%E=v@f*H=$p=0e`fN2Jk~KOYc#xze?BL} zJMTBoUjp9@h}rDE4ST&?S2P;EyZY4WYqi=P-Y)d}eHK-lolccRyWMu>9RLC8=+W9v z3hVi`4NX<~iL7Qpm7Q$6mH{W@I-RQ-ut$rlP~}HtTx~qsP`J@*K}1Q0c%D3wZdx!m5R$z+EzoC43kt zR@tuIZkKiT$HtE3%@=+|tJS(@^}@x46x$8z9+Y>mPF#>VBNPg8RUeN>Di(_t?ysFo zM^+Ky8b+g$)N5xQL9KybfGG>;)dT+yC+z*~MRX?G&&%QZACh;2`2pYj3e2qQ$K3z` N002ovPDHLkV1h`j8?^uc literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship4-1.png b/kbattleship/kbattleship/pictures/ship4-1.png new file mode 100644 index 0000000000000000000000000000000000000000..51241360714904f37065e81b9817ef1ea46122de GIT binary patch literal 607 zcmV-l0-*hgP)k4LcsFdmP~9cVbC!+yUPhr{99 zL08hWCMG~bBSBRZ5^z0rU7aErjYjgiUauAMLIPcbIF7}1Iu)DEMm(R-pCc#}QK>f@ z5s@930y4yD3Q-ivA^|WR*c1X6G@H#gLE5gCI2`|-ph_A$MX+2h?Fq2n2}>l3$|?eo zfB>MWSrOo9na}6qe!q*?>*b~a(iiExSS;K{q$zwv;3OhFOa#^dd%l!Uw)=at}P32?drym7(xdcDdP!h01U*}C1XOu{e><)Ew8N}Nt7 taXz2rJvu8~YdsC|Hrea-vWB8afiHVT>urBy#LNHy002ovPDHLkV1jD?`kVj& literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship4-2-r.png b/kbattleship/kbattleship/pictures/ship4-2-r.png new file mode 100644 index 0000000000000000000000000000000000000000..187f0b17af99fe18e71dd868dcac60a32ac5902a GIT binary patch literal 575 zcmV-F0>J%=P)jIgjyG7V$3-LLDtc~!Pbuh+B4!=ZeAe0VBAHx0wX_a{<4W&a`f z1^5o=q6rPd6jP3-X>k1Sb*10$dwLJ}J>WawJHYqA<#M6h?MCPGnNFt@#bPlE27|xu z1@UFO-O}}XC0*A^RaJ^aB4X=LkxPJNAchDegb=3z3rd-?PYCt>%cen-Fh3olb4iYPD+5?^wM5bgiNl?99F8L6wD-;T}SS)NJOi+SMCc`Db9tdRE zRNKVn$`5@Vy)YEe3!W~>0>?XuY{3s|jIgkEVh8r-OuW5}?uAHNo0DwPUH8I}cv z!(pn|>ogn=IbpZk<%o~jY{n1fs4Eh}%E4fOC33>TDF}Flefm4TPdFNln8fMB4ZGQF zxbqW<1Rr<8vIBy^2_*LK<0~wBN(?vblLO+=yP_@{=v{<9kMF>v1K;m|8KTGC>Mb_T#`ukzxsIO}9L!~%IXiP^W*e)AH7Vc)A&MaM-T+>1u{ymf!%KR_Q*GULZ*eU8^CTd znWRZ^z<=fju)p8$*&8~Yj*i<`7l*^4gbTp3tP-c#Znx@kxugR{_!Lp%MwX+G^KwNv z9*_EYyWR2)WHCshumO@-EI=kl7!HRzyQ|eoJsyv8M=%Ov(!hK^*BQVOppe2xa2zN4 z-)^_l46aSHsDlOlhf|ROqF>kmJ|+Wzm;q&?kO4FiXaUcDzb}sghseNkxs)3qA&IiE zSS+F}h=SPdc6AT)JWoBJPk9Vb6TMzfZs2!PhJZ>*c|0Dg!C;{F`@Jj+1jrf2Qb*7? z0fC&q0ji||$PsgFO2BkXxJ>bU0`NXuDFI$Be8O`QA_6ABod@~F^K?3?Fbwqwqspr# z0=8}I0fgHTq>swQb(4fwIe}z4cUt0>uF)DyWn5=|K2++)=DfOHxcVLNYQR&#Q-CXQKA-7uIPm^2l}c2v z*G=Do_r-3vqwRJ}nD6&HU9VRh*dSB_Jz0rcfyGa|D%1&gXN&Kx%QZTCMgK$J*_- z9*@WAa=8%XLxFv27X*T#_u)7ijq$(R#h+&fb6I+=?^Y9%+EtITctj zf5WkUzi<2@kpqrKBU5n+XHL(rtNitfLXt^{!g4yO>`#?j4+mJI7-1mO?RFVo{L+u< rJ0N0w`mBmGr)Twl$m}#}NB#W)50)MugOBpA00000NkvXXu0mjf3<)xD literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship4-3.png b/kbattleship/kbattleship/pictures/ship4-3.png new file mode 100644 index 0000000000000000000000000000000000000000..d6242c14c2f940d492ea21d991a163316590c06a GIT binary patch literal 645 zcmV;00($+4P)IXdZdcBwxe2^Dhd>M^K;SZ4&m&+w=#g^@MD+^=?7uc4M$K#($Z9&LpoQc_N7WE?n z@ZAmCe>@)X3u?8RX}8<)3ldG7PA7A_-P{^fYGW5^Uhoc%B1CD7as$B!<~63@6(BF* z7@p~zQ-Mo9LNJ|91wP`B2!>+dqX5-_=u-Oq{^u@)!sCkokRgCi65*0}7{}wW>2|xa z0ntDmfDim83a%wR?(f0`3O4deqks`et$;q0k5mf4f$c(S1&v0-bUGc=YPHN{GRell z|B#3Vm5Ic`V33VOAOMk-N=14DGVvo0Y$;v=$+H#U?xkSA-v{OVd_HA!XukO%VEG;n z2T{Ox?=XUST8$`>!S7HKHz!=OTCK!&C;4_u{1yOqe;zWl;H3tTB=h+^px7R}JVe+a za$}&`Y|6;OeZ5|r<#K6uyPbKxUd4vM7W7Xx<6J|aNuf!I1~(iIaD9DkPft&FdV1=K)9JLmyu8@$?QJ54s04|# zpHz~V)zW4%85tOWUdA%?V2$oqJHem(>f^h)`OAL)E?GML#|{n-A|F5?ZEbBf zF)`sKQLY#OC5$|SjuHWeM`M}DcW=*ZaWP|4Q&Y|c^8#>nb!Gki{SH9(F#rmd)QSZX z3q+C~x>PD#Z}Br_#F}^uVzg78e%|!w0b$y)(b43k8GH02taiZ zB@L5c6N+p?T~fbc0AaGQpA_^nx`qHWx|)v?b31ZRw6?a!%)ay{j76>tNX&ywPEPs> zs!Vy)5CfqDtI%rDm&;}A=;#PdVtwqnqPZdWxFo8?0W?xm%5ZaYV_YUpO>e|;-l0@) zI+7rw8GGJ_jE9GZt+%(=FQSo=k?8c)lpveSnVu@J_W1bd$yv3MU;`uf_xBDE8_DzY zv-?a>Pdih(5t-MbVQ2V{2<`?C{a8qayv9{H>sM z0xT~tm$mUQ;W%kUP^e9pR0d81qE=sKvphx! zuAg(yy>)I?C2BUC?)`nk#c}K|E-qX|!+1QlhSSs2MZ;t=u?9M-Ca>$$-Pd!xTY9P_Nh1xx`>k2MECd zxFFx;+QJM)APa_>8{jYSD$@kXgcR_eS_(RpAAea88f!-%V+c}XQUDCZ-rnBa*Vk8& z31fY;ER_iX2IODq#aN4#0DhwQ-re1`Cldrh7(oDkvH-GeNdbjc!Iy0o^zZ2O5>{Qt zoCf&4Z{$6$0zrN_lnzP*DCXzq=VAbM1--L$XoCr4emb3o0T@Vw6y__j)9Kjc(P-oj z4h~iWJUl#Djy3fzgU>-I%H*-=>0T3%7p~+AO0QqV= z$pQRiga81s1E6{c0{HTcmt{@~3qUJU2;eW}&d!bjP&@*#qoQmGok|3l;5!$A5DA@SL^&J|b50%~A5FhL0pSD*Q1pQLw-BD% zqC!L0JXTU$m#E|?B8PbRD0?W`fmuVdkKOB{-tI8{{aEY Vq|*A}8qELz002ovPDHLkV1iPxlGgwL literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/pictures/ship4-view.png b/kbattleship/kbattleship/pictures/ship4-view.png new file mode 100644 index 0000000000000000000000000000000000000000..d1e53f6271a37c537b39ab9184a161b32a76b970 GIT binary patch literal 969 zcmV;)12+7LP)SZAzKjL zg>j%LqBu4<5XJX9*YB9Wtxh^2Ui5mPQ&p$VH#axi;NYMq zWaY(T(Vto%VkOAs@-d|Oe9p%U3kxy*#>PgZS0CT4tzY*0chS=6KX!O{82JDKX=`h< z$;nACiE_mNC}HFobd(4%JQ~YHzWe)TOG`aAJw5GgFfRbt*Vi^MFyH`W9|NFZNv&8Q zu|Oo*p-ZJwwzjtB)AC~gDXP~m0|=9a{iL9u(KQ61(bd#T%C@gvmwLU2S zKsBrH?(R-}=H}*%hZhbuot>Rto`ZvfC=4tiI@%EfvJeBbl^~!O77*U=B#~TQU3r%c zc64;)eKz6c<)!=Z8Xg)Na`q-BCTw?i*Vfn9KT1HYp&QyIA^qqG$OdHhBjHO-0DPkS z!)bvveD?PCY-eZ3_Y3~$+uPee0N^7K3KDKW>0n`YcGib%CO~M#vQbO@B}re#fUYV* zQc?xs1M~FsG`4c)pdH!V+>C*lpPzTVcD)ZOejLE+>c`Hnn6Db@(y5yGM@9NA`FlZY z1Xx*FDQV+j!g11ypirAGsSKP3M6GtdE6YE1lgn^hOP8aDhaXNVDJ7D3~0Vj_-Qbw$i?I5}QDn_*vz|$c3i+~Co9_*qiL#b8g zj?_uEatk;R2OD~RbEk~uRqgUqby(hXT%HLLtN~`$wE))a!T#q(lxPJRw&Ejx51>sa zpI0`dF&ODBp1*)$X<)=bD?~E@xNd3Tan9pQf8Gp=IV(s%z(lSCtVqjEIdEf4s~XRL zu03_}?;e=?A4n}bLj+H}82=ubyhe@N5@zkF>OO<~`fGH^LTL6*w93Ry-B&f7%TXuZ-#oSU)Tk_RaRei4*i){dFmZJ6vP{HC-h)A<941e;C_9MLE}NO1_%U6+Zj)uTh}Rd5ik@iuSvd@@r+qHA87c2$o#^-@;I>03h4YVaxPnqpImz zoNJ0LIHpOoWT7S?dL8c9>myo~m!7jz_mN@Cc7AGL;Xe?VxYhXjdsfXwY15fEecU~0 zkSSa730;8G#$9RSz9Ma1*Pnh89U^E1*!Xm(Rb7zdw%nOBZj7lw@k&F_O=gver`U|m z+3^YWoyJUPV#2Oi`)3TJu17Oz#a`r-lfQQxLJk}REU2e2SH3OVzHNWwiBHeVx_1SxL zu;e;n_6O*!W9av0^;cZPI~cZ`pRgXDb7NUHAj%Fmo;5+GK?0^&wvGS02{p#ooDJD``b^n1}F4mI2u>~H%QjBGjp0M z+oFgTQmQ_BiQiiwTfu!y?|srxu)>&9dBIHD<0kLoxU+cn6h3qT>PcnWH(Js*I&$5G zGiUAf65O+|1F$Bah z-DVKch-wS^q$7XPD^lwju_j303s&whl8j+lhgpxVQY$Zt8gAPM{)Aj7H2nY{qo*b8 z5Mm6T;p?v4B`4;bm3JRx;Fj=udt?i+MzmRYbiw@lY)M1ts3oFo(=9^lEm@c5f2?f( z`l#zyj`Blx?WOdW)3$-Hg^gDcIs2qoeOdb_T7r?6cF0NIhsv^RZ_3-g^Bx$3lXq?X z)Q-5`pWslp(cR|$Z_TmYE}|8o>HuQN)arjqTfQhOu7p`8cA_?;VxJnlh@wwXBWB%* zYA@!PW8jbz4HChSCvBXiGz2FYLeuwI$`8u&Ygy4pT&xzuUvZ^Q09)d)l~1>C#Ii^D zN>%5#_@axjj6+7E5g+v)E0{;oM%-xQc;2KePd_ku*V%tn(srXLKMN2{JL3oJkgafR z7aZRYW6lJ`7$UPxX_eRH_$@cVk`H?tI8g>YSW}6`r{S5$s=6x^!c5+WZs4Tu zq4>*K_5>jEHITJ%61HeEqpa{8E7YQ>6IRGpg#SQ*XuhoVy5`kw^v@rjzWIvhPg7!a z?$mLTKtqx0B)Lb?s-uXUV{h&(GT@b;aF!aklUaQoop(%=uEQZkIt9E56mQ5?M;X!Sd!~FF&6*`}XG)vSG3f$G=BXM$x<_v|!aYN>7bi6{da2thz}kJ;g+> z`G}T*Z38g#dU+qKMz9v}(oIOv^h9G-$CrY}J7MP0!?qtRx!#*IM~*Qd$s@Cov;kmVW#-aq;r`qvU<4bb z^^MYoX6zL-UuQhJNUOfgsrg*g^p%sm$4}X($LjgA4N|l|M7f_+b`mUI7b!L(GdF{k zn`qIRt6<3~bP+Al+4=U}^X$0?1*$zXs#(|lgnAB~n(v{y;q)nF=sYfNgDlhgM(enl zMzloB&DxE~J0Qnx)04LIpIzrPTqZv{A;jvT`7R)FBOum5N!|*S8@+hb=I$?neIu}X z1T1TSv(IZY+d518CjgY0+dr}Kd~SWe3vS`>(ZUr^-W-fE<;>7H29DZeUz@wMm^sv2pgJva1OEpv$P2slAl}~q0000< KMNUMnLSTZRtz}{W literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/sounds/Makefile.am b/kbattleship/kbattleship/sounds/Makefile.am new file mode 100644 index 00000000..459b8e50 --- /dev/null +++ b/kbattleship/kbattleship/sounds/Makefile.am @@ -0,0 +1,5 @@ +sounddir = $(kde_datadir)/kbattleship/sounds +sound_DATA = ship-player-shoot-water.ogg \ + ship-player1-shoot.ogg \ + ship-player2-shoot.ogg \ + ship-sink.ogg diff --git a/kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg b/kbattleship/kbattleship/sounds/ship-player-shoot-water.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ad677670c125afa997a15b47d22d1767396956af GIT binary patch literal 37703 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/sounds/ship-player1-shoot.ogg b/kbattleship/kbattleship/sounds/ship-player1-shoot.ogg new file mode 100644 index 0000000000000000000000000000000000000000..5fa88f22469fe44ec5a2dda4f009c53d1b41d8aa GIT binary patch literal 55778 zcmeFYWmp`|wm;f<2pTL9+$9j)9TI{=1_rkTcXv&&1d>2-CnV_L?vfB327lXEEKxs!zvK9SR;Sf!GK+V4`xsXlr~#iwdG!0aANpZitry&>D% zkytQrUm;S~ha@aY_5pnuv!q^JD61rrdM}Dhv%OCvl>4vXg{TcOSM~vQ*eZ_*Zs@wm zB~{o9Q9VLr+9q8Xfm}mcXo)DYFq5F7W+`Bz#i!5a0^J`*+jwKe|``aQGyDD0026& z83B1MiI)k_hqb{|b&hlY;SG~4DgIVRVn{e@YgL5#o(GKr^RFC`-3WOU#q%rVzd#Nl zSH}N_T`IbivHDdXKTWw(lsZGP);BIas!-*tAczND8CW_H&v_C6ZEX^aZ7>+^4zUv-%rscWqH+ z5Z^}&TN{(oN4k)dM)%fY2H}4f`CINU$jI@^5Jr4@JN{IB56d-@1+w|@45ukVyOiVDAdpbU@E$1!* zm;S>WWBnZ>5detAF|x-ou*a>6d}dOTvFYUiWH9gV$f!STpI1fVn3Up}ZR4zclL}MQ zwoU#XMX#7h60D#+9K{D{0N{xtl z0>lN@;+g=xn9u_MpPbs5fEqCpSZp8`1&Afo;^OK~!;WTpGiq1-)f^YJn^&pGVO?9_ z&l^r&Yw{#A;T55O5tDh8>>yX}2s=yZq_$i*Np6Zc2Pp_2gcM#lK$wQI>_R0|#fkKP zkpX~M0lX0jf(ikGf5n;9&^R^F1cuT6760E{WJU#KP9%{2Urdk<9ZEbpeE#TPan4~h zf!}BnuaK+9|JwcE+p0je_59I@#DBDfY)Inw{~P1_Uw8KZXYl`Z1PGB^-7@lTMo;pj ziUD{}47ex~F=5A{51}b0VW@@X{}4clvy>hCYRjYcZ!DOQ8M0$^QGgOMV?^LR!@slQ zkQrZX315WdA~S5B{P#VCEHNxVA4T{gg{6aE?LEWuNBP+QuJxZ3a%CoWL2lqYIk^Ab zCy^*i6d+_(Bnhb#d|uTfL+V&b%uxVfrk}=!TmX&b&- ztWo(zY5#C}@moMm3#=5E@0*2GmTc4VAyu4eCldEtlr|U~{;y;RWEc*1k# zo&&N)$ad4;M#r?(z@El*QTeuM5D26U=eY(6lBe;M=Udo^Z?+IfanmzGwR?7+#`>7| zJ%sG^^T~jboQ!poQ0}YjG*#hhf~S!ANtVEUjB@~qIzAOrY5w>%{v$_p#qTg20+}58 zr>G%p0MJ36PomY&!$O}+_@tXDj6{xwId2pMq00q<*h#~*l{uKdCxGnO!Gs|8qJ)QX z9L$Lz0GNJ@hWv+X+Vm;|bOb;MkHYOBb{Wu&I0uFTFH29;&aOBhA!N5lZ2{TJ=;y&LF^vVY+Qp#Zp_ z&F|qxB9+ASS6Xao`7BER2msReUw9@W#dr@rgky>nsTZh$!Nygbf3e(d;<<+n3ClmA zH9d1MyhqKFqOKGv5hP|tQ=XPHNpCru%XVk00N@ZE3;2pksq<2V{Rs}IfCcmYekns9 z8jDoPEeYiw(uXC09w6_E|Cs(nfFg3rf2^FmSEwdstdRkd3K{$x{dWMm&;57w{}IK7 zRL=ftX#Wvl0M}p<;8J?JLhPr~Z#^@lL{UPJ)4hW{kK7$sjy+HN0a5}U+6K{;a_@Oz zOu6F*4qfh0SvLB_$v2!??)Y-->|HwMuh_#tAZ4yPy=di}?{OgI(h0qfyhXW_2GOP6 zZ$2uq*NhuP^O7+-s1?;t+Z9)mo7;16)sUNi?0R5o;lPzYYlqkUfYFeXYr?>ur<;7i zFgmX;Et*&Aau&qNJ81*}-l1r~^LL;4OPDfAKVv?AQbhzHaib$dP7>stLC%U?&__|^ ztiYFhkfV)E`%BR$e7v7Mf6qT4Wn|i4Di*Z;FN#o^J@dZC{Sk>hFZ6FNp)y-uF3G=6 zOhvf~_Y_DDIa;Kk{+46TMQXVBX%BLc!c%4sM^?*AKn|Hbk4PDtIgALz&Ynl2{D2;r ziA>X0#?A>NLQc+m?2!zV*^$zv5Rd!6SL~^-q^4w`Rl^l0` z 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 literal 0 HcmV?d00001 diff --git a/kbattleship/kbattleship/sounds/ship-player2-shoot.ogg b/kbattleship/kbattleship/sounds/ship-player2-shoot.ogg new file mode 100644 index 0000000000000000000000000000000000000000..d4becd0a4c00ce3bfd2c7d7dbfb1d5868973ba07 GIT binary patch literal 55778 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 ZFnW8rW+gwra-Q*sWBGji(*c0={{TENg_8gP literal 0 HcmV?d00001 diff --git a/kblackbox/CHANGES b/kblackbox/CHANGES new file mode 100644 index 00000000..985fa5c9 --- /dev/null +++ b/kblackbox/CHANGES @@ -0,0 +1,52 @@ +1999-06-07 Petter Reinholdtsen + * kbbgame.cpp: Changed text Quit to Exit to match KDE style guide. + +0.3.0 from 0.2.6 (17.1.1999) + - KBBGraphic derived only from QWidget, not QTableView + -> a complete rewrite of gfx stuff + -> massive changes almost everywhere + - minimal size fixed + - new icons (I am just learning the povray :-) +0.2.6 from 0.2.5 (25.8.1998) + - the game is active after starting + - fixed bad drawing after setting the board size + - config. scripts from new kexample (KDE 1.0) + - moc dependencies in Makefile.am done by automoc to + avoid problems on Solaris (bug report by David Faure) + - some string fixes for better translation +0.2.5 from 0.2.4 (19.3.1998) + - es language support added + - sk language support added +0.2.4 from 0.2.3 (6.3.1998) + - pt language support added + - it language support added +0.2.3 from 0.2.2 (10.2.1998) + - new icon for fired laser + - new icon for "give up" + - "done" added to menu and toolbar (new icon) + - help file updated +0.2.2 from 0.2.1 (2.2.1998) + - "done" button removed - middle mouse button instead + - kde toolbar + - kde status bar + - new icons + - help file updated +0.2.1 from 0.2.0: + - KBlackBox.kdelnk + - icon kblackbox.xpm + - double updating during resize fixed + - saves the application size + - doesn't autoresize after changing a board size +0.2.0 from 0.1.1: + - kblackbox is now a KDE application -> help, configuration, ... + - autoconf usage (by kexample-0.4.0) + - remembers last used options + - pixmaps on fields in the box +0.1.1 from 0.1.0: + - you are warned if you try to change # of balls when a game is running + - game->resize works properly, even if you moved scrollbars now + - mouseMoveEvent reimplemented, so that you can "draw" when a button + is pressed + - marking with the right mouse button + - displaying more game statistics + - possibility to give up the game (shows you where the balls were) diff --git a/kblackbox/Makefile.am b/kblackbox/Makefile.am new file mode 100644 index 00000000..813aff83 --- /dev/null +++ b/kblackbox/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +bin_PROGRAMS = kblackbox +kblackbox_SOURCES = kbbgfx.cpp kbbgame.cpp util.cpp main.cpp +kblackbox_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kblackbox_LDADD = $(LIB_KDEGAMES) +kblackbox_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO +KDE_ICON = kblackbox + +xdg_apps_DATA = kblackbox.desktop + +EXTRA_DIST = VERSION CHANGES $(xdg_apps_DATA) + +rcdir = $(kde_datadir)/kblackbox +rc_DATA = kblackboxui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kblackbox.pot diff --git a/kblackbox/README b/kblackbox/README new file mode 100644 index 00000000..a7678515 --- /dev/null +++ b/kblackbox/README @@ -0,0 +1,28 @@ +Program: kblackbox +Author: Robert Cimrman, e-mail: cimrman3@students.zcu.cz + +A simple logical game for the KDE project. + +FEATURES: + -stunning gfx (in the next version :-) + -high logical requirements +USING: + -see help +KNOWN BUGS: + -none +UNKNOWN BUGS: + -still unknown + ->report any "bogus features" to the author, please +TODO: + -improve gfx? +HISTORY: + 23.4.1997 -> start + 28.4.1997 -> 0.1.0 + 29.4.1997 -> 0.1.1 + 29.7.1997 -> 0.2.0 + 2.2.1997 -> 0.2.2 + 10.2.1998 -> 0.2.3 + 6.3.1998 -> 0.2.4 + 19.3.1998 -> 0.2.5 + 25.8.1998 -> 0.2.6 + 17.1.1999 -> 0.3.0 diff --git a/kblackbox/VERSION b/kblackbox/VERSION new file mode 100644 index 00000000..064bd513 --- /dev/null +++ b/kblackbox/VERSION @@ -0,0 +1,2 @@ +kblackbox-0.3.0 + diff --git a/kblackbox/big_kblackbox.xpm b/kblackbox/big_kblackbox.xpm new file mode 100644 index 00000000..e448e5a7 --- /dev/null +++ b/kblackbox/big_kblackbox.xpm @@ -0,0 +1,269 @@ +/* XPM */ +static char * kblackbox_xpm[] = { +"48 48 218 2", +" c None", +". c #414141", +"+ c #404040", +"@ c #464646", +"# c #424242", +"$ c #484848", +"% c #585858", +"& c #818181", +"* c #7D7D7D", +"= c #7F7F7F", +"- c #4D4D4D", +"; c #727272", +"> c #5A5A5A", +", c #878787", +"' c #4B4B4B", +") c #6E6E6E", +"! c #666666", +"~ c #626262", +"{ c #767676", +"] c #555555", +"^ c #6F6F6F", +"/ c #5F5F5F", +"( c #797979", +"_ c #454545", +": c #858585", +"< c #272727", +"[ c #595959", +"} c #787878", +"| c #696969", +"1 c #5B5B5B", +"2 c #868686", +"3 c #262626", +"4 c #2F2F2F", +"5 c #2E2E2E", +"6 c #A5A5A5", +"7 c #575757", +"8 c #4C4C4C", +"9 c #4F4F4F", +"0 c #5E5E5E", +"a c #2D2D2D", +"b c #565656", +"c c #7E7E7E", +"d c #303030", +"e c #6B6B6B", +"f c #363636", +"g c #505050", +"h c #545454", +"i c #3F3F3F", +"j c #4E4E4E", +"k c #4A4A4A", +"l c #373737", +"m c #5D5D5D", +"n c #393939", +"o c #383838", +"p c #353535", +"q c #676767", +"r c #3D3D3D", +"s c #3E3E3E", +"t c #343434", +"u c #606060", +"v c #262600", +"w c #2F2F01", +"x c #5A5A06", +"y c #4B4B04", +"z c #2C2C01", +"A c #2D2D01", +"B c #2E2E01", +"C c #3C3C3C", +"D c #737373", +"E c #292900", +"F c #333301", +"G c #494949", +"H c #D4D400", +"I c #606000", +"J c #272700", +"K c #242400", +"L c #313103", +"M c #5C5C5C", +"N c #3B3B3B", +"O c #6B6B00", +"P c #404001", +"Q c #363603", +"R c #E0E000", +"S c #5E5E00", +"T c #5D5D00", +"U c #252500", +"V c #333304", +"W c #393907", +"X c #282828", +"Y c #878700", +"Z c #AAAA05", +"` c #373704", +" . c #303002", +".. c #323232", +"+. c #E9E902", +"@. c #5A5A00", +"#. c #565600", +"$. c #383806", +"%. c #343406", +"&. c #5A5A11", +"*. c #313131", +"=. c #292929", +"-. c #FFFF2B", +";. c #FFFF25", +">. c #5E5E03", +",. c #606002", +"'. c #545400", +"). c #5B5B05", +"!. c #353506", +"~. c #585811", +"{. c #515151", +"]. c #525252", +"^. c #F5F50A", +"/. c #646401", +"(. c #696903", +"_. c #EFEF0E", +":. c #535305", +"<. c #4D4D06", +"[. c #444406", +"}. c #54540D", +"|. c #6A6A0E", +"1. c #444444", +"2. c #DEDE00", +"3. c #626200", +"4. c #6E6E00", +"5. c #D6D60A", +"6. c #3F3F05", +"7. c #363605", +"8. c #3E3E05", +"9. c #53530C", +"0. c #61610D", +"a. c #4E4E0E", +"b. c #474747", +"c. c #B5B509", +"d. c #3D3D04", +"e. c #313104", +"f. c #83830C", +"g. c #7D7D0D", +"h. c #4D4D0D", +"i. c #646464", +"j. c #333333", +"k. c #434343", +"l. c #9F9F09", +"m. c #474706", +"n. c #51510A", +"o. c #7C7C0B", +"p. c #7C7C0C", +"q. c #7E7E0E", +"r. c #727205", +"s. c #202000", +"t. c #494909", +"u. c #7B7B0A", +"v. c #7D7D0C", +"w. c #3A3A3A", +"x. c #474708", +"y. c #484808", +"z. c #484809", +"A. c #7A7A0A", +"B. c #7B7B0B", +"C. c #9D9D0C", +"D. c #9D9D0D", +"E. c #2C2C2C", +"F. c #636363", +"G. c #464606", +"H. c #474707", +"I. c #999909", +"J. c #9B9B0A", +"K. c #8F8F0B", +"L. c #90900C", +"M. c #535353", +"N. c #585806", +"O. c #464607", +"P. c #656507", +"Q. c #676708", +"R. c #8D8D09", +"S. c #6D6D02", +"T. c #6C6C02", +"U. c #62620F", +"V. c #606005", +"W. c #454506", +"X. c #4C4C06", +"Y. c #2B2B01", +"Z. c #636301", +"`. c #616102", +" + c #7C7C02", +".+ c #70700B", +"++ c #646411", +"@+ c #575701", +"#+ c #939301", +"$+ c #989801", +"%+ c #949401", +"&+ c #8F8F01", +"*+ c #686801", +"=+ c #717103", +"-+ c #94940B", +";+ c #868601", +">+ c #8F8F02", +",+ c #707000", +"'+ c #B0B00E", +")+ c #787800", +"!+ c #A5A504", +"~+ c #717171", +"{+ c #686868", +"]+ c #D5D50A", +"^+ c #555502", +"/+ c #5C5C02", +"(+ c #353504", +"_+ c #616161", +":+ c #FFFF21", +"<+ c #606001", +"[+ c #626201", +"}+ c #6A6A02", +"|+ c #E4E404", +"1+ c #D8D801", +"2+ c #616100", +"3+ c #686800", +"4+ c #949400", +"5+ c #747474", +" ", +" ", +" ", +" . + + . + @ + + # ", +" . . + . + $ + % & . + + + . + . * + + . + + . . + + ", +" + + . . = - ; > + + + . # + . , . ' ) ! ~ + + . { + . . % + ] + + ^ . / + + ", +" + + . # . + + . . . . . + . . . ( + + . + + . ~ _ + . . + . + . + # : / . + + + + < < ", +"$ _ _ . + . . + . + . + # . + + . . + . # [ ^ % - $ } | 1 % 2 + + # . + . # ) . + + 3 < 3 ", +"4 5 5 4 4 4 - . . + . . # . + + . . + + . # . + + + # 6 . + + . 7 8 9 . . . 0 [ + 3 4 3 3 ", +"4 4 5 a ' 5 5 4 4 4 4 5 + # * 7 . b c . . # + . # + # . . + . . . . . + . . + + 3 3 < 3 ", +"5 5 d e 4 4 4 4 4 4 4 4 5 f g 5 # 4 . . . . + . . + . + + . . + h + . + = i { < < < < < ", +"d 5 5 g 5 4 4 d j d 4 4 7 5 4 5 5 k l 5 5 5 5 5 + + + . + . . m @ + . + # 3 7 < 3 < 3 < ", +"4 n o 4 5 4 4 ! 5 l p 4 o 4 a 4 4 7 q 5 4 5 4 b ' 5 5 d 4 5 + + + [ ) j 3 3 3 3 3 3 < < ", +"5 5 ' 4 4 4 4 4 5 4 9 5 4 5 5 5 5 5 ; g r 4 4 5 s k t 4 d 4 5 4 4 4 5 < < 3 < 3 < < 3 < ", +"5 4 5 u d 5 v w x y z A B B 4 5 C [ D 0 5 5 4 4 5 d # 5 4 4 5 4 # 4 4 3 3 < < < < 3 < ", +" 4 5 u 4 E w F 5 5 p G H I I J K L % M f 5 5 5 4 4 5 4 5 4 4 4 5 G 4 < < f 3 3 3 3 < ", +" 4 5 N O P Q 9 p n u 4 4 4 4 R S T U V W 5 5 d 4 4 5 4 4 4 5 5 % 4 4 a < < X 3 < 3 X ", +" 8 p 5 Y Z ` ...4 q g 5 5 _ 9 5 +.@.#.$.%.&.5 4 5 4 5 5 5 4 4 *.@ 4 < < < =.3 < l 5 ", +" 5 d / 5 -.;.>.,.4 4 k 4 4 N o 5 4 5 '.).!.%.~.5 5 5 5 4 4 5 d i {.[ < < # ] < o < < ", +" d 4 5 ].^./.(.5 4 5 5 4 5 4 4 5 4 _.:.<.[.}.}.|.5 4 5 4 5 4 i {.p i < < 1.3 < s ", +" 4 5 4 5 5 2.3.4.4 4 d 4 N 4 5 4 4 j 5.6.7.8.9.0.a.4 5 5 4 5 5 s ' 3 3 < 3 C < h ", +" p G b.4 4 4 5 4 5 5 4 4 4 f 5 s 4 [ s c.d.e.f.g.h.a.4 5 4 o G i.j.< < p u < =.3 ", +" 4 4 5 4 5 d 4 4 # 4 5 d 4 5 # 0 . 4 k.l.m.n.o.p.g.q.4 5 k.1 4 r 5 3 p 3 < 3 < < ", +" ) 4 5 5 5 ; 5 d 5 4 4 5 5 4 $ 5 4 5 r.s.t.u.o.v.g.q.5 4 4 5 N 3 3 s 3 X < *.3 ", +" r 4 4 4 5 5 s 4 4 _ d 4 d w.4 4 4 4 x.y.z.A.B.o.C.D.4 5 4 4 4 < =.E.3 < k < ", +" 5 ^ 4 4 4 4 4 4 4 F.p t j.5 5 4 d G.H.x.y.I.J.K.L.S *.d 5 4 4 3 < X 3 1.3 < ", +" M.p *.5 1.4 5 4 5 ] 5 d 4 5 4 4 N.G.O.P.Q.R.S S.T.T 4 4 4 5 4 < < 3 e t 3 3 ", +" 5 # 4 r 4 4 4 4 4 4 5 5 4 5 U.V.W.X.Y.Z.`.T.T. +5 d 5 4 5 4 < 3 o < 3 < 3 ", +" 4 4 5 - 5 4 5 d 4 4 d 4 s. ..+++@+#+$+%+&+u 4 5 4 4 4 5 5 < < < 3 3 < < ", +" 5 4 4 p 5 4 d *.d 5 5 *+=+-+;+>+#+4 4 M.4 5 D 4 5 5 5 5 w.3 3 3 < 3 < % ", +" 5 o 5 4 k s 5 5 5 5 ,+'+)+5 5 4 4 s g 4 4 s . 4 4 4 5 % < < ..3 j.< 3 ", +" l 5 b.5 ] ..) ' 4 4 !+4 4 4 4 4 4 + 4 4 4 4 5 4 4 5 5 p < < 3 < b.3 ", +" p t r 5 a 4 ~+5 7 5 4 5 4 4 4 4 5 + m 4 d 5 4 4 4 5 j.4 ' 3 C 3 w.N ", +" d 4 4 d 5 5 s + 4 ~ 5 @ 1.5 5 5 + 1.d 5 4 4 t 5 d d 4 4 ]._ 3 =.< ", +" 5 4 d {+5 4 5 ]+^+/+(+5 _ k 5 _+5 5 4 5 5 d 5 4 5 5 4 X < l < 5 ", +" 4 5 4 a 5 4 t 4 :+<+[+}+4 4 d 4 i 5 4 4 5 8 $ h 5 4 4 5 < 3 < ", +" 5 4 d _ 4 *.k.|+1+2+3+4 4 4 4 d 4 a 5 5 - % d 5 5 3 ' 3 X ", +" > 5 5 5 M.5 _+4 4+4 5 w.b.C 4 4 4 4 a 4 4 4 3 < < k. ", +" 4 8 s 5 5 4 4 5 4 g 5+d 0 4 4 4 4 5 5 4 3 3 < b ", +" 4 4 4 b.5 @ 5 5 i i.4 s 8 5 4 4 5 4 i < G ", +" d 5 4 4 4 _ 5 4 5 [ ' 5 j.4 {.l . d 3 < ", +" 4 5 5 5 j.5 4 4 5 d 5 d h 4 4 =.3 < ", +" 4 4 5 ].4 d 4 4 4 4 4 r 5 < 3 ", +" 4 4 5 u 4 4 5 _ ; 1.- 3 < ", +" 5 5 5 4 d N 5 4 4 3 ", +" 5 5 d 1 4 a < 3 ", +" 5 _ % 4 3 ", +" 4 4 p "}; diff --git a/kblackbox/hi128-app-kblackbox.png b/kblackbox/hi128-app-kblackbox.png new file mode 100644 index 0000000000000000000000000000000000000000..76d581e0355815f23c73656e7e02fbf27a6661c2 GIT binary patch literal 9260 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 literal 0 HcmV?d00001 diff --git a/kblackbox/hi16-app-kblackbox.png b/kblackbox/hi16-app-kblackbox.png new file mode 100644 index 0000000000000000000000000000000000000000..2b28f5ee483087152eca600c1b5ba2aa4db80487 GIT binary patch literal 735 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 literal 0 HcmV?d00001 diff --git a/kblackbox/hi64-app-kblackbox.png b/kblackbox/hi64-app-kblackbox.png new file mode 100644 index 0000000000000000000000000000000000000000..f97a1f5ced25816a1bd9ca13beee66f9274d8d41 GIT binary patch literal 4202 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 literal 0 HcmV?d00001 diff --git a/kblackbox/kbbgame.cpp b/kblackbox/kbbgame.cpp new file mode 100644 index 00000000..82028871 --- /dev/null +++ b/kblackbox/kbbgame.cpp @@ -0,0 +1,740 @@ +// +// KBlackbox +// +// A simple game inspired by an emacs module +// +// File: kbbgame.cpp +// +// The implementation of the KBBGame widget +// + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbbgame.h" +#include "util.h" +#include "version.h" + +/* + Names of pixmap files. +*/ + +const char *pFNames[NROFTYPES] = { + "white", + "gray", + "green", + "red", + "blue", + "cyan", + "brown", + "green2" +}; + +/* + Creates the KBBGame widget and sets saved options (if any). +*/ + +KBBGame::KBBGame() +{ + int i; + + QPixmap **pix = new QPixmap * [NROFTYPES]; + pix[0] = new QPixmap(); + *pix[0] = BarIcon( pFNames[0] ); + if (!pix[0]->isNull()) { + kdDebug(12009) << "Pixmap \"" << pFNames[0] << "\" loaded." << endl; + for (i = 1; i < NROFTYPES; i++) { + pix[i] = new QPixmap; + *pix[i] = BarIcon( pFNames[i] ); + if (!pix[i]->isNull()) { + kdDebug(12009) << "Pixmap \"" << pFNames[i] << "\" loaded." << endl; + } else { + pix[i] = pix[i-1]; + pix[i]->detach(); + kdDebug(12009) << "Cannot find pixmap \"" << pFNames[i] << "\". Using previous one." << endl; + } + } + } else { + kdDebug(12009) << "Cannot find pixmap \"" << pFNames[0] << "\". Pixmaps will not be loaded." << endl; + delete pix[0]; + delete pix; + pix = NULL; + } + gr = new KBBGraphic( pix, this, "KBBGraphic" ); + + statusBar()->insertItem(i18n("Score: 0000"), SSCORE); + statusBar()->insertItem(i18n("Placed: 00 / 00"), SBALLS); + statusBar()->insertItem(i18n("Run: yesno"), SRUN); + statusBar()->insertItem(i18n("Size: 00 x 00"), SSIZE); + + initKAction(); + + connect( gr, SIGNAL(inputAt(int,int,int)), + this, SLOT(gotInputAt(int,int,int)) ); + connect( this, SIGNAL(gameRuns(bool)), + gr, SLOT(setInputAccepted(bool)) ); + connect( gr, SIGNAL(endMouseClicked()), + this, SLOT(gameFinished()) ); + + /* + QToolTip::add( doneButton, i18n( + "Click here when you think you placed all the balls.") ); + */ + + /* + Game initializations + */ + running = FALSE; + gameBoard = NULL; + + KConfig *kConf; + int j; + kConf = kapp->config(); + kConf->setGroup( "KBlackBox Setup" ); + if (kConf->hasKey( "Balls" )) { + i = kConf->readNumEntry( "Balls" ); + balls = i; + switch (i) { + case 4: ballsAction->setCurrentItem(0); break; + case 6: ballsAction->setCurrentItem(1); break; + case 8: ballsAction->setCurrentItem(2); break; + } + } else { + balls = 4; + ballsAction->setCurrentItem(0); + } + if ((kConf->hasKey( "Width" )) && + (kConf->hasKey( "Balls" ))) { + i = kConf->readNumEntry( "Width" ); + j = kConf->readNumEntry( "Height" ); + gr->setSize( i+4, j+4 ); // +4 is the space for "lasers" and an edge... + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + switch (i) { + case 8: sizeAction->setCurrentItem(0); break; + case 10: sizeAction->setCurrentItem(1); break; + case 12: sizeAction->setCurrentItem(2); break; + } + } else { + gr->setSize( 8+4, 8+4 ); // +4 is the space for "lasers" and an edge... + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + sizeAction->setCurrentItem(0); + } + if (kConf->hasKey( "tutorial" )) { + tutorial = (bool) kConf->readNumEntry( "tutorial" ); + } else tutorial = FALSE; + tutorialAction->setChecked(tutorial); + + setCentralWidget( gr ); + + setScore( 0 ); + ballsPlaced = 0; + + updateStats(); + + newGame(); + setMinSize(); + + setupGUI(); +} + +/* + Saves the options and destroys the KBBGame widget. +*/ + +KBBGame::~KBBGame() +{ + KConfig *kConf; + QString s; + + kConf = kapp->config(); + kConf->setGroup( "KBlackBox Setup" ); + kConf->writeEntry( "Balls", balls ); + kConf->writeEntry( "Width", gr->numC() - 4); + kConf->writeEntry( "Height", gr->numR() - 4); + kConf->writeEntry( "tutorial", (int) tutorial ); + + delete gameBoard; + // All the rest has "this" for parent so it doesn't need to be deleted. +} + + +/* + Resizes yourself to fit the contents perfectly, from menu. +*/ +void KBBGame::gameResize() +{ + resize( gr->wHint(), gr->hHint() + menuBar()->height() + statusBar()->height() + + toolBar()->height() ); +} + +void KBBGame::setMinSize() +{ + setMinimumSize( gr->wHint(), gr->hHint() + menuBar()->height() + statusBar()->height() + + toolBar()->height() ); +} + +/* + Settings of various options. +*/ +void KBBGame::slotSize() +{ + int i = sizeAction->currentItem(); + bool ok = false; + switch (i) { + case 0: + ok = setSize( 8, 8 ); + break; + case 1: + ok = setSize( 10, 10 ); + break; + case 2: + ok = setSize( 12, 12 ); + break; + } + + if (!ok) { + switch(gr->numR() - 4) { + case 8: + sizeAction->setCurrentItem(0); break; + case 10: + sizeAction->setCurrentItem(1); break; + case 12: + sizeAction->setCurrentItem(2); break; + } + } +} + +void KBBGame::slotBalls() +{ + int i = ballsAction->currentItem(); + bool ok = false; + switch (i) { + case 0: + ok = setBalls( 4 ); + break; + case 1: + ok = setBalls( 6 ); + break; + case 2: + ok = setBalls( 8 ); + break; + } + if (!ok) { + switch (balls) { + case 4: + ballsAction->setCurrentItem(0); break; + case 6: + ballsAction->setCurrentItem(1); break; + case 8: + ballsAction->setCurrentItem(2); break; + } + } +} + +void KBBGame::tutorialSwitch() +{ + tutorial = !tutorial; +} + +/* + Creates a new game. +*/ +void KBBGame::newGame() +{ + int i, j; + + if (running) { + bool cancel; + cancel = KMessageBox::warningContinueCancel(0, + i18n("Do you really want to give up this game?"),QString::null,i18n("Give Up")) + == KMessageBox::Cancel; + if (cancel) + return; + + abortGame(); + } + + gameBoard->fill( INNERBBT ); + for (j = 0; j < (gr->numR()); j++) { + gameBoard->set( 0, j, OUTERBBT ); + gameBoard->set( gr->numC()-1, j, OUTERBBT ); + } + for (i = 0; i < (gr->numC()); i++) { + gameBoard->set( i, 0, OUTERBBT ); + gameBoard->set( i, gr->numR()-1, OUTERBBT ); + } + for (j = 2; j < (gr->numR()-2); j++) { + gameBoard->set( 1, j, LASERBBT ); + gameBoard->set( gr->numC()-2, j, LASERBBT ); + } + for (i = 2; i < (gr->numC()-2); i++) { + gameBoard->set( i, 1, LASERBBT ); + gameBoard->set( i, gr->numR()-2, LASERBBT ); + } + gameBoard->set( 1, 1, OUTERBBT ); + gameBoard->set( 1, gr->numR()-2, OUTERBBT ); + gameBoard->set( gr->numC()-2, 1, OUTERBBT ); + gameBoard->set( gr->numC()-2, gr->numR()-2, OUTERBBT ); + + randomBalls( balls ); + remap( gameBoard, gr->getGraphicBoard() ); + gr->repaint( TRUE ); + setScore( 0 ); + detourCounter = -1; + ballsPlaced = 0; + running = TRUE; + updateStats(); + emit gameRuns( running ); +} + +/* + Ends the current game. +*/ + +void KBBGame::gameFinished() +{ + if (running) { + QString s; + if (ballsPlaced == balls) { + getResults(); + abortGame(); + if (score <= (balls*3)) + s = i18n("Your final score is: %1\n" + "You did really well!"); + else + s = i18n("Your final score is: %1\n" + "I guess you need more practice."); + + KMessageBox::information(this, + s.arg(KGlobal::locale()->formatNumber(score, 0))); + } else { + s = i18n( "You should place %1 balls!\n" + "You have placed %2.") + .arg(KGlobal::locale()->formatNumber(balls, 0)) + .arg(KGlobal::locale()->formatNumber(ballsPlaced, 0)); + + KMessageBox::sorry(this, s); + } + } +} + +/* + Computes the final score and indicate errors. +*/ + +void KBBGame::getResults() +{ + int i, j, tgam, tgra; + RectOnArray *r = gr->getGraphicBoard(); + for (j = 0; j < (gr->numR()); j++) { + for (i = 0; i < (gr->numC()); i++) { + tgam = gameBoard->get( i, j ); + tgra = r->get( i, j ); + if ((tgam == BALLBBT) && (tgra != TBALLBBG)) { + setScore( score+5 ); + r->set( i, j, WBALLBBG ); + gr->updateElement( i, j ); + } + if ((tgam != BALLBBT) && (tgra == TBALLBBG)) { + r->set( i, j, FBALLBBG ); + gr->updateElement( i, j ); + } + } + } +} + +/* + Aborts the current game. +*/ + +void KBBGame::abortGame() +{ + if (running) { + running = FALSE; + ballsPlaced = 0; + updateStats(); + gr->clearFocus(); + emit gameRuns( running ); + } +} + +/* + Gives the game up. +*/ + +void KBBGame::giveUp() +{ + if (running) { + bool stop; + stop = KMessageBox::warningContinueCancel(0, + i18n( + "Do you really want to give up this game?"),QString::null,i18n("Give Up")) + == KMessageBox::Continue; + + if (stop) { + getResults(); + abortGame(); + } + } +} + +/* + Displays game statistics. +*/ + +void KBBGame::updateStats() +{ + QString tmp; + QString s = i18n("Run: "); + if (running) + s += i18n("Yes"); + else + s += i18n("No"); + statusBar()->changeItem( s, SRUN ); + s = i18n( "Size: " ); + s += tmp.sprintf( "%2d x %2d", + gr->numC()-4, gr->numR()-4 ); + statusBar()->changeItem( s, SSIZE ); + s = i18n( "Placed: " ); + s += tmp.sprintf( "%2d / %2d", + ballsPlaced, balls ); + statusBar()->changeItem( s, SBALLS ); +} + +/* + Sets the score value to n. +*/ + +void KBBGame::setScore( int n ) +{ + score = n; + statusBar()->changeItem( i18n("Score: %1").arg(n), SSCORE ); +} + +/* + Sets the size of the black box. +*/ + +bool KBBGame::setSize( int w, int h ) +{ + bool ok = FALSE; + if (((w+4) != gr->numC()) || ((h+4) != gr->numR())) { + if (running) { + ok = KMessageBox::warningContinueCancel(0, + i18n( + "This will be the end of the current game!"),QString::null,i18n("End Game")) + == KMessageBox::Continue; + + } else ok = TRUE; + if (ok) { + gr->setSize( w+4, h+4 ); // +4 is the space for "lasers" and an edge... + setMinSize(); + gameResize(); + delete gameBoard; + gameBoard = new RectOnArray( gr->numC(), gr->numR() ); + if (running) abortGame(); + newGame(); + // gr->repaint( TRUE ); + } + } + return ok; +} + +/* + Sets the number of balls in the black box to n. +*/ + +bool KBBGame::setBalls( int n ) +{ + bool ok = FALSE; + if (balls != n) { + if (running) { + ok = KMessageBox::warningContinueCancel(0, + i18n("This will be the end of the current game!"),QString::null,i18n("End Game")) + == KMessageBox::Continue; + } else ok = TRUE; + if (ok) { + balls = n; + if (running) abortGame(); + newGame(); + } + } + return ok; +} + +/* + Puts n balls in the black box on random positions. +*/ + +void KBBGame::randomBalls( int n ) +{ + int i; + random.setSeed(0); + for (i = 0; i < n; i++) { + int x=0, y=0; // there is OUTERBBT... + while (gameBoard->get( x, y ) != INNERBBT ) { + x = 2 + random.getLong(gameBoard->width()-4); + y = 2 + random.getLong(gameBoard->height()-4); + } + gameBoard->set( x, y, BALLBBT ); + } +} + +/* + This is, in fact, the whole game... +*/ + +int KBBGame::traceRay( int startX, int startY, int *endX, int *endY ) +{ + int type, x, y, d, refl; + int slx, scx, srx, sly, scy, sry; + bool directionChanged; + *endX = x = startX; + *endY = y = startY; + /* + Just to avoid compiler warnings + */ + type = slx = scx = srx = sly = scy = sry = 0; + /* + Get the initial direction d. + 0 .. up, 1 .. right, 2 .. down, 3 .. left + (0,0) is the upper-left corner. + */ + if ((gameBoard->get( x, y-1 ) == INNERBBT) || + (gameBoard->get( x, y-1 ) == BALLBBT)) { d = 0; } + else if ((gameBoard->get( x+1, y ) == INNERBBT) || + (gameBoard->get( x+1, y ) == BALLBBT)) { d = 1; } + else if ((gameBoard->get( x, y+1 ) == INNERBBT) || + (gameBoard->get( x, y+1 ) == BALLBBT)) { d = 2; } + else if ((gameBoard->get( x-1, y ) == INNERBBT) || + (gameBoard->get( x-1, y ) == BALLBBT)) { d = 3; } + else return WRONGSTART; + /* + And now trace the ray. + */ + while (1) { + switch (d) { + case 0: + slx = -1; scx = 0; srx = 1; + sly = -1; scy = -1; sry = -1; + break; + case 1: + slx = 1; scx = 1; srx = 1; + sly = -1; scy = 0; sry = 1; + break; + case 2: + slx = 1; scx = 0; srx = -1; + sly = 1; scy = 1; sry = 1; + break; + case 3: + slx = -1; scx = -1; srx = -1; + sly = 1; scy = 0; sry = -1; + break; + } + directionChanged = FALSE; + if (gameBoard->get( x+scx, y+scy ) == LASERBBT) { + type = DETOUR; + *endX = x+scx; + *endY = y+scy; + break; + } + if (gameBoard->get( x+scx, y+scy ) == BALLBBT) { + type = HIT; + break; + } + refl = 0; + if (gameBoard->get( x+slx, y+sly ) == BALLBBT) { + type = REFLECTION; + if (gameBoard->get( x, y ) == LASERBBT) break; + directionChanged = TRUE; + refl += 1; + } + if (gameBoard->get( x+srx, y+sry ) == BALLBBT) { + type = REFLECTION; + if (gameBoard->get( x, y ) == LASERBBT) break; + directionChanged = TRUE; + refl +=2; + } + // turn to the right + if (refl == 1) d = (d + 1) % 4; + // turn to the left + if (refl == 2) if ((d -= 1) < 0) d += 4; + // turn back -- no need to trace again the same way + if (refl == 3) break; + if (!directionChanged) { + x += scx; + y += scy; + } + } + return type; +} + +/* + Remaps the gameBoard to its graphic representation. +*/ + +void KBBGame::remap( RectOnArray *gam, RectOnArray *gra ) +{ + int i, j; + for (j = 0; j < (gam->height()); j++) { + for (i = 0; i < (gam->width()); i++) { + switch (gam->get( i,j )) { + case BALLBBT: if (tutorial) { gra->set( i,j, WBALLBBG ); break; } + case INNERBBT: gra->set( i,j, INNERBBG ); break; + case OUTERBBT: gra->set( i,j, OUTERBBG ); break; + case LASERBBT: gra->set( i,j, LASERBBG ); break; + default: gra->set( i,j, OUTERBBG ); + } + } + } +} + +/* + Processes the user input. +*/ + +void KBBGame::gotInputAt( int col, int row, int state ) +{ + RectOnArray *r = gr->getGraphicBoard(); + int type = r->get( col, row ); + int x, y; + int ex, ey; + int w = gameBoard->width() - 2; + int h = gameBoard->height() - 2; + + if (state & LeftButton) { + switch (type) { + case WBALLBBG: // because of the tutorial mode + case INNERBBG: + r->set( col, row, TBALLBBG ); + ballsPlaced++; + break; + case MARK1BBG: + r->set( col, row, INNERBBG ); + break; + case TBALLBBG: + r->set( col, row, INNERBBG ); + ballsPlaced--; + break; + case LASERBBG: + int endX, endY, result; + result = traceRay( col, row, &endX, &endY ); + r->set( col, row, LFIREBBG ); + //kdDebug << endX << " " << endY << endl; + if (col == 1) x = 0; else + if (col == w) x = w + 1; + else x = col; + + if (row == 1) y = 0; else + if (row == h) y = h + 1; + else y = row; + + switch (result) { + case DETOUR: + r->set( endX, endY, LFIREBBG ); + r->set( x, y, detourCounter ); + if (endX == 1) ex = 0; else + if (endX == w) ex = w + 1; + else ex = endX; + if (endY == 1) ey = 0; else + if (endY == h) ey = h + 1; + else ey = endY; + r->set( ex, ey, detourCounter-- ); + gr->updateElement( x, y ); + gr->updateElement( ex, ey ); + gr->updateElement( endX, endY ); + setScore( score+2 ); + break; + case REFLECTION: + r->set( x, y, RLASERBBG ); + gr->updateElement( x, y ); + setScore( score+1 ); + break; + case HIT: + r->set( x, y, HLASERBBG ); + gr->updateElement( x, y ); + setScore( score+1 ); + break; + case WRONGSTART: + kdDebug(12009) << "Wrong start?! It should't happen!!" << endl; + break; + } + break; + } + } else if (state & RightButton) { + switch (type) { + case INNERBBG: + r->set( col, row, MARK1BBG ); + break; + /*case MARK1BBG: + r->set( col, row, INNERBBG ); + break;*/ + } + } + gr->updateElement( col, row ); + updateStats(); +} + +void KBBGame::initKAction() +{ +// game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + (void)new KAction( i18n("&Give Up"), SmallIcon("giveup"), 0, this, SLOT(giveUp()), actionCollection(), "game_giveup" ); + (void)new KAction( i18n("&Done"), SmallIcon("done"), 0, this, SLOT(gameFinished()), actionCollection(), "game_done" ); + (void)new KAction( i18n("&Resize"), 0, this, SLOT(slotResize()), actionCollection(), "game_resize" ); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + +// settings + sizeAction = new KSelectAction( i18n("&Size"), 0, this, SLOT(slotSize()), actionCollection(), "options_size"); + QStringList list; + list.append(i18n(" 8 x 8 ")); + list.append(i18n(" 10 x 10 ")); + list.append(i18n(" 12 x 12 ")); + sizeAction->setItems(list); + + ballsAction = new KSelectAction( i18n("&Balls"), 0, this, SLOT(slotBalls()), actionCollection(), "options_balls"); + list.clear(); + list.append(i18n(" 4 ")); + list.append(i18n(" 6 ")); + list.append(i18n(" 8 ")); + ballsAction->setItems(list); + tutorialAction = new KToggleAction( i18n("&Tutorial"), 0, this, SLOT(tutorialSwitch()), actionCollection(), "options_tutorial" ); +// KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), +//actionCollection()); + +// keyboard only + (void)new KAction( i18n("Move Down"), Qt::Key_Down, gr, SLOT(slotDown()), actionCollection(), "move_down" ); + (void)new KAction( i18n("Move Up"), Qt::Key_Up, gr, SLOT(slotUp()), actionCollection(), "move_up" ); + (void)new KAction( i18n("Move Left"), Qt::Key_Left, gr, SLOT(slotLeft()), actionCollection(), "move_left" ); + (void)new KAction( i18n("Move Right"), Qt::Key_Right, gr, SLOT(slotRight()), actionCollection(), "move_right" ); + (void)new KAction( i18n("Trigger Action"), Qt::Key_Return, gr, SLOT(slotInput()), actionCollection(), "move_trigger" ); +} + +void KBBGame::slotResize() +{ + setMinSize(); + gameResize(); +} + +#include "kbbgame.moc" diff --git a/kblackbox/kbbgame.h b/kblackbox/kbbgame.h new file mode 100644 index 00000000..70f6ad48 --- /dev/null +++ b/kblackbox/kbbgame.h @@ -0,0 +1,104 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgame.h +// +// The definition of the KBBGame widget +// + +#ifndef KBBGAME_H +#define KBBGAME_H + +#include "kbbgfx.h" +#include "util.h" + +#include +#include + +class KSelectAction; +class KToggleAction; + +/* + Types of the boxes (used f.e.g. in the traceRay() method) +*/ + +#define OUTERBBT 0 +#define INNERBBT 1 +#define LASERBBT 2 +#define BALLBBT 3 + +/* + Ray-tracing results. +*/ + +#define WRONGSTART -1 +#define DETOUR 0 +#define REFLECTION 1 +#define HIT 2 + +/* + * Statusbar IDs. + */ +#define SSCORE 0 +#define SBALLS 1 +#define SRUN 2 +#define SSIZE 3 + +class KBBGame : public KMainWindow +{ + Q_OBJECT +public: + KBBGame(); + ~KBBGame(); + +signals: + void gameRuns( bool ); + +private slots: + void slotSize(); + void slotBalls(); + + void tutorialSwitch(); + + void newGame(); + + bool setSize( int w, int h ); + bool setBalls( int n ); + void setMinSize(); + void randomBalls( int n ); + void slotResize(); + void gameResize(); + void setScore( int n ); + void updateStats(); + void gameFinished(); + void abortGame(); + void giveUp(); + void gotInputAt( int col, int row, int state ); + +private: + int traceRay( int startX, int startY, int *endX, int *endY ); + void remap( RectOnArray *gam, RectOnArray *gra ); + void getResults(); + void initKAction(); + + int balls; + int detourCounter; + int ballsPlaced; + bool running; + bool tutorial; + RectOnArray *gameBoard; + KBBGraphic *gr; + + int score; + /* QLabel *scoreText; + QLabel *statusText;*/ + KRandomSequence random; + + KSelectAction *ballsAction, *sizeAction; + KToggleAction *tutorialAction; +}; + +#endif // KBBGAME_H diff --git a/kblackbox/kbbgfx.cpp b/kblackbox/kbbgfx.cpp new file mode 100644 index 00000000..d37edc1e --- /dev/null +++ b/kblackbox/kbbgfx.cpp @@ -0,0 +1,473 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgfx.cpp +// +// The implementation of the KBBGraphic widget +// + +#include +#include +#include +#include +#include + +#include "kbbgfx.h" +#include "util.h" + +/* + Constructs a KBBGraphic widget. +*/ + +KBBGraphic::KBBGraphic( QPixmap **p, QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + int i; + + curRow = curCol = 0; + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + setCellWidth( CELLW ); // set width of cell in pixels + setCellHeight( CELLH ); // set height of cell in pixels + setMouseTracking( FALSE ); + + pix = p; + if (pix == NULL) pixScaled = NULL; + else { + pixScaled = new QPixmap * [NROFTYPES]; + for (i = 0; i < NROFTYPES; i++) { + pixScaled[i] = new QPixmap; + } + } + graphicBoard = NULL; + drawBuffer = NULL; +} + +/* + Destructor: deallocates memory for contents +*/ + +KBBGraphic::~KBBGraphic() +{ + int i; + + if (pix != NULL) { + for (i = 0; i < NROFTYPES; i++) { + delete pix[i]; + } + delete pix; + } + if (pixScaled != NULL) { + for (i = 0; i < NROFTYPES; i++) { + delete pixScaled[i]; + } + delete pixScaled; + } + delete graphicBoard; + delete drawBuffer; +} + +/* + Sets the size of the table +*/ + +void KBBGraphic::setSize( int w, int h ) +{ + if ((w != numCols) || (h != numRows)) { + delete graphicBoard; + graphicBoard = new RectOnArray( w, h ); + graphicBoard->fill( OUTERBBG ); + setNumCols( w ); + setNumRows( h ); + setCellWidth( CELLW ); + setCellHeight( CELLH ); + minW = cellW * numRows; + minH = cellH * numCols; + emit(sizeChanged()); + } +} + +void KBBGraphic::setCellWidth( int w ) +{ + cellW = w; +} + +void KBBGraphic::setCellHeight( int h ) +{ + cellH = h; +} + +void KBBGraphic::setNumRows( int rows ) +{ + numRows = rows; +} + +void KBBGraphic::setNumCols( int cols ) +{ + numCols = cols; +} + +/* + Scales all pixmaps to desired size. +*/ + +void KBBGraphic::scalePixmaps( int w, int h ) +{ + int i, w0, h0; + QWMatrix wm; + + w0 = pix[0]->width(); + h0 = pix[0]->height(); + wm.scale( (float) w / (float) w0, (float) h / (float) h0 ); + for (i = 0; i < NROFTYPES; i++) { + *pixScaled[i] = pix[i]->xForm( wm ); + } +} + +/* + Returns the sizes of the table +*/ + +int KBBGraphic::numC() { return numCols; } +int KBBGraphic::numR() { return numRows; } +int KBBGraphic::width() { return cellW * numRows; } +int KBBGraphic::height() { return cellH * numCols; } +int KBBGraphic::wHint() const { return minW; } +int KBBGraphic::hHint() const { return minH; } +QSize KBBGraphic::sizeHint() const { return QSize(wHint(), hHint()); } + +/* + Returns a pointer to graphicBoard +*/ + +RectOnArray *KBBGraphic::getGraphicBoard() { return graphicBoard; } + +/* + Handles cell painting for the KBBGraphic widget. +*/ + +void KBBGraphic::paintCell( QPainter* p, int row, int col ) +{ + if (pix == NULL) paintCellDefault( p, row, col ); + else paintCellPixmap( p, row, col ); +} + +void KBBGraphic::paintCellPixmap( QPainter* p, int row, int col ) +{ + int w = cellW; + int h = cellH; + int x2 = w - 1; + int y2 = h - 1; + int type; + QPixmap pm; + + // kdDebug(12009) << p->viewport().width() << endl; + + switch (type = graphicBoard->get( col, row )) { + case MARK1BBG: pm = *pixScaled[MARK1BBG]; break; + case OUTERBBG: pm = *pixScaled[OUTERBBG]; break; + case INNERBBG: pm = *pixScaled[INNERBBG]; break; + case LASERBBG: pm = *pixScaled[LASERBBG]; break; + case LFIREBBG: pm = *pixScaled[LFIREBBG]; break; + case FBALLBBG: pm = *pixScaled[FBALLBBG]; break; + case TBALLBBG: pm = *pixScaled[TBALLBBG]; break; + case WBALLBBG: pm = *pixScaled[WBALLBBG]; break; + default: pm = *pixScaled[OUTERBBG]; + } + // kdDebug(12009) << pm.width() << " " << w << endl; + p->drawPixmap( 0, 0, pm ); + // bitBlt( this, col * w, row * h, &pm ); + + p->setPen( black ); + + if (type == INNERBBG) { + p->drawLine( x2, 0, x2, y2 ); // draw vertical line on right + p->drawLine( 0, y2, x2, y2 ); // draw horiz. line at bottom + p->drawLine( 0, 0, x2, 0 ); + p->drawLine( 0, 0, 0, y2 ); + } + + /* + Extra drawings for boxes aroud lasers. + */ + QString s; + switch (type) { + case RLASERBBG: + s.sprintf( "%c", 'R' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + case HLASERBBG: + s.sprintf( "%c", 'H' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + } + if (type < 0) { + s.sprintf( "%d", -type ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + } + + /* + Draw extra frame inside if this is the current cell. + */ + p->setPen( yellow ); + if ( (row == curRow) && (col == curCol) ) { // if we are on current cell, + if ( hasFocus() ) { + p->drawRect( 0, 0, x2, y2 ); + } + else { // we don't have focus, so + p->setPen( DotLine ); // use dashed line to + p->drawRect( 0, 0, x2, y2 ); + p->setPen( SolidLine ); // restore to normal + } + } +} + +void KBBGraphic::paintCellDefault( QPainter* p, int row, int col ) +{ + int w = cellW; + int h = cellH; + int x2 = w - 1; + int y2 = h - 1; + int type; + QColor color; + + switch (type = graphicBoard->get( col, row )) { + case MARK1BBG: color = darkRed; break; + case OUTERBBG: color = white; break; + case INNERBBG: color = gray; break; + case LASERBBG: color = darkGreen; break; + case LFIREBBG: color = green; break; + case FBALLBBG: color = red; break; + case TBALLBBG: color = blue; break; + case WBALLBBG: color = cyan; break; + default: color = white; + } + p->fillRect( 0, 0, x2, y2, color ); + + p->setPen( black ); + p->drawLine( x2, 0, x2, y2 ); // draw vertical line on right + p->drawLine( 0, y2, x2, y2 ); // draw horiz. line at bottom + + /* + Extra drawings for boxes aroud lasers. + */ + QString s; + switch (type) { + case RLASERBBG: + s.sprintf( "%c", 'R' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + case HLASERBBG: + s.sprintf( "%c", 'H' ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + break; + } + if (type < 0) { + s.sprintf( "%d", -type ); + p->drawText( 1, 1, x2-1, y2-1, AlignCenter, s ); + } + + /* + Draw extra frame inside if this is the current cell. + */ + if ( (row == curRow) && (col == curCol) ) { // if we are on current cell, + if ( hasFocus() ) { + p->drawEllipse( 1, 1, x2-2, y2-2 ); // draw ellipse + } + else { // we don't have focus, so + p->setPen( DotLine ); // use dashed line to + p->drawEllipse( 1, 1, x2-2, y2-2 ); // draw ellipse + p->setPen( SolidLine ); // restore to normal + } + } +} + +/* + Xperimantal... +*/ + +void KBBGraphic::paintEvent( QPaintEvent* ) +{ + int i, j; + QPainter paint( drawBuffer ); + + // kdDebug(12009) << drawBuffer->width() << endl; + for (i = 0; i < numRows; i++) { + for (j = 0; j < numCols; j++) { + paint.setViewport( j * cellW, i * cellH, width(), height() ); + paintCell( &paint, i, j ); + } + } + bitBlt( this, 0, 0, drawBuffer ); +} + +/* + Resize event of the KBBGraphic widget. +*/ + +void KBBGraphic::resizeEvent( QResizeEvent* ) +{ + int w = QWidget::width(); + int h = QWidget::height(); + int wNew, hNew; + + // kbDebug() << w << " " << h << " " << minW << " " << minH << endl; + if (w > minW) { + wNew = w / numC(); + } else { + wNew = CELLW; + } + if (h > minH) { + hNew = h / numR(); + } else { + hNew = CELLH; + } + if (pix != NULL) scalePixmaps( wNew, hNew ); + setCellWidth( wNew ); + setCellHeight( hNew ); + + delete drawBuffer; + drawBuffer = new QPixmap( cellW * numRows, cellH * numCols ); +} + +/* + Handles mouse press events for the KBBGraphic widget. +*/ +void KBBGraphic::mousePressEvent( QMouseEvent* e ) +{ + if (inputAccepted) { + /* + * Middle click finishes the game. + */ + if (e->button() == MidButton) { + emit endMouseClicked(); + return; + } + int oldRow = curRow; + int oldCol = curCol; + QPoint pos = e->pos(); // extract pointer position + curRow = pos.y() / cellH; + curCol = pos.x() / cellW; + //kdDebug(12009) << e->state() << " " << LeftButton << " " << e->state()&LeftButton << endl; + updateElement( oldCol, oldRow ); + emit inputAt( curCol, curRow, e->button() ); + } +} + + +/* + Handles mouse move events for the KBBGraphic widget. +*/ + +void KBBGraphic::mouseMoveEvent( QMouseEvent* e ) { + if (inputAccepted) { + int oldRow = curRow; + int oldCol = curCol; + QPoint pos = e->pos(); // extract pointer position + int movRow = pos.y() / cellH; + int movCol = pos.x() / cellW; + // kdDebug(12009) << movRow << " " << curRow << endl; + if ( (curRow != movRow) // if current cell has moved, + || (curCol != movCol) ) { + curRow = movRow; + curCol = movCol; + updateElement( oldCol, oldRow ); + emit inputAt( curCol, curRow, e->state() ); + } + } +} + +void KBBGraphic::slotUp() +{ + if( curRow > 0 ) { + moveSelection( -1, 0 ); + } +} + +void KBBGraphic::slotDown() +{ + if( curRow < numRows-1 ) { + moveSelection( 1, 0 ); + } +} + +void KBBGraphic::slotLeft() +{ + if( curCol > 0 ) { + moveSelection( 0, -1 ); + } +} + +void KBBGraphic::slotRight() +{ + if( curCol < numCols-1 ) { + moveSelection( 0, 1 ); + } +} + +void KBBGraphic::slotInput() +{ + if ( !inputAccepted ) { + return; + } + emit inputAt( curCol, curRow, LeftButton ); +// updateElement( curCol, curRow ); +} + +void KBBGraphic::moveSelection(int drow, int dcol) +{ + if ( !dcol && !drow || !inputAccepted ) { + return; + } + curCol += dcol; + curRow += drow; + updateElement( curCol - dcol, curRow - drow ); + updateElement( curCol, curRow ); +} + +/* + Handles focus reception events for the KBBGraphic widget. +*/ + +void KBBGraphic::focusInEvent( QFocusEvent* ) +{ + repaint( FALSE ); +} + + +/* + Handles focus loss events for the KBBGraphic widget. +*/ + +void KBBGraphic::focusOutEvent( QFocusEvent* ) +{ + repaint( FALSE ); +} + +/* + Sets whether user input is processed or not. +*/ + +void KBBGraphic::setInputAccepted( bool b ) +{ + inputAccepted = b; + if (b) setFocusPolicy( StrongFocus ); + else setFocusPolicy( NoFocus ); +} + +/* + Updates the cell at (col,row). +*/ + +void KBBGraphic::updateElement( int col, int row ) +{ + QPainter paint( this ); + + paint.setViewport( col * cellW, row * cellH, width(), height() ); + paintCell( &paint, row, col ); +} + +#include "kbbgfx.moc" diff --git a/kblackbox/kbbgfx.h b/kblackbox/kbbgfx.h new file mode 100644 index 00000000..ae4d207c --- /dev/null +++ b/kblackbox/kbbgfx.h @@ -0,0 +1,120 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: kbbgfx.h +// +// The definition of the KBBGraphic widget +// + +#ifndef KBBGFX_H +#define KBBGFX_H + +#include +#include + +#include "util.h" + +/* + Default size of a cell +*/ + +#define CELLW 32 +#define CELLH 32 + +/* + Graphical types of the boxes +*/ + +#define OUTERBBG 0 +#define INNERBBG 1 +#define LASERBBG 2 +#define FBALLBBG 3 +#define TBALLBBG 4 +#define WBALLBBG 5 +#define MARK1BBG 6 +#define LFIREBBG 7 + +/* + These have the same pixmaps as some of those above... +*/ + +#define RLASERBBG 8 +#define HLASERBBG 9 + +#define NROFTYPES 8 + +/* + Negative numbers are deflected lasers... +*/ + +class KBBGraphic : public QWidget +{ + Q_OBJECT +public: + KBBGraphic( QPixmap** p=0, QWidget* parent=0, const char* name=0 ); + ~KBBGraphic(); + + friend class KBBGame; + + void setSize( int w, int h ); + RectOnArray *getGraphicBoard(); + int numC(); + int numR(); + int width(); + int height(); + int wHint() const; + int hHint() const; + void setCellWidth( int w ); + void setCellHeight( int h ); + void setNumRows( int rows ); + void setNumCols( int cols ); + +public slots: + void setInputAccepted( bool b ); + void updateElement( int col, int row ); + void slotUp(); + void slotDown(); + void slotLeft(); + void slotRight(); + void slotInput(); + +signals: + void sizeChanged(); + void inputAt( int, int, int ); + void endMouseClicked(); + +protected: + virtual QSize sizeHint() const; + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + void focusInEvent( QFocusEvent* ); + void focusOutEvent( QFocusEvent* ); + void resizeEvent( QResizeEvent* e ); + + void moveSelection(int drow, int dcol); + +private: + void paintCell( QPainter* p, int row, int col ); + void paintCellDefault( QPainter*, int row, int col ); + void paintCellPixmap( QPainter*, int row, int col ); + void scalePixmaps( int w, int h ); + RectOnArray *graphicBoard; + int curRow; + int curCol; + bool inputAccepted; + int minW; + int minH; + int cellW; + int cellH; + int numCols; + int numRows; + QPixmap **pix; + QPixmap **pixScaled; + QPixmap *drawBuffer; +}; + +#endif // KBBGFX_H diff --git a/kblackbox/kblackbox.desktop b/kblackbox/kblackbox.desktop new file mode 100644 index 00000000..a599cec6 --- /dev/null +++ b/kblackbox/kblackbox.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +GenericName=Blackbox Logic Game +GenericName[be]=Ð›Ð°Ð³Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· чорнай ÑкрынÑй +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=যà§à¦•à§à¦¤à¦¿à¦° খেলা বà§à¦²à§à¦¯à¦¾à¦•à¦¬à¦•à§à¦¸ +GenericName[bs]=LogiÄka igra "Crna kutija" +GenericName[ca]=Joc de lògica Blackbox +GenericName[cs]=Logická hra s Äernou skříňkou +GenericName[cy]=Gêm Resymeg Blackbox +GenericName[da]=Blackbox logikspil +GenericName[de]=Logikspiel +GenericName[el]=Παιχνίδι λογικής Blackbox +GenericName[eo]=Divenlogika ludo +GenericName[es]=Juego de lógica Blackbox +GenericName[et]=Blackboxi loogikamäng +GenericName[eu]=Blackbox joko logikoa +GenericName[fa]=بازی Blackbox Logic +GenericName[fi]=Logiikkapeli +GenericName[fr]=Jeu de logique Blackbox +GenericName[he]=משחק לוגיקה +GenericName[hr]=Blackbox logiÄka igra +GenericName[hu]=Logikai +GenericName[is]=Blackbox kænskuleikur +GenericName[it]=Gioco di logica Blackbox +GenericName[ja]=ブラックボックス論ç†ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ážáž€áŸ’កវិជ្ជា Blackbox +GenericName[ko]=블랙박스 논리 게임 +GenericName[lt]=Blackbox loginis žaidimas +GenericName[lv]=MelnÄs kastes loÄ£ikas spÄ“le +GenericName[mk]=Логичка игра Blackbox +GenericName[nb]=Logikkspillet Blackbox +GenericName[nds]=Blackbox Logikspeel +GenericName[ne]=कालोबाकस यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Blackbox: Logisch spel +GenericName[nn]=Logikkspelet Blackbox +GenericName[pl]=Gra logiczna - Czarna skrzynka +GenericName[pt]=Jogo de Lógica Caixa Preta +GenericName[pt_BR]=Jogo lógico de Caixa Preta +GenericName[ru]=Чёрный Ñщик +GenericName[se]=Logihkkaspeallu Blackbox +GenericName[sk]=Blackbox logická hra +GenericName[sl]=LogiÄna igra Blackbox +GenericName[sr]=Логичка игра црне кутије +GenericName[sr@Latn]=LogiÄka igra crne kutije +GenericName[sv]=Svart lÃ¥da logiskt spel +GenericName[ta]=கறà¯à®ªà¯à®ªà¯à®ªà¯†à®Ÿà¯à®Ÿà®¿ லாஜிக௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на логіку (чорний Ñщик) +GenericName[wa]=Djeu d' lodjike noere boesse +GenericName[zh_TW]=黑盒å­æ™ºåŠ›éŠæˆ² +Exec=kblackbox %i %m -caption "%c" +Name=KBlackBox +Name[af]=Kblackbox +Name[ar]=لعبة الصندوق الأسود (KBlackBox) +Name[be]=Ð§Ð¾Ñ€Ð½Ð°Ñ ÑÐºÑ€Ñ‹Ð½Ñ +Name[bn]=কে-বà§à¦²à§à¦¯à¦¾à¦•à¦¬à¦•à§à¦¸ +Name[hi]=के-बà¥à¤²à¥‡à¤•à¤¬à¥‰à¤•à¥à¤¸ +Name[hu]=Fekete doboz +Name[is]=Svarti kassinn +Name[ne]=केडीई कालोबाकस +Name[pa]=ਕੇ-ਬਲੈਕ ਬਾਕਸ +Name[pl]=Czarna skrzynka +Name[pt_BR]=KCaixa Preta +Name[sv]=Kblackbox +Name[ta]=கேகரà¯à®ªà¯à®ªà¯ பெடà¯à®Ÿà®¿ +Name[tg]=KҚуттии Сиёҳ +Name[th]=à¸à¸¥à¹ˆà¸­à¸‡à¸”ำ - K +Name[tr]=Kara Kutu +Name[uk]=Чорна Ñкринька +Name[zh_TW]=KBlackBox é»‘ç›’å­ +Icon=kblackbox +Path= +DocPath=kblackbox/index.html +Type=Application +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kblackbox/kblackboxui.rc b/kblackbox/kblackboxui.rc new file mode 100644 index 00000000..c7c2b634 --- /dev/null +++ b/kblackbox/kblackboxui.rc @@ -0,0 +1,24 @@ + + + + + &Game + + + + + + &Settings + + + + + + +Main Toolbar + + + + + + diff --git a/kblackbox/main.cpp b/kblackbox/main.cpp new file mode 100644 index 00000000..ba0db293 --- /dev/null +++ b/kblackbox/main.cpp @@ -0,0 +1,46 @@ +// +// KBlackbox +// +// A simple game inspired by an emacs module +// +// File: main.cpp +// +// The main() function +// + + +#include +#include +#include +#include + +#include "kbbgame.h" +#include "version.h" + + +static const char description[] = I18N_NOOP("KDE Blackbox Game"); + +/* + The program starts here. +*/ + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "kblackbox", I18N_NOOP("KBlackBox"), + KBVERSION, description, KAboutData::License_GPL, + "(c) 1999-2000, Robert Cimrman"); + aboutData.addAuthor("Robert Cimrman",0, "cimrman3@students.zcu.cz"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(KBBGame) + else { + KBBGame *w = new KBBGame; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/kblackbox/pics/Makefile.am b/kblackbox/pics/Makefile.am new file mode 100644 index 00000000..bfc5d607 --- /dev/null +++ b/kblackbox/pics/Makefile.am @@ -0,0 +1,10 @@ + +# add here all files +pics_DATA = blue.xpm brown.xpm cyan.xpm gray.xpm green.xpm red.xpm white.xpm\ + green2.xpm giveup.xpm done.xpm + +picsdir = $(kde_datadir)/kblackbox/pics + +# extra files for "make dist" +EXTRA_DIST = $(pics_DATA) + diff --git a/kblackbox/pics/blue.xpm b/kblackbox/pics/blue.xpm new file mode 100644 index 00000000..fca9f04c --- /dev/null +++ b/kblackbox/pics/blue.xpm @@ -0,0 +1,199 @@ +/* XPM */ +static char *blue[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 144 2", +/* colors */ +".. c #04021c", +".# c #7476fc", +".a c #3c3e3c", +".b c #040284", +".c c #2422bc", +".d c #1412a4", +".e c #3c3edc", +".f c #acaefc", +".g c #242224", +".h c #3432cc", +".i c #5c5afc", +".j c #0402a4", +".k c #04024c", +".l c #1412bc", +".m c #4c4ef4", +".n c #9492fc", +".o c #2c2ac4", +".p c #cccafc", +".q c #141214", +".r c #545654", +".s c #0c0aa4", +".t c #3432e4", +".u c #1c1abc", +".v c #0c0a9c", +".w c #04022c", +".x c #8486fc", +".y c #040294", +".z c #1c1ab4", +".A c #3c3eec", +".B c #bcbefc", +".C c #343234", +".D c #6c6afc", +".E c #04026c", +".F c #a4a2fc", +".G c #dcdafc", +".H c #0c0ab4", +".I c #4c4a4c", +".J c #3c3adc", +".K c #2c2ad4", +".L c #646264", +".M c #1c1acc", +".N c #1412b4", +".O c #3432dc", +".P c #0406b4", +".Q c #4c4aec", +".R c #0c0a0c", +".S c #7c7efc", +".T c #b4b6fc", +".U c #2c2a2c", +".V c #6462fc", +".W c #0406a4", +".X c #04025c", +".Y c #1416bc", +".Z c #5456fc", +".0 c #9c9afc", +".1 c #d4d2fc", +".2 c #1c1a1c", +".3 c #0c0ea4", +".4 c #3c3ae4", +".5 c #1c1ebc", +".6 c #04023c", +".7 c #2426cc", +".8 c #040694", +".9 c #4442ec", +"#. c #444644", +"## c #04068c", +"#a c #1416ac", +"#b c #4442e4", +"#c c #3436d4", +"#d c #2c2ecc", +"#e c #5c5e5c", +"#f c #4446f4", +"#g c #8c8efc", +"#h c #c4c6fc", +"#i c #3c3a3c", +"#j c #7472fc", +"#k c #04027c", +"#l c #acaafc", +"#m c #e4e2fc", +"#n c #0c0eb4", +"#o c #545254", +"#p c #2c2ed4", +"#q c #6c6a6c", +"#r c #040224", +"#s c #7c7afc", +"#t c #444244", +"#u c #04028c", +"#v c #2422c4", +"#w c #1412ac", +"#x c #3c3ee4", +"#y c #242624", +"#z c #3432d4", +"#A c #5c5efc", +"#B c #0402ac", +"#C c #040254", +"#D c #4c4efc", +"#E c #9496fc", +"#F c #2c2acc", +"#G c #cccefc", +"#H c #141614", +"#I c #5c5a5c", +"#J c #4446ec", +"#K c #0c0aac", +"#L c #3436e4", +"#M c #1c1ac4", +"#N c #0c0e9c", +"#O c #040234", +"#P c #2422d4", +"#Q c #8c8afc", +"#R c #04029c", +"#S c #1c1eb4", +"#T c #3c3ef4", +"#U c #c4c2fc", +"#V c #343634", +"#W c #6c6efc", +"#X c #040274", +"#Y c #a4a6fc", +"#Z c #dcdefc", +"#0 c #4c4e4c", +"#1 c #1c1ecc", +"#2 c #1416b4", +"#3 c #3436dc", +"#4 c #8482fc", +"#5 c #bcbafc", +"#6 c #0c0e0c", +"#7 c #2c2e2c", +"#8 c #6466fc", +"#9 c #9c9efc", +"a. c #d4d6fc", +"a# c #1c1e1c", +"aa c #4c4af4", +"ab c #2426c4", +"ac c #0406ac", +"ad c #040264", +"ae c #1416c4", +"af c #0c0eac", +"ag c #3c3aec", +"ah c #1c1ec4", +"ai c #040244", +"aj c #2426d4", +"ak c #04069c", +"al c #4442f4", +"am c #0c0ebc", +"an c #2c2edc", +/* pixels */ +".I.C.Ua#.C#0#i.2#H.q#y#i#t.U.g#H.g#7.R.U#i#V#i#t#7a#.g.g#V#y#7.g#i#.#0#7#o.g.2a##y.R#i#.#0.U.g.2", +"#.#H.g#0a#.I.ga#.2#7.I.U.U#y#.a##t.U#7#7a#.2a##7#t#o#i#ya#.C.2#V.a.U.C#i.2#Va#.q#7.a.I.I#V#y.g#.", +".C.C#6#i#H#t#Va#.U#.#e#V#H.g.I#H#q.a#i.C#y.U.2#H#y#0#e#..g#H#H.2a##7#6.g#V#o#t#7#7.C#..I.2#y.q#7", +"a##ia##7#V#7#ia##t.L.L#o.U#Va##V#e.C.kad#X.b#u.y#u#u.b#u.b#k.X#V#..I#H.R.r#..ra#.qa#.g#i#y#i.Ua#", +"#t.g.C#V.Ia##6.2.a.C.C.a#V.2#7.Cadad.E#k#k.b.E#X#X#k.b#u.y#u#u#u.b.C#6.U#e.I.C.2#H#y#7#V#V.a.Ca#", +"#.#H#o#t#y#t.g.2#y#y#y#Va#.gad.E.b.b.y.y#R#R.y#R.y#R.y.y.b.b#u#R.y#u.b.a.I.I#y.q.U.C.Ua#.U#..g.U", +".I.U#0#0.C#0#Va##7.g#y#7.kad#X#k.b.y#u#k#k.b.y#R#R#Bac#B.j#u.b.y.j.j.y#k.b#V.aa#.2.U#7.a.2#H#7.r", +"#0#6#o#oa##0#i.U.2.g#0.k#Xad.E#X#k#k#k.b#k.b.b.b.b.b#Rac.P#B#R.b#B#B.j.j#R#u.L.g.U#H#7#i.C#7.2.C", +"#..U.U.U.C.C#7#0#7.g.X.Ead#X#X#k.b.b#u.b#u.y.b#u#Ram#1#P.l#B#R#u#uamac#R.j.j#k.g#H.2#V.I.Ca#a#.2", +".I.I#.#y#0#0#i#7#.#C.E.E.E#X#k#k.b#u#u#u#u#u.y#R.K.9.tae#B#R.y#u#R.jam#B#B.j#R.b.C#6.2.a.U#H.q.C", +".g#0#o#..C#0#V.U.kad#k#X#k.b.b.b.y.y.y#u.y.y.jac#A#W.H.y#R#B.jam.K.Haeam#B#R#R#R.ba#.R#y#6.I#V.g", +".U.2#V#7a#.g.g.k.k.X.E#X#k.b#u#u.y.y.y.W.Y.u.4#W.K#R.j#4.T#A#8ag#A#L#Pae.P#B#R#R.y.E#y#i#V#e.U#y", +"a#.2#ya#.g.C#y#C#C.k#X#k.b#u#u.y.y#R.W#2ab.O.m.j.j#B#A.B#1#1.Paj.##Wanam#n#B#B.j.y#u#y.2.U.2.C#y", +"a#a#.2#t#7.C.k.Xadad#X#k.b#k.b#u#Rak.W#R.j.jac#L.S#g#5am.j#R#R#B#B#Taaae.lam.P.j.y#u#X.Ua#.a#0#y", +"a#.g#y#t#t#7ai.Ead#X#X#k.b#u#u#u.yak.j#n#v.K#dab#v#x#Dam.Ham#B.j#R#1.V.Kae#M.P.j#R#u#k#y.C#t.r#i", +".g#y.U.g#.#O.k.X#X#k.b#k.b.y.y.yak.sac#F#w#2.z.5#v#pag#P#s.T.G#L.y#B.S.t#Mae.P#R#R#u#u#k#0#.#0.C", +"#7.q#V#y.C#Cad.X#X.b.b#k#u.y#R#R.W.s#K.7#aak#2.c#F#L.t.Sa.#Ua.#j#R#Ral.A#n.H.H#R.y.y#u.b.C.r#e#i", +".2a##i#V#O.kadad#k#u#u.b#uak#R.W.W.Y#F.5#N.s.dah.4.Z.i.F#m#G.T.B#B.jae.lac#n.l.j.y.y.b#uada##I.I", +"#y.g#y#i.kai.X.E#k.b.b.b#u.yak#2.z.5#d.z.v#a.e#s.#.D#9#h#5#9#l#Y.M.P#M.H.P.Namac.y.y.b.b.E#6#..L", +"#i#ia##o#C#Cad.E#X#k.b.b.b.b#uak.3#w.5.c#c#z.i.p#U#W.p.G.f#5#G.F#D.Y.M#n#Kac.j.P.y.y.y#u.b.q#.#o", +"#7#o#H.a#C.E.Xad.E#X#X#u#u#u.8.8.v#N#w.c.##Q#E.B.x#5#Ua.#m#Z.1.f#D.M.H#B#B#R#R.j.j#R#R.y#ka##V#.", +"#0.C#H#0.6.k#Cadad.E#X#k.b.yak.N.z#w#N#w#S#A#l#E#9#5#Y.T.p#5.1.G.xae.P.j.j.j#R.y#u#R.y.y.b.g#V#y", +".2#y.2a#.6.6#Cadad.E#X.b#u.yaf#3#f.Q.3.3#Sab#5#E#l.x.D#9#l#E#Z#Y#Q#L.Ham.j.y#R.b.b.j.b.b.b.g#V#y", +"#7.g.ga##O.kai.Xadad#k.b#u.s.A#j.S#A.d.v.z#a.f.0.n#s#8.p#Z.x.0#G.p.F#j.#ah.j#u#u#k#u#k.E.b.L.C#y", +".2.R#H.U#r#Cai#C.X#C.E#X#u#n.c#8#4#Q#wak#N#b#5.0.m.m#9.p.G#Q.Q#x#J.J.Z.O.N.y.y#u.b.b#k.X#k.a#y.2", +"a#.g.g.2..#Caiai.k#C.X#X#u#wak.c#E.x#j#A#4#E#W.e.h#Q.T.1.S#f#d#v.5.u#2.s#R.y.y.b.b.b#k.E.E.2.2a#", +"#H#V#Va#.wadai.6.k.k#Cad.b.y.yak.caa.m#b.Q#p#S.z.D#4.#.x#8.O#J#J#z#paf#R.y.y.b.b.b#u#k.b#u.I.U#y", +".U.U#V#y.kadai#O#C#C.Xad.b.8.b.b#u###u.v.3.d#a#c#L#c.zab.Z.V.V#A#L.O#n#R#R#u#u#u.y.y.y.b#k#y#6#V", +".r#H.g#yaiai.X.6.k.Xad#C#kak.b.b#k.b.b#u#u#w.c#F.7ab.z#a.o.9.4.sak.s#nac.j.y#R.y#R#u.y#k.E#y.a.2", +".C.U#6.2#r#O#Cai#C.X.X.X.E.W.b.b#k.b#u##akab.5.z#z#F.z.zab#aak#u.y.y#u.y#R.y#R#R#R#R#u#k.X.q.qa#", +"#7.U#V#0.r#O.k#C.Xad.X#C.y.3.8#X#X.b#u#w.5.cak.8.v#aab#v#a.s#u#R#nak.y.b#u.y.y.y.y.y#k.E#Ha#.g#i", +"#i.2.g#V#0.wai#C#C.X.Xad#u#u#k.E.E#k.y.3.v.8.b.8#u.8#N.3.s.3.W.N.u.Yac.y#u#u.y.y.y#u#X.X#i.U.C#I", +"#i#y..#.#7#Vai#C.X#Cad.E#X#k#Xadad#k#u#u.8.b#u.v#wak.b.y.s#wafafaf.N#K#u#k.y#u#u.b.E#X#7.g#7#H#7", +"a##ya#.aa#.2#r.6.k.Xad.E.Ead.E.Ead#X.b#u.3.y#k.b#w#2.3.s.3.s.s.W#Rak.y#k#k#u.b#X.Eadad#7#H.2.a#i", +"#.#0.C.U#7#t#0.6#Oai.Xadad.Xad.Ead#k#k.bak.8#X#uak.s.s.Wakak.y.b#k#u#X.E#k#k#X.Ead#Ca#.q.a#7.g#V", +".2.C#V#i#t#H.C#O.w#Oai#C#C#Cad#X#X.b.b.b.b.b#X#Xak.vakak#R.y#k#X#X#X.E.E#X#k#C.X.X.k#7.U.U#i.g.a", +".2#ta#.g#o.C#7#6.6#O#Oai.k.k.X.E#X#X#k#X#X#X#X#kak.sakakak#R#X#X#X#X.Ead.Ead.Xad#C#e.g#7#7.g.q.C", +".C#y.a.g.U#7.C.U.a..ai.6ai#C.X.Xadad.E#X#X#X.E#k#u.sak.yakak#R.y#u.Eadad#X#k.X.X#V.ra##i.a#H.2#y", +"#i#y#y#ya#.a.C#H.a#V.w.6ai.k#C#C.Xadad.E.E#X#X#X#k.y.y#k.b#u.b.y#u#uadad#k#X.X#H.2#o#7.2.2#H#7#i", +".a.2.2#.#V.2#V.q#0#0a#..#Oai.k.X.Xad.Eadad#X#k#u.b#k.X.Xadadad#k.b#k#Cad.k.k.aa#a##V#o#y#V.U#y.C", +"#o#y#V.C.a.C.U#y.a.I#V#y#r.w.6ai.X.Xad.E.E.E.X#C.k#C.k#C.k#C.k#X.b.X.kai#O#.#i.U.I#V.a#y.U#..r#y", +"#0.C#H.C.g#H.q.q#y.q.2#i#I#e#r.6.k#C#C.Xaiai.k.X#C.X.X#C#C.kai.Ead.k#O.a#V#7#0#o#.#H#i#i#y#o.I#0", +"#i#7.ga#.a#V#7#H.U#V.C#6#i#..C.U#Oai#O#O#O.6aiad.E.X.k#C.k.6.X.k.6#H#7a##y#y.a#o.I.g#0.Ca##t.r#t", +".g#6#0.I#y.q.U#t#7#y.U.a#y#H.U#V.a.g#r..#O.k#O.6.k.6ai.6#O.6#O.g.Ca##V#o#t#t.C#ta##i#i#7a##y#V#.", +".2.U.ga#.g#ya##0#i.2.C.q#V#V#y.Ca##I#e.U#H.g#7#6#7#H.C#7.C#V#7a#.C.g.g.r.r#o#0.g.g#o.C#y.ga#.qa#", +"#y#H.C.C.g.U.C#i#H.C.r.C#t#..U.2.a.r.r.a#7.2a#.2.C.C#H#H#H#t.2.2.U#y#y.g#.#o#7a#.ga##y.I.I.C.g#H", +".C.Ua#.2#V#i#7.2a##7#.#t.2#q.aa##t#o#o#0.C.q#H#V#y#y.2.a#7a#.U#y#o.a.C#6.C#o#t#y.2.r#0#0.I#t.C.U", +".Ua#.I#i#i#6#y#7#I.C#V.I#V#7a##H.U#H#i#y.2#H#i.L#t#t.C.q#t.g#i#V.a#I#.#i#i#..a#i#H#I#o#..g#V#7a#" +}; diff --git a/kblackbox/pics/brown.xpm b/kblackbox/pics/brown.xpm new file mode 100644 index 00000000..4ccd55fe --- /dev/null +++ b/kblackbox/pics/brown.xpm @@ -0,0 +1,77 @@ +/* XPM */ +static char * image_name[] = { +"48 48 26 1", +" c #69A645141861", +". c #514434D31040", +"X c #30C220810820", +"o c #41032CB21040", +"O c #618541031861", +"+ c #492430C21040", +"@ c #59653CF31040", +"# c #596538E31040", +"$ c #28A21C710820", +"% c #514438E31040", +"& c #30C21C710820", +"* c #61853CF31861", +"= c #28A218610820", +"- c #18610C300000", +"; c #38E324920820", +": c #186110400820", +"> c #208114510820", +", c #082004100000", +"< c #104008200000", +"1 c #410328A21040", +"2 c #49242CB21040", +"3 c #71C649241861", +"4 c #38E328A21040", +"5 c #69A649241861", +"6 c #208118610820", +"7 c #000000000000", +" .Xo o O ++X+@+#@O.$%.. ##+&o*.=-$;:>-,<1..#+ ", +"O2;O3# ##1o%O .##.oX*#@O.#.1&o#X1;.OX;%o+O@1+o@", +"$::+#+#@%%X$+%#1;&4.+@#@@.*#.;#O;%1#*1o*.. #+", +"&&$++411X$-=1%+;..o;+o>4o#O.++.##o##;o", +"@;o*O#*@.%+X%.o##@@#+o* +;+%+;;4>o.@*+o+#oX>;;=.", +"@1o%OO @.1>%*.#.+#@.#@ +o+++1.@+##OO#+@O%.1*@+O", +"#;o.#@#.+.o1 5@##.o1X&1o;;+o+o#O+o#* .%O*.+oO *5", +"XXo4++..1112 #+@++@%++o++ooo##++X&+@@++#ooo&+.1o", +"1.%%@#OOo+2.*## ;1+o1++%1;4% #.@o;*O*+.#1o%+#+#+", +"4o+@ @5*X+@@O@*OX1*;;o1%++%* @%O%1OO*.++o+#o%#*.", +"o*#+#o%1:X+oo+++X;@&o+++1#O#+1&&X&+41o1+;o.%4o@#", +"+ O#@1o+$+%4;+.%;1@X;.%#o11X11X&1X;1++++4211XXo1", +".@+1*#@*1@O+##@+&1*;%@++X++1#@..*## 5@@.1@+;1+*%", +"..XX+1+.&o%X4+%o$1.&+O1X=+##.+.@@+O @1o4=.12**O@", +".@11%.+oX+1&+o14X#%X1%;+# *+.#%#oX++;=XX6%;1#+.+", +"O 1o*.o;;.%o@*@#.OOX+%1o@O#+@ @%+1OO+o%#1O++@4.@", +"%+;.*.1;+ @o.+.O*OO;. +%%*#.@ @#O# O.++@+@+o%Xo+", +".+1.+o+o@ @1+2%%114:$#X;.#.+#%++##@+44;++@2+@;.@", +"++1+X+@1**+;+1.#o.+>+O+. %+*#+%#..;1+;++.o#O1.@", +"1+o+%o@o@@#2;&+#o.1&O oo#%+o+11%#+%X4.;1o%+.%$1+", +"+#11o;++@+X;X$%*++1$#@;411o&1o+++X+X;1=$;o111&%.", +".@1%@.O*O#%@2;##X+.X.%&$+%+&;.%o1;o&X%o12.1411##", +"$1;##1+.#41%o>+#=;.X*@$X+#o;+**##%*11*o1+O@#%# +", +"X+Xo;-=>$->;;-oO1@ 1O ;+ #+@ O%%#@1;.+;+O+...*+", +"$#+@@+ @%;@ +X 3o*5+@@&.#@*1+#%+1o.1X%+o# +1+oo$", +"X;$+@%** # 524 3o@@;#@X%@@@X+*%+o.%1o#.;%O;1#@O+", +".*+ ##@@o%@$&++X2%&o+$+###&1*#..%*oo+.1#@4o*OO.", +";o+5 #* O;@*X>++X1@12@X.*.@1.@;1+1o44o+;1+&;+++o", +"X+o%+1.#.;.@&=O +2+;X%;+@o#o+#;;11o4X+o4++1++%%1", +".#%#.+o++;+.>>o2&1@;XoX+%1o++%;#@.*@+@*.%#OO#O *", +"o.+%.+o.#1.%X;#o&+@XX4;+@1+%+.+*@+@@%@*+@##O@@O*", +"$>;+o2@@Oo+@.oO*# ;&@.##.#@%++@++@@@O*#@O O*@O#", +"4;+*#O OO%.@+1@.%#@1X*#.@#* O%#@++#1o1XX14o+;1#.", +"X;+@+%#@O%.@.4@@+.+oX.## @#%#++#%.#%@%+;++++X%#X", +">>>&:$Xo#2+...OO+@*#+@OO5##O++..+o%@*O@+#@@@1.@>", +"1+1+;.@+%X++1+#%11+;+.@++o+*.1.;+1o1o++4##o.1.@&", +"1o+#+@*o1$$+4@%.%++X4o1;XX&X>>4=XXX;X1;>o#+*1#Oo", +"oo.%;+@++oo@4++1%.%++#..+##%+o*++#@O.*.&oo;@+@O+", +";++1$2*...*O2++2.11X1#@@4+@@..O@%@OO+%@;@*;o1.#X", +"+%%1X@ @@#O5X@%@Oo1&1* %&+O*oo*@@@@.;# o* +@;XX;", +"o;1;o@5@++##>4o1.111.@.X:+@1>6+++1+#+O +@*1.;111", +".;;>>;.+1;.#<;+11;1++1X>-o+$6X+4;1+.X1+X11;%X#*#", +" OO+11@@#% >#@%*#.+.+O+X* .%* @@ *%1.#+2;;@;;;.", +" @1X22O @..@$*O.O##..+.>>#O#1.1@*O+1+@O.%;o@11o*", +" @>&+;@#&;o+$@#1%+++++1:X* .X1$1++44#+%%@X1 #++ ", +"o+:1@X@@o+@OoO@1o.++oo+=# %X.X@*.@*@#@@+>$.X>-X", +"o=7:+=@*@O@#1#@o.#*1;o4XO5 *+#153%1%+;1X<7$%o1X."}; diff --git a/kblackbox/pics/cyan.xpm b/kblackbox/pics/cyan.xpm new file mode 100644 index 00000000..591f9c90 --- /dev/null +++ b/kblackbox/pics/cyan.xpm @@ -0,0 +1,193 @@ +/* XPM */ +static char *cyan[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 138 2", +/* colors */ +".. c #0c0a0c", +".# c #048684", +".a c #7cfefc", +".b c #14c6c4", +".c c #044a4c", +".d c #4ceeec", +".e c #04a6a4", +".f c #34e2e4", +".g c #0cb6b4", +".h c #042a2c", +".i c #4c4a4c", +".j c #2cc6c4", +".k c #2c2a2c", +".l c #046a6c", +".m c #b4fefc", +".n c #1cb6b4", +".o c #14a6a4", +".p c #2cd6d4", +".q c #049694", +".r c #3cf2f4", +".s c #041e1c", +".t c #64fefc", +".u c #24d2d4", +".v c #5c5e5c", +".w c #045a5c", +".x c #04aeac", +".y c #043a3c", +".z c #3c3a3c", +".A c #9cfefc", +".B c #24c6c4", +".C c #4cfefc", +".D c #44e2e4", +".E c #047a7c", +".F c #d4fefc", +".G c #1cbebc", +".H c #3cdedc", +".I c #049e9c", +".J c #2ccecc", +".K c #2cdedc", +".L c #1c1a1c", +".M c #545654", +".N c #14aeac", +".O c #141214", +".P c #048e8c", +".Q c #8cfefc", +".R c #1ccecc", +".S c #045254", +".T c #0ca6a4", +".U c #3ceaec", +".V c #14b6b4", +".W c #043234", +".X c #343234", +".Y c #047274", +".Z c #c4fefc", +".0 c #34d6d4", +".1 c #046264", +".2 c #0caeac", +".3 c #044244", +".4 c #444244", +".5 c #5cfefc", +".6 c #24bebc", +".7 c #0c9e9c", +".8 c #34cecc", +".9 c #242224", +"#. c #1cc6c4", +"## c #3ce2e4", +"#a c #545254", +"#b c #24babc", +"#c c #0c9a9c", +"#d c #44eeec", +"#e c #74fefc", +"#f c #acfefc", +"#g c #54fafc", +"#h c #44eaec", +"#i c #14bebc", +"#j c #048284", +"#k c #e4fefc", +"#l c #1cb2b4", +"#m c #0c0e0c", +"#n c #048a8c", +"#o c #84fefc", +"#p c #044e4c", +"#q c #4cf2f4", +"#r c #04aaac", +"#s c #34e6e4", +"#t c #0cbabc", +"#u c #042e2c", +"#v c #4c4e4c", +"#w c #2ccacc", +"#x c #2c2e2c", +"#y c #046e6c", +"#z c #bcfefc", +"#A c #1cbabc", +"#B c #14aaac", +"#C c #2cdadc", +"#D c #049a9c", +"#E c #042624", +"#F c #6cfefc", +"#G c #24d6d4", +"#H c #646264", +"#I c #045e5c", +"#J c #04b2b4", +"#K c #043e3c", +"#L c #3c3e3c", +"#M c #a4fefc", +"#N c #24cacc", +"#O c #047e7c", +"#P c #dcfefc", +"#Q c #1cc2c4", +"#R c #04a2a4", +"#S c #2cd2d4", +"#T c #34dedc", +"#U c #1c1e1c", +"#V c #5c5a5c", +"#W c #14b2b4", +"#X c #141614", +"#Y c #049294", +"#Z c #94fefc", +"#0 c #045654", +"#1 c #0caaac", +"#2 c #14babc", +"#3 c #043634", +"#4 c #343634", +"#5 c #6c6a6c", +"#6 c #ccfefc", +"#7 c #0cb2b4", +"#8 c #24c2c4", +"#9 c #0ca2a4", +"a. c #34d2d4", +"a# c #1ccacc", +"aa c #44f2f4", +"ab c #14c2c4", +"ac c #047674", +"ad c #046664", +"ae c #044644", +"af c #444644", +"ag c #242624", +"ah c #54fefc", +/* pixels */ +".i.X.k#U.X#v.z.L#X.Oag.z.4.k.9#X.9#x...k.z#4.z.4#x#U.9.9#4ag#x.9.zaf#v#x#a.9.L#Uag...zaf#v.k.9.L", +"af#X.9#v#U.i.9#U.L#x.i.k.kagaf#U.4.k#x#x#U.L#U#x.4#a.zag#U.X.L#4#L.k.X.z.L#4#U.O#x#L.i.i#4ag.9af", +".X.X#m.z#X.4#4#U.kaf.v#4#X.9.i#X#5#L.z.Xag.k.L#Xag#v.vaf.9#X#X.L#U#x#m.9#4#a.4#x#x.Xaf.i.Lag.O#x", +"#U.z#U#x#4#x.z#U.4#H#H#a.k#4#U#4.v.X#p.1.Y#j.P#Y.P.##n.##j.E#I#4af.i#X...Maf.M#U.O#U.9.zag.z.k#U", +".4.9.X#4.i#U#m.L#L.X.X#L#4.L#x.X.1.l.l#O#O.#.l.Yac.E.##n#Y#n.P.#.##4..#x#V.i.X.L#Xag#x#4#4#L.X#U", +"af#X#a.4ag.4.9.Lagagag#4#Uag#I#y#j.##Y.q#D#D#Y#D#D.I#Y.q.#.##n.I#Y#n#j#L#v.iag.O.k.X.k#U.kaf.9.k", +".i.k#v#v.X#v#4#U#x.9ag#x.cadacac#n.P#n#O#O.#.q#D#D#r.x.x.e.P.#.q#R.e.q.E.##4#L#U.L.k#x#L.L#X#x.M", +"#v#m#a#a#U#v.z.k.L.9#v#p.Yad#yac.E#O#j#j#O#j.#.#.#.#.I#r#J.e.I#j.x.x#R#R#D.P#Hag.k#X#x.z.X#x.L.X", +"af.k.k.k.X.X#x#v#x.9#0.lad#y.Y.E#j#j.#.#.P.P#n.P.q#t.R.uab#r#D.P.P#t#J.I.e.I#j.9#X.L#4.i.X#U#U.L", +".i.iafag#v#v.z#xaf.S#y#y#yac.E#j.#.P.P#n.P.P.P#D.paa.fab#r.I#Y.P#D#R#t.x#r#R.I.#.X#m.L#L.k#X.O.X", +".9#v#aaf.X#v#4#x.cad.Eac.E#j#j.##Y.q.q.P#Y#Y#R.x.5#F.2#D.I#r.e#t.K#t.b#t#r#D#R.q.##U..ag#m.i#4.9", +".k.L#4#x#U.9.9ae.c#I#y.Y#O.##n.P.P.q.q.e#2#Q###F#C.I#R#o.m.5.t.U.5#s.uab#J#r.I#D.q.lag.z#4.v.kag", +"#U.Lag#U.9.Xag#0.S#p#y.E#j#n.P#Y.q#D#R#W.B.0#q#R.I#r.5#za#a##J#G#e#F.K#t#t#r#r#R.q#nag.L.k.L.Xag", +"#U#U.L.4#x.X#p#I.lad.Y.E.##j#j#Y#D#D#R#R#R.e#r#s.a.Q#z#t.e.I.I#r.x.r#q.b#i#t#J.e.q#n.Y.k#U#L#vag", +"#U.9ag.4.4#xae.l.1.Y.E.E#j#n#n#n#Y.I#R.g.B.J#S#8.6##.C#t.g#t.x.I#D.R.t.K#i#.#J.e.q.P#jag.X.4.M.z", +".9ag.kagaf.W#p.w#y#O#j.E.##Y#Y#D#D.T.e.J.N#W.n.G#8.p.U.u.a.m#P#s#D.e.a#T#.ab#J#R#D.P#nac#vaf#v.X", +"#x.O#4ag.X.Sad#Iac.#.##j#n#D#D.I#R#1.2#N#B.I#W#8.J.f.f.a.F.Z.F#e#D.Iaa.U.g#7.g#D.q#Y#n#n.X.M.v.z", +".L#U.z#4.W.c.1ad.E.##n#j.P#D.I.e.e#2#w#A.7#9.o#Q##ah.5#M#k#6.m#z#r.I.b#2.x.g#i.I#D#Y.##nad#U#V.i", +"ag.9ag.z#pae.w.l#O#j#n.##n.P.I.N.n#A.J#l.7#B.H.a#e#F.A.Z#z.A#f#Ma##Ja#.g.x#2#t.x.q#Y.##j#y#maf#H", +".z.z#U.M#p#0ad#yac.E#O.#.##n#n.q.T#B#A#b.0a..5#6.Z#F#6#P#f#z#6#M.Cab#..g#7.e.e.x.q#Y#D#n#O.Oaf#a", +"#x#a#X#L.S.l.wad#y.Yac.##n#n#Y.q#c#9#B#b#e.Q#Z#z#o#z.Z.F#k#P.F#f.Ca#.g#r#r#R.I.e#R.I#D#Y#j#U#4af", +"#v.X#X#v.y#p#0.1ad#yac#O.##Y.I.V.n.o#9.o.n.5#f#Z.A#z#M.m#6#z.F#P#o.b.x#r#R#R.q.q.P#D#Y#Y#j.9#4ag", +".Lag.L#U.y.y.S.1.1.lac#O#n#D.2#T#d#q.7#9.n#8#z#Z#f#o#F.A#f#Z#P#M.Q#s#7#t#R#D.I.#.##R#n#j.#.9#4ag", +"#x.9.9#U.W#p.c.wad.1#O#j.P.T.U#e.a.5.o.7#l#B#f.A#Z.a.t#6#P#o.A#6#6#M#e#ea##R.P#n#O#n.E#y.##H.Xag", +".L..#X.k#E#0.3.S#I#0.lac.P.2.6.t#o.Q#B#D#c.D#z.A#q#q.A#6#P.Q.d###h.Hah.0#W.q.P.P.##j.E#I.E#Lag.L", +"#U.9.9.L.s#0ae#K#p#0#I.Y.P#B#D.6#Z#o#e.5#o#Z#F.H.8.Q.m.F.aaa.J#8#A.G#W.T#D.q#Y.##j#j.E#y.l.L.L#U", +"#X#4#4#U.h.1ae#K.c#p#0ad#j#D#Y.I#b#q.d#h.d.J.n#l#F#o#e#o.t#T#h#d.0#S.2#D#Y#Y#n#j.#.P#O#j.P.i#xag", +".k.k#4ag.cad.3#3.S#0.w.1#j.q.##O#n#Y#n#c.7.o.N.0#T.0#l#8#g.t.t.5.f#T#7#R#D#Y#n.P#Y.q#Y#j.Eag#m#4", +".M#X.9ag.3ae#0#K.c#0.1.w.E#D.##j#j#j.##n.P#B.6#w#N.B.n#B.j#h.H.T#D#9#7#r#R.q.q.q#D.P#Y#O#yag.z.L", +".X.k#m.L#E.W.w.3#0.1#I#0#y#R.##j.E.##n.P#D#8.G.na.#w#l.n.B.N#D#Y#Y#Y#Y#Y.I.q#D#D#D#D#n#O.w#X.O#U", +"#x.k#4#v.M#u.c#0.w.1#I#0#Y.T#Yacac#j.P.N#A.6#D#Y#c.N#8#8.N.7#Y#D#7.I.P#n#n#Y.q#Y.q#Y#O.l#X#U.9.z", +".z.L.9#4#v.h.c.S.S#I.w.1.P#n#j.l.l.E#Y.T.7.P.#.q#Y#Y.7#1#9.T.I#W.G#i#r.q.#.P.q.q#Y#nac.w.z.k.X#V", +".zag...4#x#4#K.S.w.w.1#yac#O#yadad.E#n.P#Y#j.P#c.N.q#n.P#9.N.2#1.2#W.2.P.E#Y#n.P#n#yac#x.9#x#X#x", +"#U.9.L#L#U.L#E#K#p.wad#y.l.1.l.l.1.E#j.P.T#Y.E.##B#W.T#9.T.T.T#R.I.I#Yac#j#n.#ac.lad#I#x#X#U#L.z", +"af#v#x.k#x.4#a#K.W.3.wad.1#Iad.ladac#O#j#D#Dac#n#D.T#9#R#R#D#Y.#ac#n.E#y.E#O.Y.lad.S#U#X.z#x.9#4", +".L.X.z.zaf#X#x.W#u#3.3.S.S#0.1acac#j.##j.##jac.E#D.7.I#D#D.P#Oac.Yac.l.lacac.w.w.w.c#x.k#x.z.9#L", +".L.4#U#U#a.X.X#m#3.W#3ae.c.S.w.l.Yac.Eacac.Y.Y.E#D.T.I#D.I.I.Y.Y.E.Y.lad#yad.w.1#0.v.9#x#x.9.O.X", +".Xag#Lag.k#x.Xag.4.s#K#Kae#0.w.1.1ad#y#y.Yac#y.E.P#9.I.q.I#D.I.q.##y.1ad.Yac#I.w#4.M#U.z.z#X.Lag", +".zag.9ag.L#L.X.L#L#4.h.yae.c.S#0#I.1ad.l#y#yac.Y#O#Y#Y#O.##n.##Y#Y#n.l.1.E.Y.w#X.L#a#x.L#U#X#x.z", +".4.L#X.i#4#U#4.O#v#v.9.s#3.3.c#0#Iad.ladadac.E#n.##O#I.w.1.1ad#O#j#O.S.1#p.S#L#U#U#4#aag.X.kag.X", +"#aag.z.X#L.Xagag#Laf#4ag.s.W.yae.w#I.1.l#y.l.1.S#p#p#p.S#p.S#pac#j.w.c.3#3af.z.k.i#4#Lag.kaf.Mag", +"#v.X#X#x.9.L#X.O.9#X.L#4.v.v#E#K.c.S.w.waeaeae.w#0#I.w.S#0.cae.ladae.y#L#4#x#v.Maf#X.z.zag#a.i#v", +".z#x.9#U#L#4.k#X#x#4.X#m.zaf.X#x.W.3#3.W#3#3.cad#y.w.S#p.c#K.w#p.y#X#x#Uagag#L#a.i.9#v.X#Uaf.M.4", +".9#m#v.iag.Oag.4#xagag.4ag#X.k#4#Lag#E.s.W#p.W.y.c#K.3.y.y.y.W.9#4#U#4#a.4.4.X.4#U.z.z#x#Uag#4af", +".L.k.9#U.9.k#U#a.z#U.X.O.X#4ag.X#U#V.v.k#X.9#x#m#x#X#4#x.X#4#x#U.X.9.9.M.M#a.i.9ag#a.Xag.9#U.O#U", +"ag#X.X.Xagag.X#4.L.X#a.X.4af.k.L#L.M.M#L#x.L#U.L#4.X.O#X#X.4#U.L.kagag.9af#a#x#U.9#Uag.i.i.X.9#X", +".X.k#U.L#4.z.X.L#U#xaf.4.L#5#L#U.4#a#a.i#4.O#X#4agag.L#L#x#U.k.9.M#L.X#m.X#a.4ag.L#a#v#v.i.4.X.k", +".k#U.i.z.z#mag#x.M.X#4.i#4#x#U#X.k.O.zag.L#X.z#H.4.4.X.O.4.9.z#4#L#Vaf#L.zaf#L.z#X.v#aaf.9#4#x#U" +}; diff --git a/kblackbox/pics/done.xpm b/kblackbox/pics/done.xpm new file mode 100644 index 00000000..218de4e2 --- /dev/null +++ b/kblackbox/pics/done.xpm @@ -0,0 +1,33 @@ +/* XPM */ +static char * done_xpm[] = { +"26 26 4 1", +" c None", +". c #000000000000", +"X c #C71B9E799E79", +"o c #861786178617", +" ", +" ", +" ", +" .. ", +" .X. ", +" .Xo. ", +" .Xo. ", +" .XX. ", +" .XX. ", +" .XX. ", +" ..XX. ", +" ......XXX. ", +" .XXX.XoXXXX. ", +" .XXXX.XoXXXXX. ", +" .....XXoXXXXXX...... ", +" .XXXX.XoXXXXXXoXXXX. ", +" .XXXX.XXoXXXXoXXXXX. ", +" .....XXXXooooXXXoXX. ", +" .XXXX.XXXXXXXXXoXXX. ", +" .XXXX.XXoXXXXXoXXX. ", +" .....XXoXXXX....... ", +" .XXXX.XoXX.. ", +" ......... ", +" ", +" ", +" "}; diff --git a/kblackbox/pics/giveup.xpm b/kblackbox/pics/giveup.xpm new file mode 100644 index 00000000..4c67802a --- /dev/null +++ b/kblackbox/pics/giveup.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * giveup_xpm[] = { +"22 21 4 1", +" c None", +". c #000000000000", +"X c #B6DA8E388617", +"o c #FFFFFFFFFFFF", +" ", +" .. ", +" .X.... ", +" .XX.ooo. ", +" .X.ooooo. ", +" .X.oooooo... ", +" .XX.ooooooooo. ", +" .X.ooooooooooo. ", +" .X.oooooooooooo. ", +" .XX.oooooooooooo. ", +" .X. ....oooooooo. ", +" .X. .ooooooo. ", +" .X. .ooooo. ", +" .XX. ...oo. ", +" .X. .o. ", +" .X. . ", +" .XX. ", +" .X. ", +" .. ", +" ", +" "}; diff --git a/kblackbox/pics/gray.xpm b/kblackbox/pics/gray.xpm new file mode 100644 index 00000000..913b3afc --- /dev/null +++ b/kblackbox/pics/gray.xpm @@ -0,0 +1,150 @@ +/* XPM */ +static char *gray[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 95 2", +/* colors */ +".. c #070707", +".# c #080808", +".a c #090909", +".b c #0a0a0a", +".c c #0b0b0b", +".d c #0c0c0c", +".e c #0d0d0d", +".f c #0e0e0e", +".g c #0f0f0f", +".h c #101010", +".i c #111111", +".j c #121212", +".k c #131313", +".l c #141414", +".m c #151515", +".n c #161616", +".o c #171717", +".p c #181818", +".q c #191919", +".r c #1a1a1a", +".s c #1b1b1b", +".t c #1c1c1c", +".u c #1d1d1d", +".v c #1e1e1e", +".w c #1f1f1f", +".x c #202020", +".y c #212121", +".z c #222222", +".A c #232323", +".B c #242424", +".C c #252525", +".D c #262626", +".E c #272727", +".F c #282828", +".G c #292929", +".H c #2a2a2a", +".I c #2b2b2b", +".J c #2c2c2c", +".K c #2d2d2d", +".L c #2e2e2e", +".M c #2f2f2f", +".N c #303030", +".O c #313131", +".P c #323232", +".Q c #333333", +".R c #343434", +".S c #353535", +".T c #363636", +".U c #373737", +".V c #383838", +".W c #393939", +".X c #3a3a3a", +".Y c #3b3b3b", +".Z c #3c3c3c", +".0 c #3d3d3d", +".1 c #3e3e3e", +".2 c #3f3f3f", +".3 c #404040", +".4 c #414141", +".5 c #424242", +".6 c #434343", +".7 c #444444", +".8 c #454545", +".9 c #464646", +"#. c #474747", +"## c #484848", +"#a c #494949", +"#b c #4a4a4a", +"#c c #4b4b4b", +"#d c #4c4c4c", +"#e c #4d4d4d", +"#f c #4e4e4e", +"#g c #4f4f4f", +"#h c #505050", +"#i c #515151", +"#j c #525252", +"#k c #535353", +"#l c #545454", +"#m c #555555", +"#n c #565656", +"#o c #575757", +"#p c #585858", +"#q c #595959", +"#r c #5a5a5a", +"#s c #5b5b5b", +"#t c #5c5c5c", +"#u c #5d5d5d", +"#v c #5e5e5e", +"#w c #5f5f5f", +"#x c #606060", +"#y c #616161", +"#z c #626262", +"#A c #636363", +"#B c #646464", +"#C c #696969", +/* pixels */ +"#c.P.H.t.R#d.Y.r.p.j.D.X.5.I.z.n.x.N.b.F.Z.S.X.4.N.u.z.y.V.A.K.B.V.9#f.K#g.z.s.u.C.c.V#a#f.H.z.r", +".7.n.B#f.u#b.z.s.s.J#a.I.I.B.9.w.6.H.M.L.w.o.w.O.5#j.W.D.v.O.r.S.2.I.N.X.r.W.u.k.J.4#c#b.Q.G.y##", +".N.R.f.V.q.6.R.v.G.8#y.R.l.z#c.l#C.2.U.O.D.J.q.j.E#c#u.9.y.o.n.s.w.M.f.B.W#i.4.M.K.N.8#b.r.D.j.M", +".x.X.t.L.S.M.V.x.8#y#A#i.K.R.w.T#w.P.8.t.n.w.6.y.y.Q#g.6.q.o.J.T#.##.l.a#m.9#n.w.g.w.A.W.G.W.E.u", +".3.z.Q.S#b.w.g.o.2.Q.P.4.Q.q.M.K.u.0.2.L.y.u.J.E.2.J.P.E.o.G.Y.z.O.S.e.J#s#b.M.s.m.F.L.R.T.2.Q.v", +".8.n#j.4.C.3.A.s.E.B.B.T.v.C.r.x.C#t#l#i.e.x.o.o.Y.W.Q.E.G.Y.Y.D.w.C.n.1#d##.H.l.G.O.H.u.I.6.z.I", +"##.I#h#e.O#j.R.w.J.A.C.L.T#g.1.y.K.s.M#r.7.E.t.L.X.U.C.h#..1.A.Y#a.t.M#b#q.W.X.x.q.I.L.0.s.p.L#n", +"#d.i#j#i.v#d.Y.I.t.x#h.Z.T#b##.w.9#d.k.Z.E.P.q.5.S.B.s.6.1.l.O.6.7.w.I#e#c.R#A.B.F.p.J.Y.R.I.t.M", +".7.H.J.I.R.N.L#e.L.A.D.B.R.A.F.z.E.J.3.G.C.q.t.p.u.D.F.m.s.d.O.S.J.C#f.7.X.J#p.A.q.q.U#b.O.w.u.t", +"#c#a.7.D#f#h.Z.J##.R.T.O.C.3.5.j.W.L.7.S.N.N.7.2.s.p.S.t.B.s.N.m##.z.7#g.O.J.y.w.P.g.p.0.F.o.l.N", +".z#f#l.8.O#f.R.J.z.A.X#b.0.0#u.L.K.J.2.R.t.i.S.x.k.O.W.D.J#a.5.F.D.L.F.Q.S.K.l.I.M.w.c.F.h#b.S.z", +".H.s.T.K.t.z.z.B.I.t.O.4.9.O.P.p.g.S.Z.f.C.T.6.K.S.F.y.Y.H.x.v.C.E.S.Y.k.O.X.H.z.p.m.D.V.Q#v.E.F", +".v.p.C.v.z.O.G.s.X.z.u.u.q.u.2.E.4#m.Y.s.J.J.X.C.v.2.A.u#e#f#..6#g.p#i.F#q#i.1.q.X.Y.F.q.I.s.P.C", +".u.v.u.4.O.Q.V.p.C.l.Q.r.R#z#k.C#a#u.I.M#k.U.P.1.I.Q.B.G.1#e.V#k#h.u#q.4.Q.0.z.u.u.B.R.G.w.Z#e.F", +".v.z.B.6.5.K.Y.G.I.m.U.y#d.7.5.F.1.y#b#s#o.X.g.N.G.L.o.p.T#B#.#m#h.4#r.X.P#f.z.M.l.n.R.H.O.5#n.X", +".A.F.G.B.9.3.V.3.V.A.4.S##.O.u.t.0.k#e#o.R.v.w.B.p.J.L#n.K.6#j.V.i.2.Y.V.O.L.o.C.7#d.Q.A#g###e.O", +".J.j.W.D.N.J.t.O.h.z.H.C.N.s.y.o.s.Q.c.h.M.L.j.z.P.I#c#e#i.y.J.u.q.X.t.M.K.w.t.F.8#..1.V.R#k#u.X", +".s.t.V.T.y.1.T.l.w.n.q.M.A.C.R.X.7.N.R.y.I.u.C#q#x.A#.#i#a.0.9.A.I.t.O.s.5#j.D.r.t.M.V.U.K.x#s#a", +".C.z.D.Z.q.K.G.C.0.D.E.A.D.U#g#a.Y.D.I.P.q.O.q#e.V.J.I.H.4.7#..u.j.A#c##.z#s.F.S#j.y.l.K.Y.i.6#B", +".W.Z.u#l.B.z.t.E.S.J#k#b.h#f.9.o.G.g.C.t.P#t.X.U.8.t.5.W.v.V.Q.S.j.T.R#e.U.F.w.0.7.S.T.Y.P.j###i", +".L#k.q.2#d.I.Y.u.S.K.S#w.C.U.w.5.P.G.Q.P#d#h.G.Y.9.k.7.K.U.R.A.N.O.E.Q#m#h.5.j.F.O.c.2.W.5.v.R#.", +"#g.Q.k#f.1.3.9.J.s.U.R#u.6.C.h.y.A.E.k.J.P.M.q#d#f.v.D.h#e#x.K.g.u.R.4#y#y.9.F...X.r.A#a.N.x.T.B", +".p.F.n.v#d.2.0.R.8.A.U.6.1.H.U.m.P.w.D.H.N.B.9.8.V.F.z.Z.3.0.T.u.X.T.T#d#m.M.n.D.l.I##.0.v.B.V.C", +".O.y.z.y.Z#l#b.6.8.N.l.2.V.5.9.O.M.n.S.Z.8.1#o#e.T.A.v.B.Q.m.F.t.q.O.z.0.2.v.4#r.T.p.7#k.I#y.N.C", +".o.d.p.G.v.i.N.S.W.C.B.I.U.0.i.T.u.u.H.Z.E.C.F.7#..v.K.P#n#d.w.Y#g.Q.3.R.R.r###k#e.S.q.9.v.2.D.t", +".v.z.z.t.6.3.6.3.H.h.s.y#.#p#e.p#n#d.V.N.Q.B#g#..F.P.r.1.N.F.F#a.Q.P.s.d.O.A.s#e#s#a.4.y.J.r.s.u", +".n.R.U.v.3.0.W#a#d.P.Q.t.U.3#c.J.J.O.N.U.x.w.9#i.G.A.H.W.B.5#r.D.d.r.z.1#q.K.y#d.5#b#c.x#f#c.J.E", +".G.G.R.E.V.I.u.2#h.D#h.T.Q.U.Z.W.J.O.M.E.C.G.x.x.B.P.O.y.3.2.J.9.8#b.v#c#d.R.C.P.M.6#i.X.F.C.g.S", +"#o.m.x.F.O.j.T.Y.s.U.J.r.N.K.x.D.T#g.Y.S.N.C.U#i#u.L.E.0.U.o.V.2.1.2.v.2#i.Y.0.F.j.P#k#a.G.F.Y.q", +".Q.G.h.r.j.s.U#l.7.o.F.K#a.2.T.y.E#g.2.E.O.d.F.2.Q.s.G.M#h.z.x.K.5.I.M.2#j#e.8.D.T.s.J.S.K.l.i.v", +".K.G.S#e#q.V.i#..T.x.C.n#e#c.6.Y.T#f#a.w.w.c.L.w.A.n.B.s.Q.4.H.R.C.O.K.v.J.I.1.Q.m.o.K.B.q.u.z.W", +".Y.r.A.U#f.1.m.K.7#j.X.W#d#a##.9.V.w.F.Y.L.e.z.z.Z.M.V.T.H.5.w.G.l.V.t.M.w.m#a.7.5#a.M#g.X.I.S#q", +".Y.B.#.7.J.U.k.8#s.9.J.S.V.S.E#e#l.H.C#e.L.f.F.P#B.X.C.7.M.G.G.X.P.5.R.B.K.D.7#b#t#h.S.O.z.K.p.J", +".v.B.s.Z.v.s.V#g#l.q.Q.J.o.o.I#h#q.4.W#f.6.y.H.l.Z.Q.N.o.N.I.g.n.M#v#r.m.K.J.z.7.w.s.H.H.q.t.Z.X", +".9#e.M.H.M.4#h#f.S.v.5.G.w.E.X.V.5.5#e.9.p#f#o.4.y.u.V.E.N#b#..q.I.K.9.B.6.S.o.E.H.m.x.l.Y.K.A.T", +".p.R.W.Y.7.o.P.I.u.J.7.E.h.E.v.t.w.J.E.H.x#o#u#a#z.O.i.B.7#v#..B.M.k.K.x.D.L.M.0.I.P.L.H.J.X.y.1", +".s.6.w.w#j.P.O.f.i.y.S.F.n.P#e.0.P.x.Q#a.S.Q#q#A.U.w.N.E.2#e.U.A.B.n.v.4.L.v.P.J#a#A.x.O.M.z.j.P", +".O.D.0.B.G.N.O.F.6.x.Q.G.W.n.0.9#m.O.4#e#d#p#k.6.A.n.t.E.0.s.z.#.x.r.m.u.O.E.H.z.S#p.t.W.W.p.q.F", +".W.E.A.C.t.1.O.q.1.U#s#f#s.2#o#z#q.X.A.1.G.L.W.V.U##.F#l.G.7.K.A.P.e.w.z.S.9.R.q.r#h.N.q.t.q.I.Y", +".4.s.o##.R.t.R.j#c#e.A#e##.0.3.3.0.3.V#b.I.6.A.j.1.1.E#e.x#j.L.I#b.O.F#.#b#d.0.w.u.R#i.H.Q.F.B.Q", +"#j.B.W.P.1.Q.G.F.Z##.U.D.Y.O.G.x.I.e.E.y.V#r#f#f.D.w.A.M.s.M.g.G.2.s.J.3.J##.V.G#b.W.0.B.I#.#m.B", +"#e.R.n.N.z.q.l.h.B.l.q.V#t#y.X.F.Z.t.l.E#h#g#.#m.X.s.E.M.J.o.m.R.R.N.B.3.T.M#f#l##.m.Y.T.F#i#b#e", +".X.M.w.v.3.R.I.o.J.U.P.f.X#..Q.J.N.z.s.L#k.5.I.E.5.O.W.h.P.o.C.S.9.o.K.w.B.C.0#l#a.y#f.P.v.7#o.2", +".y.e#g#b.C.j.G.3.J.C.F.1.E.l.G.V.1.C.D.s.u.2.z.T.y.L.G.X.n.R.R.A.R.w.U#g.6.5.N.4.u.W.X.M.u.D.S#.", +".p.I.B.t.x.G.w#g.Z.t.Q.k.R.T.E.M.x#q#z.H.m.C.O.d.N.p.R.M.Q.W.K.x.O.z.z#p#n#k#e.A.C#k.R.B.C.t.l.t", +".F.n.P.P.D.G.N.U.q.P#l.P.2#..G.r.2#m#k.4.K.s.t.r.R.N.m.m.m.4.s.s.G.C.G.w.8#i.M.v.y.y.D#a##.O.A.m", +".Q.G.v.t.R.Y.P.s.w.L.8.7.t#C.4.t.5#k#k#c.R.i.n.W.E.D.q.1.M.w.G.B#l.3.M.f.R#i.6.E.p#k#h#f#b.7.O.G", +".J.t#b.X.Z.f.B.M#p.O.T#b.V.K.v.m.I.k.X.E.s.m.W#x.6.4.P.j.3.z.X.T.3#q#a.Z.W##.2.X.q#t#h.8.B.S.N.u" +}; diff --git a/kblackbox/pics/green.xpm b/kblackbox/pics/green.xpm new file mode 100644 index 00000000..f5d4a505 --- /dev/null +++ b/kblackbox/pics/green.xpm @@ -0,0 +1,218 @@ +/* XPM */ +static char *green[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 163 2", +/* colors */ +".. c #040204", +".# c #047e04", +".a c #14be14", +".b c #044204", +".c c #7c7e7c", +".d c #049e04", +".e c #2cde2c", +".f c #1cce1c", +".g c #0cae0c", +".h c #444244", +".i c #042204", +".j c #048e04", +".k c #3cee3c", +".l c #2cbe2c", +".m c #046204", +".n c #149e14", +".o c #34ce34", +".p c #1cae1c", +".q c #242224", +".r c #646264", +".s c #3cde3c", +".t c #049604", +".u c #041a04", +".v c #14c614", +".w c #24d624", +".x c #048604", +".y c #045204", +".z c #04a604", +".A c #0cb60c", +".B c #545254", +".C c #043204", +".D c #047204", +".E c #14a614", +".F c #1cb61c", +".G c #343234", +".H c #34d634", +".I c #747274", +".J c #3ce63c", +".K c #24be24", +".L c #34e634", +".M c #148e14", +".N c #4cee4c", +".O c #2cc62c", +".P c #149614", +".Q c #141214", +".R c #8c8a8c", +".S c #2cce2c", +".T c #0c0a0c", +".U c #0c7e0c", +".V c #1cbe1c", +".W c #044a04", +".X c #0c9e0c", +".Y c #34de34", +".Z c #14ae14", +".0 c #4c4a4c", +".1 c #042a04", +".2 c #0c8e0c", +".3 c #046a04", +".4 c #1c9e1c", +".5 c #24ae24", +".6 c #2c2a2c", +".7 c #6c6a6c", +".8 c #0c960c", +".9 c #24c624", +"#. c #2cd62c", +"## c #0c860c", +"#a c #045a04", +"#b c #0ca60c", +"#c c #14b614", +"#d c #5c5a5c", +"#e c #043a04", +"#f c #047a04", +"#g c #1ca61c", +"#h c #24b624", +"#i c #3c3a3c", +"#j c #3cd63c", +"#k c #44e644", +"#l c #34c634", +"#m c #1c1a1c", +"#n c #848684", +"#o c #24ce24", +"#p c #44ee44", +"#q c #3cd23c", +"#r c #44e244", +"#s c #1cc61c", +"#t c #7c7a7c", +"#u c #4cf64c", +"#v c #040604", +"#w c #048204", +"#x c #14c214", +"#y c #044604", +"#z c #848284", +"#A c #04a204", +"#B c #2ce22c", +"#C c #0cb20c", +"#D c #444644", +"#E c #042604", +"#F c #049204", +"#G c #2cc22c", +"#H c #046604", +"#I c #14a214", +"#J c #34d234", +"#K c #1cb21c", +"#L c #242624", +"#M c #646664", +"#N c #3ce23c", +"#O c #049a04", +"#P c #041e04", +"#Q c #24da24", +"#R c #048a04", +"#S c #045604", +"#T c #04aa04", +"#U c #0cba0c", +"#V c #545654", +"#W c #043604", +"#X c #047604", +"#Y c #14aa14", +"#Z c #1cba1c", +"#0 c #343634", +"#1 c #34da34", +"#2 c #747674", +"#3 c #3cea3c", +"#4 c #24c224", +"#5 c #34ea34", +"#6 c #149214", +"#7 c #2cca2c", +"#8 c #149a14", +"#9 c #141614", +"a. c #0c7a0c", +"a# c #8c8e8c", +"aa c #2cd22c", +"ab c #0c0e0c", +"ac c #0c820c", +"ad c #1cc21c", +"ae c #044e04", +"af c #0ca20c", +"ag c #34e234", +"ah c #4c4e4c", +"ai c #042e04", +"aj c #0c920c", +"ak c #046e04", +"al c #1ca21c", +"am c #24b224", +"an c #2c2e2c", +"ao c #6c6e6c", +"ap c #0c9a0c", +"aq c #2cda2c", +"ar c #0c8a0c", +"as c #045e04", +"at c #0caa0c", +"au c #14ba14", +"av c #5c5e5c", +"aw c #043e04", +"ax c #1caa1c", +"ay c #24ba24", +"az c #3c3e3c", +"aA c #3cda3c", +"aB c #44ea44", +"aC c #34ca34", +"aD c #1c1e1c", +"aE c #24d224", +"aF c #44f244", +"aG c #1cca1c", +/* pixels */ +"#i#M.G#L#Laoa#.0.Ban#maz.G.raoaoaz.q#D#D#0.B#M.q.q#0.Gan.0aD.6.7ah.r#Dah#D.q#9.h#Lah#D.B#ianaz.G", +".Bab#0#m#m#L.0.6abanah.6azah.B.qan.0.caz.6#i.c#V.h.7#D#Van#Laz#0.0#0.G.B#V.7#0.G#maD.6.G.0#D.h.q", +"#V#i.0.B.hazanah#L.r.0anaz.B#L.B.Q#i.Q#d.q#L.Rahanaz.q.0#i.6#V#iao.r#L.G#2.7.q#V.Q.6#0#DavaD.G#M", +"#9#i#Dah#i.0an.I#M#z#D.q.q.Gaz#D#9az.T#Maz#9#V.6.Gaz.6aDaD#m#D.0.qah#0#M.r.Baz#9.h#L#i.6#LaD.6az", +"an.haz.0az.G#i#d#V.Bao.r.6#0#m.B.i.C#eaw.b#y#y.W.W.W.W#y.baw#Waw#Wao#L#M.7#L.haD.q#L.qanah#z#m#L", +"#V#m#0#i#V.Gan#L#iazah.B.G#E#W#e#y.y#S.m.m#H.3.Dakak#H#Hak.Dak.3#H.y.y.Wao.haz#d.Ianan#d.qao.I#D", +"#d#d.q.6#L.0ah.r.Gah.G#E#e#y.Wae#S#a#H.3.D.x#R#R.j#R#R.j#R#R.x#R#R#R#R.D.W.b.6.B#D.G#ian#i#Dav.h", +"#Mavan#M#D.c#n#M.Ban.1.b.W.y#S#a.m#H#H.3.3.3#f#R#R.t#O#O.d.d.d#O.t#F#F#F#R.y.b#0#Dan#m.G.q.B.7ah", +"#D#0.B.q.h#0avan#d.1#e.Wae#S#a.D#X.3.3.3akak.#.##w#w#R#R.d.Aau.A#T.g.z#A#A#X.y.W.qazaD#Lao#Daz.r", +".r#Vah#0.G#ianah#E#e.bae.y#a.3#f#X.D.D#f#w.##f#w.#.#.##R#O#caG.a.A.A.A.A#T.z#X#S#y#D#m#i.I.0az#D", +"ao.B#i#i.7ah#i.u#W.b#y.y#S.m#X#f#X.x#F.j.j.t#F#R#R.x.x#R#O#A#s.waG.v#x.v.A.z#w.3ae#W.Qah#2#0.B#L", +".7.Qaz.T#V.7.6#Eaw#y.W.yas.3#X#f.##R#F#O.t.t.t#F.j.j.t#O#c.9.w.eaqaEaGaG#U#T#F.3.m#San.G#L#m.q.6", +"aDaD#0.hanaz#P.1#y.W.y#S#Hak#X#w.x#F.X#b#bat#A.d#Aat.V#o.Y.k.k.k#B.w.a.A#C#C.d#Rak.m#e.q#d#0ah.q", +"#L#L#dan.6#0#P.C#y.y#a.m#Hak#f#w.j.8af#Y#b#b#batau#1#p#p.L.eaG.L#5#Baq.f#U.A#O#R#f.3.W.Qah.0.7.h", +".0.h#L#m.6#V.i.C.W#a#a.m#Hak#f#R#Fapafat#Z.V#s#.#raB.YaG#saGaE.w#3.Laq#o.a.g.d#F.#ak.yab.7ao#L.h", +".Gaz.haz#L.u.i#W.bae#S#Hakak#f#R.8af#Y#4.9.o.HaA#N.J#o#sadaG#s.f#3.L.wadau#T.d#F#w#X.y.1.q#0#i.Q", +"#V.haz.h#0.u#E#eawae#a.3#f#f#war.P.E#K.5#G.o.s.J.L.YaE#..Y#.aG#x#Qaq.w.a.Aat.d#F#w.D#S.b.B#M#2.0", +"#0.0#dav#d.u.1.Caw.yasak#f#waraj.2.nalax.S.Y#N#Nag#Bag#3aBaF.L#xaG.waa.a#C.g#O#F.x.D.m.y#L#Dah#t", +"an#i#Mao.r#P.1.C#eae.m.D#f#war#6ar.M#8.5#J.J.J.L#3#3aF#kaF#u.k.w#x#x#s#Uau.A#A.t.j.Das.W#M#D.0av", +".0.haz#Man#Pai#Waw.y.m.D#f##aj.8.P#8.p.O.s#3.k#p.kaFaB#p#u#u#u#BaE.v#x#c#C#c.z#F.x.Das.yao#i.6.G", +"#V.G#9az#i.i#W.C#yas#Hak#f#war.8.n.Eay#7aa#N.J#3#p#p#N#u#u#u#u.k.e#o.a#C#U#x#C#R#w.D.m.yao#Dav#i", +"aD#0.Q#L#M#E.C.C.W#Sas#H.D.#araj#8.E.K.S.Saa.s.J.saA#r.N#p#uaF.k.L.w.fau.a#c#T#F#w.Das.y#d#0.6.q", +".6.6an.qaz#Eai.C.b.y#a#Hak#faraj.2#6ay#7.o.H.s.s#jaC#j.saAaB#paF.J.e.9.V.g.X.z#O#wak#S#S#D#9an.h", +".6.Baz#9aD.iai.C#eae#a#H.3.D.#acarar#8.O#J.H.saC..#l.H#r#J#j#J.s.H#Gad#b.t#F.j.x#w#H.y#S#L#Lah#D", +"#0#0#9.6#i#E.C.C#eaeas#H.D#f#f.#.D.U.Pay.O#J#J#G.5.l#J.s.Oay#hay#h.p#K#b#F#R#R.##Xakas#S#V.BaD#i", +"#0az.G.h.6#Eai.1#e.y.m.D#f.x.U#f.3.D.U.2#g#h.5.5#h#q#N.H.o#G.l#Gax#g.X.X.j.x.x#f#X#X.Dae.r#d.G.q", +"#i.q#L#D#0.i#E#E#W.y.3#f.#.2.2#Xak.3#Xa.##.P.4ax.OaA.s#7.YaA#1#J#Y.Eaj.2aj#R#R.x.#.D.D.Wavaoan#L", +"ab.G#ian#d.i.iai#W.W.3#f.#ac#6.#.3ak#X##.M.P.n#h.O#j.o.9aA#p#p#3.H.F.X.Eaf#O.j#R#fakakaw.B.6#z.q", +"#dan#d#V.0#P.i.Cai#e#H.D#facajacakak.D.M#8#gay#GaC.O.F.K#1#N#1.Y#1#..Fau#Y#A.d.x.D.D.3awan.7avao", +"#0aD#i#Vah#P.i#W#W#e#H.3#X#facak.m.makar#Iamamax.p.paxay.S.H#G.F.9.9#s#c.X.X.d#wak.3.m#W#L.haDaz", +"#D.6an#iav.u.i.C.C#eae.m.3akakas#a#a.3ac.Paj#8#8#8#g.F.K#h#K.E.8#8.V.K.Zap#F.jakak.3ae.C#Vanan#D", +".0aD#V#iaz.u.i#Wai#e#y.yasas.mas#S#a.3#wac.2.P#8.2#6#I.n#g.E.p.naj#Y#Z#O.##f.D.3.3#a#y.1.Iah.Baz", +".6.0.B.r#Lab#P.C#W.C.bae.y#S#a.m#Has#H#X.Uacaj.Pac#f.U.2#8.E.E.2.xap#Y.8.D#H.m#Has.y#y#M#D#M#9.7", +".Iahah#0.Gab#Pai#W#W#yae#S#Sas#H.3.3.D#X#X#X.D.#.D.makak.#.8.2.xac.Xap#w#Hasasas.yae#e#t#d#i#z.0", +".G.6.6#0ah.T.u.i.C#e.b#y.y#S#aas#H#H.3#H#Has.mak.3asas#aas.3.D#f.##Rajak#a.y#a.yae.W.1.canaDav.G", +"#2#Van.q#iahaD#P.1#Waw.b.W.W.y#S#S#S#a#a#a#a#Sasas#S.Wae.y#S#a.m#X.D#w.m#a.y.y.W.y#eaz.T#d#L.h.q", +"#M.G.q#9.6#L.Q.u.iai#W#e#e.b#y.W.Waeaeaeaeaeaeae.W.W.yaeaeae.y.y#aak.D.3.m#Sae#y.b.1.T#L.6#L.0#9", +".G#V#d#D.0ah#0#9.u.iai.C#W#eaw.b.b#y.Wae.W.W.yae.yasasas.m#Saeae.m.3.m.mas.y.W#eaian#Da#aoananan", +"an.q#Lanaz.G.h#9#9#P.i.1ai#W#e#eawawaw.b.W#S#aas.m.masas#Sae.b.W#a#a#a.y.y#y#e.1aoan.c.r.q.0aDaz", +".G#D.c#0.0.h#M.qaD#v.u#P.1.1.C.C.C#W#eaw.b#y.y.y.y.yae.W.y#y#e.b#yae.W.b.b#W#E.7#2#z.r#V.G#L.7#i", +"az.r.q#M#M#i#9#V#i.TaD.u#P.i#E.1.1aiai#Waw.b#y#y.b.b.bawaw#Wai#W#eaw#Wai.1#P#d.R#2.B.B#0.h.G#iaD", +".B.h#m#d.0.Q#0#L#L#maD#9ab.u.u#P.i.i#Eai.C#W#W.C.Caiai.C.C.1.1aiai#E.i.u#D.QaD#M#2#Lanah#d#0.6#M", +"#0az.6anaz.q#m.0.G#L.hav.0abab#v.u.u.u#P.i#P.i#P.i#P.i#P#P.u#P.u.uavaoah#dab#m.q#t#L#0.0az#m#L#M", +".q.G#L.6#V#D.6.G#D#9az#0.h.G.h#0#0an.hao#M.Gazan.Q#Daz.B#M#Mazah#0anah#V.G#0.qavav.7#V.0aDaz#L#m", +".6ah.q#V#M#daz#0.han.6az.6#maz#Lahav#L.7ahan.6#0anazan#9.hav.6#d#mazav.0#M.0anav.G#daz#0.0.G#9an", +"an#M.0.I.IaD.6#D#0.6an#m#i#0#d#0#i.B.G.h#L#0an#iaD.0.q#V.6#zah.Gaz#D.6#i.Bao.c#Vaz#V#m#m.Qan.q.B", +".r#i.r#Vah#V.h#iaD.6aDazan#v.B#0.r#M#VaD.ran.G#Lah.rav#D.G.Ban.I#t.G#0ahao.0.0.0#i#9.6#iav.c#D.0", +"#Lan.0.6.Ban.q#man.0#0#t#i.q#V.0#2#dan#D.G.q.qan#mav.0.0#L#L#L.R#V#L#M.G.B#i.G#L.G#m#0.G#D.0ah.q" +}; diff --git a/kblackbox/pics/green2.xpm b/kblackbox/pics/green2.xpm new file mode 100644 index 00000000..92ef2acb --- /dev/null +++ b/kblackbox/pics/green2.xpm @@ -0,0 +1,300 @@ +/* XPM */ +static char *green2[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 245 2", +/* colors */ +".. c #040204", +".# c #3cc23c", +".a c #044204", +".b c #848284", +".c c #444244", +".d c #2ca22c", +".e c #0c620c", +".f c #8cc68c", +".g c #4ce24c", +".h c #448244", +".i c #2c622c", +".j c #045604", +".k c #042204", +".l c #249224", +".m c #6ca66c", +".n c #147214", +".o c #449244", +".p c #44d244", +".q c #242224", +".r c #34b234", +".s c #447244", +".t c #2c722c", +".u c #2c822c", +".v c #a4a2a4", +".w c #1c521c", +".x c #54b254", +".y c #1c421c", +".z c #5cf65c", +".A c #043204", +".B c #5c925c", +".C c #1c621c", +".D c #5cc25c", +".E c #3ca23c", +".F c #94e294", +".G c #84d284", +".H c #349234", +".I c #6cb66c", +".J c #646264", +".K c #147a14", +".L c #2c7a2c", +".M c #2c8a2c", +".N c #044a04", +".O c #5ce25c", +".P c #449e44", +".Q c #54d254", +".R c #043a04", +".S c #44ca44", +".T c #448a44", +".U c #5ca25c", +".V c #7cc27c", +".W c #1c5a1c", +".X c #041a04", +".Y c #8c8a8c", +".Z c #2c6a2c", +".0 c #0c5a0c", +".1 c #3cba3c", +".2 c #7cb67c", +".3 c #1c8e1c", +".4 c #545254", +".5 c #2caa2c", +".6 c #343234", +".7 c #1c6a1c", +".8 c #747274", +".9 c #0c6a0c", +"#. c #c4c2c4", +"## c #249a24", +"#a c #b4b3b4", +"#b c #5ceb5c", +"#c c #54dc54", +"#d c #5cae5c", +"#e c #4cc34c", +"#f c #144414", +"#g c #042c04", +"#h c #247524", +"#i c #549354", +"#j c #44b544", +"#k c #7cfe7c", +"#l c #3c753c", +"#m c #3c833c", +"#n c #1c4d1c", +"#o c #5c9c5c", +"#p c #3cad3c", +"#q c #8cfe8c", +"#r c #349d34", +"#s c #247c24", +"#t c #3c7d3c", +"#u c #3c8c3c", +"#v c #143b14", +"#w c #548c54", +"#x c #7ccc7c", +"#y c #2c5d2c", +"#z c #141514", +"#A c #949694", +"#B c #248424", +"#C c #8cde8c", +"#D c #145514", +"#E c #447d44", +"#F c #64b464", +"#G c #143614", +"#H c #144b14", +"#I c #549d54", +"#J c #3c6e3c", +"#K c #7cbe7c", +"#L c #a4fea4", +"#M c #44c444", +"#N c #0c440c", +"#O c #4c4c4c", +"#P c #34a334", +"#Q c #146314", +"#R c #54ee54", +"#S c #4c854c", +"#T c #346434", +"#U c #6cce6c", +"#V c #2c942c", +"#W c #6cb26c", +"#X c #1c741c", +"#Y c #4c944c", +"#Z c #4cda4c", +"#0 c #2c2c2c", +"#1 c #3cb43c", +"#2 c #6cfe6c", +"#3 c #347434", +"#4 c #348334", +"#5 c #6c9e6c", +"#6 c #0c0d0c", +"#7 c #4cae4c", +"#8 c #acacac", +"#9 c #54cd54", +"a. c #6c6c6c", +"a# c #5c5c5c", +"aa c #3c3c3c", +"ab c #7c7c7c", +"ac c #1c1c1c", +"ad c #9cfe9c", +"ae c #74be74", +"af c #949294", +"ag c #cccacc", +"ah c #bcbabc", +"ai c #9c9e9c", +"aj c #8cce8c", +"ak c #84da84", +"al c #1c851c", +"am c #54e554", +"an c #0c540c", +"ao c #4cd34c", +"ap c #245324", +"aq c #5cb25c", +"ar c #64f364", +"as c #0c340c", +"at c #246424", +"au c #44a244", +"av c #74b874", +"aw c #1c7c1c", +"ax c #347c34", +"ay c #348c34", +"az c #0c4c0c", +"aA c #64e264", +"aB c #4c9b4c", +"aC c #0c3c0c", +"aD c #4ccc4c", +"aE c #4c8d4c", +"aF c #64a364", +"aG c #84c684", +"aH c #245c24", +"aI c #346c34", +"aJ c #145c14", +"aK c #44bb44", +"aL c #84ba84", +"aM c #248c24", +"aN c #34ac34", +"aO c #64bc64", +"aP c #246c24", +"aQ c #146c14", +"aR c #2c9c2c", +"aS c #64ee64", +"aT c #5cdc5c", +"aU c #64ac64", +"aV c #0c2d0c", +"aW c #244b24", +"aX c #64fc64", +"aY c #649d64", +"aZ c #44aa44", +"a0 c #3c9d3c", +"a1 c #84ce84", +"a2 c #9c9a9c", +"a3 c #4c7e4c", +"a4 c #74ae74", +"a5 c #5cca5c", +"a6 c #54a254", +"a7 c #3c923c", +"a8 c #84fe84", +"a9 c #94fe94", +"b. c #94de94", +"b# c #acfeac", +"ba c #74ce74", +"bb c #74fe74", +"bc c #3cc63c", +"bd c #044604", +"be c #848684", +"bf c #444644", +"bg c #2ca62c", +"bh c #0c660c", +"bi c #8cca8c", +"bj c #448644", +"bk c #2c662c", +"bl c #042604", +"bm c #249624", +"bn c #6caa6c", +"bo c #147614", +"bp c #449644", +"bq c #242624", +"br c #34b634", +"bs c #6cfa6c", +"bt c #2c762c", +"bu c #2c862c", +"bv c #a4a6a4", +"bw c #1c561c", +"bx c #54b654", +"by c #1c461c", +"bz c #043604", +"bA c #5c965c", +"bB c #1c661c", +"bC c #84d684", +"bD c #646664", +"bE c #147e14", +"bF c #2c7e2c", +"bG c #2c8e2c", +"bH c #5ce65c", +"bI c #54d654", +"bJ c #043e04", +"bK c #44ce44", +"bL c #448e44", +"bM c #5ca65c", +"bN c #7cc67c", +"bO c #1c5e1c", +"bP c #041e04", +"bQ c #8c8e8c", +"bR c #2c6e2c", +"bS c #0c5e0c", +"bT c #7cba7c", +"bU c #545654", +"bV c #2cae2c", +"bW c #343634", +"bX c #1c6e1c", +"bY c #747674", +/* pixels */ +".4af.cbWbWa2agbD.8aabqbU.cbea2#AbU#0a#a##Oa..Y#0#0#Oaaaa.JbqaabQa..b.JbDa#.6acbU.6bDa#a..4aabUbf", +"bY#z#ObqbqaabDaa#z.ca.aaa#bD.8.6aaa.#abUaa#O#8aba#afa#bY.c.6.4bfbDbf.c.8.8bQ#Obfac#0aabf.Ja#a##0", +"abbUa..8a#bU.ca.bW.ba.aabU.8.6.8#z#O#zab#0bWahbDaabU#0bD#ObW.8#Oaf.bbW.caiaf#0bYacbW#Oa#ab#0bfbQ", +"ac.4.J.8.4a.aaa2bQ#a.J.6.6bfbUa#.qa##6bQ.4acbYaaaa.4aa#0bq.q.JbD.6a.bfbebea.bUaca#.6.4aabWbqbW.4", +".ca#bUbDa#.cbU.baba.#Abeaa#Obqa..Rbd.0aQ#saM#raNaK#j.E.3aw.e.j.abz#A.6.YbQbWbUbq#0bW#0aabD#a.qbW", +"bYbq.4#Oabbf.c.6.4a#.8bYbfbP#g.AaC#nbk#E#I.I#x#C#C.G#F#daF#w#lap#N#gbl.kafbUbUaba2aa.cbY#0#Aa2.J", +".b.b#0aabWa.a.bebfbD.c.XbP.k#g.A#f.wbk#E#i.maL.fbiaj#Ka4#5#w#l#yaW#GaVbPbP.XbW.8a#.c#O.c#O.JabbU", +"bQbe.cbQ.J#8ahbQ.8.c.X.XbP.k#gasby#yaI#E#w.Uav.2avbnaYbAa3.s#TaW.yas#g.kbP.X.X#Oa#aa.q.c#0a.bQa.", +".J#O.8.6a##Obeaaab.X.X.XbP.k#gas#fapaI#t#Y.U#WavavavbnaF#EaH#n#n#vas#g.kbP.X.X.X#0.4bq.6#A.J.4be", +".YbY.8.4bfbU.ca..X.X.XbPbPbl#gas#fapaI#EaE#oaUaU#FaUaF#I#Sat#H#faCas#g.k.kbP.X.X.X.J.q#Oaia#.4.J", +"aia.bU#O#Aa.#ObP.XbP.XbP.k#g#gas#H#DbwbR#m.T#I.UbMbMa6#i#maI#D#H#Nas.A#g.kbPbP.X.X.X#za..vbf.8#0", +"#Aaca##6ab#AaabPbP.k.k.k.k#g.AaC#HbwbOaP#tbL.oaB#IaB.o#mbt.CaJ#Daz.abz#g#gblbPbPbPbP.cbf.6.q#0aa", +"#0#0#Oa#.cbU.k.k.kblblbl#g#gbzbJaz#DaJ.7#s.u#ua7a7ay#B#s#s#s.7.Can#N.R.A#g#gbl.kbP.k.k#0abbfa.#0", +"bW.6.b.caa#Obl#g#gaV.A.A.AbzbJ.aanbS#Q#XbFayayaybG.HaZ#r#B#X#Q.7#Q#DazbJbzbz#g#g#g#gbl#za.bDbQa#", +"bDa#bWbqaabY.AasaV#gasaC.RbJ.aazan#Q#Xaw#BaM#V#P#9#9#PaM#sboaQ#QbX#QaJaz#N#v#f.y.yas.A#zbQafbWa#", +"#Oa#.JbUbW.RaCasaC#v#v#N#Nazan.0#QaQbo#r#e#9.QaAbIaKaRaMalaw.nbh#XbXaJ#D#H#f#n#naW#nbJ.R#0bf#Oac", +"bYa#bUa#.4bd#DaW#n#n.w#Haz#D#Qaw#saM#P#e.Q.OaTbI#MaN.d.d.d.3bobh.n#Q#QaJ#D.wap#y#T#T#N.a.8bQ.va#", +".4a..bbeaban#3#t#yaI#y#D#D#QbX#B#V#PaK.Q.OaSaSaobcbrbVao.Q.Q.l.9.9.nbXaJ#D.WaI#J#E#l#D.C.6a#a.bv", +"aa#Oaf#AbQ.ebj#w.h#E.i.W#Q.7#sbG#r#p#M.OaSbH#Z#Z.pbKam#2aS#c#1bEbhbhaQ.CaJbOaI#E#waEat#h.Ya#.Jab", +"a.a#a#bQ.cbo#YbnaY#SbRatbBbX#B#V#P#1aoaS#b#b#R.g.g.zbb#kararao.l.KaQaQ#hatat#3#waY#ibX#uaf#Oaa.c", +"ab#O.qbU.4a0aUavaF#E#3aPaPaw#sbmaR#1.S#b#b.zaXaXaX#ka9#kbb#2#c#P.3bo#Q#Q.7aP.Z#o.m#IbF.P#Aa#ab.4", +"#0bfac.6af.x.V#xbn#w#tbt#h#s#B#V.d.r.Sam.z#2bbbb#q#L#La9#qbbaS.1aR.3aw#XaPbt#3#t.Uae.H#7bY#Oaa#0", +"aaaa.c.6bU.DaOa1bT#o.hax.L#saM#raN#Marar#2a8#ka9#Lb##Lad#qa8bsao#p#rbG.u#4.oaEbAaEaq#r.Da#acaaa#", +"aa.8bUac.6#UbabCbN.Ubj#m.u#s.MaRaN#Mam#2#2#q#q#L..b##La9#q#k#2bH#eaNbG.ubp#I#YavbN#dbma5.6.6bD.J", +"#O.4.qaa#Obx#Ca1.VaUbjax.L#saM#VaR#1ambbbb#qad#La9#L#La8bb#2aSaDaK#P#B#BbL.TaEaU.VaB.E.D.8.8bq#O", +"#Oa#bfa#aa.x#x#Fav#o#t.t#h#B#B#V##bg.#ambb#qa9#qad#L#qaXaXbb#bbI.r#Vaw#saP#mbjaE#wbn.GaZbebY.c.6", +".4.6bWbD#Oa0auaB#W.B#3aP#h.ubGbG.l##aNbc.gaXbba8ad#La8#Rbb#karaT#PaMaQ#QbB.t#t#w#tbAbT#V.b#AaabW", +"#zbfbU.c.b.uay#I#i#iaIaPaP#s.HaMaM##.5.S#caX#2#qadad#2.garbbaTaDaNbGbobXaP.taE#waI#JaFaw.8bW#a#0", +"beaabeaba.#X.L#S#l#latbObB#hbG.MalaM.dbK.O#2a8#q#qbb#ZbH#b.O.Q#j#PbGawbXbBbR#l#E#y#T#i#Qbfaf.baf", +"#O#0#Oab.8aJaP#J#yaH#nbwaJbB#sbo.n.K.l#1#cbs#2bsarao.S#caT.Q#1aR#V#B#h.7.Watbk#y#n#y#3.0.6bUbqbU", +"bDaa.c.4.b.a.W#y#nby#N#N#DanaJbS.e.9al#PaK.1#eaoaDbKaD.Q#M#1aRaw#X#X.7aJ#D#n.wbyaW#y#HbdbY.caa.J", +"bD#0ab.4a#.R#faW#vaCasaC#N.Nanan.0.ebo#V.d#p#1#1#1aKaK#1aN#rbGawaQaQbBazbJ.Ras#fby#naC.RaibD.8bU", +".ca.bYbQbWaa#gas#vas.A.A.RbJazan.0.eaQ#BaMaR#p#paRaRaR.H#VbG#BbX.0aJan#Nbz#g#gasasas.A.Y.J.Y.qaf", +"ai.8.8.4#ObWbl.kaV#gas#g.A.R.aazan#QaQ#X#s#B#B#VbG.M.M#Baw#sbX#Qanan#NbJ#g#gbl#gbl#gbl#8ab#O#8bD", +"#Oaaaa#Oa.#0bl.kbP.kbl#g#g.A.R#NanaJbB#h#hbF.ububuayay#4.L.7#Qan#N.abJ.A#g.k.k.kbP.k.k#8.c#0.bbf", +"#8ab.c.6bUa.ab.XbPbPbP.kbl#gbzbJ#N#H.C.t.L#m#u#u#u.o.o.TaxaPbO#H#N.R.A#g.k.kbPbPbPbPbU#6bY.6a#.6", +"#A#O#0.qaaaa#O.X.X.XbPbP.k#g#g.A#N.wat#3.hbL#i#I.U#I#YbL.hbRaH#DaCbz#gbl.kbPbP.X.XbP#6aaaabWbD.q", +"bfab.bbDbD.8.4bD.X.X.XbPbP.kblas#fapaI#laE#i#i#I#i.T.h#t#3aI.i#naC.A#g#g.kbP.X.X.X.ca##.a2aa.c.c", +"bfbWbWbfa#.ca#.JbU.X.X.XbP.k#gasby#y#l#E#w.haE.haE.h#S#E#E#l#T#n#vasaV.kbP.X.X.X#A.c#8bQ.6bD#0bU", +"bfbD#a.4bDbDbQ#0.8ac.X.XbPbPblaCaW.i.s#S.BaYaYaY.m.m.maY#wa3aIap.yasaV.kbP.X.X#Abv#abebY.cbWaf#O", +"a#af#0a2#Aa#ac.b.4#0bYbPbP.k#g#v#n#y#l#w.Bbn.2aGajbiaGbTaY.haIap#fasaV.kbP.X.b#.bv.8bY#Oa#bfbU#0", +"ab.Jbq.ba.ac#ObWaabq#0bUbW.k#g.A#fbwbk.haFavajb..Faka1.VaFaxaH#naC#g#g.k.J#zbq.YbvbWbfa.ab#OaabQ", +"#Oa#aabfa#.6bqa.bfaa.Jbea.aabfac.RbdanaQbFbG.E#p#1#p##albo.e.j.NbJ.Y#A.8ab#zbq.6#8bW#ObDa#bq.6af", +".6#ObWaa.bbD.c#ObDacbU.4a#bfa##O#O.c.Ja2#Abfa#.cac.Ja#.8bQ#AbUa.#Oaa.8bYbf#O.6.bbeafabbD#0bUaabq", +".c.8bW.bbQ.ba#.4.J.c.cbUaa#0bUaaa..bbWa2.8aaaa.4aabUaa.qa#.baaab.qbUbebD#AbD.cbebf.bbU.4a.bfac.c", +"bf#Aa.#8bv#0aabD.4aa.c#0bU#O.b#ObUbYbfa#bW#O.c#O#0a.#0abaaah.8#ObU.Jaa.4bY#A#aabbUbYbqbq#z.c.6.8", +"afa#af.bbYabbD#Obq.c#0bU.c#6bY#Oaf#Aab#0bQ.cbfaaa.bQ.Ya#bfbY.cai#8bf#Oa.a2a.bDbD.4.qaa.4.Y#8bDa.", +"aa.ca..cbY.c.6#0bfbD.4#a.4.6aba.bv.b.c.Jbf.6#0.cbq.ba.a.bW.6bW#.abaabQ#ObY.4#ObWbfbq#O.ca.bD.8.6" +}; diff --git a/kblackbox/pics/red.xpm b/kblackbox/pics/red.xpm new file mode 100644 index 00000000..8e96946e --- /dev/null +++ b/kblackbox/pics/red.xpm @@ -0,0 +1,202 @@ +/* XPM */ +static char *red[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 147 2", +/* colors */ +".. c #0c0a0c", +".# c #fc7674", +".a c #8c0204", +".b c #dc3e3c", +".c c #3c3a3c", +".d c #ac1614", +".e c #fcaeac", +".f c #c42a2c", +".g c #4c0204", +".h c #ac0204", +".i c #fc5a5c", +".j c #1c1e1c", +".k c #f44e4c", +".l c #dc2e2c", +".m c #fc9294", +".n c #fccacc", +".o c #9c0e0c", +".p c #545654", +".q c #ec3e3c", +".r c #c41e1c", +".s c #2c0204", +".t c #ac0e0c", +".u c #9c0204", +".v c #bc2224", +".w c #6c0204", +".x c #dc3634", +".y c #ec4644", +".z c #d42224", +".A c #141214", +".B c #fc8684", +".C c #4c4a4c", +".D c #bc1614", +".E c #fcbebc", +".F c #fc6a6c", +".G c #2c2e2c", +".H c #fca2a4", +".I c #fcdadc", +".J c #646264", +".K c #c42624", +".L c #bc0e0c", +".M c #9c0a0c", +".N c #d42a2c", +".O c #ac0a0c", +".P c #ec3a3c", +".Q c #1c0204", +".R c #fc7e7c", +".S c #940204", +".T c #e44644", +".U c #c41a1c", +".V c #444244", +".W c #b41e1c", +".X c #fcb6b4", +".Y c #5c0204", +".Z c #fc6264", +".0 c #242624", +".1 c #fc5654", +".2 c #fc9a9c", +".3 c #fcd2d4", +".4 c #f43e3c", +".5 c #cc1e1c", +".6 c #3c0204", +".7 c #b40e0c", +".8 c #a40204", +".9 c #7c0204", +"#. c #e43634", +"## c #f44644", +"#a c #e43e3c", +"#b c #b41614", +"#c c #cc2a2c", +"#d c #b40604", +"#e c #fc4e4c", +"#f c #e43234", +"#g c #a40e0c", +"#h c #5c5e5c", +"#i c #d43234", +"#j c #1c1a1c", +"#k c #fc8e8c", +"#l c #545254", +"#m c #bc1e1c", +"#n c #fcc6c4", +"#o c #fc7274", +"#p c #343634", +"#q c #fcaaac", +"#r c #fce2e4", +"#s c #6c6a6c", +"#t c #0c0e0c", +"#u c #fc7a7c", +"#v c #8c0604", +"#w c #c41614", +"#x c #3c3e3c", +"#y c #540204", +"#z c #ac0604", +"#A c #fc5e5c", +"#B c #242224", +"#C c #f45254", +"#D c #dc3234", +"#E c #fc9694", +"#F c #fccecc", +"#G c #5c5a5c", +"#H c #ec4244", +"#I c #c42224", +"#J c #340204", +"#K c #ac1214", +"#L c #9c0604", +"#M c #740204", +"#N c #dc3a3c", +"#O c #ec4a4c", +"#P c #d42624", +"#Q c #141614", +"#R c #fc8a8c", +"#S c #4c4e4c", +"#T c #bc1a1c", +"#U c #fcc2c4", +"#V c #fc6e6c", +"#W c #343234", +"#X c #fca6a4", +"#Y c #fcdedc", +"#Z c #646664", +"#0 c #cc2624", +"#1 c #bc1214", +"#2 c #a40a0c", +"#3 c #d42e2c", +"#4 c #b40a0c", +"#5 c #240204", +"#6 c #fc8284", +"#7 c #2c2a2c", +"#8 c #cc1a1c", +"#9 c #640204", +"a. c #440204", +"a# c #840204", +"aa c #fcbabc", +"ab c #f44244", +"ac c #cc2224", +"ad c #b41214", +"ae c #e43a3c", +"af c #f44a4c", +"ag c #e44244", +"ah c #b41a1c", +"ai c #a41214", +"aj c #940604", +"ak c #444644", +"al c #fc6664", +"am c #fc9e9c", +"an c #fcd6d4", +"ao c #a40604", +"ap c #cc2e2c", +"aq c #d43634", +/* pixels */ +".C#W#7.j#W#S.c#j#Q.A.0.c.V#7#B#Q#B.G..#7.c#p.c.V.G.j#B#B#p.0.G#B.cak#S.G#l#B#j.j.0...cak#S#7#B#j", +"ak#Q#B#S.j.C#B.j#j.G.C#7#7.0ak.j.V#7.G.G.j#j.j.G.V#l.c.0.j#W#j#p#x#7#W.c#j#p.j.A.G#x.C.C#p.0#Bak", +"#W#W#t.c#Q.V#p.j#7ak#h#p#Q#B.C#Q#s#x.c#W.0#7#j#Q.0#S#hak#B#Q#Q#j.j.G#t#B#p#l.V.G.G#Wak.C#j.0.A.G", +".j.c.j.G#p.G.c.j.V.J.J#l#7#p.j#p#h#W.g#9#Ma#.a.S.a.aa#.aa#.9.Y#pak.C#Q...pak.p.j.A.j#B.c.0.c#7.j", +".V#B#W#p.C.j#t#j#x#W#W#x#p#j.G#W#9#9.w.9.9a#.w#M#M.9a#.a.S.a.a.aa##W#t#7#h.C#W#j#Q.0.G#p#p#x#W.j", +"ak#Q#l.V.0.V#B#j.0.0.0#p.j#B#9.wa#a#.S.S.u.u.S.u.S.u.S.Sa#a#.a.u.S.aa##x.C.C.0.A#7#W#7.j#7ak#B#7", +".C#7#S#S#W#S#p.j.G#B.0.G.g#9#M.9a#.S.a.9.9a#.S.u.u.h#z.h.8.aa#.S.8.8.S.9a#.c.c.j#j#7.G#x#j#Q.G.p", +"#S#t#l#l.j#S.c#7#j.j#l.g#M#9.w#M.9.9.9a#.9a#a#a#a#a#.u#z#d.h.ua#.h.h.u.8.u.a.J.0.0#Q.G.c#W.G#j#W", +"ak#7#7#7#W#W.G#S#W#B#y.w.w.w#M.9a#a#.aa#.a.Sa#.a.u.L.5.z#1.h.u.a.S.L#d.u.8.u.9#B#j#j#p.C#W.j.j#j", +".C.Cak.0#S#S#x#7ak#y#M.w.w#M.9a#a#.a.a.a.a.a.S.u.N#H#f#w.h.u.S.a.S.8.L.h.h.8.8a##W#t#j#x#7#Q.A#W", +"#B#S#lak#W#S#W.G.g#9.9#M.9a#a#a#.a.u.S.a.S.S.8#z#A#V#4.S.u.h.8.L.l.L#w.L.8.u.u.Sa#.j...0#t.C#p#B", +"#7#j#p.G#j#B#Ba..g.Y.w#M.9a#a#.a.S.S.Sao.D#Tae#V.l.u.8#6.X.Zal.P.i.Pac#w#4.h.u.u.S.w.0.c#p#h#7.0", +".j#j.0.j#B#W.0#y#y.g#M.9a#.a.a.S.S.uao#b.K#D.k.8.u.h.Z.E#8#8#d#P.#.F.l.L#4.h.h.8.S.a#7#Q#7#j#W.0", +".j.j#j.V#W#W.g.Y.w#9.w.9a#.9a#.S.u#L#2.u.8.8#zae#u#k.X.L.8.u.u.h#z.4###w#1.L#d.8.ua##M#7.j.c#S#7", +".j#B.0.V#x.G.g.w#9#M.9#Ma#.a.a.a.a#L.8.7#I#c#3#I.v#a#e.L#4.L#z.8.u.5.Z.l#w#w#4.8.S.aa##7#W.V.p.c", +"#B.0#7.0ak#J.g.Y.w.9a#.9a#.S.S.u#L#2ao.N#K#bah#m#I#i.P.z#u.X.I.P.S.h.R#D.U#w#d.8.u.a.a#M#Sak#S#W", +".G.A.c.0#W#y#9.Y#M.aa#.9.a.u.S.uao.O.O#c#K.M#b#I#c#.#f.Ran#Uan#V.u.uab.q.7.O#4.u.S.Sa#.a#W.p#h.c", +"#j#j#p#p#J.g#9#9.9a#.aa#.a#L.uaoao.D#c#m.M#g#g#mae#e.i.H#Y#F.X.E.h.8#w.L#d.7#1.8.S.Sa#.a#9.j#G.C", +".0#B.0.c.ga..Y.w.9a#a#a#.a.a#2#K#Tah#iah.M#K.b#u.##V.H#naaam#X#q#8#4.U.7#z#1#1.h.S.Sa#a#.w.A.V.J", +".c#x.j#l#y#y#9.w#M.9a#a#.aa#.a#L#g#K.W.vaqaq.i.n#U#o.n.I.eaa.3.H#e#1#8.7#4.8.8#d.S.S.S.a.9.Aak#l", +".G#l#j#x#y#9.Y#9.w.w#Ma#.a.a.Sajajai#K#m#u#R.2.E#6aa#U.I#r#Y.3.e#e#8#4.h.h.u.u.8.8.u.u.Sa#.j#pak", +"#S#W.A#S#J#y#y#9.w.w.9.9a#.S#Lad#Tai#gai.W.i#q#Eamaa#X.X.naa.3.I.B#w#d.8.8.8.u.S.a.u.S.aa##B#p.0", +"#j.0#Q#B.6.6#y.Y#9.w#M.9.a.u.t.x.yaf.o#g#m.Kaa#E#q#6#V.2#q.2#Y.H#R#.#4.L.8.S.u.aa#.8.aa#a##B.c#B", +"#W#B#B.j#J.ga..Y#9#9.9a#.a#2#H#V#6#Aai.o.d.d.e.2.m#ual.n#Y#6.2#F.n.H#o.#.r.8.a.a.9a#.9.wa#.J.G.0", +"#Q#t#j#7#5#ya.#y.Y#y.w#M.aad#mal#6.B#K.M.Magaa.2#C.kam.nan#k#Oag.y#N.i#Dad.S.S.aa#a#.9.Y.9#x.0#j", +".j#B#B#j.Q#ya.a..g#y.Y#M.a#K#L.v#E#R#o.i#6#E#V.bap#R.X.3#6##ap#I#m#T#b#2.S.u.aa#a#a#.9.w.w#j#j.j", +"#Q#W#p.j.s#9a..6.g#y#y#9a#.S.S.M#mafaf.T#O#i.Wah.F#6.#.Bal#D.y.y#D.N.t.u.S.S.aa#a#.a.9a#.a.C#7.0", +"#7#7#W#7a..w.6.6.g#y.Y#9a#.Sa#a#.a#v.a.M#g#g.d#iaeaqah.K.1.Z#A#A#.#Dad.8.u.Sa#.a.S.S.S.9.9#B.A#p", +".p#Q.j#7a.a..Y.6.g#y#9#y.9#La#a#.9a#a#.a.a.d.v#c#0.Kah.d.f#Hae#2#Lao.t#z.u.S.u.S.u.a.S.9.w.0.c#j", +"#W#7#t#j#5#J#ya.#y#9.Y.Y.w#2a#a#.9a#.a#v#L#I.v#b#i#cahah.K.d#L.a.S.S.S.S.u.S.u.u.u.u.a.9.Y#Q.A.j", +".G#7#p#S#G.s.g#y.Y#9.Y#y.S#g.S#M#Ma#.a#K#m.v.Maj.M.d.K#I.d#2.a#L#K#L.a.a.a.S.S.S.S.S.9.w#j#j#B#p", +".c#j#B#p#S.sa.#y#y.Y.Y#9.a.a.9.w.w.9.S#g.M.Sa#aj.aaj.M#g#g#2.Mad.U.D.O.Sa#.a.S.S.S.a#M#y.c#7#p#G", +".c.0...V#7#pa.#y.Y#y#9.w#M.9.w#9#9.9.a.aaja#.a.M#K#La#.S#2#K.t.O.t.7.O.S.9.S.a.aa#.w#M#W#B.G#Q.G", +".j#B#j#x.j#j#5.6.g.Y#9.w.w#9.w.w#9#Ma#.a#g.S.9a##K#b#g#2#g.O#gao.u#L.S#M.9.aa##M.w.w.Y#7#j.j.c.c", +"ak#S.G#7#W.V#S.6#Ja..Y#9#9.Y#9.w#9.9.9a##Laj#M.a#L#2#2ao#L#L.Sa#.9.a#M#M.9.9#M.w#9#y.j.A.c.G#B#p", +"#j#W.c.c.V#Q#W#J.s.6.6#y#y#y#9#M#Ma#a#a#a#a##M#M#L.M#L#L.u.a.9#M#M#M.w.w#M.9#y.Y.Y.g.G#7#7.c#B#x", +"#j.V.j.j#l#W.G#t#J#J#Ja..g#y.Y.w#M#M.9#M#M#M#M.9#L#2#L#L#L.u#M#M#M#M#9#9.w#9.Y.Y#y.J#B.G.G#B#t#p", +"#W.0#x.0#7.G#W#7.V.Qa..6a.#y.Y.Y#9#9.w#M#M#M.w.9.aao#L.S#L#L.u.S.a.w#9#9#M.9.Y.Y#p.p.j.c#x#Q#j#7", +".c.0#B.0#j#x#W#Q#x.c#5.6a..g#y#y.Y#9#9.w.w#M#M#M.9.S.S.9.a.aa#.S.a.a.w#9.9#M.Y#j#j#l.G#j#j#j#7#p", +"#x#j#jak#p#j#p.A.C.C#B#5#J.6.g.Y.Y#9.w#9#9#M.9.aa#.9.Y.Y#9#9#9.9a#.9#y.Y.g.g.c.j.j#W#l.0#p.0.0#p", +"#l.0#p#W#x#W#7.0#xak#p.0#5.s.6a..Y.Y#9.w.w.w.Y#y.g#y.g.g.g#y.g#Ma#.Y.ga.#J.C#p#7.C#p#x.0#7ak.p.0", +"#S#p#Q#W.0#Q.A.A.0#Q#j.c#G.J#5.6.g#y#y.Ya.a..g.Y#y.Y.Y#y#y.ga..w#9a.#J.V#W#W#S.pak#Q.c.c.0#l.C#S", +".c.G#B.j#x#p#7#Q#7#p#W#t.cak#W#7#Ja.#J#J#J.6a.#9.w.Y.g#y.g.6.Y.g.6#Q#7.j#B.0.c.p.C#B#S#W.jak.p.V", +"#B#t#S.C.0.A#7.V.G.0#7#x.0.A#7.c#x#B#5.Q#J.g#J.6.g.6a..6#J.6#J#B#W#B#p#l.V.V.G.V#j.c.c.G.j.0#pak", +"#j#7#B.j#B.0.j#S.c.j#W.A#p#p.0.G#B#G.J#7.A.0.G...G#j#W.G#W.c#7.j#W#B#B.p.p#l#S#B.0#l#W.0#B.j.A.j", +".0#Q#W#W.0#7#W#p#j#W#l#W.Vak#7#j#x.p#l.V.G#j.j#j#W#W#Q#Q#Q#x#j.j.0.0.0#Bak#l.G.j#B.j.0.C.C#W#B#Q", +"#W#7.j#j#W.c#W#j.j.Gak.V#j#Z.V#j.V#l#lak#p.A#Q.c.0.0#j#x#W.j#7.0.p.V.G#t#W#l.V.0#j#l#S#S.C.V#W#7", +"#7.j.C.c#x#t.0.G.p#W#pak.c.G.j#Q#7.A#x.0#j#Q.c#hak#x#W.A#x#B.c#p#x#G.C#x#p.C#x.c#Q#h#lak#B#p.G.j" +}; diff --git a/kblackbox/pics/white.xpm b/kblackbox/pics/white.xpm new file mode 100644 index 00000000..06c38cec --- /dev/null +++ b/kblackbox/pics/white.xpm @@ -0,0 +1,100 @@ +/* XPM */ +static char *white[] = { +/* width height num_colors chars_per_pixel */ +" 48 48 45 1", +/* colors */ +". c #9e9e9e", +"# c #9f9f9f", +"a c #a0a0a0", +"b c #a1a1a1", +"c c #a2a2a2", +"d c #a3a3a3", +"e c #a4a4a4", +"f c #a5a5a5", +"g c #a6a6a6", +"h c #a7a7a7", +"i c #a8a8a8", +"j c #a9a9a9", +"k c #aaaaaa", +"l c #ababab", +"m c #acacac", +"n c #adadad", +"o c #aeaeae", +"p c #afafaf", +"q c #b0b0b0", +"r c #b1b1b1", +"s c #b2b2b2", +"t c #b3b3b3", +"u c #b4b4b4", +"v c #b5b5b5", +"w c #b6b6b6", +"x c #b7b7b7", +"y c #b8b8b8", +"z c #b9b9b9", +"A c #bababa", +"B c #bbbbbb", +"C c #bcbcbc", +"D c #bdbdbd", +"E c #bebebe", +"F c #bfbfbf", +"G c #c0c0c0", +"H c #c1c1c1", +"I c #c2c2c2", +"J c #c3c3c3", +"K c #c4c4c4", +"L c #c5c5c5", +"M c #c6c6c6", +"N c #c7c7c7", +"O c #c8c8c8", +"P c #c9c9c9", +"Q c #cacaca", +/* pixels */ +".aefbgjl.jtledrehhigdfkmrnehqkoffilj#ehrgefdaecd", +"abgmennigjiiichejkkyJnpglnkprkpsIFGrmcmptrleacc.", +"edcbkCtGCing#mIHINLLHLHjgGKNIILADIGJGCangjxFxumw", +"srlxyzHzFxIAGFLLGKJIJHILNLLIMFFIFJJDFFyDxptxzxqn", +"mwopxAwCzCyLFGDNLMNEFILOMOKFIxGIGLGBKIDFHysqutzi", +"usnEExCGEyGMuDHilsirFMMCyKGFIDxsBNJECEBDIzxptumu", +"qwyykvgonihhpoptsssvqltvurwNIFGtqkJFICDCDgdjgios", +"if#ilefjiehojprpxquqjvvuxstkrCBxBsrhrljmrtqljfpf", +"iebkjjqfmkrwztsvqxwrGtsuuxyutqotvjnmgiiipslndidi", +"fcptmgpBpvGJyzCyFCwBDrmppyqpnkovposormdilkuomwnd", +"vAtmtDDyEBMGCGAuBGMMHONMPMkINNMMKNHFJBFFmBurxqAw", +"uDwtuCBGADJzwJJLKONNNPPPOOMNPNPKLMLNGJJGDxEmwtBp", +"rFwGyyFxHDDJAHEMNPNNPPNNOOKNOPNIPMKKHHKLAGwuyEsv", +"xArkjvzyrzvGDIIFHLHMPPOPNPuunwuzqOKJIAIKIwtCDlvu", +"mrnwtusxqDvuwuuwltuxDmwGxtFxFCyvCtBBowilzJxuBktp", +"jpivtAuACsDCBHEFvBzwwxBDvyFIGzEEBCzvzDnfnmwAsqyv", +"msmxoryqtvywEutuyBCHFCFDGCyNNLHzzxDACwvtklhqmtso", +"wpklzBAFCxDEttJKMJMOPMMMQOQQPLNCECzzCABCGsrnrprl", +"tDyxvwxFyAEEFJKCOJFONNNPOQPQOOQOLGHGHBwIJKAEAlyt", +"mCAvvvDJwsACHJPGMNFMLNPOQOPNNOPPKDxEExHIJCFHvirw", +"uwsvyuAFxwCJEJKIKLHJLMQNQLDMINPGEvHJFHIIFHuDupnm", +"xtxxCvszAGDAEHIDKHGMNQNNONIIFJPJBJNEHFvEzxvHxtpv", +"sBBvruxpsGwKEELDHMNDGyyGIHKIBGHGILKFBuGzAAntzsBx", +"twyxlqCqIAKKvEDJIKEKFJIIMOJINELJKIOHstyJyEynryBw", +"rslylqqsFGLExGzHKHMCMKPPIONMMGDJDMIJKuABBptzzsBy", +"yqnprxCzutGMBEDCFJINIIKIIQJGEwwQHNHGCsrrDHFrzovf", +"tqjrtvAzyoAFMEDEEGMNNLCDDHDEJCECEEuDzAxDzzuqpnmj", +"oskqzyGwutuvEDDHuKGINJKDEEBGEEHDACvxzyBysCrqrwlo", +"tBvumruxDpzBFKyAwFNMFBPPOQHNPNIFBvIHByECJyAqyvpw", +"uEDCzBvByKJyEEIKJCGMJLOPPQPOQOIIFPJIGGExFEyCtArv", +"rtCAAxAEJECGzNKIMMLLJQPPOOOONKPFKIPMIEBFDJHEBxuw", +"xsHGxxxFAyxAKOKDPPNKIKLQLJOOPLPNLHALOyHFEJzEBzqz", +"ghkeDtsyHKruqMMJDwNyrEPPOMPFAIEwrBwoJJEIzuppsxrq", +"hmpkkhsolrsvsDywwGxzFHzDoCGFyAAmrxoErAxuvslyvskq", +"igkjkntygplnwrwABGvHzIvssytusDuAwqAzuyrshnlAtowg", +"qqouloxxywlgpBvzEFABBwwspxzuomqyPJxupgxrvpmvojnh", +"vwikzgwCEEFHMgitruxuszNPNOqrPKLOKJKGJGxpspCztwoh", +"yDwirCtzAJNJLMKMNMJJOOONNKOKMONLNLFJGEDGEFFttptr", +"uByvvAvzyDKHLHKKOHOKNLNPPJPMJFNJJIEILHAGEDxAwozz", +"vpwxssvznoIJKIFGELINNFJMEHNOLMMOJHHBjpGwDCzsuwBy", +"#avzxjqixhkmjJGBkhmrttqyvxqxxLrvvqlmioqsxxDBvyuu", +"phghddtolkmkilpqmyjtqtnnBsswDqvrswhgttmrqqvpjhdc", +"hdffkporpwskmupqfjshwsuugppuxkkpikmkhmmlgnjeejc.", +"dceelgkkvtjllqqrhtslnnniedke#ekorefeIzkgblcdhdgd", +"nvxBxglcnifGqdhFHMHLHIIJHDljmipKHKGEzzzEuhbg#wsy", +"ouwuwzqEDEyFEADAwFHIJNEFzIFJIGJICCEzBDFswpAwEvDw", +"osuIzFuECvDADBHGGJIJIJKDFKIGGHExCCJxzDBwstCyFEwr", +"tpvjzBtvCABHFEDEGBoHfeiILILLGKGAEHGyClgcBwGFuuie" +}; diff --git a/kblackbox/util.cpp b/kblackbox/util.cpp new file mode 100644 index 00000000..7282fe41 --- /dev/null +++ b/kblackbox/util.cpp @@ -0,0 +1,68 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: util.cpp +// +// The implementation of the RectOnArray class +// + +#include "util.h" + +RectOnArray::RectOnArray( int newWidth, int newHeight ) +{ + w = newWidth; + h = newHeight; + array = new ArrayType[w*h]; +} + +RectOnArray::~RectOnArray() +{ + delete[] array; +} + +/* + Size info... +*/ + +int RectOnArray::width() { return w; } +int RectOnArray::height() { return h; } + +/* + Utility function for mapping from 2D table to 1D array +*/ + +int RectOnArray::indexOf( int col, int row ) const +{ + return (row * w) + col; +} + +/* + Return content of cell +*/ + +ArrayType RectOnArray::get( int col, int row ) +{ + return array[indexOf( col, row )]; +} + +/* + Set content of cell +*/ + +void RectOnArray::set( int col, int row, ArrayType type ) +{ + array[indexOf( col, row )] = type; +} + +/* + Fill all cells witj type +*/ + +void RectOnArray::fill( ArrayType type ) +{ + int i; + for (i = 0; i < w*h; i++) array[i] = type; +} diff --git a/kblackbox/util.h b/kblackbox/util.h new file mode 100644 index 00000000..9effb880 --- /dev/null +++ b/kblackbox/util.h @@ -0,0 +1,42 @@ +// +// +// KBlackBox +// +// A simple game inspired by an emacs module +// +// File: util.h +// +// The definition of the RectOnArray class +// + +#ifndef UTIL_H +#define UTIL_H + +/* + This is used for gameBoard and graphicBoard fields +*/ + +#define ArrayType int + +class RectOnArray +{ +public: + RectOnArray( int w, int h ); + ~RectOnArray(); + + int get( int col, int row ); + void set( int col, int row, ArrayType type ); + void fill( ArrayType type ); + + int width(); + int height(); + +private: + int indexOf( int col, int row ) const; + + int w; + int h; + ArrayType *array; +}; + +#endif // UTIL_H diff --git a/kblackbox/version.h b/kblackbox/version.h new file mode 100644 index 00000000..f959cc8c --- /dev/null +++ b/kblackbox/version.h @@ -0,0 +1 @@ +#define KBVERSION "0.3.0" diff --git a/kbounce/Makefile.am b/kbounce/Makefile.am new file mode 100644 index 00000000..25d0909b --- /dev/null +++ b/kbounce/Makefile.am @@ -0,0 +1,76 @@ +if include_ARTS +artslib = -lartskde +else +artslib = +endif + +SUBDIRS = pics sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kbounce +kbounce_SOURCES = main.cpp kbounce.cpp game.cpp highscores.cpp +kbounce_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(KDE_USE_PIE) +kbounce_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) $(LIB_ARTS) $(artslib) +kbounce_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kbounce.desktop + +EXTRA_DIST = RULES $(xdg_apps_DATA) + +rcdir = $(kde_datadir)/kbounce +rc_DATA = kbounceui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kbounce.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/kbounce/RULES b/kbounce/RULES new file mode 100644 index 00000000..1636814b --- /dev/null +++ b/kbounce/RULES @@ -0,0 +1,25 @@ +====================== += Rules of Jezz Ball = +====================== + +Game/Level Length +1. The game will be over if + a) your number of lifes is zero or + b) your time is over. +2. When at least 75% of the field is filled. a level is completed. + +Lifes +3. A level is started with one life per ball on the field. +4. You will lose a life if a ball hits the inner of a wall. + +Balls +5. Each level include one more ball. +6. A ball is reflected by blue and black blocks. + +Walls +7. You build a wall with the left mouse buttoyn and change the direction with the right. +8. You can _always_ build _two_ "half" walls concurrently. If one half is already finished, + you can build another "half" wall. +9. A wall is finished if it is hit by a ball at its end or it hits a black block. +10. A wall is removed if it is hit by a ball at the wall's inner or two "half" walls hit each + other with their blue ends. \ No newline at end of file diff --git a/kbounce/configure.in.in b/kbounce/configure.in.in new file mode 100644 index 00000000..d34f1b32 --- /dev/null +++ b/kbounce/configure.in.in @@ -0,0 +1,4 @@ +if test "x$build_arts" != "xno"; then + AC_DEFINE_UNQUOTED(HAVE_ARTS, 1, [Define if aRts is available]) + LIB_ARTS="-lsoundserver_idl" +fi diff --git a/kbounce/game.cpp b/kbounce/game.cpp new file mode 100644 index 00000000..853a645d --- /dev/null +++ b/kbounce/game.cpp @@ -0,0 +1,699 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "game.h" + + +#define TILE_SIZE 16 + +#define TILE_FIRST ((FIELD_WIDTH-2)*(FIELD_HEIGHT-2)) +#define TILE_FREE (TILE_FIRST + 0) +#define TILE_BORDER (TILE_FIRST + 1) +#define TILE_WALLEND (TILE_FIRST + 2) +#define TILE_WALLUP (TILE_FIRST + 3) +#define TILE_WALLDOWN (TILE_FIRST + 4) +#define TILE_WALLLEFT (TILE_FIRST + 5) +#define TILE_WALLRIGHT (TILE_FIRST + 6) + +#define GAME_DELAY 15 +#define BALL_ANIM_DELAY 60 +#define WALL_DELAY 100 + + +#if HAVE_ARTS +SimpleSoundServer *JezzGame::m_artsServer = 0; +#endif +QString JezzGame::m_soundPath; +bool JezzGame::m_sound = true; + +#define MS2TICKS( ms ) ((ms)/GAME_DELAY) + +Ball::Ball(QCanvasPixmapArray* array, QCanvas* canvas) + : QCanvasSprite( array, canvas ), m_animDelay( 0 ), m_soundDelay( MS2TICKS(BALL_ANIM_DELAY)/2 ) +{ +} + +void Ball::update() +{ + // set pixmap frame + m_animDelay--; + if ( m_animDelay<=0 ) + { + m_animDelay = MS2TICKS(BALL_ANIM_DELAY); + int frameNum = frame(); + frameNum++; + if ( frameNum>=frameCount() ) + frameNum = 0; + setFrame( frameNum ); + } +} + +void Ball::advance(int stage) +{ + bool reflectX = false; + bool reflectY = false; + + m_soundDelay++; + + // ball already on a wall? (should normally never happen) + // commented out to stop bug which causes balls to + // sometimes stop when clicked on + // if ( collide(0, 0) ) setVelocity( 0, 0 ); + + // check for collisions + if ( collide(xVelocity(), 0) ) reflectX = true; + if ( collide(0, yVelocity()) ) reflectY = true; + if ( !reflectX && !reflectY && collide(xVelocity(), yVelocity()) ) reflectX = reflectY = true; + + // emit collision + QRect r = boundingRect(); + r.moveBy( xVelocity(), yVelocity() ); + JezzField* field = (JezzField *)canvas(); + + int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE ); + int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE ); + int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE ); + int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE ); + + if ( ul!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.top() / TILE_SIZE, ul ); else + if ( ur!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.top() / TILE_SIZE, ur ); else + if ( bl!=TILE_FREE ) field->emitBallCollisiton( this, r.left() / TILE_SIZE, r.bottom() / TILE_SIZE, bl ); else + if ( br!=TILE_FREE ) field->emitBallCollisiton( this, r.right() / TILE_SIZE, r.bottom() / TILE_SIZE, br ); + + // apply reflection + if ( reflectX ) setXVelocity( -xVelocity() ); + if ( reflectY ) setYVelocity( -yVelocity() ); + + // play collision sound + if ( reflectX || reflectY ) + { + if ( m_soundDelay>50 ) JezzGame::playSound( "reflect.au" ); + m_soundDelay = 0; + } + + // update field + update(); + QCanvasSprite::advance( stage ); +} + +bool Ball::collide( double dx, double dy ) +{ + QRect r = boundingRect(); + r.moveBy( dx, dy ); + JezzField* field = (JezzField *)canvas(); + + int ul = field->tile( r.left() / TILE_SIZE, r.top() / TILE_SIZE ); + int ur = field->tile( r.right() / TILE_SIZE, r.top() / TILE_SIZE ); + int bl = field->tile( r.left() / TILE_SIZE, r.bottom() / TILE_SIZE ); + int br = field->tile( r.right() / TILE_SIZE, r.bottom() / TILE_SIZE ); + + return ( ul!=TILE_FREE || ur!=TILE_FREE || bl!=TILE_FREE || br!=TILE_FREE ); +} + +/*************************************************************************/ + +Wall::Wall( JezzField *field, int x, int y, Direction dir, int tile, QObject *parent, const char *name ) + : QObject( parent, name ), m_dir( dir ), m_field( field ), m_startX( x ), m_startY( y ), + m_tile( tile ), m_delay( MS2TICKS(WALL_DELAY)/2 ), m_active( true ) +{ + //kdDebug(12008) << "Wall::Wall" << endl; + + // setup position and direction + m_dx = 0; + m_dy = 0; + switch ( m_dir ) + { + case Up: m_dy = -1; break; + case Down: m_dy = 1; break; + case Left: m_dx = -1; break; + case Right: m_dx = 1; break; + } + + m_x = m_startX; + m_y = m_startY; + + m_field->setTile( m_x, m_y, m_tile ); +} + +void Wall::finish() +{ + m_active = false; +} + +bool Wall::isFree( int x, int y ) +{ + if ( m_field->tile(x, y)==TILE_FREE ) + { + // check whether there is a ball at the moment + QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, + TILE_SIZE, TILE_SIZE) ); + if ( cols.count()==0 ) + return true; + } + + return false; +} + +void Wall::update() +{ +} + +void Wall::advance() +{ + update(); + + // move wall + if ( m_active ) + { + m_delay--; + if ( m_delay<=0 ) + { + m_delay = MS2TICKS(WALL_DELAY); + + // set previous tile + m_field->setTile( m_x, m_y, m_tile ); + + // check whether next place is still free + if ( isFree(m_x+m_dx, m_y+m_dy) ) + { + // move ball + m_x += m_dx; + m_y += m_dy; + + // set tile + m_field->setTile( m_x, m_y, TILE_WALLEND ); + } else + { + finish(); + emit finished( this, m_field->tile( m_x+m_dx, m_y+m_dy ) ); + } + } + } +} + +void Wall::fill( bool black ) +{ + if ( m_dx ) + { + for ( int x=m_startX ; x!=m_x; x+=m_dx ) + if ( m_field->tile(x, m_startY)==m_tile ) + m_field->setGameTile( x, m_startY, black ); + + m_field->setGameTile( m_x, m_startY, black ); + } else + { + for ( int y=m_startY ; y!=m_y; y+=m_dy ) + if ( m_field->tile(m_startX, y)==m_tile ) + m_field->setGameTile( m_startX, y, black ); + + m_field->setGameTile( m_startX, m_y, black ); + } +} + +/*************************************************************************/ + +JezzField::JezzField( const QPixmap &tiles, const QPixmap &background, QObject* parent, const char* name ) + : QCanvas( parent, name ), m_tiles( tiles ) +{ + setPixmaps( tiles, background ); +} + +void JezzField::setGameTile( int x, int y, bool black ) +{ + if ( m_background ) + setTile( x, y, black ? ((x-1)+(y-1)*(FIELD_WIDTH-2)) : TILE_FREE ); + else + setTile( x, y, black ? TILE_BORDER : TILE_FREE ); +} + +void JezzField::setBackground( const QPixmap &background ) +{ + // copy current field into buffer + int backup[FIELD_WIDTH][FIELD_HEIGHT]; + for ( int y=0; ybutton() & RightButton ) + { + m_vertical = !m_vertical; + if ( m_vertical ) setCursor( sizeVerCursor ); else setCursor( sizeHorCursor ); + } + + if ( ev->button() & LeftButton ) + { + emit buildWall( ev->x()/TILE_SIZE, ev->y()/TILE_SIZE, m_vertical ); + } +} + +/*************************************************************************/ + +JezzGame::JezzGame( const QPixmap &background, int ballNum, QWidget *parent, const char *name ) + : QWidget( parent, name ), m_wall1( 0 ), m_wall2( 0 ), + m_text( 0 ), m_running( false ), m_percent( 0 ), m_pictured( false ) +{ + QString path = kapp->dirs()->findResourceDir( "data", "kbounce/pics/ball0000.png" ) + "kbounce/pics/"; + + // load gfx + m_ballPixmaps = new QCanvasPixmapArray( path + "ball%1.png", 25 ); + for ( unsigned n=0; ncount(); n++ ) + m_ballPixmaps->image(n)->setOffset( 0, 0 ); + QPixmap tiles( path + "tiles.png" ); + + // setup arts +#if HAVE_ARTS + m_artsServer = new SimpleSoundServer; + *m_artsServer = Arts::Reference("global:Arts_SimpleSoundServer"); + if ( m_artsServer->isNull() ) + kdDebug(12008) << "Can't connect to aRts sound server" << endl; +#endif + m_soundPath = kapp->dirs()->findResourceDir( "data", "kbounce/sounds/death.au" ) + + "kbounce/sounds/"; + + // create field + m_field = new JezzField( tiles, background, this, "m_field" ); + m_field->resize( TILE_SIZE*FIELD_WIDTH, TILE_SIZE*FIELD_HEIGHT ); + + for ( int x=0; xsetTile( x, 0, TILE_BORDER ); + for ( int y=1; ysetTile( 0, y, TILE_BORDER ); + for ( int x=1; xsetTile( x, y, TILE_FREE ); + m_field->setTile( FIELD_WIDTH-1, y, TILE_BORDER ); + } + for ( int x=0; xsetTile( x, FIELD_HEIGHT-1, TILE_BORDER ); + + connect( m_field, SIGNAL(ballCollision(Ball *, int, int, int)), this, SLOT(ballCollision(Ball *, int, int, int)) ); + + // create view + m_view = new JezzView( m_field, this, "m_view" ); + m_view->move( 0, 0 ); + m_view->adjustSize(); + connect( m_view, SIGNAL(buildWall(int, int, bool)), this, SLOT(buildWall(int, int, bool)) ); + + // create balls + for ( int n=0; nsetVelocity( ((kapp->random() & 1)*2-1)*2, ((kapp->random() & 1)*2-1)*2 ); + ball->setFrame( kapp->random() % 25 ); + ball->move( 4*TILE_SIZE + kapp->random() % ( (FIELD_WIDTH-8)*TILE_SIZE ), + 4*TILE_SIZE + kapp->random() % ( (FIELD_HEIGHT-8)*TILE_SIZE ) ); + ball->show(); + } + + // create text label + m_text = new QCanvasText( m_field ); + + // create game clock + m_clock = new QTimer( this ); + connect( m_clock, SIGNAL(timeout()), this, SLOT(tick()) ); + m_clock->start( GAME_DELAY ); + + // setup geometry + setFixedSize( m_view->size() ); +} + +JezzGame::~JezzGame() +{ + m_balls.clear(); + delete m_view; + delete m_field; + delete m_ballPixmaps; +#if HAVE_ARTS + delete m_artsServer; +#endif +} + + +void JezzGame::display( const QString &text, int size ) +{ + qDebug("This function \"display\" shouldn't be called!!!"); + if ( !text.isEmpty() ) + { + //kdDebug(12008) << "text = " << text << endl; + + QFont font = KGlobalSettings::generalFont(); + font.setBold(true); + font.setPointSize(size); + m_text->setFont( font ); + m_text->setText( text ); + + QRect size = m_text->boundingRect(); + m_text->move( ( FIELD_WIDTH*TILE_SIZE - size.width() ) / 2, + ( FIELD_HEIGHT*TILE_SIZE - size.height() ) / 2 ); + + m_text->show(); + } else + { + m_text->hide(); + } +} + +void JezzGame::playSound( const QString &name ) +{ +#if HAVE_ARTS + if( !m_artsServer->isNull() && m_sound) + { + QString path = m_soundPath + name; + m_artsServer->play( path.latin1() ); + } +#else + return; +#endif +} + +void JezzGame::setBackground( const QPixmap &background ) +{ + m_field->setBackground( background ); +} + +void JezzGame::setSound( bool sound ) +{ + m_sound = sound; +} + +void JezzGame::start() +{ + m_running = true; +} + +void JezzGame::stop() +{ + m_running = false; +} + + +void JezzGame::makeBlack() +{ + // copy current field into buffer + for ( int y=0; ytile( x, y ); + + // fill areas that contains a ball + for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() ) + fill( ball->x()/TILE_SIZE, ball->y()/TILE_SIZE ); + + // areas still free can be blacked now + for ( int y=0; ysetGameTile( x, y, true ); + } + + m_field->update(); + m_view->repaint(); + + // count percent value of occupied area + int p = percent(); + if ( p!=m_percent ) + { + m_percent = p; + emit newPercent( m_percent ); + } +} + +int JezzGame::percent() +{ + int notFree = 0; + for ( int y=1; ytile(x,y)!=TILE_FREE ) + notFree++; + } + + return 100 * notFree / ( (FIELD_WIDTH-2) * (FIELD_HEIGHT-2) ); +} + +void JezzGame::fill( int x, int y ) +{ + if ( m_buf[x][y]!=TILE_FREE) return; + + // go left + int _x=x; + for ( ; m_buf[_x][y]==TILE_FREE; _x-- ) + m_buf[_x][y] = TILE_BORDER; + int stopx = _x; + + // fill above + for ( _x=x; _x>stopx; _x-- ) + if ( m_buf[_x][y-1]==TILE_FREE ) fill( _x, y-1 ); + + // fill below + for ( _x=x; _x>stopx; _x-- ) + if ( m_buf[_x][y+1]==TILE_FREE ) fill( _x, y+1 ); + + // go right + for ( _x=x+1; m_buf[_x][y]==TILE_FREE; _x++ ) + m_buf[_x][y] = TILE_BORDER; + stopx = _x; + + // fill above + for ( _x=x+1; _xTILE_FREE && tile!=TILE_WALLEND ) + { + kdDebug(12008) << "Collision" << endl; + + // play explosion sound + playSound( "death.au" ); + + // stop walls + if ( (tile==TILE_WALLUP || tile==TILE_WALLLEFT) && m_wall1 ) + { + kdDebug(12008) << "up or left" << endl; + m_wall1->finish(); + m_wall1->fill( false ); + delete m_wall1; + m_wall1 = 0; + } + + if ( (tile==TILE_WALLDOWN || tile==TILE_WALLRIGHT) && m_wall2 ) + { + kdDebug(12008) << "down or right" << endl; + m_wall2->finish(); + m_wall2->fill( false ); + delete m_wall2; + m_wall2 = 0; + } + + // update view + m_field->update(); + m_view->repaint(); + + // send death msg + emit died(); + } +} + +void JezzGame::buildWall( int x, int y, bool vertical ) +{ + if ( !m_running ) return; + + kdDebug(12008) << "JezzGame::buildWall( x=" << x << " y=" << y << " vertical=" << vertical << " )" << endl; + if ( m_field->tile(x, y)==TILE_FREE ) + { + playSound( "wallstart.au" ); + + // check whether there is a ball at the moment + QCanvasItemList cols = m_field->collisions( QRect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE) ); + if ( cols.count()>0 ) + { + kdDebug(12008) << "Direct collision" << endl; + emit ballCollision( (Ball*)cols.first(), x, y, TILE_WALLUP ); + return; + } + + // start walls + if ( !m_wall1 ) + { + m_wall1 = new Wall( m_field, x, y, + vertical? Wall::Up : Wall::Left, + vertical? TILE_WALLUP : TILE_WALLLEFT, + this, "m_wall1" ); + connect( m_wall1, SIGNAL(finished(Wall *, int)), + this, SLOT(wallFinished(Wall *, int)) ); } + + if ( !m_wall2 ) + { + m_wall2 = new Wall( m_field, x, y, + vertical? Wall::Down: Wall::Right, + vertical? TILE_WALLDOWN : TILE_WALLRIGHT, + this, "m_wall2" ); + connect( m_wall2, SIGNAL(finished(Wall *, int)), + this, SLOT(wallFinished(Wall *, int)) ); + } + } +} + +void JezzGame::wallFinished( Wall *wall, int tile ) +{ + //kdDebug(12008) << "wallFinished" << endl; + playSound( "wallend.au" ); + + if ( tile==TILE_WALLEND ) + { + if ( m_wall1 ) + { + m_wall1->fill( false ); + delete m_wall1; + m_wall1 = 0; + } + + if ( m_wall2 ) + { + m_wall2->fill( false ); + delete m_wall2; + m_wall2 = 0; + } + } else + { + if ( m_wall1==wall && m_wall1 ) + { + m_wall1->fill( true ); + delete m_wall1; + m_wall1 = 0; + } + + if ( m_wall2==wall && m_wall2 ) + { + m_wall2->fill( true ); + delete m_wall2; + m_wall2 = 0; + } + } + + m_field->update(); + m_view->repaint(); + + makeBlack(); +} + +void JezzGame::tick() +{ + if ( m_running ) + { + if ( m_field ) m_field->advance(); + if ( m_wall1 ) m_wall1->advance(); + if ( m_wall2 ) m_wall2->advance(); + } else + { + for ( Ball *ball=m_balls.first(); ball!=0; ball=m_balls.next() ) + ball->update(); + + if ( m_field ) m_field->update(); + if ( m_wall1 ) m_wall1->update(); + if ( m_wall2 ) m_wall2->update(); + } + + //kapp->syncX(); +} + +#include "game.moc" diff --git a/kbounce/game.h b/kbounce/game.h new file mode 100644 index 00000000..eb5208cd --- /dev/null +++ b/kbounce/game.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef GAME_H_INCLUDED +#define GAME_H_INCLUDED + +#include +#include +#include + +#if HAVE_ARTS +#include + +using namespace Arts; +#endif + +class QTimer; +class JezzField; + +#define FIELD_WIDTH 32 +#define FIELD_HEIGHT 20 + +class Ball : public QCanvasSprite +{ + public: + Ball(QCanvasPixmapArray* array, QCanvas* canvas); + + void update(); + void advance(int stage); + bool collide( double dx=0, double dy=0 ); + + protected: + int m_animDelay; + int m_soundDelay; +}; + + +class Wall : public QObject +{ + Q_OBJECT +public: + enum Direction { Up, Down, Left, Right }; + + Wall( JezzField *field, int x, int y, Direction dir, int tile, + QObject *parent=0, const char *name=0 ); + + void finish(); + void fill( bool black ); + +signals: + void finished( Wall *wall, int tile ); + +public slots: + void advance(); + void update(); + +private: + bool isFree( int x, int y ); + + Direction m_dir; + JezzField *m_field; + int m_dx, m_dy; + int m_x, m_y; + int m_startX, m_startY; + int m_tile; + int m_delay; + bool m_active; +}; + + +class JezzField : public QCanvas +{ + Q_OBJECT +public: + JezzField( const QPixmap &tiles, const QPixmap &background, QObject* parent = 0, const char* name = 0 ); + + void setGameTile( int x, int y, bool black ); + void setBackground( const QPixmap &background ); + +signals: + void ballCollision( Ball *ball, int x, int y, int tile ); + +private: + friend class Ball; + bool m_background; + QPixmap m_tiles; + QMemArray m_backTiles; + + void setPixmaps( const QPixmap &tiles, const QPixmap &background ); + void emitBallCollisiton( Ball *ball, int x, int y, int tile ) + { emit ballCollision( ball, x, y, tile ); } + +}; + + +class JezzView : public QCanvasView +{ + Q_OBJECT +public: + JezzView(QCanvas* viewing=0, QWidget* parent=0, const char* name=0, WFlags f=0); + +signals: + void buildWall( int x, int y, bool vertical ); + +protected: + void viewportMouseReleaseEvent( QMouseEvent * ); + +private: + bool m_vertical; +}; + + +class JezzGame : public QWidget +{ + Q_OBJECT + +public: + JezzGame( const QPixmap &background, int ballNum, QWidget *parent=0, const char *name=0 ); + ~JezzGame(); + + int percent(); + static void playSound( const QString &name ); + void display( const QString &text, int size=20 ); + void setBackground( const QPixmap &background ); + +signals: + void died(); + void newPercent( int percent ); + +public slots: + void start(); + void stop(); + void setSound( bool sound ); + +protected slots: + void tick(); + void buildWall( int x, int y, bool vertical ); + void wallFinished( Wall *wall, int tile ); + void ballCollision( Ball *ball, int x, int y, int tile ); + +protected: + void makeBlack(); + void fill( int x, int y ); + void fillLeft( int x, int y ); + int m_buf[FIELD_WIDTH][FIELD_HEIGHT]; + + JezzField *m_field; + JezzView *m_view; + + Wall *m_wall1, *m_wall2; + + QPtrList m_balls; + QCanvasPixmapArray *m_ballPixmaps; + QCanvasText *m_text; + + QTimer *m_clock; + bool m_running; + int m_percent; + bool m_pictured; + +#if HAVE_ARTS + static SimpleSoundServer *m_artsServer; +#endif + static QString m_soundPath; + static bool m_sound; +}; + +#endif diff --git a/kbounce/highscores.cpp b/kbounce/highscores.cpp new file mode 100644 index 00000000..fec2d1e6 --- /dev/null +++ b/kbounce/highscores.cpp @@ -0,0 +1,18 @@ +#include "highscores.h" + +#include + +using namespace KExtHighscore; + +ExtManager::ExtManager() +{ + Item *item = new Item((uint)0, i18n("Level"), Qt::AlignRight); + addScoreItem("level", item); +} + +bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + return s1.data("level").toUInt()>s2.data("level").toUInt(); + return Manager::isStrictlyLess(s1, s2); +} diff --git a/kbounce/highscores.h b/kbounce/highscores.h new file mode 100644 index 00000000..59372ed6 --- /dev/null +++ b/kbounce/highscores.h @@ -0,0 +1,17 @@ +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + +#include + +using namespace KExtHighscore; + +class ExtManager : public Manager +{ + public: + ExtManager(); + + private: + bool isStrictlyLess(const Score &s1, const Score &s2) const; +}; + +#endif diff --git a/kbounce/kbounce.cpp b/kbounce/kbounce.cpp new file mode 100644 index 00000000..eab691b8 --- /dev/null +++ b/kbounce/kbounce.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License,Life or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kbounce.h" +#include "game.h" + +KJezzball::KJezzball() + : m_gameWidget( 0 ) +{ + // setup variables + m_game.level = 1; + m_game.score = 0; + m_state = Idle; + + KConfig *config = kapp->config(); + m_backgroundDir = config->readPathEntry( "BackgroundDir" ); + m_showBackground = config->readBoolEntry( "ShowBackground", false ); + + statusBar(); + initXMLUI(); + + m_soundAction -> setChecked((config->readBoolEntry( "PlaySounds", true ))); + + // create widgets + m_view = new QWidget( this, "m_view" ); + setCentralWidget( m_view ); + + m_layout = new QGridLayout( m_view, 1, 3 ); + m_layout->setColStretch( 2, 1 ); + + QVBoxLayout *infoLayout = new QVBoxLayout; + m_layout->addLayout( infoLayout, 0, 1 ); + + QLabel *label = new QLabel( i18n("Level:"), m_view ); + infoLayout->addWidget( label ); + m_levelLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_levelLCD ); + + label = new QLabel( i18n("Score:"), m_view ); + infoLayout->addWidget( label ); + m_scoreLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_scoreLCD ); + + infoLayout->addSpacing( 20 ); + + label = new QLabel( i18n("Filled area:"), m_view ); + infoLayout->addWidget( label ); + m_percentLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_percentLCD ); + + label = new QLabel( i18n("Lives:"), m_view ); + infoLayout->addWidget( label ); + m_lifesLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_lifesLCD ); + + label = new QLabel( i18n("Time:"), m_view ); + infoLayout->addWidget( label ); + m_timeLCD = new QLCDNumber( 5, m_view ); + infoLayout->addWidget( m_timeLCD ); + + // create timers + m_nextLevelTimer = new QTimer( this, "m_nextLevelTimer" ); + connect( m_nextLevelTimer, SIGNAL(timeout()), this, SLOT(switchLevel()) ); + + m_gameOverTimer = new QTimer( this, "m_gameOverTimer" ); + connect( m_gameOverTimer, SIGNAL(timeout()), this, SLOT(gameOverNow()) ); + + m_timer = new QTimer( this, "m_timer" ); + connect( m_timer, SIGNAL(timeout()), this, SLOT(second()) ); + + // create demo game + createLevel( 1 ); + statusBar()->message( i18n("Press %1 to start a game!") + .arg(m_newAction->shortcut().toString()) ); + //m_gameWidget->display( i18n("Press to start a game!") ); + + setFocusPolicy(QWidget::StrongFocus); + setFocus(); + setupGUI(); +} + +KJezzball::~KJezzball() +{ + KConfig *config = kapp->config(); + config->writeEntry( "PlaySounds", m_soundAction->isChecked() ); +} + +/** + * create the action events create the gui. + */ +void KJezzball::initXMLUI() +{ + m_newAction = KStdGameAction::gameNew( this, SLOT(newGame()), actionCollection() ); + // AB: originally KBounce/KJezzball used Space for new game - but Ctrl+N is + // default. We solve this by providing space as an alternative key + KShortcut s = m_newAction->shortcut(); + s.append(KKeySequence(QKeySequence(Key_Space))); + m_newAction->setShortcut(s); + + KStdGameAction::quit(this, SLOT(close()), actionCollection() ); + KStdGameAction::highscores(this, SLOT(showHighscore()), actionCollection() ); + m_pauseButton = KStdGameAction::pause(this, SLOT(pauseGame()), actionCollection()); + KStdGameAction::end(this, SLOT(closeGame()), actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()),actionCollection()); + + new KAction( i18n("&Select Background Folder..."), 0, this, SLOT(selectBackground()), + actionCollection(), "background_select" ); + m_backgroundShowAction = + new KToggleAction( i18n("Show &Backgrounds"), 0, this, SLOT(showBackground()), + actionCollection(), "background_show" ); + m_backgroundShowAction->setCheckedState(i18n("Hide &Backgrounds")); + m_backgroundShowAction->setEnabled( !m_backgroundDir.isEmpty() ); + m_backgroundShowAction->setChecked( m_showBackground ); + + m_soundAction = new KToggleAction( i18n("&Play Sounds"), 0, 0, 0, actionCollection(), "toggle_sound"); +} + +void KJezzball::newGame() +{ + // Check for running game + closeGame(); + if ( m_state==Idle ) + { + // untoggles the pause button in case it was toggled + m_pauseButton->setChecked(false); + + // update displays + m_game.level = 1; + m_game.score = 0; + + m_levelLCD->display( m_game.level ); + m_scoreLCD->display( m_game.score ); + + statusBar()->clear(); + + // start new game + m_state = Running; + + createLevel( m_game.level ); + startLevel(); + } +} + +void KJezzball::closeGame() +{ + if ( m_state!=Idle ) + { + int old_state = m_state; + if (old_state == Running) + pauseGame(); + int ret = KMessageBox::questionYesNo( this, i18n("Do you really want to close the running game?"), QString::null, KStdGuiItem::close(), KStdGuiItem::cancel() ); + if ( ret==KMessageBox::Yes ) + { + stopLevel(); + m_state = Idle; + highscore(); + } + else if (old_state == Running) + { + pauseGame(); // Unpause + } + } +} + +void KJezzball::pauseGame() +{ + switch ( m_state ) + { + case Running: + m_state = Paused; + statusBar()->message(i18n("Game paused.") ); + //m_gameWidget->display( i18n("Game paused. Press P to continue!") ); + stopLevel(); + break; + + case Paused: + case Suspend: + m_state = Running; + statusBar()->clear(); + //m_gameWidget->display( QString::null ); + startLevel(); + break; + + case Idle: + break; + } +} + +void KJezzball::gameOver() +{ + stopLevel(); + m_gameOverTimer->start( 100, TRUE ); +} + + +void KJezzball::gameOverNow() +{ + m_state = Idle; + + QString score; + score.setNum( m_game.score ); + KMessageBox::information( this, i18n("Game Over! Score: %1").arg(score) ); + statusBar()->message( i18n("Game over. Press for a new game") ); + //m_gameWidget->display( i18n("Game over. Press for a new game!") ); + highscore(); +} + +/** + * Bring up the standard kde high score configure dialog. + */ +void KJezzball::configureHighscores() +{ + KExtHighscore::configure(this); +} + +/** + * Bring up the standard kde high score dialog. + */ +void KJezzball::showHighscore() +{ + KExtHighscore::show(this); +} + +/** + * Select a background image. + */ +void KJezzball::selectBackground() +{ + QString path = KFileDialog::getExistingDirectory( m_backgroundDir, this, + i18n("Select Background Image Folder") ); + if ( !path.isEmpty() && path!=m_backgroundDir ) { + m_backgroundDir = path; + + // enable action + m_backgroundShowAction->setEnabled(true); + + // save settings + KConfig *config = kapp->config(); + config->writePathEntry( "BackgroundDir", m_backgroundDir ); + config->sync(); + + // apply background setting + if ( m_showBackground ) { + if ( m_background.width()==0 ) + m_background = getBackgroundPixmap(); + + m_gameWidget->setBackground( m_background ); + } + else{ + KMessageBox::information( this, i18n("You may now turn on background images.")); + } + } +} + +void KJezzball::showBackground() +{ + bool show = m_backgroundShowAction->isChecked(); + if ( show!=m_showBackground ) { + + m_showBackground = show; + + // save setting + KConfig *config = kapp->config(); + config->writeEntry( "ShowBackground", m_showBackground ); + config->sync(); + + // update field + if ( m_showBackground ) { + if ( m_background.width()==0 ) + m_background = getBackgroundPixmap(); + } + + m_gameWidget->setBackground( m_showBackground ? m_background : QPixmap() ); + } +} + +QPixmap KJezzball::getBackgroundPixmap() +{ + // list directory + QDir dir( m_backgroundDir, "*.png *.jpg", QDir::Name|QDir::IgnoreCase, QDir::Files ); + if ( !dir.exists() ) { + kdDebug(12008) << "Directory not found" << endl; + return QPixmap(); + } + + if (dir.count() > 1) + { + // return random pixmap + int num = kapp->random() % dir.count(); + return QPixmap( dir.absFilePath( dir[num] ) ); + } + else if (dir.count()==1) + { + return QPixmap( dir.absFilePath(dir[0]) ); + } + else return QPixmap(); +} + +void KJezzball::focusOutEvent( QFocusEvent *ev ) +{ + if ( m_state==Running ) + { + stopLevel(); + m_state = Suspend; + m_pauseButton->setChecked(true); + statusBar()->message( i18n("Game suspended") ); + // m_gameWidget->display( i18n("Game suspended") ); + } + + KMainWindow::focusOutEvent( ev ); +} + +void KJezzball::focusInEvent ( QFocusEvent *ev ) +{ + if ( m_state==Suspend ) + { + startLevel(); + m_state = Running; + statusBar()->clear(); + m_pauseButton->setChecked(false); + //m_gameWidget->display( QString::null ); + } + + KMainWindow::focusInEvent( ev ); +} + +void KJezzball::second() +{ + m_level.time--; + m_timeLCD->display( m_level.time ); + if ( m_level.time<=0 ) + { + JezzGame::playSound( "timeout.au" ); + gameOver(); + } else + if ( m_level.time<=30 ) + JezzGame::playSound( "seconds.au" ); +} + +void KJezzball::died() +{ + kdDebug(12008) << "died" << endl; + m_level.lifes--; + m_lifesLCD->display( m_level.lifes ); + if ( m_level.lifes==0 ) gameOver(); +} + +void KJezzball::newPercent( int percent ) +{ + m_percentLCD->display( percent ); + if ( percent>=75 ) + { + m_level.score = m_level.lifes*15 + (percent-75)*2*(m_game.level+5); + nextLevel(); + } +} + +void KJezzball::createLevel( int level ) +{ + // destroy old game + if ( m_gameWidget ) delete m_gameWidget; + + // create new game widget + if ( m_showBackground ) + m_background = getBackgroundPixmap(); + else + m_background = QPixmap(); + + m_gameWidget = new JezzGame( m_background, level+1, m_view, "m_gameWidget" ); + m_gameWidget->setSound(m_soundAction->isChecked()); + + m_gameWidget->show(); + m_layout->addWidget( m_gameWidget, 0, 0 ); + connect( m_gameWidget, SIGNAL(died()), this, SLOT(died()) ); + connect( m_gameWidget, SIGNAL(newPercent(int)), this, SLOT(newPercent(int)) ); + connect( m_soundAction, SIGNAL(toggled(bool)), m_gameWidget, SLOT(setSound(bool)) ); + + // update displays + m_level.lifes = level+1; + m_lifesLCD->display( m_level.lifes ); + m_percentLCD->display( 0 ); + + m_level.time = (level+2)*30; + m_timeLCD->display( m_level.time ); + + m_level.score = 0; +} + +void KJezzball::startLevel() +{ + if ( m_gameWidget ) + { + m_timer->start( 1000 ); + m_gameWidget->start(); + } +} + +void KJezzball::stopLevel() +{ + if ( m_gameWidget ) + { + m_gameWidget->stop(); + m_timer->stop(); + } +} + +void KJezzball::nextLevel() +{ + stopLevel(); + m_nextLevelTimer->start( 100, TRUE ); +} + +void KJezzball::switchLevel() +{ + m_game.score += m_level.score; + + // make sure the LCD provides enough digits for the score + // (fixes #96841) + int numDigits=0; + int temp_score = m_game.score; + for ( ; temp_score > 0; ++numDigits ) temp_score /= 10; + if ( numDigits < 5 ) numDigits = 5; // set numDigits to at least 5, otherwise it does not look well + + m_scoreLCD->setNumDigits( numDigits ); + m_scoreLCD->display( m_game.score ); + + QString score; + score.setNum( m_level.score ); + + QString level; + level.setNum( m_game.level ); + +QString foo = QString( +i18n("You have successfully cleared more than 75% of the board.\n") + +i18n("%1 points: 15 points per remaining life\n").arg(m_level.lifes*15) + +i18n("%1 points: Bonus\n").arg((m_gameWidget->percent()-75)*2*(m_game.level+5)) + +i18n("%1 points: Total score for this level\n").arg(score) + +i18n("On to level %1. Remember you get %2 lives this time!")).arg(m_game.level+1).arg(m_game.level+2); + + KMessageBox::information( this,foo ); + + + // KMessageBox::information( this, i18n("You've completed level %1 with " + // "a score of %2.\nGet ready for the next one!").arg(level).arg(score)); + + m_game.level++; + m_levelLCD->display( m_game.level ); + + createLevel( m_game.level ); + startLevel(); +} + + +void KJezzball::highscore() +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(m_game.score); + score.setData("level", m_game.level); + KExtHighscore::submitScore(score, this); +} + +#include "kbounce.moc" diff --git a/kbounce/kbounce.desktop b/kbounce/kbounce.desktop new file mode 100644 index 00000000..0f629644 --- /dev/null +++ b/kbounce/kbounce.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=KBounce +Name[af]=K-hop +Name[ar]=لعبة الإرتداد (KBounce) +Name[be]=ÐеÑÑƒÑ†Ñ–ÑˆÐ½Ñ‹Ñ ÑˆÐ°Ñ€Ñ‹ÐºÑ– +Name[bn]=কে-বাউনà§à¦¸ +Name[hi]=के-बाउंस +Name[ne]=केडीई उफà¥à¤°à¤¿à¤¨à¥‡ +Name[pa]=ਕੇ-ਬਾਊਂਸ +Name[sv]=Kbounce +Name[ta]=கேபவà¯à®©à¯à®¸à¯ +Name[tg]=KТӯби ҷаҳанда +Name[tr]=Zıplayan toplar +Name[zh_TW]=KBounce å½ˆåŠ›çƒ + +Type=Application +Exec=kbounce %i %m -caption "%c" +DocPath=kbounce/index.html +Icon=kbounce + +GenericName=Bounce Ball Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž шарыкі +GenericName[bg]=ПодÑкачащи топки +GenericName[bn]=বাউনà§à¦¸ বল খেলা +GenericName[bs]=Igra odbijajuće loptice +GenericName[ca]=Joc de boles rebotadores +GenericName[cs]=Hra s poskakujícími míÄky +GenericName[cy]=Gêm Bêl Sboncio +GenericName[da]=Spil med springende bolde +GenericName[de]=Ballspiel +GenericName[el]=Παιχνίδι αναπηδοÏμενης μπάλας +GenericName[eo]=Saltpilka ludo +GenericName[es]=Juego de pelotas que rebotan +GenericName[et]=Põrkava palli mäng +GenericName[eu]=Pilota errebotatzeko jokoa +GenericName[fa]=بازی Bounce Ball +GenericName[fi]=Lautapeli +GenericName[fr]=Jeu de balles rebondissantes +GenericName[he]=משחק ×›×“×•×¨×™× ×§×•×¤×¦×™× +GenericName[hr]=Igra s poskakujućom loptom +GenericName[hu]=Labdás +GenericName[is]=Skoppandi boltaleikur +GenericName[it]=Gioco del BounceBall +GenericName[ja]=è·³ã­ã‚‹ãƒœãƒ¼ãƒ«ã®ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​បាល់​លោហ+GenericName[lt]=Bounce Ball žaidimas +GenericName[lv]=LÄ“kÄjoÅ¡o bumbu spÄ“le +GenericName[mk]=Игра Ñо топки што Ñкокаат +GenericName[nb]=Sprettballspill +GenericName[nds]=Ballspeel +GenericName[ne]=बल हिरà¥à¤•à¤¾à¤‰à¤¨à¥‡ खेल +GenericName[nl]=Bal-stuiter spel +GenericName[nn]=Sprettballspel +GenericName[pl]=PiÅ‚ki +GenericName[pt]=Jogo de Bola Saltitante +GenericName[pt_BR]=Jogo de bolas que pulam +GenericName[ru]=Ðеуёмные шарики +GenericName[sk]=Bounce loptová hra +GenericName[sl]=Igra odbijajoÄe žoge +GenericName[sr]=Игра одбијања лопте +GenericName[sr@Latn]=Igra odbijanja lopte +GenericName[sv]=Studsande boll spel +GenericName[ta]=திரà¯à®®à¯à®ªà¯à®®à¯ பநà¯à®¤à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Кульки-Ñтрибунці +GenericName[zh_TW]=彈跳çƒéŠæˆ² +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kbounce/kbounce.h b/kbounce/kbounce.h new file mode 100644 index 00000000..a4e0a95f --- /dev/null +++ b/kbounce/kbounce.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef KBOUNCE_H +#define KBOUNCE_H + +#include + +class JezzGame; +class QLCDNumber; +class QGridLayout; +class KToggleAction; +class KAction; + +class KJezzball : public KMainWindow +{ + Q_OBJECT + +public: + KJezzball(); + ~KJezzball(); + +public slots: + void newGame(); + void pauseGame(); + void closeGame(); + void showHighscore(); + void selectBackground(); + void showBackground(); + +protected slots: + void died(); + void newPercent( int percent ); + void second(); + void switchLevel(); + void gameOverNow(); + void configureHighscores(); + +protected: + void createLevel( int level ); + void startLevel(); + void stopLevel(); + void nextLevel(); + void highscore(); + void gameOver(); + void initXMLUI(); + + void focusOutEvent( QFocusEvent * ); + void focusInEvent ( QFocusEvent * ); + + QPixmap getBackgroundPixmap(); + + JezzGame *m_gameWidget; + QWidget *m_view; + QGridLayout *m_layout; + QLCDNumber *m_levelLCD; + QLCDNumber *m_lifesLCD; + QLCDNumber *m_scoreLCD; + QLCDNumber *m_percentLCD; + QLCDNumber *m_timeLCD; + KToggleAction *m_pauseButton, *m_backgroundShowAction, *m_soundAction; + KAction *m_newAction; + + QTimer *m_timer; + QTimer *m_nextLevelTimer; + QTimer *m_gameOverTimer; + + QString m_backgroundDir; + bool m_showBackground; + QPixmap m_background; + + enum { Idle, Running, Paused, Suspend } m_state; + + struct + { + int lifes; + int time; + int score; + } m_level; + + struct + { + int level; + int score; + } m_game; +}; + +#endif // KBOUNCE_h + diff --git a/kbounce/kbounceui.rc b/kbounce/kbounceui.rc new file mode 100644 index 00000000..058ab702 --- /dev/null +++ b/kbounce/kbounceui.rc @@ -0,0 +1,17 @@ + + + + + + + + + + + +Main Toolbar + + + + + diff --git a/kbounce/main.cpp b/kbounce/main.cpp new file mode 100644 index 00000000..390b7c62 --- /dev/null +++ b/kbounce/main.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000 Stefan Schimanski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#if HAVE_ARTS +#include +#endif + +#include "kbounce.h" + +#include +#include + +using namespace std; + +#if HAVE_ARTS +using namespace Arts; +#endif + +static const char description[] = I18N_NOOP("KDE Bounce Ball Game"); +static const char version[] = "0.5"; + +int main(int argc, char **argv) +{ + KHighscore::init("kbounce"); + + KAboutData aboutData( "kbounce", I18N_NOOP("KBounce"), + version, description, KAboutData::License_GPL, + "(c) 2000, Stefan Schimanski"); + + aboutData.addAuthor("Stefan Schimanski", I18N_NOOP("Original author"), "schimmi@kde.org"); + aboutData.addAuthor("Sandro Sigala", I18N_NOOP("Highscore"), "ssigala@globalnet.it"); + aboutData.addAuthor("Benjamin Meyer", I18N_NOOP("Contributions"), "ben+kbounce@meyerhome.net"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + ExtManager manager; + + // setup MCOP +#if HAVE_ARTS + Dispatcher dispatcher; +#endif + + if (a.isRestored()) + RESTORE(KJezzball) + else { + KJezzball *w = new KJezzball; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} + diff --git a/kbounce/pics/Makefile.am b/kbounce/pics/Makefile.am new file mode 100644 index 00000000..e7ad910e --- /dev/null +++ b/kbounce/pics/Makefile.am @@ -0,0 +1,34 @@ +pics_DATA = \ + ball0000.png \ + ball0001.png \ + ball0002.png \ + ball0003.png \ + ball0004.png \ + ball0005.png \ + ball0006.png \ + ball0007.png \ + ball0008.png \ + ball0009.png \ + ball0010.png \ + ball0011.png \ + ball0012.png \ + ball0013.png \ + ball0014.png \ + ball0015.png \ + ball0016.png \ + ball0017.png \ + ball0018.png \ + ball0019.png \ + ball0020.png \ + ball0021.png \ + ball0022.png \ + ball0023.png \ + ball0024.png \ + tiles.png + +picsdir = $(kde_datadir)/kbounce/pics + +KDE_ICON = kbounce + +EXTRA_DIST = $(pics_DATA) + diff --git a/kbounce/pics/ball0000.png b/kbounce/pics/ball0000.png new file mode 100644 index 0000000000000000000000000000000000000000..3afe8f0e3936a9a93f64d718ce3d04f4f0e77082 GIT binary patch literal 641 zcmV-{0)G98P);rE*vXLd);MHVzxVbwAtF=E3Y6!MS+ zWw~`JAq))k2T0H*I`;?a5Qtuu4qg_r4teVu85kWjbHyxmo!xcTo%v>G=j&we`MJG9 zDFwi4KHm{-Y`k<=-akB9`_`uHIKjY$z|KDS+0xxTdv{{uGk{P^v6|0!l-|5vk3M`% zCUhNB(+HHp>FnU});P6$*~a;>F?WA*@~u!xu{<^PtC7p4yM&a;8trXPxfjn3k#3;bGdW^NCKTvM5hoW4TI$6A=MYjCC}xv|Y3MX*8v8QHXzq5QF93W%Dupx*B;bvW;f;?Igdt%VQja1^ zfowHMB#Ko?9|H$MfUaR>uHao8rg(CK<9k%4k1r*^#>L==xJl-&wW#bvbZ%=vH>?mK5_VV!9IFF{zR1<|^IW)68vOV0{ZlNZJGv=b(qDDB^8#Mv`m`t-T%wX2uj0I0gWF#dKS`SO+9YFXGq z5R}Rkv=+Ia^NrsTuU|;ra^v!qC&C!RST?)SG%}JpEG0=Pi8Uy8fz4XTa@=L5ua~vK z>rt;A>rn5;$8Va^(NqFxHim=75DOzpLI6-yib6b&Jdm&!x4aK3Kld$bhaooL0FE&@ zTB89+DQqFI+uLc7lJy`sqWpZmISy2TU1Qh6L$!~l3zE4`&&#m_p6MyD*ytd!7pL?re zi0`_1i3Ek7<7^IGz^hi+@^&~Xn0<8n&Y-BDe*ED6?GFQ^dJ%k$@RZU6uP07*qo IM6N<$fwVAHWk$$ z5VS}La{{SVi;~>5aP1}t30$-=2$O-9QXnH#)Fx1A5t&6FE*hN~KbSjry!ZXJP&=Dv zb5_q$A_BlnKA$dq{4iRXp1u~&%{N31DI3J&k(0@MX}ogv?)ARDw*X2+n91kUzsH`; z)lKDE(^?ZVh8j?eA__qH{JH4R;NY#BH~JDGlI-B1Xdo@rMApimqw6*wvFG39nwPq`sB#6SaLn>9+ zgd^*~U$*f?@PQSeXbhW)1l405gnr;UG^Br(;BUXD|CAYyf4T=*Cz{ zBq(=vQoL}H?)v1bp=|cP+AaNfcxZI~#pJzev1GLCVh`-6mN`k{L{F`wJ~c5skiGrC z^e*FL<6Wzj@|~?}a5&i0luUZwr)F&)4fOYycb$Lflg#IkqjpFE3y2d{{fwwk=wr-j+Huk_(NFr7ulRz5_r+$Sy8Mo?I9!z5vPU zy1X?E$(p8QbX^`OCDZX(XYTRCl!#yeY-FCl4`f%uF{KD;8a`de0Ro-}O5yz8(0)9f zzdvvHywXp{;}bhGGuNX)R4F1ruTq!*o`)ks4JZ~0rVI`yjopuH*DSySY!PhF!v;JN z>~3xWrrV;N&nJz&&CTN)&;%U75kc#8XedQP1Q%!nEx;+4{D$ZESL(nH@R$FxfrbcM zKpEHtnx=^x3@Sqphrd?ftgj2#yA#|lTm(u$&ELb3L?Wm4jU;c$sR1Vs`~GHb zdwbQ&iBHcmsZya}Xn+T}0YB#XbDiPw@z=)!!HWQD{~7>~W@r10fATlljuUg8<^f;g k1?qDma-yP1}D!1jWH3xf~5r8?8y`U6RW^hg{zB-thT; zhR0Wl2mqhf*W(4-o-XG;-i&2)DL2*Q*AB+L*4go`p>q?H*D{$k07Qhfd_Mko@{aW$ zB&%tX*LBH;Lh?@2WD#U`V4$`1>g81t!2tNOxsgBi^jZ3-QY1Bvs9{hCVj>_y-L|zK zE3cQ{n9-bma(MVc>)y;vf77EM=v9gY5K^iQi%cM5upKiKoh7J?UMHyNqO@lpFo8w@{|3rH6-bPYf7W_>`=0z8JJTou1>h&J z73?BV3Ih9w4{>B@@P+Fs+QdUKZF zf!}kU=_nBa;9VvYTmAUq&fkTFQ+_tPUE162dPj$EhKIj)9zAyH^w`*Y07Qhh>2&Pz z$jRjwAd6Z{+89|3h2*)`GOd){>gw{J+@CokA{c+Cl~oh*%3E zc!h$qG&}q7X-6#M4D|IKU%P%Im9&;!KtgLeQG@`j#RF<0lnTX=?1&GVO7`<*$M*>X zu2Q(x;sO?MMF@**b&f-IIls@;bGd^WSO+{2JV0AZ189iQ1bo2T+{BhkQA0FZs>9wY z@SCl1MQ8vkz#pI<2z{^V8WoTKScM~P0sIdL1I+*~0cD^DbPo-E(K`oou#+?zR6&M!@DR4RMCjb> h^xnQWKK`r4{0Gn7*2OcE`o{nO002ovPDHLkV1g&Q{4)Rm literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0006.png b/kbounce/pics/ball0006.png new file mode 100644 index 0000000000000000000000000000000000000000..c3f024ae2d997dbb26b5a4ad310a886966a574eb GIT binary patch literal 589 zcmV-T0LZ zrdF*Gs6!VOK@dtt9b8JNi=dsfi)}Zf6bDQH0WAu)))q;$Nvk$#ZgX#PkApYhS0jRQa32noxg(-;~86~D=9tHpO! zw$Aj`dvD)4WKMQ>U;Fd$(aoePNf8`iuLx0qAfTa26(|==QSI8FwrU?g+=vAMF~C;E z4gzdd0uk)cj{#9&V__j}ZIwzr5nvl=01Z_lfExt3fCn@IA8>#C+-VVymut|w39RvN zY*k#K0&D=aFmT)R#F9jO`4^n+4B>iM!3*Isunts#1F6$rBD;^B$OT6Zdqv`&Cj26n@y^~?=ZdcpFVXpGkbq5Ka8rPsyx1T_enOH z^an(A&=?&sMlVMqnsJ=w==H(ZZBE*+0q``RKf3n$(}PyM?)aY9(XlJOlIS}=dMi7! b(q{ew0zK7>N|*WM00000NkvXXu0mjfZwvXb literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0007.png b/kbounce/pics/ball0007.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d4f803b00dc750f89a429f218ed71ea6f7017b GIT binary patch literal 591 zcmV-V0EKi|8{9f?t6DxMLChy)@+LRUwB{0HI? zy0reFmEs_xF116^PDQX-#3Efp5TxC!Eg3|eS`_S*SPe8_(j<*nF1_S(uR|{{ygbkF zc^{tPRU!hwda;;j|M>oBe|2@N_x0N;IXM)#@pvzi&6bl_uid*bI=Tj+M1=KXF|o7! zVbhv_nNH|Brc!j3q8S9#`+b_W9h{$jQW%-I``!T9S^M`hHQPPr~;l)_eu6PiW@Km<<&M}%6nYH6qKtkK^3IjOf= zn1Cg6xV}#SSV4d(f(b-``o_j3qf@D*1vmgaz!O0ep#yY8=mI{_1H5MQh@qmgciU77*odT)+gnAzTLH!ra3L zFUD-!FDNA=x-JF7kn5Tzg;c6HGk)jYfK!9l09agD%r#0|6Q1j4`krT4v3O}{I6E_b dYx2*4`46D)(u97h3h)=s^G+Uh87VnI5JMG!%xppIH3P$vrxQmIlK#L~1iF-g6c%U$m7>tF)D<-Os( z#~Yp!5dhv5i;4Q?=KVjbpU(TQULTN_6Sm{~d}DObVMmik~-?@Ep{QBf`17P>_>f63sGeem$0X3ik`~qwslg+Ma10%;~<-~Ef z415Plz_%{{0LtBfW11Y!W}oOU78mz6noVzgdHIYJ1lT~M+g|-o=Pwst-h41;AtHzf zb2B#{UP-0=F{NZ&*JaEw-$o$1|Ca iiNW-}iK(gGp64$=Tj7EVg_|P)0000;#y literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0009.png b/kbounce/pics/ball0009.png new file mode 100644 index 0000000000000000000000000000000000000000..aedbd608036964eb4ad765a1045f0b2ec16985b1 GIT binary patch literal 592 zcmV-W0*`O4 zNHa4KCZTO11nNIvAq})}HbH9*+SD}CqLN%26f|ZAIoj1pn~&T5nESTy4)ku$Sv=?P zE{+lr07~U@rjE=wmC6l&zL;n?o49>RU(e+#$tzd$*T=@*11J%pR4!+#(*6{G^f=Y~ zri`f+9#9j(4nu0`ba-NVIzKuwQ8WO4wu6s{UM!@Jy)Kc|b>fDBtDxKS0dTjswe{ls z-19`@rGEP2g=CPU;&m0 zRuB+I-ULh_2Gmwp2aRTR>$tY`mcM`txM7H<6b|5s-~j>P1CDKX8|?4d4PEd5({A&N zgR?|%fE}O)G~l4n50s(0;_JUn^HSV#0BA)ocoA#^yFeW{I&ki**3*-iY4_*6P2fB5 z4X8x=1K5lLj%ATPJMc_jSX|s)a~iBIuUvLKk29p2hJKpBrdwNF+>JO{TGZw z!vekp3^Bw-r#cvA#FxZDER!)YA@~Ovu|@|LA`XfskcPz8wzNQd;Praf`#N|$(=&XY zC(rOG5dmPeSWLDgym#1VO!=+Wu&lfbJMC7W+xAv!?Bbnklan6+l!&leEGB2jIWlc&VX6+B&MKm>X{ zylPD=EiJuzok$k-?D+VVgA?)lX~*Hz+#L3Hi6Kp+4?qM@1P7>B{+QZH`<&6z)U2*_ z%$JMAHh&S*G-5$O2*kn=Qv?&}1M2JRmkc)u&PcUN=i?_lzzah(z*P!Ygf0*OKHxSQ z1BQ~Y=4@=xuGOgXUopTHp#kgxhd>8VmK7MP>zDR_ZW52+zkmsJBX}2R0uC^m&3-ld z{a~RdOjW>ifkD8CX0(C*DA4KeCzZ)O*H@O8o7R?P@sEy1 p{UER`Grp4=JD;DvF}rij`~#$h;*_w>wb=jw002ovPDHLkV1i%%1q}cI literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0011.png b/kbounce/pics/ball0011.png new file mode 100644 index 0000000000000000000000000000000000000000..4db6a483629834c1d6848b5e92050b1615457e7c GIT binary patch literal 613 zcmV-r0-F7aP)oG!~;RA)s{-ZCYH4ySq!Z zL#Yxw)hcz;$!4gNs34YtMZut=C=SjJTJdF31g#Do8fjiEHcevQdb#EGPktFF`;R~c5z^*kq+_4LnQ*7}EIvlH?72LRQ zVE4zWQJlMM6oDGh0NiTLr)V}arF!*5cewWqo3NeB+Ri*h+7ZbzM@xI*e(=H}-gAR>qew{PCKJ3TPqPHLJ=8HP+MC8u>=;)jQv>lbIAcAVCE4S@T1 zmPQKc^$X2%`JiL(1Ok!B*YMF}iRpO!Z^!%ts0QJ?v6fU700000NkvXXu0mjfOnMWU literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0012.png b/kbounce/pics/ball0012.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2f758f7d84dc7914f74f8aa4ebe70d9bb96526 GIT binary patch literal 623 zcmV-#0+9WQP)P}~0qjXcK~#90HIhw86kz~B-^@3EtFHUgj*^HJLEwgXFzOt2?354{ zS_Fa~Itv|A=~PLFP$)wPE9z!JAXElM9y)X=l8vY}B!Ad;-Cf;vzn$6nm@ayFxA$&O z3&RkAcZ-Yd6(J_1WnO9s5mQPb>(VzpKbw^G;fwwK%K-X;yyaLcDiI%PbKUq+*VVdQ zS91@g>_%=Y{d4Tr($goiLjVTAy3jwjM5C$BSb}6rGZA6nsel$ugQbsnnIGcM!cEHbxo78efZ@wm?sZ%CBl-1Puu(3vE(9AwkkfVQZEdP&aNup% znX~5sw1YJOW~Zl*=U20%L3yXM;d_n~jjtv9y2nO_Zmb?fW$5?gb;KgHu@LnEN%|q zWJm;KjESq0%tm6Os0_wPhy;R*;tzgUS7E}Cpn`-b1Pd+DqrKkUd-t61JAb#2WsCvf z)7)IHDpGf6r(xj4><}3eDRomYk++N3-YQ2tlMZ z$N273L7VkaS8=&ptQVvUIxZ=8m*}-O{f*2R-`P8Kt%*PCi?OjwYWqKgvH_sX=(M> z59(Bc8pWy$fGZ?{R_y)xP5IRrM?f9GwzafkwXvW0q#YX!)WQ%)rihfL{P+pGUl%C> zKG2@eFG??pA0{fgzHKZgh$5OA8jw<=-o2&r;wAe)2?#xpmhSFhF+DX^T3=aV-LG5> zq6j|>IhdWLJUPW)smuZ3yDsMj2cF)&KlH#lmL47-d-Sd1->h%^5~=qeNLIfSX^oT1 zsk2uuPu?4O*85-jxY5zk?$XZAU|1}6>LXveUS{oN{>;!o|EbU>_ N002ovPDHLkV1hZI76bqQ literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0014.png b/kbounce/pics/ball0014.png new file mode 100644 index 0000000000000000000000000000000000000000..851cd6cb6e7267ccc0b97b6b8389093c5a650ae6 GIT binary patch literal 635 zcmV->0)+jEP)Q*j(eKllE=_gRxR5s57UL8XJGh+zK&(Zy1% zI{Mg(Aasy|gV4pHi-UtuXbVxq3L?725OvVa#VXf|PpC;Grp@c-zJI^_b1|OzEQg~= zDFFbLYPH}JyX_HO5JZ_Y6*7z>&cf(ZKH)bm&d$CB07XiPO0`<}ZD{YUO!nZIZ9``a zu@GqcK3e@Ay1kw_l<=#U&doniq?9PVe&tyC+_B*LumOpInx7|9@Kn$R=`KqBHCZWN>S@q?!R_&H53Qx}-Rpc8|^ zG#ErMG!3buz+f75M$m~ta|dWU&T;b7^r$KXLM{*qj!1B*ih_X02t8QjnrF)qG#JolufYk&m&D1uHZVlL43 z1GM@*>^*r3_uF?A&(D9M15j?(f@rQGJ5~_K$eJdI2wVvC!w{X#N7yS@5ZE^MPfc$q zQcA2o-duX;4OVQ+f@v6FstS=x_;269fA$>RonJU{b2-M5VfeF z2L&&RqT*c;Tfcycr=k}l6!9kNNs8@3P^cb6q4rcPHiaZjve{&2XZJT5FUIHkK9`q= zIp+WXA6HjPU5ES1=T}L{3K1d8(#%Vejk3vZU7VZy1OUuAhY!ok)g51a3k${lrF_AXXiA}NhLi|IglsrOvb{YnK-BE( zWV)vn8iN1;5O4wg??XG$=$izIQlQOECqT5-8N??;UjhzL#=sXIQnF|(jb<;xZrn$_ zwuY%nWnC09TCj#L)E)D6nxc@)K?nh|7QG}vCyufEYaROY7gSE2t}*8v7GEtr{HpbX zd>A4Z1mKE+X(asRdfJkJA$)*+1}$nQVw z^uFr5g~w0M{VzS@d960nvUE3ftZI!8WvAWPTP@$adF{^ck@FV{MGFZl-C&vk0000< KMNUMnLSTYJ=L6mV literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0016.png b/kbounce/pics/ball0016.png new file mode 100644 index 0000000000000000000000000000000000000000..cd762eb9ba74d8257d560ee50276228c96677a8d GIT binary patch literal 608 zcmV-m0-ybfP)Ug&Ocj1R{u) zMzN9z2CIMwVv)v1Y%FYIBIut;wh%-R1xqUpA|bok*WLS=duQ(0B%by=&ByVaa{z32 zI?aJ|>sjF6E~RW(W2#K&CMe6FEnPmmwYc~Rz;n*A-RU&?a`r zp;TmtANqO5MCasM!)Sz}EO!S8Bap@rZ+XLY$_Yt@y5v;jQgX zlBK`S3$vnNHi}S6VXS4G=M3VQgV7NE`xkTbr(S!`Io^Ey@bv2>eo?R2iNX*m1h#Ud zIw#E%lD&N{*J|(9R-fGUj!M6JzxDD*96!!+QV6K5qcA1Yp44sFT3dPa;D6~O-fnEP ud)BUMYcG_pil&A5c3k<-?k_J7kDPy8rv|vlH;6- z;nC4T4EtOPv0X9NB_haDYNqt}er5J!m)& zE?m8GCv5~fE8QMex*aSx+vo)WgkS(HlrhMZMwI8mT6>AlT=$;GI6ThaGXbA-_(Gs| z9QaIN&V=h?TFMPRBU)!fC=Ef8fg%IfaUhL`qzc*?7-LY?HB_nE+*W!tljT|}3;fA_ z0D#Xp;c#Z?Yn*47Lr(w}D3pT7U1&p?6cSOIV4Tg66$MUN`;9Nb zd2~|9tzS0wrPk;K0XXL{L`cgLah746Od&@RmRg;GW39!Dw{P}8<+G=aFht<{V6F?Q zEechlkU6GtiVY#&KY8%*8V7*Ao420juMf_C$+F#Xl5p1rYO>I#g3yL8wwlA!et7-= z)APL?49-Vn_KoALRz!hU*T?-Pdwl=)?(y9D3+br~@1G8F0RR9107*qoM6N<$f@X^j ATmS$7 literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0018.png b/kbounce/pics/ball0018.png new file mode 100644 index 0000000000000000000000000000000000000000..e500118e8b812350ffda6c2a2cdd4adc28bac0d3 GIT binary patch literal 609 zcmV-n0-pVeP)DuqA6B87s5 z;|=o*R?Um3SE5fRrb;4rKL{*!h6p~zu)~Guo<^I$GbB#L{eg$W0>b` zPsR*Kqk7)Do7XR1S{L4X)<17-#=W_Pqn$Z=%~={!Q98>`mhp46$FH4j^6_MPqA;iA z*~P^R<5uIz;dYlJoi+z&Tg)X1VJHDlX)S4K7-U%#z-g5RVp$4Fq$IHniIpM_Luw(2 zWk@6yk&=W#z`*3^m3H=|caFk3N@ppZB@hB_4cb*0Yq8c+)iv5yO@(uVsVlo&m?a7$kbV{1%9Fy{^nla{u zv*y|#FCuLW?Ib}dg>{ZeQLvY$?2bp2MZtmD)@$Lt=go)rD_?Z}ywPZo#4$1maJ45d z3v!*2O*IRx*4oosw{M93(=Xqxz514B4-32u0;rwG*ov_B_Dr++aplhPmH(&rdAquL vZr8a7m9OWks!Eb5+Umvr+5M|`xAvXCemV=K(}Tg#00000NkvXXu0mjfbCD6s literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0019.png b/kbounce/pics/ball0019.png new file mode 100644 index 0000000000000000000000000000000000000000..4695261422f9ad85102da68044cc6da7d602a0b5 GIT binary patch literal 610 zcmV-o0-gPdP)^*yn1c^{g<-!eD9RFN%2-L~zYX*3T-t~sfG$fKD#AL?o;D07V|KHBGObxs>Z>|} zxcej5Yrld>jJ0%{O_WkN@0k<@!z^QWJi=(re7n;VAp{=3d~xT!(GRB5lxC73qX=II zbXlNH&S;Wzw%vYxfBpIeaaj7{^QVtKXZfvyAfpIsAF!?>twVLH)p~VredEgi(uX|h w^-k@0zgY>6m(I0XLVTPFY~9${_;cv|1A@c`zHtr(djJ3c07*qoM6N<$f`*X?RR910 literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0020.png b/kbounce/pics/ball0020.png new file mode 100644 index 0000000000000000000000000000000000000000..70d1d658425ffe7938df69068ef59caca520e90b GIT binary patch literal 604 zcmV-i0;BzjP)FDu0oh4JK~#90J&`+W6j1<1&&=FAvybeqyM@7!A{wkjK~(T3qzfpB zAQ}U)u@WprF@>FtonRrs1b>5J(Z<#$ooNJ-6xn36yYsj+ckaW+^|aqZkYC*S`Y<-h1|k!^NXS9<(~$^XFPE;wVB}%eW{Q z&8Cbe6L-TI9+IMF7^5>^|~y!JMKPhWejPh8RfYQ;i|~I+lV7UEFzLfk_bT}B`yd^ zBFTJ{VaTyAZi(EOmC#yBW2vm6vW74SP)4I{jWz~j47GD8Q!fat8)d3oovDK3JOLny z1nS?1ajD3vlCrAMx<*Lr4$t!Rb~BdXp{Oci5u%M{Tqs7fDW_>hS`;kH_@`)j|Hh@+ z{Uaa5N^81}20{powam(rvoz&&Izbf$gHCrh@ZR(O^WOFkrCv3gO&Uo;6oy#mDXNk} z1@Uvi869oDVxY*G`;$ qt{nsGZBxSW1sT8I+SoXpJAVPG;0NxiQ0{vG0000Ki literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0021.png b/kbounce/pics/ball0021.png new file mode 100644 index 0000000000000000000000000000000000000000..f219ffcf639ca921c27e0aefc12b97e519e3a5f8 GIT binary patch literal 609 zcmV-n0-pVeP)XieY7q)SX)U8X zr=P@3;@GZK=Jw4iSKoThId(pN*bh6WFP@xRU~#5RQ%JNm46=+LlQBOBebjg|Jyn{H zSX*7aGz`SEnKe{6R$ecY`uSaZ{ybg(?`77 v-aa>|?Gt6|6>v20dW+6If3UIf=g9dBpHU9%Ph1H?00000NkvXXu0mjfNHGa5 literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0022.png b/kbounce/pics/ball0022.png new file mode 100644 index 0000000000000000000000000000000000000000..bb4bb0a8721bf8fb08f947c6ba745411efb5bd63 GIT binary patch literal 629 zcmV-*0*d{KP)cmd&EpYk zEdXGr-L9v-{{2i0E|qh0Vd?r1QWQkwUr!Wz_44xadjKHTTI{sj^@BIBKUU71tsP%l zf=4NoG3arQP7))EV>40c=CzfJTf|z6=eKX|Rdg}iSi6I<>M({VgESh4S%&?nkDrGf zWO2ez=W3dspPabTfAw;8d~FTmr|PH=hZqe4xGn`?A+<&-6*^hwT5IQ7w7c8%78c;u zYVfHGpE3BZi;@ud)P+YGJVxO<4kDw^F&;%T&h#X5Z6J+?G#U;e5L!W)0!nLWtx=RE zq*5U(M@Rd7Y8r{)_>=hn0G~35e+|7{U?6kkQbH<484asn`rp4@7^>F+3vemnF$YQ; z^teDLiP235NI6Ht_4e4wiK)B$zkjV9bbG!~3L`-P#u&6QNb(&0G(|U#AUMZZII>Nw zwRrO4+4={m9#*PV1ilaII54FJmpQn|5G5JrkA-hHnyp20bo$Zu*5glu^o9f+#{o)X zp>zSyLeEr&-`#6nU;Kaih^L#IvmK+?H6e|{m>>Z8JT`;}H?OvS965gh{x%7@vKoob P00000NkvXXu0mjf4Tc8S literal 0 HcmV?d00001 diff --git a/kbounce/pics/ball0023.png b/kbounce/pics/ball0023.png new file mode 100644 index 0000000000000000000000000000000000000000..80f8d7927550a3d864d00f093a1822ca13b89afb GIT binary patch literal 638 zcmV-^0)hRBP)x_9vw5rO@H zE@h->N>E@#-J(k!qDyB_q6T$HrCW!P=np6%O2|V|Dek(vGqW?Z-|Y9zbjjZNT^`R! zk_3SF8yk7&+t$O@hm9${yK_WMTt^m59ijDBkL=vOIzIjpz(|sWckAo@JIhO-va4%H z^XJZzn!iU>Dp3yts;)z$+0@6Bp1g7S%94>J35%0AzhqZlo+y^f6kfj~TPz{9qUQVj zY&Q5+tKvIuP;N)V*2&?ai{6u`cLoXt24|<~KYxLqOa|MwfCQ-&o{XsazHP#&@HRK+ ztRx{dS|L3%OIirhwoPmpq%E72B}fSi+cepWf-@rA-5oM=Iog%uNI+_ZX&8hmBGers zrBF)IiDN?5$qA#ccc;}`sJJ5|?D;+bX-g3PYpAzFTCz=BO0&Game + + + &Move + + + + + &Edit + + + + + + &Settings + + + + + + + + + &Settings + + + + + + + + + + + + + + Main Toolbar + + + + + + + + + + + diff --git a/kenolaba/toolbar/Makefile.am b/kenolaba/toolbar/Makefile.am new file mode 100644 index 00000000..5fdccbb7 --- /dev/null +++ b/kenolaba/toolbar/Makefile.am @@ -0,0 +1,7 @@ +tb_DATA = new.xpm stop.xpm edit.xpm hint.xpm undo.xpm \ + redball.xpm yellowball.xpm noball.xpm warning.xpm ok.xpm \ + spy0.xpm spy1.xpm spy2.xpm spy3.xpm network.xpm +tbdir = $(kde_datadir)/kenolaba/pics + +EXTRA_DIST = $(tb_DATA) + diff --git a/kenolaba/toolbar/edit.xpm b/kenolaba/toolbar/edit.xpm new file mode 100644 index 00000000..20412f2e --- /dev/null +++ b/kenolaba/toolbar/edit.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*ab[]={ +"22 22 13 1", +"j c #ffa858", +"d c #c0c0c0", +"c c #d6cdbb", +"g c #ffc0c0", +"h c #c05800", +"# c #000000", +"k c #ffdca8", +". c None", +"e c #c00000", +"b c #a0a0a4", +"a c #dcdcdc", +"i c #ffffff", +"f c #ff0000", +"......................", +"................#aa...", +"...............#aa#b..", +".............cc#aa#bc.", +"......#########aa#bb..", +".....#dddddddd#aa#bc..", +".....#dededed#aa#bb...", +"....#ddfgfgfg#aa#bcc..", +"....#ddddddd####bb.cc.", +"...#dddddde#aa##bc....", +"...#dddhdd#iia##b.....", +"..#ddddjkd#iab##b#c...", +"..#dddddd#iab##bb#c...", +"..c#ddhdd#ab##bb#c....", +"...#ddjk#ib##bbi#c....", +"...c#dd####bbbi#c.....", +"....#ddhbbbbddi#c.....", +"....c#djkjkjki#c......", +".....#iiiiiiii#c......", +".....c########c.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/help.xpm b/kenolaba/toolbar/help.xpm new file mode 100644 index 00000000..71348f4d --- /dev/null +++ b/kenolaba/toolbar/help.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * help_xpm[] = { +"22 22 4 1", +" c None", +". c #000000", +"X c #000086", +"o c #868286", +" ", +" ", +" ", +" ", +" . XXXXXo ", +" .. XX oXXo ", +" ... XXo XXX ", +" .... XXo XXX ", +" ..... oXX oXXo ", +" ...... XXo ", +" ....... XX ", +" ........ XXo ", +" ..... XXo ", +" .. .. ", +" . .. XXX ", +" .. XXX ", +" .. ", +" .. ", +" ", +" ", +" ", +" "}; diff --git a/kenolaba/toolbar/hint.xpm b/kenolaba/toolbar/hint.xpm new file mode 100644 index 00000000..399c7c07 --- /dev/null +++ b/kenolaba/toolbar/hint.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * hint_xpm[] = { +"22 22 6 1", +" c None", +". c #000086", +"X c #868286", +"o c black", +"O c red", +"+ c yellow", +" ", +" ", +" .....X ", +" .. X..X ", +" ..X ... ", +" ..X ... ", +" X.. X..X ", +" ..X ", +" .. ", +" oooo ..X ", +" oooooooo ..X ", +" ooOOOOoooo ", +" oOOo oooo ... ", +" oOOo oo oooo... ", +" oOo oooo o+o ", +" oOo oooo o+o ", +" oooo oo o++o ", +" oooo o++o ", +" ooooo+++oo ", +" oooooooo ", +" oooo ", +" "}; diff --git a/kenolaba/toolbar/network.xpm b/kenolaba/toolbar/network.xpm new file mode 100644 index 00000000..0878c2bb --- /dev/null +++ b/kenolaba/toolbar/network.xpm @@ -0,0 +1,35 @@ +/* XPM */ +static char*net[]={ +"22 22 10 1", +". c None", +"b c #808080", +"f c #a0a0a0", +"a c #c3c3c3", +"# c #000000", +"e c #0000c0", +"h c #ffff00", +"d c #585858", +"c c #ffffff", +"g c #ff0000", +"......................", +"......................", +".....#######..........", +"...##aaaabbb##........", +"...#ccaaaaabbb##......", +"...#ddccaaaaabbb#.....", +"...#eeddccaaabbbb#....", +"...#eeeeddcaadbbb#....", +"...#eeeeeecbbdbbb#....", +"...#eeeeeecbbdbbb#....", +"...#aeeeeecbbdbbb#....", +"....#aaeeecbbdbb#.....", +"....###aaecbb###......", +"....#aa##aa##dd#......", +".....##aa##dd##.......", +"..###..##ad##..d##d...", +".##aa##..##.#.##aa##..", +".#aaaaa##..#.#fgagaf#.", +"..##daaaa##..#aaaaaa##", +"....##daa#...#ahaaha#.", +"......###.....##ah##..", +"...............d##d..."}; diff --git a/kenolaba/toolbar/new.xpm b/kenolaba/toolbar/new.xpm new file mode 100644 index 00000000..e5aa03fc --- /dev/null +++ b/kenolaba/toolbar/new.xpm @@ -0,0 +1,36 @@ +/* XPM */ +static char*ab[]={ +"22 22 11 1", +"h c #ffa858", +"d c #d6cdbb", +"b c #c0c0c0", +"f c #ffc0c0", +"g c #c05800", +"# c #000000", +"i c #ffdca8", +". c None", +"c c #c00000", +"a c #ffffff", +"e c #ff0000", +"......................", +"................#a....", +"................#a....", +".............#a.#a.#a.", +"......#######.#a#a#a..", +".....#bbbbbbbb.#.#....", +".....#bcbcbcba.d......", +"....#bbefefefb#a#a#a..", +"....#bbbbbbbb#a.#a.#a.", +"...#bbbbbbcbcbb.#a....", +"...#bbbgbbefefba#a....", +"..#bbbbhibbbbbbba#d...", +"..#bbbbbbbbbbcbba#d...", +"..d#bbgbbbgbbefa#d....", +"...#bbhibbhibbba#d....", +"...d#bbbbbbbbba#d.....", +"....#bbgbgbgbba#d.....", +"....d#bhihihia#d......", +".....#aaaaaaaa#d......", +".....d########d.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/noball.xpm b/kenolaba/toolbar/noball.xpm new file mode 100644 index 00000000..14673de6 --- /dev/null +++ b/kenolaba/toolbar/noball.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static char*noball[]={ +"16 16 3 1", +"# c #808080", +". c None", +"a c #ffffff", +".....#####......", +"...#########....", +"..###aaaaa###...", +".###a.....a###a.", +".##a........##a.", +"##a..........##a", +"##a..........##a", +"##a..........##a", +"##a..........##a", +"##a..........##a", +".##a........##a.", +".###.......###a.", +"..###.....###a..", +"...#########a...", +"...aa#####aa....", +".....aaaaa......"}; diff --git a/kenolaba/toolbar/ok.xpm b/kenolaba/toolbar/ok.xpm new file mode 100644 index 00000000..290d4657 --- /dev/null +++ b/kenolaba/toolbar/ok.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*ok[]={ +"16 16 6 1", +"d c #00ff00", +"b c #004000", +"# c #c0c0ff", +"a c #000000", +". c None", +"c c #ffffff", +".....#####......", +"...#######......", +"..#######.abbc..", +".#######.abbbc..", +".######.abbdbc..", +"#######.abdbc.#.", +"######.abdbc.##.", +"##.###.abbc.###.", +"#.ac#.abdc.####.", +"#abc..abbc.####.", +"..bbcabbc.####..", +".#.bcbbc.#####..", +"...abbc.#####...", +"....bbc#####....", +"....ac.###......", +"................"}; diff --git a/kenolaba/toolbar/redball.xpm b/kenolaba/toolbar/redball.xpm new file mode 100644 index 00000000..24450be5 --- /dev/null +++ b/kenolaba/toolbar/redball.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*redball[]={ +"16 16 6 1", +"# c #000000", +"a c #400000", +"b c #800000", +". c None", +"d c #ffffff", +"c c #ff0000", +".....#####......", +"...##aaaaa##....", +"..##aabbbbb##...", +".##aabbbbbbb##..", +".#aabbbbbbbbb#..", +"#aabbbbbbbbbbb#.", +"#aabbbbbcccbbb#.", +"#aabbbbcccccbb#.", +"#aabbbcccccccb#.", +"#aabbbccccdccb#.", +".#aabbcccdddc#..", +".##aabbcccdc##..", +"..##aabbccc##...", +"...##aaaaa##....", +".....#####......", +"................"}; diff --git a/kenolaba/toolbar/spy0.xpm b/kenolaba/toolbar/spy0.xpm new file mode 100644 index 00000000..a6cbb1c5 --- /dev/null +++ b/kenolaba/toolbar/spy0.xpm @@ -0,0 +1,39 @@ +/* XPM */ +static char*spy0[]={ +"22 22 14 1", +"a c #808080", +"j c #ffa858", +"l c #d6cdbb", +"c c #c0c0c0", +"g c #ffc0c0", +"i c #c05800", +"# c #000000", +". c None", +"k c #ffdca8", +"d c #c00000", +"h c #585858", +"e c #dcdcdc", +"b c #ffffff", +"f c #ff0000", +"......................", +"...........####.......", +"..........#....#......", +".........#.aaaa.#.....", +"......####baaaa.#b....", +".....#ccc#baaaa.#b....", +".....#cdc#eaaaa.#b....", +"....#ccfgf#ebbe#b.....", +"....#cccccc#####h.....", +"...#ccccccdcdcc.hb....", +"...#ccciccfgfgc.#h....", +"..#ccccjkccccccc.hb...", +"..#ccccccccccdcc.#h...", +"..l#cciccciccfgb#.hb..", +"...#ccjkccjkcccb#..h..", +"...l#cccccccccb#l..h..", +"....#cciciciccb#l.....", +"....l#cjkjkjkb#l......", +".....#bbbbbbbb#l......", +".....l########l.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy1.xpm b/kenolaba/toolbar/spy1.xpm new file mode 100644 index 00000000..24baa2ba --- /dev/null +++ b/kenolaba/toolbar/spy1.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*spy1[]={ +"22 22 13 1", +"i c #ffa858", +"k c #d6cdbb", +"e c #c0c0c0", +"g c #ffc0c0", +"h c #c05800", +"# c #000000", +". c None", +"j c #ffdca8", +"f c #c00000", +"d c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"......................", +"...........####.......", +"..........#abba#......", +".........#ac##ca#.....", +"......####bdccbb#b....", +".....#eee#bbccbb#b....", +".....#efe#acbbca#b....", +"....#eecgc#abba#bb....", +"....#eeeeee######b....", +"...#eeeeeefefee##bb...", +"...#eeeheecgcge###b...", +"..#eeeeijeeeeeee##b...", +"..#eeeeeeeeeefee###b..", +"..k#eeheeeheecgbb#....", +"...#eeijeeijeeebb.....", +"...k#eeeeeeeeeb#k.....", +"....#eeheheheeb#k.....", +"....k#eijijijb#k......", +".....#bbbbbbbb#k......", +".....k########k.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy2.xpm b/kenolaba/toolbar/spy2.xpm new file mode 100644 index 00000000..cd2cb150 --- /dev/null +++ b/kenolaba/toolbar/spy2.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static char*spy2[]={ +"22 22 15 1", +"j c #ffa858", +"g c #c3c3c3", +"l c #d6cdbb", +"e c #c0c0c0", +"m c #ffc0c0", +"i c #c05800", +"# c #000000", +". c None", +"h c #800000", +"k c #ffdca8", +"f c #c00000", +"d c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"......................", +"...........####.......", +".........##abba##.....", +".........#ac##ca#.....", +"......####bdccbb##....", +".....#ee#b#bccb#b#....", +".....#ef#gachhcab#....", +"....#eec#bhhhhhhb#....", +"....#eee##ehhhhb##....", +"...#eeeee#gggbgg#bb...", +"...#eeeie##bbbb###b...", +"..#eeeejkee#######bb..", +"..#eeeeeeeeeefee###b..", +"..l#eeieeeieecmbb#....", +"...#eejkeejkeeebb.....", +"...l#eeeeeeeeeb#l.....", +"....#eeieieieeb#l.....", +"....l#ejkjkjkb#l......", +".....#bbbbbbbb#l......", +".....l########l.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/spy3.xpm b/kenolaba/toolbar/spy3.xpm new file mode 100644 index 00000000..b19cbdcc --- /dev/null +++ b/kenolaba/toolbar/spy3.xpm @@ -0,0 +1,38 @@ +/* XPM */ +static char*spy3[]={ +"22 22 13 1", +"j c #ffa858", +"d c #c3c3c3", +"h c #d6cdbb", +"f c #c0c0c0", +"i c #c05800", +"# c #000000", +". c None", +"g c #800000", +"k c #ffdca8", +"e c #a0a0a4", +"a c #dcdcdc", +"b c #ffffff", +"c c #ff0000", +"...........####.......", +".........########.....", +"........###abba###....", +".......###ac##ca###...", +"......###dbeccbbd##...", +".....#f##b#cccc#b##...", +".....###ddacggcabd##..", +"....#f##dbggggggbd##..", +"....#f##d###gg###d##..", +"...#ff##dd#cccc#dd##..", +"...#ff###ddbccbdd##...", +"..#ffff###dddddd###...", +"..#ffffd####dd####....", +"..h#ffifd######b#.....", +"...#ffjkfdd######.....", +"...h#fffffffffb#h.....", +"....#ffifififfb#h.....", +"....h#fjkjkjkb#h......", +".....#bbbbbbbb#h......", +".....h########h.......", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/stop.xpm b/kenolaba/toolbar/stop.xpm new file mode 100644 index 00000000..c8e641c6 --- /dev/null +++ b/kenolaba/toolbar/stop.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * ab_stop_xpm[] = { +"22 22 5 1", +" c none", +". c black", +"X c white", +"o c red", +"O c #820782078207", +" ", +" ", +" ", +" ....... ", +" ......... ", +" ..XXXXXX... ", +" ..X o o o ... ", +" ..X o o o o ... ", +" ..Xo o o o oO.. ", +" ..X o o o o O.. ", +" ..Xo o o o oO.. ", +" ..X o o o o O.. ", +" ..Xo o o o oO.. ", +" ... o o o o O.. ", +" ... o o o O.. ", +" ...OOOOOO.. ", +" ......... ", +" ....... ", +" ", +" ", +" ", +" "}; diff --git a/kenolaba/toolbar/undo.xpm b/kenolaba/toolbar/undo.xpm new file mode 100644 index 00000000..9b002026 --- /dev/null +++ b/kenolaba/toolbar/undo.xpm @@ -0,0 +1,27 @@ +/* XPM */ +static char*undo[]={ +"22 22 2 1", +"# c #000080", +". c None", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................", +"..........######......", +".........########.....", +"........####....##....", +"........###...........", +".......###............", +".....#######..........", +"......#####...........", +".......###............", +"........#.............", +"......................", +"......................", +"......................", +"......................", +"......................", +"......................"}; diff --git a/kenolaba/toolbar/warning.xpm b/kenolaba/toolbar/warning.xpm new file mode 100644 index 00000000..b2674f2a --- /dev/null +++ b/kenolaba/toolbar/warning.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static char*warning[]={ +"16 16 7 1", +"e c #d6cdbb", +"# c #c0c0ff", +"a c #000000", +". c None", +"c c #400000", +"b c #ffffff", +"d c #ff0000", +"................", +"...##.aaab.#....", +"..##.accccb.#...", +".###.accdcb.##..", +".###.accdcb.##..", +"####..adcb.####.", +"#####.accb.####.", +"######.ab.#####.", +"######.ab.#####.", +"######.b.######.", +".######..#####..", +".#####.ab.####..", +"..###.adcb.##...", +"...###.cbe##....", +".....#.be#......", +"................"}; diff --git a/kenolaba/toolbar/yellowball.xpm b/kenolaba/toolbar/yellowball.xpm new file mode 100644 index 00000000..fc986c52 --- /dev/null +++ b/kenolaba/toolbar/yellowball.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char*yellowball[]={ +"16 16 6 1", +"b c #c0c000", +"a c #808000", +"# c #000000", +"c c #ffff00", +". c None", +"d c #ffffff", +".....#####......", +"...##aaaaa##....", +"..##aaaaaaa##...", +".##aaaabbbaa##..", +".#aaaabbbbbaa#..", +"#aaaabbbbbbbaa#.", +"#aaabbbbbbbbba#.", +"#aaabbbccccbba#.", +"#aaabbbcccccba#.", +"#aaaabbcccdcaa#.", +".#aaaabccddda#..", +".##aaaabccdc##..", +"..##aaaaaaa##...", +"...##aaaaa##....", +".....#####......", +"................"}; diff --git a/kenolaba/version.h b/kenolaba/version.h new file mode 100644 index 00000000..90e8ddd1 --- /dev/null +++ b/kenolaba/version.h @@ -0,0 +1 @@ +#define KENOLABA_VERSION "1.06b" diff --git a/kfouleggs/CHANGELOG b/kfouleggs/CHANGELOG new file mode 100644 index 00000000..d7e56fd2 --- /dev/null +++ b/kfouleggs/CHANGELOG @@ -0,0 +1 @@ +see "ksirtet" CHANGELOG diff --git a/kfouleggs/LICENSE b/kfouleggs/LICENSE new file mode 100644 index 00000000..621e9325 --- /dev/null +++ b/kfouleggs/LICENSE @@ -0,0 +1,18 @@ +KFOULEGGS +--------- +Copyright (c) 1999-2004 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/kfouleggs/Makefile.am b/kfouleggs/Makefile.am new file mode 100644 index 00000000..a47789a4 --- /dev/null +++ b/kfouleggs/Makefile.am @@ -0,0 +1,75 @@ +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(top_srcdir)/libksirtet/base -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +SUBDIRS = pics + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kfouleggs +kfouleggs_LDADD = $(top_builddir)/libksirtet/common/libksirtetcommon.la +kfouleggs_LDFLAGS = $(KDE_USE_PIE) $(all_libraries) $(KDE_RPATH) +kfouleggs_SOURCES = piece.cpp board.cpp ai.cpp field.cpp main.cpp prefs.kcfgc +METASOURCES = board.moc ai.moc field.moc main.moc + +rcdir = $(kde_datadir)/kfouleggs +rc_DATA = kfouleggsui.rc + +xdg_apps_DATA = kfouleggs.desktop +kde_kcfg_DATA = kfouleggs.kcfg + +appdatadir = $(kde_datadir)/kfouleggs +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kfouleggs.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + +field.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h +main.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h +prefs.o: ../libksirtet/common/commonprefs.h ../libksirtet/base/baseprefs.h + diff --git a/kfouleggs/README b/kfouleggs/README new file mode 100644 index 00000000..46ffcb4c --- /dev/null +++ b/kfouleggs/README @@ -0,0 +1,14 @@ +KFOULEGGS : a puyopuyo-alike game +--------------------------------- +Copyright (c) 1999-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU General Public License + + +KFoulEggs is an adaptation of the also well-known (at least in Japan) +Puyo-Puyo game. It provides multiplayers functionality and "artificial +intelligence" player. + +The code links to the ksirtet libraries. + + +Enjoy ! diff --git a/kfouleggs/TODO b/kfouleggs/TODO new file mode 100644 index 00000000..b999a6fe --- /dev/null +++ b/kfouleggs/TODO @@ -0,0 +1,3 @@ +see also the TODO list for libksirtet + + * better (a -very- simple AI is already quite annoying :) and varied AIs diff --git a/kfouleggs/ai.cpp b/kfouleggs/ai.cpp new file mode 100644 index 00000000..ee8a6c4b --- /dev/null +++ b/kfouleggs/ai.cpp @@ -0,0 +1,40 @@ +#include "ai.h" +#include "ai.moc" + +#include + +#include "board.h" + + +const AI::Data FEAI::DATA[] = { + { "OccupiedLines", I18N_NOOP("Occupied lines:"), 0, + false, nbOccupiedLines }, + { "Spaces", I18N_NOOP("Number of spaces:"), I18N_NOOP("Number of spaces under mean height"), + false, nbSpaces }, + { "Peak2Peak", I18N_NOOP("Peak-to-peak distance:"), 0, + false, peakToPeak }, + { "MeanHeight", I18N_NOOP("Mean height:"), 0, + false, mean }, + { "RemovedEggs", I18N_NOOP("Number of removed eggs:"), 0, + false, nbRemoved }, + { "Puyos", I18N_NOOP("Number of puyos:"), 0, + true, nbPuyos }, + { "ChainedPuyos", I18N_NOOP("Number of chained puyos:"), 0, + true, nbChained }, + LastData +}; + +FEAI::FEAI() +: AI(0, 200, DATA) +{} + +double FEAI::nbPuyos(const Board &main, const Board ¤t) +{ + return static_cast(current).nbPuyos() + - static_cast(main).nbPuyos(); +} + +double FEAI::nbChained(const Board &, const Board ¤t) +{ + return static_cast(current).chained(); +} diff --git a/kfouleggs/ai.h b/kfouleggs/ai.h new file mode 100644 index 00000000..86e8f5b2 --- /dev/null +++ b/kfouleggs/ai.h @@ -0,0 +1,21 @@ +#ifndef FE_AI_H +#define FE_AI_H + +#include "common/ai.h" + + +class FEAI : public AI +{ + Q_OBJECT + + public: + FEAI(); + + private: + static const Data DATA[]; + + static double nbPuyos(const Board &, const Board &); + static double nbChained(const Board &, const Board &); +}; + +#endif diff --git a/kfouleggs/board.cpp b/kfouleggs/board.cpp new file mode 100644 index 00000000..836c7e1e --- /dev/null +++ b/kfouleggs/board.cpp @@ -0,0 +1,161 @@ +#include "board.h" +#include "board.moc" + +#include + +#include "common/misc_ui.h" +#include "piece.h" + + +using namespace KGrid2D; + +FEBoard::FEBoard(bool graphic, QWidget *parent) +: Board(graphic, new GiftPool(parent), parent), + _field(matrix().width(), matrix().height()), _chainedPuyos(4) +{ + init(); +} + +void FEBoard::init() +{ + _nbPuyos = 0; + _chained = 0; + _giftRest = 0; + _lastChained = 0; + for (uint i=0; i<4; i++) { + _chainedPuyos[i] = 0; + _lastChained += (2<(g)._nbPuyos; +} + +void FEBoard::start(const GTInitData &data) +{ + init(); + Board::start(data); +} + +void FEBoard::computeInfos() +{ + Board::computeInfos(); + if ( graphic() ) computeNeighbours(); +} + +bool FEBoard::afterGlue(bool doAll, bool first) +{ + return !doFall(doAll, first, false); +} + +void FEBoard::removeBlock(const Coord &c) +{ + Board::removeBlock(c); + + // remove surrounding garbage + CoordList list = matrix().neighbours(c, true, true); + for (CoordList::const_iterator i = list.begin(); i!=list.end(); ++i) + if ( matrix()[*i]!=0 && matrix()[*i]->isGarbage() ) + Board::removeBlock(*i); +} + +bool FEBoard::toBeRemoved(const Coord &c) const +{ + return ( _field[c]>=4 ); +} + +bool FEBoard::toFall(const Coord &c) const +{ + Coord under = c - Coord(0, 1); + return ( matrix()[under]==0 ); +} + +void FEBoard::remove() +{ + Board::remove(); + + // score calculation from another game + // not sure it is the "official" way + uint nbPuyos = _groups.size(); // number of group detroyed + uint nbEggs = 0; // number of eggs destroyed + for (uint k=0; k<_groups.size(); k++) nbEggs += _groups[k]; + + uint bonus = nbEggs - 3; // more than 4 since we are here ! + if ( nbEggs==11 ) bonus += 2; + if ( nbPuyos>=2 ) bonus += 3 * (1 << (nbPuyos-2)); // 3 * 2^(nb-2) + if ( _chained>=1 ) bonus += 1 << (_chained+2); // 2^(chained+2) + uint dscore = 10 * nbPuyos * bonus; + + uint i = kMin(_chained, (uint)3); + _chainedPuyos[i] += nbPuyos; + _lastChained = 2 << i; + _chained++; + _giftRest += dscore; + _nbPuyos += nbPuyos; + updateRemoved(nbRemoved() + nbEggs); + updateScore(score() + dscore); + + updateLevel(); +} + +Board::AfterRemoveResult FEBoard::afterRemove(bool doAll, bool first) +{ + Board::AfterRemoveResult res = Board::afterRemove(doAll, first); + if ( res==Done && needRemoving() ) return NeedRemoving; + return res; +} + +bool FEBoard::needRemoving() +{ + _groups = findGroups(_field, 4); + if ( _groups.size()==0 ) _chained = 0; + return _groups.size(); +} + +/*****************************************************************************/ +// Multiplayers methods +uint FEBoard::gift() +{ + uint n = _giftRest / 60; + _giftRest = _giftRest % 60; + return n; +} + +bool FEBoard::putGift(uint n) +{ + QMemArray free(matrix().width()); + + // garbage blocks are put randomly on conlumns with more than 5 free lines. + uint nbFree = 0; + for (uint i=0; i=(int)matrix().height()-5 ) free[i] = false; + else { + free[i] = true; + nbFree++; + } + } + uint nb = kMin(nbFree, n); + while (nbFree && nb) { + uint k = (uint)randomGarbage.getLong(nbFree); + uint l = 0; + for (uint i=0; igarbageBlock(); + gb->sprite()->show(); + Coord c(i, matrix().height()-1); + setBlock(c, gb); + free[i] = false; + nbFree--; + nb--; + break; + } + l++; + } + } + return true; +} diff --git a/kfouleggs/board.h b/kfouleggs/board.h new file mode 100644 index 00000000..e0889b3b --- /dev/null +++ b/kfouleggs/board.h @@ -0,0 +1,44 @@ +#ifndef FE_BOARD_H +#define FE_BOARD_H + +#include "common/board.h" + + +class FEBoard : public Board +{ + Q_OBJECT + public: + FEBoard(bool graphic, QWidget *parent); + void copy(const GenericTetris &); + + void start(const GTInitData &); + + uint nbPuyos() const { return _nbPuyos; } + uint chained() const { return _chained; } + uint nbChainedPuyos(uint i) const { return _chainedPuyos[i]; } + uint lastChained() const { return _lastChained; } + + private: + // standard methods + bool afterGlue(bool doAll, bool first); + AfterRemoveResult afterRemove(bool doAll, bool first); + bool afterGift(bool first) { return !doFall(false, first, true); } + void removeBlock(const KGrid2D::Coord &); + bool toBeRemoved(const KGrid2D::Coord &) const; + bool toFall(const KGrid2D::Coord &) const; + void remove(); + bool needRemoving(); + void computeInfos(); + + void init(); + + // Multiplayers methods + uint gift(); + bool putGift(uint); + + KGrid2D::Square _field; + QMemArray _groups, _chainedPuyos; + uint _nbPuyos, _chained, _giftRest, _lastChained; +}; + +#endif diff --git a/kfouleggs/eventsrc b/kfouleggs/eventsrc new file mode 100644 index 00000000..11663f03 --- /dev/null +++ b/kfouleggs/eventsrc @@ -0,0 +1,358 @@ +[!Global!] +IconName=kfouleggs +Comment=KFoulEggs +Comment[ar]=لعبة البيضات المعÙنة (KFoulEggs) +Comment[be]=Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ Ñйкі +Comment[bn]=কে-ফাউলà¦à¦— +Comment[cs]=Zkažená vejce +Comment[hi]=के-फाउलà¤à¤—à¥à¤¸ +Comment[hr]=KPokvarena jaja +Comment[is]=Fúlegg +Comment[ne]=केडीई फल à¤à¤— +Comment[pt_BR]=KOvos Sujos +Comment[sv]=Kfouleggs +Comment[ta]=கேதவறான à®®à¯à®Ÿà¯à®Ÿà¯ˆà®•à®³à¯ +Comment[tg]=KТухмҳои Ðфтанда + +[removed] +Name=Blocks removed +Name[ar]=لقد أزيل الطوب +Name[be]=Блокі Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ñ‹Ñ +Name[bg]=Премахнати Ñа блокове +Name[bn]=গà§à¦Ÿà¦¿ সরিয়ে ফেলা হয়েছে +Name[bs]=Uklonjeni blokovi +Name[ca]=Blocs eliminats +Name[cs]=Bloky odstranÄ›ny +Name[cy]=Gwaredwyd blociau +Name[da]=Blokke fjernet +Name[de]=Entfernte Klötzchen +Name[el]=Οι γÏαμμές αφαιÏέθηκαν +Name[eo]=Blokoj forigitaj +Name[es]=Bloques eliminados +Name[et]=Eemaldatud blokid +Name[eu]=Kendutako blokeak +Name[fa]=بلوکها حذ٠شدند +Name[fi]=Palikoita poistettu +Name[fr]=Blocs supprimés +Name[ga]=Bloic bainte +Name[gl]=Bloques eliminados +Name[he]=×‘×œ×•×§×™× ×©×”×•×¡×¨×• +Name[hi]=पिणà¥à¤¡ हटाठ+Name[hr]=Uklonjeni blokovi +Name[hu]=Blokk eltávolítva +Name[is]=Kubbar fjarlægðir +Name[it]=Blocchi rimossi +Name[ja]=ブロックを消ã—㟠+Name[km]=ដុំ​ដែល​បាន​យក​ចáŸáž‰ +Name[lt]=Blokai panaikinti +Name[lv]=Noņemti bloki +Name[mk]=ОтÑтранети Ñе блокови +Name[nb]=Blokker fjernet +Name[nds]=Wegdaan Steen +Name[ne]=खणà¥à¤¡ हटाइयो +Name[nl]=Blokken verwijderd +Name[nn]=Blokker fjerna +Name[pa]=ਬਲਾਕ ਹਟਾਠ+Name[pl]=UsuniÄ™cie bloków +Name[pt]=Blocos removidos +Name[pt_BR]=Blocos removidos +Name[ro]=Blocuri eliminate +Name[ru]=Блок удалён +Name[se]=Bihtát leat eretváldon +Name[sk]=Bloky odstránené +Name[sl]=Odstranjeni bloki +Name[sr]=Уклоњени блокови +Name[sr@Latn]=Uklonjeni blokovi +Name[sv]=Block borttagna +Name[ta]=கடà¯à®Ÿà®™à¯à®•à®³à¯ நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=БаÑтаҳо ҷойивазкарда шудаанд +Name[tr]=Silinen bloklar +Name[uk]=Блоків вилучено +Name[zh_CN]=消去的å—æ•° +Name[zh_TW]=移除的方塊 +Comment=Blocks removed +Comment[be]=Блокі Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ñ‹Ñ +Comment[bg]=Премахнати Ñа блокове +Comment[bn]=গà§à¦Ÿà¦¿ সরিয়ে ফেলা হয়েছে +Comment[bs]=Uklonjeni blokovi +Comment[ca]=Blocs eliminats +Comment[cs]=Bloky odstranÄ›ny +Comment[cy]=Gwaredwyd blociau +Comment[da]=Blokke fjernet +Comment[de]=Entfernte Klötzchen +Comment[el]=Οι γÏαμμές αφαιÏέθηκαν +Comment[eo]=Blokoj forigitaj +Comment[es]=Bloques eliminados +Comment[et]=Eemaldatud blokid +Comment[eu]=Kendutako blokeak +Comment[fa]=بلوکها حذ٠شدند +Comment[fi]=Palikoita poistettu +Comment[fr]=Blocs supprimés +Comment[ga]=Bloic bainte +Comment[gl]=Bloques eliminados +Comment[he]=×‘×œ×•×§×™× ×©×”×•×¡×¨×• +Comment[hi]=पिणà¥à¤¡ हटाठ+Comment[hr]=Uklonjeni blokovi +Comment[hu]=Blokk eltávolítva +Comment[is]=Kubbar fjarlægðir +Comment[it]=Blocchi rimossi +Comment[ja]=ブロック消ã—㟠+Comment[km]=ដុំ​ដែល​បាន​យក​ចáŸáž‰ +Comment[lt]=Blokai panaikinti +Comment[lv]=Noņemti bloki +Comment[mk]=ОтÑтранети Ñе блокови +Comment[nb]=Blokker fjernet +Comment[nds]=Wegdaan Steen +Comment[ne]=खणà¥à¤¡ हटाइयो +Comment[nl]=Blokken verwijderd +Comment[nn]=Blokker fjerna +Comment[pa]=ਬਲਾਕ ਹਟਾਠ+Comment[pl]=UsuniÄ™cie bloków +Comment[pt]=Blocos removidos +Comment[pt_BR]=Blocos removidos +Comment[ro]=Blocuri eliminate +Comment[ru]=Блок удалён +Comment[se]=Bihtát leat eretváldon +Comment[sk]=Bloky odstránené +Comment[sl]=Odstranjeni bloki +Comment[sr]=Уклоњени блокови +Comment[sr@Latn]=Uklonjeni blokovi +Comment[sv]=Block borttagna +Comment[ta]=தடைகள௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=БаÑтаҳо ҷойивазкарда шудаанд +Comment[tr]=Silinen bloklar +Comment[uk]=Блоків вилучено +Comment[zh_CN]=消去的å—æ•° +Comment[zh_TW]=移除的方塊 +default_presentation=0 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over +Comment[be]=Канец гульні +Comment[bg]=Край на играта +Comment[bn]=খেল খতম +Comment[br]=Echu an abadenn +Comment[bs]=Kraj igre +Comment[ca]=Fi de la partida +Comment[cs]=Hra skonÄena +Comment[cy]=Gêm drosodd +Comment[da]=Spil forbi +Comment[de]=Das Spiel ist vorbei +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Comment[eo]=Ludo finita +Comment[es]=Fin de la partida +Comment[et]=Mäng läbi +Comment[eu]=Jokoa amaitu da +Comment[fa]=بازی تمام شد +Comment[fi]=Peli loppui +Comment[fr]=Fin de la partie +Comment[ga]=Cluiche thart +Comment[he]=×¡×™×•× ×ž×©×—×§ +Comment[hr]=Kraj igre +Comment[hu]=Vége a játéknak +Comment[is]=Leik lokið +Comment[it]=Gioco terminato +Comment[ja]=ゲームオーãƒãƒ¼ +Comment[km]=ល្បែង​ចប់ +Comment[lt]=Žaidimas baigtas +Comment[lv]=SpÄ“le beigusies +Comment[mk]=Играта заврши +Comment[nb]=Spillet er slutt +Comment[nds]=Speel vörbi +Comment[ne]=खेल समापà¥à¤¤ +Comment[nl]=Het spel is afgelopen +Comment[nn]=Spelet er slutt +Comment[pa]=ਖੇਡ ਖਤਮ +Comment[pl]=Koniec gry +Comment[pt]=Fim do jogo +Comment[pt_BR]=Fim do Jogo +Comment[ru]=Конец игры +Comment[se]=Speallu nogai +Comment[sk]=Koniec hry +Comment[sl]=Konec igre +Comment[sr]=Крај игре +Comment[sr@Latn]=Kraj igre +Comment[sv]=Spelet slut +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[tr]=Oyun bitti +Comment[uk]=Кінець гри +Comment[wa]=Li djeu est houte +Comment[zh_CN]=游æˆç»“æŸ +Comment[zh_TW]=éŠæˆ²çµæŸ +default_presentation=0 + +[glued] +Name=Piece glued +Name[ar]=لقد ألصقت القطعة +Name[be]=Кавалачак зліпнуўÑÑ +Name[bg]=Залепени Ñа парчета +Name[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Name[bs]=Komad spojen +Name[ca]=Peça enganxada +Name[cs]=Blok spojen +Name[cy]=Darn wedi ei gludo +Name[da]=Brik klæbet fast +Name[de]=Zusammengefügte Klötzchen +Name[el]=Το κομμάτι κολλήθηκε +Name[eo]=Peco gluita +Name[es]=Ficha pegada +Name[et]=Kleepunud klots +Name[eu]=Pieza itsatsita +Name[fa]=قطعه چسبید +Name[fi]=Pala liimattu +Name[fr]=Morceau collé +Name[gl]=Peza pegada +Name[he]=חתיכה מודבקת +Name[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Name[hr]=Zalijepljeni dio +Name[hu]=Elem lekötve +Name[is]=Hlutur límdur +Name[it]=Pezzo incollato +Name[ja]=ピースをãã£ã¤ã‘㟠+Name[km]=បំណែក​បាន​បិទ +Name[lt]=Dalis įklijuota +Name[lv]=PielÄ«mÄ“ts gabals +Name[mk]=Залепено е парче +Name[nb]=Brikke festet +Name[nds]=Steen tosamenbackt +Name[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Name[nl]=Samengevoegde stukken +Name[nn]=Brikke festa +Name[pl]=Sklejenie elementów +Name[pt]=Peça colada +Name[pt_BR]=Peça colada +Name[ru]=Слипание Ñиц +Name[se]=Bihttá lea liibmejuvvon +Name[sk]=Kus zlepený +Name[sl]=Kos prilepljen +Name[sr]=Залепљено парче +Name[sr@Latn]=Zalepljeno parÄe +Name[sv]=Pjäs fastsatt +Name[ta]= தà¯à®£à¯à®Ÿà¯ கிலூயிட௠+Name[tg]=ҚиÑмҳои чаÑбанда +Name[tr]=Yapıştırılan parçalar +Name[uk]=Фігура приклеєна +Name[zh_CN]=粘连的å—æ•° +Name[zh_TW]=é»è‘—çš„å°ç¢Žç‰‡ +Comment=Piece glued +Comment[ar]=لقد ألصقت القطعة +Comment[be]=Кавалачак зліпнуўÑÑ +Comment[bg]=Залепени Ñа парчета +Comment[bn]=গà§à¦Ÿà¦¿ আটকে আছে +Comment[bs]=Komad spojen +Comment[ca]=Peça enganxada +Comment[cs]=Blok spojen +Comment[cy]=Darn wedi ei gludo +Comment[da]=Brik klæbet fast +Comment[de]=Zusammengefügte Klötzchen +Comment[el]=Το κομμάτι κολλήθηκε +Comment[eo]=Peco gluita +Comment[es]=Ficha pegada +Comment[et]=Paikapandud klots +Comment[eu]=Pieza itsatsita +Comment[fa]=قطعه چسبید +Comment[fi]=Pala liimattu +Comment[fr]=Morceau collé +Comment[gl]=Peza pegada +Comment[he]=חתיכה מודבקת +Comment[hi]=टà¥à¤•à¤¡à¤¼à¥‡ चिपकाठ+Comment[hr]=Zalijepljeni dio +Comment[hu]=Elem lekötve +Comment[is]=Hlutur límdur +Comment[it]=Pezzo incollato +Comment[ja]=ピースをãã£ã¤ã‘㟠+Comment[km]=បំណែក​បាន​បិទ +Comment[lt]=Dalis įklijuota +Comment[lv]=Gabals ir pielÄ«mÄ“ts +Comment[mk]=Залепено е парче +Comment[nb]=Brikke festet +Comment[nds]=Steen tosamenbackt +Comment[ne]=टाà¤à¤¸à¤¿à¤à¤•à¥‹ टà¥à¤•à¥à¤°à¤¾ +Comment[nl]=Samengevoegde stukken +Comment[nn]=Brikke festa +Comment[pl]=Sklejenie elementu +Comment[pt]=Peça colada +Comment[pt_BR]=Peça colada +Comment[ru]=Слипание Ñиц +Comment[se]=Bihttá lea liibmejuvvon +Comment[sk]=Kus zlepený +Comment[sl]=Kos prilepljen +Comment[sr]=Залепљено парче +Comment[sr@Latn]=Zalepljeno parÄe +Comment[sv]=Pjäs fastsatt +Comment[ta]=தà¯à®£à¯à®Ÿà¯ ஒடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=ҚиÑмҳои чаÑбанда +Comment[tr]=Parça yapıştırıldı +Comment[uk]=Фігура приклеєна +Comment[zh_CN]=粘连的å—æ•° +default_presentation=0 diff --git a/kfouleggs/field.cpp b/kfouleggs/field.cpp new file mode 100644 index 00000000..9d7abe85 --- /dev/null +++ b/kfouleggs/field.cpp @@ -0,0 +1,61 @@ +#include "field.h" +#include "field.moc" + +#include + +#include +#include + +#include "common/commonprefs.h" +#include "board.h" + + +FEField::FEField(QWidget *parent) + : Field(parent) +{ + Board *b = static_cast(board); + QWhatsThis::add(b->giftPool(), i18n("Display the amount of foul eggs sent by your opponent.")); +} + +void FEField::removedUpdated() +{ + Field::removedUpdated(); + const FEBoard *feb = static_cast(board); + KGameLCD *lcd = static_cast(removedList->lcd(0)); + lcd->displayInt(feb->nbPuyos()); + if ( feb->nbPuyos() ) lcd->highlight(); + if ( CommonPrefs::showDetailedRemoved() ) + for (uint i=0; i<4; i++) { + if ( !(feb->lastChained() & 2<(removedList->lcd(i+1)); + lcd->displayInt(feb->nbChainedPuyos(i)); + if ( feb->nbChainedPuyos(i) ) lcd->highlight(); + } +} + +void FEField::settingsChanged() +{ + Field::settingsChanged(); + + removedList->clear(); + KGameLCD *lcd = new KGameLCD(6, removedList); + removedList->append(i18n("Total:"), lcd); + uint nb = static_cast(board)->nbPuyos(); + lcd->displayInt(nb); + lcd->show(); + + if ( CommonPrefs::showDetailedRemoved() ) { + QWhatsThis::add(removedList, + i18n("Display the number of removed groups (\"puyos\") classified by the number of chained removal.")); + for (uint i=0; i<4; i++) { + KGameLCD *lcd = new KGameLCD(6, removedList); + QString s = (i==3 ? ">3" : QString::number(i)); + removedList->append(s, lcd); + uint nb = static_cast(board)->nbChainedPuyos(i); + lcd->displayInt(nb); + lcd->show(); + } + } else + QWhatsThis::add(removedList, + i18n("Display the number of removed groups (\"puyos\").")); +} diff --git a/kfouleggs/field.h b/kfouleggs/field.h new file mode 100644 index 00000000..df7812f2 --- /dev/null +++ b/kfouleggs/field.h @@ -0,0 +1,19 @@ +#ifndef FE_FIELD_H +#define FE_FIELD_H + +#include "common/field.h" +#include "common/misc_ui.h" + + +class FEField : public Field +{ + Q_OBJECT + public: + FEField(QWidget *parent); + + private slots: + virtual void removedUpdated(); + void settingsChanged(); +}; + +#endif diff --git a/kfouleggs/kfouleggs.desktop b/kfouleggs/kfouleggs.desktop new file mode 100644 index 00000000..d02296d3 --- /dev/null +++ b/kfouleggs/kfouleggs.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Name=KFoulEggs +Name[af]=K-vrot-eier +Name[ar]=لعبة البيضات المعÙنة (KFoulEggs) +Name[be]=Ð¤Ð°Ñ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ Ñйкі +Name[bn]=কে-ফাউলà¦à¦— +Name[hi]=के-फाउलà¤à¤—à¥à¤¸ +Name[hr]=KPokvarena jaja +Name[is]=Fúlegg +Name[ne]=के फल à¤à¤— +Name[pl]=Foul Eggs +Name[pt_BR]=KOvos Sujos +Name[sv]=Kfouleggs +Name[ta]=கேதவறான à®®à¯à®Ÿà¯à®Ÿà¯ˆà®•à®³à¯ +Name[tg]=KТухмҳои Ðфтанда +Name[tr]=Çürük Yumurtalar +Icon=kfouleggs +Exec=kfouleggs -caption "%c" %i %m +Type=Application +DocPath=kfouleggs/index.html +GenericName=Japanese PuyoPuyo-like Game +GenericName[be]=ЯпонÑкі Ñ‚ÑÑ‚Ñ€Ñ‹Ñ +GenericName[bg]=ЯпонÑки Ñ‚ÐµÑ‚Ñ€Ð¸Ñ +GenericName[bn]=জাপানি পà§à¦‡à§Ÿà§‹à¦ªà§à¦‡à§Ÿà§‹-জাতীয় খেলা +GenericName[br]=C'hoari doare PuyoPuyo japaneg +GenericName[bs]=Japanska igra nalik na PuyoPuyo +GenericName[ca]=Jocs similar al PuyoPuyo japonès +GenericName[cs]=Japonská hra podobná PuyoPuyo +GenericName[cy]=Gêm Fwrdd sy'n debyg i PuyoPuyo Siapanaidd +GenericName[da]=Japansk PuyoPuyo-lignende spil +GenericName[de]=Puyo-Puyo-ähnliches Brettspiel +GenericName[el]=Ιαπωνικό παιχνίδι παÏόμοιο με το PuyoPuyo +GenericName[eo]=Japana PuyoPuyo-simila bretludo +GenericName[es]=Juego japonés similar al PuyoPuyo +GenericName[et]=Jaapani PuyoPuyo moodi mäng +GenericName[eu]=PuyoPuyo bezalaeko joko japoniarra +GenericName[fa]=بازی شبیه PuyoPuyo ژاپنی +GenericName[fi]=Japanilainen PuyoPuyo-tyylinen peli +GenericName[fr]=Jeu japonais dans le style de PuyoPuyo +GenericName[he]=חיקוי PuyoPuyo יפני +GenericName[hr]=Igra poput japanskog PuyoPuyo +GenericName[hu]=PuyoPuyo-szerű játék +GenericName[is]=Leikur sem líkist Japönskum PuyoPuyo leik +GenericName[it]=Gioco simile al giapponese PuyoPuyo +GenericName[ja]=プヨプヨã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ដូច PuyoPuyo របស់​ជប៉ុន +GenericName[lv]=SpÄ“le lÄ«dzÄ«ga JapÄņu PuyoPuyo +GenericName[mk]=Игри на Ñлична на јапонÑката Пујо-пујо +GenericName[nb]=Japansk PuyoPuyo-lignende spill +GenericName[nds]=Japaansch Puyo-Puyo-liek Brettspeel +GenericName[ne]=जापानी पà¥à¤¯à¥‹à¤ªà¥à¤¯à¥‹ जसà¥à¤¤à¥‹ खेल +GenericName[nl]=Japans PuyoPuyo-achtig spel +GenericName[nn]=Japansk PuyoPuyo-liknande spel +GenericName[pl]=Gra typu PuyoPuyo +GenericName[pt]=Jogo tipo PuyoPuyo +GenericName[pt_BR]=Jogo parecido com PuyoPuyo japonês +GenericName[ru]=Роковые Ñйца +GenericName[se]=JapanalaÅ¡ PuyoPuyo-lágan speallu +GenericName[sk]=Japonská hra typu PuyoPuyo +GenericName[sl]=Igra, podobna japonskemu PuyoPuyo +GenericName[sr]=Игра налик на јапанÑки PuyoPuyo +GenericName[sr@Latn]=Igra nalik na japanski PuyoPuyo +GenericName[sv]=Japanskt PuyoPuyo-liknande spel +GenericName[ta]=ஜபà¯à®ªà®¾à®©à®¿à®¯ பà¯à®¯à¯‹-பà¯à®¯à¯‹ போனà¯à®± விளையாடà¯à®Ÿà¯ +GenericName[uk]=Варіант тетріÑу Пуйо-Пуйо +GenericName[zh_TW]=類似日本 PuyoPuyo éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kfouleggs/kfouleggs.kcfg b/kfouleggs/kfouleggs.kcfg new file mode 100644 index 00000000..685e29e1 --- /dev/null +++ b/kfouleggs/kfouleggs.kcfg @@ -0,0 +1,60 @@ + + + + + + -2.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 0.0 + -100.0 + 0.0 + + + + 20.0 + 0.0 + 100.0 + + + + 50.0 + 0.0 + 100.0 + + + 0 + 0 + 10 + + + + 80.0 + 0.0 + 100.0 + + + 0 + 0 + 10 + + + diff --git a/kfouleggs/kfouleggsui.rc b/kfouleggs/kfouleggsui.rc new file mode 100644 index 00000000..f4fb0d59 --- /dev/null +++ b/kfouleggs/kfouleggsui.rc @@ -0,0 +1,27 @@ + + + + + + &Multiplayer + + + + + + + + + + + + + + + + + + + + + diff --git a/kfouleggs/main.cpp b/kfouleggs/main.cpp new file mode 100644 index 00000000..4fbee4c7 --- /dev/null +++ b/kfouleggs/main.cpp @@ -0,0 +1,92 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include + +#include "common/inter.h" +#include "common/highscores.h" +#include "prefs.h" +#include "piece.h" + +//----------------------------------------------------------------------------- +const MPGameInfo MP_GAME_INFO = { + "004", // multiplayer id (increase when incompatible changes are made) + 4, // max nb local games + 500, // interval + true, // IA allowed + 0, 0 // no setting slots +}; + +const MainData MAIN_DATA = { + "kfouleggs", + I18N_NOOP("KFoulEggs"), + I18N_NOOP("KFoulEggs is an adaptation of the well-known\n(at least in " + "Japan) PuyoPuyo game"), + "http://kfouleggs.sourceforge.net/", + I18N_NOOP("Puyos"), + "2.1.11", + "2.1.11 (12 September 2004)" +}; + +const uint HISTOGRAM_SIZE = 8; +const uint HISTOGRAM[HISTOGRAM_SIZE] = { + 1, 5000, 10000, 20000, 50000, 100000, 200000, 300000 +}; + +const BaseBoardInfo BASE_BOARD_INFO = { + 6, 15, // width - height + true, // with pieces + + 150, // before remove time + 10, // after removed time + 3, // nb toggles + 5, // nb partial fall stages + + 0, // nb arcade stages + + HISTOGRAM, HISTOGRAM_SIZE, false, // score is not bound +}; + +const CommonBoardInfo COMMON_BOARD_INFO = { + 1000, // base time + 10, // drop down time + 10, // before glue time + 10, // after glue time + 10, // after gift time + 3, // nb bump stages + + 100, // nb removed to level + 5, 6, 800, 2000 // nb leds, max to send, shower timeout, pool timeout +}; + +FEFactory::FEFactory() + : CommonFactory(MAIN_DATA, BASE_BOARD_INFO, COMMON_BOARD_INFO) +{} + +BaseInterface *FEFactory::createInterface(QWidget *parent) +{ + return new Interface(MP_GAME_INFO, parent); +} + + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + KHighscore::init(MAIN_DATA.appName); + FEFactory fef; + fef.init(argc, argv); + + FEPieceInfo pieceInfo; + CommonHighscores highscores; + (void) Prefs::self(); // Create preferences + + if ( kapp->isRestored() ) RESTORE(FEMainWindow) + else { + FEMainWindow *mw = new FEMainWindow; + kapp->setMainWidget(mw); + mw->show(); + } + return kapp->exec(); +} diff --git a/kfouleggs/main.h b/kfouleggs/main.h new file mode 100644 index 00000000..5831d546 --- /dev/null +++ b/kfouleggs/main.h @@ -0,0 +1,34 @@ +#ifndef FE_MAIN_H +#define FE_MAIN_H + +#include "common/main.h" +#include "common/factory.h" +#include "board.h" +#include "ai.h" +#include "field.h" + + +//----------------------------------------------------------------------------- +class FEFactory : public CommonFactory +{ + public: + FEFactory(); + + protected: + virtual BaseBoard *createBoard(bool graphic, QWidget *parent) + { return new FEBoard(graphic, parent); } + virtual BaseField *createField(QWidget *parent) + { return new FEField(parent); } + virtual BaseInterface *createInterface(QWidget *parent); + virtual AI *createAI() { return new FEAI; } +}; + +//----------------------------------------------------------------------------- +class FEMainWindow : public MainWindow +{ + Q_OBJECT + public: + FEMainWindow() { init(); } +}; + +#endif diff --git a/kfouleggs/pics/Makefile.am b/kfouleggs/pics/Makefile.am new file mode 100644 index 00000000..c471f62b --- /dev/null +++ b/kfouleggs/pics/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON= AUTO + diff --git a/kfouleggs/pics/hi128-app-kfouleggs.png b/kfouleggs/pics/hi128-app-kfouleggs.png new file mode 100644 index 0000000000000000000000000000000000000000..a912985802bf383cea03c614fe4308dc4390fd29 GIT binary patch literal 17630 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 RY?uH5002ovPDHLkV1jHt1r7iJ literal 0 HcmV?d00001 diff --git a/kfouleggs/pics/hi16-app-kfouleggs.png b/kfouleggs/pics/hi16-app-kfouleggs.png new file mode 100644 index 0000000000000000000000000000000000000000..ce66ca75ec6e944f8caa03d9f7e971560df57144 GIT binary patch literal 824 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 Hu0mjfS0*%n literal 0 HcmV?d00001 diff --git a/kfouleggs/pics/hi48-app-kfouleggs.png b/kfouleggs/pics/hi48-app-kfouleggs.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b5fc5a149fb4d81006f7b653166e7426b28ef7 GIT binary patch literal 4003 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 JPDHLkV1mw}rQHAk literal 0 HcmV?d00001 diff --git a/kfouleggs/pics/hi64-app-kfouleggs.png b/kfouleggs/pics/hi64-app-kfouleggs.png new file mode 100644 index 0000000000000000000000000000000000000000..5c4ff35a48c7e28f4ef33c958e4213768e5d5490 GIT binary patch literal 6222 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 + +#include +#include + +#include + +#include "base/board.h" + + +const FEPieceInfo::Form FEPieceInfo::FORM = { + {{ 0, 0}, {-1, 0}, { 0, 0}, { 0, -1}}, + {{ 0, -1}, {-1, -1}, {-1, 0}, {-1, -1}} +}; + +const char *FEPieceInfo::DEFAULT_COLORS[NB_NORM_BLOCK_TYPES + 1] = { + "#64C864", "#64C8C8", "#C86464", "#C864C8", "#C8C8C8" +}; + +QColor FEPieceInfo::defaultColor(uint i) const +{ + if ( i>=nbColors() ) return QColor(); + return QColor(DEFAULT_COLORS[i]); +} + +QString FEPieceInfo::colorLabel(uint i) const +{ + return (i==NB_NORM_BLOCK_TYPES ? i18n("Garbage color:") + : i18n("Color #%1:").arg(i+1)); +} + +void FEPieceInfo::draw(QPixmap *pixmap, uint blockType, uint, + bool lighted) const +{ + QColor col = color(blockType); + if (lighted) col = col.light(); + pixmap->fill(col); +} + +void FEPieceInfo::setMask(QPixmap *pixmap, uint blockMode) const +{ + Q_ASSERT( pixmap->width()==pixmap->height() ); // drawing code assumes that + QBitmap bitmap(pixmap->size(), true); + QPainter p(&bitmap); + p.setBrush(Qt::color1); + p.setPen( QPen(Qt::NoPen) ); + + // base circle + int w = pixmap->width(); + int d = (int)((sqrt(2)-2./3)*w); + QRect cr = QRect(0, 0, d, d); + cr.moveCenter(QPoint(w/2, w/2)); + p.drawEllipse(cr); + + if (blockMode) { + int a = (int)(w/(3.*sqrt(2))); + int ra = 2*w/3+1; + cr = QRect(0, 0, ra, ra); + + // first drawing with color1 + if ( blockMode & BaseBoard::Up ) p.drawRect( 0, 0, w, a); + if ( blockMode & BaseBoard::Right ) p.drawRect(w-a+1, 0, a, w); + if ( blockMode & BaseBoard::Down ) p.drawRect( 0, w-a+1, w, a); + if ( blockMode & BaseBoard::Left ) p.drawRect( 0, 0, a, w); + + // second drawing with color0 + p.setBrush(Qt::color0); + if ( (blockMode & BaseBoard::Up) || (blockMode & BaseBoard::Left) ) { + cr.moveCenter(QPoint(0, 0)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Right) || (blockMode & BaseBoard::Up) ) { + cr.moveCenter(QPoint(w-1, 0)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Down) || (blockMode & BaseBoard::Right) ){ + cr.moveCenter(QPoint(w-1, w-1)); + p.drawEllipse(cr); + } + if ( (blockMode & BaseBoard::Left) || (blockMode & BaseBoard::Down) ) { + cr.moveCenter(QPoint(0, w-1)); + p.drawEllipse(cr); + } + } + + p.end(); + pixmap->setMask(bitmap); +} diff --git a/kfouleggs/piece.h b/kfouleggs/piece.h new file mode 100644 index 00000000..ba90e902 --- /dev/null +++ b/kfouleggs/piece.h @@ -0,0 +1,50 @@ +#ifndef PIECE_H +#define PIECE_H + +#include "base/piece.h" + + +class FEPieceInfo : public GPieceInfo +{ + public: + FEPieceInfo() {} + + virtual uint nbBlocks() const { return NB_BLOCKS; } + virtual uint nbForms() const { return 1; } + virtual uint nbTypes() const + { return NB_NORM_BLOCK_TYPES * NB_NORM_BLOCK_TYPES; } + + virtual const int *i(uint, uint rot) const { return FORM.i[rot]; } + virtual const int *j(uint, uint rot) const { return FORM.j[rot]; } + virtual uint value(uint type, uint n) const + { return (n%2 ? type/4 : type%4); } + virtual uint form(uint) const { return 0; } + virtual uint nbConfigurations(uint type) const + { return ((type%4)==(type/4) ? 2 : 4);} + + virtual uint nbNormalBlockTypes() const { return NB_NORM_BLOCK_TYPES; } + virtual uint nbGarbageBlockTypes() const { return 1; } + virtual uint nbBlockModes() const { return NB_BLOCK_MODES; } + + virtual uint nbColors() const { return NB_NORM_BLOCK_TYPES + 1; } + virtual QString colorLabel(uint i) const; + virtual QColor defaultColor(uint i) const; + + private: + void draw(QPixmap *, uint blockType, uint blockMode, bool lighted) const; + void setMask(QPixmap *, uint blockMode) const; + + enum { NB_BLOCKS = 2, + NB_NORM_BLOCK_TYPES = 4, + NB_BLOCK_MODES = 1+4+6+4+1 }; // all possible connections + + struct Form { + int i[4][NB_BLOCKS]; + int j[4][NB_BLOCKS]; + }; + static const Form FORM; + + static const char *DEFAULT_COLORS[NB_NORM_BLOCK_TYPES + 1]; +}; + +#endif diff --git a/kfouleggs/prefs.kcfgc b/kfouleggs/prefs.kcfgc new file mode 100644 index 00000000..cf32e226 --- /dev/null +++ b/kfouleggs/prefs.kcfgc @@ -0,0 +1,9 @@ +# Code generation options for kconfig_compiler +File=kfouleggs.kcfg +IncludeFiles=common/commonprefs.h +Inherits=CommonPrefs +ClassName=Prefs +Singleton=true +#Mutators=true +#CustomAdditions=true +#Mutators=true diff --git a/kgoldrunner/AUTHORS b/kgoldrunner/AUTHORS new file mode 100644 index 00000000..9d51d2b2 --- /dev/null +++ b/kgoldrunner/AUTHORS @@ -0,0 +1 @@ +Ian Wadham and Marco Krüger diff --git a/kgoldrunner/BUGS b/kgoldrunner/BUGS new file mode 100644 index 00000000..c866690d --- /dev/null +++ b/kgoldrunner/BUGS @@ -0,0 +1,38 @@ +KGoldrunner v2.0 - Known Problems +--------------------------------- + +1. Development of the KDE 1 version of KGoldrunner has been discontinued. + There is still a tarball for it on "apps.kde.com" under "kgoldrunner". + +2. Now that KGoldrunner v2.0 has been re-written as a KDE 3 application + it is no longer portable to non-KDE systems, however a portable + (non-KDE) version of KGoldrunner, based on Qt 3, has been written + and is currently under test. + +3. KGoldrunner 2.0 is not designed to work with KDE 2. It has been tested with + KDE 3.1.1 and Qt 3.1.1. + +4. In KGoldrunner 2.0, the KDE library produces the following error message + occasionally when KDialogbase or KMessageBox is run: + + WARNING: KDE detected X Error: BadMatch (invalid parameter attributes) + \x08 Major opcode: * + + This apppears to be harmless, i.e. all the desired dialog entries are + received by the application and the messages appear on the screen. One + way the X error message can be forced to appear is to run "exec()" twice on + one dialog object, e.g, as in a data-entry validation loop: + + while (dialogName->exec() == QDialog::Accepted) { + if validation fails + issue error message; + continue; + else + break; + } + + Maybe this is a bug in KDE 3.1.1 and maybe it is already fixed in later KDE. + It happens if you do "exec()" twice on an empty KDialogbase object (i.e. one + with just OK and Cancel buttons and a caption), so I don't think KGoldrunner + is responsible. Also, the "while ... exec()" loop works fine with QDialog + objects and always has. diff --git a/kgoldrunner/COPYING b/kgoldrunner/COPYING new file mode 100644 index 00000000..0b84a43f --- /dev/null +++ b/kgoldrunner/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kgoldrunner/ChangeLog b/kgoldrunner/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/kgoldrunner/Doxyfile b/kgoldrunner/Doxyfile new file mode 100644 index 00000000..e76fadd7 --- /dev/null +++ b/kgoldrunner/Doxyfile @@ -0,0 +1,232 @@ +# Doxyfile 1.3.2-Gideon + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = kgoldrunner +PROJECT_NUMBER = 0.1 +OUTPUT_DIRECTORY = +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = /home/ianw/KGR_2/kgoldrunner +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.C \ + *.H \ + *.tlh \ + *.diff \ + *.patch \ + *.moc \ + *.xpm +RECURSIVE = yes +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = yes +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/kgoldrunner/INSTALL b/kgoldrunner/INSTALL new file mode 100644 index 00000000..aed967ab --- /dev/null +++ b/kgoldrunner/INSTALL @@ -0,0 +1,197 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile, install and uninstall this package is: + + 0. Make sure you uncompress the correct release file for your system. + File "kgoldrunner-2.0.tar.gz" is for use with Qt 3.1.1 library + and KDE 3.1.1 desk top. Also, if you want to do step 3 (install), + make sure you are logged in as "root", the superuser, or use `su root'. + + Some earlier releases, on "http://apps.kde.com", are file + "kgoldrunner-1.0-kde1.tar.gz", for use with Qt 1.x library and + KDE 1.x desk top, and "kgoldrunner-1.0-kde2.tar.gz", for Qt 2.x + library and KDE 2.x desk top. + + 1. `cd' to the top directory containing the file "configure.in" and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Type `make install' to install program, data files and documentation + in the KDE desktop "Games/Arcade" area (working as "root"). + + 4. Log out of or "exit" from "root", then, logged in as yourself, + type `kgoldrunner' to start the game. It will then check that all + the files it requires are in the right places and will report any + problems. For further details on installation, see the online help + documentation section on "Installation" or see the README file if + installation has failed and you cannot run `kgoldrunner'. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. To uninstall the package, type `make uninstall' (working as "root"). + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. + diff --git a/kgoldrunner/Makefile.am b/kgoldrunner/Makefile.am new file mode 100644 index 00000000..48d41ca5 --- /dev/null +++ b/kgoldrunner/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src gamedata diff --git a/kgoldrunner/NEWS b/kgoldrunner/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/kgoldrunner/README b/kgoldrunner/README new file mode 100644 index 00000000..0bca08a4 --- /dev/null +++ b/kgoldrunner/README @@ -0,0 +1,36 @@ +KGoldrunner v2.0 - README + +Ian Wadham +Marco Krüger +---------------------------------------------------------------------- + +KGoldrunner is a game in which you, the hero, have to run through a maze +of ladders, bricks, concrete and climbing bars to collect gold nuggets, +while being pursued by enemies. When you have collected all the gold, +you can move to the next level of the game by climbing to the top of the +playing area. This version has several games (sets of levels) of +varying difficulty and includes a Tutorial game. It also has an editor +you can use to make up your own games or vary the released games. You +use the mouse to guide the hero and dig holes in bricks. + +KGoldrunner is based on the Lode Runner (TM) game written in the USA +by Doug Smith in 1983 for the Apple II and Commodore 64 computers. + +See the file INSTALL for instructions on how to compile, build and +install KGoldrunner. + +The source code and other files are essentially the same for KDE2/Qt2 +and KDE3/Qt3, depending on the -DQT3 compiler flag, but the configuration +and make files are quite different. WARNING: This version has been built, +compiled and tested ONLY with KDE 3.1.1 and Qt 3.1.1. + +Just in case you cannot build and install KGoldrunner ... +--------------------------------------------------------- + +The sub-directory "kgoldrunner/docs/en" contains the KGoldrunner User's +Guide in English, which is made available via online help in KGoldrunner +when installed, but you can read it directly with your browser if you have +not yet installed KGoldrunner. It contains details of how to play the game, +including screen shots. The Installation and Technical Details sections +may be useful if you are having difficulty installing KGoldrunner. + diff --git a/kgoldrunner/TODO b/kgoldrunner/TODO new file mode 100644 index 00000000..e9c961e9 --- /dev/null +++ b/kgoldrunner/TODO @@ -0,0 +1,34 @@ +KGoldrunner v2.0 - TODO +----------------------- + +Some ideas for the future:- + + 1. Port KGoldrunner to other O/S's, such as MS Windows and Mac. + Although KGoldrunner v2.0, as released with the KDE desktop, is + a KDE-based application and is not portable, KGoldrunner v2.0-qt, + as configured for Qt 3.x library and compiled with -DKGR_PORTABLE, + has very small dependence on the KDE desktop (i.e. locations of + directories only). Because Qt 3 is intended to be portable to + MS Windows and Mac, so should KGoldrunner v2.0-qt be. However Qt + development licences are NOT free on non-UNIX type O/S's. + + 2. Add sounds for hero actions such as falling, collecting a nugget, + getting killed and completing a level. + + 3. Design and program extra game objects, such as movable bricks, + water, teleportation exits and aquatic enemies. + + Movable bricks would be pushed up or sideways or would fall into + an empty space, to change the maze layout. As with fall-through + bricks, they should look just like ordinary bricks. + + With water, the hero could jump in and swim across the surface, at + a slower speed than on land, but the land enemies would not follow + him. If he has to dive below the surface, he can only hold his + breath for a limited time before he dies. + + Teleportation exits would be connected in pairs. If you go through + one exit, you reappear at the other and vice-versa. + + Aquatic enemies would just make life more difficult for the hero + in the water. Maybe there could be amphibian enemies too. diff --git a/kgoldrunner/VERSION b/kgoldrunner/VERSION new file mode 100644 index 00000000..e3b297a4 --- /dev/null +++ b/kgoldrunner/VERSION @@ -0,0 +1 @@ +KGoldrunner v2.0-kde3 diff --git a/kgoldrunner/gamedata/Makefile.am b/kgoldrunner/gamedata/Makefile.am new file mode 100644 index 00000000..a7fcca1e --- /dev/null +++ b/kgoldrunner/gamedata/Makefile.am @@ -0,0 +1,13 @@ +gamedatadir = $(kde_datadir)/kgoldrunner/system +gamedata_DATA = games.dat hi_level.dat hi_plws.dat hi_plwv.dat hi_wad.dat + +# Untar the file levels.tar and install levels/*.grl files. The "chown" and +# "chmod" ensure that installer owns files: even if installer is "root". The +# "|| true" commands make everything succeed when the installer is NOT "root". +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(gamedatadir) + pwd=`cd $(srcdir) && pwd` ;\ + cd $(DESTDIR)$(gamedatadir) && tar -xf $$pwd/levels.tar ;\ + chown -R root:root levels 2> /dev/null || true ;\ + chmod 644 levels/* 2> /dev/null || true + diff --git a/kgoldrunner/gamedata/games.dat b/kgoldrunner/gamedata/games.dat new file mode 100644 index 00000000..0834f587 --- /dev/null +++ b/kgoldrunner/gamedata/games.dat @@ -0,0 +1,14 @@ +100 T plws Initiation +These 100 levels make an excellent introductory game, as well as a good opportunity for experts to build up high scores. They were composed by Peter Wadham and use traditional playing rules.\n\nThe last few levels are very hard, but if you are looking for even more of a challenge, have a go at 'Vengeance of Peter W' .... ;-) .... +022 T wad Challenge +These tricky little levels were composed by Peter, Simon, Genevieve and their father Ian Wadham. They use traditional playing rules. Enjoy! .... ;-) .... +020 T plwv Vengeance of Peter W +Gooood luck !!\nMwarrhh hwwarrrr haarrrr !!! +018 K level KGoldrunner +These levels were composed by Marco Krüger, the original author of the KGoldrunner program, and some of his friends and contributors. They use KGoldrunner rules. The enemies run fast and have an aggressive search strategy. Enjoy! .... :-) .... +015 K sot State Of Terror +Just when you thought it was safe ... fifteen levels of pure terror, using those quirky-yet-fast KGoldrunner rules.\n\nComposed by Stuart Popejoy - spopejoy@panix.com +007 T tute Tutorial +This tutorial is a collection of easy levels that teaches you the rules of KGoldrunner and helps you develop the skills you need to get started. Each level has a brief explanation, then you play .....\n\nWhen you move on to play more advanced levels, you will find that KGoldrunner combines action, strategy and puzzle solving --- all in one game. +005 T tutea Advanced Tutorial +This tutorial is preparation for some of the things you might find in the middle levels of the 'Initiation' game. Enjoy .... diff --git a/kgoldrunner/gamedata/hi_level.dat b/kgoldrunner/gamedata/hi_level.dat new file mode 100644 index 0000000000000000000000000000000000000000..74f042dfef04390c2808fc596aa0c6038ec0cc14 GIT binary patch literal 120 zcmZQzVBq#l%u@(YOvy;hWnkc7VA#2jfq{YFtteB$$iToz*T7KMzyv6Qs!ouBL9iO6 a&Nn{~q|Q*+&`8(V5JMd!NCXJb)d2vQAQp-M literal 0 HcmV?d00001 diff --git a/kgoldrunner/gamedata/hi_plws.dat b/kgoldrunner/gamedata/hi_plws.dat new file mode 100644 index 0000000000000000000000000000000000000000..6f62932f02824346b4e1cb2e00df78e9512c07dc GIT binary patch literal 40 vcmZQzVBq#l%u@(YOvy;hWnl1QkX&5Fz`(%oR+On=WME*VYhb8rV8Q?ZuJ#B` literal 0 HcmV?d00001 diff --git a/kgoldrunner/gamedata/hi_plwv.dat b/kgoldrunner/gamedata/hi_plwv.dat new file mode 100644 index 0000000000000000000000000000000000000000..46b0d30fde16263c794b49bcb1be340d735534b8 GIT binary patch literal 40 vcmZQzVBq#l%u@(YOvy;hWnkcCU>3N`z`(#CT$-m~WME*ZYiOivWXu2nqudAG literal 0 HcmV?d00001 diff --git a/kgoldrunner/gamedata/hi_wad.dat b/kgoldrunner/gamedata/hi_wad.dat new file mode 100644 index 0000000000000000000000000000000000000000..5e3779dad32c02c1d629843bcd521c80180c03c0 GIT binary patch literal 160 zcmZQzVBq#l%u@(YOvy;hWnkcEVE)s@0Hl5M^AwB>3=DM*jdYDnfFh{s_!t;t!a?eS eOA+df4KUO(Gcefw1*s!o9#EZtEV?>lLk0jIULP0$ literal 0 HcmV?d00001 diff --git a/kgoldrunner/gamedata/levels.tar b/kgoldrunner/gamedata/levels.tar new file mode 100644 index 0000000000000000000000000000000000000000..851ff50ea19c24a931af75e5fe1862d338402b9d GIT binary patch literal 317440 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
  • ab|4+dG*Q@wHtpDA_$GswR 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 literal 0 HcmV?d00001 diff --git a/kgoldrunner/kgoldrunner.kdevelop b/kgoldrunner/kgoldrunner.kdevelop new file mode 100644 index 00000000..8dcc793b --- /dev/null +++ b/kgoldrunner/kgoldrunner.kdevelop @@ -0,0 +1,142 @@ + + + + Ian Wadham + ianw2@optusnet.com.au + 1.0.4-kde3 + KDevKDEAutoProject + C++ + + C++ + Code + Qt + KDE + + . + false + + + + + + src/kgoldrunner + default + + + src/kgoldrunner + + false + true + + + + + + + + false + 1 + false + + + + + + + + + + + + + false + *.o,*.lo,CVS + + + + + gtk + gnustep + python + php + perl + + + /home/ianw/KGR_2/kgoldrunner/html/ + /home/ianw/KGR_2/kgoldrunner/html/ + + + + + libtool + + + true + false + false + true + + + false + true + + + + + + + + + + + + -f + + + + -dP + -f + -C -d -P + -u3 -p + + + + + + false + + + .h + .cpp + true + + + + true + 2 + + + false + false + + kgoldrunner + 1.0.4-kde3 + + + + + + + + + false + false + false + 0 + false + false + false + false + + + diff --git a/kgoldrunner/kgoldrunner.lsm b/kgoldrunner/kgoldrunner.lsm new file mode 100644 index 00000000..b0a6746d --- /dev/null +++ b/kgoldrunner/kgoldrunner.lsm @@ -0,0 +1,24 @@ +Begin3 +Title: KGoldrunner +Version: 2.0 +Entered-date: 12AUG2003 +Description: KGoldrunner is an action game where the hero runs through a + maze and dodges enemies. You must guide him with the mouse + and collect all the gold nuggets, then you can climb up into + the next level. Your only weapon is to dig holes in the + floor and trap the enemies. + + KGoldrunner combines action, strategy and puzzle solving + --- all in one game. It is based on Lode Runner (TM), an + all-time classic computer game. It has a Tutorial and + over 100 levels: some easy, some very hard. +Keywords: arcade game Lode Runner KGoldrunner Loderunner Goldrunner +Author: ianw2@optusnet.com.au (Ian Wadham) + grisuji@gmx.de (Marco Krüger) +Maintained-by: ianw2@optusnet.com.au (Ian Wadham) +Primary-site: +Alternate-site: +Original-site: +Platforms: Linux with KDE 3 Desktop +Copying-policy: GPL +End diff --git a/kgoldrunner/src/KGoldrunner.desktop b/kgoldrunner/src/KGoldrunner.desktop new file mode 100644 index 00000000..6e22384a --- /dev/null +++ b/kgoldrunner/src/KGoldrunner.desktop @@ -0,0 +1,122 @@ +# KDE Config File +[Desktop Entry] +Name=KGoldrunner +Name[ar]=لعبة البحث عن الذهب (KGoldrunner) +Name[be]=Ð—Ð°Ð»Ð°Ñ‚Ð°Ñ Ð»Ñ–Ñ…Ð°Ð¼Ð°Ð½ÐºÐ° +Name[bn]=কে-গোলà§à¦¡à¦°à¦¾à¦¨à¦¾à¦° +Name[fr]=KGoldRunner +Name[hi]=के-गोलà¥à¤¡à¤°à¤¨à¤° +Name[hr]=KPotraga za zlatom +Name[ne]=केडीई गोलà¥à¤¡ रनर +Name[pa]=ਕੇ-ਗੋਲਡ ਰਨਰ +Name[sv]=Kgoldrunner +Name[ta]=கேதஙà¯à®•à®®à¯ ஓடà¯à®ªà®µà®°à¯ +Name[tg]=KÒ¶Ó¯Ñндаи тилло +Name[uk]=Золотошукач +Name[zh_TW]=KGoldrunner 撿金塊 +GenericName=Action & Puzzle Solving Game +GenericName[be]=ÐÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· галаваломкамі +GenericName[bg]=Бегач за злато +GenericName[bn]=অà§à¦¯à¦¾à¦•à¦¶à¦¨ আর ধাà¦à¦§à¦¾à¦ সমাধানের খেলা +GenericName[bs]=Igra akcije i rjeÅ¡avanja zagonetki +GenericName[ca]=Joc d'acció i resolució de trencaclosques +GenericName[cs]=ŘeÅ¡ení hádanek a akce +GenericName[cy]=Gêm Gynhyrfus o Ddatrys Posau +GenericName[da]=Handlings & mysteriums løsningsspil +GenericName[de]=Spiel zum Rätsellösen +GenericName[el]=Παιχνίδι δÏάσης και επίλυσης παζλ +GenericName[eo]=Agada kaj enigmosolva ludo +GenericName[es]=Juego de acción y resolución de rompecabezas +GenericName[et]=Põnev mõistatuste lahendamise mäng +GenericName[eu]=Ekintza eta puzzleen ebazpen jokoa +GenericName[fa]=بازی کنشی Ùˆ حل معما +GenericName[fi]=Toiminta- & palikkapeli +GenericName[fr]=Jeu de résolution de puzzle et d'actions +GenericName[he]=משחק ×קשן ופתרון חידות +GenericName[hr]=Igra akcije i rjeÅ¡avanja zagonetki +GenericName[hu]=Ãœgyességi és logikai +GenericName[is]=Leysa þrautir og læti +GenericName[it]=Gioco di azione e rompicapo +GenericName[ja]=アクションã¨è¬Žè§£ãゲーム +GenericName[km]=សកម្មភាព Puzzle ដោះ​ស្រាយ​ស្បែង +GenericName[ko]=ì•¡ì…˜ ë° í¼ì¦ 풀기 게임 +GenericName[lt]=Veiskmo ir dÄ—lionių žaidimas +GenericName[lv]=DarbÄ«bas un mÄ«klu atrisinÄÅ¡anas spÄ“le +GenericName[mk]=Игра Ñо акција и решавање Ñложувалка +GenericName[nb]=Handlingsfylt grublespill +GenericName[nds]=Akschoon- un Radelsspeel +GenericName[ne]=काम र पजल समाधान गरà¥à¤¨à¥‡ खेल +GenericName[nl]=Actie- en puzzelspel +GenericName[nn]=Handlingsfylt grublespel +GenericName[pl]=UkÅ‚adanka zrÄ™cznoÅ›ciowa +GenericName[pt]=Jogo de Acção e Puzzles +GenericName[pt_BR]=Jogo de ação e quebra-cabeça +GenericName[ru]=ЗолотоиÑкатель +GenericName[sk]=AkÄná a skladaÄková hra +GenericName[sl]=Igra akcije in reÅ¡evanja ugank +GenericName[sr]=Ðкциона игра Ñа решавањем загонетки +GenericName[sr@Latn]=Akciona igra sa reÅ¡avanjem zagonetki +GenericName[sv]=Action och pussellösningsspel +GenericName[ta]=செயல௠& பà¯à®¤à®¿à®°à¯ விடà¯à®µà®¿à®•à¯à®•à¯à®®à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Тактичка гра-головоломка +GenericName[zh_CN]=åŠ¨ä½œä¸Žè§£è¿·æ¸¸æˆ +GenericName[zh_TW]=動作與解謎éŠæˆ² +Type=Application +Exec=kgoldrunner +Icon=kgoldrunner +DocPath=kgoldrunner/index.html +Comment=A game of action and puzzle-solving +Comment[ar]=لعبة حركة وحلَ ألغاز +Comment[be]=ÐÐºÑ‚Ñ‹ÑžÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ð· галаваломкамі +Comment[bg]=Екшън игра Ñ Ñ€ÐµÑˆÐ°Ð²Ð°Ð½Ðµ на главоблъÑканици +Comment[bn]=ধাà¦à¦§à¦¾à¦ ও অà§à¦¯à¦¾à¦•à¦¶à¦¨ মেশানো à¦à¦• দারà§à¦£ খেলা +Comment[bs]=Akciona igra sa rjeÅ¡avanjem zagonetke +Comment[ca]=Un joc d'acció i solució de trencaclosques +Comment[cs]=AkÄní hra s Å™eÅ¡ením hádanek +Comment[cy]=Gêm gynhyrfus o ddatrys posau +Comment[da]=Et spil med handling og problemløsning +Comment[de]=Aktionsreiches Spiel für Rätsellöser +Comment[el]=Ένα παιχνίδι δÏάσης και λÏσης γÏίφων +Comment[es]=Un juego de acción y resolución de rompecabezas +Comment[et]=Põnev mõistatuste lahendamise mäng +Comment[eu]=Ekintzarako eta puzzleen ebazpenerako jokoa +Comment[fa]=یک بازی کنشی Ùˆ حل معما +Comment[fi]=Toiminta- ja pulmanratkaisupeli +Comment[fr]=Jeu d'action et de résolution d'énigmes +Comment[gl]=Un xogo de acción e resolución de puzzles +Comment[he]=משחק ×קשן ופתרון חידות +Comment[hi]=पहेलियाठहल करने और सकà¥à¤°à¤¿à¤¯à¤¤à¤¾ का à¤à¤• खेल +Comment[hr]=Igra akcije i rjeÅ¡avanja zagonetki +Comment[hu]=Ãœgyességi játék rejtvényekkel tarkítva +Comment[is]=Leikur sem reynir á hraða og kænsku +Comment[it]=Un gioco d'azione e un rompicapo +Comment[ja]=アクションã¨è¬Žè§£ãゲーム +Comment[km]=ល្បែង​នៃ​សកម្មភាព និង​ការ​ដោះ​ស្រាយ puzzle +Comment[ko]=ì•¡ì…˜ ë° í¼ì¦ 풀기 게임 +Comment[lt]=Veiksmo ir galvosÅ«kių žaidimas +Comment[lv]=DarbÄ«bas un loÄ£ikas spÄ“le +Comment[mk]=Игра Ñо акција и решавање на загатки +Comment[nb]=Et handlingsfylt spill med innlagte gÃ¥ter +Comment[nds]=Een Speel mit veel Akschoon un Radels +Comment[ne]=काम र पजल समाधान गरà¥à¤¨à¥‡ खेल +Comment[nl]=Een spel met veel actie en puzzels +Comment[nn]=Eit handlingsfylt spel med innlagde gÃ¥ter +Comment[pl]=Gra akcji i rozwiÄ…zywania ukÅ‚adanek +Comment[pt]=Um jogo de acção e resolução de quebra-cabeças +Comment[pt_BR]=Um jogo de ação e quebra-cabeça +Comment[ro]=Un joc de acÅ£iune ÅŸi rezolvare de puzzle +Comment[ru]=Решай головоломки на ходу +Comment[sk]=Kombinovaná akÄná a logická hra +Comment[sl]=Igra akcije in reÅ¡evanja ugank +Comment[sr]=Игра акције и решавања загонетки +Comment[sr@Latn]=Igra akcije i reÅ¡avanja zagonetki +Comment[sv]=Ett spel med action och pussellösning +Comment[ta]=செயலà¯à®ªà®¾à®Ÿà¯ மறà¯à®±à¯à®®à¯ பà¯à®¤à®¿à®°à¯ விடà¯à®µà®¿à®•à¯à®•à¯à®®à¯ விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии амалиёт ва ҳалли муаммоҳо +Comment[tr]=Aksiyon ve bulmaca çözümlerini barındıran bir oyun +Comment[uk]=Ð’Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ Ð³Ð¾Ð»Ð¾Ð²Ð¾Ð»Ð¾Ð¼Ð¾Ðº на ходу +Comment[zh_CN]=åŠ¨ä½œä¸Žè§£è¿·æ¸¸æˆ +Comment[zh_TW]= 一個動作和解謎的éŠæˆ² +Comment[zu]=Umdlalo womnyakazo kanye neyokuxazulula i puzzle +Terminal=false +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kgoldrunner/src/Makefile.am b/kgoldrunner/src/Makefile.am new file mode 100644 index 00000000..b243765b --- /dev/null +++ b/kgoldrunner/src/Makefile.am @@ -0,0 +1,42 @@ +## Makefile.am for kgoldrunner + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kgoldrunner + +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +# the library search path. +kgoldrunner_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +# the libraries to link against. +kgoldrunner_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) $(LIB_KDEPRINT) +kgoldrunner_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +# which sources should be compiled for kgoldrunner +kgoldrunner_SOURCES = kgoldrunner.cpp kgrcanvas.cpp kgrfigure.cpp kgrgame.cpp kgrobject.cpp main.cpp kgrdialog.cpp + +# these are the headers for your project +noinst_HEADERS = enemy1.xpm enemy2.xpm hero.xpm kgoldrunner.h kgrcanvas.h kgrconsts.h kgrdialog.h kgrfigure.h kgrgame.h kgrobject.h kgraphics.h + +# client stuff + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kgoldrunner.pot + +KDE_ICON = kgoldrunner + +# this is where the kdelnk file will go +xdg_apps_DATA = KGoldrunner.desktop + +# this is where the XML-GUI resource file goes +rcdir = $(kde_datadir)/kgoldrunner +rc_DATA = kgoldrunnerui.rc + +AM_CXXFLAGS = -DQT3 + diff --git a/kgoldrunner/src/data_messages.cpp b/kgoldrunner/src/data_messages.cpp new file mode 100644 index 00000000..838db2a8 --- /dev/null +++ b/kgoldrunner/src/data_messages.cpp @@ -0,0 +1,608 @@ +/* This is a dummy *.cpp program that contains strings extracted from + KGoldrunner game data files. It is not intended to be compiled or + executed. Its only function is to hold the strings in a place where + they will be re-formatted into a .pot file for the translators. +*/ + +#include + +dont_call_me() { + +i18n("TRANSLATORS: Please see the notes in the data_messages.cpp file."); + +// NOTES: Strings in this file originate from KGoldrunner DATA files, +// as installed in $KDEDIR/share/apps/kgoldrunner/system. They are +// names and descriptions of games and names and hints for levels. The +// C++ comments indicate which data file the strings came from. You +// may sometimes need to look at the corresponding game or level graphics +// to work out a good translation (e.g. Initiation 25, Mongolian Horde). + +// To ease the burden on translators, this file does not include all the +// data messages. For example, the hints on championship games are not +// included. A player that good should not need a hint and maybe has no +// need of a translation ... :-) The most urgent translations would be +// names and hints for the Tutorial games, then the game names and +// descriptions (as appearing in the main dialog box) and then the other +// level names and hints. + +// From levels/tute001.grl (in levels.tar). +i18n("Hi !!"); +i18n("Hi ! Welcome to KGoldrunner ! The idea of the game is to pick up all the gold nuggets, then climb to the top of the playing area and move up to the next level. A hidden ladder will appear as you collect the last nugget." +"\n" +"\nThe hero (the green figure) is your deputy. To collect the nuggets, just point the mouse where you want him to go. At first gravity takes over and he falls ..." +); + +// From levels/tute002.grl (in levels.tar). +i18n("Navigation"); +i18n("This is an exercise in moving around. Follow the track of gold nuggets until the ladder pops up at the right. The hero can only follow the mouse along simple paths (like _ | L or U), so be careful not to get too far ahead of him." +"\n" +"\nDANGER: Try not to fall off the ladder or bar into the concrete pit at the bottom right. If you do get trapped there, the only way out is to kill the hero (press key Q for quit) and start the level again." +); + +// From levels/tute003.grl (in levels.tar). +i18n("Digging"); +i18n("Now you have to dig to get the gold! Just use the left and right mouse buttons to dig left or right of the hero's position. The hero can then jump into and through the hole he has dug. He can also dig several holes in a row and run sideways through the dug holes. Be careful though. After a while the holes close up and you can get trapped and killed." +"\n" +"\nIn the third box down, you have to dig two holes, jump in and quickly dig one more, to get through two layers. On the right, you have to dig three, then two then one to get through. There are also two little puzzles to work out along the way. Good luck!" +"\n" +"\nBy the way, you can dig through brick, but not concrete." +); + +// From levels/tute004.grl (in levels.tar). +i18n("You Have ENEMIES !!!"); +i18n("Well, it's been nice and easy up to now, but the game would be no fun without enemies. They are after the gold too: worse still they are after you! You die if they catch you, but maybe you will have a few lives left and can start again." +"\n" +"\nYou can handle enemies by running away, digging a hole or luring them into part of the playing area where they get stranded." +"\n" +"\nIf an enemy falls into a hole, he gives up any gold he is carrying, then gets stuck in the hole for a time and climbs out. If the hole closes while he is in it, he dies and reappears somewhere else on the screen. You can deliberately kill enemies by digging several holes in a row." +"\n" +"\nMore importantly, you can run over an enemy's head. You must do that right at the start of this level. Dig a hole, trap the enemy, wait for him to fall all the way in, then run over him, with the other enemy in hot pursuit..." +); + +// From levels/tute005.grl (in levels.tar). +i18n("Bars"); +i18n("You can move horizontally along bars (or poles), but if you move down you will let go and fall .... Also, note that you can collect gold by falling onto it." +); + +// From levels/tute006.grl (in levels.tar). +i18n("False Bricks"); +i18n("Some of the bricks in this level are not what they seem. If you walk onto them you fall through. The enemies fall through them too." +); + +// From levels/tute007.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/tutea001.grl (in levels.tar). +i18n("Bars and Ladders"); +i18n("There's nowhere to dig, so you must dodge the enemies and avoid falling to the concrete at the wrong time. Try to keep the enemies together." +"\n" +"\nIf an enemy has a gold outline, he is holding a nugget. He might drop it as he runs over the concrete or maybe at the top of a ladder ...... patience, patience !!" +); + +// From levels/tutea002.grl (in levels.tar). +i18n("To kill ....."); +i18n("You cannot get up to the gold, so you must get the enemies to bring it down to you. But how do you get them to keep going back?" +"\n" +"\nIf you are feeling stressed, you can hit the P or Esc key and take a break. Also, you can use the Settings menu to slow down the action." +); + +// From levels/tutea003.grl (in levels.tar). +i18n("... Or not to kill?"); +i18n("It is best not to kill the enemy. Try it and you will find out why ... Heh, heh, heh !! ... ;-)" +"\n" +"\nIf you do kill him unintentionally, before you have collected the gold at the top left, you can still finish the level by digging away the side of the pit he is in." +); + +// From levels/tutea004.grl (in levels.tar). +i18n("Traps"); +i18n("Some of the bricks here are not what they seem. They are known as false bricks, fall-through bricks or traps. If you try to walk on them, you fall through. If the enemies walk on them, they can descend on you without warning. Sometimes you have to fall through a brick to get some gold." +"\n" +"\nAt the start, jump into the concrete pit, dig and drop through. That will get one enemy tied up for a while .... :-)" +); + +// From levels/tutea005.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/level000.grl (in levels.tar). + +// From levels/level001.grl (in levels.tar). + +// From levels/level002.grl (in levels.tar). + +// From levels/level003.grl (in levels.tar). + +// From levels/level004.grl (in levels.tar). + +// From levels/level005.grl (in levels.tar). + +// From levels/level006.grl (in levels.tar). + +// From levels/level007.grl (in levels.tar). + +// From levels/level008.grl (in levels.tar). + +// From levels/level009.grl (in levels.tar). + +// From levels/level010.grl (in levels.tar). + +// From levels/level011.grl (in levels.tar). + +// From levels/level012.grl (in levels.tar). + +// From levels/level013.grl (in levels.tar). + +// From levels/level014.grl (in levels.tar). + +// From levels/level015.grl (in levels.tar). + +// From levels/level016.grl (in levels.tar). + +// From levels/level017.grl (in levels.tar). + +// From levels/level018.grl (in levels.tar). + +// From levels/plws001.grl (in levels.tar). +i18n("Don't Panic"); + +// From levels/plws002.grl (in levels.tar). +i18n("Lust for Gold"); +i18n("When you kill the enemies you can trap them permanently in the pit at top right." +); + +// From levels/plws003.grl (in levels.tar). +i18n("Ladders? Trust me !"); + +// From levels/plws004.grl (in levels.tar). +i18n("Drop In and Say Hello"); + +// From levels/plws005.grl (in levels.tar). +i18n("The Mask"); + +// From levels/plws006.grl (in levels.tar). +i18n("Check for Traps"); + +// From levels/plws007.grl (in levels.tar). +i18n("Take It Easy !"); + +// From levels/plws008.grl (in levels.tar). +i18n("Fall on a Fortune"); + +// From levels/plws009.grl (in levels.tar). +i18n("The Lattice"); + +// From levels/plws010.grl (in levels.tar). +i18n("Shower of Gold"); + +// From levels/plws011.grl (in levels.tar). +i18n("The Foundry"); + +// From levels/plws012.grl (in levels.tar). +i18n("Soft Landings"); + +// From levels/plws013.grl (in levels.tar). +i18n("Unlucky for Some"); + +// From levels/plws014.grl (in levels.tar). +i18n("The Balance"); + +// From levels/plws015.grl (in levels.tar). +i18n("Gold Bars"); + +// From levels/plws016.grl (in levels.tar). +i18n("Hard Row to Hoe"); + +// From levels/plws017.grl (in levels.tar). +i18n("Golden Maze"); + +// From levels/plws018.grl (in levels.tar). +i18n("Delayed Trap"); + +// From levels/plws019.grl (in levels.tar). +i18n("Nowhere to Hide"); + +// From levels/plws020.grl (in levels.tar). +i18n("Watch the Centre"); + +// From levels/plws021.grl (in levels.tar). +i18n("Where to Dig?"); + +// From levels/plws022.grl (in levels.tar). +i18n("Easy Stages"); + +// From levels/plws023.grl (in levels.tar). +i18n("Gold Mesh"); + +// From levels/plws024.grl (in levels.tar). +i18n("Acrobat"); + +// From levels/plws025.grl (in levels.tar). +i18n("Mongolian Horde"); + +// From levels/plws026.grl (in levels.tar). +i18n("Rocky Terrain"); + +// From levels/plws027.grl (in levels.tar). +i18n("Down the Chimney"); + +// From levels/plws028.grl (in levels.tar). +i18n("Space Invader"); + +// From levels/plws029.grl (in levels.tar). +i18n("Winding Road"); + +// From levels/plws030.grl (in levels.tar). +i18n("Light My Fire"); + +// From levels/plws031.grl (in levels.tar). +i18n("Cockroach"); + +// From levels/plws032.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plws033.grl (in levels.tar). +i18n("Speedy"); + +// From levels/plws034.grl (in levels.tar). +i18n("Dig Deep"); + +// From levels/plws035.grl (in levels.tar). +i18n("Zig Zag"); + +// From levels/plws036.grl (in levels.tar). +i18n("Free Fall"); + +// From levels/plws037.grl (in levels.tar). +i18n("Forgotten Gold"); + +// From levels/plws038.grl (in levels.tar). +i18n("Two of Diamonds"); + +// From levels/plws039.grl (in levels.tar). +i18n("Suicide Jump"); + +// From levels/plws040.grl (in levels.tar). +i18n("Easy Access"); + +// From levels/plws041.grl (in levels.tar). +i18n("Gold Braid"); + +// From levels/plws042.grl (in levels.tar). +i18n("Cat's Eyes"); + +// From levels/plws043.grl (in levels.tar). +i18n("Keep 'em Coming"); + +// From levels/plws044.grl (in levels.tar). +i18n("The Funnel"); + +// From levels/plws045.grl (in levels.tar). +i18n("Lattice Maze"); + +// From levels/plws046.grl (in levels.tar). +i18n("Hard Work for Poor Pay"); + +// From levels/plws047.grl (in levels.tar). +i18n("Forked Ladders"); + +// From levels/plws048.grl (in levels.tar). +i18n("Snowing Gold"); + +// From levels/plws049.grl (in levels.tar). +i18n("Left or Right?"); + +// From levels/plws050.grl (in levels.tar). +i18n("Houndstooth"); + +// From levels/plws051.grl (in levels.tar). +i18n("Five Levels"); + +// From levels/plws052.grl (in levels.tar). +i18n("Pitfalls"); + +// From levels/plws053.grl (in levels.tar). +i18n("Get IN There !!"); + +// From levels/plws054.grl (in levels.tar). +i18n("A Steady Climb"); + +// From levels/plws055.grl (in levels.tar). +i18n("Fall-through Lattice"); + +// From levels/plws056.grl (in levels.tar). +i18n("Get me OUT of Here !!"); + +// From levels/plws057.grl (in levels.tar). +i18n("Empty Cellar"); + +// From levels/plws058.grl (in levels.tar). +i18n("The Rose"); + +// From levels/plws059.grl (in levels.tar). +i18n("Lotus Puzzle"); + +// From levels/plws060.grl (in levels.tar). +i18n("Long Drop"); + +// From levels/plws061.grl (in levels.tar). +i18n("Party On !!!"); + +// From levels/plws062.grl (in levels.tar). +i18n("Cross-stitch"); + +// From levels/plws063.grl (in levels.tar). +i18n("Can't Get Up There"); + +// From levels/plws064.grl (in levels.tar). +i18n("They're Everywhere !!!"); + +// From levels/plws065.grl (in levels.tar). +i18n("Rooftops"); + +// From levels/plws066.grl (in levels.tar). +i18n("Tricky Traps"); + +// From levels/plws067.grl (in levels.tar). +i18n("Make Them Work for You"); + +// From levels/plws068.grl (in levels.tar). +i18n("Get Going !!"); + +// From levels/plws069.grl (in levels.tar). +i18n("Three Chimneys"); + +// From levels/plws070.grl (in levels.tar). +i18n("The Archway"); + +// From levels/plws071.grl (in levels.tar). +i18n("Starwave"); + +// From levels/plws072.grl (in levels.tar). +i18n("Amazing Finish"); + +// From levels/plws073.grl (in levels.tar). +i18n("Overcrowding"); + +// From levels/plws074.grl (in levels.tar). +i18n("Pillars"); + +// From levels/plws075.grl (in levels.tar). +i18n("Hopeful Descent"); + +// From levels/plws076.grl (in levels.tar). +i18n("The Rack"); + +// From levels/plws077.grl (in levels.tar). +i18n("Twists and Turns"); + +// From levels/plws078.grl (in levels.tar). +i18n("The Saucer"); + +// From levels/plws079.grl (in levels.tar). +i18n("The Dotted Line"); + +// From levels/plws080.grl (in levels.tar). +i18n("Don't Look Down (1)"); + +// From levels/plws081.grl (in levels.tar). +i18n("Getting Started"); + +// From levels/plws082.grl (in levels.tar). +i18n("Digging Hassle"); + +// From levels/plws083.grl (in levels.tar). +i18n("Easy Middle"); + +// From levels/plws084.grl (in levels.tar). +i18n("Don't Look Down (2)"); + +// From levels/plws085.grl (in levels.tar). +i18n("Which Way?"); + +// From levels/plws086.grl (in levels.tar). +i18n("Don't Look Down (3)"); + +// From levels/plws087.grl (in levels.tar). +i18n("Drop ???"); + +// From levels/plws088.grl (in levels.tar). +i18n("Help !!!"); + +// From levels/plws089.grl (in levels.tar). +i18n("Yorick's Skull"); + +// From levels/plws090.grl (in levels.tar). +i18n("No Mercy"); + +// From levels/plws091.grl (in levels.tar). +i18n("Gold Sandwich"); + +// From levels/plws092.grl (in levels.tar). +i18n("Golden Curtain"); + +// From levels/plws093.grl (in levels.tar). +i18n("Are you spider or fly?"); + +// From levels/plws094.grl (in levels.tar). +i18n("Funny?"); + +// From levels/plws095.grl (in levels.tar). +i18n("Hard Landings"); + +// From levels/plws096.grl (in levels.tar). +i18n("Golden Tower"); + +// From levels/plws097.grl (in levels.tar). +// i18n("Das Boot"); // Not translated: it's the name of a German movie. +i18n("This level is named after the famous German submarine war film and dedicated to Marco Krüger of Berlin, the original author of KGoldrunner." +"\n" +"\nJust one small hint .... if you stand on the right hand end of the boat you can get the enemy to fall towards you .... the rest is up to you !!!!" +); + +// From levels/plws098.grl (in levels.tar). +i18n("Quick ! RUN !!!"); + +// From levels/plws099.grl (in levels.tar). +i18n("Surprise Ending"); + +// From levels/plws100.grl (in levels.tar). +i18n("Diagonal Disaster"); + +// From levels/wad001.grl (in levels.tar). +i18n("Easy Start"); + +// From levels/wad002.grl (in levels.tar). +i18n("Mobile Bricks"); + +// From levels/wad003.grl (in levels.tar). +i18n("The Big Haul"); + +// From levels/wad004.grl (in levels.tar). +i18n("Quick Off The Mark"); + +// From levels/wad005.grl (in levels.tar). +i18n("Who Needs Enemies?"); + +// From levels/wad006.grl (in levels.tar). +i18n("Asymmetrical"); + +// From levels/wad007.grl (in levels.tar). +i18n("Goldrunner Prophecy"); + +// From levels/wad008.grl (in levels.tar). +i18n("The Rosette"); + +// From levels/wad009.grl (in levels.tar). +i18n("He's Got the Gold"); + +// From levels/wad010.grl (in levels.tar). +i18n("Towers of Gold"); + +// From levels/wad011.grl (in levels.tar). +i18n("The Box"); + +// From levels/wad012.grl (in levels.tar). +i18n("Delayed Drop"); + +// From levels/wad013.grl (in levels.tar). +i18n("Maze of Ladders"); + +// From levels/wad014.grl (in levels.tar). +i18n("Ride 'em Down"); + +// From levels/wad015.grl (in levels.tar). +i18n("Hair's Breadth Timing"); + +// From levels/wad016.grl (in levels.tar). +i18n("The Three Musketeers"); + +// From levels/wad017.grl (in levels.tar). +i18n("Rat Trap"); + +// From levels/wad018.grl (in levels.tar). +i18n("Head Case"); + +// From levels/wad019.grl (in levels.tar). +i18n("Under the Stairs"); + +// From levels/wad020.grl (in levels.tar). +i18n("Bertie Beetle"); + +// From levels/wad021.grl (in levels.tar). +i18n("Short Circuit"); + +// From levels/wad022.grl (in levels.tar). +i18n("Synchronised Running"); + +// From levels/plwv001.grl (in levels.tar). +i18n("Impossible?"); + +// From levels/plwv002.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plwv003.grl (in levels.tar). +i18n("Short Cut?"); + +// From levels/plwv004.grl (in levels.tar). +i18n("Sky Walker"); + +// From levels/plwv005.grl (in levels.tar). +i18n("The Vault"); + +// From levels/plwv006.grl (in levels.tar). +i18n("Patchwork Quilt"); + +// From levels/plwv007.grl (in levels.tar). +i18n("Do You Need Him?"); + +// From levels/plwv008.grl (in levels.tar). +i18n("Stuck in Storage"); + +// From levels/plwv009.grl (in levels.tar). +i18n("So Far for So Little"); + +// From levels/plwv010.grl (in levels.tar). +i18n("Pharaoh's Tomb"); + +// From levels/plwv011.grl (in levels.tar). +i18n("Entangled"); + +// From levels/plwv012.grl (in levels.tar). +i18n("Flying Tower"); + +// From levels/plwv013.grl (in levels.tar). +i18n("Pot Hole"); + +// From levels/plwv014.grl (in levels.tar). +i18n("Sticky Ladders"); + +// From levels/plwv015.grl (in levels.tar). +i18n("The Laboratory"); + +// From levels/plwv016.grl (in levels.tar). +i18n("Pete likes Ladders"); + +// From levels/plwv017.grl (in levels.tar). +i18n("Where's the Roof?"); + +// From levels/plwv018.grl (in levels.tar). +i18n("Ninja Style"); + +// From levels/plwv019.grl (in levels.tar). +i18n("Cooperation?"); + +// From levels/plwv020.grl (in levels.tar). +i18n("Triple Trap"); + +// The following messages are titles and descriptions of games, +// which have been extracted from the file gamedata/games.dat. + +i18n("Initiation"); +i18n("These 100 levels make an excellent introductory game, as well as a good opportunity for experts to build up high scores. They were composed by Peter Wadham and use traditional playing rules.\n\nThe last few levels are very hard, but if you are looking for even more of a challenge, have a go at 'Vengeance of Peter W' .... ;-) ...."); + +i18n("Challenge"); +i18n("These tricky little levels were composed by Peter, Simon, Genevieve and their father Ian Wadham. They use traditional playing rules. Enjoy! .... ;-) ...."); + +i18n("Vengeance of Peter W"); +i18n("Gooood luck !!\nMwarrhh hwwarrrr haarrrr !!!"); + +i18n("KGoldrunner"); +i18n("These levels were composed by Marco Krüger, the original author of the KGoldrunner program, and some of his friends and contributors. They use KGoldrunner rules. The enemies run fast and have an aggressive search strategy. Enjoy! .... :-) ...."); + +i18n("Tutorial"); +i18n("This tutorial is a collection of easy levels that teaches you the rules of KGoldrunner and helps you develop the skills you need to get started. Each level has a brief explanation, then you play .....\n\nWhen you move on to play more advanced levels, you will find that KGoldrunner combines action, strategy and puzzle solving --- all in one game."); + +i18n("Advanced Tutorial"); +i18n("This tutorial is preparation for some of the things you might find in the middle levels of the 'Initiation' game. Enjoy ...."); +} diff --git a/kgoldrunner/src/enemy1.xpm b/kgoldrunner/src/enemy1.xpm new file mode 100644 index 00000000..a494edb7 --- /dev/null +++ b/kgoldrunner/src/enemy1.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static const char * enemy1_xpm[] = { +"320 16 6 1", +" c None", +". c #00FFFF", +"+ c #008080", +"@ c #0000FF", +"# c #000080", +"$ c #000000", +" .++ .++ ", +" .+@ +## +## +## .+@ .+@ +## .@@ +## .@@ +## .++ .+@ .+@ +@# ++# ", +" .@@# +## +## +### .@@ .@@ +## ++@@ +## .@@ +### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" +++## .++@@ +++## .++@@ ++++# .++@@ ++++# .++@@ ++@@#.++@.@@@ .@@ ++@@ .++@++@@ .@@ .@@@.++@++@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# ++@@++@@ ++# +++# .@@ .@@ +++# ++# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ ++@@.@@++@# .@++++ ++@@++@+@@# .@@++@ ++@+@@+.@@# .++@@@ ++@+@@+.@@# .+@.@@ +++@+@@ ++ ++ .@@+++# .@@# .@@@++@# ++@#.@@@ ++@@ ", +" ++@@## +++.@@@# ++@@## .@@@# +#+@@# +@@@@#++ +#+@@# ++@@@ ++@@@+@@# .@.@@@ ++@@@@@@# .@@@@@ ++@+@@@@# +@@+@@ ++@+@@@@# +@@.@@ ++@@@+++ .+.@@@@+ .@@@+++@@+@@# ++@@@@#+++@@@ ", +" +++#+@@# .@@@@@@@@@++@ ++++@@@@@## ++@ +++@@@@@@ ++@@++++ .+++@@@@@@@@@ .++ +++@@@@@@## .@@@@@@@# ++@@@@ ..+@ ++@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"+++@@@@@@@+ ++@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@+#.+++ .@@@@@@@++# +@@@+@@@@@+@@ .@@+@@@@@@#@@@@ +++@@@@@+@@@ .@@@# .@@+++ .@@@# .@@@++ ++@@@ +++@@@ ++@@@ ++.@@@ .@@@@@.# ++.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@+## .@@ .@@@@@@@ .@@++@@++@@@+@@@+@@@@@@##+@@ ##++@@@@## .@@++@@@ .@@ .@@@+.@++@@@ .@@@# ++@@@@@ .@@@# ++@@@@@ ++@@@ .@@@@@@ ++@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# ++@@@ ", +".@@ .@@@+@@@# .@@@@ ### ++@@@ +++@@@@$+## #@@@@+@@@ .@@ .@@@@ .@@@# +## +## .@@@@@# ++@@+@@@@ .@.### ++@@@@@@@ .@@### .+@@+@@@# +##.@@@ .+@@+@@@# +##.@@@# .@@@@@@@ .@@@@@@@ +++@@@@ .@@@@## ", +" .@@@++## .@@@+ .@@@@ .@@@+ ++++@@@@ ++@@@@ .@@@@ .@@@@ .@@@ ++@@ ++@@ .@@@ ++@@ ++@@ .@@# .@@@ .@@@ .@@# .@@@ .@@@ .@@.@@@@ .@@@.@@@ ++@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ +++@@@@@ .++@ +@@@@@# +++@@@@@ .@@@@@@@@ .@@@@@++ ++@@@@+ .++@ .@@@@@#++ ++@@ .@@@ .@@@ ++@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ ++@@ .@@# .@@ .@@# .@@ .@@ ++@@ ", +" .@@@ ++@@ .@@@@@+@@@@ +@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ ++@@# .@@ .@+@@ ++@@# +@@ .+.@@ +@@ ++@@# .@.+@ +@@ ++@@# .@.+@ ### .@@# ++@@ +## ++@@# .@@ .@@ ++@@# ", +" ++@@ .@@# +@####+#+@@@ #@@@@ .@@# +@####.@@@ ++@@ .@@# .@@@#+####@@ ++@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ ++@@ .@@ ++@@ .@@ ++@@ .@@# +@@# .@@ .@@ ++@@ ", +"++@@# +## +@@# ++@@# .@@@ ### ++@@# +@@# ++@@# .@@@ .@@# ++@@ .@@ .@@ ", +".@@# ++@@ ++@@@ ++@@ .@@# .@@@# +@@@ +@@# +@# ++# "}; diff --git a/kgoldrunner/src/enemy2.xpm b/kgoldrunner/src/enemy2.xpm new file mode 100644 index 00000000..7de334d0 --- /dev/null +++ b/kgoldrunner/src/enemy2.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * enemy2_xpm[] = { +"320 16 7 1", +" c None", +". c #FFFF00", +"+ c #808080", +"@ c #0000FF", +"# c #808000", +"$ c #000080", +"% c #000000", +" .++ .++ ", +" .+@ #$$ #$$ #$$ .+@ .+@ #$$ .@@ #$$ .@@ #$$ .++ .+@ .+@ #@$ #+$ ", +" .@@$ #$$ #$$ #$$$ .@@ .@@ #$$ #+@@ #$$ .@@ #$$$ .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" ###$$ .++@@ ###$$ .++@@ ####$ .++@@ ####$ .++@@ #+@@$.++@.@@@ .@@ #+@@ .++@#+@@ .@@ .@@@.++@#+@@$ .@@ .@@$.++@ .@@$ .@@ .@@++@@$ #+@@++@@ ##$ ###$ .@@ .@@ ###$ ##$ ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ #+@@.@@++@$ .@++++ #+@@++@+@@$ .@@++@ #+@+@@+.@@$ .++@@@ #+@+@@+.@@$ .+@.@@ #++@+@@ ## ## .@@+++$ .@@$ .@@@#+@$ #+@$.@@@ #+@@ ", +" #+@@$$ ###.@@@$ #+@@$$ .@@@$ #$+@@$ #@@@@$## #$+@@$ #+@@@ #+@@@+@@$ .@.@@@ #+@@@@@@$ .@@@@@ #+@+@@@@$ +@@+@@ #+@+@@@@$ +@@.@@ #+@@@+++ .+.@@@@# .@@@###@@+@@$ #+@@@@$##+@@@ ", +" ###$+@@$ .@@@@@@@@@#+@ ##++@@@@@$$ #+@ #++@@@@@@ #+@@#### .+#+@@@@@@@@@ .+# ##+@@@@@@$$ .@@@@@@@$ #+@@@@ ..+@ #+@@@@ .@@+ .@@@@$ .++@ .@@@@$ .@@@ .@@@+@@ .@@@@@@ $@@@@@@@@@ .@@@@@@@@$ ", +"#++@@@@@@@+ #+@@ .@+@@@@@+@@@$ .@@@$+@@@@@+@@@ .@@+@@@+@@#$.++# .@@@@@@@++$ #@@@+@@@@@+@@ .@@+@@@@@@$@@@@ ##+@@@@@+@@@ .@@@$ .@@+## .@@@$ .@@@## #+@@@ ##+@@@ #+@@@ ##.@@@ .@@@@@.$ #+.@@@@@ $+@@@@ .@@@@$ ", +".@@$$+@@@@@+@@@$ $$+@@@@#$$ .@@ .@@@@@@@ .@@++@@#+@@@#@@@+@@@@@@$$+@@ $$#+@@@@$$ .@@++@@@ .@@ .@@@#.@++@@@ .@@@$ #+@@@@@ .@@@$ #+@@@@@ #+@@@ .@@@@@@ #+@@@ .@@.@@@$ .+.@@@@@ .@@@@@@@ .@@@$ #+@@@ ", +".@@ .@@@+@@@$ .@@@@ $$$ #+@@@ #++@@@@%#$$ $@@@@+@@@ .@@ .@@@@ .@@@$ #$$ #$$%.@@@@@$ #+@@+@@@@ .@.$$$ #+@@@@@@@ .@@$$$ .+@@+@@@$ #$$.@@@ .+@@+@@@$ #$$.@@@$ .@@@@@@@ .@@@@@@@ ##+@@@@ .@@@@$$ ", +" .@@@+#$$ .@@@+ .@@@@ .@@@+ ###+@@@@ #+@@@@ .@@@@ .@@@@ .@@@ #+@@ #+@@ .@@@ #+@@ #+@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@@ .@@.@@@@ .@@@.@@@ #+@@@@@@@@ .@@@++@@@$ ", +" .@@@@@@@@ ##+@@@@@ .++@ #@@@@@$ ##+@@@@@ .@@@@@@@@ .@@@@@## #+@@@@# .++@ .@@@@@$## #+@@ .@@@ .@@@ #+@@ .@@@ .@@@ .@@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@ #+@@ .@@$ .@@ .@@$ .@@ .@@ #+@@ ", +" .@@@ #+@@ .@@@@@+@@@@ #@@@@@@@+@@@ .@@@@@+@@@ .@@$ .@@@ .@@@+@@@@@@ .@@@+@@+@@@$ .@@@@@@@@@ #+@@$ .@@ .@+@@ #+@@$ +@@ .+.@@ +@@ #+@@$ .@.+@ +@@ #+@@$ .@.+@ $$$ .@@$ #+@@ #$$ #+@@$ .@@ .@@ #+@@$ ", +" #+@@ .@@$ +@$$$$#$+@@@ $@@@@ .@@$ +@$$$$.@@@ #+@@ .@@$ .@@@$#$$$$@@ #+@@ @@@@$ .@@@$$$$@@ .@@$ .@@ .@@$ .@@ #+@@ .@@ #+@@ .@@ #+@@ .@@$ +@@$ .@@ .@@ #+@@ ", +"#+@@$ #$$ #@@$ #+@@$ .@@@ $$$ #+@@$ #@@$ #+@@$ .@@@ .@@$ #+@@ .@@ .@@ ", +".@@$ #+@@ #+@@@ #+@@ .@@$ .@@@$ #@@@ +@@$ #@$ #+$ "}; diff --git a/kgoldrunner/src/hero.xpm b/kgoldrunner/src/hero.xpm new file mode 100644 index 00000000..6cfa2e2c --- /dev/null +++ b/kgoldrunner/src/hero.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * hero_xpm[] = { +"320 16 7 1", +" c None", +". c #00FF00", +"+ c #00C000", +"@ c #008000", +"# c #004000", +"$ c #000000", +"% c #303030", +" .++ .++ ", +" .+@ @## @## @## .+@ .+@ @## .@@ @## .@@ @## .++ .+@ .+@ @@# @+# ", +" .@@# @## @## @### .@@ .@@ @## @+@@ @## .@@ @### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" @@@## .++@@ @@@## .++@@ @@@@# .++@@ @@@@# .++@@ @+@@#.++@.@@@ .@@ @+@@ .++@@+@@ .@@ .@@@.++@@+@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# @+@@++@@ @@# @@@# .@@ .@@ @@@# @@# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ @+@@.@@++@# .@++++ @+@@++@+@@# .@@++@ @+@+@@+.@@# .++@@@ @+@+@@+.@@# .+@.@@ @++@+@@ @@ @@ .@@+++# .@@# .@@@@+@# @+@#.@@@ @+@@ ", +" @+@@## @@@.@@@# @+@@## .@@@# @#+@@# @@@@@#@@ @#+@@# @+@@@ @+@@@+@@# .@.@@@ @+@@@@@@# .@@@@@ @+@+@@@@# +@@+@@ @+@+@@@@# +@@.@@ @+@@@+++ .+.@@@@@ .@@@@@@@@+@@# @+@@@@#@@+@@@ ", +" @@@#+@@# .@@@@@@@@@@+@ @@++@@@@@## @+@ @++@@@@@@ @+@@@@@@ .+@+@@@@@@@@@ .+@ @@+@@@@@@## .@@@@@@@# @+@@@@ ..+@ @+@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"@++@@@@@@@+ @+@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@@#.++@ .@@@@@@@++# @@@@+@@@@@+@@ .@@+@@@@@@#@@@@ @@+@@@@@+@@@ .@@@# .@@+@@ .@@@# .@@@@@ @+@@@ @++@@@ @+@@@ @..@@@ .@@@@@.# @+.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@@## .@@ .@@@@@@@ .@@++@@@+@@@@@@@+@@@@@@##+@@ ##@+@@@@## .@@++@@@ .@@ .@@@@.@++@@@ .@@@# @+@@@@@ .@@@# @+@@@@@ @+@@@ .@@@+@@ @+@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# @+@@@ ", +".@@ .@@@+@@@# .@@@@ ### @+@@@ @++@@@@$@## #@@@@+@@@ .@@ .@@@@ .@@@# @## @##$.@@@@@# @+@@+@@@@ .@.### @+@@@@@@@ .@@### .+@@+@@@# @##.@@@ .+@@+@@@# @# .@@@# .@@@@@@@ .@@@@@@@ @@+@@@@ .@@@@## ", +" .@@@+@## .@@@+ .@@@@ .@@@+ @@@+@@@@ @+@@@@ .@@@@ .@@@@ .@@@ @+@@ @+@@ .@@@ @+@@ @+@@ .@@% .@@@ .@@@ .@@% .@@@ .@@@ .@@.@@@@ .@@@.@@@ @+@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ @@+@@@@@ .++@ @@@@@@# @@+@@@@@ .@@@@@@@@ .@@@@@@@ @+@@@@@ .++@ .@@@@@#@@ @+@@ .@@@ .@@@ @+@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ @+@@ .@@# .@@ .@@# .@@ .@@ @+@@ ", +" .@@@ @+@@ .@@@@@+@@@@ @@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ @+@@# .@@ .@+@@ @+@@# +@@ .+.@@ +@@ @+@@# .@.+@ +@@ @+@@# .@.+@ ### .@@# @+@@ @## @+@@# .@@ .@@ @+@@# ", +" @+@@ .@@# +@#%##@#+@@@ #@@@@ .@@# +@#%##.@@@ @+@@ .@@# .@@@#@####@@ @+@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ @+@@ .@@ @+@@ .@@ @+@@ .@@# +@@# .@@ .@@ @+@@ ", +"@+@@# @## @@@# @+@@# .@@@ ### @+@@# @@@% @+@@# .@@@ .@@# @+@@ .@@ .@@ ", +".@@# @+@@ @+@@@ @+@@ .@@# .@@@# @@@@ +@@# @@# @+# "}; diff --git a/kgoldrunner/src/hi128-app-kgoldrunner.png b/kgoldrunner/src/hi128-app-kgoldrunner.png new file mode 100644 index 0000000000000000000000000000000000000000..d47375a8625f28f50f67d8aa6b86eeda4de91206 GIT binary patch literal 17842 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 literal 0 HcmV?d00001 diff --git a/kgoldrunner/src/hi32-app-kgoldrunner.png b/kgoldrunner/src/hi32-app-kgoldrunner.png new file mode 100644 index 0000000000000000000000000000000000000000..1b72581af0a733b5464ebeb4306a60c319d01b38 GIT binary patch literal 2043 zcmVX<7nlN~=mmY7#Z1swy>YNR>X+4{cQeg(6fMAygoclt&?f2!d-c20Pf^^#gmo z@5jvS%-p&CU{fU(RdGsMsp?<4AMVxF`Ty?KIY$ThUr|j>UG)B zp6`q$Wwz~^U9SM(|6}G;tE=mdpFG!@lQqT|W2Z0umHDdbc77se&6U0--(Fm^6#xeS zRaLe1C(c~#RaBiZ#@L1TI@q%HJ3_q8wepdh2dwz?FB;sM{QYKjVAmH9u2{Qa?Wr>t z`|_H>Ow(jwp?RfXZ;pYLI29TWy zfMo)AP1dW;u(NBAo@VkCaA zn|pTMiWRy@`YzuLw?9&V1q&Cf_} zLnT*kCK2zyYuVM(+^mUw=ar7|?SBhk&mM5kHCGmR%I0qhlsC*c`?HCrvK4 z16JO`6Yx?Y@z&O1PSZSM%|&swJ%nhu*S2HL^3_S2zka=Y_=ArHaCQ$Y75;L+-|K8N zNO8-sR#rSUc!JKTb=v?N2MFMK5A32Jg6JpSsWk{pE@Zac0J+uIkCC2U+m4o{tCCc^ z*47>AnGnF9MX(fB_$E0dscDkLf0enV>6f~a_NSI-IZ3c{fCc(+1cVg8B2_>Xec(s| zM86NEWknF=FuBz?h){1>+WGm#E5q^JJDuTJ|3d*BX#wx9woh_Od}AQ!*;%@9);zD@ zu;*hP{ESM8aCQj*K&&<_c2BxC;Lg>%#e~|j+)UvuI*kL{oVccZ*Ew;GMvi4 zb2}6r_*VdA@hrIQf_1Lf$!#w!bu<-DD-rE3!q?Q=Kpa*O9so=b3jyIM0Ap;A47B_J z(((WlmO{|aGnPw`OPyH^bq_he-qNuAdT;#PKr}P_PXS(E0f7pr^gFSos8Co!lUaK{ zG|0KUcEITbv2g&$0U!X_xOpC6GStC4Flh>zY#g2<7YWW?KuTRYJ~h~BEo~f$f~e71i(VZCxQC_8bB0q zq6VU5ksMBfW-_p*Vt`3m04bts2Hp2|meZmmo)@<^E@>M~yfqNXMn3}30`LWH1cN2~ z7CUFo5_!XJFOc}6InzNb94saUA>7#Gi3#uwAU1&M0L_C?1H7ssJ)FYePzs^;Uc|cx zVX^S+Q)L|O3TdNn+>o7GBvQ0z+Y?V-?TMWnisur42e<{0*#gR^1i4n0H3O0rl1&6j z-Hk)b=3o;oW1c1hbRCRxAc7yD3`oU+oCcCrp{qI)@f@NF6{)OBSXyCzuV4{4+Shi+ z9Jx3k7pwP1i(Y!Vp|LHLI6WB4C3yhYSTB}sesRtV1)MTd8Ina&fIu~!(7XacvH%UXtLSh14@eRP{Rp1@Ns}L_{l4P{6@3IhISRG_T~Du46Eah?s!4c`Py=jaM5DuTMpcuO>z@qr^O^#ln#apae}3@ps~H{b zHUfGxYwk0E_Xz+yfQH6twzb=r3eC%w7FQHc^Nl5(Lj+6?YTkebFtOM}Y(6hw0D4MB zKAB{CPGMFX56K~LJQ$Es(HBafr#}haFtBLze4IYiJ^0GcI)C-&+vaOywF@uCrl$-U zuLc3YBp#SkUyO!@j}_F`&u~wvtaSM%PjxvwLAPixvfD(Ti%@C>g8;(u0H*<_0;F=# zmyDx}HL92qqUq2K_RCKxpdr)knql0|X~%c!bJrzlHTgNqpcOSv!nOba002ovPDHLkV1iy)w=Mtx literal 0 HcmV?d00001 diff --git a/kgoldrunner/src/hi48-app-kgoldrunner.png b/kgoldrunner/src/hi48-app-kgoldrunner.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae5f636e0702486d86fdbf136e05cd967f65634 GIT binary patch literal 4077 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 literal 0 HcmV?d00001 diff --git a/kgoldrunner/src/kgoldrunner.cpp b/kgoldrunner/src/kgoldrunner.cpp new file mode 100644 index 00000000..6e8cfb3c --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" +#include "kgrgame.h" +#include "kgoldrunner.h" + +KGoldrunner::KGoldrunner() + : KMainWindow (0, "KGoldrunner"), + view (new KGrCanvas (this)) +{ +/******************************************************************************/ +/************* FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE *************/ +/******************************************************************************/ + + // Avoid "saveOK()" check if an error-exit occurs during the file checks. + startupOK = TRUE; + + // Get directory paths for the system levels, user levels and manual. + if (! getDirectories()) { + fprintf (stderr, "getDirectories() FAILED\n"); + startupOK = FALSE; + return; // If games directory not found, abort. + } + + // This message is to help diagnose distribution or installation problems. + printf + ("The games data and handbook should be in the following locations:\n"); + printf ("System games: %s\nUser data: %s\nHandbook: %s\n", + systemDataDir.myStr(), userDataDir.myStr(), systemHTMLDir.myStr()); + +/******************************************************************************/ +/************************ SET PLAYFIELD AND GAME DATA ***********************/ +/******************************************************************************/ + + game = new KGrGame (view, systemDataDir, userDataDir); + + // Initialise the collections of levels (i.e. the list of games). + if (! game->initCollections()) { + startupOK = FALSE; + return; // If no game files, abort. + } + + hero = game->getHero(); // Get a pointer to the hero. + +/******************************************************************************/ +/************************* SET UP THE USER INTERFACE ************************/ +/******************************************************************************/ + + // Get catalog for translation + KGlobal::locale()->insertCatalogue("libkdegames"); + + // Tell the KMainWindow that this is the main widget + setCentralWidget (view); + + // Set up our actions (menu, toolbar and keystrokes) ... + setupActions(); + + // and a status bar. + initStatusBar(); + + // Connect the game actions to the menu and toolbar displays. + connect(game, SIGNAL (setEditMenu (bool)), SLOT (setEditMenu (bool))); + connect(game, SIGNAL (markRuleType (char)), SLOT (markRuleType (char))); + connect(game, SIGNAL (hintAvailable(bool)), SLOT (adjustHintAction(bool))); + connect(game, SIGNAL (defaultEditObj()), SLOT (defaultEditObj())); + + // Apply the saved mainwindow settings, if any, and ask the mainwindow + // to automatically save settings if changed: window size, toolbar + // position, icon size, etc. + setAutoSaveSettings(); + +#ifdef QT3 + // Base size of playing-area and widgets on the monitor resolution. + int dw = KApplication::desktop()->width(); + if (dw > 800) { // More than 800x600. + view->changeSize (+1); // Scale 1.25:1. + } + if (dw > 1024) { // More than 1024x768. + view->changeSize (+1); + view->changeSize (+1); // Scale 1.75:1. + setUsesBigPixmaps (TRUE); // Use big toolbar buttons. + } + view->setBaseScale(); // Set scale for level-names. +#endif + setFixedSize (view->size()); + + makeEditToolbar(); // Uses pixmaps from "view". + editToolbar->hide(); + setDockEnabled (DockBottom, FALSE); + setDockEnabled (DockLeft, FALSE); + setDockEnabled (DockRight, FALSE); + + // Make it impossible to turn off the editor toolbar. + // Accidentally hiding it would make editing impossible. + setDockMenuEnabled (FALSE); + + // Set mouse control of the hero as the default. + game->setMouseMode (TRUE); + + // Paint the main widget (title, menu, status bar, blank playfield). + show(); + + // Force the main widget to appear before the "Start Game" dialog does. + qApp->processEvents(); + + // Call the "Start Game" function and pop up the "Start Game" dialog. + game->startLevelOne(); +} + +KGoldrunner::~KGoldrunner() +{ + delete editToolbar; +} + +void KGoldrunner::setupActions() +{ + /**************************************************************************/ + /****************************** GAME MENU ******************************/ + /**************************************************************************/ + + // New Game... + // Load Saved Game... + // Play Any Level... + // Play Next Level... + // Tutorial + // -------------------------- + + KAction * newAction = KStdGameAction:: + gameNew ( + game, + SLOT(startLevelOne()), actionCollection()); + newAction-> setText (i18n("&New Game...")); + KAction * loadGame = KStdGameAction:: + load ( + game, SLOT(loadGame()), actionCollection()); + loadGame-> setText (i18n("&Load Saved Game...")); + (void) new KAction ( + i18n("&Play Any Level..."), + 0, + game, SLOT(startAnyLevel()), actionCollection(), + "play_any"); + (void) new KAction ( + i18n("Play &Next Level..."), + 0, + game, + SLOT(startNextLevel()), actionCollection(), + "play_next"); + + // Save Game... + // Save Edits... (extra copy) + // -------------------------- + + saveGame = KStdGameAction:: + save ( + game, SLOT(saveGame()), actionCollection()); + saveGame-> setText (i18n("&Save Game...")); + saveGame-> setShortcut (Key_S); // Alternate key. + + // Pause + // Show High Scores + // Get a Hint + // Kill the Hero + // -------------------------- + + myPause = KStdGameAction:: + pause ( + this, SLOT(stopStart()), actionCollection()); + myPause-> setShortcut (Key_Escape); // Alternate key. + highScore = KStdGameAction:: + highscores ( + game, SLOT(showHighScores()), actionCollection()); + hintAction = new KAction ( + i18n("&Get Hint"), "ktip", + 0, + game, SLOT(showHint()), actionCollection(), + "get_hint"); + killHero = new KAction ( + i18n("&Kill Hero"), + Key_Q, + game, SLOT(herosDead()), actionCollection(), + "kill_hero"); + + // Quit + // -------------------------- + + (void) KStdGameAction:: + quit ( + this, SLOT(close()), actionCollection()); + + /**************************************************************************/ + /*************************** GAME EDITOR MENU **************************/ + /**************************************************************************/ + + // Create a Level + // Edit Any Level... + // Edit Next Level... + // -------------------------- + + (void) new KAction ( + i18n("&Create Level"), + 0, + game, SLOT(createLevel()), actionCollection(), + "create"); + (void) new KAction ( + i18n("&Edit Any Level..."), + 0, + game, SLOT(updateLevel()), actionCollection(), + "edit_any"); + (void) new KAction ( + i18n("Edit &Next Level..."), + 0, + game, SLOT(updateNext()), actionCollection(), + "edit_next"); + + // Save Edits... + // Move Level... + // Delete Level... + // -------------------------- + + saveEdits = new KAction ( + i18n("&Save Edits..."), + 0, + game, SLOT(saveLevelFile()), actionCollection(), + "save_edits"); + saveEdits->setEnabled (FALSE); // Nothing to save, yet. + + (void) new KAction ( + i18n("&Move Level..."), + 0, + game, SLOT(moveLevelFile()), actionCollection(), + "move_level"); + (void) new KAction ( + i18n("&Delete Level..."), + 0, + game, + SLOT(deleteLevelFile()), actionCollection(), + "delete_level"); + + // Create a Game + // Edit Game Info... + // -------------------------- + + (void) new KAction ( + i18n("Create Game..."), + 0, + this, SLOT(createGame()), actionCollection(), + "create_game"); + (void) new KAction ( + i18n("Edit Game Info..."), + 0, + this, + SLOT(editGameInfo()), actionCollection(), + "edit_game"); + + /**************************************************************************/ + /*************************** LANDSCAPES MENU ***************************/ + /**************************************************************************/ + + // Default shortcut keys are set by "kgoldrunnerui.rc". + + setKGoldrunner = new KRadioAction ( + "K&Goldrunner", + 0, // Default Shift+G + this, SLOT(lsKGoldrunner()), actionCollection(), + "kgoldrunner"); + setAppleII = new KRadioAction ( + "&Apple II", + 0, // Default Shift+A + this, SLOT(lsApple2()), actionCollection(), + "apple_2"); + setIceCave = new KRadioAction ( + i18n("&Ice Cave"), + 0, // Default Shift+I + this, SLOT(lsIceCave()), actionCollection(), + "ice_cave"); + setMidnight = new KRadioAction ( + i18n("&Midnight"), + 0, // Default Shift+M + this, SLOT(lsMidnight()), actionCollection(), + "midnight"); + setKDEKool = new KRadioAction ( + i18n("&KDE Kool"), + 0, // Default Shift+K + this, SLOT(lsKDEKool()), actionCollection(), + "kde_kool"); + + setKGoldrunner-> setExclusiveGroup ("landscapes"); + setAppleII-> setExclusiveGroup ("landscapes"); + setIceCave-> setExclusiveGroup ("landscapes"); + setMidnight-> setExclusiveGroup ("landscapes"); + setKDEKool-> setExclusiveGroup ("landscapes"); + setKGoldrunner-> setChecked (TRUE); + + /**************************************************************************/ + /**************************** SETTINGS MENU ****************************/ + /**************************************************************************/ + + // Mouse Controls Hero + // Keyboard Controls Hero + // -------------------------- + + setMouse = new KRadioAction ( + i18n("&Mouse Controls Hero"), + 0, + this, + SLOT(setMouseMode()), actionCollection(), + "mouse_mode"); + setKeyboard = new KRadioAction ( + i18n("&Keyboard Controls Hero"), + 0, + this, + SLOT(setKeyBoardMode()), actionCollection(), + "keyboard_mode"); + + setMouse-> setExclusiveGroup ("control"); + setKeyboard-> setExclusiveGroup ("control"); + setMouse-> setChecked (TRUE); + + // Normal Speed + // Beginner Speed + // Champion Speed + // Increase Speed + // Decrease Speed + // -------------------------- + + KRadioAction * nSpeed = new KRadioAction ( + i18n("Normal Speed"), + 0, + this, SLOT(normalSpeed()), actionCollection(), + "normal_speed"); + KRadioAction * bSpeed = new KRadioAction ( + i18n("Beginner Speed"), + 0, + this, SLOT(beginSpeed()), actionCollection(), + "beginner_speed"); + KRadioAction * cSpeed = new KRadioAction ( + i18n("Champion Speed"), + 0, + this, SLOT(champSpeed()), actionCollection(), + "champion_speed"); + (void) new KAction ( // Repeatable action. + i18n("Increase Speed"), + Key_Plus, + this, SLOT(incSpeed()), actionCollection(), + "increase_speed"); + (void) new KAction ( // Repeatable action. + i18n("Decrease Speed"), + Key_Minus, + this, SLOT(decSpeed()), actionCollection(), + "decrease_speed"); + + nSpeed-> setExclusiveGroup ("speed"); + bSpeed-> setExclusiveGroup ("speed"); + cSpeed-> setExclusiveGroup ("speed"); + nSpeed-> setChecked (TRUE); + + // Traditional Rules + // KGoldrunner Rules + // -------------------------- + + tradRules = new KRadioAction ( + i18n("&Traditional Rules"), + 0, + this, SLOT(setTradRules()), actionCollection(), + "trad_rules"); + kgrRules = new KRadioAction ( + i18n("K&Goldrunner Rules"), + 0, + this, SLOT(setKGrRules()), actionCollection(), + "kgr_rules"); + + tradRules-> setExclusiveGroup ("rules"); + kgrRules-> setExclusiveGroup ("rules"); + tradRules-> setChecked (TRUE); + + // Larger Playing Area + // Smaller Playing Area + // -------------------------- + + (void) new KAction ( + i18n("Larger Playing Area"), + 0, + this, SLOT(makeLarger()), actionCollection(), + "larger_area"); + (void) new KAction ( + i18n("Smaller Playing Area"), + 0, + this, SLOT(makeSmaller()), actionCollection(), + "smaller_area"); + + // Configure Shortcuts... + // Configure Toolbars... + // -------------------------- + + KStdAction::keyBindings ( + this, SLOT(optionsConfigureKeys()), + actionCollection()); + // KStdAction::configureToolbars ( + // this, SLOT(optionsConfigureToolbars()), + // actionCollection()); + + /**************************************************************************/ + /************************** KEYSTROKE ACTIONS **************************/ + /**************************************************************************/ + + // Two-handed KB controls and alternate one-handed controls for the hero. + + (void) new KAction (i18n("Move Up"), Key_Up, + this, SLOT(goUp()), actionCollection(), "move_up"); + (void) new KAction (i18n("Move Right"), Key_Right, + this, SLOT(goR()), actionCollection(), "move_right"); + (void) new KAction (i18n("Move Down"), Key_Down, + this, SLOT(goDown()), actionCollection(), "move_down"); + (void) new KAction (i18n("Move Left"), Key_Left, + this, SLOT(goL()), actionCollection(), "move_left"); + (void) new KAction (i18n("Stop"), Key_Space, + this, SLOT(stop()), actionCollection(), "stop"); + (void) new KAction (i18n("Dig Right"), Key_C, + this, SLOT(digR()), actionCollection(), "dig_right"); + (void) new KAction (i18n("Dig Left"), Key_Z, + this, SLOT(digL()), actionCollection(), "dig_left"); + + // Alternate one-handed controls. Set up in "kgoldrunnerui.rc". + + // Key_I, "move_up" + // Key_L, "move_right" + // Key_K, "move_down" + // Key_J, "move_left" + // Key_Space, "stop" (as above) + // Key_O, "dig_right" + // Key_U, "dig_left" + +#ifdef KGR_DEBUG + // Authors' debugging aids. + + (void) new KAction (i18n("Step"), Key_Period, + game, SLOT(doStep()), actionCollection(), "do_step"); + (void) new KAction (i18n("Test Bug Fix"), Key_B, + game, SLOT(bugFix()), actionCollection(), "bug_fix"); + (void) new KAction (i18n("Show Positions"), Key_D, + game, SLOT(showFigurePositions()), actionCollection(), "step"); + (void) new KAction (i18n("Start Logging"), Key_G, + game, SLOT(startLogging()), actionCollection(), "logging"); + (void) new KAction (i18n("Show Hero"), Key_H, + game, SLOT(showHeroState()), actionCollection(), "show_hero"); + (void) new KAction (i18n("Show Object"), Key_Question, + game, SLOT(showObjectState()), actionCollection(), "show_obj"); + (void) new KAction (i18n("Show Enemy") + "0", Key_0, + this, SLOT(showEnemy0()), actionCollection(), "show_enemy_0"); + (void) new KAction (i18n("Show Enemy") + "1", Key_1, + this, SLOT(showEnemy1()), actionCollection(), "show_enemy_1"); + (void) new KAction (i18n("Show Enemy") + "2", Key_2, + this, SLOT(showEnemy2()), actionCollection(), "show_enemy_2"); + (void) new KAction (i18n("Show Enemy") + "3", Key_3, + this, SLOT(showEnemy3()), actionCollection(), "show_enemy_3"); + (void) new KAction (i18n("Show Enemy") + "4", Key_4, + this, SLOT(showEnemy4()), actionCollection(), "show_enemy_4"); + (void) new KAction (i18n("Show Enemy") + "5", Key_5, + this, SLOT(showEnemy5()), actionCollection(), "show_enemy_5"); + (void) new KAction (i18n("Show Enemy") + "6", Key_6, + this, SLOT(showEnemy6()), actionCollection(), "show_enemy_6"); +#endif + + /**************************************************************************/ + /************************** NOW SET IT ALL UP **************************/ + /**************************************************************************/ + + createGUI(); +} + +/******************************************************************************/ +/********************** SLOTS FOR STATUS BAR UPDATES ************************/ +/******************************************************************************/ + +void KGoldrunner::initStatusBar() +{ + QString s = statusBar()->fontInfo().family(); // Set bold font. + int i = statusBar()->fontInfo().pointSize(); + statusBar()->setFont (QFont (s, i, QFont::Bold)); + + statusBar()->setSizeGripEnabled (FALSE); // Use Settings menu ... + + statusBar()->insertItem ("", ID_LIVES); + statusBar()->insertItem ("", ID_SCORE); + statusBar()->insertItem ("", ID_LEVEL); + statusBar()->insertItem ("", ID_HINTAVL); + statusBar()->insertItem ("", ID_MSG, QSizePolicy::Horizontally); + + showLives (5); // Start with 5 lives. + showScore (0); + showLevel (0); + adjustHintAction (FALSE); + + // Set the PAUSE/RESUME key-names into the status bar message. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (FALSE); + + statusBar()->setItemFixed (ID_LIVES, -1); // Fix current sizes. + statusBar()->setItemFixed (ID_SCORE, -1); + statusBar()->setItemFixed (ID_LEVEL, -1); + + connect(game, SIGNAL (showLives (long)), SLOT (showLives (long))); + connect(game, SIGNAL (showScore (long)), SLOT (showScore (long))); + connect(game, SIGNAL (showLevel (int)), SLOT (showLevel (int))); + connect(game, SIGNAL (gameFreeze (bool)), SLOT (gameFreeze (bool))); +} + +void KGoldrunner::showLives (long newLives) +{ + QString tmp; + tmp.setNum (newLives); + if (newLives < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Lives: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LIVES); +} + +void KGoldrunner::showScore (long newScore) +{ + QString tmp; + tmp.setNum (newScore); + if (newScore < 10000) + tmp = tmp.rightJustify (5, '0'); + tmp.insert (0, i18n(" Score: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_SCORE); +} + +void KGoldrunner::showLevel (int newLevelNo) +{ + QString tmp; + tmp.setNum (newLevelNo); + if (newLevelNo < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Level: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LEVEL); +} + +void KGoldrunner::gameFreeze (bool on_off) +{ + if (on_off) + statusBar()->changeItem + (i18n("Press \"%1\" to RESUME").arg(pauseKeys), ID_MSG); + else + statusBar()->changeItem + (i18n("Press \"%1\" to PAUSE").arg(pauseKeys), ID_MSG); +} + +void KGoldrunner::adjustHintAction (bool hintAvailable) +{ + hintAction->setEnabled (hintAvailable); + + if (hintAvailable) { + statusBar()->changeItem (i18n(" Has hint "), ID_HINTAVL); + } + else { + statusBar()->changeItem (i18n(" No hint "), ID_HINTAVL); + } +} + +void KGoldrunner::markRuleType (char ruleType) +{ + if (ruleType == 'T') + tradRules->activate(); + else + kgrRules->activate(); +} + +void KGoldrunner::setEditMenu (bool on_off) +{ + saveEdits->setEnabled (on_off); + + saveGame->setEnabled (! on_off); + hintAction->setEnabled (! on_off); + killHero->setEnabled (! on_off); + highScore->setEnabled (! on_off); + + if (on_off){ + editToolbar->show(); + } + else { + editToolbar->hide(); + } +} + +/******************************************************************************/ +/******************* SLOTS FOR MENU AND KEYBOARD ACTIONS *******************/ +/******************************************************************************/ + +// Slot to halt (pause) or restart the game play. + +void KGoldrunner::stopStart() +{ + if (! (KGrObject::frozen)) { + game->freeze(); + } + else { + game->unfreeze(); + } +} + +// Local slots to create or edit game information. + +void KGoldrunner::createGame() {game->editCollection (SL_CR_GAME);} +void KGoldrunner::editGameInfo() {game->editCollection (SL_UPD_GAME);} + +// Local slots to set the landscape (colour scheme). + +void KGoldrunner::lsKGoldrunner() {view->changeLandscape ("KGoldrunner");} +void KGoldrunner::lsApple2() {view->changeLandscape ("Apple II");} +void KGoldrunner::lsIceCave() {view->changeLandscape ("Ice Cave");} +void KGoldrunner::lsMidnight() {view->changeLandscape ("Midnight");} +void KGoldrunner::lsKDEKool() {view->changeLandscape ("KDE Kool");} + +// Local slots to set mouse or keyboard control of the hero. + +void KGoldrunner::setMouseMode() {game->setMouseMode (TRUE);} +void KGoldrunner::setKeyBoardMode() {game->setMouseMode (FALSE);} + +// Local slots to set game speed. + +void KGoldrunner::normalSpeed() {hero->setSpeed (NSPEED);} +void KGoldrunner::beginSpeed() {hero->setSpeed (BEGINSPEED);} +void KGoldrunner::champSpeed() {hero->setSpeed (CHAMPSPEED);} +void KGoldrunner::incSpeed() {hero->setSpeed (+1);} +void KGoldrunner::decSpeed() {hero->setSpeed (-1);} + +// Slots to set Traditional or KGoldrunner rules. + +void KGoldrunner::setTradRules() +{ + KGrFigure::variableTiming = TRUE; + KGrFigure::alwaysCollectNugget = TRUE; + KGrFigure::runThruHole = TRUE; + KGrFigure::reappearAtTop = TRUE; + KGrFigure::searchStrategy = LOW; +} + +void KGoldrunner::setKGrRules() +{ + KGrFigure::variableTiming = FALSE; + KGrFigure::alwaysCollectNugget = FALSE; + KGrFigure::runThruHole = FALSE; + KGrFigure::reappearAtTop = FALSE; + KGrFigure::searchStrategy = MEDIUM; +} + +// Local slots to make playing area larger or smaller. + +void KGoldrunner::makeLarger() +{ + if (view->changeSize (+1)) + setFixedSize (view->size()); +} + +void KGoldrunner::makeSmaller() +{ + if (view->changeSize (-1)) + setFixedSize (view->size()); +} + +// Local slots for hero control keys. + +void KGoldrunner::goUp() {setKey (KB_UP);} +void KGoldrunner::goR() {setKey (KB_RIGHT);} +void KGoldrunner::goDown() {setKey (KB_DOWN);} +void KGoldrunner::goL() {setKey (KB_LEFT);} +void KGoldrunner::stop() {setKey (KB_STOP);} +void KGoldrunner::digR() {setKey (KB_DIGRIGHT);} +void KGoldrunner::digL() {setKey (KB_DIGLEFT);} + +// Local slots for authors' debugging aids. + +void KGoldrunner::showEnemy0() {game->showEnemyState (0);} +void KGoldrunner::showEnemy1() {game->showEnemyState (1);} +void KGoldrunner::showEnemy2() {game->showEnemyState (2);} +void KGoldrunner::showEnemy3() {game->showEnemyState (3);} +void KGoldrunner::showEnemy4() {game->showEnemyState (4);} +void KGoldrunner::showEnemy5() {game->showEnemyState (5);} +void KGoldrunner::showEnemy6() {game->showEnemyState (6);} + +void KGoldrunner::saveProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. Anything you write here will be available + // later when this app is restored. + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::saveProperties.\n"); + // config->writeEntry("qqq", qqq); +} + +void KGoldrunner::readProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. This function is automatically called whenever + // the app is being restored. Read in here whatever you wrote + // in 'saveProperties' + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::readProperties.\n"); + // QString qqq = config->readEntry("qqq"); +} + +void KGoldrunner::optionsShowToolbar() +{ + // this is all very cut and paste code for showing/hiding the + // toolbar + // if (m_toolbarAction->isChecked()) + // toolBar()->show(); + // else + // toolBar()->hide(); +} + +void KGoldrunner::optionsShowStatusbar() +{ + // this is all very cut and paste code for showing/hiding the + // statusbar + // if (m_statusbarAction->isChecked()) + // statusBar()->show(); + // else + // statusBar()->hide(); +} + +void KGoldrunner::optionsConfigureKeys() +{ + KKeyDialog::configure(actionCollection()); + + // Update the PAUSE/RESUME message in the status bar. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (KGrObject::frozen); // Refresh the status bar text. +} + +void KGoldrunner::optionsConfigureToolbars() +{ + // use the standard toolbar editor +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + saveMainWindowSettings(KGlobal::config()); +# endif +#else + saveMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::newToolbarConfig() +{ + // this slot is called when user clicks "Ok" or "Apply" in the toolbar editor. + // recreate our GUI, and re-apply the settings (e.g. "text under icons", etc.) + createGUI(); + +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + applyMainWindowSettings(KGlobal::config()); +# endif +#else + applyMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::optionsPreferences() +{ + // popup some sort of preference dialog, here + // KGoldrunnerPreferences dlg; + // if (dlg.exec()) + // { + // redo your settings + // } +} + +void KGoldrunner::changeStatusbar(const QString& text) +{ + // display the text on the statusbar + statusBar()->message(text); +} + +void KGoldrunner::changeCaption(const QString& text) +{ + // display the text on the caption + setCaption(text); +} + +bool KGoldrunner::getDirectories() +{ + bool result = TRUE; + + // WHERE THINGS ARE: In the KDE 3 environment (Release 3.1.1), application + // documentation and data files are in a directory structure given by + // $KDEDIRS (e.g. "/usr/local/kde" or "/opt/kde3/"). Application user data + // files are in a directory structure given by $KDEHOME ("$HOME/.kde"). + // Within those two structures, the three sub-directories will typically be + // "share/doc/HTML/en/kgoldrunner/", "share/apps/kgoldrunner/system/" and + // "share/apps/kgoldrunner/user/". Note that it is necessary to have + // an extra path level ("system" or "user") after "kgoldrunner", otherwise + // all the KGoldrunner files have similar path names (after "apps") and + // KDE always locates directories in $KDEHOME and never the released games. + + // The directory strings are set by KDE at run time and might change in + // later releases, so use them with caution and only if something gets lost. + + KStandardDirs * dirs = new KStandardDirs(); + +#ifdef QT3 + QString myDir = "kgoldrunner"; +#else + QString myDir = "kgoldrun"; +#endif + + // Find the KGoldrunner Users' Guide, English version (en). + systemHTMLDir = dirs->findResourceDir ("html", "en/" + myDir + "/"); + if (systemHTMLDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find documentation sub-folder 'en/%1/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("html"))); + // result = FALSE; // Don't abort if the doc is missing. + } + else + systemHTMLDir.append ("en/" + myDir + "/"); + + // Find the system collections in a directory of the required KDE type. + systemDataDir = dirs->findResourceDir ("data", myDir + "/system/"); + if (systemDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find system games sub-folder '%1/system/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("data"))); + result = FALSE; // ABORT if the games data is missing. + } + else + systemDataDir.append (myDir + "/system/"); + + // Locate and optionally create directories for user collections and levels. + bool create = TRUE; + userDataDir = dirs->saveLocation ("data", myDir + "/user/", create); + if (userDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create user games sub-folder '%1/user/' " + "in area '%2' of the KDE user area ($KDEHOME).") + .arg(myDir).arg(dirs->kde_default ("data"))); + // result = FALSE; // Don't abort if user area is missing. + } + else { + create = dirs->makeDir (userDataDir + "levels/"); + if (! create) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create 'levels/' folder in " + "sub-folder '%1/user/' in the KDE user area ($KDEHOME).").arg(myDir)); + // result = FALSE; // Don't abort if user area is missing. + } + } + + return (result); +} + +// This method is invoked when top-level window is closed, whether by selecting +// "Quit" from the menu or by clicking the "X" at the top right of the window. + +bool KGoldrunner::queryClose () +{ + // Last chance to save: user has clicked "X" widget or menu-Quit. + bool cannotContinue = TRUE; + game->saveOK (cannotContinue); + return (TRUE); +} + +void KGoldrunner::setKey (KBAction movement) +{ + if (game->inEditMode()) return; + + // Using keyboard control can automatically disable mouse control. + if (game->inMouseMode()) { + // Halt the game while a message is displayed. + game->setMessageFreeze (TRUE); + + switch (KGrMessage::warning (this, i18n("Switch to Keyboard Mode"), + i18n("You have pressed a key that can be used to move the " + "Hero. Do you want to switch automatically to keyboard " + "control? Mouse control is easier to use in the long term " + "- like riding a bike rather than walking!"), + i18n("Switch to &Keyboard Mode"), i18n("Stay in &Mouse Mode"))) + { + case 0: game->setMouseMode (FALSE); // Set internal mouse mode OFF. + setMouse->setChecked (FALSE); // Adjust the Settings menu. + setKeyboard->setChecked (TRUE); + break; + case 1: break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + game->setMessageFreeze (FALSE); + + if (game->inMouseMode()) + return; // Stay in Mouse Mode. + } + + if ( game->getLevel() != 0 ) + { + if (! hero->started ) // Start when first movement + game->startPlaying(); // key is pressed ... + game->heroAction (movement); + } +} + +/******************************************************************************/ +/********************** MAKE A TOOLBAR FOR THE EDITOR **********************/ +/******************************************************************************/ + +#include +void KGoldrunner::makeEditToolbar() +{ + // Set up the pixmaps for the editable objects. + QPixmap pixmap; + QImage image; + + QPixmap brickbg = view->getPixmap (BRICK); + QPixmap fbrickbg = view->getPixmap (FBRICK); + + QPixmap freebg = view->getPixmap (FREE); + QPixmap nuggetbg = view->getPixmap (NUGGET); + QPixmap polebg = view->getPixmap (POLE); + QPixmap betonbg = view->getPixmap (BETON); + QPixmap ladderbg = view->getPixmap (LADDER); + QPixmap hladderbg = view->getPixmap (HLADDER); + QPixmap edherobg = view->getPixmap (HERO); + QPixmap edenemybg = view->getPixmap (ENEMY); + + if (usesBigPixmaps()) { // Scale up the pixmaps (to give cleaner looking + // icons than leaving it for QToolButton to do). + QWMatrix w; + w = w.scale (2.0, 2.0); + + // The pixmaps shown on the buttons used to remain small and incorrectly + // painted, in KDE, in spite of the 2x (32x32) scaling. "insertButton" + // calls QIconSet, to generate a set of icons from each pixmapx, then + // seems to select the small size to paint on the button. The following + // line forces all icons, large and small, to be size 32x32 in advance. + QIconSet::setIconSize (QIconSet::Small, QSize (32, 32)); + + brickbg = brickbg.xForm (w); + fbrickbg = fbrickbg.xForm (w); + + freebg = freebg.xForm (w); + nuggetbg = nuggetbg.xForm (w); + polebg = polebg.xForm (w); + betonbg = betonbg.xForm (w); + ladderbg = ladderbg.xForm (w); + hladderbg = hladderbg.xForm (w); + edherobg = edherobg.xForm (w); + edenemybg = edenemybg.xForm (w); + } + + editToolbar = new KToolBar (this, Qt::DockTop, TRUE, "Editor", TRUE); + + // Choose a colour that enhances visibility of the KGoldrunner pixmaps. + // editToolbar->setPalette (QPalette (QColor (150, 150, 230))); + + // editToolbar->setHorizontallyStretchable (TRUE); // Not effective in KDE. + + // All those separators are just to get reasonable visual spacing of the + // pixmaps in KDE. "setHorizontallyStretchable(TRUE)" does it in Qt. + + editToolbar->insertSeparator(); + + editToolbar->insertButton ("filenew", 0, SIGNAL(clicked()), game, + SLOT(createLevel()), TRUE, i18n("&Create a Level")); + editToolbar->insertButton ("fileopen", 1, SIGNAL(clicked()), game, + SLOT(updateLevel()), TRUE, i18n("&Edit Any Level...")); + editToolbar->insertButton ("filesave", 2, SIGNAL(clicked()), game, + SLOT(saveLevelFile()),TRUE, i18n("&Save Edits...")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton ("ktip", 3, SIGNAL(clicked()), game, + SLOT(editNameAndHint()),TRUE,i18n("Edit Name/Hint")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton (freebg, (int)FREE, SIGNAL(clicked()), this, + SLOT(freeSlot()), TRUE, i18n("Empty space")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edherobg, (int)HERO, SIGNAL(clicked()), this, + SLOT (edheroSlot()), TRUE, i18n("Hero")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edenemybg, (int)ENEMY, SIGNAL(clicked()), this, + SLOT (edenemySlot()), TRUE, i18n("Enemy")); + editToolbar->insertSeparator(); + editToolbar->insertButton (brickbg, (int)BRICK, SIGNAL(clicked()), this, + SLOT (brickSlot()), TRUE, i18n("Brick (can dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (betonbg, (int)BETON, SIGNAL(clicked()), this, + SLOT (betonSlot()), TRUE, i18n("Concrete (cannot dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (fbrickbg, (int)FBRICK, SIGNAL(clicked()), this, + SLOT (fbrickSlot()), TRUE, i18n("Trap (can fall through)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (ladderbg, (int)LADDER, SIGNAL(clicked()), this, + SLOT (ladderSlot()), TRUE, i18n("Ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (hladderbg, (int)HLADDER, SIGNAL(clicked()), this, + SLOT (hladderSlot()), TRUE, i18n("Hidden ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (polebg, (int)POLE, SIGNAL(clicked()), this, + SLOT (poleSlot()), TRUE, i18n("Pole (or bar)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (nuggetbg, (int)NUGGET, SIGNAL(clicked()), this, + SLOT (nuggetSlot()), TRUE, i18n("Gold nugget")); + + editToolbar->setToggle ((int) FREE, TRUE); + editToolbar->setToggle ((int) HERO, TRUE); + editToolbar->setToggle ((int) ENEMY, TRUE); + editToolbar->setToggle ((int) BRICK, TRUE); + editToolbar->setToggle ((int) BETON, TRUE); + editToolbar->setToggle ((int) FBRICK, TRUE); + editToolbar->setToggle ((int) LADDER, TRUE); + editToolbar->setToggle ((int) HLADDER, TRUE); + editToolbar->setToggle ((int) POLE, TRUE); + editToolbar->setToggle ((int) NUGGET, TRUE); + + pressedButton = (int) BRICK; + editToolbar->setButton (pressedButton, TRUE); +} + +/******************************************************************************/ +/********************* EDIT-BUTTON SLOTS **********************************/ +/******************************************************************************/ + +void KGoldrunner::freeSlot() + { game->setEditObj (FREE); setButton ((int) FREE); } +void KGoldrunner::edheroSlot() + { game->setEditObj (HERO); setButton ((int) HERO); } +void KGoldrunner::edenemySlot() + { game->setEditObj (ENEMY); setButton ((int) ENEMY); } +void KGoldrunner::brickSlot() + { game->setEditObj (BRICK); setButton ((int) BRICK); } +void KGoldrunner::betonSlot() + { game->setEditObj (BETON); setButton ((int) BETON); } +void KGoldrunner::fbrickSlot() + { game->setEditObj (FBRICK); setButton ((int) FBRICK); } +void KGoldrunner::ladderSlot() + { game->setEditObj (LADDER); setButton ((int) LADDER); } +void KGoldrunner::hladderSlot() + { game->setEditObj (HLADDER); setButton ((int) HLADDER); } +void KGoldrunner::poleSlot() + { game->setEditObj (POLE); setButton ((int) POLE); } +void KGoldrunner::nuggetSlot() + { game->setEditObj (NUGGET); setButton ((int) NUGGET); } +void KGoldrunner::defaultEditObj() + { setButton ((int) BRICK); } + +void KGoldrunner::setButton (int btn) +{ + editToolbar->setButton (pressedButton, FALSE); + pressedButton = btn; + editToolbar->setButton (pressedButton, TRUE); +} + +#include "kgoldrunner.moc" diff --git a/kgoldrunner/src/kgoldrunner.h b/kgoldrunner/src/kgoldrunner.h new file mode 100644 index 00000000..57123596 --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#ifndef _KGOLDRUNNER_H_ +#define _KGOLDRUNNER_H_ + +#ifdef HAVE_CONFIG_H +#include +#endif + +// Status bar +const int ID_LIVES = 0; // Field IDs in KDE status bar. +const int ID_SCORE = 1; +const int ID_LEVEL = 2; +const int ID_HINTAVL = 3; +const int ID_MSG = 4; + +const int L_LIVES = 15; // Lengths of fields. +const int L_SCORE = 17; +const int L_LEVEL = 15; + +#include +#include +#include +#include + +class KGrGame; +class KGrCanvas; +class KGrHero; + +/** + * This class serves as the main window for KGoldrunner. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author $AUTHOR <$EMAIL> + * @version $APP_VERSION + */ +class KGoldrunner : public KMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KGoldrunner(); + + /** + * Default Destructor + */ + virtual ~KGoldrunner(); + + bool startedOK() {return (startupOK);} + +protected: + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties. + */ + void readProperties(KConfig *); + + bool queryClose (); // To save edits before closing. + +private slots: + // Slot to pause or restart the game. + void stopStart(); + + // Local slots to create or edit game information. + void createGame(); + void editGameInfo(); + + // Local slots to set the landscape (colour scheme). + void lsKGoldrunner(); + void lsApple2(); + void lsIceCave(); + void lsMidnight(); + void lsKDEKool(); + + // Local slots to set mouse/keyboard control of the hero. + void setMouseMode(); + void setKeyBoardMode(); + + // Local slots to set game speed. + void normalSpeed(); + void beginSpeed(); + void champSpeed(); + void incSpeed(); + void decSpeed(); + + // Slots to set Traditional or KGoldrunner rules. + void setTradRules(); + void setKGrRules(); + + // Local slots to make playing area larger or smaller. + void makeLarger(); + void makeSmaller(); + + // Local slots for hero control keys. + void goUp(); + void goR(); + void goDown(); + void goL(); + void stop(); + void digR(); + void digL(); + + void setKey (KBAction movement); + + // Local slots for authors' debugging aids. + void showEnemy0(); + void showEnemy1(); + void showEnemy2(); + void showEnemy3(); + void showEnemy4(); + void showEnemy5(); + void showEnemy6(); + + void optionsShowToolbar(); + void optionsShowStatusbar(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + void optionsPreferences(); + void newToolbarConfig(); + + void changeStatusbar(const QString& text); + void changeCaption(const QString& text); + + void showLevel (int); // Show the current level number. + void showLives (long); // Show how many lives are remaining. + void showScore (long); // Show the player's score. + void gameFreeze (bool); // Status feedback on freeze/unfreeze. + + void adjustHintAction (bool); // Enable/disable "Hint" action. + void markRuleType (char ruleType); // Check game's rule type in the menu. + void setEditMenu (bool on_off); // Enable/disable "Save Edits" action. + +private: + void setupAccel(); + void setupActions(); + void initStatusBar(); + void makeEditToolbar(); + void setButton (int btn); + +private: + bool startupOK; + + KGrCanvas * view; + KGrGame * game; + + bool getDirectories(); // Get directory paths, as below. + QString systemHTMLDir; // Where the manual is stored. + QString systemDataDir; // Where the system levels are stored. + QString userDataDir; // Where the user levels are stored. + + KAction * saveGame; // Save game, level, lives and score. + + KAction * myPause; // Pause or resume the game. + QString pauseKeys; // Keystroke names to put in status bar. + + KAction * hintAction; // Display a hint, if available. + KAction * killHero; // Kill hero (disabled during edits). + KAction * highScore; // High scores (disabled during edits). + + KAction * saveEdits; // Save a level that has been edited. + + KRadioAction * setKGoldrunner; // Show default "KGoldrunner" landscape. + KRadioAction * setAppleII; // Show "Apple II" landscape. + KRadioAction * setIceCave; // Show "Ice Cave" landscape. + KRadioAction * setMidnight; // Show "Midnight" landscape. + KRadioAction * setKDEKool; // Show "KDE Kool" landscape. + + KRadioAction * setMouse; // Show mouse/keyboard mode on menu. + KRadioAction * setKeyboard; // Show mouse/keyboard mode on menu. + + KRadioAction * tradRules; // Set Traditional rules. + KRadioAction * kgrRules; // Set KGoldrunner rules. + + KGrHero * hero; // Pointer to the hero. + + // KToggleAction * m_toolbarAction; + // KToggleAction * m_statusbarAction; + + KToolBar * editToolbar; // Toolbar for creating/editing levels. + int pressedButton; // ID of currently set toolbar button. + +private slots: + void freeSlot(); // Set editObj to Free Space. + void edheroSlot(); // Set editObj to Hero. + void edenemySlot(); // Set editObj to Enemy. + void brickSlot(); // Set editObj to Brick. + void betonSlot(); // Set editObj to Concrete. + void fbrickSlot(); // Set editObj to Fall-through Brick. + void ladderSlot(); // Set editObj to Ladder. + void hladderSlot(); // Set editObj to Hidden Ladder. + void poleSlot(); // Set editObj to Pole (or Bar). + void nuggetSlot(); // Set editObj to Gold Nugget. + void defaultEditObj(); // Set editObj to default (brick). +}; + +#endif // _KGOLDRUNNER_H_ diff --git a/kgoldrunner/src/kgoldrunnerui.rc b/kgoldrunner/src/kgoldrunnerui.rc new file mode 100644 index 00000000..9b8ce1fe --- /dev/null +++ b/kgoldrunner/src/kgoldrunnerui.rc @@ -0,0 +1,75 @@ + + + + + + + + + + + + &Editor + + + + + + + + + + + + &Landscapes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kgoldrunner/src/kgraphics.h b/kgoldrunner/src/kgraphics.h new file mode 100644 index 00000000..81f004ad --- /dev/null +++ b/kgoldrunner/src/kgraphics.h @@ -0,0 +1,357 @@ +/*************************************************************************** + kgrgraphics.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRAPHICS_H +#define KGRAPHICS_H + +// List of colour schemes. +static const char * colourScheme [] = { + "KGoldrunner", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #c8b0a0", /* Background */ + "; c #b8a090", /* Background mortar */ + "o c #ff0000", /* Solid light */ + "x c #c05040", /* Solid */ + "s c #b00020", /* Solid dark */ + "+ c #500000", /* Mortar */ + ": c #b47444", /* Ladder light */ + "# c #845424", /* Ladder dark */ + "a c #ffffff", /* Pole or bar */ + "Apple II", + "#8a8acb", /* Border - Apple II blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #001020", /* Background */ + "; c #001020", /* Background mortar */ + "o c #8a8acb", /* Solid light */ + "x c #8a8acb", /* Solid */ + "s c #8a8acb", /* Solid dark */ + "+ c #001020", /* Mortar */ + ": c #dddddd", /* Ladder light */ + "# c #dddddd", /* Ladder dark */ + "a c #dddddd", /* Pole or bar */ + "Ice Cave", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #efefff", /* Background */ + "; c #d0dfef", /* Background mortar */ + "o c #ffffff", /* Solid light */ + "x c #d0f0ff", /* Solid */ + "s c #b0d8f0", /* Solid dark */ + "+ c #a8c8ff", /* Mortar */ + ": c #ffffff", /* Ladder light */ + "# c #f9d26a", /* Ladder dark */ + "a c #40a0ff", /* Pole or bar */ + "Midnight", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #000040", /* Background */ + "; c #000020", /* Background mortar */ + "o c #880000", /* Solid light */ + "x c #702820", /* Solid */ + "s c #680010", /* Solid dark */ + "+ c #200000", /* Mortar */ + ": c #563622", /* Ladder light */ + "# c #422a12", /* Ladder dark */ + "a c #666666", /* Pole or bar */ + "KDE Kool", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #eef7ff", /* Background */ + "; c #eef7ff", /* Background mortar */ + "o c #ecfdfe", /* Solid light */ + "x c #c1dafe", /* Solid */ + "s c #c1dafe", /* Solid dark */ + "+ c #9a9afe", /* Mortar */ + ": c #f9d26a", /* Ladder light */ + "# c #c19a68", /* Ladder dark */ + "a c #af7516", /* Pole or bar */ + "" /* TERMINATOR */ +}; + +/* XPM - Background brick or square (free space) */ +static const char * hgbrick_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Editor's icon for the hero */ +static const char * edithero_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"c c #008000", +"a c #00c000", +"b c #00ff00", +"d c #808080", +"...........;....", +"...........;....", +"..........a;....", +"........baacc...", +"........bcccc...", +".......bccca....", +"....caacccccc...", +";;;;bccacccaccc.", +"...;bccaacccaccc", +"...;caacccc.....", +"...;..dbccca....", +"...;.ccacccca...", +"...bcccccaccc...", +"...ac....bccc...", +"...;.....bccc...", +";;;;;;;;;caccc;;"}; + +/* XPM - Editor's icon for an enemy */ +static const char * editenemy_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"d c #000080", +"c c #0000ff", +"b c #008080", +"a c #00ffff", +"...........;....", +"...........;....", +"...........;....", +"........abbcc...", +"........acccc...", +".......acccd....", +"....bbbcccccc...", +";;;;accbcccbccbd", +"....accbbccbbccc", +"....bbbcccc.bdd.", +"....;..acccb....", +"....;bbbccccc...", +"...acccccbccc...", +"...bcddddaccc...", +"....;....accc...", +";;;;;;;;;bbccc;;"}; + +/* XPM - Ladder */ +static const char * ladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#::::::::::::##", +":############:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":#::::::::::::##", +":############:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Hidden ladder (for Editor only) */ +static const char * hladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#:::::::..;.:##", +":########..;.:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":##.;..:::::::##", +":##.;..######:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Nugget */ +static const char * nugget_xpm []={ +"16 16 12 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"a c #c0b000", +"c c #e08000", +"b c #ffff00", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"......bba..;....", +";;;;bbbbbcca;;;;", +"...bbbababacc...", +"..abbbababaac...", +"..bbbbbababaa...", +"..bbbbbbabaac...", +"..abbababacca...", +"...ababacacc....", +"....acacacc;....", +";;;;;;aaa;;;;;;;"}; + +/* XPM - Pole or bar */ +static const char * pole_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"aaaaaaaaaaaaaaaa", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Concrete */ +static const char * beton_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sxssxssxssxsssss", +"sxxsxxsxxsxsxxsx", +"xsxsxsxssxxssxxs", +"xssxsxxsxssxsxss", +"sxxsxssxsxsxxsxs", +"xxsxxxssxsxsxxsx", +"sxsxssxxsxxssxsx", +"xsxsxssxsxsxsxss", +"xssxxsxssxssxsxs", +"xsxsxssxssxxxsxx", +"sxxssxxsxxsxsxss", +"sxsxsssxsxsxsxxs", +"sxssxxssxsxssxss", +"ssxssxsxsxsxsxxs", +"xsxxsxssxsxxsxss", +"++++++++++++++++"}; + +/* XPM - Bricks (10 pics - from whole brick to background [hole] and back) */ +static const char * bricks_xpm []={ +"160 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sosossosos++osssssx;x;.x;;++s++sx;+;x;.x;;;;;+++x;x+x+.x;;;;;;.;;;;;;;.;;;;;;+.;...........;....osos+;.x+++;ssossososossos++osssososososso++ossssossossoso++osss", +"sxsxsxsxsx++oxsxsx+..;...x;++;+x+....;...++;;+++.....;+.+x;;;..;.....;...x;;+..;...........;....xs;++;...x;;+++xsx+;;+;+;+++o+xxxsxsx;sxsx++oxsx+xsxsxxxsx++osxs", +"xsxsxsxxsx++osxsxxsxs;+x;.;+osxs+;+;++;x;.;;x;+;+x;.x.;x;.;;++++;x;.x.;x;.;;x;.;...........;....+;+;x+;x;.+;+;sxs+;++++++;++o++xsxxx+++sxx++osxssxsxssxsxx++oxsx", +"xsxssxsxsx++oxxsxxssxsx+x+++osxsxsxs;..+;x;;++;s+++.;....x+;;.;+..;.;.+..x;;;.;;...........;....s++.;....x+;;+;xsxs++.+++++;s++xsssx++xsx++;osxxxsxxxxsxss++oxss", +"xsxxxsxxsx++osxssxsxsxsxsx++osxss+++x+++++;;x++;+.x.x;.+.;+;x+x;;.x.x;.;.;;;x.x;...........;....;.x.++.;.;;;++x.s;+xx++;.;+;o;+sxxxsx+.;.+;;s+sxsxssxsxx+x++osxs", +"xsxsx+sxss++oxsxxxxxsxsxss++osxxsx+++;+;+;+;++sxs;.+..x.;x;;.+++.;.;..x.++;;.;.x...........;....+;...+x.;x;;.+.xxs+;.+x.+;+;o+;ssxs++.x.+;+;osxxsxx++;s+++++oxsx", +"ssxxsxxxsx++oxssxssxxsxxxs++oxsxxs+;+++++++;oxss;+;.;;;;++;;++;s;x+.;;;+.;;;;.;+...........;....;x....+..;;;+.;;s;++;;+;.+;;s++xsxxs;;;;.+;;oxsxsx+sxsx;.;++oxsx", +"+++++++++++++++++++++++++++++++++++++++;+++;++++;;;;;+;;;;;;++++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;;;;;;;;;++;;++;;;;;;;;;;;++++++++;;;;;;;++++++++++;;;;;+++++", +"sss++ossssossssssss++ossssosssososo++osssssossso+;;+;s;+;x++++so;...;x;++x;x+.x+....;...........x....x+x;x;x;.x;s+++;x;x;x;x;++ssss++++x;x++++++sso++osx;xs+ssso", +"xsx++osxsx+xsxxsxsx++oxsxxsxsxsxxsx++oxsxsxsxxsxsxx+;+++...+;+;s++x+;;......;+......;.............x+;;......+...x+;+;+......++;sxsx++s;..+;+;+;sxsx++ox.+s++xsxs", +"sxs++oxsxsxsxssx+sx++osxs+xsxxx+sxs++osx+xs+ss;sxs;+;++;++.;+++x.;;;;x++;++x+;+;....;...........;+;;;x..x..x;.++s+++;+;;;;;s;+xsxxs+++++;+sx++xsxxs++osxx+sxsxxx", +"xsx++oxssxsxsxssxss++oxsxsxxsssxsxx++oxsssxxsxssxsx++os;+xs+xsxsx.++;+++++++++++....;...........++x+;.....;.+.;+x+;++os..s;+++++xsx++ox++;s+xxsxxsx++oxs++s++xss", +"xsx++oxsxxxsxxxsxxx++oxsxsx+xsxxxss++osxxxssxsxxsxx++oxssxsxsxxs;+;;;;+;+;++;+x+....;...........;+;;;+.x;.+;+..+s;+++ox+;;++;+++sxx++os;++;xssxssxx++osxsxsxssxx", +"xsx++ox+xssxssxsxsx++oxxssxsxxssxxx++osxxxxsxxsxssx++osxsxxsxssxsx;;;+++++x+++++....;...........xs++;.+.++;s+.+;sxs++o++sx+x++;;ssx++oxsxs+sxsxxsxs++oxsxsxxxxsx", +"sxs++osssxxsxxsxxsx++osxsxsxssxssxs++oxssxsxsxsxxxs++osxxsxsxsxssx+;;+;;;+++++++....;...........s;s+;.x..x.;+;sxx+x++o+;++++++sxsxs++ossxs;sxsxsxxs++oxssxssxssx", +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++;;;;;;++;+++;;;;;;;;;;;;;;;;+;++;;;;;;;;;+++++++++++++++++++++++++++++++++++++++++++++++++++"}; +#endif // KGRAPHICS_H diff --git a/kgoldrunner/src/kgrcanvas.cpp b/kgoldrunner/src/kgrcanvas.cpp new file mode 100644 index 00000000..6104939e --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.cpp @@ -0,0 +1,561 @@ +/*************************************************************************** + kgrcanvas.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" + +#include "kgrdialog.h" +#include "kgrcanvas.h" +#include "kgrgame.h" + +// Graphics files for moving figures and background. +#include "hero.xpm" +#include "enemy1.xpm" +#include "enemy2.xpm" +#include "kgraphics.h" + +class KGoldrunner; + +KGrCanvas::KGrCanvas (QWidget * parent, const char *name) + : QCanvasView (0, parent, name) +{ + setBackgroundMode (NoBackground); + m = new QCursor (); // For handling the mouse. + + scaleStep = STEP; // Initial scale is 1:1. + baseScale = scaleStep; + baseFontSize = fontInfo().pointSize(); + + border = 4; // Allow 2 tile-widths on each side for border. + cw = 4*STEP; // Playfield cell width (= four steps). + bw = border*cw/2; // Total border width (= two cells). + lw = cw/8; // Line width (for edge of border). + mw = bw - lw; // Border main-part width. + + initView(); // Set up the graphics, etc. +} + +KGrCanvas::~KGrCanvas() +{ +} + +void KGrCanvas::changeLandscape (const QString & name) +{ + for (int i = 0; strcmp (colourScheme [i], "") != 0; i++) { + if (colourScheme [i] == name) { + + // Change XPM colours and re-draw the tile-pictures used by QCanvas. + changeColours (& colourScheme [i]); + makeTiles(); + + // Set all cells to same tile-numbers as before, but new colours. + int tileNo [FIELDWIDTH] [FIELDHEIGHT]; + int offset = border / 2; + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + tileNo[x][y] = field->tile (x + offset, y + offset); + } + } + + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); // Sets all tile-numbers to 0. + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + field->setTile (x + offset, y + offset, tileNo[x][y]); + } + } + + borderB->setBrush (QBrush (borderColor)); + borderL->setBrush (QBrush (borderColor)); + borderR->setBrush (QBrush (borderColor)); + + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Repaint the playing area. + updateCanvas(); + return; + } + } +} + +bool KGrCanvas::changeSize (int d) +{ +#ifdef QT3 + if ((d < 0) && (scaleStep <= STEP)) { + // Note: Smaller scales lose detail (e.g. the joints in brickwork). + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any smaller.")); + return FALSE; + } + + if ((d >= 0) && (scaleStep >= 2 * STEP)) { + // Note: Larger scales go off the edge of the monitor. + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any larger.")); + return FALSE; + } + + QWMatrix wm = worldMatrix(); + double wmScale = 1.0; + + // Set the scale back to 1:1 and calculate the new scale factor. + wm.reset(); + scaleStep = (d < 0) ? (scaleStep - 1) : (scaleStep + 1); + + // If scale > 1:1, scale up to the new factor (e.g. 1.25:1, 1.5:1, etc.) + if (scaleStep > STEP) { + wmScale = (wmScale * scaleStep) / STEP; + wm.scale (wmScale, wmScale); + } + setWorldMatrix (wm); + + // Force the title size and position to be re-calculated. + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Fit the QCanvasView and its frame to the canvas. + int frame = frameWidth()*2; + setFixedSize ((FIELDWIDTH + 4) * 4 * scaleStep + frame, + (FIELDHEIGHT + 4) * 4 * scaleStep + frame); + return TRUE; + +#else + KGrMessage::information (this, i18n( "Change Size" ), + i18n( "Sorry, you cannot change the size of the playing area. " + "That function requires Qt Library version 3 or later." )); + return FALSE; +#endif +} + +void KGrCanvas::updateCanvas() +{ + field->update(); +} + +void KGrCanvas::paintCell (int x, int y, char type, int offset) +{ + int tileNumber = 0; + + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + tileNumber = tileNumber + offset; // Offsets 1-9 are for digging sequence. + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + x++; y++; + field->setTile (x, y, tileNumber); // Paint cell with required pixmap. +} + +void KGrCanvas::setBaseScale () +{ + // Synchronise the desktop font size with the initial canvas scale. + baseScale = scaleStep; + QString t = title->text(); + makeTitle (); + setTitle (t); +} + +void KGrCanvas::setTitle (QString newTitle) +{ + title->setText (newTitle); +} + +void KGrCanvas::makeTitle () +{ + // This uses a calculated QLabel and QFont size because a QCanvasText + // object does not always display scaled-up fonts cleanly (in Qt 3.1.1). + + if (title != 0) + title->close (TRUE); // Close and delete previous title. + + title = new QLabel ("", this); + title->setFixedWidth (((FIELDWIDTH * cw + 2 * bw) * scaleStep) / STEP); + title->setFixedHeight ((mw * scaleStep) / STEP); + title->move (0, 0); + title->setPaletteBackgroundColor (borderColor); + title->setPaletteForegroundColor (textColor); + title->setFont (QFont (fontInfo().family(), + (baseFontSize * scaleStep) / baseScale, QFont::Bold)); + title->setAlignment (Qt::AlignCenter); + title->raise(); + title->show(); +} + +void KGrCanvas::contentsMousePressEvent (QMouseEvent * m) { + emit mouseClick (m->button ()); +} + +void KGrCanvas::contentsMouseReleaseEvent (QMouseEvent * m) { + emit mouseLetGo (m->button ()); +} + +QPoint KGrCanvas::getMousePos () +{ + int i, j; + int fw = frameWidth(); + int cell = 4 * scaleStep; + QPoint p = mapFromGlobal (m->pos()); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i = ((p.x() - fw) / cell) - 1; j = ((p.y() - fw) / cell) - 1; + + return (QPoint (i, j)); +} + +void KGrCanvas::setMousePos (int i, int j) +{ + int fw = frameWidth(); + int cell = 4 * scaleStep; + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + //m->setPos (mapToGlobal (QPoint (i * 4 * STEP + 8, j * 4 * STEP + 8))); + //m->setPos (mapToGlobal (QPoint (i * 5 * STEP + 10, j * 5 * STEP + 10))); + m->setPos (mapToGlobal ( + QPoint (i * cell + fw + cell / 2, j * cell + fw + cell / 2))); +} + +void KGrCanvas::makeHeroSprite (int i, int j, int startFrame) +{ + heroSprite = new QCanvasSprite (heroArray, field); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + heroSprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + heroSprite->setZ (1); + heroSprite->setVisible (TRUE); +} + +void KGrCanvas::setHeroVisible (bool newState) +{ + heroSprite->setVisible (newState); // Show or hide the hero. +} + +void KGrCanvas::makeEnemySprite (int i, int j, int startFrame) +{ + QCanvasSprite * enemySprite = new QCanvasSprite (enemyArray, field); + + enemySprites->append (enemySprite); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + enemySprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + enemySprite->setZ (2); + enemySprite->show(); +} + +void KGrCanvas::moveHero (int x, int y, int frame) +{ + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + heroSprite->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::moveEnemy (int id, int x, int y, int frame, int nuggets) +{ + if (nuggets != 0) { // If enemy is carrying gold, + frame = frame + goldEnemy; // show him with gold outline. + } + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + enemySprites->at(id)->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::deleteEnemySprites() +{ + enemySprites->clear(); +} + +QPixmap KGrCanvas::getPixmap (char type) +{ + QPixmap pic (bgw, bgh, bgd); + QPainter p (& pic); + int tileNumber; + + // Get a pixmap from the tile-array for use on an edit-button. + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + // Copy a tile of width bgw and height bgh from the tile-array. + p.drawPixmap (0, 0, bgPix, tileNumber * bgw, 0, bgw, bgh); + p.end(); + + return (pic); +} + +void KGrCanvas::initView() +{ + changeColours (& colourScheme [0]); // Set "KGoldrunner" landscape. + + // Set up the pixmaps for the editable objects. + freebg = 0; // Free space. + nuggetbg = 1; // Nugget. + polebg = 2; // Pole or bar. + ladderbg = 3; // Ladder. + hladderbg = 4; // Hidden ladder (for editing). + edherobg = 5; // Static hero (for editing). + edenemybg = 6; // Static enemy (for editing). + betonbg = 7; // Concrete. + + // The bricks have 10 pixmaps (showing various stages of digging). + brickbg = 8; // Solid brick - 1st pixmap. + fbrickbg = 15; // False brick - 8th pixmap (for editing). + + QPixmap pixmap; + QImage image; + + pixmap = QPixmap (hgbrick_xpm); + + bgw = pixmap.width(); // Save dimensions for "getPixmap". + bgh = pixmap.height(); + bgd = pixmap.depth(); + + // Assemble the background and editing pixmaps into a strip (18 pixmaps). + bgPix = QPixmap ((brickbg + 10) * bgw, bgh, bgd); + + makeTiles(); // Fill the strip with 18 tiles. + + // Define the canvas as an array of tiles. Default tile is 0 (free space). + int frame = frameWidth()*2; + field = new QCanvas ((FIELDWIDTH+border) * bgw, (FIELDHEIGHT+border) * bgh); + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); + + // Embed the canvas in the view and make it occupy the whole of the view. + setCanvas (field); + setVScrollBarMode (QScrollView::AlwaysOff); + setHScrollBarMode (QScrollView::AlwaysOff); + setFixedSize (field->width() + frame, field->height() + frame); + + ////////////////////////////////////////////////////////////////////////// + // The pixmaps for hero and enemies are arranged in strips of 20: walk // + // right (4), walk left (4), climb right along bar (4), climb left (4), // + // climb up ladder (2) and fall (2) --- total 20. // + ////////////////////////////////////////////////////////////////////////// + + // Convert the pixmap strip for hero animation into a QCanvasPixmapArray. + pixmap = QPixmap (hero_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + QPixmap pm; + QValueList pmList; + + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + heroArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + QPixmap * pm; + QPoint * pt; + QList pmList; + QList ptList; + + pt = new QPoint (0, 0); // "Hot spot" not used in KGoldrunner. + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + heroArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + // Convert pixmap strips for enemy animations into a QCanvasPixmapArray. + // First convert the pixmaps for enemies with no gold ... + pixmap = QPixmap (enemy1_xpm); + image = pixmap.convertToImage (); + + pmList.clear(); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } +#else + ptList.clear(); + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } +#endif + + // ... then convert the gold-carrying enemies. + pixmap = QPixmap (enemy2_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + enemyArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + enemyArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + goldEnemy = 20; // Offset of gold-carrying frames. + + // Draw the border around the playing area (z = 0). + makeBorder(); + + // Create a title item, in off-white colour, on top of the border. + title = 0; + makeTitle(); + + // Create an empty list of enemy sprites. +#ifdef QT3 + enemySprites = new QPtrList (); +#else + enemySprites = new QList (); +#endif + enemySprites->setAutoDelete(TRUE); +} + +void KGrCanvas::makeTiles () +{ + QPainter p (& bgPix); + + // First draw the single pixmaps (8 tiles) ... + p.drawPixmap (freebg * bgw, 0, QPixmap (hgbrick_xpm)); // Free space. + p.drawPixmap (nuggetbg * bgw, 0, QPixmap (nugget_xpm)); // Nugget. + p.drawPixmap (polebg * bgw, 0, QPixmap (pole_xpm)); // Pole or bar. + p.drawPixmap (ladderbg * bgw, 0, QPixmap (ladder_xpm)); // Ladder. + p.drawPixmap (hladderbg * bgw, 0, QPixmap (hladder_xpm)); // Hidden laddr. + p.drawPixmap (edherobg * bgw, 0, QPixmap (edithero_xpm)); // Static hero. + p.drawPixmap (edenemybg * bgw, 0, QPixmap (editenemy_xpm)); // Static enemy. + p.drawPixmap (betonbg * bgw, 0, QPixmap (beton_xpm)); // Concrete. + + // ... then add the 10 brick pixmaps. + p.drawPixmap (brickbg * bgw, 0, QPixmap (bricks_xpm)); // Bricks. + + p.end(); +} + +void KGrCanvas::makeBorder () +{ + // Draw main part of border, in the order: top, bottom, left, right. + // Allow some overlap to prevent slits appearing when using "changeSize". + colour = borderColor; + + // The first rectangle is actually a QLabel drawn by "makeTitle()". + // borderT = drawRectangle (11, 0, 0, FIELDWIDTH*cw + 2*bw, mw); + borderB = drawRectangle (11, 0, FIELDHEIGHT*cw + bw + lw, + FIELDWIDTH*cw + 2*bw, mw); + borderL = drawRectangle (12, 0, bw - lw - 1, mw, FIELDHEIGHT*cw + 2*lw + 2); + borderR = drawRectangle (12, FIELDWIDTH*cw + bw + lw, bw - lw - 1, + mw, FIELDHEIGHT*cw + 2*lw + 2); + + // Draw inside edges of border, in the same way. + colour = QColor (black); + drawRectangle (10, bw-lw, bw-lw-1, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw-lw, FIELDHEIGHT*cw + bw, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw - lw, bw, lw, FIELDHEIGHT*cw); + drawRectangle (10, FIELDWIDTH*cw + bw, bw, lw, FIELDHEIGHT*cw); +} + +QCanvasRectangle * KGrCanvas::drawRectangle (int z, int x, int y, int w, int h) +{ + QCanvasRectangle * r = new QCanvasRectangle (x, y, w, h, field); + + r->setBrush (QBrush (colour)); + r->setPen (QPen (NoPen)); + r->setZ (z); + r->show(); + + return (r); +} + +void KGrCanvas::changeColours (const char * colours []) +{ + recolourObject (hgbrick_xpm, colours); + recolourObject (nugget_xpm, colours); + recolourObject (pole_xpm, colours); + recolourObject (ladder_xpm, colours); + recolourObject (hladder_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (editenemy_xpm, colours); + recolourObject (beton_xpm, colours); + recolourObject (bricks_xpm, colours); + + borderColor = QColor (colours [1]); + textColor = QColor (colours [2]); + + KGrThumbNail::backgroundColor = QColor (QString(colours [3]).right(7)); + KGrThumbNail::brickColor = QColor (QString(colours [6]).right(7)); + KGrThumbNail::ladderColor = QColor (QString(colours [9]).right(7)); + KGrThumbNail::poleColor = QColor (QString(colours [11]).right(7)); +} + +void KGrCanvas::recolourObject (const char * object [], const char * colours []) +{ + int i; + for (i = 0; i < 9; i++) { + object [i+1] = colours [i+3]; + } +} + +#include "kgrcanvas.moc" diff --git a/kgoldrunner/src/kgrcanvas.h b/kgoldrunner/src/kgrcanvas.h new file mode 100644 index 00000000..02b89bed --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.h @@ -0,0 +1,111 @@ +/*************************************************************************** + kgrcanvas.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRCANVAS_H +#define KGRCANVAS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +class KGrCanvas : public QCanvasView +{ + Q_OBJECT +public: + KGrCanvas (QWidget * parent = 0, const char *name = 0); + virtual ~KGrCanvas(); + + void changeLandscape (const QString & name); + + QPoint getMousePos (); + void setMousePos (int, int); + + bool changeSize (int); + void setBaseScale (); + + void updateCanvas (); + void paintCell (int, int, char, int offset = 0); + void setTitle (QString); + + void makeHeroSprite (int, int, int); + void setHeroVisible (bool); + void moveHero (int, int, int); + + void makeEnemySprite (int, int, int); + void moveEnemy (int, int, int, int, int); + void deleteEnemySprites(); + + QPixmap getPixmap (char type); + +signals: + void mouseClick (int); + void mouseLetGo (int); + +protected: + void contentsMousePressEvent (QMouseEvent *); + void contentsMouseReleaseEvent (QMouseEvent *); + +private: + QCursor * m; + + QCanvas * field; + QCanvasView * fieldView; + int scaleStep; // Current scale-factor of canvas. + int baseScale; // Starting scale-factor of canvas. + int baseFontSize; + + int border; // Number of tiles allowed for border. + int cw, bw, lw, mw; // Dimensions (in pixels) of the border. + QColor borderColor, textColor; // Border colours. + QLabel * title; // Title and top part of border. + QCanvasRectangle * borderB; // Bottom part of border. + QCanvasRectangle * borderL; // Left-hand part of border. + QCanvasRectangle * borderR; // Right-hand part of border. + + int freebg, nuggetbg, polebg, ladderbg, hladderbg; + int edherobg, edenemybg, betonbg, brickbg, fbrickbg; + int bgw, bgh, bgd; + QPixmap bgPix; + + QCanvasPixmapArray * heroArray; + QCanvasPixmapArray * enemyArray; + int goldEnemy; + + QCanvasSprite * heroSprite; +#ifdef QT3 + QPtrList * enemySprites; +#else + QList * enemySprites; +#endif + + void initView(); + void makeTiles(); + void makeBorder(); + void makeTitle(); + QColor colour; + QCanvasRectangle * drawRectangle (int, int, int, int, int); + void changeColours (const char * colours []); + void recolourObject (const char * object [], const char * colours []); +}; + +#endif // KGRCANVAS_H diff --git a/kgoldrunner/src/kgrconsts.h b/kgoldrunner/src/kgrconsts.h new file mode 100644 index 00000000..2006648a --- /dev/null +++ b/kgoldrunner/src/kgrconsts.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * kgrconsts.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRCONSTS_H +#define KGRCONSTS_H + +enum Owner {SYSTEM, USER}; + +const char FREE = ' '; +const char ENEMY = 'E'; +const char HERO = 'R'; +const char BETON = 'X'; +const char BRICK = 'M'; +const char FBRICK = 'F'; +const char HLADDER = 'Z'; +const char LADDER = 'H'; +const char NUGGET = 'N'; +const char POLE = 'T'; +const char HOLE = 'O'; +const char USEDHOLE= 'U'; + +const char CANWALKLEFT = 0x1; +const char CANWALKRIGHT = 0x2; +const char CANWALKUP = 0x4; +const char CANWALKDOWN = 0x8; +const char VISITED = 0x10; + +const char FIELDWIDTH = 28; +const char FIELDHEIGHT = 20; + +const char VERTIKAL = 0; +const char HORIZONTAL = 1; + +/* Action times ... */ +#define NSPEED 12 +#define MAXSPEED NSPEED * 2 +#define MINSPEED NSPEED / 4 + +#define BEGINSPEED NSPEED / 2 +#define NOVICESPEED (3 * NSPEED) / 4 +#define CHAMPSPEED (3 * NSPEED) / 2 + +typedef struct { + int hwalk; + int hfall; + int ewalk; + int efall; + int ecaptive; + int hole; +} Timing; + +const int DIGDELAY = 200; + +const int STEP = 4; + +const double DROPNUGGETDELAY = 70.0; // Enemy holds gold for avg. 12.5 cells. + +enum Position {RIGHTWALK1,RIGHTWALK2,RIGHTWALK3,RIGHTWALK4, + LEFTWALK1,LEFTWALK2,LEFTWALK3,LEFTWALK4, + RIGHTCLIMB1,RIGHTCLIMB2,RIGHTCLIMB3,RIGHTCLIMB4, + LEFTCLIMB1,LEFTCLIMB2,LEFTCLIMB3,LEFTCLIMB4, + CLIMB1,CLIMB2, + FALL1,FALL2}; +enum Status {STANDING,FALLING,WALKING,CLIMBING,CAPTIVE}; +enum Direction {RIGHT,LEFT,UP,DOWN,STAND}; +enum SearchStrategy {LOW,MEDIUM,HIGH}; + +// Keyboard action codes +enum KBAction {KB_UP, KB_DOWN, KB_LEFT, KB_RIGHT, + KB_DIGLEFT, KB_DIGRIGHT, KB_STOP}; + +// Action codes when selecting a level or game for play or editing. +enum SelectAction {SL_START, SL_ANY, SL_CREATE, SL_UPDATE, SL_SAVE, + SL_MOVE, SL_DELETE, SL_CR_GAME, SL_UPD_GAME}; + +#endif // KGRCONSTS_H diff --git a/kgoldrunner/src/kgrdialog.cpp b/kgoldrunner/src/kgrdialog.cpp new file mode 100644 index 00000000..61ca2f29 --- /dev/null +++ b/kgoldrunner/src/kgrdialog.cpp @@ -0,0 +1,974 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrcanvas.h" +#include "kgrgame.h" +#include "kgrdialog.h" + +#ifndef KGR_PORTABLE +#include +#endif + +/******************************************************************************/ +/***************** DIALOG BOX TO SELECT A GAME AND LEVEL *****************/ +/******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Game"), + KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help, + KDialogBase::Ok, parent, name) +#endif +{ + slAction = action; + defaultLevel = requestedLevel; + defaultGame = collnIndex; + collections = gamesList; + game = theGame; + collection = collections.at(defaultGame); + slParent = parent; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + collnL = new QLabel (i18n("List of games:"), dad); + mainLayout->addWidget (collnL); + colln = new QListBox (dad); + mainLayout->addWidget (colln); + + QHBox * gameInfo = new QHBox (dad); + mainLayout->addWidget (gameInfo); + gameInfo->setSpacing (spacing); + collnN = new QLabel ("", gameInfo); // Name of selected collection. + QFont f = collnN->font(); + f.setBold (TRUE); + collnN->setFont (f); + collnA = new QPushButton (i18n("More Info"), gameInfo); + + collnD = new QLabel ("", dad); // Description of collection. + mainLayout->addWidget (collnD); + + QFrame * separator = new QFrame (dad); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + if ((action == SL_START) || (action == SL_UPD_GAME)) { + dad-> setCaption (i18n("Select Game")); + QLabel * startMsg = new QLabel + ("" + i18n("Level 1 of the selected game is:") + "", dad); + mainLayout->addWidget (startMsg); + } + else { + dad-> setCaption (i18n("Select Game/Level")); + QLabel * selectLev = new QLabel (i18n("Select level:"), dad); + mainLayout->addWidget (selectLev); + } + + QGridLayout * grid = new QGridLayout (3, 2, -1); + mainLayout->addLayout (grid); + + // Initial range 1->150, small step 1, big step 10 and default value 1. + number = new QScrollBar (1, 150, 1, 10, 1, + QScrollBar::Horizontal, dad); + grid->addWidget (number, 1, 1); + + QHBox * numberPair = new QHBox (dad); + grid->addWidget (numberPair, 2, 1); + numberPair->setSpacing (spacing); + numberL = new QLabel (i18n("Level number:"), numberPair); + display = new QLineEdit (numberPair); + + levelNH = new QPushButton (i18n("Edit Level Name && Hint"), dad); + mainLayout->addWidget (levelNH); + + slName = new QLabel ("", dad); + grid->addWidget (slName, 3, 1); + thumbNail = new KGrThumbNail (dad); + grid->addMultiCellWidget (thumbNail, 1, 3, 2, 2); + + // Set thumbnail cell size to about 1/5 of game cell size. + int cellSize = parent->width() / (5 * (FIELDWIDTH + 4)); + thumbNail-> setFixedWidth ((FIELDWIDTH * cellSize) + 2); + thumbNail-> setFixedHeight ((FIELDHEIGHT * cellSize) + 2); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (this); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + HELP = new QPushButton (i18n("Help"), buttons); + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); + + OK-> setAccel (Key_Return); + HELP-> setAccel (Key_F1); + CANCEL-> setAccel (Key_Escape); +#endif + + // Set the default for the level-number in the scrollbar. + number-> setTracking (TRUE); + number->setValue (requestedLevel); + + slSetCollections (defaultGame); + + // Vary the dialog according to the action. + QString OKText = ""; + switch (slAction) { + case SL_START: // Must start at level 1, but can choose a collection. + OKText = i18n("Start Game"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + case SL_ANY: // Can start playing at any level in any collection. + OKText = i18n("Play Level"); + break; + case SL_UPDATE: // Can use any level in any collection as edit input. + OKText = i18n("Edit Level"); + break; + case SL_CREATE: // Can save a new level only in a USER collection. + OKText = i18n("Save New"); + break; + case SL_SAVE: // Can save an edited level only in a USER collection. + OKText = i18n("Save Change"); + break; + case SL_DELETE: // Can delete a level only in a USER collection. + OKText = i18n("Delete Level"); + break; + case SL_MOVE: // Can move a level only into a USER collection. + OKText = i18n("Move To..."); + break; + case SL_UPD_GAME: // Can only edit USER collection details. + OKText = i18n("Edit Game Info"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + + default: break; // Keep the default settings. + } + if (!OKText.isEmpty()) { +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + } + + // Set value in the line-edit box. + slShowLevel (number->value()); + + if (display->isEnabled()) { + display->setFocus(); // Set the keyboard input on. + display->selectAll(); + display->setCursorPosition (0); + } + + // Paint a thumbnail sketch of the level. + thumbNail->setFrameStyle (QFrame::Box | QFrame::Plain); + thumbNail->setLineWidth (1); + slPaintLevel(); + thumbNail->show(); + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slColln (int))); + connect (collnA, SIGNAL (clicked ()), this, SLOT (slAboutColln ())); + + connect (display, SIGNAL (textChanged (const QString &)), + this, SLOT (slUpdate (const QString &))); + + connect (number, SIGNAL (valueChanged(int)), this, SLOT(slShowLevel(int))); + + // Only enable name and hint dialog here if saving a new or edited level. + // At other times the name and hint have not been loaded or initialised yet. + if ((slAction == SL_CREATE) || (slAction == SL_SAVE)) { + connect (levelNH, SIGNAL (clicked()), game, SLOT (editNameAndHint())); + } + else { + levelNH->setEnabled (FALSE); + levelNH->hide(); + } + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slPaintLevel ())); + connect (number, SIGNAL (sliderReleased()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextPage()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevPage()), this, SLOT (slPaintLevel())); + +#ifdef KGR_PORTABLE + // Set the exits from this dialog box. + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); + connect (HELP, SIGNAL (clicked ()), this, SLOT (slotHelp ())); +#endif +} + +KGrSLDialog::~KGrSLDialog() +{ +} + +/******************************************************************************/ +/***************** LOAD THE LIST OF GAMES (COLLECTIONS) *****************/ +/******************************************************************************/ + +void KGrSLDialog::slSetCollections (int cIndex) +{ + int i; + int imax = collections.count(); + + // Set values in the combo box that holds collection names. + colln->clear(); + slCollnIndex = -1; + + for (i = 0; i < imax; i++) { + colln->insertItem (collections.at(i)->name, -1); + if (slCollnIndex < 0) { + slCollnIndex = i; // There is at least one collection. + } + } + + if (slCollnIndex < 0) { + return; // There are no collections (unlikely). + } + // Mark the currently selected collection (or default 0). + colln->setCurrentItem (cIndex); + colln->setSelected (cIndex, TRUE); + + // Fetch and display information on the selected collection. + slColln (cIndex); +} + +/******************************************************************************/ +/***************** SLOTS USED BY LEVEL SELECTION DIALOG *****************/ +/******************************************************************************/ + +void KGrSLDialog::slColln (int i) +{ + if (slCollnIndex < 0) { + // Ignore the "highlighted" signal caused by inserting in an empty box. + return; + } + + // User "highlighted" a new collection (with one click) ... + colln->setSelected (i, TRUE); // One click = selected. + slCollnIndex = i; + int n = slCollnIndex; // Collection selected. + int N = defaultGame; // Current collection. + if (collections.at(n)->nLevels > 0) + number->setMaxValue (collections.at(n)->nLevels); + else + number->setMaxValue (1); // Avoid range errors. + + // Set a default level number for the selected collection. + switch (slAction) { + case SL_ANY: + case SL_UPDATE: + case SL_DELETE: + case SL_UPD_GAME: + // If selecting the current collection, use the current level number. + if (n == N) + number->setValue (defaultLevel); + else + number->setValue (1); // Else use level 1. + break; + case SL_CREATE: + case SL_SAVE: + case SL_MOVE: + if ((n == N) && (slAction != SL_CREATE)) { + // Saving/moving level in current collection: use current number. + number->setValue (defaultLevel); + } + else { + // Saving new/edited level or relocating a level: use "nLevels + 1". + number->setMaxValue (collections.at(n)->nLevels + 1); + number->setValue (number->maxValue()); + } + break; + default: + number->setValue (1); // Default is level 1. + break; + } + + slShowLevel (number->value()); + +#ifndef KGR_PORTABLE + int levCnt = collections.at(n)->nLevels; + if (collections.at(n)->settings == 'K') + collnD->setText (i18n("1 level, uses KGoldrunner rules.", + "%n levels, uses KGoldrunner rules.", levCnt)); + else + collnD->setText (i18n("1 level, uses Traditional rules.", + "%n levels, uses Traditional rules.", levCnt)); +#else + QString levCnt; + levCnt = levCnt.setNum (collections.at(n)->nLevels); + if (collections.at(n)->settings == 'K') + collnD->setText (levCnt + i18n(" levels, uses KGoldrunner rules.")); + else + collnD->setText (levCnt + i18n(" levels, uses Traditional rules.")); +#endif + collnN->setText (collections.at(n)->name); +} + +void KGrSLDialog::slAboutColln () +{ + // User clicked the "About" button ... + int n = slCollnIndex; + QString title = i18n("About \"%1\"").arg(collections.at(n)->name); + + if (collections.at(n)->about.length() > 0) { + // Convert game description to ASCII and UTF-8 codes, then translate it. + KGrMessage::wrapped (slParent, title, + i18n((const char *) collections.at(n)->about.utf8())); + } + else { + KGrMessage::wrapped (slParent, title, + i18n("Sorry, there is no further information about this game.")); + } +} + +void KGrSLDialog::slShowLevel (int i) +{ + // Display the level number as the slider is moved. + QString tmp; + tmp.setNum(i); + tmp = tmp.rightJustify(3,'0'); + display->setText(tmp); +} + +void KGrSLDialog::slUpdate (const QString & text) +{ + // Move the slider when a valid level number is entered. + QString s = text; + bool ok = FALSE; + int n = s.toInt (&ok); + if (ok) { + number->setValue (n); + slPaintLevel(); + } + else + KGrMessage::information (this, i18n("Select Level"), + i18n("This level number is not valid. It can not be used.")); +} + +void KGrSLDialog::slPaintLevel () +{ + // Repaint the thumbnail sketch of the level whenever the level changes. + int n = slCollnIndex; + if (n < 0) { + return; // Owner has no collections. + } + QString filePath = game->getFilePath + (collections.at(n)->owner, collections.at(n), number->value()); + thumbNail->setFilePath (filePath, slName); + thumbNail->repaint(); // Will call "drawContents (p)". +} + +void KGrSLDialog::slotHelp () +{ + // Help for "Select Game and Level" dialog box. + QString s = + i18n("The main button at the bottom echoes the " + "menu action you selected. Click it after choosing " + "a game and level - or use \"Cancel\"."); + + if (slAction == SL_START) { + s += i18n("\n\nIf this is your first time in KGoldrunner, select the " + "tutorial game or click \"Cancel\" and click that item in " + "the Game or Help menu. The tutorial game gives you hints " + "as you go.\n\n" + "Otherwise, just click on the name of a game (in the list box), " + "then, to start at level 001, click on the main button at the " + "bottom. Play begins when you move the mouse or press a key."); + } + else { + switch (slAction) { + case SL_UPDATE: + s += i18n("\n\nYou can select System levels for editing (or " + "copying), but you must save the result in a game you have " + "created. Use the mouse as a paintbrush and the editor " + "toolbar buttons as a palette. Use the 'Empty Space' button " + "to erase."); + break; + case SL_CREATE: + s += i18n("\n\nYou can add a name and hint to your new level here, " + "but you must save the level you have created into one of " + "your own games. By default your new level will go at the " + "end of your game, but you can also select a level number and " + "save into the middle of your game."); + break; + case SL_SAVE: + s += i18n("\n\nYou can create or edit a name and hint here, before " + "saving. If you change the game or level, you can do a copy " + "or \"Save As\", but you must always save into one of your " + "own games. If you save a level into the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_DELETE: + s += i18n("\n\nYou can only delete levels from one of your own " + "games. If you delete a level from the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_MOVE: + s += i18n("\n\nTo move (re-number) a level, you must first select " + "it by using \"Edit Any Level...\", then you can use " + "\"Move Level...\" to assign it a new number or even a different " + "game. Other levels are automatically re-numbered as " + "required. You can only move levels within your own games."); + break; + case SL_UPD_GAME: + s += i18n("\n\nWhen editing game info you only need to choose a " + "game, then you can go to a dialog where you edit the " + "details of the game."); + break; + default: + break; + } + s += i18n("\n\nClick on the list box to choose a game. " + "Below the list box you can see \"More Info\" about the " + "selected game, how many levels there are and what " + "rules the enemies follow (see the Settings menu).\n\n" + "You select " + "a level number by typing it or using the scroll bar. As " + "you vary the game or level, the thumbnail area shows a " + "preview of your choice."); + } + + KGrMessage::wrapped (slParent, i18n("Help: Select Game & Level"), s); +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Name & Hint"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * nameL = new QLabel (i18n("Name of level:"), dad); + mainLayout->addWidget (nameL); + nhName = new QLineEdit (dad); + mainLayout->addWidget (nhName); + + QLabel * mleL = new QLabel (i18n("Hint for level:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + dad-> setCaption (i18n("Edit Name & Hint")); +#endif + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + + // Configure the text box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (9); +#endif + + nhName-> setText (levelName); + mle-> setText (levelHint); + +#ifdef KGR_PORTABLE + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + CANCEL-> setAccel (Key_Escape); + + connect (OK, SIGNAL (clicked ()), dad, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), dad, SLOT (reject ())); +#endif +} + +KGrNHDialog::~KGrNHDialog() +{ +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE OR EDIT A GAME (COLLECTION) *************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Game Info"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ + collections = gamesList; + defaultGame = collnIndex; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QHBox * nameBox = new QHBox (dad); + mainLayout->addWidget (nameBox); + nameBox->setSpacing (spacing); + nameL = new QLabel (i18n("Name of game:"), nameBox); + ecName = new QLineEdit (nameBox); + + QHBox * prefixBox = new QHBox (dad); + mainLayout->addWidget (prefixBox); + prefixBox->setSpacing (spacing); + prefixL = new QLabel (i18n("File name prefix:"), prefixBox); + ecPrefix = new QLineEdit (prefixBox); + + ecGrp = new QButtonGroup (1, QButtonGroup::Horizontal, 0, dad); + mainLayout->addWidget (ecGrp); + ecTradB = new QRadioButton (i18n("Traditional rules"), ecGrp); + ecKGrB = new QRadioButton (i18n("KGoldrunner rules"), ecGrp); + + nLevL = new QLabel (i18n( "0 levels" ), dad); + mainLayout->addWidget (nLevL); + + mleL = new QLabel (i18n("About this game:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); +#endif + + if (action == SL_CR_GAME) { + setCaption (i18n("Create Game")); + } + else { + setCaption (i18n("Edit Game Info")); + } + + QString OKText = ""; + if (action == SL_UPD_GAME) { // Edit existing collection. + ecName-> setText (collections.at(defaultGame)->name); + ecPrefix-> setText (collections.at(defaultGame)->prefix); + if (collections.at(defaultGame)->nLevels > 0) { + // Collection already has some levels, so cannot change the prefix. + ecPrefix-> setEnabled (FALSE); + } + QString s; +#ifndef KGR_PORTABLE + nLevL-> setText (i18n("1 level", "%n levels", + collections.at(defaultGame)->nLevels)); +#else + nLevL-> setText (i18n("%1 levels") + .arg(collections.at(defaultGame)->nLevels)); +#endif + OKText = i18n("Save Changes"); + } + else { // Create a collection. + ecName-> setText (""); + ecPrefix-> setText (""); + nLevL-> setText (i18n("0 levels")); + OKText = i18n("Save New"); + } +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + + if ((action == SL_CR_GAME) || + (collections.at(defaultGame)->settings == 'T')) { + ecSetRules ('T'); // Traditional settings. + } + else { + ecSetRules ('K'); // KGoldrunner settings. + } + + // Configure the edit box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (8); +#endif + + if ((action == SL_UPD_GAME) && + (collections.at(defaultGame)->about.length() > 0)) { + // Display and edit the game description in its original language. + mle-> setText (collections.at(defaultGame)->about); + } + else { + mle-> setText (""); + } + + connect (ecKGrB, SIGNAL (clicked ()), this, SLOT (ecSetKGr ())); + connect (ecTradB, SIGNAL (clicked ()), this, SLOT (ecSetTrad ())); + +#ifdef KGR_PORTABLE + OK-> setGeometry (10, 145 + mle->height(), 100, 25); + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + + CANCEL-> setGeometry (190, 145 + mle->height(), 100, 25); + CANCEL-> setAccel (Key_Escape); + + dad-> resize (300, 175 + mle->height()); + + connect (OK, SIGNAL (clicked ()), this, SLOT (accept())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject())); +#endif +} + +KGrECDialog::~KGrECDialog() +{ +} + +void KGrECDialog::ecSetRules (const char settings) +{ + ecKGrB-> setChecked (FALSE); + ecTradB-> setChecked (FALSE); + if (settings == 'K') + ecKGrB-> setChecked (TRUE); + else + ecTradB-> setChecked (TRUE); +} + +void KGrECDialog::ecSetKGr () {ecSetRules ('K');} // Radio button slots. +void KGrECDialog::ecSetTrad () {ecSetRules ('T');} + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList & collections, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList & collections, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Saved Game"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * lgHeader = new QLabel ( + i18n("Game Level/Lives/Score " + "Day Date Time "), dad); + + lgList = new QListBox (dad); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f.setFixedPitch (TRUE); + lgList-> setFont (f); + f.setBold (TRUE); + lgHeader-> setFont (f); + + mainLayout-> addWidget (lgHeader); + mainLayout-> addWidget (lgList); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + buttons-> setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + mainLayout-> addWidget (buttons); + + dad-> setCaption (i18n("Select Saved Game")); + + // Base the geometry of the list box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*c, p.y()+2*c); + lgList-> setMinimumHeight ((FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (4*c); + CANCEL-> setMaximumWidth (4*c); + + OK-> setAccel (Key_Return); + CANCEL-> setAccel (Key_Escape); +#endif + + lgHighlight = -1; + + QTextStream gameText (savedGames); + QString s = ""; + QString pr = ""; + int i; + int imax = collections.count(); + + // Read the saved games into the list box. + while (! gameText.endData()) { + s = gameText.readLine(); // Read in one saved game. + pr = s.left (s.find (" ", 0, FALSE)); // Get the collection prefix. + for (i = 0; i < imax; i++) { // Get the collection name. + if (collections.at(i)->prefix == pr) { + s = s.insert (0, + collections.at(i)->name.leftJustify (20, ' ', TRUE) + " "); + break; + } + } + lgList-> insertItem (s); + } + savedGames->close(); + + // Mark row 0 (the most recently saved game) as the default selection. + lgList-> setCurrentItem (0); + lgList-> setSelected (0, TRUE); + lgHighlight = 0; + + connect (lgList, SIGNAL (highlighted (int)), this, SLOT (lgSelect (int))); +#ifdef KGR_PORTABLE + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); +#endif +} + +void KGrLGDialog::lgSelect (int n) +{ + lgHighlight = n; +} + +/******************************************************************************* +*********************** CENTRALISED MESSAGE FUNCTIONS ************************ +*******************************************************************************/ + +void KGrMessage::information (QWidget * parent, const QString &caption, const QString &text) +{ +#ifdef KGR_PORTABLE + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + QMessageBox::information (parent, caption, + "" + text + ""); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + KMessageBox::information (parent, text, caption); +#endif +} + +int KGrMessage::warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2) +{ + int ans = 0; +#ifdef KGR_PORTABLE + // Display a box with 2 or 3 buttons, depending on if label2 is empty or not. + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + ans = QMessageBox::warning (parent, caption, + "" + text + "", + label0, label1, label2, + 0, (label2.isEmpty()) ? 1 : 2); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + if (label2.isEmpty()) { + // Display a box with 2 buttons. + ans = KMessageBox::questionYesNo (parent, text, caption, + label0, label1); + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } + else { + // Display a box with 3 buttons. + ans = KMessageBox::questionYesNoCancel (parent, text, caption, + label0, label1); + if (ans == KMessageBox::Cancel) + ans = 2; + else + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } +#endif + return (ans); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrMessage::wrapped (QWidget * parent, QString title, QString contents) +{ +#ifndef KGR_PORTABLE + KMessageBox::information (parent, contents, title); +#else + QDialog * mm = new QDialog (parent, "wrappedMessage", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (mm, margin, spacing); + + // Make text background grey not white (i.e. same as widget background). + QPalette pl = mm->palette(); +#ifdef QT3 + pl.setColor (QColorGroup::Base, mm->paletteBackgroundColor()); +#else + pl.setColor (QColorGroup::Base, mm->backgroundColor()); +#endif + mm-> setPalette (pl); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + QTextEdit * mle = new QTextEdit (mm); + mle-> setTextFormat (PlainText); +#else + QMultiLineEdit * mle = new QMultiLineEdit (mm); +#endif + mainLayout->addWidget (mle); + + // Button is for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), mm); + mainLayout->addWidget (OK, Qt::AlignHCenter); + + mm-> setCaption (title); + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + mm-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (3*c); + + mle-> setFrameStyle (QFrame::NoFrame); + mle-> setAlignment (AlignLeft); + mle-> setReadOnly (TRUE); + mle-> setText (contents); + +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (10); + if (mle-> numLines() < 10) { + mle-> setFixedVisibleLines (mle->numLines()); + } +#endif + + OK-> setAccel (Key_Return); + connect (OK, SIGNAL (clicked ()), mm, SLOT (accept ())); + + mm-> exec (); + + delete mm; +#endif // KGR_PORTABLE +} + +#include "kgrdialog.moc" diff --git a/kgoldrunner/src/kgrdialog.h b/kgoldrunner/src/kgrdialog.h new file mode 100644 index 00000000..2e2e847c --- /dev/null +++ b/kgoldrunner/src/kgrdialog.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRDIALOG_QT_H +#define KGRDIALOG_QT_H + +// If portable version, use QDialog and QMessageBox. +// If KDE version, use KDialogBase and KMessageBox. + +#ifdef KGR_PORTABLE +#include +#define KGR_DIALOG QDialog +#include + +#else +#include +#include +#define KGR_DIALOG KDialogBase +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef QT3 +#include +#else +#include +#endif + +#include + +/** +@author Ian Wadham and Marco Krüger +*/ + +class KGrCanvas; +class KGrGame; +class KGrCollection; +class KGrThumbNail; + +/******************************************************************************/ +/******************* DIALOG TO SELECT A GAME AND LEVEL *******************/ +/******************************************************************************/ + +class KGrSLDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList & gamesList, KGrGame * theGame, + QWidget * parent = 0, const char *name = 0); + ~KGrSLDialog(); + + int selectedLevel() {return (number->value());} + int selectedGame() {return (slCollnIndex);} + +private slots: + void slSetCollections (int cIndex); + void slColln (int i); + void slAboutColln (); + void slShowLevel (int i); + void slUpdate (const QString & text); + void slPaintLevel (); + void slotHelp (); // Will replace KDE slotHelp(). + +private: + int slAction; + QPtrList collections; // List of games. + int defaultLevel; + int defaultGame; + int slCollnIndex; + KGrGame * game; + KGrCollection * collection; + QWidget * slParent; + + QLabel * collnL; + QListBox * colln; + QLabel * collnN; + QLabel * collnD; + QPushButton * collnA; + + QLabel * numberL; + QLineEdit * display; + QScrollBar * number; + QPushButton * levelNH; + QLabel * slName; + KGrThumbNail * thumbNail; + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * HELP; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +class KGrNHDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrNHDialog (const QString & levelName, const QString & levelHint, + QWidget * parent = 0, const char * name = 0); + ~KGrNHDialog(); + + QString getName() {return (nhName->text());} + QString getHint() {return (mle->text());} + +private: + QLineEdit * nhName; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif +}; + +/******************************************************************************* +***************** DIALOG TO CREATE OR EDIT A GAME (COLLECTION) ***************** +*******************************************************************************/ + +class KGrECDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrECDialog (int action, int collnIndex, + QPtrList & gamesList, + QWidget *parent = 0, const char *name = 0); + ~KGrECDialog(); + + QString getName() {return (ecName->text());} + QString getPrefix() {return (ecPrefix->text());} + bool isTrad() {return (ecTradB->isChecked());} + QString getAboutText() {return (mle->text());} + +private slots: + void ecSetRules (const char settings); + void ecSetKGr(); // Radio button slots. + void ecSetTrad(); + +private: + QPtrList collections; // List of existing games. + int defaultGame; + + QLabel * nameL; + QLineEdit * ecName; + QLabel * prefixL; + QLineEdit * ecPrefix; + QButtonGroup * ecGrp; + QRadioButton * ecKGrB; + QRadioButton * ecTradB; + QLabel * nLevL; + + QLabel * mleL; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#include +#include + +class KGrLGDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrLGDialog (QFile * savedGames, QPtrList & collections, + QWidget * parent, const char * name); + QString getCurrentText() {return (lgList->currentText());} + +private slots: + void lgSelect (int n); + +private: + QListBox * lgList; + int lgHighlight; +}; + +/******************************************************************************* +****************** PORTABLE MESSAGE FUNCTIONS (Qt Version) ******************* +*******************************************************************************/ + +class KGrMessage : public QDialog +{ +public: + static void information (QWidget * parent, const QString &caption, const QString &text); + static int warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2 = ""); + static void wrapped (QWidget * parent, QString caption, QString text); +}; + +#endif diff --git a/kgoldrunner/src/kgrfigure.cpp b/kgoldrunner/src/kgrfigure.cpp new file mode 100644 index 00000000..08fc91d4 --- /dev/null +++ b/kgoldrunner/src/kgrfigure.cpp @@ -0,0 +1,1803 @@ +/*************************************************************************** + * kgrfigure.cpp - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrcanvas.h" + +#include "kgrfigure.h" + +#include + +KGrFigure :: KGrFigure (int px, int py) +{ + x = mem_x = px; + y = mem_y = py; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = px*16; + absy = py*16; + + nuggets = 0; + status = STANDING; + + walkTimer = new QTimer (this); + fallTimer = new QTimer (this); +} + +// Initialise the global settings flags. +bool KGrFigure::variableTiming = TRUE; +bool KGrFigure::alwaysCollectNugget = TRUE; +bool KGrFigure::runThruHole = TRUE; +bool KGrFigure::reappearAtTop = TRUE; +SearchStrategy KGrFigure::searchStrategy = LOW; + +int KGrFigure::herox = 0; +int KGrFigure::heroy = 0; + +// Initialise the global game-speed factors. +int KGrFigure::speed = NSPEED; +int KGrBrick::speed = NSPEED; + +// Initialise constants for fixed (KGoldrunner) and variable (Traditional) +// timing. Each row contains timings for hero walk and fall, enemy walk and +// fall, enemy captured in hole and dug brick. + +Timing KGrFigure::fixedTiming = {45, 50, 55, 100, 500, 40}; // KGr original. + +Timing KGrFigure::varTiming[6] = { // Traditional. + {40, 58, 78, 88, 170, 23}, // No enemies. + {50, 68, 78, 88, 170, 32}, // 1 enemy. + {57, 67, 114, 128, 270, 37}, // 2 enemies. + {60, 70, 134, 136, 330, 40}, // 3 enemies. + {63, 76, 165, 150, 400, 46}, // 4 enemies. + {70, 80, 189, 165, 460, 51} // >4 enemies. +}; + +int KGrBrick::HOLETIME = 0; + +int KGrFigure::getx() +{ + return absx; +} + +int KGrFigure::gety() +{ + return absy; +} + +Status KGrFigure::getStatus() +{ + return status; +} + +void KGrFigure::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + nuggets = 0; + status = STANDING; +} + +void KGrFigure:: setNuggets(int n) +{ + nuggets = n; +} + + +bool KGrFigure::canWalkRight() +{ + return (((*playfield)[x+1][y]->whatIam() != BRICK) && + ((*playfield)[x+1][y]->whatIam() != BETON) && + ((*playfield)[x+1][y]->whatIam() != FBRICK)); +} + +bool KGrFigure::canWalkLeft() +{ + return (((*playfield)[x-1][y]->whatIam() != BRICK) && + ((*playfield)[x-1][y]->whatIam() != BETON) && + ((*playfield)[x-1][y]->whatIam() != FBRICK)); + } + +bool KGrFigure::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + ((*playfield)[x][y]->whatIam() == LADDER)); +} + +bool KGrFigure::canWalkDown() +{ + return (((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON) && + // v0.3 FIX - Let figure step down into FBRICK from a ladder. + // ((*playfield)[x][y+1]->whatIam() != FBRICK)&& + (((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +bool KGrFigure::canStand() +{ + return (((*playfield)[x][y+1]->whatIam() == BRICK) || + ((*playfield)[x][y+1]->whatIam() == BETON) || + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)|| + ((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER)|| + standOnEnemy()); + } + +bool KGrFigure::hangAtPole() +{ + return ((*playfield)[x][y]->whatIam() == POLE); +} + +void KGrFigure::walkUp(int WALKDELAY) +{ + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step up, if possible. + if (canWalkUp()) { + rely -= STEP; + absy -= STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move up to next cell, if possible. + if (canWalkUp()) { + y--; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Wait for caller to set next direction. + status = STANDING; + } +} + +void KGrFigure::walkDown(int WALKDELAY, int FALLDELAY) +{ + if (hangAtPole() || (! canStand())) { + // On bar or no firm ground underneath: so must fall. + initFall (FALL2, FALLDELAY); + } + else { + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step down, if possible. + if (canWalkDown()) { + rely += STEP; + absy += STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move down to next cell, if possible. + if (canWalkDown()) { + y++; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Must be able to halt at a pole when going down. + if (! (canStand() || hangAtPole())) + initFall(FALL2, FALLDELAY); // kein Halt....urgs + else + // Wait for caller to set next direction. + status = STANDING; + } + } +} + +void KGrFigure::walkLeft (int WALKDELAY, int FALLDELAY) +{ + // If counter != 0, the figure is walking, otherwise he is turning around. + if (walkCounter++ != 0) { + // Change to the next pixmap in the animation. + if ((++actualPixmap%4) != 0) { + // Not end of 4-pixmap cycle: move one step left, if possible. + if (canWalkLeft()) { + relx -= STEP; + absx -=STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-pixmap cycle: start again, in next cell if possible. + actualPixmap -= 4; + if (canWalkLeft()) { + x--; + } + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + // If cannot stand or hang, start fall, else await next assignment. + if (! (canStand() || hangAtPole())) + initFall (FALL1, FALLDELAY); + else + status = STANDING; // Caller should set next direction. + } + } + else { + status = STANDING; // The figure is turning around. + } +} + +void KGrFigure::walkRight(int WALKDELAY, int FALLDELAY) +{ + if (walkCounter++) { // wenn 0, soll sich Figur nur umdrehen + if (++actualPixmap % 4) { // wenn true, dann ist kein vollständiger Schritt gemacht + if (canWalkRight()) { + relx += STEP; + absx += STEP; // nur vorwärts gehen, wenn es auch möglich ist + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + actualPixmap -= 4; // Schritt war vollendet + if (canWalkRight()) { + x++; + } //gehe entgültig nach rechts + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + if (!(canStand()||hangAtPole())) // kein Halt mehr...arrrgghhh + initFall (FALL2, FALLDELAY); + else + status = STANDING; // Figur hat Halt + } + } + else { + status = STANDING; // Figur sollte sich nur Umdrehen. + } +} + +void KGrFigure::initFall(int apm, int FALLDELAY) +{ + status = FALLING; + actualPixmap = apm; + walkCounter=1; + walkTimer->stop(); + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); +} + +void KGrFigure::showFigure () +{ +} + +void KGrFigure::setPlayfield (KGrObject * (*p)[30][22]) +{ + playfield = p; +} + +KGrFigure :: ~KGrFigure () +{ +} + +KGrHero :: KGrHero (KGrCanvas * view, int x, int y) + :KGrFigure (x, y) +{ + heroView = view; + status = STANDING; + actualPixmap = FALL1; + + herox = x; + heroy = y; + + started = FALSE; + mouseMode = TRUE; + walkCounter = 1; + + walkFrozen = FALSE; + fallFrozen = FALSE; + + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrHero::WALKDELAY = 0; +int KGrHero::FALLDELAY = 0; + +/* Es ist Notwendig der eigentlichen Timerfunktion diese + Startwalk vorzuschalten, um bei einem evtl. notwendigem + Richtungswechsel der Figur diese Bewegung mit einzufügen */ +void KGrHero::startWalk () +{ + switch (nextDir) { + case UP: + if ((*playfield)[x][y]->whatIam () == LADDER) + {walkCounter = 1; + direction = UP;} + break; + case RIGHT: + if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + if (direction != RIGHT) + walkCounter = 0; + else + walkCounter = 1; + direction = RIGHT; + break; + case DOWN: + if (((*playfield)[x][y]->whatIam () == LADDER)|| + ((*playfield)[x][y+1]->whatIam () == LADDER)) + {walkCounter = 1; + direction = DOWN;} + else // wenn figur an Stange haengt und nichts unter ihr, + if (hangAtPole() && (!canStand())) // dann soll sie fallen + { status = STANDING; + actualPixmap = (direction==RIGHT)?19:18; + walkCounter=1; + direction=STAND; + walkTimer->stop(); + } + break; + case LEFT: + if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + if (direction != LEFT) + walkCounter = 0; + else + walkCounter = 1; + direction = LEFT; + break; + default : + direction = STAND; + status = FALLING; + break; + } + nextDir = STAND; + if (status != FALLING)//immer ausführen, ausser beim fallen + { status = WALKING; // da sonst der FALLINGstatus wieder + showFigure (); // geaendert wird und der falsche Timer anspringt. + } +} // END KGrHero::startWalk + +void KGrHero::setKey(Direction key) +{ + // Keyboard control of hero: direction is fixed until next key is pressed. + // Sets a simulated mouse-pointer above, below, left, right or on the hero. + mouseMode = FALSE; + stopped = FALSE; + switch (key) { + case UP: mousex = x; mousey = 0; break; + case DOWN: mousex = x; mousey = FIELDHEIGHT + 1; break; + case LEFT: mousex = 0; mousey = y; break; + case RIGHT: mousex = FIELDWIDTH + 1; mousey = y; break; + case STAND: stopped = TRUE; mousex = x; mousey = y; break; + } +} + +void KGrHero::setDirection(int i, int j) +{ + // Mouse control of hero: direction is updated continually on a timer. + mouseMode = TRUE; + stopped = FALSE; + mousex = i; + mousey = j; +} + +void KGrHero::setNextDir() +{ + int dx, dy; + + if (! mouseMode) { + // Keyboard control of hero: adjust simulated mouse-pointer. + if (stopped) { + mousex = x; + mousey = y; + } + if ((mousey < 1) || (mousey > FIELDHEIGHT)) { + mousex = x; // Stay directly above/below the hero. + } + else if ((mousex < 1) || (mousex > FIELDWIDTH)) { + mousey = y; // Stay directly left/right of the hero. + } + } + + dx = mousex - x; dy = mousey - y; + + if ((dy == 0) && (y == 1) && (nuggets <= 0)) { + nextDir = UP; + } + else if ((dy > 0) && + (canWalkDown() || + standOnEnemy() || + (hangAtPole() && ((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON)))) { + nextDir = DOWN; + } + else if ((dy < 0) && canWalkUp ()) { + nextDir = UP; + } + else if (dx > 0) { + nextDir = RIGHT; + } + else if (dx < 0) { + nextDir = LEFT; + } + else if (dx == 0) { + nextDir = STAND; + } +} + +void KGrHero::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } +} + +void KGrHero::showState(char option) +{ + printf("(%02d,%02d) - Hero ", x, y); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d next dirn %d\n", direction, nextDir); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + printf ("\n"); + break; + } +} + +void KGrHero::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + walkCounter = 1; + started = FALSE; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = 16*x; + absy = 16*y; + + nuggets = 0; + + if (herox < 1) { // If first call to init, ... + heroView->makeHeroSprite (x, y, actualPixmap); + } + herox = x; + heroy = y; + + actualPixmap = FALL2; + heroView->moveHero (absx, absy, actualPixmap); +} + +void KGrHero::start() +{ + started = TRUE; + walkFrozen = FALSE; + fallFrozen = FALSE; + + if (!(canStand()||hangAtPole())) { // Held muss wohl fallen... + status = FALLING; + fallTimeDone(); + } + else { + status = STANDING; + walkTimeDone(); + } +} + +void KGrHero::setSpeed (int gamespeed) +{ + if (gamespeed >= 0) { + if (gamespeed < MINSPEED) + speed++; // Increase speed. + else + speed = gamespeed; // Set selected speed. + if (speed > MAXSPEED) + speed = MAXSPEED; // Set upper limit. + } + else { + speed--; // Reduce speed. + if (speed < MINSPEED) + speed = MINSPEED; // Set lower limit. + } + + KGrBrick::speed = speed; // Make a copy for bricks. +} + +void KGrHero::walkTimeDone () +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + if ((*playfield)[x][y]->whatIam() == BRICK) { + emit caughtHero(); // Brick closed over hero. + return; + } + + if ((y==1)&&(nuggets<=0)) { // If on top row and all nuggets collected, + emit leaveLevel(); // the hero has won and can go to next level. + return; + } + + if (status == STANDING) + setNextDir(); + if ((status == STANDING) && (nextDir != STAND)) { + if ((standOnEnemy()) && (nextDir == DOWN)) { + emit caughtHero(); // Hero is going to step down into an enemy. + return; + } + startWalk(); + } + if (status != STANDING) { + switch (direction) { + case UP: walkUp (WALKDELAY); break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default : + // The following code is strange. It makes the hero fall off a pole. + // It works because of other strange code in "startWalk(), case DOWN:". + if (!canStand()||hangAtPole()) // falling + initFall(FALL1, FALLDELAY); + else status = STANDING; + break; + } + herox=x;heroy=y; // Koordinatenvariablen neu + // wenn Held genau ein Feld weitergelaufen ist, + if ((relx==0)&&(rely==0)) // dann setzte statische + { + collectNugget(); // und nehme evtl. Nugget + } + showFigure(); // Is this REDUNDANT now? See showFigure() below. + ////////////////////////////////////////////////// + } + if (status == STANDING) + if (!canStand()&&!hangAtPole()) + initFall(FALL1, FALLDELAY); + else + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + + // This additional showFigure() is to update the hero position after it is + // altered by the hero-enemy deadlock fix in standOnEnemy(). Messy, but ... + //////////////////////////////////////////////////////////////////////////// + showFigure(); + if(isInEnemy()) { + walkTimer->stop(); + emit caughtHero(); + } +} + +void KGrHero::fallTimeDone() +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if (!standOnEnemy()) { + if (walkCounter++ < 4) { // Held fällt vier Positionen + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + rely+=STEP; + absy+=STEP; + } + else { //Held ist ein Feld weitergefallen + // Verschiebung der Figur zum 0-Punkt des Objekts (Brick etc...) + heroy = ++y; + rely = 0; + absy = y*16; // wird Null und Figur eins runter + collectNugget(); // gesetzt. Zeit evtl. Nugget zu nehmen + if (! (canStand()||hangAtPole())) { // Held muss wohl weiterfallen. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter = 1; + } + else { // Held hat Boden (oder Feind) unter den + status = STANDING; // Füssen oder hängt an Stange -> steh! + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + } + } + showFigure(); + } + else { + if (rely == 0) { + // If at the bottom of a cell, try to walk or just stand still. + status = STANDING; + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // Else, freeze hero until enemy moves out of the way. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + } + } + if (isInEnemy() && (! standOnEnemy())) + emit caughtHero(); +} + + +void KGrHero::showFigure () { + + heroView->moveHero (absx, absy, actualPixmap); + + // Merke alte Werte zum löschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +void KGrHero::dig(){ + if (direction == LEFT) + digLeft(); + else + if (direction == RIGHT) + digRight(); +} + +void KGrHero::digLeft(){ + int i = 1; // If stationary or moving up/down, dig at x-1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 2; // If walking left, dig at x-2 and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 0; // If walking right, dig at x and stop at x+1. + } + if (((*playfield)[x-i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x-i][y]->whatIam() == HLADDER)|| + ((*playfield)[x-i][y]->whatIam() == FREE)|| + ((*playfield)[x-i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x-i][y+1])->dig(); +} + +void KGrHero::digRight(){ + int i = 1; // If stationary or moving up/down, dig at x+1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 0; // If walking left, dig at x and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 2; // If walking right, dig at x+2 and stop at x+1. + } + if (((*playfield)[x+i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x+i][y]->whatIam() == HLADDER)|| + ((*playfield)[x+i][y]->whatIam() == FREE)|| + ((*playfield)[x+i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x+i][y+1])->dig(); +} + +#ifdef QT3 +void KGrHero::setEnemyList(QPtrList *e) +#else +void KGrHero::setEnemyList(QList *e) +#endif +{ + enemies = e; +} + +bool KGrHero::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 0) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if hero's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + if (((absy + 12) == enemy->gety()) && + (enemy->getStatus() != FALLING)) { + absy = absy - rely; // Bounce back from overlap, to avoid + rely = 0; // hero-enemy mid-cycle deadlock. + walkCounter = 1; + } + return true; + } + } + } + return false; +} + +void KGrHero::collectNugget(){ + + if ((*playfield)[x][y]->whatIam() == NUGGET) + { + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + if (!(--nuggets)) + emit haveAllNuggets(); // sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen + emit gotNugget(250); // sendet der Application ein Nugget um Score zu erhöhen + + } +} + +void KGrHero::loseNugget() { + + // Enemy trapped or dead and could not drop nugget (NO SCORE for this). + if (! (--nuggets)) + emit haveAllNuggets(); // Sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen. +} + +bool KGrHero::isInEnemy(){ + + int c=0; + int range=enemies->count(); + if (range) + for (KGrEnemy *enemy=enemies->at(c);cat(c++); + if (isInside(enemy->getx(),enemy->gety())|| + isInside(enemy->getx()-15,enemy->gety())|| + isInside(enemy->getx(),enemy->gety()-15)) + return true;} + return false; +} + +bool KGrHero::isInside(int enemyx, int enemyy){ + + return ((absx >= enemyx)&& + (absx <= enemyx+15)&& + (absy >= enemyy)&& + (absy <= enemyy+15)); +} + + +KGrHero :: ~KGrHero (){ + + delete walkTimer; + delete fallTimer; +} + + +KGrEnemy :: KGrEnemy (KGrCanvas * view, int x, int y) + : KGrFigure (x, y) +{ + enemyView = view; + actualPixmap = FALL1; + nuggets = 0; + enemyView->makeEnemySprite (x, y, actualPixmap); + + walkCounter = 1; + captiveCounter = 0; + + searchStatus = HORIZONTAL; + + birthX=x; + birthY=y; + + walkFrozen = FALSE; + fallFrozen = FALSE; + captiveFrozen = FALSE; + + captiveTimer = new QTimer (this); + connect (captiveTimer,SIGNAL(timeout()),SLOT(captiveTimeDone())); + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrEnemy::WALKDELAY = 0; +int KGrEnemy::FALLDELAY = 0; +int KGrEnemy::CAPTIVEDELAY = 0; + +void KGrEnemy::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } + if (captiveFrozen) { + captiveFrozen = FALSE; + captiveTimeDone(); + } +} + +void KGrEnemy::showState(char option) +{ + printf("(%02d,%02d) - Enemy [%d]", x, y, enemyId); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d search %d capt-ctr %d\n", + direction, searchStatus, captiveCounter); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + if (captiveFrozen) printf (" cBlock"); + printf ("\n"); + break; + } +} + +void KGrEnemy::init(int a,int b) +{ + walkTimer->stop(); // alles stoppen bevor die Werte neu gesetzt + fallTimer->stop(); // werden, da es sonst zu ungewollten Effekten + captiveTimer->stop(); // kommen kann + walkCounter = 1; + captiveCounter = 0; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx=16*x; + absy=16*y; + + actualPixmap = 19; + + status = STANDING; +} + +void KGrEnemy::walkTimeDone () +{ + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + // Check we are alive BEFORE checking for friends being in the way. + // Maybe a friend overhead is blocking our escape from a brick. + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "walkTimeDone" when an enemy dies. + } + + if (! bumpingFriend()) { + switch (direction) { + case UP: walkUp (WALKDELAY); + if ((rely == 0) && + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)) + // Enemy kletterte grad aus einem Loch hinaus + // -> gib es frei! + ((KGrBrick *)(*playfield)[x][y+1])->unUseHole(); + break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default: // Switch search direction in KGoldrunner search (only). + searchStatus = (searchStatus==VERTIKAL) ? + HORIZONTAL : VERTIKAL; + + // In KGoldrunner rules, if a hole opens under an enemy + // who is standing and waiting to move, he should fall. + if (!(canStand()||hangAtPole())) { + initFall (actualPixmap, FALLDELAY); + } + else { + status = STANDING; + } + + break; + } + // wenn die Figur genau ein Feld gelaufen ist + if (status == STANDING) { // dann suche den Helden + direction = searchbestway(x,y,herox,heroy); // und + if (walkCounter >= 4) { + if (! nuggets) + collectNugget(); + else + dropNugget(); + } + status = WALKING; // initialisiere die Zählervariablen und + walkCounter = 1; // den Timer um den Held weiter + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); // zu jagen + startWalk (); + } + } + else { + // A friend is in the way. Try a new direction, but not if leaving a hole. + Direction dirn; + + // In KGoldrunner rules, change the search strategy, + // to avoid enemy-enemy deadlock. + searchStatus = (searchStatus==VERTIKAL) ? HORIZONTAL : VERTIKAL; + + dirn = searchbestway (x, y, herox, heroy); + if ((dirn != direction) && ((*playfield)[x][y]->whatIam() != USEDHOLE)) { + direction = dirn; + status = WALKING; + walkCounter = 1; + relx = 0; absx = 16 * x; + rely = 0; absy = 16 * y; + startWalk (); + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + showFigure(); +} + +void KGrEnemy::fallTimeDone () +{ + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if ((*playfield)[x][y+1]->whatIam() == HOLE) { // wenn Enemy ins Loch fällt + ((KGrBrick*)(*playfield)[x][y+1])->useHole(); // reserviere das Loch, damit + // kein anderer es benutzt und + if (nuggets) { // er muss Gold vorher fallen lassen + nuggets=0; + switch ((*playfield)[x][y]->whatIam()) { + case FREE: + case HLADDER: + ((KGrFree *)(*playfield)[x][y])->setNugget(true); break; + default: + emit lostNugget(); break; // Cannot drop the nugget here. + } + } + emit trapped (75); // Enemy trapped: score 75. + } + else if (walkCounter <= 1) { + // Enemies collect nuggets when falling. + if (!nuggets) { + collectNugget(); + } + } + + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "fallTimeDone" when an enemy dies. + } + + if (standOnEnemy()) { // Don't fall into a friend. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + return; + } + + if (walkCounter++ < 4){ + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + { rely+=STEP; absy+=STEP;} + } + else { + rely = 0; y ++; absy=16*y; + if ((*playfield)[x][y]->whatIam() == USEDHOLE) { + captiveCounter = 0; + status = CAPTIVE; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + } + else if (!(canStand()||hangAtPole())) { + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter=1; + } + else { + status = STANDING; + if (hangAtPole()) + actualPixmap=(direction ==RIGHT)?8:12; + } + } + if (status == STANDING) { + status = WALKING; + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + startWalk (); + if (!nuggets) + collectNugget(); + else + dropNugget(); + } + showFigure(); +} + +void KGrEnemy::captiveTimeDone() +{ + if (KGrObject::frozen) {captiveFrozen = TRUE; return; } + if ((*playfield)[x][y]->whatIam()==BRICK) + dieAndReappear(); + else + if (captiveCounter > 6){ + status = WALKING; + walkCounter = 1; + direction = UP; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + captiveCounter = 0; + } else { + captiveCounter ++; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + showFigure(); + } +} + +void KGrEnemy::startSearching() +{ + // Called from "KGoldrunner::startPlaying" and "KGrEnemy::dieAndReappear". + init(x,y); + + if (canStand()||((*playfield)[x][y+1]->whatIam()==USEDHOLE)) { + status = WALKING; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + status = FALLING; + fallTimer->start ((FALLDELAY * NSPEED) / speed, TRUE); + } + + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + startWalk(); +} + +void KGrEnemy::collectNugget() +{ + if (((*playfield)[x][y]->whatIam() == NUGGET) && (nuggets == 0) && + (alwaysCollectNugget || ((int)(5.0*rand()/RAND_MAX+1.0) > 4))){ + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + nuggets=1; + } +} + +void KGrEnemy::dropNugget() +{ + if (((int)(DROPNUGGETDELAY*rand()/RAND_MAX+1.0) > DROPNUGGETDELAY-5) && + ((*playfield)[x][y]->whatIam() == FREE)) { + ((KGrFree *)(*playfield)[x][y])->setNugget(true); + nuggets=0; + } +} + +void KGrEnemy::showFigure () +{ + enemyView->moveEnemy (enemyId, absx, absy, actualPixmap, nuggets); + + // Merke alte Werte zum löschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +bool KGrEnemy::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + (((*playfield)[x][y]->whatIam() == USEDHOLE) || + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +void KGrEnemy::startWalk () +{ + switch (direction) { + case UP: break; + case RIGHT: if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + break; + case DOWN: break; + case LEFT: if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + break; + default: break; + } +} + +void KGrEnemy::dieAndReappear() +{ + bool looking; + int i; + + if (nuggets > 0) { + nuggets = 0; // Enemy died and could not drop nugget. + emit lostNugget(); // NO SCORE for lost nugget. + } + emit killed (75); // Killed an enemy: score 75. + + if (reappearAtTop) { + // Follow Traditional rules: enemies reappear at top. + looking = TRUE; + y = 2; + // Randomly look for a free spot in row 2. Limit the number of tries. + for (i = 1; ((i <= 3) && looking); i++) { + x = (int)((FIELDWIDTH * (float) rand()) / RAND_MAX) + 1; + switch ((*playfield)[x][2]->whatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + // If unsuccessful, choose the first free spot in row 3 or below. + while ((ywhatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + } + } + else { + // Follow KGoldrunner rules: enemies reappear where they started. + x = birthX; + y = birthY; + } + + // Enemy reappears and starts searching for the hero. + startSearching(); +} + +Direction KGrEnemy::searchbestway(int ew,int eh,int hw,int hh) +{ + Direction dirn; + + if ((*playfield)[x][y]->whatIam() == USEDHOLE) // Could not get out of + return UP; // hole (eg. brick above + // closed): keep trying. + + if (!canStand() && // Cannot stand, + !hangAtPole() && // not on pole, not + !standOnEnemy() && // walking on friend and + !((*playfield)[x][y+1]->whatIam() == HOLE)) // not just out of hole, + return DOWN; // so must fall. + + switch (searchStrategy) { + + // Traditional search strategy. + case LOW: + dirn = STAND; + if (eh == hh) { // Hero on same row. + dirn = lowGetHero (ew, eh, hw); + } + if (dirn != STAND) return (dirn); // Can go towards him. + + if (eh >= hh) { // Hero above enemy. + dirn = lowSearchUp (ew, eh, hh); // Find a way up. + } + else { // Hero below enemy. + dirn = lowSearchDown (ew, eh, hh); // Find way down to him. + if (dirn == STAND) { + dirn = lowSearchUp (ew, eh, hh); // No go: try up. + } + } + + if (dirn == STAND) { // When all else fails, + dirn = lowSearchDown (ew, eh, eh - 1); // find way below hero. + } + return dirn; + break; + + // KGoldrunner search strategy. + case MEDIUM: + case HIGH: + if(searchStatus==VERTIKAL){ + if (eh > hh) + return searchupway(ew,eh); + if (eh < hh) + return searchdownway(ew,eh); + return STAND; + } else { + if (ew > hw) + return searchleftway(ew,eh); + if (ew < hw) + return searchrightway(ew,eh); + return STAND; + } + break; + } + return STAND; +} + +/////////////////////////////////////////////// +// Methods for medium-level search strategy. // +/////////////////////////////////////////////// + +Direction KGrEnemy :: searchdownway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKDOWN) + return DOWN; + else while((i>=0)||(j<=FIELDWIDTH)){ + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKDOWN) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKDOWN) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + return STAND; +} + +Direction KGrEnemy :: searchupway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKUP) + return UP; + else while((i>=0)||(j<=FIELDWIDTH)){// search for the first way up + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKUP) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKUP) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + // BUG FIX - Ian W., 30/4/01 - Don't leave an enemy standing in mid air. + if (!canStand()) return DOWN; else return STAND; +} + +Direction KGrEnemy :: searchleftway(int ew,int eh){ +int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKLEFT) || /* kann figur nach links laufen ?*/ + (runThruHole && ( (*playfield)[ew-1][eh]->whatIam() == HOLE))) + return LEFT; + else while((i>=0)||(j<=FIELDHEIGHT)){ /* in den grenzen ?*/ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKLEFT) || /* ein weg nach links- oben gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][i]->whatIam() == HOLE))) + return UP; /* geh nach oben */ + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) /* sonst ...*/ + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKLEFT) || /* ein weg nach links- unten gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][j]->whatIam() == HOLE))) + return DOWN; /* geh nach unten */ + else + if (!( (*playfield)[ew][j++]->searchValue&CANWALKDOWN)) /* sonst ... */ + j=FIELDHEIGHT+1; + } + return STAND; /* default */ +} + +Direction KGrEnemy :: searchrightway(int ew,int eh) +{ + int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][eh]->whatIam() == HOLE))) + return RIGHT; + else while((i>=0)||(j<=FIELDHEIGHT)){ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][i]->whatIam() == HOLE))) + return UP; + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][j]->whatIam() == HOLE))) + return DOWN; + else + if (!( (*playfield)[ew][j++]->searchValue & CANWALKDOWN)) + j=FIELDHEIGHT+1; + } + return STAND; +} + +//////////////////////////////////////////// +// Methods for low-level search strategy. // +//////////////////////////////////////////// + +Direction KGrEnemy::lowSearchUp (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs; + + deltah = eh - hh; // Get distance up to hero's level. + + // Search for the best ladder right here or on the left. + i = ew; ilen = 0; ipos = -1; + while (i >= 1) { + rungs = distanceUp (i, eh, deltah); + if (rungs > ilen) { + ilen = rungs; // This the best yet. + ipos = i; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best ladder on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + rungs = distanceUp (j, eh, deltah); + if (rungs > jlen) { + jlen = rungs; // This the best yet. + jpos = j; + } + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // No ladder found. + return STAND; + + // Choose a ladder to go to. + if (ilen != jlen) { // If the ladders are not the same + // length, choose the longer one. + if (ilen > jlen) { + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else + return LEFT; + } + else + return RIGHT; + } + else { // Both ladders are the same length. + + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else if (ilen == deltah) { // If both reach the hero's level, + if ((ew - ipos) <= (jpos - ew)) // choose the closest. + return LEFT; + else + return RIGHT; + } + else return LEFT; // Else choose the left ladder. + } +} + +Direction KGrEnemy::lowSearchDown (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs, path; + + deltah = hh - eh; // Get distance down to hero's level. + + // Search for the best way down, right here or on the left. + ilen = 0; ipos = -1; + i = (willNotFall (ew, eh)) ? ew : -1; + rungs = distanceDown (ew, eh, deltah); + if (rungs > 0) { + ilen = rungs; ipos = ew; + } + + while (i >= 1) { + rungs = distanceDown (i - 1, eh, deltah); + if (((rungs > 0) && (ilen == 0)) || + ((deltah > 0) && (rungs > ilen)) || + ((deltah <= 0) && (rungs < ilen) && (rungs != 0))) { + ilen = rungs; // This the best way yet. + ipos = i - 1; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best way down, on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + rungs = distanceDown (j + 1, eh, deltah); + if (((rungs > 0) && (jlen == 0)) || + ((deltah > 0) && (rungs > jlen)) || + ((deltah <= 0) && (rungs < jlen) && (rungs != 0))) { + jlen = rungs; // This the best way yet. + jpos = j + 1; + } + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // Found no way down. + return STAND; + + // Choose a way down to follow. + if (ilen == 0) + path = jpos; + else if (jlen == 0) + path = ipos; + else if (ilen != jlen) { // If the ways down are not same length, + // choose closest to hero's level. + if (deltah > 0) { + if (jlen > ilen) + path = jpos; + else + path = ipos; + } + else { + if (jlen > ilen) + path = ipos; + else + path = jpos; + } + } + else { // Both ways down are the same length. + if ((deltah > 0) && // If both reach the hero's level, + (ilen == deltah)) { // choose the closest. + if ((ew - ipos) <= (jpos - ew)) + path = ipos; + else + path = jpos; + } + else + path = ipos; // Else, go left or down. + } + + if (path == ew) + return DOWN; + else if (path < ew) + return LEFT; + else + return RIGHT; +} + +Direction KGrEnemy::lowGetHero (int ew, int eh, int hw) +{ + int i, inc, returnValue; + + inc = (ew > hw) ? -1 : +1; + i = ew; + while (i != hw) { + returnValue = canWalkLR (inc, i, eh); + if (returnValue > 0) + i = i + inc; // Can run further towards the hero. + else if (returnValue < 0) + break; // Will run into a wall regardless. + else + return STAND; // Won't run over a hole. + } + + if (i < ew) return LEFT; + else if (i > ew) return RIGHT; + else return STAND; +} + +int KGrEnemy::distanceUp (int x, int y, int deltah) +{ + int rungs = 0; + + // If there is a ladder at (x.y), return its length, else return zero. + while ( (*playfield)[x][y - rungs]->whatIam() == LADDER) { + rungs++; + if (rungs >= deltah) // To hero's level is enough. + break; + } + return rungs; +} + +int KGrEnemy::distanceDown (int x, int y, int deltah) +{ + // When deltah > 0, we want an exit sideways at the hero's level or above. + // When deltah <= 0, we can go down any distance (as a last resort). + + int rungs = -1; + int exitRung = 0; + bool canGoThru = TRUE; + char objType; + + // If there is a way down at (x,y), return its length, else return zero. + // Because rungs == -1, we first check that level y is not blocked here. + while (canGoThru) { + objType = (*playfield)[x][y + rungs + 1]->whatIam(); + switch (objType) { + case BRICK: + case BETON: + case HOLE: // Enemy cannot go DOWN through a hole. + case USEDHOLE: + if ((deltah > 0) && (rungs <= deltah)) + exitRung = rungs; + if ((objType == HOLE) && (rungs < 0)) + rungs = 0; // Enemy can go SIDEWAYS through a hole. + else + canGoThru = FALSE; // Cannot go through here. + break; + case LADDER: + case POLE: // Can go through or stop. + rungs++; // Add to path length. + if ((deltah > 0) && (rungs >= 0)) { + // If at or above hero's level, check for an exit from ladder. + if ((rungs - 1) <= deltah) { + // Maybe can stand on top of ladder and exit L or R. + if ((objType == LADDER) && (searchOK (-1, x, y+rungs-1) || + searchOK (+1, x, y+rungs-1))) + exitRung = rungs - 1; + // Maybe can exit L or R from ladder body or pole. + if ((rungs <= deltah) && (searchOK (-1, x, y+rungs) || + searchOK (+1, x, y+rungs))) + exitRung = rungs; + } + else + canGoThru = FALSE; // Should stop at hero's level. + } + break; + default: + rungs++; // Can go through. Add to path length. + break; + } + } + if (rungs == 1) { + for (KGrEnemy *enemy=enemies->first();enemy!=0;enemy=enemies->next()) { + if((x*16==enemy->getx()) && (y*16+16==enemy->gety())) + rungs = 0; // Pit is blocked. Find another way. + } + } + if (rungs <= 0) + return 0; // There is no way down. + else if (deltah > 0) + return exitRung; // We want to take an exit, if any. + else + return rungs; // We can go down all the way. +} + +bool KGrEnemy::searchOK (int direction, int x, int y) +{ + // Check whether it is OK to search left or right. + if (canWalkLR (direction, x, y) > 0) { + // Can go into that cell, but check for a fall. + if (willNotFall (x+direction, y)) + return TRUE; + } + return FALSE; // Cannot go into and through that cell. +} + +int KGrEnemy::canWalkLR (int direction, int x, int y) +{ + if (willNotFall (x, y)) { + switch ((*playfield)[x+direction][y]->whatIam()) { + case BETON: + case BRICK: + case USEDHOLE: + return -1; break; // Will be halted in current cell. + default: + // NB. FREE, LADDER, HLADDER, NUGGET and POLE are OK of course, + // but enemies can also walk left/right through a HOLE and + // THINK they can walk left/right through a FBRICK. + + return +1; break; // Can walk into next cell. + } + } + else + return 0; // Will fall before getting there. +} + +bool KGrEnemy::willNotFall (int x, int y) +{ + int c, cmax; + KGrEnemy *enemy; + + // Check the ceiling. + switch ( (*playfield)[x][y]->whatIam()) { + case LADDER: + case POLE: + return TRUE; break; // OK, can hang on ladder or pole. + default: + break; + } + + // Check the floor. + switch ( (*playfield)[x][y+1]->whatIam()) { + + // Cases where the enemy knows he will fall. + case FREE: + case HLADDER: + case FBRICK: + + // N.B. The enemy THINKS he can run over a NUGGET, a buried POLE or a HOLE. + // The last of these cases allows the hero to trap the enemy, of course. + + // Note that there are several Traditional levels that require an enemy to + // be trapped permanently in a pit containing a nugget, as he runs towards + // you. It is also possible to use a buried POLE in the same way. + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->getx()==16*x) && (enemy->gety()==16*(y+1))) + return TRUE; // Standing on a friend. + } + return FALSE; break; // Will fall: there is no floor. + + default: + return TRUE; break; // OK, will not fall. + } +} + +#ifdef QT3 +void KGrEnemy::setEnemyList(QPtrList *e) +#else +void KGrEnemy::setEnemyList(QList *e) +#endif +{ + enemies = e; +} + +bool KGrEnemy::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 1) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if enemy's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + return true; + } + } + } + return false; +} + +bool KGrEnemy::bumpingFriend() +{ +// Enemies that are falling are treated as being invisible (i.e. a walking +// enemy will walk through them or they will stop on that enemy's head). +// +// If two enemies are moving in opposite directions, they are allowed to come +// within two cell widths of each other (8 steps). Then one must stop before +// entering the next cell and let the other one go into it. If both are about +// to enter a new cell, the one on the right or above gives way to the one on +// the left or below (implemented by letting the latter approach to 7 steps +// apart before detecting an impending collision, by which time the first +// enemy should have stopped and given way). +// +// In all other cases, enemies are allowed to approach to 4 steps apart (i.e. +// bumping a friend), before being forced to stop and wait. If they somehow +// get closer than 4 steps (i.e. overlapping), the lower ID enemy is allowed +// to move out while the higher ID enemy waits. This can happen if one enemy +// falls into another or is reborn where another enemy is located. + + int c, cmax; + KGrEnemy *enemy; + int dx, dy; + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->enemyId != enemyId) && (enemy->status != FALLING)) { + dx = enemy->getx() - absx; + dy = enemy->gety() - absy; + switch (direction) { + case LEFT: + if ((dx >= -32) && (dx < 16) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == RIGHT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx >= -16) { + if ((dx > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case RIGHT: + if ((dx > -16) && (dx < 32) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == LEFT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx <= 16) { + if ((dx < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case UP: + if ((dy >= -32) && (dy < 16) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == DOWN) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy >= -16) { + if ((dy > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one above. + } + } + break; + case DOWN: + if ((dy > -16) && (dy < 32) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == UP) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy <= 16) { + if ((dy < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one below. + } + } + break; + default: + break; + } + } + } + return FALSE; +} + +KGrEnemy :: ~KGrEnemy () +{ + delete captiveTimer; + delete walkTimer; + delete fallTimer; +} + +#include "kgrfigure.moc" diff --git a/kgoldrunner/src/kgrfigure.h b/kgoldrunner/src/kgrfigure.h new file mode 100644 index 00000000..e6ee0f2e --- /dev/null +++ b/kgoldrunner/src/kgrfigure.h @@ -0,0 +1,231 @@ +/*************************************************************************** + * kgrfigure.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * email : See menu "Help, About KGoldrunner" * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRFIGURE_H +#define KGRFIGURE_H + +// Obsolete - #include +#include + +#include +#ifdef QT3 +#include +#else +#include +#endif +#include +#include +#include +#include +#include // für Zufallsfunktionen + +class KGrCanvas; +class KGrObject; +class KGrEnemy; + +class KGrFigure : public QObject +{ + Q_OBJECT +public: + KGrFigure (int, int); + virtual ~KGrFigure(); + + // STATIC GLOBAL FLAGS. + static bool variableTiming; // More enemies imply less speed. + static bool alwaysCollectNugget; // Enemies always collect nuggets. + static bool runThruHole; // Enemy can run L/R through dug hole. + static bool reappearAtTop; // Enemies are reborn at top of screen. + static SearchStrategy searchStrategy; // Low, medium or high difficulty. + + static Timing fixedTiming; // Original set of 6 KGr timing values. + + static Timing varTiming [6]; // Optional 6 sets of timing values, + // dependent on number of enemies. + int getx(); + int gety(); + Status getStatus(); + + int getnuggets(); + void setNuggets(int n); + void setPlayfield(KGrObject *(*p)[30][22]); + void showFigure(); //zeigt Figur + virtual void init(int,int); + void eraseOldFigure(); + +protected: + // STATIC GLOBAL VARIABLES. + static int herox; + static int heroy; + + static int speed; // Adjustable game-speed factor. + + int x, y; + int absx, absy; + int relx, rely; // Figur wird um relx,rely Pixel verschoben + int mem_x,mem_y,mem_relx,mem_rely; + int walkCounter; + int nuggets; + int actualPixmap; // ArrayPos der zu zeichnenden Pixmap + QTimer *walkTimer; + QTimer *fallTimer; + + KGrObject *(*playfield)[30][22]; + Status status; + Direction direction; + bool canWalkRight(); + bool canWalkLeft(); + virtual bool canWalkUp(); + bool canWalkDown(); + bool canStand(); + bool hangAtPole(); + virtual bool standOnEnemy()=0; + void walkUp(int); + void walkDown(int, int); + void walkRight(int, int); + void walkLeft(int, int); + void initFall(int, int); + + bool walkFrozen; + bool fallFrozen; +}; + +class KGrHero : public KGrFigure +{ + Q_OBJECT +public: + KGrHero(KGrCanvas *, int , int); + virtual ~KGrHero(); + bool started; + void showFigure(); + void dig(); + void digLeft(); + void digRight(); + void startWalk(); +#ifdef QT3 + void setEnemyList(QPtrList *); +#else + void setEnemyList(QList *); +#endif + void init(int,int); + void setKey(Direction); + void setDirection(int, int); + void start(); + void loseNugget(); + static int WALKDELAY; + static int FALLDELAY; + void setSpeed(int); + void doStep(); + void showState (char); + +private: +#ifdef QT3 + QPtrList *enemies; +#else + QList *enemies; +#endif + KGrCanvas * heroView; + bool standOnEnemy(); + bool isInEnemy(); + bool isInside(int, int); + + Direction nextDir; + void collectNugget(); + + bool mouseMode; + bool stopped; + int mousex; + int mousey; + void setNextDir(); + +public slots: + void walkTimeDone(); + void fallTimeDone(); + +signals: + void gotNugget(int); + void haveAllNuggets(); + void leaveLevel(); + void caughtHero(); +}; + +class KGrEnemy : public KGrFigure +{ + Q_OBJECT +public: + KGrEnemy (KGrCanvas *, int , int); + virtual ~KGrEnemy(); + void showFigure(); + void startSearching(); +#ifdef QT3 + void setEnemyList(QPtrList *); +#else + void setEnemyList(QList *); +#endif + virtual void init(int,int); + static int WALKDELAY; + static int FALLDELAY; + static int CAPTIVEDELAY; + int enemyId; + void doStep(); + void showState (char); + +private: + KGrCanvas * enemyView; + int birthX, birthY; + int searchStatus; + int captiveCounter; + QTimer *captiveTimer; + bool canWalkUp(); +#ifdef QT3 + QPtrList *enemies; +#else + QList *enemies; +#endif + bool standOnEnemy(); + bool bumpingFriend(); + + void startWalk(); + void dieAndReappear(); + Direction searchbestway(int,int,int,int); + Direction searchdownway(int,int); + Direction searchupway(int,int); + Direction searchleftway(int,int); + Direction searchrightway(int,int); + + Direction lowSearchUp(int,int,int); + Direction lowSearchDown(int,int,int); + Direction lowGetHero(int,int,int); + + int distanceUp (int, int, int); + int distanceDown (int, int, int); + bool searchOK (int, int, int); + int canWalkLR (int, int, int); + bool willNotFall (int, int); + + void collectNugget(); + void dropNugget(); + + bool captiveFrozen; + +public slots: + void walkTimeDone(); + void fallTimeDone(); + void captiveTimeDone(); + +signals: + void lostNugget(); + void trapped(int); + void killed(int); +}; + +#endif // KGRFIGURE_H diff --git a/kgoldrunner/src/kgrgame.cpp b/kgoldrunner/src/kgrgame.cpp new file mode 100644 index 00000000..93164d88 --- /dev/null +++ b/kgoldrunner/src/kgrgame.cpp @@ -0,0 +1,2613 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" + +#include "kgrgame.h" + +// Obsolete - #include +#include +#include +#include + +#include +#include + +#ifndef KGR_PORTABLE +#include +#endif + +/******************************************************************************/ +/*********************** KGOLDRUNNER GAME CLASS *************************/ +/******************************************************************************/ + +KGrGame::KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir) +{ + view = theView; + systemDataDir = theSystemDir; + userDataDir = theUserDir; + + // Set the game-editor OFF, but available. + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + shouldSave = FALSE; + + enemies.setAutoDelete(TRUE); + + hero = new KGrHero (view, 0, 0); // The hero is born ... Yay !!! + hero->setPlayfield (&playfield); + + setBlankLevel (TRUE); // Fill the playfield with blank walls. + + enemy = NULL; + newLevel = TRUE; // Next level will be a new one. + loading = TRUE; // Stop input until it is loaded. + + modalFreeze = FALSE; + messageFreeze = FALSE; + + connect (hero, SIGNAL (gotNugget(int)), SLOT (incScore(int))); + connect (hero, SIGNAL (caughtHero()), SLOT (herosDead())); + connect (hero, SIGNAL (haveAllNuggets()), SLOT (showHiddenLadders())); + connect (hero, SIGNAL (leaveLevel()), SLOT (goUpOneLevel())); + + dyingTimer = new QTimer (this); + connect (dyingTimer, SIGNAL (timeout()), SLOT (finalBreath())); + + // Get the mouse position every 40 msec. It is used to steer the hero. + mouseSampler = new QTimer (this); + connect (mouseSampler, SIGNAL(timeout()), SLOT (readMousePos ())); + mouseSampler->start (40, FALSE); + + srand(1); // initialisiere Random-Generator +} + +KGrGame::~KGrGame() +{ +} + +/******************************************************************************/ +/************************* GAME SELECTION PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::startLevelOne() +{ + startLevel (SL_START, 1); +} + +void KGrGame::startAnyLevel() +{ + startLevel (SL_ANY, level); +} + +void KGrGame::startNextLevel() +{ + startLevel (SL_ANY, level + 1); +} + +void KGrGame::startLevel (int startingAt, int requestedLevel) +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + // Use dialog box to select game and level: startingAt = ID_FIRST or ID_ANY. + int selectedLevel = selectLevel (startingAt, requestedLevel); + if (selectedLevel > 0) { // If OK, start the selected game and level. + newGame (selectedLevel, selectedGame); + } else { + level = 0; + } +} + +/******************************************************************************/ +/************************ MAIN GAME EVENT PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::incScore (int n) +{ + score = score + n; // SCORING: trap enemy 75, kill enemy 75, + emit showScore (score); // collect gold 250, complete the level 1500. +} + +void KGrGame::herosDead() +{ + if ((level < 1) || (lives <= 0)) + return; // Game over: we are in the "ENDE" screen. + + // Lose a life. + if (--lives > 0) { + // Still some life left, so PAUSE and then re-start the level. + emit showLives (lives); + KGrObject::frozen = TRUE; // Freeze the animation and let + dyingTimer->start (1500, TRUE); // the player see what happened. + } + else { + // Game over: display the "ENDE" screen. + emit showLives (lives); + freeze(); + QString gameOver = "" + i18n("GAME OVER !!!") + ""; + KGrMessage::information (view, collection->name, gameOver); + checkHighScore(); // Check if there is a high score for this game. + + enemyCount = 0; + enemies.clear(); // Stop the enemies catching the hero again ... + view->deleteEnemySprites(); + unfreeze(); // ... NOW we can unfreeze. + newLevel = TRUE; + level = 0; + loadLevel (level); // Display the "ENDE" screen. + newLevel = FALSE; + } +} + +void KGrGame::finalBreath() +{ + // Fix bug 95202: Avoid re-starting if the player selected + // edit mode before the 1.5 seconds were up. + if (! editMode) { + enemyCount = 0; // Hero is dead: re-start the level. + loadLevel (level); + } + KGrObject::frozen = FALSE; // Unfreeze the game, but don't move yet. +} + +void KGrGame::showHiddenLadders() +{ + int i,j; + for (i=1;i<21;i++) + for (j=1;j<29;j++) + if (playfield[j][i]->whatIam()==HLADDER) + ((KGrHladder *)playfield[j][i])->showLadder(); + view->updateCanvas(); + initSearchMatrix(); +} + +void KGrGame::goUpOneLevel() +{ + lives++; // Level completed: gain another life. + emit showLives (lives); + incScore (1500); + + if (level >= collection->nLevels) { + freeze(); + KGrMessage::information (view, collection->name, + i18n("CONGRATULATIONS !!!!" + "

    You have conquered the last level in the %1 game !!

    ") + .arg("\"" + collection->name + "\"")); + checkHighScore(); // Check if there is a high score for this game. + + unfreeze(); + level = 0; // Game completed: display the "ENDE" screen. + } + else { + level++; // Go up one level. + emit showLevel (level); + } + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::loseNugget() +{ + hero->loseNugget(); // Enemy trapped/dead and holding a nugget. +} + +KGrHero * KGrGame::getHero() +{ + return (hero); // Return a pointer to the hero. +} + +int KGrGame::getLevel() // Return the current game-level. +{ + return (level); +} + +bool KGrGame::inMouseMode() +{ + return (mouseMode); // Return TRUE if game is under mouse control. +} + +bool KGrGame::inEditMode() +{ + return (editMode); // Return TRUE if the game-editor is active. +} + +bool KGrGame::isLoading() +{ + return (loading); // Return TRUE if a level is being loaded. +} + +void KGrGame::setMouseMode (bool on_off) +{ + mouseMode = on_off; // Set Mouse OR keyboard control. +} + +void KGrGame::freeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (TRUE); // Do visual feedback in the GUI. + } + KGrObject::frozen = TRUE; // Halt the game, by blocking all timer events. +} + +void KGrGame::unfreeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (FALSE);// Do visual feedback in the GUI. + } + KGrObject::frozen = FALSE; // Restart the game. Because frozen == FALSE, + restart(); // the game goes on running after the next step. +} + +void KGrGame::setMessageFreeze (bool on_off) +{ + if (on_off) { // Freeze the game action during a message. + messageFreeze = FALSE; + if (! KGrObject::frozen) { + messageFreeze = TRUE; + freeze(); + } + } + else { // Unfreeze the game action after a message. + if (messageFreeze) { + unfreeze(); + messageFreeze = FALSE; + } + } +} + +void KGrGame::setBlankLevel(bool playable) +{ + for (int j=0;j<20;j++) + for (int i=0;i<28;i++) { + if (playable) { + //playfield[i+1][j+1] = new KGrFree (freebg, nuggetbg, false, view); + playfield[i+1][j+1] = new KGrFree (FREE,i+1,j+1,view); + } + else { + //playfield[i+1][j+1] = new KGrEditable (freebg, view); + playfield[i+1][j+1] = new KGrEditable (FREE); + view->paintCell (i+1, j+1, FREE); + } + editObjArray[i+1][j+1] = FREE; + } + for (int j=0;j<30;j++) { + //playfield[j][0]=new KGrBeton(QPixmap ()); + playfield[j][0]=new KGrObject (BETON); + editObjArray[j][0] = BETON; + //playfield[j][21]=new KGrBeton(QPixmap ()); + playfield[j][21]=new KGrObject (BETON); + editObjArray[j][21] = BETON; + } + for (int i=0;i<22;i++) { + //playfield[0][i]=new KGrBeton(QPixmap ()); + playfield[0][i]=new KGrObject (BETON); + editObjArray[0][i] = BETON; + //playfield[29][i]=new KGrBeton(QPixmap ()); + playfield[29][i]=new KGrObject (BETON); + editObjArray[29][i] = BETON; + } + //for (int j=0;j<22;j++) + //for (int i=0;i<30;i++) { + //playfield[i][j]->move(16+i*16,16+j*16); + //} +} + +void KGrGame::newGame (const int lev, const int gameIndex) +{ + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; // "loadLevel (level)" will reset it. + + if (editMode) { + emit setEditMenu (FALSE); // Disable edit menu items and toolbar. + + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + + view->setHeroVisible (TRUE); + } + + newLevel = TRUE; + level = lev; + collnIndex = gameIndex; + collection = collections.at (collnIndex); + owner = collection->owner; + + lives = 5; // Start with 5 lives. + score = 0; + startScore = 0; + + emit showLives (lives); + emit showScore (score); + emit showLevel (level); + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::startTutorial() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + int i, index; + int imax = collections.count(); + bool found = FALSE; + + index = 0; + for (i = 0; i < imax; i++) { + index = i; // Index within owner. + if (collections.at(i)->prefix == "tute") { + found = TRUE; + break; + } + } + if (found) { + // Start the tutorial. + collection = collections.at (index); + owner = collection->owner; + emit markRuleType (collection->settings); + collnIndex = index; + level = 1; + newGame (level, collnIndex); + } + else { + KGrMessage::information (view, i18n("Start Tutorial"), + i18n("Cannot find the tutorial game (file-prefix %1) in " + "the %2 files.") + .arg("'tute'").arg("'games.dat'")); + } +} + +void KGrGame::showHint() +{ + // Put out a hint for this level. + QString caption = i18n("Hint"); + + if (levelHint.length() > 0) + myMessage (view, caption, levelHint); + else + myMessage (view, caption, + i18n("Sorry, there is no hint for this level.")); +} + +int KGrGame::loadLevel (int levelNo) +{ + int i,j; + QFile openlevel; + + if (! openLevelFile (levelNo, openlevel)) { + return 0; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + nuggets = 0; + enemyCount=0; + startScore = score; // What we will save, if asked. + + // lade den Level + for (j=1;j<21;j++) + for (i=1;i<29;i++) { + changeObject(openlevel.getch(),i,j); + } + + // Absorb a newline character, then read in the level name and hint (if any). + int c = openlevel.getch(); + levelName = ""; + levelHint = ""; + QCString levelNameC = ""; + QCString levelHintC = ""; + i = 1; + while ((c = openlevel.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + openlevel.close(); + + // If there is a name, recode any UTF-8 substrings and translate it right now. + if (levelNameC.length() > 0) + levelName = i18n((const char *) levelNameC); + + // Indicate on the menus whether there is a hint for this level. + int len = levelHintC.length(); + emit hintAvailable (len > 0); + + // If there is a hint, remove the final newline and translate it right now. + if (len > 0) + levelHint = i18n((const char *) levelHintC.left(len-1)); + + // Disconnect edit-mode slots from signals from "view". + disconnect (view, SIGNAL (mouseClick(int)), 0, 0); + disconnect (view, SIGNAL (mouseLetGo(int)), 0, 0); + + if (newLevel) { + hero->setEnemyList (&enemies); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) + enemy->setEnemyList(&enemies); + } + + hero->setNuggets(nuggets); + setTimings(); + + // Set direction-flags to use during enemy searches. + initSearchMatrix(); + + // Re-draw the playfield frame, level title and figures. + view->setTitle (getTitle()); + view->updateCanvas(); + + // Check if this is a tutorial collection and we are not on the "ENDE" screen. + if ((collection->prefix.left(4) == "tute") && (levelNo != 0)) { + // At the start of a tutorial, put out an introduction. + if (levelNo == 1) + myMessage (view, collection->name, + i18n((const char *) collection->about.utf8())); + + // Put out an explanation of this level. + myMessage (view, getTitle(), levelHint); + } + + // Put the mouse pointer on the hero. + if (mouseMode) + view->setMousePos (startI, startJ); + + // Connect play-mode slot to signal from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doDig(int))); + + // Re-enable player input. + loading = FALSE; + + return 1; +} + +bool KGrGame::openLevelFile (int levelNo, QFile & openlevel) +{ + QString filePath; + QString msg; + + filePath = getFilePath (owner, collection, levelNo); + + openlevel.setName (filePath); + + // gucken ob und welcher Level existiert + + if (! openlevel.exists()) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot find file '%1'. Please make sure '%2' has been " + "run in the '%3' folder.") + .arg(filePath).arg("tar xf levels.tar").arg(systemDataDir.myStr())); + return (FALSE); + } + + // öffne Level zum lesen + if (! openlevel.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + return (TRUE); +} + +void KGrGame::changeObject (unsigned char kind, int i, int j) +{ + delete playfield[i][j]; + switch(kind) { + case FREE: createObject(new KGrFree (FREE,i,j,view),FREE,i,j);break; + case LADDER: createObject(new KGrObject (LADDER),LADDER,i,j);break; + case HLADDER: createObject(new KGrHladder (HLADDER,i,j,view),FREE,i,j);break; + case BRICK: createObject(new KGrBrick (BRICK,i,j,view),BRICK,i,j);break; + case BETON: createObject(new KGrObject (BETON),BETON,i,j);break; + case FBRICK: createObject(new KGrObject (FBRICK),BRICK,i,j);break; + case POLE: createObject(new KGrObject (POLE),POLE,i,j);break; + case NUGGET: createObject(new KGrFree (NUGGET,i,j,view),NUGGET,i,j); + nuggets++;break; + case HERO: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + hero->init(i,j); + startI = i; startJ = j; + hero->started = FALSE; + hero->showFigure(); + break; + case ENEMY: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + if (newLevel){ + // Starting a level for the first time. + enemy = new KGrEnemy (view, i, j); + enemy->setPlayfield(&playfield); + enemy->enemyId = enemyCount++; + enemies.append(enemy); + connect(enemy, SIGNAL(lostNugget()), SLOT(loseNugget())); + connect(enemy, SIGNAL(trapped(int)), SLOT(incScore(int))); + connect(enemy, SIGNAL(killed(int)), SLOT(incScore(int))); + } else { + // Starting a level again after losing. + enemy=enemies.at(enemyCount); + enemy->enemyId=enemyCount++; + enemy->setNuggets(0); + enemy->init(i,j); // Re-initialise the enemy's state information. + } + enemy->showFigure(); + break; + default : createObject(new KGrBrick(BRICK,i,j,view),BRICK,i,j);break; + } +} + +void KGrGame::createObject (KGrObject *o, char picType, int x, int y) +{ + playfield[x][y] = o; + view->paintCell (x, y, picType); // Pic maybe not same as object. +} + +void KGrGame::setTimings () +{ + Timing * timing; + int c = -1; + + if (KGrFigure::variableTiming) { + c = enemies.count(); // Timing based on enemy count. + c = (c > 5) ? 5 : c; + timing = &(KGrFigure::varTiming[c]); + } + else { + timing = &(KGrFigure::fixedTiming); // Fixed timing. + } + + KGrHero::WALKDELAY = timing->hwalk; + KGrHero::FALLDELAY = timing->hfall; + KGrEnemy::WALKDELAY = timing->ewalk; + KGrEnemy::FALLDELAY = timing->efall; + KGrEnemy::CAPTIVEDELAY = timing->ecaptive; + KGrBrick::HOLETIME = timing->hole; +} + +void KGrGame::initSearchMatrix() +{ + // Called at start of level and also when hidden ladders appear. + int i,j; + + for (i=1;i<21;i++){ + for (j=1;j<29;j++) + { + // If on ladder, can walk L, R, U or D. + if (playfield[j][i]->whatIam()==LADDER) + playfield[j][i]->searchValue = CANWALKLEFT + CANWALKRIGHT + + CANWALKUP + CANWALKDOWN; + else + // If on solid ground, can walk L or R. + if ((playfield[j][i+1]->whatIam()==BRICK)|| + (playfield[j][i+1]->whatIam()==HOLE)|| + (playfield[j][i+1]->whatIam()==USEDHOLE)|| + (playfield[j][i+1]->whatIam()==BETON)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT; + else + // If on pole or top of ladder, can walk L, R or D. + if ((playfield[j][i]->whatIam()==POLE)|| + (playfield[j][i+1]->whatIam()==LADDER)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT+CANWALKDOWN; + else + // Otherwise, gravity takes over ... + playfield[j][i]->searchValue=CANWALKDOWN; + + // Clear corresponding bits if there are solids to L, R, U or D. + if(playfield[j][i-1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKUP; + if(playfield[j-1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKLEFT; + if(playfield[j+1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKRIGHT; + if(playfield[j][i+1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKDOWN; + } + } +} + +void KGrGame::startPlaying () { + if (! hero->started) { + // Start the enemies and the hero. + for (--enemyCount; enemyCount>=0; --enemyCount) { + enemy=enemies.at(enemyCount); + enemy->startSearching(); + } + hero->start(); + } +} + +QString KGrGame::getFilePath (Owner o, KGrCollection * colln, int lev) +{ + QString filePath; + + if (lev == 0) { + // End of game: show the "ENDE" screen. + o = SYSTEM; + filePath = "level000.grl"; + } + else { + filePath.setNum (lev); // Convert INT -> QString. + filePath = filePath.rightJustify (3,'0'); // Add 0-2 zeros at left. + filePath.append (".grl"); // Add KGoldrunner level-suffix. + filePath.prepend (colln->prefix); // Add collection file-prefix. + } + + filePath.prepend (((o == SYSTEM)? systemDataDir : userDataDir) + "levels/"); + + return (filePath); +} + +QString KGrGame::getTitle() +{ + QString levelTitle; + if (level == 0) { + // Generate a special title: end of game or creating a new level. + if (! editMode) + levelTitle = "E N D --- F I N --- E N D E"; + else + levelTitle = i18n("New Level"); + } + else { + // Generate title string "Collection-name - NNN - Level-name". + levelTitle.setNum (level); + levelTitle = levelTitle.rightJustify (3,'0'); + levelTitle = collection->name + " - " + levelTitle; + if (levelName.length() > 0) { + levelTitle = levelTitle + " - " + levelName; + } + } + return (levelTitle); +} + +void KGrGame::readMousePos() +{ + QPoint p; + int i, j; + + // If loading a level for play or editing, ignore mouse-position input. + if (loading) return; + + // If game control is currently by keyboard, ignore the mouse. + if ((! mouseMode) && (! editMode)) return; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + if (editMode) { + // Editing - check if we are in paint mode and have moved the mouse. + if (paintEditObj && ((i != oldI) || (j != oldJ))) { + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + } + } + else { + // Playing - if the level has started, control the hero. + if (KGrObject::frozen) return; // If game is stopped, do nothing. + + hero->setDirection (i, j); + + // Start playing when the mouse moves off the hero. + if ((! hero->started) && ((i != startI) || (j != startJ))) { + startPlaying(); + } + } +} + +void KGrGame::doDig (int button) { + + // If game control is currently by keyboard, ignore the mouse. + if (editMode) return; + if (! mouseMode) return; + + // If loading a level for play or editing, ignore mouse-button input. + if ((! loading) && (! KGrObject::frozen)) { + if (! hero->started) { + startPlaying(); // If first player-input, start playing. + } + switch (button) { + case LeftButton: hero->digLeft (); break; + case RightButton: hero->digRight (); break; + default: break; + } + } +} + +void KGrGame::heroAction (KBAction movement) +{ + switch (movement) { + case KB_UP: hero->setKey (UP); break; + case KB_DOWN: hero->setKey (DOWN); break; + case KB_LEFT: hero->setKey (LEFT); break; + case KB_RIGHT: hero->setKey (RIGHT); break; + case KB_STOP: hero->setKey (STAND); break; + case KB_DIGLEFT: hero->setKey (STAND); hero->digLeft (); break; + case KB_DIGRIGHT: hero->setKey (STAND); hero->digRight (); break; + } +} + +/******************************************************************************/ +/************************** SAVE AND RE-LOAD GAMES **************************/ +/******************************************************************************/ + +void KGrGame::saveGame() // Save game ID, score and level. +{ + if (editMode) {myMessage (view, i18n("Save Game"), + i18n("Sorry, you cannot save your game play while you are editing. " + "Please try menu item %1.").arg("\"" + i18n("&Save Edits...") + "\"")); + return; + } + if (hero->started) {myMessage (view, i18n("Save Game"), + i18n("Please note: for reasons of simplicity, your saved game " + "position and score will be as they were at the start of this " + "level, not as they are now.")); + } + + QDate today = QDate::currentDate(); + QTime now = QTime::currentTime(); + QString saved; + QString day; +#ifdef QT3 + day = today.shortDayName(today.dayOfWeek()); +#else + day = today.dayName(today.dayOfWeek()); +#endif + saved = saved.sprintf + ("%-6s %03d %03ld %7ld %s %04d-%02d-%02d %02d:%02d\n", + collection->prefix.myStr(), level, lives, startScore, + day.myStr(), + today.year(), today.month(), today.day(), + now.hour(), now.minute()); + + QFile file1 (userDataDir + "savegame.dat"); + QFile file2 (userDataDir + "savegame.tmp"); + + if (! file2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "savegame.tmp")); + return; + } + QTextStream text2 (&file2); + text2 << saved; + + if (file1.exists()) { + if (! file1.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + QTextStream text1 (&file1); + int n = 30; // Limit the file to the last 30 saves. + while ((! text1.endData()) && (--n > 0)) { + saved = text1.readLine() + "\n"; + text2 << saved; + } + file1.close(); + } + + file2.close(); + + QDir dir; + dir.rename (file2.name(), file1.name(), TRUE); + KGrMessage::information (view, i18n("Save Game"), + i18n("Your game has been saved.")); +} + +void KGrGame::loadGame() // Re-load game, score and level. +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + QFile savedGames (userDataDir + "savegame.dat"); + if (! savedGames.exists()) { + // Use myMessage() because it stops the game while the message appears. + myMessage (view, i18n("Load Game"), + i18n("Sorry, there are no saved games.")); + return; + } + + if (! savedGames.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + // Halt the game during the loadGame() dialog. + modalFreeze = FALSE; + if (!KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + QString s; + + KGrLGDialog * lg = new KGrLGDialog (&savedGames, collections, + view, "loadDialog"); + + if (lg->exec() == QDialog::Accepted) { + s = lg->getCurrentText(); + } + + bool found = FALSE; + QString pr; + int lev; + int i; + int imax = collections.count(); + + if (! s.isNull()) { + pr = s.mid (21, 7); // Get the collection prefix. + pr = pr.left (pr.find (" ", 0, FALSE)); + + for (i = 0; i < imax; i++) { // Find the collection. + if (collections.at(i)->prefix == pr) { + collection = collections.at(i); + collnIndex = i; + owner = collections.at(i)->owner; + found = TRUE; + break; + } + } + if (found) { + // Set the rules for the selected game. + emit markRuleType (collection->settings); + lev = s.mid (28, 3).toInt(); + newGame (lev, collnIndex); // Re-start the selected game. + lives = s.mid (32, 3).toLong(); // Update the lives. + emit showLives (lives); + score = s.mid (36, 7).toLong(); // Update the score. + emit showScore (score); + } + else { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot find the game with prefix '%1'.").arg(pr)); + } + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete lg; +} + +/******************************************************************************/ +/************************** HIGH-SCORE PROCEDURES ***************************/ +/******************************************************************************/ + +void KGrGame::checkHighScore() +{ + bool prevHigh = TRUE; + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + QString thisUser = i18n("Unknown"); + int highCount = 0; + + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") + return; + + if (score <= 0) + return; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + prevHigh = FALSE; + } + } + + // If a previous high score file exists, check the current score against it. + if (prevHigh) { + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + // Read previous users, levels and scores from the high score file. + s1.setDevice (&high1); + bool found = FALSE; + highCount = 0; + while (! s1.endData()) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + delete prevUser; + delete prevDate; + highCount++; + if (score > prevScore) { + found = TRUE; // We have a high score. + break; + } + } + + // Check if higher than one on file or fewer than 10 previous scores. + if ((! found) && (highCount >= 10)) { + return; // We did not have a high score. + } + } + + /* ************************************************************* */ + /* If we have come this far, we have a new high score to record. */ + /* ************************************************************* */ + + QFile high2 (userDataDir + "hi_" + collection->prefix + ".tmp"); + QDataStream s2; + + if (! high2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "hi_" + collection->prefix + ".tmp")); + return; + } + + // Dialog to ask the user to enter their name. + QDialog * hsn = new QDialog (view, "hsNameDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hsn, margin, spacing); + + QLabel * hsnMessage = new QLabel ( + i18n("Congratulations !!! " + "You have achieved a high " + "score in this game. Please enter your name so that " + "it may be enshrined in the KGoldrunner Hall of Fame."), + hsn); + QLineEdit * hsnUser = new QLineEdit (hsn); + QPushButton * OK = new KPushButton (KStdGuiItem::ok(), hsn); + + mainLayout-> addWidget (hsnMessage); + mainLayout-> addWidget (hsnUser); + mainLayout-> addWidget (OK); + + hsn-> setCaption (i18n("Save High Score")); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hsn-> move (p.x() + 50, p.y() + 50); + + OK-> setAccel (Key_Return); + hsnUser-> setFocus(); // Set the keyboard input on. + + connect (hsnUser, SIGNAL (returnPressed ()), hsn, SLOT (accept ())); + connect (OK, SIGNAL (clicked ()), hsn, SLOT (accept ())); + + while (TRUE) { + hsn->exec(); + thisUser = hsnUser->text(); + if (thisUser.length() > 0) + break; + KGrMessage::information (view, i18n("Save High Score"), + i18n("You must enter something. Please try again.")); + } + + delete hsn; + + QDate today = QDate::currentDate(); + QString hsDate; +#ifdef QT3 + QString day = today.shortDayName(today.dayOfWeek()); +#else + QString day = today.dayName(today.dayOfWeek()); +#endif + hsDate = hsDate.sprintf + ("%s %04d-%02d-%02d", + day.myStr(), + today.year(), today.month(), today.day()); + + s2.setDevice (&high2); + + if (prevHigh) { + high1.reset(); + bool scoreRecorded = FALSE; + highCount = 0; + while ((! s1.endData()) && (highCount < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + if ((! scoreRecorded) && (score > prevScore)) { + highCount++; + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + scoreRecorded = TRUE; + } + if (highCount < 10) { + highCount++; + s2 << prevUser; + s2 << prevLevel; + s2 << prevScore; + s2 << prevDate; + } + delete prevUser; + delete prevDate; + } + if ((! scoreRecorded) && (highCount < 10)) { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + high1.close(); + } + else { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krüger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + + high2.close(); + + QDir dir; + dir.rename (high2.name(), + userDataDir + "hi_" + collection->prefix + ".dat", TRUE); + KGrMessage::information (view, i18n("Save High Score"), + i18n("Your high score has been saved.")); + + showHighScores(); + return; +} + +void KGrGame::showHighScores() +{ + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, we do not keep high scores for tutorial games.")); + return; + } + + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + int n = 0; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, there are no high scores for the %1 game yet.") + .arg("\"" + collection->name + "\"")); + return; + } + } + + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + QDialog * hs = new QDialog (view, "hsDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hs, margin, spacing); + + QLabel * hsHeader = new QLabel (i18n ( + "

    KGoldrunner Hall of Fame


    " + "

    \"%1\" Game

    ") + .arg(collection->name), + hs); + QLabel * hsColHeader = new QLabel ( + i18n(" Name " + "Level Score Date"), hs); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f. setFixedPitch (TRUE); + f. setBold (TRUE); + hsColHeader-> setFont (f); + + QLabel * hsLine [10]; + + QHBox * buttons = new QHBox (hs); + buttons-> setSpacing (spacing); + QPushButton * OK = new KPushButton (KStdGuiItem::close(), buttons); + + mainLayout-> addWidget (hsHeader); + mainLayout-> addWidget (hsColHeader); + + hs-> setCaption (i18n("High Scores")); + + OK-> setAccel (Key_Return); + + // Set up the format for the high-score lines. + f. setBold (FALSE); + QString line; + const char * hsFormat = "%2d. %-30.30s %3d %7ld %s"; + + // Read and display the users, levels and scores from the high score file. + s1.setDevice (&high1); + n = 0; + while ((! s1.endData()) && (n < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + + // QString::sprintf expects UTF-8 encoding in its string arguments, so + // prevUser has been saved on file as UTF-8 to allow non=ASCII chars + // in the user's name (e.g. "Krüger" is encoded as "Krüger" in UTF-8). + + line = line.sprintf (hsFormat, + n+1, prevUser, prevLevel, prevScore, prevDate); + hsLine [n] = new QLabel (line, hs); + hsLine [n]->setFont (f); + mainLayout->addWidget (hsLine [n]); + + delete prevUser; + delete prevDate; + n++; + } + + QFrame * separator = new QFrame (hs); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + OK-> setMaximumWidth (100); + mainLayout-> addWidget (buttons); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hs-> move (p.x() + 50, p.y() + 50); + + // Start up the dialog box. + connect (OK, SIGNAL (clicked ()), hs, SLOT (accept ())); + hs-> exec(); + + delete hs; +} + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +void KGrGame::doStep() +{ + if (KGrObject::frozen) { // The game must have been halted. + restart(); // Do one step and halt again. + } +} + +void KGrGame::restart() +{ + bool temp; + int i,j; + + if (editMode) // Can't move figures when in Edit Mode. + return; + + temp = KGrObject::frozen; + + KGrObject::frozen = FALSE; // Temporarily restart the game, by re-running + // any timer events that have been blocked. + + readMousePos(); // Set hero's direction. + hero->doStep(); // Move the hero one step. + + j = enemies.count(); // Move each enemy one step. + for (i = 0; i < j; i++) { + enemy = enemies.at(i); // Need to use an index because called methods + enemy->doStep(); // change the "current()" of the "enemies" list. + } + + for (i=1; i<=28; i++) + for (j=1; j<=20; j++) { + if ((playfield[i][j]->whatIam() == HOLE) || + (playfield[i][j]->whatIam() == USEDHOLE) || + (playfield[i][j]->whatIam() == BRICK)) + ((KGrBrick *)playfield[i][j])->doStep(); + } + + KGrObject::frozen = temp; // If frozen was TRUE, halt again, which gives a + // single-step effect, otherwise go on running. +} + +void KGrGame::showFigurePositions() +{ + if (KGrObject::frozen) { + hero->showState('p'); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + enemy->showState('p'); + } + } +} + +void KGrGame::showHeroState() +{ + if (KGrObject::frozen) { + hero->showState('s'); + } +} + +void KGrGame::showEnemyState(int enemyId) +{ + if (KGrObject::frozen) { + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + if (enemy->enemyId == enemyId) enemy->showState('s'); + } + } +} + +void KGrGame::showObjectState() +{ + QPoint p; + int i, j; + KGrObject * myObject; + + if (KGrObject::frozen) { + p = view->getMousePos (); + i = p.x(); j = p.y(); + myObject = playfield[i][j]; + switch (myObject->whatIam()) { + case BRICK: + case HOLE: + case USEDHOLE: + ((KGrBrick *)myObject)->showState(i, j); break; + default: myObject->showState(i, j); break; + } + } +} + +void KGrGame::bugFix() +{ + if (KGrObject::frozen) { // Toggle a bug fix on/off dynamically. + KGrObject::bugFixed = (KGrObject::bugFixed) ? FALSE : TRUE; + printf ("%s", (KGrObject::bugFixed) ? "\n" : ""); + printf (">>> Bug fix is %s\n", (KGrObject::bugFixed) ? "ON" : "OFF\n"); + } +} + +void KGrGame::startLogging() +{ + if (KGrObject::frozen) { // Toggle logging on/off dynamically. + KGrObject::logging = (KGrObject::logging) ? FALSE : TRUE; + printf ("%s", (KGrObject::logging) ? "\n" : ""); + printf (">>> Logging is %s\n", (KGrObject::logging) ? "ON" : "OFF\n"); + } +} + +/******************************************************************************/ +/************ GAME EDITOR FUNCTIONS ACTIVATED BY MENU OR TOOLBAR ************/ +/******************************************************************************/ + +void KGrGame::setEditObj (char newEditObj) +{ + editObj = newEditObj; +} + +void KGrGame::createLevel() +{ + int i, j; + + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Create Level"), + i18n("You cannot create and save a level " + "until you have created a game to hold " + "it. Try menu item \"Create Game\".")); + return; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = 0; + initEdit(); + levelName = ""; + levelHint = ""; + + // Clear the playfield. + editObj = FREE; + for (i = 1; i <= FIELDWIDTH; i++) + for (j = 1; j <= FIELDHEIGHT; j++) { + insertEditObj (i, j); + editObjArray[i][j] = editObj; + } + + editObj = HERO; + insertEditObj (1, 1); + editObjArray[1][1] = editObj; + editObj = BRICK; + + showEditLevel(); + + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Re-enable player input. + loading = FALSE; + + view->updateCanvas(); // Show the edit area. + view->update(); // Show the level name. +} + +void KGrGame::updateLevel() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("You cannot edit and save a level until you " + "have created a game and a level. Try menu item \"Create Game\".")); + return; + } + + if (level < 0) level = 0; + int lev = selectLevel (SL_UPDATE, level); + if (lev == 0) + return; + + if (owner == SYSTEM) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("It is OK to edit a system level, but you MUST save " + "the level in one of your own games. You're not just " + "taking a peek at the hidden ladders " + "and fall-through bricks, are you? :-)")); + } + + loadEditLevel (lev); +} + +void KGrGame::updateNext() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + level++; + updateLevel(); +} + +void KGrGame::loadEditLevel (int lev) +{ + int i, j; + QFile levelFile; + + if (! openLevelFile (lev, levelFile)) + return; + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = lev; + initEdit(); + + // Load the level. + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + editObj = levelFile.getch (); + insertEditObj (i, j); + editObjArray[i][j] = editObj; + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Read a newline character, then read in the level name and hint (if any). + int c = levelFile.getch(); + QCString levelHintC = ""; + QCString levelNameC = ""; + levelHint = ""; + levelName = ""; + i = 1; + while ((c = levelFile.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + + // Retain the original language of the name and hint when editing, + // but remove the final \n and convert non-ASCII, UTF-8 substrings + // to Unicode (eg. ü to ü). + int len = levelHintC.length(); + if (len > 0) + levelHint = QString::fromUtf8((const char *) levelHintC.left(len-1)); + + len = levelNameC.length(); + if (len > 0) + levelName = QString::fromUtf8((const char *) levelNameC); + + editObj = BRICK; // Reset default object. + levelFile.close (); + + view->setTitle (getTitle()); // Show the level name. + view->updateCanvas(); // Show the edit area. + showEditLevel(); // Reconnect signals. + + // Re-enable player input. + loading = FALSE; +} + +void KGrGame::editNameAndHint() +{ + if (! editMode) + return; + + // Run a dialog box to create/edit the level name and hint. + KGrNHDialog * nh = new KGrNHDialog (levelName, levelHint, view, "NHDialog"); + + if (nh->exec() == QDialog::Accepted) { + levelName = nh->getName(); + levelHint = nh->getHint(); + shouldSave = TRUE; + } + + delete nh; +} + +bool KGrGame::saveLevelFile() +{ + bool isNew; + int action; + int selectedLevel = level; + + int i, j; + QString filePath; + + if (! editMode) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Inappropriate action: you are not editing a level.")); + return (FALSE); + } + + // Save the current collection index. + int N = collnIndex; + + if (selectedLevel == 0) { + // New level: choose a number. + action = SL_CREATE; + } + else { + // Existing level: confirm the number or choose a new number. + action = SL_SAVE; + } + + // Pop up dialog box, which could change the collection or level or both. + selectedLevel = selectLevel (action, selectedLevel); + if (selectedLevel == 0) + return (FALSE); + + // Get the new collection (if changed). + int n = collnIndex; + + // Set the name of the output file. + filePath = getFilePath (owner, collection, selectedLevel); + QFile levelFile (filePath); + + if ((action == SL_SAVE) && (n == N) && (selectedLevel == level)) { + // This is a normal edit: the old file is to be re-written. + isNew = FALSE; + } + else { + isNew = TRUE; + // Check if the file is to be inserted in or appended to the collection. + if (levelFile.exists()) { + switch (KGrMessage::warning (view, i18n("Save Level"), + i18n("Do you want to insert a level and " + "move existing levels up by one?"), + i18n("&Insert Level"), i18n("&Cancel"))) { + + case 0: if (! reNumberLevels (n, selectedLevel, + collections.at(n)->nLevels, +1)) { + return (FALSE); + } + break; + case 1: return (FALSE); + break; + } + } + } + + // Open the output file. + if (! levelFile.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the level. + for (j = 1; j < 21; j++) + for (i = 1; i < 29; i++) { + levelFile.putch (editObjArray[i][j]); + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + levelFile.putch ('\n'); + + // Save the level name, changing non-ASCII chars to UTF-8 (eg. ü to ü). + QCString levelNameC = levelName.utf8(); + int len1 = levelNameC.length(); + if (len1 > 0) { + for (i = 0; i < len1; i++) + levelFile.putch (levelNameC[i]); + levelFile.putch ('\n'); // Add a newline. + } + + // Save the level hint, changing non-ASCII chars to UTF-8 (eg. ü to ü). + QCString levelHintC = levelHint.utf8(); + int len2 = levelHintC.length(); + char ch = '\0'; + + if (len2 > 0) { + if (len1 <= 0) + levelFile.putch ('\n'); // Leave blank line for name. + for (i = 0; i < len2; i++) { + ch = levelHintC[i]; + levelFile.putch (ch); // Copy the character. + } + if (ch != '\n') + levelFile.putch ('\n'); // Add a newline character. + } + + levelFile.close (); + shouldSave = FALSE; + + if (isNew) { + collections.at(n)->nLevels++; + saveCollections (owner); + } + + level = selectedLevel; + emit showLevel (level); + view->setTitle (getTitle()); // Display new title. + view->updateCanvas(); // Show the edit area. + return (TRUE); +} + +void KGrGame::moveLevelFile () +{ + if (level <= 0) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must first load a level to be moved. Use " + "the %1 or %2 menu.") + .arg("\"" + i18n("Game") + "\"") + .arg("\"" + i18n("Editor") + "\"")); + return; + } + + int action = SL_MOVE; + + int fromC = collnIndex; + int fromL = level; + int toC = fromC; + int toL = fromL; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You cannot move a level until you " + "have created a game and at least two levels. Try " + "menu item \"Create Game\".")); + return; + } + + if (collections.at(fromC)->owner != USER) { + KGrMessage::information (view, i18n("Move Level"), + i18n("Sorry, you cannot move a system level.")); + return; + } + + // Pop up dialog box to get the collection and level number to move to. + while ((toC == fromC) && (toL == fromL)) { + toL = selectLevel (action, toL); + if (toL == 0) + return; + + toC = collnIndex; + + if ((toC == fromC) && (toL == fromL)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must change the level or the game or both.")); + } + } + + QDir dir; + QString filePath1; + QString filePath2; + + // Save the "fromN" file under a temporary name. + filePath1 = getFilePath (USER, collections.at(fromC), fromL); + filePath2 = filePath1; + filePath2 = filePath2.append (".tmp"); + dir.rename (filePath1, filePath2, TRUE); + + if (toC == fromC) { // Same collection. + if (toL < fromL) { // Decrease level. + // Move "toL" to "fromL - 1" up by 1. + if (! reNumberLevels (toC, toL, fromL-1, +1)) { + return; + } + } + else { // Increase level. + // Move "fromL + 1" to "toL" down by 1. + if (! reNumberLevels (toC, fromL+1, toL, -1)) { + return; + } + } + } + else { // Different collection. + // In "fromC", move "fromL + 1" to "nLevels" down and update "nLevels". + if (! reNumberLevels (fromC, fromL + 1, + collections.at(fromC)->nLevels, -1)) { + return; + } + collections.at(fromC)->nLevels--; + + // In "toC", move "toL + 1" to "nLevels" up and update "nLevels". + if (! reNumberLevels (toC, toL, collections.at(toC)->nLevels, +1)) { + return; + } + collections.at(toC)->nLevels++; + + saveCollections (USER); + } + + // Rename the saved "fromL" file to become "toL". + filePath1 = getFilePath (USER, collections.at(toC), toL); + dir.rename (filePath2, filePath1, TRUE); + + level = toL; + collection = collections.at(toC); + view->setTitle (getTitle()); // Re-write title. + view->updateCanvas(); // Re-display details of level. + emit showLevel (level); +} + +void KGrGame::deleteLevelFile () +{ + int action = SL_DELETE; + int lev = level; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Delete Level"), + i18n("You cannot delete a level until you " + "have created a game and a level. Try " + "menu item \"Create Game\".")); + return; + } + + // Pop up dialog box to get the collection and level number. + lev = selectLevel (action, level); + if (lev == 0) + return; + + QString filePath; + + // Set the name of the file to be deleted. + int n = collnIndex; + filePath = getFilePath (USER, collections.at(n), lev); + QFile levelFile (filePath); + + // Delete the file for the selected collection and level. + if (levelFile.exists()) { + if (lev < collections.at(n)->nLevels) { + switch (KGrMessage::warning (view, i18n("Delete Level"), + i18n("Do you want to delete a level and " + "move higher levels down by one?"), + i18n("&Delete Level"), i18n("&Cancel"))) { + case 0: break; + case 1: return; break; + } + levelFile.remove (); + if (! reNumberLevels (n, lev + 1, collections.at(n)->nLevels, -1)) { + return; + } + } + else { + levelFile.remove (); + } + } + else { + KGrMessage::information (view, i18n("Delete Level"), + i18n("Cannot find file '%1' to be deleted.").arg(filePath)); + return; + } + + collections.at(n)->nLevels--; + saveCollections (USER); + if (lev <= collections.at(n)->nLevels) { + level = lev; + } + else { + level = collections.at(n)->nLevels; + } + + // Repaint the screen with the level that now has the selected number. + if (editMode && (level > 0)) { + loadEditLevel (level); // Load level in edit mode. + } + else if (level > 0) { + enemyCount = 0; // Load level in play mode. + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; + } + else { + createLevel(); // No levels left in collection. + } + emit showLevel (level); +} + +void KGrGame::editCollection (int action) +{ + int lev = level; + int n = -1; + + // If editing, choose a collection. + if (action == SL_UPD_GAME) { + lev = selectLevel (SL_UPD_GAME, level); + if (lev == 0) + return; + level = lev; + n = collnIndex; + } + + KGrECDialog * ec = new KGrECDialog (action, n, collections, + view, "editGameDialog"); + + while (ec->exec() == QDialog::Accepted) { // Loop until valid. + + // Validate the collection details. + QString ecName = ec->getName(); + int len = ecName.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a name for the game.")); + continue; + } + + QString ecPrefix = ec->getPrefix(); + if ((action == SL_CR_GAME) || (collections.at(n)->nLevels <= 0)) { + // The filename prefix could have been entered, so validate it. + len = ecPrefix.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a filename prefix for the game.")); + continue; + } + if (len > 5) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should not " + "be more than 5 characters.")); + continue; + } + + bool allAlpha = TRUE; + for (int i = 0; i < len; i++) { + if (! isalpha(ecPrefix.myChar(i))) { + allAlpha = FALSE; + break; + } + } + if (! allAlpha) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should be " + "all alphabetic characters.")); + continue; + } + + bool duplicatePrefix = FALSE; + KGrCollection * c; + int imax = collections.count(); + for (int i = 0; i < imax; i++) { + c = collections.at(i); + if ((c->prefix == ecPrefix) && (i != n)) { + duplicatePrefix = TRUE; + break; + } + } + + if (duplicatePrefix) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix '%1' is already in use.") + .arg(ecPrefix)); + continue; + } + } + + // Save the collection details. + char settings = 'K'; + if (ec->isTrad()) { + settings = 'T'; + } + if (action == SL_CR_GAME) { + collections.append (new KGrCollection (USER, + ecName, ecPrefix, settings, 0, ec->getAboutText())); + } + else { + collection->name = ecName; + collection->prefix = ecPrefix; + collection->settings = settings; + collection->about = ec->getAboutText(); + } + + saveCollections (USER); + break; // All done now. + } + + delete ec; +} + +/******************************************************************************/ +/********************* SUPPORTING GAME EDITOR FUNCTIONS *********************/ +/******************************************************************************/ + +bool KGrGame::saveOK (bool exiting) +{ + int i, j; + bool result; + QString option2 = i18n("&Go on editing"); + + result = TRUE; + + if (editMode) { + if (exiting) { // If window is closing, + option2 = ""; // can't go on editing. + } + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { // Check cell changes. + if ((shouldSave) || (editObjArray[i][j] != lastSaveArray[i][j])) { + // If shouldSave == TRUE, level name or hint was edited. + switch (KGrMessage::warning (view, i18n("Editor"), + i18n("You have not saved your work. Do " + "you want to save it now?"), + i18n("&Save"), i18n("&Don't Save"), option2)) { + case 0: result = saveLevelFile(); break;// Save and continue. + case 1: shouldSave = FALSE; break; // Continue: don't save. + case 2: result = FALSE; break; // Go back to editing. + } + return (result); + } + } + } + return (result); +} + +void KGrGame::initEdit() +{ + if (! editMode) { + + editMode = TRUE; + emit setEditMenu (TRUE); // Enable edit menu items and toolbar. + + // We were previously in play mode: stop the hero running or falling. + hero->init (1, 1); + view->setHeroVisible (FALSE); + } + + paintEditObj = FALSE; + + // Set the default object and button. + editObj = BRICK; + emit defaultEditObj(); // Set default edit-toolbar button. + + oldI = 0; + oldJ = 0; + heroCount = 0; + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + nuggets = 0; + + emit showLevel (level); + emit showLives (0); + emit showScore (0); + + deleteLevel(); + setBlankLevel(FALSE); // Fill playfield with Editable objects. + + view->setTitle (getTitle());// Show title of level. + view->updateCanvas(); // Show the edit area. + + shouldSave = FALSE; // Used to flag editing of name or hint. +} + +void KGrGame::deleteLevel() +{ + int i,j; + for (i = 1; i <= FIELDHEIGHT; i++) + for (j = 1; j <= FIELDWIDTH; j++) + delete playfield[j][i]; +} + +void KGrGame::insertEditObj (int i, int j) +{ + if ((i < 1) || (j < 1) || (i > FIELDWIDTH) || (j > FIELDHEIGHT)) + return; // Do nothing: mouse pointer is out of playfield. + + if (editObjArray[i][j] == HERO) { + // The hero is in this cell: remove him. + editObjArray[i][j] = FREE; + heroCount = 0; + } + + if (editObj == HERO) { + if (heroCount != 0) { + // Can only have one hero: remove him from his previous position. + for (int m = 1; m <= FIELDWIDTH; m++) + for (int n = 1; n <= FIELDHEIGHT; n++) { + if (editObjArray[m][n] == HERO) { + setEditableCell (m, n, FREE); + } + } + } + heroCount = 1; + } + + setEditableCell (i, j, editObj); +} + +void KGrGame::setEditableCell (int i, int j, char type) +{ + ((KGrEditable *) playfield[i][j])->setType (type); + view->paintCell (i, j, type); + editObjArray[i][j] = type; +} + +void KGrGame::showEditLevel() +{ + // Disconnect play-mode slots from signals from "view". + disconnect (view, SIGNAL(mouseClick(int)), 0, 0); + disconnect (view, SIGNAL(mouseLetGo(int)), 0, 0); + + // Connect edit-mode slots to signals from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doEdit(int))); + connect (view, SIGNAL(mouseLetGo(int)), SLOT(endEdit(int))); +} + +bool KGrGame::reNumberLevels (int cIndex, int first, int last, int inc) +{ + int i, n, step; + QDir dir; + QString file1, file2; + + if (inc > 0) { + i = last; + n = first - 1; + step = -1; + } + else { + i = first; + n = last + 1; + step = +1; + } + + while (i != n) { + file1 = getFilePath (USER, collections.at(cIndex), i); + file2 = getFilePath (USER, collections.at(cIndex), i - step); + if (! dir.rename (file1, file2, TRUE)) { // Allow absolute paths. + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot rename file '%1' to '%2'.") + .arg(file1).arg(file2)); + return (FALSE); + } + i = i + step; + } + + return (TRUE); +} + +void KGrGame::setLevel (int lev) +{ + level = lev; + return; +} + +/******************************************************************************/ +/********************* EDIT ACTION SLOTS **********************************/ +/******************************************************************************/ + +void KGrGame::doEdit (int button) +{ + // Mouse button down: start making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = TRUE; + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + break; + default: + break; + } +} + +void KGrGame::endEdit (int button) +{ + // Mouse button released: finish making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = FALSE; + if ((i != oldI) || (j != oldJ)) { + insertEditObj (i, j); + view->updateCanvas(); + } + break; + default: + break; + } +} + +/******************************************************************************/ +/********************** LEVEL SELECTION DIALOG BOX **********************/ +/******************************************************************************/ + +int KGrGame::selectLevel (int action, int requestedLevel) +{ + int selectedLevel = 0; // 0 = no selection (Cancel) or invalid. + + // Halt the game during the dialog. + modalFreeze = FALSE; + if (! KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + // Create and run a modal dialog box to select a game and level. + KGrSLDialog * sl = new KGrSLDialog (action, requestedLevel, collnIndex, + collections, this, view, "levelDialog"); + while (sl->exec() == QDialog::Accepted) { + selectedGame = sl->selectedGame(); + selectedLevel = 0; // In case the selection is invalid. + if (collections.at(selectedGame)->owner == SYSTEM) { + switch (action) { + case SL_CREATE: // Can save only in a USER collection. + case SL_SAVE: + case SL_MOVE: + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only save or move " + "into one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_DELETE: // Can delete only in a USER collection. + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only delete a level " + "from one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_UPD_GAME: // Can edit info only in a USER collection. + KGrMessage::information (view, i18n("Edit Game Info"), + i18n("Sorry, you can only edit the game " + "information on your own games.")); + continue; // Re-run the dialog box. + break; + default: + break; + } + } + + selectedLevel = sl->selectedLevel(); + if ((selectedLevel > collections.at (selectedGame)->nLevels) && + (action != SL_CREATE) && (action != SL_SAVE) && + (action != SL_MOVE) && (action != SL_UPD_GAME)) { + KGrMessage::information (view, i18n("Select Level"), + i18n("There is no level %1 in %2, " + "so you cannot play or edit it.") + .arg(selectedLevel) + .arg("\"" + collections.at(selectedGame)->name + "\"")); + selectedLevel = 0; // Set an invalid selection. + continue; // Re-run the dialog box. + } + + // If "OK", set the results. + collection = collections.at (selectedGame); + owner = collection->owner; + collnIndex = selectedGame; + // Set default rules for selected game. + emit markRuleType (collection->settings); + break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete sl; + return (selectedLevel); // 0 = cancelled or invalid. +} + +bool KGrGame::ownerOK (Owner o) +{ + // Check that this owner has at least one collection. + KGrCollection * c; + bool OK = FALSE; + + for (c = collections.first(); c != 0; c = collections.next()) { + if (c->owner == o) { + OK = TRUE; + break; + } + } + + return (OK); +} + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +KGrThumbNail::KGrThumbNail (QWidget * parent, const char * name) + : QFrame (parent, name) +{ + // Let the parent do all the work. We need a class here so that + // QFrame::drawContents (QPainter *) can be re-implemented and + // the thumbnail can be automatically re-painted when required. +} + +QColor KGrThumbNail::backgroundColor = QColor ("#dddddd"); +QColor KGrThumbNail::brickColor = QColor ("#ff0000"); +QColor KGrThumbNail::ladderColor = QColor ("#ddcc00"); +QColor KGrThumbNail::poleColor = QColor ("#aa7700"); + +void KGrThumbNail::setFilePath (QString & fp, QLabel * sln) +{ + filePath = fp; // Keep safe copies of file + lName = sln; // path and level name field. +} + +void KGrThumbNail::drawContents (QPainter * p) // Activated via "paintEvent". +{ + QFile openFile; + QPen pen = p->pen(); + char obj = FREE; + int fw = 1; // Set frame width. + int n = width() / FIELDWIDTH; // Set thumbnail cell-size. + + pen.setColor (backgroundColor); + p->setPen (pen); + + openFile.setName (filePath); + if ((! openFile.exists()) || (! openFile.open (IO_ReadOnly))) { + // There is no file, so fill the thumbnail with "FREE" cells. + p->drawRect (QRect(fw, fw, FIELDWIDTH*n, FIELDHEIGHT*n)); + return; + } + + for (int j = 0; j < FIELDHEIGHT; j++) + for (int i = 0; i < FIELDWIDTH; i++) { + + obj = openFile.getch(); + + // Set the colour of each object. + switch (obj) { + case BRICK: + case BETON: + case FBRICK: + pen.setColor (brickColor); p->setPen (pen); break; + case LADDER: + pen.setColor (ladderColor); p->setPen (pen); break; + case POLE: + pen.setColor (poleColor); p->setPen (pen); break; + case HERO: + pen.setColor (green); p->setPen (pen); break; + case ENEMY: + pen.setColor (blue); p->setPen (pen); break; + default: + // Set the background for FREE, HLADDER and NUGGET. + pen.setColor (backgroundColor); p->setPen (pen); break; + } + + // Draw nxn pixels as n lines of length n. + p->drawLine (i*n+fw, j*n+fw, i*n+(n-1)+fw, j*n+fw); + if (obj == POLE) { + // For a pole, only the top line is drawn in white. + pen.setColor (backgroundColor); + p->setPen (pen); + } + for (int k = 1; k < n; k++) { + p->drawLine (i*n+fw, j*n+k+fw, i*n+(n-1)+fw, j*n+k+fw); + } + + // For a nugget, add just a vertical touch of yellow (2-3 pixels). + if (obj == NUGGET) { + int k = (n/2)+fw; + // pen.setColor (QColor("#ffff00")); + pen.setColor (ladderColor); + p->setPen (pen); + p->drawLine (i*n+k, j*n+k, i*n+k, j*n+(n-1)+fw); + p->drawLine (i*n+k+1, j*n+k, i*n+k+1, j*n+(n-1)+fw); + } + } + + // Absorb a newline character, then read in the level name (if any). + int c = openFile.getch(); + QCString s = ""; + while ((c = openFile.getch()) != EOF) { + if (c == '\n') // Level name is on one line. + break; + s += (char) c; + } + if (s.length() > 0) // If there is a name, translate it. + lName->setText (i18n((const char *) s)); + else + lName->setText (""); + + openFile.close(); +} + +/******************************************************************************/ +/************************* COLLECTIONS HANDLING ***************************/ +/******************************************************************************/ + +// NOTE: Macros "myStr" and "myChar", defined in "kgrgame.h", are used +// to smooth out differences between Qt 1 and Qt2 QString classes. + +bool KGrGame::initCollections () +{ + // Initialise the list of collections of levels (i.e. the list of games). + collections.setAutoDelete(TRUE); + owner = SYSTEM; // Use system levels initially. + if (! loadCollections (SYSTEM)) // Load system collections list. + return (FALSE); // If no collections, abort. + loadCollections (USER); // Load user collections list. + // If none, don't worry. + + mapCollections(); // Check ".grl" file integrity. + + // Set the default collection (first one in the SYSTEM "games.dat" file). + collnIndex = 0; + collection = collections.at (collnIndex); + level = 1; // Default start is at level 1. + + return (TRUE); +} + +void KGrGame::mapCollections() +{ + QDir d; + KGrCollection * colln; + QString d_path; + QString fileName1; + QString fileName2; + + // Find KGoldrunner level files, sorted by name (same as numerical order). + for (colln = collections.first(); colln != 0; colln = collections.next()) { + d.setPath ((colln->owner == SYSTEM) ? systemDataDir + "levels/" + : userDataDir + "levels/"); + d_path = d.path(); + if (! d.exists()) { + // There is no "levels" sub-directory: OK if game has no levels yet. + if (colln->nLevels > 0) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There is no folder '%1' to hold levels for" + " the '%2' game. Please make sure '%3' " + "has been run in the '%4' folder.") + .arg(d_path) + .arg(colln->name) + .arg("tar xf levels.tar") + .arg(systemDataDir)); + } + continue; + } + + const QFileInfoList * files = d.entryInfoList + (colln->prefix + "???.grl", QDir::Files, QDir::Name); + QFileInfoListIterator i (* files); + QFileInfo * file; + + if ((files->count() <= 0) && (colln->nLevels > 0)) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There are no files '%1/%2???.grl' for the %3 game.") + .arg(d_path) + .arg(colln->prefix) + .arg("\"" + colln->name + "\"")); + continue; + } + + // If the prefix is "level", the first file is the "ENDE" screen. + int lev = (colln->prefix == "level") ? 0 : 1; + + while ((file = i.current())) { + // Get the name of the file found on disk. + fileName1 = file->fileName(); + + while (TRUE) { + // Work out what the file name should be, based on the level no. + fileName2.setNum (lev); // Convert to QString. + fileName2 = fileName2.rightJustify (3,'0'); // Add zeros. + fileName2.append (".grl"); // Add level-suffix. + fileName2.prepend (colln->prefix); // Add colln. prefix. + + if (lev > colln->nLevels) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is beyond the highest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else if (fileName1 == fileName2) { + lev++; + break; + } + else if (fileName1.myStr() < fileName2.myStr()) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is before the lowest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("Cannot find file '%1' for the %2 game.") + .arg(fileName2) + .arg("\"" + colln->name + "\"")); + lev++; + } + } + ++i; // Go to next file info entry. + } + } +} + +bool KGrGame::loadCollections (Owner o) +{ + QString filePath; + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + if (! c.exists()) { + // If the user has not yet created a collection, don't worry. + if (o == SYSTEM) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot find game info file '%1'.") + .arg(filePath)); + } + return (FALSE); + } + + if (! c.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + QCString line = ""; + QCString name = ""; + QString prefix = ""; + char settings = ' '; + int nLevels = -1; + + int ch = 0; + while (ch >= 0) { + ch = c.getch(); + if (((char) ch != '\n') && (ch >= 0)) { + // If not end-of-line and not end-of-file, add to the line. + if (ch == '\r') {line += '\n';} + else if (ch == '\\') {ch = c.getch(); line += '\n';} + else {line += (char) ch;} + } + else { + // If first character is a digit, we have a new collection. + if (isdigit(line[0])) { + if (nLevels >= 0) { + // If previous collection with no "about" exists, load it. + collections.append (new KGrCollection + (o, name, prefix, settings, nLevels, "")); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + // Decode the first (maybe the only) line in the new collection. + line = line.simplifyWhiteSpace(); + int i, j, len; + len = line.length(); + i = 0; j = line.find(' ',i); nLevels = line.left(j).toInt(); + i = j+1; j = line.find(' ',i); settings = line[i]; + i = j+1; j = line.find(' ',i); prefix = line.mid(i,j-i); + i = j+1; name = line.right(len-i); + } + // If first character is not a digit, the line should be an "about". + else if (nLevels >= 0) { + collections.append (new KGrCollection + (o, i18n((const char *) name), // Translate now. + prefix, settings, nLevels, + QString::fromUtf8((const char *) line))); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + else if (ch >= 0) { + // Not EOF: it's an empty line or out-of-context "about" line. + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Format error in game info file '%1'.") + .arg(filePath)); + c.close(); + return (FALSE); + } + line = ""; + } + } + + c.close(); + return (TRUE); +} + +bool KGrGame::saveCollections (Owner o) +{ + QString filePath; + + if (o != USER) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You can only modify user games.")); + return (FALSE); + } + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + // Open the output file. + if (! c.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the collections. + KGrCollection * colln; + QCString line; + int i, len; + char ch; + + for (colln = collections.first(); colln != 0; colln = collections.next()) { + if (colln->owner == o) { + line.sprintf ("%03d %c %s %s\n", colln->nLevels, colln->settings, + colln->prefix.myStr(), + (const char *) colln->name.utf8()); + len = line.length(); + for (i = 0; i < len; i++) + c.putch (line[i]); + + len = colln->about.length(); + if (len > 0) { + QCString aboutC = colln->about.utf8(); + len = aboutC.length(); // Might be longer now. + for (i = 0; i < len; i++) { + ch = aboutC[i]; + if (ch != '\n') { + c.putch (ch); // Copy the character. + } + else { + c.putch ('\\'); // Change newline to \ and n. + c.putch ('n'); + } + } + c.putch ('\n'); // Add a real newline. + } + } + } + + c.close(); + return (TRUE); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrGame::myMessage (QWidget * parent, QString title, QString contents) +{ + // Halt the game while the message is displayed. + setMessageFreeze (TRUE); + + KGrMessage::wrapped (parent, title, contents); + + // Unfreeze the game, but only if it was previously unfrozen. + setMessageFreeze (FALSE); +} + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +KGrCollection::KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a) +{ + // Holds information about a collection of KGoldrunner levels (i.e. a game). + owner = o; name = n; prefix = p; settings = s; nLevels = nl; about = a; +} + +#include "kgrgame.moc" diff --git a/kgoldrunner/src/kgrgame.h b/kgoldrunner/src/kgrgame.h new file mode 100644 index 00000000..86144ef6 --- /dev/null +++ b/kgoldrunner/src/kgrgame.h @@ -0,0 +1,309 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krüger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRGAME_H +#define KGRGAME_H + +// Macros to smooth out the differences between Qt 1 and Qt 2 classes. +// +// "myStr" converts a QString object to a C language "char*" character string. +// "myChar" extracts a C language character (type "char") from a QString object. +// "endData" checks for an end-of-file condition. +// +#define myStr latin1 +#define myChar(i) at((i)).latin1() +#define endData atEnd + +#include + +#ifdef QT3 +#include +#else +#include +#endif +#include + +#include +#include +#include +#include + +#include + +/** +Sets up games and levels in KGoldrunner and controls the play. + +@author Ian Wadham +*/ + +class KGrObject; +class KGrHero; +class KGrEnemy; +class KGrCollection; + +class KGrGame : public QObject +{ +Q_OBJECT +public: + KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir); + ~KGrGame(); + + bool initCollections(); + KGrHero * getHero(); + + int getLevel(); + + void startPlaying(); + + bool inMouseMode(); // True if the game is in mouse mode. + bool inEditMode(); // True if the game is in editor mode. + bool isLoading(); // True if a level is being loaded. + + bool saveOK (bool exiting); // Check if edits were saved. + + QString getTitle(); // Collection - Level NNN, Name. + + void setEditObj (char newEditObj); // Set object for editor to paint. + + QString getFilePath (Owner o, KGrCollection * colln, int lev); + +public slots: + void startLevelOne(); // Start any game from level 1. + void startAnyLevel(); // Start any game from any level. + void startNextLevel(); // Start next level of current game. + + void setMouseMode (bool on_off); // Set mouse OR keyboard control. + void startLevel (int startingAt, int requestedLevel); + void newGame (const int lev, const int gameIndex); + void startTutorial(); // Start tutorial game. + void showHint(); // Show hint for current level. + + void showHighScores(); // Show high scores for current game. + + void incScore(int); // Update the score. + void herosDead(); // Hero was caught or he quit (key Q). + void showHiddenLadders(); // Show hidden ladders (nuggets gone). + void goUpOneLevel(); // Hero completed the level. + void loseNugget(); // Nugget destroyed (not collected). + void heroAction (KBAction movement);// Move hero under keyboard control. + + void saveGame(); // Save game ID, score and level. + void loadGame(); // Re-load game, score and level. + +signals: + void showScore (long); // For main window to show the score. + void showLives (long); // For main window to show lives left. + void showLevel (int); // For main window to show the level. + + void hintAvailable (bool); // For main window to adjust menu text. + + void setEditMenu (bool); // Enable/Disable edit menu items. + void defaultEditObj(); // Set default edit-toolbar button. + + void markRuleType (char); // Mark KGoldrunner/Traditional rules. + void gameFreeze (bool); // Do visual feedback in the GUI. + +private slots: + void finalBreath (); // Hero is dead: re-start the level. + void readMousePos (); // Timed reading of mouse position. + void doDig (int button); // Dig when under mouse-button control. + +private: + void setBlankLevel (bool playable); + int loadLevel (int levelNo); + bool openLevelFile (int levelNo, QFile & openlevel); + void changeObject (unsigned char kind, int i, int j); + void createObject (KGrObject *o, char picType, int x, int y); + void setTimings (); + void initSearchMatrix(); + + void checkHighScore(); // Check if high score for current game. + + int selectLevel (int action, int requestedLevel); + int selectedGame; + + void restart(); // Kickstart the game action. + +/******************************************************************************/ +/************************** PLAYFIELD AND GAME DATA *************************/ +/******************************************************************************/ + +private: + KGrCanvas * view; // Where the game is displayed. + QString systemDataDir; // System games are stored here. + QString userDataDir; // User games are stored here. + + KGrObject * playfield[30][22]; // Array of playfield objects. + char editObjArray[30][22]; // Character-code equivalent. + char lastSaveArray[30][22]; // Copy for use in "saveOK()". + + int level; // Current play/edit level. + QString levelName; // Level name (optional). + QString levelHint; // Level hint (optional). + + long lives; // Lives remaining. + long score; // Current score. + long startScore; // Score at start of level. + + KGrHero * hero; // The HERO figure !! Yay !!! + int startI, startJ; // The hero's starting position. + +#ifdef QT3 + QPtrList enemies; // The list of enemies. +#else + QList enemies; // The list of enemies. +#endif + int enemyCount; // How many enemies. + KGrEnemy * enemy; // One of the enemies. + + int nuggets; // How many gold nuggets. + + bool newLevel; // Next level will be a new one. + bool loading; // Stop input until it's loaded. + + bool modalFreeze; // Stop game during dialog. + bool messageFreeze; // Stop game during message. + + QTimer * mouseSampler; // Timer for mouse tracking. + QTimer * dyingTimer; // For pause when the hero dies. + + int lgHighlight; // Row selected in "loadGame()". + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +public slots: + void doStep(); // Do one animation step. + void showFigurePositions(); // Show everybody's co-ordinates. + void showHeroState(); // Show hero's co-ordinates and state. + void showEnemyState (int); // Show enemy's co-ordinates and state. + void showObjectState(); // Show an object's state. + void bugFix(); // Turn a bug fix on/off dynamically. + void startLogging(); // Turn logging on/off. + +/******************************************************************************/ +/******************** GAME EDITOR PROPERTIES AND METHODS ********************/ +/******************************************************************************/ + +public slots: // Slots connected to the Menu and Edit Toolbar. + void createLevel(); // Set up a blank level-display for edit. + void updateLevel(); // Update an existing level. + void updateNext(); // Update the current level + 1. + void editNameAndHint(); // Run a dialog to edit the level name and hint. + bool saveLevelFile(); // Save the edited level in a text file (.grl). + void moveLevelFile(); // Move level to another collection or number. + void deleteLevelFile(); // Delete a level file. + + void editCollection (int action); + + void setLevel (int lev); // Set level to be edited. + + void freeze(); // Stop the gameplay action. + void unfreeze(); // Restart the gameplay action. + void setMessageFreeze (bool); + +private: + bool mouseMode; // Flag to set up keyboard OR mouse control. + bool editMode; // Flag to change keyboard and mouse functions. + char editObj; // Type of object to be painted by the mouse. + bool paintEditObj; // Sets painting on/off (toggled by clicking). + int oldI, oldJ; // Last mouse position painted. + int editLevel; // Level to be edited (= 0 for new level). + int heroCount; // Can enter at most one hero. + bool shouldSave; // True if name or hint was edited. + +private: + void loadEditLevel (int); // Load and display an existing level for edit. + void initEdit(); + void deleteLevel(); + void insertEditObj (int, int); + void setEditableCell (int, int, char); + void showEditLevel(); + bool reNumberLevels (int, int, int, int); + bool ownerOK (Owner o); + + // Pixmaps for repainting objects as they are edited. + QPixmap digpix[10]; + QPixmap brickbg, fbrickbg; + QPixmap freebg, nuggetbg, polebg, betonbg, ladderbg, hladderbg; + QPixmap edherobg, edenemybg; + +private slots: + void doEdit(int); // For mouse-click when in edit-mode. + void endEdit(int); // For mouse-release when in edit-mode. + +/******************************************************************************/ +/******************** COLLECTION PROPERTIES AND METHODS *******************/ +/******************************************************************************/ + +private: + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +#ifdef QT3 + QPtrList collections; // List of ALL collections. +#else + QList collections; // List of ALL collections. +#endif + + KGrCollection * collection; // Collection currently in use. + Owner owner; // Collection owner. + int collnIndex; // Index in collections list. + + void mapCollections(); + bool loadCollections (Owner); + bool saveCollections (Owner); + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + + void myMessage (QWidget * parent, QString title, QString contents); +}; + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +class KGrThumbNail : public QFrame +{ +public: + KGrThumbNail (QWidget *parent = 0, const char *name = 0); + void setFilePath (QString &, QLabel *); // Set filepath and name field. + + static QColor backgroundColor; + static QColor brickColor; + static QColor ladderColor; + static QColor poleColor; + +protected: + void drawContents (QPainter *); // Draw a preview of a level. + QString filePath; + QLabel * lName; +}; + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +class KGrCollection +{ +public: + KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a); + Owner owner; // Collection owner: "System" or "User". + QString name; // Collection name. + QString prefix; // Collection's filename prefix. + char settings; // Collection rules: KGoldrunner or Traditional. + int nLevels; // Number of levels in the collection. + QString about; // Optional text about the collection. +}; + +#endif diff --git a/kgoldrunner/src/kgrobject.cpp b/kgoldrunner/src/kgrobject.cpp new file mode 100644 index 00000000..948fd540 --- /dev/null +++ b/kgoldrunner/src/kgrobject.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + kgrobject.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrcanvas.h" + +#include "kgrobject.h" + +#include + +KGrObject::KGrObject (char objType) +{ + iamA = objType; + searchValue = 0; + blocker = FALSE; + if ((objType == BRICK) || (objType == BETON) || (objType == FBRICK)) { + blocker = TRUE; + } + xpos = 0; + ypos = 0; + objectView = NULL; +} + +bool KGrObject::frozen = FALSE; // Initialise game as running, not halted. +bool KGrObject::bugFixed = FALSE;// Initialise game with dynamic bug-fix OFF. +bool KGrObject::logging = FALSE;// Initialise game with log printing OFF. + +char KGrObject::whatIam () +{ + return iamA; +} + +void KGrObject::showState (int i, int j) +{ + printf("(%02d,%02d) - Object [%c] search %d", i, j, iamA, searchValue); + if (blocker) printf(" blocker"); + printf("\n"); +} + +KGrObject :: ~KGrObject () +{ +} + +KGrEditable::KGrEditable (char editType) : KGrObject (editType) +{ +} + +void KGrEditable::setType (char editType) +{ + iamA = editType; +} + +KGrEditable::~KGrEditable () +{ +} + +KGrFree::KGrFree (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + theRealMe = FREE; // Remember what we are, even "iamA == NUGGET". +} + +void KGrFree::setNugget(bool nug) +{ + // This code must work over a hidden ladder as well as a free cell. + if (! nug) { + iamA = theRealMe; + objectView->paintCell (xpos, ypos, FREE); + } + else { + iamA = NUGGET; + objectView->paintCell (xpos, ypos, NUGGET); + } +} + +KGrFree :: ~KGrFree () +{ +} + +/* +++++++++++++++ BRICK ++++++++++++++++ */ + +KGrBrick::KGrBrick (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + dig_counter = 0; + holeFrozen = FALSE; + iamA = BRICK; + timer = new QTimer (this); + connect (timer, SIGNAL (timeout ()), SLOT (timeDone ())); +} + +void KGrBrick::dig (void) +{ + dig_counter = 1; + hole_counter = HOLETIME; + iamA = HOLE; + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); +} + +void KGrBrick::doStep() { + if (holeFrozen) { + holeFrozen = FALSE; + timeDone(); + } +} + +void KGrBrick::showState (int i, int j) +{ + printf ("(%02d,%02d) - Brick [%c] search %d dig-counter %d", + i, j, iamA, searchValue, dig_counter); + if (blocker) + printf (" blocker"); + printf ("\n"); +} + +void KGrBrick::timeDone () +{ + if (KGrObject::frozen) {holeFrozen = TRUE; return;} + + // When the hole is complete, we need a longer delay. + if (dig_counter == 5) { + hole_counter--; + if (hole_counter > 0) { + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + return; + } + } + if (dig_counter < 9) { + dig_counter++; + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + if (dig_counter >= 8) + iamA = BRICK; + } + else + dig_counter = 0; + + // Brick pix:- 0 normal, 1-4 crumbling, 5 hole complete, 6-9 re-growing. + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); +} + +void KGrBrick::useHole() { + if (iamA == HOLE) + iamA = USEDHOLE; +} + +void KGrBrick::unUseHole() { + if (iamA == USEDHOLE) + iamA = HOLE; +} + +KGrBrick :: ~KGrBrick () +{ + delete timer; +} + +KGrHladder::KGrHladder (char objType, int i, int j, KGrCanvas * view) + : KGrFree (objType, i, j, view) + // Must inherit "setNugget()" from "KGrFree". +{ + theRealMe = HLADDER; // But remember we are a hidden ladder ... +} + +void KGrHladder::showLadder() +{ + iamA = LADDER; + objectView->paintCell (xpos, ypos, LADDER); +} + +KGrHladder :: ~KGrHladder () +{ +} + +#include "kgrobject.moc" diff --git a/kgoldrunner/src/kgrobject.h b/kgoldrunner/src/kgrobject.h new file mode 100644 index 00000000..a48eba88 --- /dev/null +++ b/kgoldrunner/src/kgrobject.h @@ -0,0 +1,107 @@ +/*************************************************************************** + kgrobject.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krüger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGROBJECT_H +#define KGROBJECT_H + +// Obsolete - #include +#include + +#include +#include // for random + +class KGrCanvas; + +class KGrObject : public QObject +{ + Q_OBJECT +public: + KGrObject (char objType); + virtual ~KGrObject(); + + // STATIC GLOBAL FLAGS. + static bool frozen; // Game play halted (use ESCAPE key). + static bool bugFixed; // Dynamic bug fix turned on (key B, if halted). + static bool logging; // Log printing turned on. + + char whatIam(); + int searchValue; + bool blocker; // Beton or Brick -> TRUE + void showState (int, int); + +protected: + KGrCanvas * objectView; + int xpos; + int ypos; + char iamA; +}; + +class KGrEditable : public KGrObject +{ + Q_OBJECT +public: + KGrEditable (char editType); + virtual ~KGrEditable (); + void setType (char); +}; + +class KGrFree : public KGrObject +{ Q_OBJECT +public: + KGrFree (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrFree(); + void setNugget(bool); + +protected: + char theRealMe; // Set to FREE or HLADDER, even when "iamA == NUGGET". +}; + +class KGrBrick : public KGrObject +{ + Q_OBJECT +public: + KGrBrick (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrBrick(); + void dig(void); + void useHole(); + void unUseHole(); + static int speed; // Digging & repair speed (copy of KGrFigure::speed). + static int HOLETIME; // Number of timing cycles for a hole to remain open. + void doStep(); + void showState (int, int); + +protected slots: + void timeDone(void); + +private: + int dig_counter; + int hole_counter; + bool holeFrozen; + QTimer *timer; +}; + +class KGrHladder : public KGrFree +{ + Q_OBJECT +public: + // BUG FIX - Ian W., 21/6/01 - must inherit "setNugget()" from "KGrFree". + KGrHladder (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrHladder(); + void showLadder(); +}; + +#endif // KGROBJECT_H diff --git a/kgoldrunner/src/main.cpp b/kgoldrunner/src/main.cpp new file mode 100644 index 00000000..63be6a71 --- /dev/null +++ b/kgoldrunner/src/main.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger + */ + +#include +#include +#include +#include +#include + +#include "kgrconsts.h" +#include "kgoldrunner.h" + +static const char description[] = + I18N_NOOP("KGoldrunner is a game of action and puzzle solving"); + +static const char version[] = "2.0"; + +int main (int argc, char **argv) +{ + KAboutData about("kgoldrunner", I18N_NOOP("KGoldrunner" ), + version, description, + KAboutData::License_GPL, + "(C) 2003 Ian Wadham and Marco Krüger"); + about.addAuthor( "Ian Wadham", I18N_NOOP("Current author"), + "ianw2@optusnet.com.au" ); + about.addAuthor( "Marco Krüger", I18N_NOOP("Original author"), 0); + + KCmdLineArgs::init (argc, argv, &about); + + KApplication app; + + // Register as a DCOP client. + app.dcopClient()->registerAs (app.name(), false); + + // See if we are starting with session management. + if (app.isRestored()) + { + RESTORE(KGoldrunner); + return app.exec(); + } + else + { + KGoldrunner * widget = new KGoldrunner; + if (widget->startedOK()) { + widget->show(); + return app.exec(); + } + } +} diff --git a/kjumpingcube/AUTHORS b/kjumpingcube/AUTHORS new file mode 100644 index 00000000..2dc6d21a --- /dev/null +++ b/kjumpingcube/AUTHORS @@ -0,0 +1,2 @@ +Matthias Kiefer +Benjamin Meyer diff --git a/kjumpingcube/ChangeLog b/kjumpingcube/ChangeLog new file mode 100644 index 00000000..f6b6f5cc --- /dev/null +++ b/kjumpingcube/ChangeLog @@ -0,0 +1,27 @@ +chagnes 1.1: +- Added Settings dialog +- finetuned the code +- removed a lot of unessessary code with newer qt3 code. + +changes 0.7.3: +- finetuning and some bugfixes + +changes 0.7.2: +- rewritten everything (just to learn better c++ ;-) +- implemented better and faster algorithm for the computerplayer +- added possibility to save/load games in/from any file. +- KJumpingCube is now fully KDE-compliant + +changes 0.7.0.1: +- rewrote parts of CubeBox and Cube, move is now managed from CubeBox +- added computerplayer +- added undo-function + +changes 0.6.1: +- after reseting cubes are drawn in the global configured backgroundcolor + +changes 0.6: +- added sessionmanagment +- added standardaccelerators +- added possibility to save and restore a game + diff --git a/kjumpingcube/Makefile.am b/kjumpingcube/Makefile.am new file mode 100644 index 00000000..fdb5a185 --- /dev/null +++ b/kjumpingcube/Makefile.am @@ -0,0 +1,23 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO +KDE_ICON = kjumpingcube + +bin_PROGRAMS = kjumpingcube +kjumpingcube_SOURCES = kjumpingcube.cpp kcubeboxwidget.cpp main.cpp \ + kcubewidget.cpp cube.cpp brain.cpp cubebox.cpp \ + settings.ui prefs.kcfgc + +kjumpingcube_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kjumpingcube_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) +kjumpingcube_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kjumpingcube.desktop +kde_kcfg_DATA = kjumpingcube.kcfg + +rcdir = $(kde_datadir)/kjumpingcube +rc_DATA = kjumpingcubeui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kjumpingcube.pot + diff --git a/kjumpingcube/README b/kjumpingcube/README new file mode 100644 index 00000000..52c9040f --- /dev/null +++ b/kjumpingcube/README @@ -0,0 +1,39 @@ +KJumpingCube +Matthias Kiefer +---------------------------------------------------------------------- + +KJumpingCube is a tactical one- or two-player game. +For further information read the online help-documentation. + +For installing instructions read INSTALL. + +I hope you will enjoy it. Have a lot of fun :-) + + +Don't hesitate to contact me if you find bugs or if you have +questions/suggestions on the implementation. + + +Thanks to Krzysztof P. Jasiutowicz for suggestions and moral support. + + + + Copyright (C) 1998,1999 by Matthias Kiefer + + KJumpingCube is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +-- +Matthias Kiefer +5.Apr 1999 diff --git a/kjumpingcube/brain.cpp b/kjumpingcube/brain.cpp new file mode 100644 index 00000000..e2d63815 --- /dev/null +++ b/kjumpingcube/brain.cpp @@ -0,0 +1,621 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ + +#include "brain.h" +#include "cube.h" + +#include + +#include + +#undef DEBUG // uncomment this to get useful messages +#include + +#ifdef DEBUG +#include +#endif + +#include "prefs.h" + +Brain::Brain(int initValue) +{ + setSkill(Prefs::EnumSkill::Beginner); + stopped=false; + active=false; + currentLevel=0; + + // initialize the random number generator + random.setSeed(initValue); +} + +void Brain::setSkill(int newSkill) +{ + _skill=newSkill; + + switch(_skill) + { + case Prefs::EnumSkill::Beginner: + maxLevel=1; + break; + case Prefs::EnumSkill::Average: + maxLevel=3; + break; + case Prefs::EnumSkill::Expert: + maxLevel=5; + break; + default: + break; + } +} + +int Brain::skill() const +{ + return _skill; +} + +void Brain::stop() +{ + stopped=true; +} + + +bool Brain::isActive() const +{ + return active; +} + + + +bool Brain::getHint(int& row, int& column,CubeBox::Player player ,CubeBox box) +{ + if(isActive()) + return false; + + active=true; + stopped=false; + currentPlayer=player; + + int i=0,j=0; + int moves=0; // how many moves are the favourable ones + CubeBox::Player opponent=(player==CubeBox::One)?CubeBox::Two : CubeBox::One; + + // if more than one cube has the same rating this array is used to select + // one + coordinate* c2m=new coordinate[box.dim()*box.dim()]; + + // Array, which holds the assessment of the separate moves + double **worth=new double*[box.dim()]; + for(i=0;iowner()!=(Cube::Owner)opponent) + { + if(worth[c2m[i].row][c2m[i].column]>max ) + { + max=worth[c2m[i].row][c2m[i].column]; + } + } + } + +#ifdef DEBUG + cerr << "found Maximum : " << max << endl; +#endif + + // found maximum more than one time ? + int counter=0; + for(i=0;iowner() != (Cube::Owner)opponent) + { + c2m[counter].row=c2m[i].row; + c2m[counter].column=c2m[i].column; + counter++; + } + } + + assert(counter>0); + + + // if some moves are equal, choose a random one + if(counter>1) + { + +#ifdef DEBUG + cerr << "choosing a random cube: " << endl ; +#endif + counter=random.getLong(counter); + } + + row=c2m[counter].row; + column=c2m[counter].column; +#ifdef DEBUG + cerr << "cube: " << row << "," << column << endl; +#endif + } + + // clean up + for(i=0;iprocessEvents(); + + // if thinking process stopped + if(stopped) + { + currentLevel--; + return 0; + } + + // simulate every possible move + worth+=doMove(c2m[i].row,c2m[i].column,player,box); + } + } + delete [] c2m; + currentLevel--; + return worth; + + } + else + { + // if maximum depth of recursive calls are reached, return the assessment + currentLevel--; + box.simulateMove(player,row,column); + + return box.assessField(currentPlayer); + } + +} + +int Brain::findCubes2Move(coordinate *c2m,CubeBox::Player player,CubeBox& box) +{ + int i,j; + int opponent=(player==CubeBox::One)? CubeBox::Two : CubeBox::One; + int moves=0; + int min=9999; + + if(_skill==Prefs::EnumSkill::Beginner) + { + int max=0; + for(i=0;iowner() != opponent) + { + c2m[moves].row=i; + c2m[moves].column=j; + c2m[moves].val=box[i][j]->value(); + + if(c2m[moves].val>max) + max=c2m[moves].val; + + moves++; + + } + } + + // find all moves with maximum value + int counter=0; + for(i=0;iowner() != opponent) + { + int val; + + // check neighbours of every cube + val=assessCube(i,j,player,box); + + +#ifdef DEBUG + if(currentLevel==0) + cerr << i << "," << j << " : " << val << endl; +#endif + // only if val >= 0 its a favourable move + if( val > 0 ) + { + if(valowner() != opponent) + { + c2m[moves].row=i; + c2m[moves].column=j; + c2m[moves].val=( box[i][j]->max() - box[i][j]->value() ); + if(c2m[moves].val maxMoves) + { + // find maxMoves random cubes to move with + coordinate* tempC2M=new coordinate[maxMoves]; + + coordinate tmp={-1,-1,0}; + for(i=0;imax()-box[row][column]->value() ); + + int val; + val=diff-temp+1; + val=val*(temp+1); + + return val; +} + + +int Brain::getDiff(int row,int column, CubeBox::Player player, CubeBox& box) const +{ + int diff; + + if(box[row][column]->owner() != (Cube::Owner)player) + { + diff=( box[row][column]->max() - box[row][column]->value() ); + } + else + { + diff=( box[row][column]->max() - box[row][column]->value()+1 ); + } + + return diff; +} + diff --git a/kjumpingcube/brain.h b/kjumpingcube/brain.h new file mode 100644 index 00000000..8f270705 --- /dev/null +++ b/kjumpingcube/brain.h @@ -0,0 +1,135 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef BRAIN_H +#define BRAIN_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "cubebox.h" + +/** @internal */ +struct coordinate +{ + int row; + int column; + + int val; +}; + + +/** +* Class Brain computes a (good) possibility to move +* for a given playingfield. +* +* It puts a value on every cube by looking at its neighbours +* and searches the best cubes to move. It then simulates what would +* happen, if you would click on these cubes. This is done recursively +* to a certain depth and the playingfield will be valued. +* +* @short The games brain +*/ +class Brain +{ +public: + /** + * @param initValue value to initialize the random number generator with + * if no value is given a truly random value is used + */ + Brain(int initValue=0); + + /** + * Computes a good possible move at the given field. + * The index of this Cube is stored in given 'row' and 'column' + * + * @return false if computing was stopped + * @see Brain#stop; + */ + bool getHint(int& row, int& column, CubeBox::Player player, CubeBox field); + + /** stops thinking */ + void stop(); + /** @return true if the Brain is thinking at the moment */ + bool isActive() const; + + /** skill according to Prefs::EnumSkill **/ + void setSkill(int); + int skill() const; + +private: + /** + * checks if a move is possible at cube row,column from player 'player' and + * simulates this move. Then it checks the new playingfield for possible moves + * and calls itself for every possible move until the maximum depth 'maxLevel' + * is reached. + * + * If the maximum depth is reached, it puts a value on the playingfield and returns this. + * @see CubeBox#simulateMove + * @see CubeBox#assessField + * @see Brain#findCubes2Move + * + * @param row,column coordinates of cube to increase + * @param player for which player the cube has to be increased + * @param box playingfield to do the moves on + * @return the value put on the field + */ + double doMove(int row,int column,CubeBox::Player player, CubeBox box); + /** + * Checks the given playingfield, which cubes are favourable to do a move + * by checking every cubes neighbours. And looking for the difference to overflow. + * + * @param c2m Array in which the coordinates of the best cubes to move will be stored + * @param player for which player to check + * @param box playingfield to check + * @param debug if debugmessages should be printed + * @return number of found cubes to move + */ + int findCubes2Move(coordinate* c2m,CubeBox::Player player,CubeBox& box); + /** + * + */ + int assessCube(int row,int column,CubeBox::Player,CubeBox& box) const; + int getDiff(int row,int column, CubeBox::Player player, CubeBox& box) const; + + /** current depth of recursive simulating of the moves */ + int currentLevel; + /** maximum depth of recursive thinking */ + int maxLevel; + /** the player for which to check the moves */ + CubeBox::Player currentPlayer; + + + /** flag, if the engine has to be stopped */ + bool stopped; + /** flag, if the engine is active */ + bool active; + /** skill of the Brain, see Prefs::EnumSkill */ + int _skill; + + /** Sequence generator */ + KRandomSequence random; +}; + +#endif //BRAIN_H diff --git a/kjumpingcube/cube.cpp b/kjumpingcube/cube.cpp new file mode 100644 index 00000000..59e2dc58 --- /dev/null +++ b/kjumpingcube/cube.cpp @@ -0,0 +1,99 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "cube.h" +#include + +/* ****************************************************** ** +** Class Cube ** +** ****************************************************** */ + +Cube::Cube(Owner owner,int value,int maximum) +{ + _owner = owner; + _value = value; + _max = maximum; +} + + +Cube::Owner Cube::setOwner(Owner owner) +{ + Owner old=_owner; + _owner=owner; + + return old; +} + +void Cube::setValue(int value) +{ +#ifdef DEBUG + assert(value>0); +#endif + + _value = (value<1)? 1 : value; +} + + +void Cube::setMax(int max) +{ +#ifdef DEBUG + assert(max>1); +#endif + + _max = (max<2)? 2 : max; +} + + +void Cube::decrease() +{ + setValue(_value-_max); +} + +Cube::Owner Cube::owner() const +{ + return _owner; +} + + +int Cube::value() const +{ + return _value; +} + +bool Cube::increase(Owner newOwner) +{ + setValue(value()+1); + setOwner(newOwner); + + return (_value > _max); +} + +int Cube::max() const +{ + return _max; +} + + +bool Cube::overMax() const +{ + return (_value > _max); +} + diff --git a/kjumpingcube/cube.h b/kjumpingcube/cube.h new file mode 100644 index 00000000..9a58c02f --- /dev/null +++ b/kjumpingcube/cube.h @@ -0,0 +1,99 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBE_H +#define CUBE_H + + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +* This Class is the internal representation of a cube. +*/ +class Cube +{ +public: + enum Owner{Nobody=0,One=1,Two=2}; + + /** + * constructs a Cube + */ + Cube(Owner owner=Nobody,int value=1,int max=4); + + + /** + * changes owner of the Cube + * @return old Owner + */ + virtual Owner setOwner(Owner owner); + + /** + * changes value of the Cube + */ + virtual void setValue(int value); + + /** + * sets maximum value of the Cube + */ + virtual void setMax(int max); + + /** + * increase the value of the Cube and set the owner of the Cube + * to 'newOwner'. + * @return true if the Cube's new value is over maximum + */ + virtual bool increase(Owner newOwner); + + /** + * substracts the maximum from the Cube's value + */ + virtual void decrease(); + + /** + * returns current owner + */ + Owner owner() const; + /** + * returns current value + */ + int value() const; + /** + * returns the maximum value of the cube + */ + int max() const; + + /** + * checks if the Cube's value is over maximum + */ + bool overMax() const; + +private: + + Owner _owner; + int _value; + int _max; + +}; + + +#endif diff --git a/kjumpingcube/cubebox.cpp b/kjumpingcube/cubebox.cpp new file mode 100644 index 00000000..91a90cc6 --- /dev/null +++ b/kjumpingcube/cubebox.cpp @@ -0,0 +1,292 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include +#include +#include "cubebox.h" +#include "kcubeboxwidget.h" + + +CubeBox::CubeBox(const int d) + :CubeBoxBase(d) +{ + initCubes(); +} + +CubeBox::CubeBox(const CubeBox& box) + :CubeBoxBase(box.dim()) +{ + initCubes(); + + int i,j; + for(i=0;i(box.dim()) +{ + initCubes(); + + int i,j; + for(i=0;iowner()!=(Cube::Owner)fromWhom && cubes[row][column]->owner()!=Cube::Nobody) + return false; + + cubes[row][column]->increase((Cube::Owner)fromWhom); + + do + { + int i,j; + finished=true; + playerWon=true; + + // check all Cubes + for(i=0;ioverMax()) + { + increaseNeighbours(fromWhom,i,j); + cubes[i][j]->decrease(); + finished=false; + } + + if(cubes[i][j]->owner()!=(Cube::Owner)fromWhom) + playerWon=false; + } + } + + if(playerWon) + return true; + } + while(!finished); + + + return true; +} + +double CubeBox::assessField(Player player) const +{ + int cubesOne=0; + int cubesTwo=0; + int pointsOne=0; + int pointsTwo=0; + Player otherPlayer = ((player==One)? Two : One); + bool playerWon=true; + bool otherPlayerWon=true; + + int i,j; + + for(i=0;iowner()==(Cube::Owner)One) + { + cubesOne++; + pointsOne+=(int)pow((float)cubes[i][j]->value(),2); + } + else if(cubes[i][j]->owner()==(Cube::Owner)Two) + { + cubesTwo++; + pointsTwo+=(int)pow((float)cubes[i][j]->value(),2); + } + + if(cubes[i][j]->owner()!=(Cube::Owner)player) + playerWon=false; + + if(cubes[i][j]->owner()!=(Cube::Owner)otherPlayer) + otherPlayerWon=false; + } + + } + + + + if(player==One) + { + return (int)pow((float)cubesOne,2)+pointsOne-(int)pow(cubesTwo,2)-pointsTwo; + } + else + return (int)pow((float)cubesTwo,2)+pointsTwo-(int)pow(cubesOne,2)-pointsOne; + +} + +bool CubeBox::playerWon(Player who) const +{ + int i,j; + + for(i=0;iowner()!=(Cube::Owner)who) + return false; + } + + return true; +} + + +void CubeBox::increaseNeighbours(CubeBox::Player forWhom,int row,int column) +{ + Cube::Owner _player = (Cube::Owner)(forWhom); + + if(row==0) + { + if(column==0) // top left corner + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // top right corner + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // top edge + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // left bottom corner + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // right bottom corner + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // bottom edge + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // left edge + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // right edge + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} diff --git a/kjumpingcube/cubebox.h b/kjumpingcube/cubebox.h new file mode 100644 index 00000000..d1236410 --- /dev/null +++ b/kjumpingcube/cubebox.h @@ -0,0 +1,62 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBEBOX_H +#define CUBEBOX_H + +#include "cubeboxbase.h" + +class Cube; +class KCubeBoxWidget; + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +* Class for storing information about the playingfield, e.g. +* to undo a move or computing the next move +*/ +class CubeBox : public CubeBoxBase +{ +public: + /** + * constructs a CubeBox with 'dim' x 'dim' Cubes + */ + CubeBox(const int dim=1); + CubeBox(const CubeBox&); + CubeBox(KCubeBoxWidget&); + virtual ~CubeBox(); + + CubeBox& operator= (const CubeBox& box); + CubeBox& operator= (KCubeBoxWidget& box); + + bool simulateMove(Player fromWhom,int row, int column); + double assessField(Player forWhom) const; + bool playerWon(Player who) const; + +private: + void increaseNeighbours(CubeBox::Player forWhom,int row,int column); + +}; + +#endif // CUBEBOX_H + diff --git a/kjumpingcube/cubeboxbase.h b/kjumpingcube/cubeboxbase.h new file mode 100644 index 00000000..63bc0529 --- /dev/null +++ b/kjumpingcube/cubeboxbase.h @@ -0,0 +1,246 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef CUBEBOXBASE_H +#define CUBEBOXBASE_H + +#ifdef DEBUG +#include +#endif + +template +class CubeBoxBase +{ +public: + enum Player{One=1,Two=2}; + + CubeBoxBase(const int dim=1); + virtual ~CubeBoxBase(); + + T** operator[](const int index); + /** + * sets number of Cubes in a row/column to 'size'. + */ + virtual void setDim(int dim); + + /** + * returns number of Cubes in a row/column + */ + inline int dim() const { return _dim; } + + inline Player player() const { return currentPlayer; } + +protected: + virtual void deleteCubes(); + virtual void initCubes(); + + /** increases the neighbours of cube at ['row','column'] */ + //void increaseNeighbours(int forWhom,int row,int column); + + T*** cubes; + Player currentPlayer; + +private: + int _dim; + +}; + +template +CubeBoxBase::CubeBoxBase(const int dim) +{ +#ifdef DEBUG + assert(dim>0); +#endif + + _dim=dim; + currentPlayer=One; +} + +template +CubeBoxBase::~CubeBoxBase() +{ + if(cubes) + deleteCubes(); +} + +template +T** CubeBoxBase::operator[](const int index) +{ +#ifdef DEBUG + assert(index >= 0); +#endif + + return cubes[index]; +} + +template +void CubeBoxBase::setDim(int d) +{ + if(d != _dim) + { + deleteCubes(); + + _dim=d; + + initCubes(); + } +} + + +template +void CubeBoxBase::initCubes() +{ + const int s=dim(); + + int i,j; + // create new cubes + cubes = new T**[s]; + for(i=0;isetMax(2); + cubes[0][max]->setMax(2); + cubes[max][0]->setMax(2); + cubes[max][max]->setMax(2); + + for(i=0;i<=max;i++) + { + cubes[i][0]->setMax(3); + cubes[i][max]->setMax(3); + cubes[0][i]->setMax(3); + cubes[max][i]->setMax(3); + } + + for(i=1;isetMax(4); + } +} + +template +void CubeBoxBase::deleteCubes() +{ + int i,j; + for(i=0;i +void CubeBoxBase::increaseNeighbours(int forWhom,int row,int column) +{ + int _player = (T::Owner)(forWhom); + + if(row==0) + { + if(column==0) // linke obere Ecke + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // rechte obere Ecke + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // oberer Rand + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // linke untere Ecke + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // rechte untere Ecke + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // unterer Rand + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // linker Rand + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // rechter Rand + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} +*/ + +#endif // CUBEBOXBASE_H + diff --git a/kjumpingcube/hi128-app-kjumpingcube.png b/kjumpingcube/hi128-app-kjumpingcube.png new file mode 100644 index 0000000000000000000000000000000000000000..30bf2fa23a86610fe918ca163c9fbc31703f1cd4 GIT binary patch literal 9130 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 WhwXz~W)D{Y00007t literal 0 HcmV?d00001 diff --git a/kjumpingcube/hi22-app-kjumpingcube.png b/kjumpingcube/hi22-app-kjumpingcube.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8608223561417d5a3efc91052a7c2da800afe GIT binary patch literal 1225 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 R=m7u#002ovPDHLkV1m`uG(i9W literal 0 HcmV?d00001 diff --git a/kjumpingcube/hi48-app-kjumpingcube.png b/kjumpingcube/hi48-app-kjumpingcube.png new file mode 100644 index 0000000000000000000000000000000000000000..7bcd613d12c0bcff329c96a51b1b9f0be7dd9007 GIT binary patch literal 3034 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 b7LWf00JH7#Eoiir00000NkvXXu0mjfhazLZ literal 0 HcmV?d00001 diff --git a/kjumpingcube/kcubeboxwidget.cpp b/kjumpingcube/kcubeboxwidget.cpp new file mode 100644 index 00000000..98f305d9 --- /dev/null +++ b/kjumpingcube/kcubeboxwidget.cpp @@ -0,0 +1,711 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kcubeboxwidget.h" + +#include +#include +#include +#include +#include +#include + +#include "prefs.h" + +KCubeBoxWidget::KCubeBoxWidget(const int d,QWidget *parent,const char *name) + : QWidget(parent,name), + CubeBoxBase(d) +{ + init(); +} + + + +KCubeBoxWidget::KCubeBoxWidget(CubeBox& box,QWidget *parent,const char *name) + :QWidget(parent,name), + CubeBoxBase(box.dim()) +{ + init(); + + int i,j; + for(i=0;i(box.dim()) +{ + init(); + + int i,j; + for(i=0;ireset(); + } + + KCubeWidget::enableClicks(true); + + currentPlayer=One; + + emit playerChanged(One); + checkComputerplayer(One); +} + +void KCubeBoxWidget::undo() +{ + if(isActive()) + return; + + Player oldPlayer=currentPlayer; + + *this=*undoBox; + + if(oldPlayer!=currentPlayer) + emit playerChanged(currentPlayer); + + checkComputerplayer(currentPlayer); + +} + +void KCubeBoxWidget::getHint() +{ + if(isActive()) + return; + + int d=dim(); + for(int i=0;istopHint(); + } + + int row=0,column=0; + CubeBox field=*this; + + emit startedThinking(); + bool canceled=!brain.getHint(row,column,(CubeBox::Player)currentPlayer,field); + emit stoppedThinking(); + + if(canceled) + { + return; // return if thinking was stopped + } + cubes[row][column]->showHint(); +} + +void KCubeBoxWidget::setColor(Player player,QPalette color) +{ + KCubeWidget::setColor((Cube::Owner)player,color); + + for(int row=0;rowupdateColors(); + } +} + +void KCubeBoxWidget::setDim(int d) +{ + if(d != dim()) + { + undoBox->setDim(d); + CubeBoxBase::setDim(d); + } +} + +void KCubeBoxWidget::setComputerplayer(Player player,bool flag) +{ + if(player==One) + computerPlOne=flag; + else if(player==Two) + computerPlTwo=flag; +} + + +void KCubeBoxWidget::stopActivities() +{ + if(moveTimer->isActive()) + { + stopLoop(); + emit stoppedMoving(); + } + if(brain.isActive()) + { + brain.stop(); + emit stoppedThinking(); + } + +} + +void KCubeBoxWidget::saveProperties(KConfigBase* config) +{ + if(isMoving()) + { + stopActivities(); + undo(); + } + else if(brain.isActive()) + stopActivities(); + + // save current player + config->writeEntry("onTurn",(int)currentPlayer); + + QStrList list; + list.setAutoDelete(true); + QString owner, value, key; + int cubeDim=dim(); + + for(int row=0; row < cubeDim ; row++) + for(int column=0; column < cubeDim ; column++) + { + key.sprintf("%u,%u",row,column); + owner.sprintf("%u",cubes[row][column]->owner()); + value.sprintf("%u",cubes[row][column]->value()); + list.append(owner.ascii()); + list.append(value.ascii()); + config->writeEntry(key , list); + + list.clear(); + } + config->writeEntry("CubeDim",dim()); +} + +void KCubeBoxWidget::readProperties(KConfigBase* config) +{ + QStrList list; + list.setAutoDelete(true); + QString owner, value, key; + setDim(config->readNumEntry("CubeDim",5)); + int cubeDim=dim(); + + for(int row=0; row < cubeDim ; row++) + for(int column=0; column < cubeDim ; column++) + { + key.sprintf("%u,%u",row,column); + config->readListEntry(key, list); + owner=list.first(); + value=list.next(); + cubes[row][column]->setOwner((KCubeWidget::Owner)owner.toInt()); + cubes[row][column]->setValue(value.toInt()); + + list.clear(); + } + + + // set current player + int onTurn=config->readNumEntry("onTurn",1); + currentPlayer=(Player)onTurn; + emit playerChanged(onTurn); + checkComputerplayer((Player)onTurn); +} + +/* ***************************************************************** ** +** slots ** +** ***************************************************************** */ +void KCubeBoxWidget::setWaitCursor() +{ + setCursor(KCursor::waitCursor()); +} + + + +void KCubeBoxWidget::setNormalCursor() +{ + setCursor(KCursor::handCursor()); +} + +void KCubeBoxWidget::stopHint() +{ + + int d=dim(); + for(int i=0;istopHint(); + } + +} + +bool KCubeBoxWidget::checkClick(int row,int column, bool isClick) +{ + if(isActive()) + return false; + + // make the game start when computer player is player one and user clicks + if(isClick && currentPlayer == One && computerPlOne) + { + checkComputerplayer(currentPlayer); + return false; + } + else if((Cube::Owner)currentPlayer==cubes[row][column]->owner() || + cubes[row][column]->owner()==Cube::Nobody) + { + doMove(row,column); + return true; + } + else + return false; +} + +void KCubeBoxWidget::checkComputerplayer(Player player) +{ + // checking if a process is running or the Widget isn't shown yet + if(isActive() || !isVisibleToTLW()) + return; + if((player==One && computerPlOne && currentPlayer==One) + || (player==Two && computerPlTwo && currentPlayer==Two)) + { + KCubeWidget::enableClicks(false); + + CubeBox field(*this); + int row=0,column=0; + emit startedThinking(); + bool canceled=!brain.getHint(row,column,(CubeBoxBase::Player)player,field); + emit stoppedThinking(); + + if(!canceled) + { + cubes[row][column]->showHint(500,2); + + bool result=checkClick(row,column,false); + assert(result); + } + } + +} + +/* ***************************************************************** ** +** status functions ** +** ***************************************************************** */ + +bool KCubeBoxWidget::isActive() const +{ + bool flag=false; + if(moveTimer->isActive()) + flag=true; + else if(brain.isActive()) + flag=true; + + return flag; +} + +bool KCubeBoxWidget::isMoving() const +{ + return moveTimer->isActive(); +} + +bool KCubeBoxWidget::isComputer(Player player) const +{ + if(player==One) + return computerPlOne; + else + return computerPlTwo; +} + + +int KCubeBoxWidget::skill() const +{ + return brain.skill(); +} + +QPalette KCubeBoxWidget::color(Player forWhom) +{ + return KCubeWidget::color((KCubeWidget::Owner)forWhom); +} + +/* ***************************************************************** ** +** initializing functions ** +** ***************************************************************** */ +void KCubeBoxWidget::init() +{ + initCubes(); + + undoBox=new CubeBox(dim()); + + currentPlayer=One; + moveDelay=100; + moveTimer=new QTimer(this); + computerPlOne=false; + computerPlTwo=false; + KCubeWidget::enableClicks(true); + loadSettings(); + + connect(moveTimer,SIGNAL(timeout()),SLOT(nextLoopStep())); + connect(this,SIGNAL(startedThinking()),SLOT(setWaitCursor())); + connect(this,SIGNAL(stoppedThinking()),SLOT(setNormalCursor())); + connect(this,SIGNAL(startedMoving()),SLOT(setWaitCursor())); + connect(this,SIGNAL(stoppedMoving()),SLOT(setNormalCursor())); + connect(this,SIGNAL(playerWon(int)),SLOT(stopActivities())); + + setNormalCursor(); + + emit playerChanged(One); +} + +void KCubeBoxWidget::initCubes() +{ + const int s=dim(); + int i,j; + + // create Layout + layout=new QGridLayout(this,s,s); + + + for(i=0;isetRowStretch(i,1); + layout->setColStretch(i,1); + } + + + // create new cubes + cubes = new KCubeWidget**[s]; + for(i=0;isetCoordinates(i,j); + layout->addWidget(cubes[i][j],i,j); + cubes[i][j]->show(); + connect(cubes[i][j],SIGNAL(clicked(int,int,bool)),SLOT(stopHint())); + connect(cubes[i][j],SIGNAL(clicked(int,int,bool)),SLOT(checkClick(int,int,bool))); + } + + // initialize cubes + int max=dim()-1; + + cubes[0][0]->setMax(2); + cubes[0][max]->setMax(2); + cubes[max][0]->setMax(2); + cubes[max][max]->setMax(2); + + for(i=1;isetMax(3); + cubes[i][max]->setMax(3); + cubes[0][i]->setMax(3); + cubes[max][i]->setMax(3); + } + + for(i=1;isetMax(4); + } + +} + +QSize KCubeBoxWidget::sizeHint() const +{ + return QSize(400,400); +} + +void KCubeBoxWidget::deleteCubes() +{ + if(layout) + delete layout; + + CubeBoxBase::deleteCubes(); +} + + +/* ***************************************************************** ** +** other private functions ** +** ***************************************************************** */ + +void KCubeBoxWidget::doMove(int row,int column) +{ + // if a move hasn't finished yet don't do another move + if(isActive()) + return; + + // for undo-function copy field + *undoBox=*this; + + cubes[row][column]->increase((Cube::Owner)currentPlayer); + + if(cubes[row][column]->overMax()) + { + KCubeWidget::enableClicks(false); + startLoop(); + } + else + changePlayer(); +} + +void KCubeBoxWidget::startLoop() +{ + emit startedMoving(); + + KCubeWidget::enableClicks(false); + + loop.row=0; + loop.column=0; + loop.finished=true; + + moveTimer->start(moveDelay); +} + +void KCubeBoxWidget::stopLoop() +{ + moveTimer->stop(); + emit stoppedMoving(); + KCubeWidget::enableClicks(true); +} + +void KCubeBoxWidget::nextLoopStep() +{ + // search cube with to many points + while(!cubes[loop.row][loop.column]->overMax()) + { + loop.column++; + if(loop.column==dim()) + { + if(loop.row==dim()-1) + { + if(!loop.finished) + { + loop.row=0; + loop.column=0; + loop.finished=true; + return; + } + else // loop finished + { + stopLoop(); + changePlayer(); + + return; + } + } + else + { + loop.row++; + loop.column=0; + } + } + } + + + increaseNeighbours(currentPlayer,loop.row,loop.column); + cubes[loop.row][loop.column]->decrease(); + loop.finished=false; + + if(hasPlayerWon(currentPlayer)) + { + emit playerWon((int)currentPlayer); + stopLoop(); + return; + } +} + +bool KCubeBoxWidget::hasPlayerWon(Player player) +{ + for(int i=0;iowner()!=(Cube::Owner)player) + { + return false; + } + } + return true; +} + +KCubeBoxWidget::Player KCubeBoxWidget::changePlayer() +{ + currentPlayer=(currentPlayer==One)? Two : One; + + emit playerChanged(currentPlayer); + checkComputerplayer(currentPlayer); + KCubeWidget::enableClicks(true); + return currentPlayer; +} + + +void KCubeBoxWidget::increaseNeighbours(KCubeBoxWidget::Player forWhom,int row,int column) +{ + KCubeWidget::Owner _player = (KCubeWidget::Owner)(forWhom); + + if(row==0) + { + if(column==0) // top left corner + { + cubes[0][1]->increase(_player); + cubes[1][0]->increase(_player); + return; + } + else if(column==dim()-1) // top right corner + { + cubes[0][dim()-2]->increase(_player); + cubes[1][dim()-1]->increase(_player); + return; + } + else // top edge + { + cubes[0][column-1]->increase(_player); + cubes[0][column+1]->increase(_player); + cubes[1][column]->increase(_player); + return; + } + } + else if(row==dim()-1) + { + if(column==0) // left bottom corner + { + cubes[dim()-2][0]->increase(_player); + cubes[dim()-1][1]->increase(_player); + return; + } + + else if(column==dim()-1) // right bottom corner + { + cubes[dim()-2][dim()-1]->increase(_player); + cubes[dim()-1][dim()-2]->increase(_player); + return; + } + else // bottom edge + { + cubes[dim()-1][column-1]->increase(_player); + cubes[dim()-1][column+1]->increase(_player); + cubes[dim()-2][column]->increase(_player); + return; + } + } + else if(column==0) // left edge + { + cubes[row-1][0]->increase(_player); + cubes[row+1][0]->increase(_player); + cubes[row][1]->increase(_player); + return; + } + else if(column==dim()-1) // right edge + { + cubes[row-1][dim()-1]->increase(_player); + cubes[row+1][dim()-1]->increase(_player); + cubes[row][dim()-2]->increase(_player); + return; + } + else + { + cubes[row][column-1]->increase(_player); + cubes[row][column+1]->increase(_player); + cubes[row-1][column]->increase(_player); + cubes[row+1][column]->increase(_player); + return; + } + + +} + +#include "kcubeboxwidget.moc" + diff --git a/kjumpingcube/kcubeboxwidget.h b/kjumpingcube/kcubeboxwidget.h new file mode 100644 index 00000000..ff2f4ac0 --- /dev/null +++ b/kjumpingcube/kcubeboxwidget.h @@ -0,0 +1,188 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KCUBEBOXWIDGET_H +#define KCUBEBOXWIDGET_H + +#include "cubeboxbase.h" +#include "kcubewidget.h" +#include "brain.h" +#include + +class QGridLayout; +class CubeBox; +class QPalette; +class QTimer; +class KConfigBase; + +#ifdef HAVE_CONFIG_H +#include +#endif + +/** +*@internal +*/ +struct Loop +{ + int row; + int column; + bool finished; +}; + + +class KCubeBoxWidget : public QWidget , public CubeBoxBase +{ + Q_OBJECT +public: + KCubeBoxWidget(const int dim=1,QWidget *parent=0,const char *name=0); + + KCubeBoxWidget(CubeBox& box, QWidget *parent=0,const char *name=0); + KCubeBoxWidget(const KCubeBoxWidget& box,QWidget *parent=0,const char *name=0); + virtual ~KCubeBoxWidget(); + + KCubeBoxWidget& operator= (CubeBox& box); + KCubeBoxWidget& operator= ( const KCubeBoxWidget& box); + + /** + * reset cubebox for a new game + */ + void reset(); + + /** undo last move */ + void undo(); + + /** + * set colors that are used to show owners of the cubes + * + * @param forWhom for which player the color should be set + * @param color color for player one + */ + void setColor(Player forWhom,QPalette color); + /** + * sets number of Cubes in a row/column to 'size'. + */ + virtual void setDim(int dim); + + /** + * sets player 'player' as computer or human + * + * @param player + * @param flag: true for computer, false for human + */ + void setComputerplayer(Player player,bool flag); + + /** returns current skill, according to Prefs::EnumSkill */ + int skill() const; + + /** returns true if player 'player' is a computerPlayer */ + bool isComputer(Player player) const; + + /** returns true if CubeBox is doing a move or getting a hint */ + bool isActive() const; + bool isMoving() const; + + /** returns current Color for Player ´forWhom´ */ + QPalette color(Player forWhom); + + /** + * checks if 'player' is a computerplayer an computes next move if TRUE + */ + void checkComputerplayer(Player player); + + inline void saveGame(KConfigBase *c) { saveProperties(c); } + inline void restoreGame(KConfigBase *c) { readProperties(c); } + +public slots: + /** stops all activities like getting a hint or doing a move */ + void stopActivities(); + /** + * computes a possibility to move and shows it by highlightning + * this cube + */ + void getHint(); + + void loadSettings(); + +signals: + void playerChanged(int newPlayer); + void playerWon(int player); + void startedMoving(); + void startedThinking(); + void stoppedMoving(); + void stoppedThinking(); + +protected: + virtual QSize sizeHint() const; + virtual void deleteCubes(); + virtual void initCubes(); + + void saveProperties(KConfigBase *); + void readProperties(KConfigBase *); + +protected slots: + /** sets the cursor to an waitcursor */ + void setWaitCursor(); + /** restores the original cursor */ + void setNormalCursor(); + +private: + void init(); + + QGridLayout *layout; + CubeBox *undoBox; + Brain brain; + + QTimer *moveTimer; + int moveDelay; + Loop loop; + /** */ + void startLoop(); + /** */ + void stopLoop(); + + Player changePlayer(); + bool hasPlayerWon(Player player); + bool computerPlOne; + bool computerPlTwo; + + /** + * increases the cube at row 'row' and column 'column' , + * and starts the Loop for checking the playingfield + */ + void doMove(int row,int column); + + void increaseNeighbours(KCubeBoxWidget::Player forWhom,int row,int column); + +private slots: + void nextLoopStep(); + /** + * checks if cube at ['row','column'] is clickable by the current player. + * if true, it increases this cube and checks the playingfield + */ + bool checkClick(int row,int column,bool isClick); + + /** turns off blinking, if an other cube is clicked */ + void stopHint(); + +}; + +#endif // KCUBEBOXWIDGET_H + diff --git a/kjumpingcube/kcubewidget.cpp b/kjumpingcube/kcubewidget.cpp new file mode 100644 index 00000000..f23e3cce --- /dev/null +++ b/kjumpingcube/kcubewidget.cpp @@ -0,0 +1,348 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kcubewidget.h" + +#include +#include + +#include +#include + +/* ****************************************************** ** +** static elements ** +** ****************************************************** */ +bool KCubeWidget::_clicksAllowed=true; +QPalette KCubeWidget::color1; +QPalette KCubeWidget::color2; + + +void KCubeWidget::enableClicks(bool flag) +{ + _clicksAllowed=flag; +} + + +void KCubeWidget::setColor(Owner forWhom, QPalette newPalette) +{ + if(forWhom==One) + { + color1=newPalette; + } + else if(forWhom==Two) + { + color2=newPalette; + } +} + +QPalette KCubeWidget::color(Owner forWhom) +{ + QPalette color; + if(forWhom==One) + { + color=color1; + } + else if(forWhom==Two) + { + color=color2; + } + + return color; +} + + +/* ****************************************************** ** +** public functions ** +** ****************************************************** */ + +KCubeWidget::KCubeWidget(QWidget* parent,const char* name + ,Owner owner,int value,int max) + : QFrame(parent,name), + Cube(owner,value,max) +{ + setFrameStyle(Panel|Raised); + //setLineWidth(2); + setMinimumSize(20,20); + + setCoordinates(0,0); + + //initialize hintTimer + // will be automatically destroyed by the parent + hintTimer = new QTimer(this); + hintCounter=0; + connect(hintTimer,SIGNAL(timeout()),SLOT(hint())); + + setPalette(kapp->palette()); + + // show values + repaint(false); +} + +KCubeWidget::~KCubeWidget() +{ +} + + +KCubeWidget& KCubeWidget::operator=(const Cube& cube) +{ + if(this!=&cube) + { + setOwner(cube.owner()); + setValue(cube.value()); + setMax(cube.max()); + } + + return *this; +} + +KCubeWidget& KCubeWidget::operator=(const KCubeWidget& cube) +{ + if(this!=&cube) + { + setOwner(cube.owner()); + setValue(cube.value()); + setMax(cube.max()); + } + + return *this; +} +KCubeWidget::Owner KCubeWidget::setOwner(Owner newOwner) +{ + Owner old=Cube::setOwner(newOwner); + + updateColors(); + + return old; +} + +void KCubeWidget::setValue(int newValue) +{ + Cube::setValue(newValue); + update(); +} + + +void KCubeWidget::showHint(int interval,int number) +{ + if(hintTimer->isActive()) + return; + + hintCounter=2*number; + hintTimer->start(interval); +} + + +void KCubeWidget::animate(bool ) +{ +} + + +void KCubeWidget::setCoordinates(int row,int column) +{ + _row=row; + _column=column; +} + +int KCubeWidget::row() const +{ + return _row; +} + +int KCubeWidget::column() const +{ + return _column; +} + + + + +/* ****************************************************** ** +** public slots ** +** ****************************************************** */ + +void KCubeWidget::reset() +{ + setValue(1); + setOwner(Nobody); +} + + +void KCubeWidget::updateColors() +{ + if(owner()==One) + setPalette(color1); + else if(owner()==Two) + setPalette(color2); + else if(owner()==Nobody) + setPalette(kapp->palette()); +} + +void KCubeWidget::stopHint() +{ + if(hintTimer->isActive()) + { + hintTimer->stop(); + setBackgroundMode(PaletteBackground); + } + +} + + + +/* ****************************************************** ** +** protected slots ** +** ****************************************************** */ + +void KCubeWidget::hint() +{ + hintCounter--; + if(hintCounter%2==1) + { + setBackgroundMode(PaletteLight); + } + else + { + setBackgroundMode(PaletteBackground); + } + if(hintCounter==0) + { + stopHint(); + } +} + + + +/* ****************************************************** ** +** Event handler ** +** ****************************************************** */ + +void KCubeWidget::mouseReleaseEvent(QMouseEvent *e) +{ + // only accept click if it was inside this cube + if(e->x()< 0 || e->x() > width() || e->y() < 0 || e->y() > height()) + return; + + if(e->button() == LeftButton && _clicksAllowed) + { + stopHint(); + emit clicked(row(),column(),true); + } +} + + + +void KCubeWidget::drawContents(QPainter *painter) +{ + QRect contents=contentsRect(); + QPixmap buffer(contents.size()); + buffer.fill(this,contents.topLeft()); + QPainter *p=new QPainter; + p->begin(&buffer); + int h=contents.height(); + int w=contents.width(); + int circleSize=(hsetBrush(brush); + p->setPen(pen); + switch(points) + { + case 1: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + break; + + case 3: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + case 2: + p->drawEllipse(w/4-circleSize/2,h/4-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + break; + + case 5: + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2,circleSize,circleSize); + case 4: + p->drawEllipse(w/4-circleSize/2,h/4-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/4-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/4-circleSize/2, + circleSize,circleSize); + break; + + case 8: + p->drawEllipse(w/2-circleSize/2,2*h/3-circleSize/2, + circleSize,circleSize); + case 7: + p->drawEllipse(w/2-circleSize/2,h/3-circleSize/2, + circleSize,circleSize); + case 6: + p->drawEllipse(w/4-circleSize/2,h/6-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + break; + + + case 9: + p->drawEllipse(w/4-circleSize/2,h/6-circleSize/2,circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,3*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(3*w/4-circleSize/2,5*h/6-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,2*h/7-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,5*h/7-circleSize/2, + circleSize,circleSize); + p->drawEllipse(w/2-circleSize/2,h/2-circleSize/2, + circleSize,circleSize); + break; + + default: + kdDebug() << "cube had value " << points << endl; + QString s; + s.sprintf("%d",points); + p->drawText(w/2,h/2,s); + break; + } + p->end(); + delete p; + + painter->drawPixmap(contents.topLeft(),buffer); + +} + +#include "kcubewidget.moc" diff --git a/kjumpingcube/kcubewidget.h b/kjumpingcube/kcubewidget.h new file mode 100644 index 00000000..f0d8d8cd --- /dev/null +++ b/kjumpingcube/kcubewidget.h @@ -0,0 +1,119 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KCUBEWIDGET_H +#define KCUBEWIDGET_H + +#include +#include "cube.h" + +class QPalette; +class QTimer; + + +/** +* +*/ +class KCubeWidget : public QFrame , public Cube +{ + Q_OBJECT + +public: + /** constructs a new KCubeWidget*/ + KCubeWidget(QWidget* parent=0,const char* name=0 + ,Owner owner=Cube::Nobody,int value=1,int max=0); + virtual ~KCubeWidget(); + + virtual Owner setOwner(Owner newOwner); + virtual void setValue(int newValue); + + + /** takes the information from a Cube */ + KCubeWidget& operator=(const Cube&); + KCubeWidget& operator=(const KCubeWidget&); + + /** shows a tip e.g. blinks with the interval 500 and number times */ + void showHint(int interval=500,int number=5); + /** stops showing a hint */ + void stopHint(); + + /** + * animates the cube if possible (if feature is enabled) + * In KCubeWidget this function does nothing, it's just for having + * a public interface for all classes that inherits KCubeWidget + */ + virtual void animate(bool flag); + + /** + * sets the coordinates of the Cube in a Cubebox; + * needed for identification when clicked. + */ + void setCoordinates(int row,int column); + /** returns the row */ + int row() const; + /** returns the column */ + int column() const; + + /** enables or disables possibility to click a cube*/ + static void enableClicks(bool flag); + static void setColor(Owner forWhom, QPalette newPalette); + static QPalette color(Owner forWhom); + +public slots: + /** resets the Cube to default values */ + virtual void reset(); + /** shows changed colors*/ + virtual void updateColors(); + +signals: + void clicked(int row,int column,bool isClick); + +protected: + /** checks, if mouseclick was inside this cube*/ + virtual void mouseReleaseEvent(QMouseEvent*); + + /** refreshs the contents of the Cube */ + virtual void drawContents(QPainter*); + + + + int hintCounter; + +protected slots: + /** + * To this function the hintTimer is connected. + * It manage a periodical way of showing a hint. + */ + virtual void hint(); + +private: + int _row; + int _column; + + QTimer *hintTimer; + + static bool _clicksAllowed; + static QPalette color1; + static QPalette color2; +}; + + +#endif // KCUBEWIDGET_H diff --git a/kjumpingcube/kjumpingcube.cpp b/kjumpingcube/kjumpingcube.cpp new file mode 100644 index 00000000..13619d99 --- /dev/null +++ b/kjumpingcube/kjumpingcube.cpp @@ -0,0 +1,278 @@ +/***************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "kjumpingcube.h" +#include "kcubeboxwidget.h" +#include "version.h" + +// Settings +#include "settings.h" +#include + +#include "prefs.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ID_STATUS_TURN_TEXT 1000 +#define ID_STATUS_TURN 2000 + +#define MESSAGE_TIME 2000 + + +KJumpingCube::KJumpingCube() + : view(new KCubeBoxWidget(5, this, "KCubeBoxWidget")) +{ + connect(view,SIGNAL(playerChanged(int)),SLOT(changePlayer(int))); + connect(view,SIGNAL(stoppedMoving()),SLOT(disableStop())); + connect(view,SIGNAL(stoppedThinking()),SLOT(disableStop())); + connect(view,SIGNAL(startedMoving()),SLOT(enableStop_Moving())); + connect(view,SIGNAL(startedThinking()),SLOT(enableStop_Thinking())); + connect(view,SIGNAL(playerWon(int)),SLOT(showWinner(int))); + + // tell the KMainWindow that this is indeed the main widget + setCentralWidget(view); + + // init statusbar + QString s = i18n("Current player:"); + statusBar()->insertItem(s,ID_STATUS_TURN_TEXT, false); + statusBar()->changeItem(s,ID_STATUS_TURN_TEXT); + statusBar()->setItemAlignment (ID_STATUS_TURN_TEXT, AlignLeft | AlignVCenter); + statusBar()->setFixedHeight( statusBar()->sizeHint().height() ); + + currentPlayer = new QWidget(this, "currentPlayer"); + currentPlayer->setFixedWidth(40); + statusBar()->addWidget(currentPlayer, ID_STATUS_TURN, false); + statusBar()->setItemAlignment(ID_STATUS_TURN, AlignLeft | AlignVCenter); + + initKAction(); + changePlayer(1); +} + +void KJumpingCube::initKAction() { + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::load(this, SLOT(openGame()), actionCollection()); + KStdGameAction::save(this, SLOT(save()), actionCollection()); + KStdGameAction::saveAs(this, SLOT(saveAs()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + hintAction = KStdGameAction::hint(view, SLOT(getHint()), actionCollection()); + stopAction = new KAction(i18n("Stop &Thinking"), "stop", + Qt::Key_Escape, this, SLOT(stop()), actionCollection(), "game_stop"); + stopAction->setEnabled(false); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + undoAction->setEnabled(false); + KStdAction::preferences(this, SLOT(showOptions()), actionCollection()); + + setupGUI(); +} + +void KJumpingCube::newGame(){ + undoAction->setEnabled(false); + view->reset(); + statusBar()->message(i18n("New Game"),MESSAGE_TIME); +} + +void KJumpingCube::saveGame(bool saveAs) +{ + if(saveAs || gameURL.isEmpty()) + { + int result=0; + KURL url; + + do + { + url = KFileDialog::getSaveURL(gameURL.url(),"*.kjc",this,0); + + if(url.isEmpty()) + return; + + // check filename + QRegExp pattern("*.kjc",true,true); + if(!pattern.exactMatch(url.filename())) + { + url.setFileName( url.filename()+".kjc" ); + } + + if(KIO::NetAccess::exists(url,false,this)) + { + QString mes=i18n("The file %1 exists.\n" + "Do you want to overwrite it?").arg(url.url()); + result = KMessageBox::warningContinueCancel(this, mes, QString::null, i18n("Overwrite")); + if(result==KMessageBox::Cancel) + return; + } + } + while(result==KMessageBox::No); + + gameURL=url; + } + + KTempFile tempFile; + tempFile.setAutoDelete(true); + KSimpleConfig config(tempFile.name()); + + config.setGroup("KJumpingCube"); + config.writeEntry("Version",KJC_VERSION); + config.setGroup("Game"); + view->saveGame(&config); + config.sync(); + + if(KIO::NetAccess::upload( tempFile.name(),gameURL,this )) + { + QString s=i18n("game saved as %1"); + s=s.arg(gameURL.url()); + statusBar()->message(s,MESSAGE_TIME); + } + else + { + KMessageBox::sorry(this,i18n("There was an error in saving file\n%1").arg(gameURL.url())); + } +} + +void KJumpingCube::openGame() +{ + bool fileOk=true; + KURL url; + + do + { + url = KFileDialog::getOpenURL( gameURL.url(), "*.kjc", this, 0 ); + if( url.isEmpty() ) + return; + if(!KIO::NetAccess::exists(url,true,this)) + { + QString mes=i18n("The file %1 does not exist!").arg(url.url()); + KMessageBox::sorry(this,mes); + fileOk=false; + } + } + while(!fileOk); + + QString tempFile; + if( KIO::NetAccess::download( url, tempFile, this ) ) + { + KSimpleConfig config(tempFile,true); + config.setGroup("KJumpingCube"); + if(!config.hasKey("Version")) + { + QString mes=i18n("The file %1 isn't a KJumpingCube gamefile!") + .arg(url.url()); + KMessageBox::sorry(this,mes); + return; + } + + gameURL=url; + config.setGroup("Game"); + view->restoreGame(&config); + + undoAction->setEnabled(false); + + KIO::NetAccess::removeTempFile( tempFile ); + } + else + KMessageBox::sorry(this,i18n("There was an error loading file\n%1").arg( url.url() )); +} + +void KJumpingCube::stop() +{ + + if(view->isMoving()) + undoAction->setEnabled(true); + + view->stopActivities(); + + statusBar()->message(i18n("stopped activity"),MESSAGE_TIME); +} + +void KJumpingCube::undo() +{ + if(view->isActive()) + return; + view->undo(); + undoAction->setEnabled(false); +} + +void KJumpingCube::changePlayer(int newPlayer) +{ + undoAction->setEnabled(true); + currentPlayer->setBackgroundColor(newPlayer == 1 ? Prefs::color1() : Prefs::color2()); + currentPlayer->repaint(); +} + +void KJumpingCube::showWinner(int player) { + QString s=i18n("Winner is Player %1!").arg(player); + KMessageBox::information(this,s,i18n("Winner")); + view->reset(); +} + +void KJumpingCube::disableStop() +{ +// toolBar()->setItemEnabled(ID_GAME_STOP_HINT,false); +// game->setItemEnabled(ID_GAME_STOP_HINT,false); +// toolBar()->setItemEnabled(ID_GAME_HINT,true); +// game->setItemEnabled(ID_GAME_HINT,true); + stopAction->setEnabled(false); + hintAction->setEnabled(true); + statusBar()->clear(); +} + + +void KJumpingCube::enableStop_Moving() +{ +// toolBar()->setItemEnabled(ID_GAME_STOP_HINT,true); +// game->setItemEnabled(ID_GAME_STOP_HINT,true); +// toolBar()->setItemEnabled(ID_GAME_HINT,false); +// game->setItemEnabled(ID_GAME_HINT,false); + stopAction->setEnabled(true); + hintAction->setEnabled(false); + statusBar()->message(i18n("Performing move.")); +} + +void KJumpingCube::enableStop_Thinking(){ + stopAction->setEnabled(true); + hintAction->setEnabled(false); + statusBar()->message(i18n("Computing next move.")); +} + +/** + * Show Configure dialog. + */ +void KJumpingCube::showOptions(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + dialog->addPage(new Settings(0, "General"), i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), view, SLOT(loadSettings())); + dialog->show(); +} + +#include "kjumpingcube.moc" + diff --git a/kjumpingcube/kjumpingcube.desktop b/kjumpingcube/kjumpingcube.desktop new file mode 100644 index 00000000..3df8f3d6 --- /dev/null +++ b/kjumpingcube/kjumpingcube.desktop @@ -0,0 +1,102 @@ +[Desktop Entry] +Name=KJumpingCube +Name[af]=Kjumpingcube +Name[ar]=لعبة المكعب القاÙز (KJumpingCube) +Name[az]=K Hoppanan Kub +Name[be]=Скокаючы кубік +Name[bn]=কে-জামà§à¦ªà¦¿à¦‚কিউব +Name[br]=KDiñsALamm +Name[eo]=Saltanta kubo +Name[hi]=के-जमà¥à¤ªà¤¿à¤‚गकà¥à¤¯à¥‚ब +Name[hu]=Ugráló kocka +Name[is]=Hoppandi kubbur +Name[ne]=केडीई जमà¥à¤ªà¤¿à¤™ कà¥à¤¯à¥à¤¬ +Name[pa]=ਕੇ-ਜੰਪ ਘਣ +Name[pl]=SkaczÄ…cy szeÅ›cian +Name[pt]=Cubo Saltitão +Name[pt_BR]=KSaltandoCubo +Name[ro]=Cubul săritor +Name[sk]=KSkákajúca kocka +Name[sv]=Kjumpingcube +Name[ta]=கேகà¯à®¤à®¿à®•à¯à®•à¯à®®à¯ கனசதà¯à®°à®®à¯ +Name[tg]=KКубикҳои Ҷиҳанда +Name[tr]=Zıplayan Küp +Name[xh]=Ityhubhu yeKJumping +Name[zh_TW]=KJumpingCube è·³èºç«‹æ–¹é«” +Exec=kjumpingcube -caption "%c" %i %m +Icon=kjumpingcube +Type=Application +DocPath=kjumpingcube/index.html +GenericName=Tactical Game +GenericName[af]=Taktiese Speletjie +GenericName[ar]=لعبة تكتيكية +GenericName[az]=Taktik oyunu +GenericName[be]=Ð¢Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ТактичеÑка игра +GenericName[bn]=কৌশলের খেলা +GenericName[br]=C'hoari a vrezelekaouriezh +GenericName[bs]=TaktiÄka igra +GenericName[ca]=Joc de tàctica +GenericName[cs]=Taktická hra +GenericName[cy]=Gêm Dactegol +GenericName[da]=Taktisk spil +GenericName[de]=Taktikspiel +GenericName[el]=Παιχνίδι τακτικής +GenericName[eo]=Taktika ludo +GenericName[es]=Juego de táctica +GenericName[et]=Taktikamäng +GenericName[eu]=Joko taktikoa +GenericName[fa]=بازی برنامه‌ریزی‌شده +GenericName[fi]=Taktiikkapeli +GenericName[fo]=Taktiskt spæl +GenericName[fr]=Jeu de tactique +GenericName[ga]=Cluiche Taicticiúil +GenericName[gl]=Xogo de Táctica +GenericName[he]=משחק טקטי +GenericName[hi]=रणनीतिक खेल +GenericName[hr]=TaktiÄka igra +GenericName[hu]=Logikai +GenericName[id]=Permainan Taktik +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco di tattica +GenericName[ja]=戦略的ゲーム +GenericName[km]=ល្បែង​ក្បួន​យុទ្ធសាស្ážáŸ’ážš +GenericName[ko]=ì „ëžµ 게임 +GenericName[lt]=Taktinis žaidimas +GenericName[lv]=TaktiskÄ spÄ“le +GenericName[mk]=Тактичка игра +GenericName[mt]=Logħba ta' tattika +GenericName[nb]=Taktikk-spill +GenericName[nds]=Taktikspeel +GenericName[ne]=यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Tactisch spel +GenericName[nn]=Taktisk spel +GenericName[pa]=ਟਾਕਟੀਕਲ ਖੇਡ +GenericName[pl]=Gra taktyczna +GenericName[pt]=Jogo de Estratégia +GenericName[pt_BR]=Jogo Tático +GenericName[ro]=Un joc de tactică +GenericName[ru]=ТактичеÑÐºÐ°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino Mugambi +GenericName[se]=Taktihkkaspeallu +GenericName[sk]=Taktická hra +GenericName[sl]=TaktiÄna igra +GenericName[sr]=Тактичка игра +GenericName[sr@Latn]=TaktiÄka igra +GenericName[sv]=Taktikspel +GenericName[ta]=தநà¯à®¤à®¿à®°à®®à®¾à®© விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Тактикӣ +GenericName[th]=เà¸à¸¡à¸§à¸²à¸‡à¹à¸œà¸™ +GenericName[tr]=Taktik oyunu +GenericName[uk]=Тактична гра +GenericName[ven]=Mutambo wa Tactical +GenericName[vi]=Trò chÆ¡i chiến thuật +GenericName[wa]=Djeu di tactike +GenericName[xh]=Umdlalo onamaqhinga +GenericName[zh_CN]=æˆ˜æœ¯æ¸¸æˆ +GenericName[zh_TW]=戰術éŠæˆ² +GenericName[zu]=Umdlalo wamasu +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kjumpingcube/kjumpingcube.h b/kjumpingcube/kjumpingcube.h new file mode 100644 index 00000000..7ed961c7 --- /dev/null +++ b/kjumpingcube/kjumpingcube.h @@ -0,0 +1,76 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#ifndef KJUMPINGCUBE_H +#define KJUMPINGCUBE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +class KAction; +class KCubeBoxWidget; + +/** + * This class serves as the main window for KJumpingCube. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author Matthias Kiefer + * @version 0.7.2 + */ +class KJumpingCube : public KMainWindow { + Q_OBJECT + +public: + /** Default Constructor */ + KJumpingCube(); + +private: + KCubeBoxWidget *view; + QWidget *currentPlayer; + KAction *undoAction, *stopAction, *hintAction; + + KURL gameURL; + void initKAction(); + +private slots: + void newGame(); + void saveGame(bool saveAs=false); + inline void saveAs() { saveGame(true); } + inline void save() { saveGame(false); } + void openGame(); + void stop(); + void undo(); + void changePlayer(int newPlayer); + void showWinner(int); + void disableStop(); + void enableStop_Moving(); + void enableStop_Thinking(); + + void showOptions(); +}; + +#endif // KJUMPINGCUBE_H + diff --git a/kjumpingcube/kjumpingcube.kcfg b/kjumpingcube/kjumpingcube.kcfg new file mode 100644 index 00000000..ecd27d09 --- /dev/null +++ b/kjumpingcube/kjumpingcube.kcfg @@ -0,0 +1,38 @@ + + + + + + + darkred + + + + darkblue + + + + 6 + + + + + + + + + Average + + + + false + + + + true + + + diff --git a/kjumpingcube/kjumpingcubeui.rc b/kjumpingcube/kjumpingcubeui.rc new file mode 100644 index 00000000..359a87db --- /dev/null +++ b/kjumpingcube/kjumpingcubeui.rc @@ -0,0 +1,20 @@ + + + + + &Game + + + + +Main Toolbar + + + + + + + + + + diff --git a/kjumpingcube/main.cpp b/kjumpingcube/main.cpp new file mode 100644 index 00000000..eda42342 --- /dev/null +++ b/kjumpingcube/main.cpp @@ -0,0 +1,58 @@ +/* **************************************************************************** + This file is part of the game 'KJumpingCube' + + Copyright (C) 1998-2000 by Matthias Kiefer + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +**************************************************************************** */ +#include "version.h" +#include "kjumpingcube.h" +#include +#include +#include + + +static const char description[] = + I18N_NOOP("Tactical one or two player game"); + +// A hack to circumvent tricky i18n issue, not used later on in the code. +// Both context and contents must be exactly the same as for the entry in +// kdelibs/kdeui/ui_standards.rc +static const char dummy[] = I18N_NOOP2("Menu title", "&Move"); + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kjumpingcube", I18N_NOOP("KJumpingCube"), + KJC_VERSION, description, KAboutData::License_GPL, + "(c) 1998-2000, Matthias Kiefer"); + aboutData.addAuthor("Matthias Kiefer",0, "matthias.kiefer@gmx.de"); + aboutData.addAuthor("Benjamin Meyer",I18N_NOOP("Various improvements"), "ben+kjumpingcube@meyerhome.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // All session management is handled in the RESTORE macro + if (app.isRestored()) + RESTORE(KJumpingCube) + else { + KJumpingCube *kjumpingcube = new KJumpingCube; + app.setMainWidget(kjumpingcube); + kjumpingcube->show(); + } + return app.exec(); +} diff --git a/kjumpingcube/prefs.kcfgc b/kjumpingcube/prefs.kcfgc new file mode 100644 index 00000000..95d81c69 --- /dev/null +++ b/kjumpingcube/prefs.kcfgc @@ -0,0 +1,8 @@ +# Code generation options for kconfig_compiler +File=kjumpingcube.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +#Mutators=true +#CustomAdditions=true +#Mutators=true diff --git a/kjumpingcube/settings.ui b/kjumpingcube/settings.ui new file mode 100644 index 00000000..43908b76 --- /dev/null +++ b/kjumpingcube/settings.ui @@ -0,0 +1,268 @@ + +Settings + + + Settings + + + + 0 + 0 + 422 + 214 + + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Plain + + + 0 + + + + unnamed + + + + groupBox3 + + + GroupBoxPanel + + + Sunken + + + Board Size + + + + unnamed + + + + kcfg_CubeDim + + + 5 + + + 10 + + + 1 + + + 6 + + + Horizontal + + + Right + + + + + textLabel6 + + + 5x5 + + + + + textLabel8 + + + 10x10 + + + AlignVCenter|AlignRight + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + groupBox7 + + + Board Color + + + + unnamed + + + + kcfg_Color2 + + + + + + + + textLabel1 + + + Player 1: + + + + + textLabel2 + + + Player 2: + + + + + kcfg_Color1 + + + + + + + + + + groupBox2 + + + Computer Skill + + + + unnamed + + + + textLabel4 + + + Average + + + AlignCenter + + + + + textLabel3 + + + Beginner + + + + + textLabel5 + + + Expert + + + AlignVCenter|AlignRight + + + + + kcfg_Skill + + + 0 + + + 2 + + + 1 + + + Horizontal + + + Right + + + + + + + groupBox1 + + + Computer Plays As + + + + unnamed + + + + kcfg_ComputerPlayer1 + + + Player 1 + + + + + kcfg_ComputerPlayer2 + + + Player 2 + + + true + + + + + + + + + + + kcolorbutton.h + + diff --git a/kjumpingcube/version.h b/kjumpingcube/version.h new file mode 100644 index 00000000..bbd8f90d --- /dev/null +++ b/kjumpingcube/version.h @@ -0,0 +1 @@ +#define KJC_VERSION "1.1" diff --git a/klickety/CHANGELOG b/klickety/CHANGELOG new file mode 100644 index 00000000..2ecde970 --- /dev/null +++ b/klickety/CHANGELOG @@ -0,0 +1,22 @@ +1.0.3 (4 July 2004) [KDE 3.3 stable] + * use zoom in/out actions (bug #65456) + +1.0.2 (10 March 2004) [KDE 3.2.2 stable] + * fix bug #65490 (default with simpler removed display) + * fix bug #77170 (crash at startup) + +1.0.1 (18 December 2002) [KDE 3.2 stable] + * notifications + +1.0.0b (18 December 2002) [KDE 3.1 stable] + * fix uninitialized variable + +1.0.0 (16 June 2002) + * adapt to library changes + * world-wide highscores + +0.0.1 (23 May 2002) + * initial version in CVS + +----------------- +see also the CHANGELOG for ksirtet diff --git a/klickety/LICENSE b/klickety/LICENSE new file mode 100644 index 00000000..250ac00d --- /dev/null +++ b/klickety/LICENSE @@ -0,0 +1,18 @@ +KLICKETY +-------- +Copyright (c) 2001-2004 Nicolas HADACEK (hadacek@kde.org) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/klickety/Makefile.am b/klickety/Makefile.am new file mode 100644 index 00000000..5d9e6336 --- /dev/null +++ b/klickety/Makefile.am @@ -0,0 +1,70 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_builddir)/libksirtet -I$(top_srcdir)/libksirtet -I$(top_srcdir)/libkdegames/highscore -I$(top_srcdir)/libkdegames $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = klickety +klickety_LDADD = $(top_builddir)/libksirtet/base/libksirtetbase.la +klickety_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +klickety_LDFLAGS = $(KDE_USE_PIE) $(LIB_KDEGAMES) $(all_libraries) $(KDE_RPATH) +klickety_SOURCES = piece.cpp board.cpp field.cpp highscores.cpp main.cpp +METASOURCES = board.moc field.moc main.moc + +rcdir = $(kde_datadir)/klickety +rc_DATA = klicketyui.rc + +xdg_apps_DATA = klickety.desktop + +appdatadir = $(kde_datadir)/klickety +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(klickety_SOURCES) -o $(podir)/klickety.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true diff --git a/klickety/README b/klickety/README new file mode 100644 index 00000000..2a548cf6 --- /dev/null +++ b/klickety/README @@ -0,0 +1,12 @@ +KLICKETY : a clickomania-alike game +------------------------------------- +Copyright (c) 2001-2004 Nicolas HADACEK (hadacek@kde.org) +Distributed under the GNU General Public License + +Klickety is an adaptation of the (perharps) well-known Clickomania game ; it +is very similar to the "same" game. + +The code links to the ksirtet libraries. + + +Enjoy ! diff --git a/klickety/board.cpp b/klickety/board.cpp new file mode 100644 index 00000000..92969d6e --- /dev/null +++ b/klickety/board.cpp @@ -0,0 +1,163 @@ +#include "board.h" +#include "board.moc" + +#include "base/factory.h" + + +using namespace KGrid2D; + +void KLBoard::contentsMouseReleaseEvent(QMouseEvent *e) +{ + if ( e->button()!=LeftButton || blocked ) return; + QCanvasItemList list = canvas()->collisions(e->pos()); + if ( list.count()==0 ) return; + + QCanvasSprite *spr = static_cast(list.first()); + Coord c = findSprite(spr); + field.fill(0); + addRemoved = findGroup(field, c); + if ( addRemoved>=2 ) { + if ( state!=Normal ) { + state = Normal; + emit firstBlockClicked(); + } + blocked = true; + _beforeRemove(true); + } +} + +KLBoard::KLBoard(QWidget *parent) + : BaseBoard(true, parent), + field(matrix().width(), matrix().height()), + empty(matrix().width()), + blocked(false) +{} + +void KLBoard::start(const GTInitData &data) +{ + BaseBoard::start(data); + + updateScore(matrix().width() * matrix().height()); + state = GameOver; + sliding = false; + blocked = false; + for (uint i=0; isetValue(Piece::info().generateType(&randomSequence()), main); + Coord c(i, j); + setBlock(c, block); + } + computeInfos(); + showBoard(true); +} + +Coord KLBoard::findSprite(QCanvasSprite *spr) const +{ + for (uint i=0; isprite()==spr ) return c; + } + Q_ASSERT(false); + return Coord(); +} + +bool KLBoard::toBeRemoved(const Coord &c) const +{ + return ( field[c]==-1 ); +} + +void KLBoard::remove() +{ + BaseBoard::remove(); + updateRemoved(nbRemoved() + addRemoved); + updateScore(score() - addRemoved); +} + +bool KLBoard::toFall(const Coord &c) const +{ + Coord under(c.first, c.second-1); + return ( matrix()[under]==0 ); +} + +void KLBoard::computeInfos() +{ + BaseBoard::computeInfos(); + if ( graphic() ) computeNeighbours(); + empty.fill(true); + for (uint i=0; ibbi.nbFallStages); + + for (uint j=0; j heights(matrix().width()); + for (uint i=1; iupdate(); + if (final) computeInfos(); + return final; +} + +BaseBoard::AfterRemoveResult KLBoard::afterRemove(bool doAll, bool first) +{ + AfterRemoveResult res = Done; // dummy default + if (sliding) { + res = (doSlide(doAll, loop==bfactory->bbi.nbFallStages+1, false) ? Done + : NeedAfterRemove); + if ( res==Done ) sliding = false; + } else { + res = BaseBoard::afterRemove(doAll, first); + if ( res==Done ) { + res = NeedAfterRemove; + sliding = true; + loop++; + } + } + return res; +} + +bool KLBoard::afterAfterRemove() +{ + // check if there are remaining groups + field.fill(0); + QMemArray groups = findGroups(field, 2, true); + blocked = false; + return groups.size()!=0; +} diff --git a/klickety/board.h b/klickety/board.h new file mode 100644 index 00000000..d239f83d --- /dev/null +++ b/klickety/board.h @@ -0,0 +1,38 @@ +#ifndef KL_BOARD_H +#define KL_BOARD_H + +#include "base/board.h" +#include "base/piece.h" + +class KLBoard : public BaseBoard +{ + Q_OBJECT + public: + KLBoard(QWidget *parent); + + void start(const GTInitData &data); + + signals: + void firstBlockClicked(); + + private: + KGrid2D::Square field; + bool sliding; + QMemArray empty; + uint addRemoved; + bool blocked; + + KGrid2D::Coord findSprite(QCanvasSprite *) const; + AfterRemoveResult afterRemove(bool doAll, bool first); + bool afterAfterRemove(); + bool toBeRemoved(const KGrid2D::Coord &) const; + void remove(); + bool toFall(const KGrid2D::Coord &) const; + bool toSlide(const KGrid2D::Coord &) const; + bool doSlide(bool doAll, bool first, bool lineByLine); + void computeInfos(); + + void contentsMouseReleaseEvent(QMouseEvent *); +}; + +#endif diff --git a/klickety/eventsrc b/klickety/eventsrc new file mode 100644 index 00000000..912a83e6 --- /dev/null +++ b/klickety/eventsrc @@ -0,0 +1,249 @@ +[!Global!] +IconName=klickety +Comment=Klickety +Comment[ar]=لعبة Klickety +Comment[be]=Ðдбітак +Comment[bn]=কà§à¦²à¦¿à¦•à§‡à¦Ÿà¦¿ +Comment[hi]=के-लिकेटी +Comment[ne]=केलिकेटी +Comment[ta]=கேலிஙà¯à®•à®Ÿà¯à®Ÿà®¿ + +[removed] +Name=Line removed +Name[ar]=لقد أزيل الخط +Name[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Name[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Name[bn]=লাইন সরিয়ে ফেলা হয়েছে +Name[br]=Linenn lemet +Name[bs]=Uklonjena linija +Name[ca]=Línia eliminada +Name[cs]=OdstranÄ›n řádek +Name[cy]=Gwaredwyd llinell +Name[da]=Linje fjernet +Name[de]=Zeile entfernt +Name[el]=ΓÏαμμή αφαιÏέθηκε +Name[eo]=Linio forigita +Name[es]=Línea eliminada +Name[et]=Eemaldatud rida +Name[eu]=Lerroa kendu da +Name[fa]=خط حذ٠شد +Name[fi]=Rivi poistettu +Name[fr]=Ligne supprimée +Name[ga]=Líne bainte +Name[gl]=Liña eliminada +Name[he]=שורה הוסרה +Name[hi]=पंकà¥à¤¤à¤¿ हटाठ+Name[hr]=Uklonjena linija +Name[hu]=Sor eltávolítva +Name[is]=Lína fjarlægð +Name[it]=Riga rimossa +Name[ja]=線を消ã—ã¾ã—㟠+Name[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Name[lt]=Linija panaikinta +Name[lv]=Rinda noņemta +Name[mk]=ОтÑтранета е линија +Name[nb]=Linje fjernet +Name[nds]=Reeg wegdaan +Name[ne]=रेखा हटाइयो +Name[nl]=Regel verwijderd +Name[nn]=Linje fjerna +Name[pa]=ਸਤਰ ਹਟਾਈ +Name[pl]=Linia usuniÄ™ta +Name[pt]=Linha removida +Name[pt_BR]=Linha removida +Name[ro]=Linie eliminată +Name[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Name[se]=Linnjá lea eretváldon +Name[sk]=ÄŒiara odstránená +Name[sl]=Vrstica odstranjena +Name[sr]=Уклоњена линија +Name[sr@Latn]=Uklonjena linija +Name[sv]=Rad borttagen +Name[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Сатр хориҷ карда шуд +Name[tr]=Silinen Çizgiler +Name[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Name[zh_CN]=消去的行数 +Name[zh_TW]=消去的行 +Comment=Line removed +Comment[ar]=لقد أزيل الخط +Comment[be]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ñ‹Ð´Ð°Ð»ÐµÐ½Ð°Ñ +Comment[bg]=Премахната е Ð»Ð¸Ð½Ð¸Ñ +Comment[bn]=লাইন সরিয়ে ফেলা হয়েছে +Comment[br]=Linenn lemet +Comment[bs]=Uklonjena linija +Comment[ca]=Línia eliminada +Comment[cs]=OdstranÄ›n řádek +Comment[cy]=Gwaredwyd llinell +Comment[da]=Linje fjernet +Comment[de]=Zeile entfernt +Comment[el]=ΓÏαμμή αφαιÏέθηκε +Comment[eo]=Linio forigita +Comment[es]=Línea eliminada +Comment[et]=Eemaldatud rida +Comment[eu]=Lerroa kendu da +Comment[fa]=خط حذ٠شد +Comment[fi]=Rivi poistettu +Comment[fr]=Ligne supprimée +Comment[ga]=Líne bainte +Comment[gl]=Liña eliminada +Comment[he]=שורה הוסרה +Comment[hi]=पंकà¥à¤¤à¤¿ हटाठ+Comment[hr]=Uklonjena linija +Comment[hu]=Sor eltávolítva +Comment[is]=Lína fjarlægð +Comment[it]=Linea rimossa +Comment[ja]=線を消ã—ã¾ã—㟠+Comment[km]=បន្ទាážáŸ‹â€‹ážŠáŸ‚ល​បាន​យក​ចáŸáž‰ +Comment[lt]=Linija panaikinta +Comment[lv]=Rinda ir noņemta +Comment[mk]=ОтÑтранета е линија +Comment[nb]=Linje fjernet +Comment[nds]=Reeg wegdaan +Comment[ne]=रेखा हटाइयो +Comment[nl]=Regel verwijderd +Comment[nn]=Linje fjerna +Comment[pa]=ਸਤਰ ਹਟਾਈ +Comment[pl]=Linia usuniÄ™ta +Comment[pt]=Linha removida +Comment[pt_BR]=Linha removida +Comment[ro]=Linie eliminată +Comment[ru]=Ð›Ð¸Ð½Ð¸Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð° +Comment[se]=Linnjá lea eretváldon +Comment[sk]=ÄŒiara odstránená +Comment[sl]=Odstranjena vrstica +Comment[sr]=Уклоњена линија +Comment[sr@Latn]=Uklonjena linija +Comment[sv]=Rad borttagen +Comment[ta]=கோட௠நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Сатр хориҷ карда шуд +Comment[tr]=Silinen çizgiler +Comment[uk]=Ð›Ñ–Ð½Ñ–Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð° +Comment[zh_CN]=消去的行数 +Comment[zh_TW]=消去的行 +default_presentation=0 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over +Comment[be]=Канец гульні +Comment[bg]=Край на играта +Comment[bn]=খেল খতম +Comment[br]=Echu an abadenn +Comment[bs]=Kraj igre +Comment[ca]=Fi de la partida +Comment[cs]=Hra skonÄena +Comment[cy]=Gêm drosodd +Comment[da]=Spil forbi +Comment[de]=Das Spiel ist vorbei +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Comment[eo]=Ludo finita +Comment[es]=Fin de la partida +Comment[et]=Mäng läbi +Comment[eu]=Jokoa amaitu da +Comment[fa]=بازی تمام شد +Comment[fi]=Peli loppui +Comment[fr]=Fin de la partie +Comment[ga]=Cluiche thart +Comment[he]=×¡×™×•× ×ž×©×—×§ +Comment[hr]=Kraj igre +Comment[hu]=Vége a játéknak +Comment[is]=Leik lokið +Comment[it]=Gioco terminato +Comment[ja]=ゲームオーãƒãƒ¼ +Comment[km]=ល្បែង​ចប់ +Comment[lt]=Žaidimas baigtas +Comment[lv]=SpÄ“le beigusies +Comment[mk]=Играта заврши +Comment[nb]=Spillet er slutt +Comment[nds]=Speel vörbi +Comment[ne]=खेल समापà¥à¤¤ +Comment[nl]=Het spel is afgelopen +Comment[nn]=Spelet er slutt +Comment[pa]=ਖੇਡ ਖਤਮ +Comment[pl]=Koniec gry +Comment[pt]=Fim do jogo +Comment[pt_BR]=Fim do Jogo +Comment[ru]=Конец игры +Comment[se]=Speallu nogai +Comment[sk]=Koniec hry +Comment[sl]=Konec igre +Comment[sr]=Крај игре +Comment[sr@Latn]=Kraj igre +Comment[sv]=Spelet slut +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[tr]=Oyun bitti +Comment[uk]=Кінець гри +Comment[wa]=Li djeu est houte +Comment[zh_CN]=游æˆç»“æŸ +Comment[zh_TW]=éŠæˆ²çµæŸ +default_presentation=0 diff --git a/klickety/field.cpp b/klickety/field.cpp new file mode 100644 index 00000000..4b85a3fe --- /dev/null +++ b/klickety/field.cpp @@ -0,0 +1,80 @@ +#include "field.h" +#include "field.moc" + +#include +#include + +#include +#include +#include + +#include "base/board.h" + + +Field::Field(QWidget *parent) + : QWidget(parent, "field"), BaseField(this) +{ + KGameLCDList *sc = new KGameLCDList(i18n("Remaining blocks"), this); + showScore = new KGameLCD(3, sc); + sc->append(showScore); + QWhatsThis::add(sc, i18n("Display the number of remaining " + "blocks.
    " + "It turns blue" + " if it is a highscore " + "and red " + "if it is the best local score.
    ")); + lcds->addWidget(sc, 1, 0); + lcds->setRowStretch(2, 1); + + KGameLCDList *et = new KGameLCDList(i18n("Elapsed time"), this); + elapsedTime = new KGameLCDClock(et); + connect(board, SIGNAL(firstBlockClicked()), elapsedTime, SLOT(start())); + et->append(elapsedTime); + lcds->addWidget(et, 5, 0); + lcds->setRowStretch(6, 1); + + connect(board, SIGNAL(scoreUpdated()), SLOT(scoreUpdatedSlot())); + connect(board, SIGNAL(gameOverSignal()), SLOT(gameOver())); + + settingsChanged(); + connect(parent, SIGNAL(settingsChanged()), SLOT(settingsChanged())); + QTimer::singleShot(0, this, SLOT(start())); +} + +void Field::pause() +{ + if ( board->isGameOver() ) return; + bool paused = board->isPaused(); + if (paused) elapsedTime->start(); + else elapsedTime->stop(); + BaseField::pause(!paused); +} + +void Field::start() +{ + init(false, false, true, true, QString::null); + GTInitData data; + data.seed = kapp->random(); + BaseField::start(data); + elapsedTime->reset(); +} + +void Field::gameOver() +{ + elapsedTime->stop(); + stop(true); + BaseField::gameOver(currentScore(), this); +} + +KExtHighscore::Score Field::currentScore() const +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(board->score()); + score.setData("time", 3600 - elapsedTime->seconds()); + return score; +} + +bool Field::_isPaused() const +{ + return board->isPaused(); +} diff --git a/klickety/field.h b/klickety/field.h new file mode 100644 index 00000000..67e6529b --- /dev/null +++ b/klickety/field.h @@ -0,0 +1,33 @@ +#ifndef KL_FIELD_H +#define KL_FIELD_H + +#include + +#include "base/field.h" +#include "base/inter.h" + +class KGameLCDClock; + +class Field : public QWidget, public BaseField, public BaseInterface +{ + Q_OBJECT + public: + Field(QWidget *parent); + + private slots: + void scoreUpdatedSlot() { scoreUpdated(); } + void start(); + void gameOver(); + void settingsChanged() { BaseField::settingsChanged(); } + + private: + KGameLCDClock *elapsedTime; + + void pause(); + KExtHighscore::Score currentScore() const; + void _start() { start(); } + void _pause() { pause(); } + bool _isPaused() const; +}; + +#endif diff --git a/klickety/highscores.cpp b/klickety/highscores.cpp new file mode 100644 index 00000000..6c49952e --- /dev/null +++ b/klickety/highscores.cpp @@ -0,0 +1,27 @@ +#include "highscores.h" + +#include "base/factory.h" +#include "base/board.h" + + +using namespace KExtHighscore; + +KLHighscores::KLHighscores() +{ + Item *item = createItem(ScoreDefault); + setScoreItem(bfactory->bbi.width * bfactory->bbi.height + 1, item); + addScoreItem("time", createItem(ElapsedTime)); +} + +bool KLHighscores::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + return s1.data("time").toUInt()s2.score(); +} + +void KLHighscores::additionalQueryItems(KURL &url, const Score &s) const +{ + uint time = s.data("time").toUInt(); + addToQueryURL(url, "scoreTime", QString::number(time)); +} diff --git a/klickety/highscores.h b/klickety/highscores.h new file mode 100644 index 00000000..27548e69 --- /dev/null +++ b/klickety/highscores.h @@ -0,0 +1,18 @@ +#ifndef KL_HIGHSCORES_H +#define KL_HIGHSCORES_H + +#include "base/highscores.h" + + +class KLHighscores : public BaseHighscores +{ + public: + KLHighscores(); + + private: + bool isStrictlyLess(const KExtHighscore::Score &, + const KExtHighscore::Score &) const; + void additionalQueryItems(KURL &url, const KExtHighscore::Score &) const; +}; + +#endif diff --git a/klickety/klickety.desktop b/klickety/klickety.desktop new file mode 100644 index 00000000..dd93b3b2 --- /dev/null +++ b/klickety/klickety.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Name=Klickety +Name[ar]=لعبة استراتيجية (Klickety) +Name[be]=Ðдбітак +Name[bn]=কà§à¦²à¦¿à¦•à§‡à¦Ÿà¦¿ +Name[hi]=के-लिकेटी +Name[ne]=केलिकेटि +Name[ta]=கேலிஙà¯à®•à®Ÿà¯à®Ÿà®¿ +Name[zu]=I-Klickety +GenericName=Board Game +GenericName[af]=Bord Speletjie +GenericName[ar]=لعبة لوح +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=Табла +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula +GenericName[cs]=Desková Same +GenericName[cy]=Gêm Fwrdd +GenericName[da]=Brætspil +GenericName[de]=Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι +GenericName[eo]=Tabuloludo +GenericName[es]=Juego de tablero +GenericName[et]=Lauamäng +GenericName[eu]=Mahai-jokoa +GenericName[fa]=بازی تخته +GenericName[fi]=Lautapeli +GenericName[fo]=Borðspæl +GenericName[fr]=Jeu de plateau +GenericName[ga]=Cluiche Chláir +GenericName[gl]=Xogo de Taboleiro +GenericName[he]=משחק לוח +GenericName[hi]=बिसात का खेल +GenericName[hr]=Igra na ploÄi +GenericName[hu]=Táblajáték +GenericName[is]=Borðleikur +GenericName[it]=Gioco da tavolo +GenericName[ja]=ボードゲーム +GenericName[km]=ល្បែង​ក្ដារ +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Stalo žaidimas +GenericName[lv]=Galda spÄ“le +GenericName[mk]=Игра на табла +GenericName[mt]=Logħba tal-bord +GenericName[nb]=Brettspill +GenericName[nds]=Brettspeel +GenericName[ne]=बोरà¥à¤¡ खेल +GenericName[nl]=Bordspel +GenericName[nn]=Brettspel +GenericName[pa]=ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa +GenericName[pt]=Jogo de Tabuleiro +GenericName[pt_BR]=Jogo de Tabuleiro +GenericName[ro]=Un joc de table +GenericName[ru]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino w'Ikibaho +GenericName[se]=Duolbbášspeallu +GenericName[sk]=Stolová hra +GenericName[sl]=Namizna igra +GenericName[sr]=Игра на табли +GenericName[sr@Latn]=Igra na tabli +GenericName[sv]=Brädspel +GenericName[ta]=பலகை விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Рӯи Мизӣ +GenericName[th]=เà¸à¸¡à¸à¸£à¸°à¸”าน +GenericName[tr]=Tahta Oyunu +GenericName[uk]=Гра на дошці +GenericName[uz]=Stol oÊ»yini +GenericName[uz@cyrillic]=Стол ўйини +GenericName[ven]=Mutambo wa Bodo +GenericName[vi]=Game bàn +GenericName[wa]=Djeu d' platea +GenericName[xh]=Umdlalo Webhodi +GenericName[zh_CN]=æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=棋盤éŠæˆ² +GenericName[zu]=Umdlalo webhodi +Icon=klickety +Exec=klickety -caption "%c" %i %m +Type=Application +DocPath=klickety/index.html +Comment= +Categories=Qt;KDE;Game;StrategyGame; diff --git a/klickety/klicketyui.rc b/klickety/klicketyui.rc new file mode 100644 index 00000000..2571e1ef --- /dev/null +++ b/klickety/klicketyui.rc @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/klickety/main.cpp b/klickety/main.cpp new file mode 100644 index 00000000..da6b0afe --- /dev/null +++ b/klickety/main.cpp @@ -0,0 +1,73 @@ +#include "main.h" +#include "main.moc" + +#include +#include +#include +#include + +#include "base/settings.h" +#include "piece.h" +#include "highscores.h" + +//----------------------------------------------------------------------------- +const MainData MAIN_DATA = { + "klickety", + I18N_NOOP("Klickety"), + I18N_NOOP("Klickety is an adaptation of the \"clickomania\" game"), + "http://klickety.sourceforge.net/", + I18N_NOOP("Removed blocks"), + "1.0.3", + "1.0.3 (5 August 2004)" +}; + +const uint HISTOGRAM_SIZE = 16; +const uint HISTOGRAM[HISTOGRAM_SIZE] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 160 +}; + +const BaseBoardInfo BASE_BOARD_INFO = { + 10, 16, false, // width, height, with pieces + + 50, // before remove time + 10, // after removed time + 3, // nb toggles + 7, // nb partial fall stages + + 0, // nb arcade stages + + HISTOGRAM, HISTOGRAM_SIZE, true, // score is bound +}; + +KLFactory::KLFactory() + : BaseFactory(MAIN_DATA, BASE_BOARD_INFO) +{ + _aboutData->addCredit("Dan Hill", I18N_NOOP("Icons")); +} + + +//----------------------------------------------------------------------------- +KLMainWindow::KLMainWindow() +{ + Field *field = static_cast(_inter); + buildGUI(field); +} + +//----------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + KHighscore::init(MAIN_DATA.appName); + KLFactory flf; + flf.init(argc, argv); + + KLPieceInfo pieceInfo; + KLHighscores highscores; + + if ( kapp->isRestored() ) RESTORE(KLMainWindow) + else { + KLMainWindow *mw = new KLMainWindow; + kapp->setMainWidget(mw); + mw->show(); + } + return kapp->exec(); +} diff --git a/klickety/main.h b/klickety/main.h new file mode 100644 index 00000000..e5b335f8 --- /dev/null +++ b/klickety/main.h @@ -0,0 +1,29 @@ +#ifndef KL_MAIN_H +#define KL_MAIN_H + +#include "base/main.h" +#include "base/factory.h" +#include "board.h" +#include "field.h" + + +class KLFactory : public BaseFactory +{ + public: + KLFactory(); + + protected: + virtual BaseBoard *createBoard(bool, QWidget *parent) + { return new KLBoard(parent); } + virtual BaseInterface *createInterface(QWidget *parent) + { return new Field(parent); } +}; + +class KLMainWindow : public BaseMainWindow +{ + Q_OBJECT + public: + KLMainWindow(); +}; + +#endif diff --git a/klickety/pics/Makefile.am b/klickety/pics/Makefile.am new file mode 100644 index 00000000..c471f62b --- /dev/null +++ b/klickety/pics/Makefile.am @@ -0,0 +1,3 @@ + +KDE_ICON= AUTO + diff --git a/klickety/pics/hi128-app-klickety.png b/klickety/pics/hi128-app-klickety.png new file mode 100644 index 0000000000000000000000000000000000000000..7e9ee5a51a98b6f700b1ae1ae70384ba40d5c25c GIT binary patch literal 9711 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 literal 0 HcmV?d00001 diff --git a/klickety/pics/hi22-app-klickety.png b/klickety/pics/hi22-app-klickety.png new file mode 100644 index 0000000000000000000000000000000000000000..f1c969a1fcafc1e78f075fd944f7d6d2acbba7a6 GIT binary patch literal 1089 zcmV-H1it%;P)z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v&w@E}nRCwC7mAj5yMG!zwRX^@y9{Uon_z?NB?-JM;}?9R;X+g${A)H zQlEOO#LRdVqP!vz2mbw=uYJ6Ku)kAEG2y=vI5EyvUVkiaPeK*NuEMnKwf!QZdUkSl zes=oJ=OS?J!TtNc-TUa?bhBEa7$R_GK!D1_qr(*sZ`|k3TQ|s?1ZG$<$OX#=eW8wl zhhKf$ef{8rH#|_6+@;m|G2MENiXj-Xg(3zX(q+feHZ0lDvjtnh%%Bt~g>~+s${kR7 z1sARNEKXt5fowg#Iso2?T~FsdFEo(VfGt=I$e2MWf;H0s)7C45+Bz@GIocfAc2v(nj@U*Xr{_$;%ErSmm%7z&ph0OSnrj9;Y4B1+amVA@$HF$O&>c zl)LlSfl2LEtX1#GemgFGCg(h)LWVY716^5jw|s(?7V8T+8)XaalhOB3vT$~*=k(5> zwU46Bep|RU9*Pz{cn2EXR`H;YkP7d;^9AwE!P~wJ9;Y6<44X8(Jzc}2FKK3@lzM0lWJKegP}LPM9l>-Dc6MMghITX!ARyXRL`GGHVuWINyTZF!p+c#Ez*z zJ)-f~$1b|bHA*u>M>Ck*fXM-jrVz)VHI$msUd_P>bhV<<7gX+?kvm}{e{)%mnWty0 zHqTk43+jWDiN9XvKK%3uoyz*=%T$`5d191y7DhCmTNf5O~+PJ1e|CGj92P zS`Jv~Qy%99hsVaxr^d5Y4*rLw9)0$`{91fMsw0zX4k1#2()Hv`BCivRb>X|%$>kyT zNQM@VazYy}h}9*%bu3me@6gRJ{4T&0nElV|?o?jYGW}-&Q}?fr5Cj6!00000NkvXX Hu0mjftT*fT literal 0 HcmV?d00001 diff --git a/klickety/pics/hi32-app-klickety.png b/klickety/pics/hi32-app-klickety.png new file mode 100644 index 0000000000000000000000000000000000000000..d2bec9385cbc1bc354dd996e4963e82425bb045e GIT binary patch literal 1716 zcmV;l221&gP)yJ&z+d7Lx6U5D^V6UF_=oB26t5Kl1Vf~)E4Q?MyVTGrzxvHn-h2B;eC2CzQsx2< zxZj)GvtA9%p*k@N@Bd-q7e9L^905WVPCYu+4&Y4!#S!N}0$5dw&y=npClxu=+n_kO zwR1OX0M&7GEC^wo5vdz@h(L&{+4nol7jQ$|F>z#l1EdA_+jGE#RCgQ@c2y`YU@NY9 z%b+=K0#(|3RVNsf5Kz^j))gS7(966BW-vo=L>v)N06D54$Waa#hI&JdfqjV7;&@mC zi-9;UM{HROL{N8X3W%Hx&;Vi*$vWSFDdL6*Ex0%$a;t*EoeB+;tpo5{uLlND1|vjrT`XWlU`e{sSaYe zJMIQ*xKzZ2+66@N0K^D{lN38CQ(GBF?*SZV?m--jRK0Kj7J&-53V8cdlV0&);DW2* z0mPgoIw9w7na~KtAxaJu+(4)(tNhB^l6>MEfq*mA9j3-4U@M%{fL)~(r07}`6-TJW z*y9jWe^emmX*-m^{}J7cXg{KThX!p!1yMn4ooQ_r-0Agz1(*YPSUb?b70epc;xJcb zzX(}0!xKbp)OF1P$fVL_z6h&__hXO4APTY`pk_w&v=>9UCXVky)U=CNE!A zmUBmE!;igR*W$0u*SrD+q_pckfyikOqzFV6vNykIHMHhb0M&cubF=`5SwPB5o^;2Z5z++1r2LE z2zFz9ESRv`2 zj*4laN~O~Lz6z+VJ_1NRZfFfGceE~0eCVTTO6_J00(}R455oXmhTK6u0>m{H0&}6G zHUJi?954>laGbWa%vAPN_5|5c;sF)E=U~mKRH5roGMgBKk9~3_33MCSY+y6OutN0xnu^>Mi1DsE zuNSgKdW_0~F;SQ(>`*$CEfRKj`mY@n0_2Pm8KSzToOFHb6F6BRjwROmu?}}TM0y=ODK^s2E315giVBT1Dksgw`u4bL^`G+GzCs( z5;HwQVWKh-*b~S|+h`Hi?Od<1U~R`%=sSeO5Iab*i!g*1Wawb%+Zcy7`o4{vn+hR7 zR(uFV@5q3V)}XrXg%zjZECdcs89h+fCdvs2^l{VWaeVj?9*r%M0c(VOn{xsc<%p@vY54 z9@dHJsS?I3(zU@E@F2h#p;Jf(OwC;?cvW0G`jYt57h#r#E*$8(1368^JQ32aIgTf4 zIc0ZHUhW5;?{0X0Gx5RmJ#j24=4CI#$A2SlK?mTqM=-wu_BwC|*1@*I^)*~v!1+0H zevVvLwy)sl--W{y`0@kz{A=*JNAN}mk2z)FGZfkLDm~?SHJ%mt=M6l&guh%snUT0R z<)bTQTnwEOSVX1;F1B#u&~}H2uFW7a$UOm9KJ3iXNZkf5da%2Y=SMcF8IO&xQ zr;MQ^C1D98ySnA_1}?7Q{2F%C>WmNJJ74PM2WE8OuE$URY}?uZ0000< KMNUMnLSTZLWe>ps literal 0 HcmV?d00001 diff --git a/klickety/pics/hi48-app-klickety.png b/klickety/pics/hi48-app-klickety.png new file mode 100644 index 0000000000000000000000000000000000000000..d4d61bac5610b0c589663a0d3f317ac3a756b92d GIT binary patch literal 3000 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 IM6N<$f_hhkAOHXW literal 0 HcmV?d00001 diff --git a/klickety/piece.cpp b/klickety/piece.cpp new file mode 100644 index 00000000..4a33910b --- /dev/null +++ b/klickety/piece.cpp @@ -0,0 +1,52 @@ +#include "piece.h" + +#include +#include +#include "base/board.h" + + +const char *KLPieceInfo::DEFAULT_COLORS[NB_BLOCK_TYPES] = { + "#C86464", "#64C864", "#6464C8", "#C8C864", "#C864C8" +}; + +QColor KLPieceInfo::defaultColor(uint i) const +{ + if ( i>=nbColors() ) return QColor(); + return QColor(DEFAULT_COLORS[i]); +} + +QString KLPieceInfo::colorLabel(uint i) const +{ + return i18n("Color #%1:").arg(i+1); +} + +void KLPieceInfo::draw(QPixmap *pixmap, uint blockType, uint bMode, + bool lighted) const +{ + QColor col = color(blockType); + if (lighted) col = col.light(); + pixmap->fill(col); + + QPainter p(pixmap); + QRect r = pixmap->rect(); + + p.setPen(col.dark()); + if ( !(bMode & BaseBoard::Up) ) + p.drawLine(r.topLeft(), r.topRight()); + if ( !(bMode & BaseBoard::Down) ) + p.drawLine(r.bottomLeft(), r.bottomRight()); + if ( !(bMode & BaseBoard::Left) ) + p.drawLine(r.topLeft(), r.bottomLeft()); + if ( !(bMode & BaseBoard::Right) ) + p.drawLine(r.topRight(),r.bottomRight()); + + p.setPen(col.dark(110)); + if (bMode & BaseBoard::Up) + p.drawLine(r.topLeft()+QPoint(1,0), r.topRight()+QPoint(-1,0)); + if (bMode & BaseBoard::Down) + p.drawLine(r.bottomLeft()+QPoint(1,0), r.bottomRight()+QPoint(-1,0)); + if (bMode & BaseBoard::Left) + p.drawLine(r.topLeft()+QPoint(0,1), r.bottomLeft()+QPoint(0,-1)); + if (bMode & BaseBoard::Right) + p.drawLine(r.topRight()+QPoint(0,1), r.bottomRight()+QPoint(0,-1)); +} diff --git a/klickety/piece.h b/klickety/piece.h new file mode 100644 index 00000000..b7d192f0 --- /dev/null +++ b/klickety/piece.h @@ -0,0 +1,42 @@ +#ifndef KL_PIECE_H +#define KL_PIECE_H + +#include "base/piece.h" + + +class KLPieceInfo : public GPieceInfo +{ + public: + KLPieceInfo() {} + + virtual uint nbBlocks() const { return 0; } + virtual uint nbTypes() const { return NB_BLOCK_TYPES; } + virtual uint nbForms() const { return 0; } + + virtual const int *i(uint, uint) const { return 0; } + virtual const int *j(uint, uint) const { return 0; } + virtual uint value(uint, uint) const { return 0; } + virtual uint form(uint) const { return 0; } + virtual uint nbConfigurations(uint) const { return 0; } + + virtual uint nbNormalBlockTypes() const { return NB_BLOCK_TYPES; } + virtual uint nbGarbageBlockTypes() const { return 0; } + virtual uint nbBlockModes() const { return 1+4+6+4+1; } + + virtual uint nbColors() const { return NB_BLOCK_TYPES; } + virtual QString colorLabel(uint i) const; + virtual QColor defaultColor(uint i) const; + + protected: + void draw(QPixmap *, uint blockType, uint blockMode, + bool lighted) const; + + private: + static const uint NB_BLOCK_TYPES = 5; + static const char *DEFAULT_COLORS[NB_BLOCK_TYPES]; +}; + +#endif + + + diff --git a/klines/AUTHORS b/klines/AUTHORS new file mode 100644 index 00000000..99eadd5e --- /dev/null +++ b/klines/AUTHORS @@ -0,0 +1,2 @@ +Roman Merzlyakov +Roman Razilov diff --git a/klines/Makefile.am b/klines/Makefile.am new file mode 100644 index 00000000..37c8a6ec --- /dev/null +++ b/klines/Makefile.am @@ -0,0 +1,27 @@ + +INCLUDES= -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = klines +klines_SOURCES = prompt.cpp mwidget.cpp linesboard.cpp field.cpp cell.cpp \ + ballpainter.cpp klines.cpp main.cpp prefs.kcfgc +klines_LDFLAGS = $(all_libraries) $(KDE_RPATH) +klines_LDADD = $(LIB_KDEGAMES) +klines_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +picsdir = $(kde_datadir)/klines/ +pics_DATA = balls.jpg field.jpg fire.jpg + +xdg_apps_DATA = klines.desktop +kde_kcfg_DATA = klines.kcfg + +EXTRA_DIST = $(pics_DATA) + +KDE_ICON = klines + +rcdir = $(kde_datadir)/klines +rc_DATA = klinesui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/klines.pot; + diff --git a/klines/ballpainter.cpp b/klines/ballpainter.cpp new file mode 100644 index 00000000..06380a6f --- /dev/null +++ b/klines/ballpainter.cpp @@ -0,0 +1,140 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +//#include "shotcounter.h" +#include +#include "linesboard.h" +//#include +#include +#include +#include +#include + +#include "prefs.h" + +#define PIXSIZE (CELLSIZE - 2) + +int colorLinesArr[NCOLORS] = + {0x0000ff, 0x00ff00, 0xff0000, 0x00ffff, 0xff00ff, 0xffff00, 0x005080}; + // 0x00bbggrr + // red , green , blue , yellow , violet , cyan , brown + + + +BallPainter::BallPainter() + : QObject(), backgroundPix(0) +{ + createPix(); +} + +BallPainter::~BallPainter() +{ + deletePix(); +} + +void BallPainter::deletePix() +{ + delete backgroundPix; + for(int c=0; cisNull() ||backgroundPix->isNull() || fire->isNull() ) { + KMessageBox::error(0, i18n("Unable to find graphics. Check your installation."), i18n("Error")); + qApp->exit(1); + return; // Error + } + + for(int c=0; c=NCOLORS) || (animstep<0) || color == NOBALL ){ + return *backgroundPix; + } + if ( panim == ANIM_JUMP ) + { + if ( ( animstep < 0 ) || ( animstep >= PIXTIME ) ) + return *backgroundPix; + else + return *imgCash[color][animstep]; + } + else if ( panim == ANIM_BURN ) + { + if ( animstep < FIREBALLS ) + return *imgCash[color][animstep + PIXTIME + BOOMBALLS + 1]; + else if ( animstep < FIREBALLS + FIREPIX ) + return *firePix[animstep - FIREBALLS]; + } + else if ( panim == ANIM_BORN ) + { + if ( animstep < BOOMBALLS ) + return *imgCash[color][animstep + PIXTIME]; + else + return *imgCash[color][NORMALBALL]; + } + // rest is not imlemented yet + return *imgCash[color][NORMALBALL]; + +} + +#include "ballpainter.moc" diff --git a/klines/ballpainter.h b/klines/ballpainter.h new file mode 100644 index 00000000..a0ad2cbb --- /dev/null +++ b/klines/ballpainter.h @@ -0,0 +1,47 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef BALLPAINTER_H +#define BALLPAINTER_H + +#include +#include +#include "cfg.h" + +#define CELLSIZE 32 + +class BallPainter : public QObject +{ + Q_OBJECT + QPixmap* imgCash[NCOLORS][PIXTIME + FIREBALLS + BOOMBALLS + 1]; + QPixmap* backgroundPix; + QPixmap* firePix[FIREPIX]; + + +public: + BallPainter(); + ~BallPainter(); + + void deletePix(); + void createPix(); + + QPixmap GetBall( int color, int animstep, int panim ); + QPixmap GetNormalBall(int color) { return GetBall(color,0,ANIM_NO); } + QPixmap GetBackgroundPix() { return GetBall(NOBALL,0,ANIM_NO); } +}; + +#endif diff --git a/klines/balls.jpg b/klines/balls.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67a3aaf5271695759becf58346d6d82836a04c6f GIT binary patch literal 73953 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 qESLUy8B@1TyZ-^s(w>H6=mw5W@r_$WJ< +#include "cfg.h" +#include "field.moc" + +Field::Field(QWidget* parent, const char* name) + : QWidget( parent, name ) +{ + clearField(); +} + +Field::~Field() +{ +} + +void Field::clearField() +{ + for(int y=0; y=0) && (x=0) && (y= NUMCELLSW) || (iy >= NUMCELLSH)) + continue; + int b = field[iy][ix].getColor(); +//printf("[%d,%d]%d", ix,iy,b); + if (b == NOBALL) + { + run++; + empty++; + } + else if (b == current_color) + { + run++; + score++; + empty = 0; + } + else + { + run = empty+1; + score = 1; + current_color = b; + empty = 0; + } + if (run > lpr) + { + lpr = run; + lprscore = score; + } + } + if (lpr < 5) + lprscore = -10; + else + lprscore = lprscore*lprscore; +// printf("= %d\n", lprscore); + return lprscore; +} + +int Field::calcPosScore(int x, int y, int whatIf) +{ + int score = -10; + int color = field[y][x].getColor(); + field[y][x].setColor(whatIf); + score = QMAX(score, calcRun(x, y-4, 0, 1)); + score = QMAX(score, calcRun(x-4, y-4, 1, 1)); + score = QMAX(score, calcRun(x-4, y, 1, 0)); + score = QMAX(score, calcRun(x-4, y+4, 1, -1)); + field[y][x].setColor(color); + return score; +} + diff --git a/klines/field.h b/klines/field.h new file mode 100644 index 00000000..de9ee8ce --- /dev/null +++ b/klines/field.h @@ -0,0 +1,65 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef FIELD_H +#define FIELD_H + +#include +#include +#include "cell.h" +// size of game field +#define NUMCELLSW 9 +#define NUMCELLSH 9 + +class Field: public QWidget +{ + Q_OBJECT +public: + void clearField(); + + int calcPosScore(int x, int y, int whatIf); + void saveUndo(); + +protected: + Field(QWidget* parent, const char* name); + ~Field(); + + void putBall(int x, int y, int color); + void putBallRun(int x, int y, int color); + void removeBall(int x, int y ); + int getBall(int x, int y); + int getAnim(int x, int y); + void setAnim(int x, int y, int anim ); + void clearAnim(); + int deleteAnimatedBalls(); + void moveBall(int xa, int ya, int xb, int yb); + int calcRun(int sx, int sy, int dx, int dy); + + + bool checkBounds( int x, int y ); +// virtual int erase5Balls(){ return 0;} + int freeSpace(); + void restoreUndo(); + +private: + Cell field[NUMCELLSH][NUMCELLSW]; + Cell field_undo[NUMCELLSH][NUMCELLSW]; +// void search5Balls(); + +}; + +#endif diff --git a/klines/field.jpg b/klines/field.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d021b6fadf6acb7b9e91d3bd061c274a44b9fb68 GIT binary patch literal 338 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 literal 0 HcmV?d00001 diff --git a/klines/hi128-app-klines.png b/klines/hi128-app-klines.png new file mode 100644 index 0000000000000000000000000000000000000000..4784a9ecd5d22205ae8a4e5336048e698a15c89c GIT binary patch literal 10828 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 P00000NkvXXu0mjf?7fTb literal 0 HcmV?d00001 diff --git a/klines/hi22-app-klines.png b/klines/hi22-app-klines.png new file mode 100644 index 0000000000000000000000000000000000000000..cb456ae32fad010fbd0d0133263b4e29048ccfff GIT binary patch literal 1281 zcmV+c1^)VpP)z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v(cS%G+RCwB?l}l`uMHI(>XJ)?d-rJT^%C!{`P*Wg4 z4T(_!fuMq06BBizD~*~M7aBM24LePYMvQS`q6-s|1Y@F!@c{`B5kbZBP+LkLlv3K< z-nRFC_w~&j7kvU>d6j7fp5^5DhWR7uAd@UilZHy!x$20jI|^;Mi@PH44+gHV{ix<0$NJQ{#wUx@;EDmK_&Xbk=6~QU{q&C6P?x5 z7zAh}Wu^zNQ0(u6w2reaz?{YwkM||wAF_mz6nlHX|6(ACQL9j?Q;HghD%rXvH345L zQW?EQarhdJ6q8$qbp{v0Jg{I0grp3n!rGZTXgFN>Lp;5!W z`z`sO3QPw>dHb`p?!Sk^i7)9G_y!ZjSeXSj^Ig%HnPyFgW$R-*SanYaTlZ~dmA}qd zW00}u+hJ`P>1r_XVjizE^lpER%&TwH{KRuizWgx*EAJ;0Dhm#E3ZRNFm1qf*aPdv3 z)gkLxeV5Wxjj6W%&UHDDSjDtK90^0+J2_o~Xwoy&ww&FY9w2k_I7D-XK?G3JiR%pf zew8%z!Kz_ASLq*2Xd5wXJ``BnlwrNgvf+@Dn-T85a)hbdU~G$}sWC+Nm9J3e7COew zKOQbr7$1o^{&AhwF2i(hgTiP`yeyFaPzhfY!7FAw!nbAEec}VUikB&@yPNeten(4T zn2^a}Qbmzl&T~dFl5i<~m?VCRDBr^!V;^CHr>QMw^gpcd!K%U_Q#(AgcW(=)WO3P z@{WPGAETpd8znzQ`JK<%`pGaR4!3{>B~F#GuI(IMp6BFHN-g79-MyFnKOdkqa|zD8 z0UBs>Ra$dG|7Kx)?FFjE3;3KNez1mos&hsHqX zyasJoAwS5?Yr~WhSl!p4t<;ZO)lZzi!s-h*i5p<7%rW*}n%mo@vwI62)hcfteVrSV zJ82ymVomuR=|g*%t_fpTo4^T^g)*JH6`R7kPa~=iHQ|OEahe#b!MK2U*zp2QeA_N4}t8lmg1~e-eCo(Z!OTB1S rfSg5kDB21H{zoVngH$|v`tJb%u$&FwX&Iio00000NkvXXu0mjfWpg(O literal 0 HcmV?d00001 diff --git a/klines/hi32-app-klines.png b/klines/hi32-app-klines.png new file mode 100644 index 0000000000000000000000000000000000000000..e38cb1a53a4a1e436a0e922450ec6076c926fc8a GIT binary patch literal 2070 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 IM6N<$f+M^Cf&c&j literal 0 HcmV?d00001 diff --git a/klines/hi64-app-klines.png b/klines/hi64-app-klines.png new file mode 100644 index 0000000000000000000000000000000000000000..0efed5879b7397e7954bd02156bcb873e8d216ef GIT binary patch literal 4483 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "prefs.h" +#include +#include "klines.moc" + +enum { Nb_Levels = 5 }; +static const char *LEVEL[Nb_Levels] = { + I18N_NOOP("Very Easy"), I18N_NOOP("Easy"), I18N_NOOP("Normal"), I18N_NOOP("Hard"), + I18N_NOOP("Very Hard") +}; + + +/* + Creates the KLines widget and sets saved options (if any). +*/ + +KLines::KLines() +{ + mwidget = new MainWidget(this); + setCentralWidget( mwidget ); + + lsb = mwidget->GetLsb(); + connect(lsb, SIGNAL(endTurn()), this, SLOT(makeTurn())); + connect(lsb, SIGNAL(eraseLine(int)), this, SLOT(addScore(int))); + connect(lsb, SIGNAL(endGame()), this, SLOT(endGame())); + connect(lsb, SIGNAL(userTurn()), this, SLOT(userTurn())); + + lPrompt = mwidget->GetPrompt(); + + score = 0; + score_undo = 0; + bDemo = false; + + statusBar()->insertItem(i18n(" Score:"), 1, 1); + statusBar()->setItemAlignment(1, AlignVCenter | AlignLeft); + statusBar()->insertItem(i18n(" Level: "), 0, 1); + statusBar()->setItemAlignment(0, AlignVCenter | AlignLeft); + + initKAction(); + + connect(&demoTimer, SIGNAL(timeout()), this, SLOT(slotDemo())); + + setFocusPolicy(StrongFocus); + setFocus(); + + startGame(); +} + +/* + Saves the options and destroys the KLines widget. +*/ +KLines::~KLines() +{ + Prefs::setLevel(levelAction->currentItem()-2); + Prefs::writeConfig(); +} + +/* + Init KAction objects (menubar, toolbar, shortcuts) +*/ +void KLines::initKAction() +{ + KStdGameAction::gameNew(this, SLOT(startGame()), actionCollection()); + act_demo = KStdGameAction::demo(this, SLOT(startDemo()), actionCollection()); + act_demo->setText(i18n("Start &Tutorial")); + KStdGameAction::highscores(this, SLOT(viewHighScore()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + endTurnAction = KStdGameAction::endTurn(this, SLOT(makeTurn()), actionCollection()); + showNextAction = new KToggleAction(i18n("&Show Next"), KShortcut(CTRL+Key_P), + this, SLOT(switchPrompt()), actionCollection(), "options_show_next"); + showNextAction->setCheckedState(i18n("Hide Next")); + showNumberedAction = new KToggleAction(i18n("&Use Numbered Balls"), KShortcut(), + this, SLOT(switchNumbered()), actionCollection(), "options_show_numbered"); + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + + levelAction = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList items; + for (uint i=0; isetItems(items); + + levelAction->setCurrentItem(Prefs::level()+2); + showNextAction->setChecked(Prefs::showNext()); + showNumberedAction->setChecked(Prefs::numberedBalls()); + lPrompt->setPrompt(Prefs::showNext()); + + (void)new KAction(i18n("Move Left"), Key_Left, lsb, SLOT(moveLeft()), actionCollection(), "left"); + (void)new KAction(i18n("Move Right"), Key_Right, lsb, SLOT(moveRight()), actionCollection(), "right"); + (void)new KAction(i18n("Move Up"), Key_Up, lsb, SLOT(moveUp()), actionCollection(), "up"); + (void)new KAction(i18n("Move Down"), Key_Down, lsb, SLOT(moveDown()), actionCollection(), "down"); + (void)new KAction(i18n("Move Ball"), Key_Space, lsb, SLOT(placePlayerBall()), actionCollection(), "place_ball"); + + setupGUI( KMainWindow::Save | Keys | StatusBar | Create ); +} + +void KLines::startGame() +{ + score = 0; + score_undo = 0; + bUndo = true; + bNewTurn = true; + if(bDemo) + stopDemo(); + + bFirst = true; + + int level = levelAction->currentItem()-2; + setLevel(level); + + lsb->setLevel(level); + lsb->setGameOver(false); + lsb->clearField(); + generateRandomBalls(); + placeBalls(); + generateRandomBalls(); + undoAction->setEnabled(false); + endTurnAction->setEnabled(true); + updateStat(); +} + +void KLines::setLevel(int level) { + levelStr = i18n(LEVEL[level+2]); + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); +} + +void KLines::startDemo() +{ + if (bDemo) + { + stopDemo(); + return; + } + score = 0; + score_undo = 0; + bUndo = true; + bNewTurn = true; + bDemo = true; + act_demo->setText(i18n("Stop &Tutorial")); + bFirst = true; + + levelStr = i18n("Tutorial"); + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); + + lsb->startDemoMode(); + lsb->setGameOver(false); + lsb->clearField(); + lsb->update(); + undoAction->setEnabled(false); + endTurnAction->setEnabled(false); + generateRandomBalls(); + + demoStep = 0; + demoTimer.start(1000, true); +} + +void KLines::stopDemo() +{ + bDemo = false; + lsb->hideDemoText(); + demoTimer.stop(); + statusBar()->changeItem(i18n(" Level: %1").arg(i18n("Tutorial - Stopped")), 0); + act_demo->setText(i18n("Start &Tutorial")); +} + +void KLines::slotDemo() +{ + bool newBalls = false; + int ballColors = -1; + int clickX = 0; + int clickY = 0; + QString msg; + demoStep++; + if ((demoStep % 2) == 0) + { + lsb->hideDemoText(); + demoTimer.start(1000, true); + return; + } + if (demoStep == 1) + { + msg = i18n("The goal of the game is to put\n" + "5 balls of the same color in line."); + } + else if (demoStep == 3) + { + newBalls = true; + } + else if (demoStep == 5) + { + msg = i18n("You can make horizontal, vertical\n" + "and diagonal lines."); + } + else if (demoStep == 7) + { + newBalls = true; + } + else if (demoStep == 9) + { + msg = i18n("Each turn, three new balls are placed on the board."); + } + else if (demoStep == 11) + { + newBalls = true; + } + else if (demoStep == 13) + { + msg = i18n("Every turn, you can move one ball."); + } + else if (demoStep == 15) + { + newBalls = true; + ballColors = 56; + } + else if (demoStep == 17) + { + msg = i18n("To move a ball, click on it with the mouse,\n" + "then click where you want the ball to go."); + } + else if (demoStep == 19) + { + clickX = 6; + clickY = 6; + } + else if (demoStep == 21) + { + clickX = 6; + clickY = 9; + } + else if (demoStep == 23) + { + msg = i18n("You just moved the blue ball!"); + } + else if (demoStep == 25) + { + newBalls = true; + } + else if (demoStep == 27) + { + msg = i18n("Balls can be moved to every position on the board,\n" + "as long as there are no other balls in their way."); + } + else if (demoStep == 29) + { + clickX = 4; + clickY = 3; + demoStep++; + } + else if (demoStep == 31) + { + clickX = 7; + clickY = 9; + } + else if (demoStep == 33) + { + msg = i18n("Now we only need one more blue ball."); + } + else if (demoStep == 35) + { + newBalls = true; + } + else if (demoStep == 37) + { + msg = i18n("It seems to be our lucky day!"); + } + else if (demoStep == 39) + { + clickX = 8; + clickY = 2; + demoStep++; + } + else if (demoStep == 41) + { + clickX = 8; + clickY = 9; + } + else if (demoStep == 43) + { + msg = i18n("Hurray! And away they go!\n" + "Now lets try the green balls."); + } + else if (demoStep == 45) + { + clickX = 8; + clickY = 7; + demoStep++; + } + else if (demoStep == 47) + { + clickX = 4; + clickY = 5; + lsb->demoAdjust(42); + } + else if (demoStep == 49) + { + newBalls = true; + } + else if (demoStep == 51) + { + msg = i18n("Now you try!\n" + "Click on the green ball and move it to the others!"); + demoStep++; + } + else if (demoStep == 53) + { + lsb->hideDemoText(); + lsb->adjustDemoMode(true, false); + demoStep++; + } + else if (demoStep == 55) + { + msg = i18n("Almost, try again!"); + demoStep -= 4; + } + else if (demoStep == 57) + { + msg = i18n("Very good!"); + } + else if (demoStep == 59) + { + msg = i18n("Whenever you complete a line you get an extra turn."); + } + else if (demoStep == 61) + { + msg = i18n("This is the end of this tutorial.\n" + "Feel free to finish the game!"); + demoStep++; + } + else if (demoStep == 63) + { + lsb->hideDemoText(); + lsb->adjustDemoMode(true, true); + bDemo = false; + act_demo->setText(i18n("Start &Tutorial")); + } + + if (!msg.isEmpty()) + { + lsb->showDemoText(msg); + demoTimer.start(3500 + msg.contains("\n")*1500, true); + return; + } + if (newBalls) + { + placeBalls(); + if (ballColors == -1) + { + generateRandomBalls(); + } + else + { + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls[i] = ballColors % 10; + ballColors = ballColors / 10; + lPrompt->SetBalls(nextBalls); + } + } + + updateStat(); + demoTimer.start(1000, true); + return; + } + if (clickX) + { + lsb->demoClick(clickX-1, clickY-1); + if (hasFocus()) + demoTimer.start(1000, true); + return; + } +} + +void KLines::focusOutEvent(QFocusEvent *ev) +{ + if (bDemo) + { + lsb->hideDemoText(); + demoTimer.stop(); + statusBar()->changeItem(i18n(" Level: %1").arg(i18n("Tutorial - Paused")), 0); + } + KMainWindow::focusOutEvent(ev); +} + +void KLines::focusInEvent(QFocusEvent *ev) +{ + if (bDemo) + { + statusBar()->changeItem(i18n(" Level: %1").arg(levelStr), 0); + slotDemo(); + } + KMainWindow::focusInEvent(ev); +} + +void KLines::stopGame() +{ + if (!lsb->gameOver()) + endGame(); + startGame(); +} + +void KLines::searchBallsLine() +{ +} + +void KLines::generateRandomBalls() +{ + score_undo = score; + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls_undo[i] = nextBalls[i]; + nextBalls[i] = bUndo ? + lsb->random(NCOLORS) : + nextBalls_redo[i]; + } + lPrompt->SetBalls(nextBalls); +} + +void KLines::placeBalls() +{ + lsb->placeBalls(nextBalls); +} + +void KLines::undo() +{ + if (lsb->gameOver()) + return; + if (!bUndo) + return; + for( int i = 0 ; i < BALLSDROP ; i++ ) + { + nextBalls_redo[i] = nextBalls[i]; + nextBalls[i] = nextBalls_undo[i]; + } + score = score_undo; + updateStat(); + lPrompt->SetBalls(nextBalls); + lsb->undo(); + switchUndo(FALSE); +} + +void KLines::makeTurn() +{ + if (bDemo) + { + lsb->adjustDemoMode(false, false); + demoTimer.start(100, true); + } + if (lsb->gameOver()) + return; + if (!bDemo){ + placeBalls(); + if(sender() != lsb) + lsb->saveUndo(); + } + bNewTurn = true; +} + +void KLines::userTurn() +{ + if(bNewTurn) + { + bNewTurn = false; + if (!bDemo) + generateRandomBalls(); + if (!bFirst && !bDemo) + switchUndo(true); + } + bFirst = false; +} + +void KLines::addScore(int ballsErased) +{ if(ballsErased >= 5){ + score += 2*ballsErased*ballsErased - 20*ballsErased + 60 ; + if( !lPrompt->getState() ) score+= 1; + updateStat(); + }; + if (bDemo) + { + lsb->adjustDemoMode(false, false); + demoStep += 2; + if (hasFocus()) + demoTimer.start(100, true); + } +} + +void KLines::updateStat() +{ + statusBar()->changeItem(i18n(" Score: %1").arg(score), 1); +} + +void KLines::viewHighScore() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Level, this); + d.exec(); +} + +void KLines::endGame() +{ + lsb->setGameOver(true); + lsb->repaint(false); + + if (bDemo) + return; + + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score | KScoreDialog::Level, this); + KScoreDialog::FieldInfo scoreInfo; + scoreInfo.insert(KScoreDialog::Level, levelStr); + if (d.addScore(score, scoreInfo, true)) + d.exec(); +} + +void KLines::switchPrompt() +{ + Prefs::setShowNext(!Prefs::showNext()); + lPrompt->setPrompt(Prefs::showNext()); + showNextAction->setChecked(Prefs::showNext()); +} + +void KLines::switchNumbered() +{ + Prefs::setNumberedBalls(!Prefs::numberedBalls()); + lPrompt->setPrompt(Prefs::showNext()); + showNumberedAction->setChecked(Prefs::numberedBalls()); + mwidget->updatePix(); +} + +void KLines::switchUndo(bool bu) +{ + bUndo = bu; + undoAction->setEnabled(bu); +} + +void KLines::keyPressEvent(QKeyEvent *e) +{ + if (lsb->gameOver() && (e->key() == Qt::Key_Space)) + { + e->accept(); + startGame(); + return; + } + KMainWindow::keyPressEvent(e); +} diff --git a/klines/klines.desktop b/klines/klines.desktop new file mode 100644 index 00000000..dba71f39 --- /dev/null +++ b/klines/klines.desktop @@ -0,0 +1,111 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Exec=klines -caption "%c" %i %m +Icon=klines +DocPath=klines/index.html +GenericName=Tactical Game +GenericName[af]=Taktiese Speletjie +GenericName[ar]=لعبة تكتيكية +GenericName[az]=Taktik oyunu +GenericName[be]=Ð¢Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ТактичеÑка игра +GenericName[bn]=কৌশলের খেলা +GenericName[br]=C'hoari a vrezelekaouriezh +GenericName[bs]=TaktiÄka igra +GenericName[ca]=Joc de tàctica +GenericName[cs]=Taktická hra +GenericName[cy]=Gêm Dactegol +GenericName[da]=Taktisk spil +GenericName[de]=Taktikspiel +GenericName[el]=Παιχνίδι τακτικής +GenericName[eo]=Taktika ludo +GenericName[es]=Juego de táctica +GenericName[et]=Taktikamäng +GenericName[eu]=Joko taktikoa +GenericName[fa]=بازی برنامه‌ریزی‌شده +GenericName[fi]=Taktiikkapeli +GenericName[fo]=Taktiskt spæl +GenericName[fr]=Jeu de tactique +GenericName[ga]=Cluiche Taicticiúil +GenericName[gl]=Xogo de Táctica +GenericName[he]=משחק טקטי +GenericName[hi]=रणनीतिक खेल +GenericName[hr]=TaktiÄka igra +GenericName[hu]=Logikai +GenericName[id]=Permainan Taktik +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco di tattica +GenericName[ja]=戦略的ゲーム +GenericName[km]=ល្បែង​ក្បួន​យុទ្ធសាស្ážáŸ’ážš +GenericName[ko]=ì „ëžµ 게임 +GenericName[lt]=Taktinis žaidimas +GenericName[lv]=TaktiskÄ spÄ“le +GenericName[mk]=Тактичка игра +GenericName[mt]=Logħba ta' tattika +GenericName[nb]=Taktikk-spill +GenericName[nds]=Taktikspeel +GenericName[ne]=यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Tactisch spel +GenericName[nn]=Taktisk spel +GenericName[pa]=ਟਾਕਟੀਕਲ ਖੇਡ +GenericName[pl]=Gra taktyczna +GenericName[pt]=Jogo de Estratégia +GenericName[pt_BR]=Jogo Tático +GenericName[ro]=Un joc de tactică +GenericName[ru]=ТактичеÑÐºÐ°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino Mugambi +GenericName[se]=Taktihkkaspeallu +GenericName[sk]=Taktická hra +GenericName[sl]=TaktiÄna igra +GenericName[sr]=Тактичка игра +GenericName[sr@Latn]=TaktiÄka igra +GenericName[sv]=Taktikspel +GenericName[ta]=தநà¯à®¤à®¿à®°à®®à®¾à®© விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Тактикӣ +GenericName[th]=เà¸à¸¡à¸§à¸²à¸‡à¹à¸œà¸™ +GenericName[tr]=Taktik oyunu +GenericName[uk]=Тактична гра +GenericName[ven]=Mutambo wa Tactical +GenericName[vi]=Trò chÆ¡i chiến thuật +GenericName[wa]=Djeu di tactike +GenericName[xh]=Umdlalo onamaqhinga +GenericName[zh_CN]=æˆ˜æœ¯æ¸¸æˆ +GenericName[zh_TW]=戰術éŠæˆ² +GenericName[zu]=Umdlalo wamasu +Terminal=false +Name=Kolor Lines +Name[af]=Kleur Lyne +Name[ar]=خطوط Kolor +Name[be]=КалÑÑ€Ð¾Ð²Ñ‹Ñ Ð»Ñ–Ð½Ñ–Ñ– +Name[bn]=কালার লাইনà§à¦¸ +Name[br]=Linenn Kolor +Name[eo]=Kolorlinioj +Name[fa]=خطوط Kolor +Name[fr]=KLines +Name[hi]=कलर लाइनà¥à¤¸ +Name[hr]=KNiz u boji +Name[hu]=Színes vonalak +Name[it]=KLines +Name[nb]=Klinjer +Name[ne]=रङ रेखा +Name[nso]=Methalo ya Mmala +Name[pa]=ਰੰਗ ਖੇਡ +Name[pl]=Kolorowe linie +Name[pt]=Linhas Coloridas +Name[pt_BR]=KLinhas de Cores +Name[ro]=Linii colorate +Name[ru]=Цветные линии +Name[sl]=Barvne Ärte +Name[sv]=Färglinjer +Name[ta]=வணà¯à®£à®•à¯à®•à¯‹à®Ÿà¯à®•à®³à¯ +Name[tg]=Хатҳои Рангӣ +Name[tr]=Renkli çizgiler +Name[uk]=Кольорові лінії +Name[ven]=Mitalo ya Kolor +Name[wa]=Royes di coleur +Name[xh]=Iilayini zeKolor +Name[zh_TW]=Kolor Lines å½©è‰²ç·šæ¢ +Name[zu]=Olayini bemibala +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/klines/klines.h b/klines/klines.h new file mode 100644 index 00000000..73720f03 --- /dev/null +++ b/klines/klines.h @@ -0,0 +1,93 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KLINES_H +#define KLINES_H + +#include + +#include "linesboard.h" +#include "mwidget.h" +#include "prompt.h" + +class KSelectAction; +class KAction; +class KToggleAction; + +class KLines : public KMainWindow +{ + Q_OBJECT +public: + KLines(); + ~KLines(); + +protected: + void keyPressEvent(QKeyEvent *e); + void initKAction(); + void setLevel(int level); + + void focusOutEvent(QFocusEvent *); + void focusInEvent(QFocusEvent *); + +public slots: + void startGame(); + void startDemo(); + void stopGame(); + void endGame(); + void makeTurn(); + void userTurn(); + void addScore(int ballsErased); + void switchPrompt(); + void switchNumbered(); + void undo(); + void slotDemo(); + +private slots: + void viewHighScore(); + +private: + LinesBoard* lsb; + MainWidget *mwidget; + LinesPrompt *lPrompt; + KAction *act_demo, *undoAction, *endTurnAction; + KSelectAction *levelAction; + KToggleAction *showNextAction; + KToggleAction *showNumberedAction; + QString levelStr; + + bool bNewTurn; + + int score; + int score_undo; + + int nextBalls[BALLSDROP]; + int nextBalls_undo[BALLSDROP]; + int nextBalls_redo[BALLSDROP]; + bool bUndo; + bool bFirst; + bool bDemo; + + int demoStep; + QTimer demoTimer; + + void searchBallsLine(); + void generateRandomBalls(); + void placeBalls(); + void updateStat(); + void switchUndo( bool bu ); + void stopDemo(); +}; + +#endif diff --git a/klines/klines.kcfg b/klines/klines.kcfg new file mode 100644 index 00000000..bc5b6f63 --- /dev/null +++ b/klines/klines.kcfg @@ -0,0 +1,23 @@ + + + + + + + 0 + -2 + 2 + + + + true + + + + false + + + diff --git a/klines/klinesui.rc b/klines/klinesui.rc new file mode 100644 index 00000000..13af3734 --- /dev/null +++ b/klines/klinesui.rc @@ -0,0 +1,11 @@ + + + + + &Settings + + + + + + diff --git a/klines/linesboard.cpp b/klines/linesboard.cpp new file mode 100644 index 00000000..5078b8f9 --- /dev/null +++ b/klines/linesboard.cpp @@ -0,0 +1,754 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "linesboard.h" +#include "linesboard.moc" + +/* + Constructs a LinesBoard widget. +*/ + +LinesBoard::LinesBoard( BallPainter * abPainter, QWidget* parent, const char* name ) + : Field( parent, name ) +{ + demoLabel = 0; + bGameOver = false; + anim = ANIM_NO; + focusX = -1; + focusY = -1; +// waypos = 0; +// waylen = 0; +// jumpingRow = -1; +// jumpingCol = -1; + painting = 0; + way = new Waypoints[NUMCELLSW*NUMCELLSH]; + + bPainter = abPainter; + + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + + setMouseTracking( FALSE ); + setFixedSize(wHint(), hHint()); + + timer = new QTimer(this); + connect( timer, SIGNAL(timeout()), SLOT(timerSlot()) ); + timer->start( TIMERCLOCK, FALSE ); + +} + +/* + Destructor: deallocates memory for contents +*/ + +LinesBoard::~LinesBoard() +{ + timer->stop(); + delete timer; + delete [] way; +} + +void LinesBoard::startDemoMode() +{ + level = DEMO_LEVEL; + rnd_demo = KRandomSequence(DEMO_SEQUENCE); + bAllowMove = false; +} + +void LinesBoard::adjustDemoMode(bool allowMove, bool off) +{ + bAllowMove = allowMove; + if (off) + level = -2; +} + +void LinesBoard::demoAdjust(int a) +{ + rnd_demo.modulate(a); +} + + +void LinesBoard::placeBalls(int pnextBalls[BALLSDROP]) +{ + for(int i=0; i < BALLSDROP; i++) + nextBalls[i] = pnextBalls[i]; + + nextBallToPlace = 0; + placeBall(); +} + +void LinesBoard::placeBall( ) +{ + char* xx = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH ); + char* yy = (char*)malloc( sizeof(char)*NUMCELLSW*NUMCELLSH ); + int empty = 0; + for(int y=0; y 0 ? 1000: -1000; + int maxtry = level > 0 ? level+1 : 1-level; + for(int i=0;i 0) && (score < best_score)) || + ((level < 0) && (score > best_score))) + { + best_pos = pos; + best_score = score; + } + } + pos = best_pos; + } + + putBall( xx[pos], yy[pos], color ); + clearAnim(); + setAnim( xx[pos], yy[pos], ANIM_BORN ); + nextBallToPlace++; + AnimStart(ANIM_BORN); + free(xx); + free(yy); + } + else + { + free(xx); + free(yy); + emit endGame(); + } +} + + +/*id LinesBoard::doAfterBalls() { + erase5Balls(); + repaint(FALSE); +} +*/ +/* + Sets the size of the table +*/ + +int LinesBoard::width() { return CELLSIZE * NUMCELLSW; } +int LinesBoard::height() { return CELLSIZE * NUMCELLSH; } +int LinesBoard::wHint() { return width(); } +int LinesBoard::hHint() { return height(); } + +void LinesBoard::setGameOver(bool b) +{ + bGameOver = b; + focusX = -1; + focusY = -1; +} + + +void LinesBoard::paintEvent( QPaintEvent* ) +{ + QPainter *paint; + KPixmap *pixmap = 0; + if (bGameOver) + { + pixmap = new KPixmap(); + pixmap->resize(width(), height()); + paint = new QPainter( pixmap ); + } + else + { + paint = new QPainter( this ); + } + + for( int y=0; y < NUMCELLSH; y++ ){ + for( int x=0; x < NUMCELLSW; x++ ){ + if( getBall(x,y) == NOBALL ) + { + paint->drawPixmap(x*CELLSIZE, y*CELLSIZE, bPainter->GetBackgroundPix() ); + } + else + { + paint->drawPixmap(x*CELLSIZE, y*CELLSIZE, + bPainter->GetBall(getBall(x,y),animstep,getAnim(x,y))); + } + } + } + if (bGameOver) + { + paint->end(); + + KPixmapEffect::fade(*pixmap, 0.5, Qt::black); + + QPainter p(this); + p.drawPixmap(0,0, *pixmap); + delete pixmap; + + QFont gameover_font = font(); + gameover_font.setPointSize(48); + gameover_font.setBold(true); + p.setFont(gameover_font); + p.setPen(Qt::white); + QString gameover_text = i18n("Game Over"); + p.drawText(0, 0, width(), height(), AlignCenter|Qt::WordBreak, gameover_text); + } + else + { + if ((focusX >= 0) && (focusX < NUMCELLSW) && + (focusY >= 0) && (focusY < NUMCELLSH)) + { + QRect r; + r.setX(focusX*CELLSIZE+2); + r.setY(focusY*CELLSIZE+2); + r.setWidth(CELLSIZE-4); + r.setHeight(CELLSIZE-4); + paint->setPen(QPen(Qt::DotLine)); + paint->drawRect(r); + } + } + delete paint; +} + +/* + Handles mouse press events for the LinesBoard widget. +*/ +void LinesBoard::mousePressEvent( QMouseEvent* e ) +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove) && e->spontaneous()) return; + + int curRow = e->y() / CELLSIZE; + int curCol = e->x() / CELLSIZE; + + placePlayerBall(curCol, curRow); +} + +void LinesBoard::placePlayerBall(int curCol, int curRow) +{ + //check range + if (!checkBounds( curCol, curRow ) ) + return; +// if( running || anim != ANIM_NO ) return; + if(anim != ANIM_JUMP && anim != ANIM_NO) return; + if ( anim == ANIM_JUMP ) + { + if ( getBall(curCol,curRow) == NOBALL ) + { + if(existPath(jumpingCol, jumpingRow, curCol, curRow)) + { + saveRandomState(); + rnd.modulate(jumpingCol); + rnd.modulate(jumpingRow); + rnd.modulate(curCol); + rnd.modulate(curRow); + saveUndo(); + AnimStart(ANIM_RUN); + } + } + else + AnimJump(curCol,curRow); + } + else + AnimJump(curCol,curRow); +} + +/* + Move focus thingy +*/ +void LinesBoard::moveFocus(int dx, int dy) +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove)) return; + if (focusX == -1) + { + focusX = 0; + focusY = 0; + } + else + { + focusX = (focusX + dx + NUMCELLSW) % NUMCELLSW; + focusY = (focusY + dy + NUMCELLSH) % NUMCELLSH; + } + repaint(FALSE); +} + +void LinesBoard::moveLeft() +{ + moveFocus(-1, 0); +} + +void LinesBoard::moveRight() +{ + moveFocus(1, 0); +} + +void LinesBoard::moveUp() +{ + moveFocus(0, -1); +} + +void LinesBoard::moveDown() +{ + moveFocus(0, 1); +} + +void LinesBoard::placePlayerBall() +{ + if (bGameOver) return; + if ((level == DEMO_LEVEL) && (!bAllowMove)) return; + placePlayerBall(focusX, focusY); +} + +void LinesBoard::AnimJump(int x, int y ) { + if ( getBall(x,y) != NOBALL ) + if (!( anim == ANIM_JUMP && jumpingCol == x && jumpingRow == y )) + if ( AnimEnd() ) + { + clearAnim(); + setAnim(x,y,ANIM_JUMP); + jumpingCol = x; + jumpingRow = y; + AnimStart(ANIM_JUMP); + } +} +void LinesBoard::AnimStart(int panim) { + if (anim != ANIM_NO) + AnimEnd(); + animstep = 0; + animdelaystart = 1; + switch(panim) { + case ANIM_NO: + break; + case ANIM_BORN: + animdelaystart=1; + animmax = BOOMBALLS; + break; + case ANIM_JUMP: + direction = -1; + animstep = 4; + animmax = PIXTIME -1; + break; + case ANIM_RUN: + animdelaystart=3; + // do first step on next timer; + animdelaycount = 0; + // animmax already set + break; + case ANIM_BURN: + animdelaystart=1; + animmax = FIREBALLS + FIREPIX - 1; + break; + default: + ; + } + anim = panim; + animdelaycount = animdelaystart; +} + +int LinesBoard::AnimEnd( ) +{ + if (anim == ANIM_NO ) return true; + int oldanim = anim; + anim = ANIM_NO; + if (oldanim == ANIM_RUN) { + if (animstep != animmax) { + moveBall(way[animstep].x,way[animstep].y,way[animmax].x,way[animmax].y); + } + if ( erase5Balls() == 0 ) { + emit endTurn(); + return true; + } + else + return false; + } + else if ( oldanim == ANIM_BURN ) + { + emit eraseLine( deleteAnimatedBalls() ); + repaint(FALSE); + if ( nextBallToPlace < BALLSDROP ) + { + placeBall(); + // continue with born + return false; + } + else + { + emit userTurn(); + return true; + } + } + else if ( oldanim == ANIM_BORN ) + { + if ( erase5Balls() == 0 ) + { + if ( freeSpace() > 0) + { + if ( nextBallToPlace < BALLSDROP ) + { + placeBall(); + return false; + } + else + { + emit userTurn(); + return true; + } + } + else + { + emit endGame(); + return false; + } + } + else + { + // wait for user input + emit userTurn(); + return true; + } + } + emit userTurn(); + return true; +} + +void LinesBoard::AnimNext() { + if ( anim != ANIM_NO ) + { + if ( anim == ANIM_JUMP ) { + if ( (direction > 0 && animstep == animmax) || ( direction < 0 && animstep == 0)) + direction = -direction; + animstep += direction; + repaint(FALSE); + } else { + if ( animstep >= animmax ) + AnimEnd(); + else { + animdelaycount--; + if (animdelaycount <= 0) { + if ( anim == ANIM_RUN ) + moveBall(way[animstep].x,way[animstep].y,way[animstep+1].x,way[animstep+1].y); + animstep++; + animdelaycount = animdelaystart; + repaint( FALSE ); + } + } + } + } +} +int LinesBoard::getAnim( int x, int y ) +{ + return (( Field::getAnim(x,y) != ANIM_NO )? anim : ANIM_NO); +} + +void LinesBoard::timerSlot() +{ + AnimNext(); +} + +int LinesBoard::erase5Balls() +{ + int nb=5; // minimum balls for erasure + + bool bit_erase[NUMCELLSH][NUMCELLSW]; //bool array for balls, that must be erased + for(int y=0; y= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y][x-i] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + } + + //for vertical + for(int x=0; x= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + } + + + //for left-down to rigth-up diagonal + for ( int xs = NUMCELLSW-1,ys = NUMCELLSH-nb; xs >= nb-1; ) { + count = 0; + color = NOBALL; + for ( int x = xs, y = ys; x >= 0 && y < NUMCELLSH; x--, y++ ) { + if ( (newcolor = getBall(x,y)) == color) { + if ( color != NOBALL) { + count++; + if ( count >= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x+i] = true; + else bit_erase[y][x] = true; + } + } else { + color = newcolor; + count = 1; + } + } + if ( ys > 0 ) ys--; else xs--; + } + + + //for left-up to rigth-down diagonal + for ( int xs = 0,ys = NUMCELLSH-nb; xs <= NUMCELLSW-nb; ) + { + count = 0; + color = NOBALL; + for ( int x = xs, y = ys; x < NUMCELLSW && y < NUMCELLSH; x++, y++ ) + { + if ( (newcolor = getBall(x,y)) == color) + { + if ( color != NOBALL) + { + count++; + if ( count >= nb ) + if ( count == nb ) + for (int i = 0; i < nb; i++) + bit_erase[y-i][x-i] = true; + else + bit_erase[y][x] = true; + } + } + else + { + color = newcolor; + count = 1; + } + } + if ( ys > 0 ) ys--; else xs++; + } + + //remove all lines balls, that more than nb + int cb=0; + for(int y=0; y < NUMCELLSH; y++) + for(int x=0; x < NUMCELLSW; x++) + { + if (bit_erase[y][x]) + { + setAnim(x,y,ANIM_YES); + cb++; + } + else + { + setAnim(x,y,ANIM_NO); + } + } + if ( cb > 0 ) + { + AnimStart(ANIM_BURN); + //emit eraseLine(cb); + } + + return cb; +} + + +#define GO_EMPTY 4 +#define GO_A 5 +#define GO_B 6 +#define GO_BALL 7 + +bool LinesBoard::existPath(int bx, int by, int ax, int ay) +{ + int dx[4]={1,-1, 0, 0}; + int dy[4]={0, 0, 1,-1}; + + if (getBall(ax,ay) != NOBALL) + return false; + + char pf[NUMCELLSH][NUMCELLSW]; + for(int y=0; y=0) && (nx=0) && (nysetMargin(1); + demoLabel->setIndent(0); + demoLabel->setAutoMask( FALSE ); + demoLabel->setFrameStyle( QFrame::Plain | QFrame::Box ); + demoLabel->setLineWidth( 1 ); + demoLabel->setAlignment( AlignHCenter | AlignTop ); + demoLabel->setPalette(QToolTip::palette()); + demoLabel->polish(); + } + demoLabel->setText(text); + demoLabel->adjustSize(); + QSize s = demoLabel->sizeHint(); + QPoint p = QPoint(x() + (width()-s.width())/2, y() + (height()-s.height())/2); + demoLabel->move(mapToGlobal(p)); + demoLabel->show(); +} + +void LinesBoard::hideDemoText() +{ + if (demoLabel) + demoLabel->hide(); +} + +void LinesBoard::demoClick(int x, int y) +{ + QPoint lDest = QPoint(x*CELLSIZE+(CELLSIZE/2), y*CELLSIZE+(CELLSIZE/2)); + QPoint dest = mapToGlobal(lDest); + QPoint cur = QCursor::pos(); + for(int i = 0; i < 25;) + { + i++; + QPoint p = cur + i*(dest-cur) / 25; + QCursor::setPos(p); + QApplication::flushX(); + QTimer::singleShot(80, this, SLOT(demoClickStep())); + kapp->enter_loop(); + } + QCursor::setPos(dest); + QMouseEvent ev(QEvent::MouseButtonPress, lDest, dest, LeftButton, LeftButton); + mousePressEvent(&ev); +} + +void LinesBoard::demoClickStep() +{ + kapp->exit_loop(); +} diff --git a/klines/linesboard.h b/klines/linesboard.h new file mode 100644 index 00000000..044887b4 --- /dev/null +++ b/klines/linesboard.h @@ -0,0 +1,130 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef linesboard_h +#define linesboard_h + +#include +#include +#include +#include + +#include + +#include "field.h" +//#include "shotcounter.h" +#include "ballpainter.h" + +class LinesBoard : public Field +{ + Q_OBJECT +public: + LinesBoard( BallPainter * abPainter, QWidget* parent=0, const char* name=0 ); + ~LinesBoard(); + + int width(); + int height(); + int wHint(); + int hHint(); +// void doAfterBalls(); + void placeBalls(int nextBalls[BALLSDROP]); + void undo(); + bool gameOver() { return bGameOver; } + void setGameOver(bool b); + int random(int max) { return (level == DEMO_LEVEL) ? rnd_demo.getLong(max) : rnd.getLong(max); } + void saveRandomState() { rnd_saved = rnd; } + void restoreRandomState() { rnd = rnd_saved; } + void setLevel(int _level) { level = _level; } + void startDemoMode(); + void adjustDemoMode(bool allowMove, bool off); + void showDemoText(const QString &); + void hideDemoText(); + void demoClick(int x, int y); + void demoAdjust(int a); + +signals: + void endTurn(); + void endGame(); + void eraseLine(int nb); + void userTurn(); + +private: + int anim; + + struct Waypoints { + int x,y; + } *way; + int nextBalls[BALLSDROP]; + + int animmax; + + //used for running and animation phase + int painting; + int animstep; + int animdelaycount; + int animdelaystart; + int direction; + + + int nextBallToPlace; + int jumpingCol; + int jumpingRow; + + int focusX; + int focusY; + + int level; + + QTimer* timer; +// ShotCounter* shCounter; + BallPainter* bPainter; + bool bGameOver; + KRandomSequence rnd; + KRandomSequence rnd_saved; + KRandomSequence rnd_demo; + + QLabel *demoLabel; + bool bAllowMove; + + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + + void AnimStart(int panim); + void AnimNext(); + int AnimEnd(); + int getAnim(int x, int y ); // returns if the specifyed cell is animated.. + void AnimJump( int col, int row ); + + int erase5Balls(); + bool existPath(int ax, int ay, int bx, int by); + void placeBall(); + void placePlayerBall(int col, int row); + void moveFocus(int dx, int dy); + +public slots: + void moveLeft(); + void moveRight(); + void moveUp(); + void moveDown(); + void placePlayerBall(); + +protected slots: + void timerSlot(); + void demoClickStep(); +}; + +#endif diff --git a/klines/main.cpp b/klines/main.cpp new file mode 100644 index 00000000..66f5959c --- /dev/null +++ b/klines/main.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + /* + * Roman Razilov 2000-05-19 debug dummmy + * Roman Razilov 2000-05-21 qimgio +*/ + + +#include +#include +#include +#include + +#include "klines.h" + +static const char description[] = I18N_NOOP("Kolor Lines - a little game about balls and how to get rid of them"); + +// A hack to circumvent tricky i18n issue, not used later on in the code. +// Both context and contents must be exactly the same as for the entry in +// kdelibs/kdeui/ui_standards.rc +static const char dummy[] = I18N_NOOP2("Menu title", "&Move"); + +int main( int argc, char **argv ) +{ + KAboutData aboutData("klines", I18N_NOOP("Kolor Lines"), LINESVERSION, + description, KAboutData::License_GPL); + aboutData.addAuthor("Roman Merzlyakov", I18N_NOOP("Original author"), "roman@sbrf.barrt.ru"); + aboutData.addAuthor("Roman Razilov", I18N_NOOP("Rewrite and Extension"), "Roman.Razilov@gmx.de"); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(KLines) + else { + KLines *w = new KLines; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/klines/mwidget.cpp b/klines/mwidget.cpp new file mode 100644 index 00000000..a6555835 --- /dev/null +++ b/klines/mwidget.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "mwidget.moc" + +#include + +#include +#include + +#include "ballpainter.h" + +MainWidget::MainWidget( QWidget* parent, const char* name ) + : QFrame( parent, name ) +{ + QBoxLayout *grid = new QHBoxLayout( this, 5 ); //(rows,col) + bPainter = new BallPainter(); + + lsb = new LinesBoard(bPainter, this); + grid->addWidget( lsb ); + + QBoxLayout *right = new QVBoxLayout(grid, 2); + QLabel *label = new QLabel(i18n("Next balls:"), this); + lPrompt = new LinesPrompt(bPainter, this); + connect(lPrompt, SIGNAL(PromptPressed()), parent, SLOT(switchPrompt())); + + right->addWidget( label, 0, Qt::AlignBottom | Qt::AlignHCenter ); + right->addWidget( lPrompt, 0, Qt::AlignTop | Qt::AlignHCenter ); + + grid->activate(); + grid->freeze(0,0); + +// warning("width: %i height: %i", width(), height() ); + +// warning("wh: %i hh: %i", sizeHint().width(), sizeHint().height() ); + +} + +/* + Destructor: deallocates memory for contents +*/ + +MainWidget::~MainWidget() +{ +} + +LinesBoard * MainWidget::GetLsb() +{ + return lsb; +} + +LinesPrompt * MainWidget::GetPrompt() +{ + return lPrompt; +} + +void MainWidget::updatePix() +{ + bPainter->deletePix(); + bPainter->createPix(); + lPrompt->update(); + lsb->update(); +} diff --git a/klines/mwidget.h b/klines/mwidget.h new file mode 100644 index 00000000..140ccd51 --- /dev/null +++ b/klines/mwidget.h @@ -0,0 +1,45 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MWIDGET_H +#define MWIDGET_H + +#include +#include +#include +#include +#include "linesboard.h" +#include "prompt.h" + +class BallPainter; + +class MainWidget : public QFrame +{ + Q_OBJECT + LinesBoard * lsb; + LinesPrompt * lPrompt; + BallPainter *bPainter; + +public: + MainWidget( QWidget* parent=0, const char* name=0 ); + ~MainWidget(); + LinesBoard * GetLsb(); + LinesPrompt * GetPrompt(); + void updatePix(); +}; + +#endif diff --git a/klines/povray/ball.pov b/klines/povray/ball.pov new file mode 100644 index 00000000..16d577ed --- /dev/null +++ b/klines/povray/ball.pov @@ -0,0 +1,289 @@ +/*************************************************************************** + ball.pov - script for rendering Kolor Lines graphics + ------------------- + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#version 3.0 + +// Objectname = Main View +// Objecttype = view + +// This camera is build for ratio 451:277 +#include "colors.inc" +#include "textures.inc" +#declare BallColor = Green +#include "clr.inc" + +#declare CELLSIZE = 32 +#declare PIXTIME = 10 +#declare BALLDOWN = 3 +#declare BALLSPACE = 4 + +#warning concat("XXXX clock:",str(clock,5,2),"\n") + +#switch (clock) +// --------------------burning ball------------------- + #range (0,1.001) + + #declare explosion = 1 + #declare jump = 0 + #declare n = 0 + + #declare t0 = 0 + #declare t1 = 0.2 + #declare t2 = 0.5 + #declare t3 = 0.8 + #declare t4 = 1 + #declare tt = clock + #if ( tt = t0 ) + //turbulence + #declare tur = 0 + #declare trb = 0 + #declare trf = 0 + #declare pf = 0 + #end + #if ( tt > t0 & tt <= t1 ) + #declare tl = (tt - t0)/(t1-t0) + #declare tur = 3+(0.2-3)*tl + #declare trb = 0+(0-0)*tl + #declare trf = 1+(3-1)*tl + #declare pf = 0+(0.3-0)*t1 + #end + #if ( tt > t1 & tt <= t2 ) + #declare tl = (tt - t1)/(t2-t1) + #declare tur = 0.2+(0.5-0.1)*tl + #declare trb = 0+(1-0)*tl + #declare trf = 3+(1-3)*tl + #declare pf = 0.3+(0.2-0.3)*t1 + #end + #if ( tt > t2 & tt <= t3 ) + #declare tl = (tt - t2)/(t3-t2) + #declare tur = 0.5+(2-0.5)*tl + #declare trb = 1+(1-1)*tl + #declare trf = 1+(1-1)*tl + #declare pf = 0.2-(0.0-0.2)*t1 + #end + #if ( tt > t3 ) + #declare tl = (tt - t3)/(t4-t3) + #declare tur = 2+(5-2)*tl + #declare trb = 1+(1-1)*tl + #declare trf = 1+(0-1)*tl + #declare pf = 0.0-(0.0-0.0)*t1 + #end + // ball position / size + #declare by = 0 + #declare bh = CELLSIZE-2-BALLSPACE + #declare bw = CELLSIZE-2-BALLSPACE + #break +// ----------------borning ball---------------------- + #range (5,6.001) + + #declare explosion = 0 + #declare jump = 0 + + // fire turbulence + #declare tur = 0 + // trancperancy ball + #declare trb = 0 + // trancperancy fire + #declare trf = 0 + // lambda + #declare pf = 0 + // relative clock + #declare tt = clock - 5 + // y position + #declare by = 0 + #declare r=sqrt(tt) + #declare bh = (CELLSIZE-2-BALLSPACE)*r + #declare bw = (CELLSIZE-2-BALLSPACE)*r + // normal + #declare n = (1-tt)*r*2 + + #break + + // -------------jumping ball---------------------- + #range (10,11.001) + #declare explosion = 0 + #declare jump = 1 + #declare tur = 0 + #declare trb = 0 + #declare trf = 0 + #declare pf = 0 + #declare n = 0 + #declare tt = clock - 10 + // ball free fall: y = -(g * t^2)/2 + // ball jums on X*sin( sqrt(k)*(t0 - t1 + t ) ) + // t whole motion time + // g - gravity acceleration + // A - frei movement amplitude + // BB way with deflation + #declare A = BALLDOWN + #declare B = BALLSPACE + #declare ta = PIXTIME + #declare k = (pi + 2*sqrt( B*(A+B))/A - acos( A/(A+2*B)) )/ta + #declare k = k*k + #warning concat("XXXX k:",str(k,5,2),"\n") + #declare g = k*A*A/(2*(A+B)) + #warning concat("XXXX g:",str(g,5,2),"\n") + #declare t1 = sqrt(2*B/g) + #warning concat("XXXX t1:",str(t1,5,2),"\n") + #declare X = A*(A+2*B)/(2*(A+B)) + #warning concat("XXXX X:",str(X,5,2),"\n") + + #declare tc = PIXTIME - tt*PIXTIME + + #warning concat("XXXX tc:",str(tc,5,2),"\n") + // ball position + #if (tc < t1) + #declare by = B/2 - g*tc*tc/2 + #else + #declare by = -B/2 - g/k - X*cos( sqrt(k)*(ta-tc)) + #end + #if ( by < -2 ) + #declare bh=CELLSIZE-2+2*by + #declare bw = CELLSIZE-2-BALLSPACE+(-by-2) + #else + #declare bh = CELLSIZE-2-BALLSPACE + #declare bw = CELLSIZE-2-BALLSPACE + #end + + #break + + #else + #warning " ERROR, clock out of range !!! \n" + +#end + + +#warning concat("XXXX clock:",str(clock,5,2),"\n") + +#warning concat("XXXX tt:",str(tt,5,2),"\n") + + + + + +camera +{ + orthographic + location <0,0,-100> + look_at <0,0,0> + up (CELLSIZE-2)*y + right (CELLSIZE)*x +} + +// Global settings +global_settings +{ + ambient_light rgb<0.5,0.5,0.5> +} +light_source +{ + <-400, 600, -600> + color White + cylinder + radius 200 + falloff 1000 + tightness 1 + point_at <0,0,0> +} +// Background +background +{ + color rgb<.5,.5,.5> +} + +// fire +#if (explosion = 1) +sphere { 0, 1 + pigment { color rgbt<0,0,0,1> + } + halo { + emitting + spherical_mapping + poly + max_value 40 + exponent 0.1 + linear + turbulence tur +// phase pf + lambda 2-pf + frequency 1 + octaves 6 + color_map { + [ 0 color rgbt <1, 0, 0, 1> ] + [ 0.2 color rgbt <1, 0, 0, 1-1*trf> ] + [ 0.5 color rgbt <1, 0, 0, 1-3*trf> ] + [ 0.9 color rgbt <1, 1, 0, 1-4*trf> ] + [ 1 color rgbt <1, 1, 0.5, 1-6*trf> ] + } + samples 30 + } + hollow + scale<15,15,15> + translate <-0.5,-0.5,0> + } +#end +// Ball +#if ( trb < 1 & bh > 0 ) +sphere +{ + <0,0,0>,1 + pigment { + + wood +// agate_turb 0.5 + turbulence .1 + frequency 2 +// lambda + rotate <30,10,15> + color_map { + [0.5 BallColor transmit trb] + [0.3 BallColor*0.95 transmit trb] + [0.2 BallColor*0.9 transmit trb] + } + } + no_shadow + #if ( n > 0 ) + normal + { + //wood 0.5 + //frequency 2 + //turbulence .1 + //rotate <30,10,15> + bumps n + scale 0.3 + } + #end + finish { + ambient 0.3 + diffuse 0.8 + specular 1 + roughness 0.001 + reflection .1 + phong 0.9 * ( 1-trb) + phong_size 60 + + } + #if (trb > 0) + hollow + #end + scale + translate <-0.5,by - 0.5,0> + +#warning concat("XXXX by:",str(by,5,2),"\n") +#warning concat("XXXX bh:",str(bh,5,2),"\n") +#warning concat("XXXX bw:",str(bw,5,2),"\n") +} +#end diff --git a/klines/povray/balls.sh b/klines/povray/balls.sh new file mode 100755 index 00000000..1897afaa --- /dev/null +++ b/klines/povray/balls.sh @@ -0,0 +1,13 @@ +#!/bin/sh +echo "#declare BallColor = $2" >clr.inc +#### jumping balls +povray -w30 -h30 -V1 -P +A +KI10 +KF11 +KFF10 -Iball.pov +Oa$1.tga +#### borning balls +povray -w30 -h30 -V1 -P +A +KI5 +KF6 +KFF6 +SF2 +EF5 -Iball.pov +Ob$1.tga +#### burning balls balls +povray -w30 -h30 -V1 -P +A +KI0 +KF1 +KFF11 +SF1 +EF5 -Iball.pov +Oe$1.tga + +montage -geometry 30x30 -tile 100x1 -page 570x30 a$1??.tga b$1?.tga e$1??.tga bl_$1.tga +mogrify -crop 570x30+0+0 bl_$1.tga + + diff --git a/klines/povray/clean.sh b/klines/povray/clean.sh new file mode 100755 index 00000000..df78c905 --- /dev/null +++ b/klines/povray/clean.sh @@ -0,0 +1,2 @@ +#!/bin/sh +rm *.tga *.jpg \ No newline at end of file diff --git a/klines/povray/field.pov b/klines/povray/field.pov new file mode 100644 index 00000000..1256c733 --- /dev/null +++ b/klines/povray/field.pov @@ -0,0 +1,31 @@ +#declare CELLSIZE = 32 +#include "colors.inc" + +camera +{ + orthographic + location <0,0,-100> + look_at <0,0,0> + up (CELLSIZE-2)*y + right (CELLSIZE)*x +} +// Global settings +global_settings +{ + ambient_light rgb<0.5,0.5,0.5> +} +light_source +{ + <-400, 600, -600> + color White + cylinder + radius 100 + falloff 1000 + tightness 1 + point_at <0,0,0> +} +background +{ + color rgb<.5,.5,.5> +} + diff --git a/klines/povray/field.sh b/klines/povray/field.sh new file mode 100755 index 00000000..f4a784d2 --- /dev/null +++ b/klines/povray/field.sh @@ -0,0 +1,5 @@ +#!/bin/sh +if (povray -w32 -h32 -V1 -P +A +K10 -Ifield.pov +Ofield.tga) +then + mogrify -raise 1x1 field.tga +fi diff --git a/klines/povray/fire.sh b/klines/povray/fire.sh new file mode 100755 index 00000000..2b6b5f9a --- /dev/null +++ b/klines/povray/fire.sh @@ -0,0 +1,9 @@ +#! /bin/sh +echo "#declare BallColor = White" >clr.inc +if (povray -w30 -h30 -V1 -P +A +KI0 +KF1 +KFF11 +SF6 +EF10 -Iball.pov \ ++Ofire.tga) +then # mogrify -raise 1x1 e$1??.tga + montage -geometry 30x30 -tile 100x1 -page 150x30 fire*.tga fire.tga + mogrify -crop 150x30+0+0 fire.tga +# animate -delay 7 $1??.tga +fi diff --git a/klines/povray/makeballs.sh b/klines/povray/makeballs.sh new file mode 100755 index 00000000..62769605 --- /dev/null +++ b/klines/povray/makeballs.sh @@ -0,0 +1,36 @@ +#! /bin/sh +#### clean + +clean.sh + +#### render + +field.sh +fire.sh +balls.sh Red "rgb<1,0,0>" +balls.sh Green "rgb<0,1,0>" +balls.sh Blue "rgb<0,0,1>" +balls.sh Yellow "rgb<1,1,0>" +balls.sh Violet "rgb<1,0,1>" +balls.sh Cyan "rgb<0,1,1>" +balls.sh Brown "rgb<1/2,1/3,0>" + +#### montage balls ( 20x7 cells each 30x30) + +montage -geometry 570x30 -tile 1x10 bl_*.tga balls.tga +mogrify -crop 570x210+0+0 balls.tga + +#### convert to jpeg + +convert -quality 95 balls.tga balls.jpg +convert -quality 95 fire.tga fire.jpg +convert -quality 95 field.tga field.jpg + + +#### copy to data + +cp *jpg ../ + +#### clean + +#clean.sh \ No newline at end of file diff --git a/klines/povray/povray.ini b/klines/povray/povray.ini new file mode 100644 index 00000000..22ddf8ec --- /dev/null +++ b/klines/povray/povray.ini @@ -0,0 +1,59 @@ +; PERSISTENCE OF VISION RAY TRACER +; +; POV-Ray VERSION 3.0 +; +; SAMPLE POVRAY.INI FILE +; +; This file contains options which control how POV-Ray does its job. +; The file should be placed in the same directory as POVRAY.EXE and +; it will automaticaly read when POVRAY is run. +; +; The general form of the options is "Variable=value". Everything +; between the equals sign and the end of the line is considered part +; of the value. The spacing and layout is free-form but only one option +; per line is allowed. Variables and values are not case-sensitive. +; +; Note: characters after a semi-colon are treated as a comment +; +; Traditional POV-Ray switches beginning with + or - are also allowed +; and they may be given with more than one switch per line. +; +; These options have been put in this file because they will help +; you get up and running quickly. This file assumes you have installed +; the program in the /usr/local/lib directory. If you have installed +; the program on a directory you must edit the Library_Path lines below. +; See the general and Linux specific documentation for full instructions +; on how to use INI options. +; +; Add your own options at the bottom and/or edit these to suit. +; +; +; Width of image in pixels. Accepts integer values. +; +Width = 320 +; +; +; Height of image in pixels. Accepts integer values. +; +Height = 240 +; +; +; Turns pause when done feature off/on. Accepts boolean values. +; +Pause_when_Done = on +; +; +; Sets minimum number of objects before auto bounding kicks in. +; +Bounding_Threshold = 3 +; +; +; Test for user abort with any keypress every 100 pixels. +; +Test_Abort=On +Test_Abort_Count=100 +; +; Specify path to search for any files not found in current +; directory. Up to 25 such paths may be specified. +Library_Path=/usr/lib/povray3 +Library_Path=/usr/lib/povray3/include diff --git a/klines/prefs.kcfgc b/klines/prefs.kcfgc new file mode 100644 index 00000000..6ed42a2f --- /dev/null +++ b/klines/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=klines.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +#CustomAdditions=true +Mutators=true diff --git a/klines/prompt.cpp b/klines/prompt.cpp new file mode 100644 index 00000000..09c17e2a --- /dev/null +++ b/klines/prompt.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + begin : Fri May 19 2000 + copyright : (C) 2000 by Roman Merzlyakov + email : roman@sbrf.barrt.ru + copyright : (C) 2000 by Roman Razilov + email : Roman.Razilov@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "prompt.h" +#include "prompt.moc" + +LinesPrompt::LinesPrompt( BallPainter * abPainter, QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + + bPainter = abPainter; + + setFocusPolicy( NoFocus ); + setBackgroundColor( gray ); + + setMouseTracking( FALSE ); + setFixedSize(wPrompt(), hPrompt()); + + PromptEnabled = true; + cb[0]=NOBALL; + cb[1]=NOBALL; + cb[2]=NOBALL; +} + +LinesPrompt::~LinesPrompt() +{ +} + +int LinesPrompt::width() { return CELLSIZE * 3 ; } +int LinesPrompt::height() { return CELLSIZE ; } + +int LinesPrompt::wPrompt() { return CELLSIZE * 3 ; } +int LinesPrompt::hPrompt() { return CELLSIZE ; } + +void LinesPrompt::paintEvent( QPaintEvent* ) +{ + QPainter paint( this ); + if(PromptEnabled){ + paint.drawPixmap(0, 0, bPainter->GetNormalBall(cb[0]) ); + paint.drawPixmap(CELLSIZE, 0, bPainter->GetNormalBall(cb[1]) ); + paint.drawPixmap(2*CELLSIZE, 0, bPainter->GetNormalBall(cb[2]) ); + } + else{ + paint.drawPixmap(0, 0, bPainter->GetBackgroundPix() ); + paint.drawPixmap(CELLSIZE, 0, bPainter->GetBackgroundPix() ); + paint.drawPixmap(2*CELLSIZE, 0, bPainter->GetBackgroundPix() ); + } +} + +/* + Handles mouse press events for the LinesPrompt widget. +*/ +void LinesPrompt::mousePressEvent( QMouseEvent* ) +{ + emit PromptPressed(); +} + +void LinesPrompt::SetBalls( int *pcb ) +{ + for (int i = 0; i +#include "ballpainter.h" + +class LinesPrompt : public QWidget +{ + Q_OBJECT + + BallPainter* bPainter; + bool PromptEnabled; + int cb[BALLSDROP]; + + void paintEvent( QPaintEvent* ); + void mousePressEvent( QMouseEvent* ); + +public: + LinesPrompt( BallPainter * abPainter, QWidget * parent=0, const char * name=0 ); + ~LinesPrompt(); + + void setPrompt(bool enabled); + bool getState(); // enabled = true + void SetBalls( int *pcb ); + + int width(); + int height(); + int wPrompt(); + int hPrompt(); + +signals: + void PromptPressed(); + +}; + +#endif diff --git a/klines/templates/cpp_template b/klines/templates/cpp_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/klines/templates/cpp_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/klines/templates/header_template b/klines/templates/header_template new file mode 100644 index 00000000..6afef5d4 --- /dev/null +++ b/klines/templates/header_template @@ -0,0 +1,16 @@ +/*************************************************************************** + |FILENAME| - description + ------------------- + begin : |DATE| + copyright : (C) |YEAR| by |AUTHOR| + email : |EMAIL| + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ diff --git a/kmahjongg/Background.cpp b/kmahjongg/Background.cpp new file mode 100644 index 00000000..749607b3 --- /dev/null +++ b/kmahjongg/Background.cpp @@ -0,0 +1,115 @@ + +#include "Background.h" +#include + +Background::Background(): tile(true) { + + sourceImage = 0; + backgroundImage = 0; + backgroundPixmap = 0; + backgroundShadowPixmap = 0; +} + +Background::~Background() { + delete sourceImage; + delete backgroundImage; + delete backgroundPixmap; + delete backgroundShadowPixmap; +} + +bool Background::load(const QString &file, short width, short height) { + w=width; + h=height; + + if (file == filename) { + return true; + } + sourceImage = new QImage(); + backgroundImage = new QImage(); + backgroundPixmap = new QPixmap(); + backgroundShadowPixmap = new QPixmap(); + + // try to load the image, return on failure + if(!sourceImage->load(file )) + return false; + + // Just in case the image is loaded 8 bit + if (sourceImage->depth() != 32) + *sourceImage = sourceImage->convertDepth(32); + + // call out to scale/tile the source image into the background image + sourceToBackground(); + filename = file; + + return true; +} + +// slot used when tile/scale option changes +void Background::scaleModeChanged() { + sourceToBackground(); +} + +void Background::sizeChanged(int newW, int newH) { + if (newW == w && newH == h) + return; + w = newW; + h = newH; + sourceToBackground(); +} + +void Background::sourceToBackground() { + + // Deallocate the old image and create the new one + if (!backgroundImage->isNull()) + backgroundImage->reset(); + + // the new version of kmahjongg uses true color images + // to avoid the old color limitation. + backgroundImage->create(w, h, 32); + + // Now we decide if we should scale the incoming image + // or if we tile. First we check for an exact match which + // should be true for all images created specifically for us. + if ((sourceImage->width() == w) && (sourceImage->height() == h)) { + *backgroundImage = *sourceImage; + return; + } + + if (tile) { + // copy new to background wrapping on w and height + for (int y=0; yheight(); y++) { + QRgb *dest = (QRgb *) backgroundImage->scanLine(y); + QRgb *src = (QRgb *) sourceImage->scanLine(y % sourceImage->height()); + for (int x=0; x< backgroundImage->width(); x++) { + *dest = *(src + (x % sourceImage->width())); + dest++; + } + } + } else { + *backgroundImage = sourceImage->smoothScale(w, h); + // Just incase the image is loaded 8 bit + if (backgroundImage->depth() != 32) + *backgroundImage = backgroundImage->convertDepth(32); + } + + // Save a copy of the background as a pixmap for easy and quick + // blitting. + backgroundPixmap->convertFromImage(*backgroundImage); + + QImage tmp; + tmp.create(backgroundImage->width(), backgroundImage->height(), 32); + for (int ys=0; ys < tmp.height(); ys++) { + QRgb *src = (QRgb *) backgroundImage->scanLine(ys); + QRgb *dst = (QRgb *) tmp.scanLine(ys); + for (int xs=0; xs < tmp.width(); xs++) { + *dst=QColor(*src).dark(133).rgb(); + src++; + dst++; + } + } + + backgroundShadowPixmap->convertFromImage(tmp); + + return; +} + diff --git a/kmahjongg/Background.h b/kmahjongg/Background.h new file mode 100644 index 00000000..095f0f6e --- /dev/null +++ b/kmahjongg/Background.h @@ -0,0 +1,36 @@ +#ifndef _BACKGROUND_H +#define _BACKGROUND_H +#include + +class QPixmap; +class QImage; + + +class Background +{ + + + public: + Background(); + ~Background(); + bool tile; + + bool load(const QString &file, short width, short height); + void sizeChanged(int newW, int newH); + void scaleModeChanged(); + QPixmap *getBackground() {return backgroundPixmap;} + QPixmap *getShadowBackground() {return backgroundShadowPixmap;} + private: + void sourceToBackground(); + + int tileMode; // scale background = 0, tile = 1 + QImage *backgroundImage; + QImage *sourceImage; + QPixmap *backgroundPixmap; + QPixmap *backgroundShadowPixmap; + QString filename; + short w; + short h; +}; + +#endif diff --git a/kmahjongg/BoardLayout.cpp b/kmahjongg/BoardLayout.cpp new file mode 100644 index 00000000..56659611 --- /dev/null +++ b/kmahjongg/BoardLayout.cpp @@ -0,0 +1,300 @@ + +#include "BoardLayout.h" +#include +#include +#include + + + + + +BoardLayout::BoardLayout() +{ + filename=""; + clearBoardLayout(); +} + +BoardLayout::~BoardLayout() +{ +} + +void BoardLayout::clearBoardLayout() { + loadedBoard=""; + initialiseBoard(); +} + +bool BoardLayout::saveBoardLayout(const QString where) { + QFile f(where); + if (!f.open(IO_ReadWrite)) { + return false; + } + + QCString tmp = layoutMagic1_0.utf8(); + if (f.writeBlock(tmp, tmp.length()) == -1) { + return(false); + } + + for (int z=0; z0; x--) { + board[z][y][x] = board[z][y][x-1]; + } + board[z][y][0] = 0; + } + } +} +void BoardLayout::shiftUp() { + for (int z=0; z0; y--) { + for (int x=0; x= depth -1) + return false; + if( board[z+1][y][x] || board[z+1][y+1][x] || board[z+1][y][x+1] || board[z+1][y+1][x+1] ) { + return true; + } + return false; +} + + +bool BoardLayout::blockedLeftOrRight(short z, short y, short x) { + return( (board[z][y][x-1] || board[z][y+1][x-1]) && + (board[z][y][x+2] || board[z][y+1][x+2]) ); +} + +void BoardLayout::deleteTile(POSITION &p) { + if ( p.e +#include "KmTypes.h" + +const QString layoutMagic1_0 = "kmahjongg-layout-v1.0"; + +class BoardLayout { + +public: + BoardLayout(); + ~BoardLayout(); + + bool loadBoardLayout(const QString from); + bool saveBoardLayout(const QString where); + UCHAR getBoardData(short z, short y, short x) {return board[z][y][x];} + + // is there a tile anywhere above here (top left to bot right quarter) + bool tileAbove(short z, short y, short x); + bool tileAbove(POSITION &p) { return(tileAbove(p.e, p.y, p.x)); } + + // is this tile blocked to the left or right + bool blockedLeftOrRight(short z, short y, short x); + + void deleteTile(POSITION &p); + + bool anyFilled(POSITION &p); + bool allFilled(POSITION &p); + void insertTile(POSITION &p); + bool isTileAt(POSITION &p) { return board[p.e][p.y][p.x] == '1'; } + + + + const char *getBoardLayout(); + void copyBoardLayout(UCHAR *to , unsigned short &numTiles); + void clearBoardLayout(); + void shiftLeft(); + void shiftRight(); + void shiftUp(); + void shiftDown(); + + + enum { width = 32, + height = 16, + depth = 5 }; + enum { maxTiles = (depth*width*height)/4 }; + + QString &getFilename() {return filename;} + +protected: + + void initialiseBoard(); + +private: + QString filename; + QString loadedBoard; + UCHAR board[depth][height][width]; + unsigned short maxTileNum; +}; + +#endif diff --git a/kmahjongg/ChangeLog b/kmahjongg/ChangeLog new file mode 100644 index 00000000..dae64050 --- /dev/null +++ b/kmahjongg/ChangeLog @@ -0,0 +1,97 @@ + +This is the change log for kmahjongg. + +Personel + Albert Astals Cid: Some bug fixes and wishlists + Michael Haertjens: Solvable game generation + David Black: Fold, spindle and mutilate 0.4.1 to 0.5.0 + Osvaldo Stark: Tileset creator, doc guinepig and tester 0.5.0 + Robert Williams: Bug fixes 0.4.0 to 0.4.1 + Mathias Muller: Implementor of the original kmahjongg. + +0.7.4 to 0.7.6 ++ Fix bug #73944 ++ Implement wishlist #63171 ++ Implement wishlist #56607 ++ Fix bug #26595 + +0.7.3 to 0.7.4 ++ Fixed bug #31639 Kmahjongg flashes wrong tiles. ++ Fixed bug #26872 Kmahjongg timer not reset. + +0.7.2 to 0.7.3 ++ Added ability to generate solvable games. + This includes a fair amount of code in kmahjongg.cpp, plus support + for a new entry in the Preferences dialog. ++ Place checkmark on Game menu when Demo Mode is active. ++ Change "Show Hiscores" to "Show High Scores" in Game Menu. + +0.7.0 to 0.7.2 + ++ Fixed bug in show matching tiles so that tiles on the top row and + the left -most column are detected in matched (the dreaded off by + one error. Fix provided by Robert Schroeter. ++ Fixed bug in board generation where tiles went missing. Fix + provided by Robert Schroeter. ++ Fixed bug where the default tileset was listed twice in the + preview dialog ++ Fixed bug in the board editor, which stopped you putting + tiles in completely legal places. + +Version 0.4.1 to 0.7.0 (beta) + ++ Took over sources from Mathias Muller. ++ Changed tileset highlighting which previousely used a swap of two + fixed pallete colours, to a new tileset format where the designer + supplies the selected and unselected tile backgrounds. ++ Changed the main game engine to a 24 bit colour system. This removed + the old problem where the tileset and background combined could + only use a maximum of 128 colours. No limitations now exist. For + low color depths the game screen is dithered. ++ converted the rendering method to use pixmaps and blitting. ++ added a tool bar for commonly used features. ++ Added a tileset load feature (Yeah!!). ++ Added preferences dialog ++ Added preference to disable shadow generation (after complaints + about 3D visualisation. ++ Configuration and preferences now persist across sessions. ++ Tidied up transparency for tilesets. Now the top left colour of + the background tile determines the transparency colour, not a + fixed value of 0. ++ Added a hiscore system based on time taken, tiles removed and + penalties for using _cheats_ ++ Added a game timer to the tool bar. ++ added a hiscore dialog. ++ Added a pause mode for hiscore play (blanks the screen because lets face it + people cheat). ++ Background images may be tiled or scalled. Saved as a preference ++ The main rendering functions are now independant of the tileset + metric. In future this will allow for variable size tiles. ++ Added preview dialogs for load background, tileset and game board, + now you can see what you will get. ++ Added a redo to compliment the undo. ++ Fixed up the menu system and added more accelerators. ++ Added a play with mini-tiles option. Not necessarily for everyone, + but some people (well at least one, and thats me, so there) use + kmahjongg on an 800x600 lcd laptop display. So this helps! ++ Added a show removed tiles option. This allows you to determine if + it is safe to remove a pair etc. Nice aid to game play. ++ Moved the file selectors over to the kde style. This should + allow urls to be supplied for tilesets, backgrounds and boards etc. In + future I hope to maintain a web page with new tilesets and layout etc. + Ultimately it would be nice to have a per-boardlayout internet hiscore + table (mayhaps in version 0.6) ++ Started documenting how to design tilesets etc. ++ Fixed a few bugs and introduced many (probably) ++ Added an embryonic board editor ++ Rewrote game generation for new board layout possibilities ++ Speed improvements for tile set load using fixed masks ++ High score now displays time as well as score (requested feature) ++ Plus lots of bits I forgot :-) + + + +Version original to 0.4.1 +* [Robert Williams] New games now start at 10 +* [Robert Williams] Added kapp->getHelpMenu() +* [Robert Williams] Added -caption "%c" to kmahjongg.kdelnk diff --git a/kmahjongg/Editor.cpp b/kmahjongg/Editor.cpp new file mode 100644 index 00000000..4e141259 --- /dev/null +++ b/kmahjongg/Editor.cpp @@ -0,0 +1,675 @@ + +#include +#include +#include +#include + +#include "Editor.h" +#include "prefs.h" + +#include +#include // Needed to use KLocale +#include // +#include +#include + +#define ID_TOOL_NEW 100 +#define ID_TOOL_LOAD 101 +#define ID_TOOL_SAVE 102 +#define ID_TOOL_ADD 103 +#define ID_TOOL_DEL 104 +#define ID_TOOL_MOVE 105 +#define ID_TOOL_SELECT 106 +#define ID_TOOL_CUT 107 +#define ID_TOOL_COPY 108 +#define ID_TOOL_PASTE 109 +#define ID_TOOL_LEFT 110 +#define ID_TOOL_RIGHT 111 +#define ID_TOOL_UP 112 +#define ID_TOOL_DOWN 113 + +#define ID_TOOL_STATUS 199 + +#define ID_META_EXIT 201 + + + +// When we assign a tile to draw in a slot we do it in order from te following +// table, wrapping on the tile number. It makes the tile layout look more +// random. + + +Editor::Editor +( + QWidget* parent, + const char* name +) + : + QDialog( parent, name, true, 0 ), tiles(false) +{ + + clean= true; + numTiles=0; + mode = insert; + + int sWidth = (BoardLayout::width+2)*(tiles.qWidth()); + int sHeight =( BoardLayout::height+2)*tiles.qHeight(); + + sWidth += 4*tiles.shadowSize(); + + drawFrame = new FrameImage( this, "drawFrame" ); + drawFrame->setGeometry( 10, 40 ,sWidth ,sHeight); + drawFrame->setMinimumSize( 0, 0 ); + drawFrame->setMaximumSize( 32767, 32767 ); + drawFrame->setFocusPolicy( QWidget::NoFocus ); + drawFrame->setBackgroundMode( QWidget::PaletteBackground ); + drawFrame->setFrameStyle( 49 ); + drawFrame->setMouseTracking(true); + + // setup the tool bar + setupToolbar(); + + QVBoxLayout *layout = new QVBoxLayout(this, 1); + layout->addWidget(topToolbar,0); + layout->addWidget(drawFrame,1); + layout->activate(); + + resize( sWidth+60, sHeight+60); + setMinimumSize( sWidth+60, sHeight+60); + setMaximumSize( sWidth+60, sHeight+60); + + QString tile = Prefs::tileSet(); + tiles.loadTileset(tile); + + // tell the user what we do + setCaption(kapp->makeStdCaption(i18n("Edit Board Layout"))); + + + connect( drawFrame, SIGNAL(mousePressed(QMouseEvent *) ), + SLOT(drawFrameMousePressEvent(QMouseEvent *))); + connect( drawFrame, SIGNAL(mouseMoved(QMouseEvent *) ), + SLOT(drawFrameMouseMovedEvent(QMouseEvent *))); + + statusChanged(); + + update(); +} + + + +Editor::~Editor() +{ +} + +// --------------------------------------------------------- +void Editor::setupToolbar() +{ + + KIconLoader *loader = KGlobal::iconLoader(); + topToolbar = new KToolBar( this, "editToolBar" ); + KToolBarRadioGroup *radio = new KToolBarRadioGroup(topToolbar); + + // new game + topToolbar->insertButton(loader->loadIcon("filenew", KIcon::Toolbar), + ID_TOOL_NEW, true, i18n("New board")); + // open game + topToolbar->insertButton(loader->loadIcon("fileopen", KIcon::Toolbar), + ID_TOOL_LOAD, true, i18n("Open board")); + // save game + topToolbar->insertButton(loader->loadIcon("filesave", KIcon::Toolbar), + ID_TOOL_SAVE, true, i18n("Save board")); + topToolbar->setButtonIconSet(ID_TOOL_SAVE,loader->loadIconSet("filesave", KIcon::Toolbar)); + +#ifdef FUTURE_OPTIONS + // Select + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("rectangle_select", KIcon::Toolbar), + ID_TOOL_SELECT, true, i18n("Select")); + topToolbar->insertButton(loader->loadIcon("editcut", KIcon::Toolbar), + ID_TOOL_CUT, true, i18n("Cut")); + topToolbar->insertButton(loader->loadIcon("editcopy", KIcon::Toolbar), + ID_TOOL_COPY, true, i18n("Copy")); + topToolbar->insertButton(loader->loadIcon("editpaste", KIcon::Toolbar), + ID_TOOL_PASTE, true, i18n("Paste")); + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("move", KIcon::Toolbar), + ID_TOOL_MOVE, true, i18n("Move tiles")); +#endif + topToolbar->insertButton(loader->loadIcon("pencil", KIcon::Toolbar), + ID_TOOL_ADD, true, i18n("Add tiles")); + topToolbar->insertButton(loader->loadIcon("editdelete", KIcon::Toolbar), + ID_TOOL_DEL, true, i18n("Remove tiles")); + + topToolbar->setToggle(ID_TOOL_ADD); + topToolbar->setToggle(ID_TOOL_MOVE); + topToolbar->setToggle(ID_TOOL_DEL); + topToolbar->toggleButton(ID_TOOL_ADD); + radio->addButton(ID_TOOL_ADD); +#ifdef FUTURE_OPTIONS + radio->addButton(ID_TOOL_MOVE); +#endif + radio->addButton(ID_TOOL_DEL); + + // board shift + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("back", KIcon::Toolbar), + ID_TOOL_LEFT, true, i18n("Shift left")); + topToolbar->insertButton(loader->loadIcon("up", KIcon::Toolbar), + ID_TOOL_UP, true, i18n("Shift up")); + topToolbar->insertButton(loader->loadIcon("down", KIcon::Toolbar), + ID_TOOL_DOWN, true, i18n("Shift down")); + topToolbar->insertButton(loader->loadIcon("forward", KIcon::Toolbar), + ID_TOOL_RIGHT, true, i18n("Shift right")); + + topToolbar->insertSeparator(); + topToolbar->insertButton(loader->loadIcon("exit", KIcon::Toolbar), + ID_META_EXIT, true, i18n("Exit")); + + // status in the toolbar for now (ick) + + theLabel = new QLabel(statusText(), topToolbar); + int lWidth = theLabel->sizeHint().width(); + + topToolbar->insertWidget(ID_TOOL_STATUS,lWidth, theLabel ); + topToolbar->alignItemRight( ID_TOOL_STATUS, true ); + + //addToolBar(topToolbar); + connect( topToolbar, SIGNAL(clicked(int) ), SLOT( topToolbarOption(int) ) ); + + topToolbar->updateRects(0); + topToolbar->setFullSize(true); + topToolbar->setBarPos(KToolBar::Top); +// topToolbar->enableMoving(false); + topToolbar->adjustSize(); + setMinimumWidth(topToolbar->width()); + + +} + +void Editor::statusChanged() { + bool canSave = ((numTiles !=0) && ((numTiles & 1) == 0)); + theLabel->setText(statusText()); + topToolbar->setItemEnabled( ID_TOOL_SAVE, canSave); +} + + +void Editor::topToolbarOption(int option) { + + switch(option) { + case ID_TOOL_NEW: + newBoard(); + break; + case ID_TOOL_LOAD: + loadBoard(); + break; + case ID_TOOL_SAVE: + saveBoard(); + break; + case ID_TOOL_LEFT: + theBoard.shiftLeft(); + repaint(false); + break; + case ID_TOOL_RIGHT: + theBoard.shiftRight(); + repaint(false); + break; + case ID_TOOL_UP: + theBoard.shiftUp(); + repaint(false); + break; + case ID_TOOL_DOWN: + theBoard.shiftDown(); + repaint(false); + break; + case ID_TOOL_DEL: + mode=remove; + break; + + case ID_TOOL_MOVE: + mode=move; + break; + + case ID_TOOL_ADD: + mode = insert; + break; + case ID_META_EXIT: + close(); + break; + + default: + + break; + } + +} + +QString Editor::statusText() { + QString buf; + + int x=currPos.x; + int y=currPos.y; + int z= currPos.e; + + if (z == 100) + z = 0; + else + z=z+1; + + if (x >=BoardLayout::width || x <0 || y >=BoardLayout::height || y <0) + x = y = z = 0; + + buf = i18n("Tiles: %1 Pos: %2,%3,%4").arg(numTiles).arg(x).arg(y).arg(z); + return buf; +} + + +void Editor::loadBoard() { + + if ( !testSave() ) + return; + + KURL url = KFileDialog::getOpenURL( + NULL, + i18n("*.layout|Board Layout (*.layout)\n" + "*|All Files"), + this, + i18n("Open Board Layout" )); + + if ( url.isEmpty() ) + return; + + + theBoard.loadBoardLayout( url.path() ); + + repaint(false); +} + + +// Clear out the contents of the board. Repaint the screen +// set values to their defaults. +void Editor::newBoard() { + if (!testSave()) + return; + + + + theBoard.clearBoardLayout(); + + + + clean=true; + numTiles=0; + statusChanged(); + repaint(false); +} + +bool Editor::saveBoard() { + // get a save file name + KURL url = KFileDialog::getSaveURL( + NULL, + i18n("*.layout|Board Layout (*.layout)\n" + "*|All Files"), + this, + i18n("Save Board Layout" )); + if( url.isEmpty() ) return false; + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return false; + } + + if ( url.isEmpty() ) + return false; + + QFileInfo f( url.path() ); + if ( f.exists() ) { + // if it already exists, querie the user for replacement + int res=KMessageBox::warningContinueCancel(this, + i18n("A file with that name " + "already exists. Do you " + "wish to overwrite it?"), + i18n("Save Board Layout" ), KStdGuiItem::save()); + if (res != KMessageBox::Continue) + return false; + } + + bool result = theBoard.saveBoardLayout( url.path() ); + if (result==true){ + clean = true; + return true; + } else { + return false; + } +} + + +// test if a save is required and return true if the app is to continue +// false if cancel is selected. (if ok then call out to save the board +bool Editor::testSave() +{ + + if (clean) + return(true); + + int res; + res=KMessageBox::warningYesNoCancel(this, + i18n("The board has been modified. Would you " + "like to save the changes?"),QString::null,KStdGuiItem::save(),KStdGuiItem::dontSave()); + + if (res == KMessageBox::Yes) { + // yes to save + if (saveBoard()) { + return true; + } else { + KMessageBox::sorry(this, i18n("Save failed. Aborting operation.")); + } + } else { + return (res != KMessageBox::Cancel); + } + return(true); +} + + +// The main paint event, draw in the grid and blit in +// the tiles as specified by the layout. + +void Editor::paintEvent( QPaintEvent* ) { + + + // first we layer on a background grid + QPixmap buff; + QPixmap *dest=drawFrame->getPreviewPixmap(); + buff.resize(dest->width(), dest->height()); + drawBackground(&buff); + drawTiles(&buff); + bitBlt(dest, 0,0,&buff, 0,0,buff.width(), buff.height(), CopyROP); + + drawFrame->repaint(false); +} + +void Editor::drawBackground(QPixmap *pixmap) { + + QPainter p(pixmap); + + // blast in a white background + p.fillRect(0,0,pixmap->width(), pixmap->height(), QColor(white)); + + + // now put in a grid of tile quater width squares + int sy = (tiles.height()/2)+tiles.shadowSize(); + int sx = (tiles.width()/2); + + for (int y=0; y<=BoardLayout::height; y++) { + int nextY=sy+(y*tiles.qHeight()); + p.drawLine(sx, nextY,sx+(BoardLayout::width*tiles.qWidth()), nextY); + } + + for (int x=0; x<=BoardLayout::width; x++) { + int nextX=sx+(x*tiles.qWidth()); + p.drawLine(nextX, sy, nextX, sy+BoardLayout::height*tiles.qHeight()); + } +} + +void Editor::drawTiles(QPixmap *dest) { + + QPainter p(dest); + + QString tile1 = Prefs::tileSet(); + tiles.loadTileset(tile1); + + + int xOffset = tiles.width()/2; + int yOffset = tiles.height()/2; + short tile = 0; + + // we iterate over the depth stacking order. Each successive level is + // drawn one indent up and to the right. The indent is the width + // of the 3d relief on the tile left (tile shadow width) + for (int z=0; z=0; x--) { + int sx = x*(tiles.qWidth() )+xOffset; + int sy = y*(tiles.qHeight() )+yOffset; + if (theBoard.getBoardData(z, y, x) != '1') { + continue; + } + QPixmap *t; + tile=(z*BoardLayout::depth)+ + (y*BoardLayout::height)+ + (x*BoardLayout::width); +// if (mode==remove && currPos.x==x && currPos.y==y && currPos.e==z) { +// t = tiles.selectedPixmaps(44)); +// } else { + t = tiles.unselectedPixmaps(43); +// } + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + if ((x>1) && (y>0) && theBoard.getBoardData(z,y-1,x-2)=='1'){ + + bitBlt( dest, + sx+tiles.shadowSize(), sy, + t, tiles.shadowSize() ,0, + t->width()-tiles.shadowSize(), + t->height()/2, CopyROP ); + + + bitBlt( dest, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( dest, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + + + tile++; + tile = tile % 143; + } + } + xOffset +=tiles.shadowSize(); + yOffset -=tiles.shadowSize(); + } +} + + +// convert mouse position on screen to a tile z y x coord +// different to the one in kmahjongg.cpp since if we hit ground +// we return a result too. + +void Editor::transformPointToPosition( + const QPoint& point, + POSITION& MouseClickPos, + bool align) +{ + + short z = 0; // shut the compiler up about maybe uninitialised errors + short y = 0; + short x = 0; + MouseClickPos.e = 100; + + // iterate over z coordinate from top to bottom + for( z=BoardLayout::depth-1; z>=0; z-- ) + { + // calculate mouse coordiantes --> position in game board + // the factor -theTiles.width()/2 must keep track with the + // offset for blitting in the print zvent (FIX ME) + x = ((point.x()-tiles.width()/2)-(z+1)*tiles.shadowSize())/ tiles.qWidth(); + y = ((point.y()-tiles.height()/2)+ z*tiles.shadowSize()) / tiles.qHeight(); + + + // skip when position is illegal + if (x<0 || x>=BoardLayout::width || y<0 || y>=BoardLayout::height) + continue; + + // + switch( theBoard.getBoardData(z,y,x) ) + { + case (UCHAR)'3': if (align) { + x--; + y--; + } + break; + + case (UCHAR)'2': if (align) + x--; + break; + + case (UCHAR)'4': if (align) + y--; + break; + + case (UCHAR)'1': break; + + default : continue; + } + // if gameboard is empty, skip + if ( ! theBoard.getBoardData(z,y,x) ) + continue; + + // here, position is legal + MouseClickPos.e = z; + MouseClickPos.y = y; + MouseClickPos.x = x; + MouseClickPos.f = theBoard.getBoardData(z,y,x); + break; + } + if (MouseClickPos.e == 100) { + MouseClickPos.x = x; + MouseClickPos.y = y; + MouseClickPos.f=0; + } +} + + +// we swallow the draw frames mouse clicks and process here +void Editor::drawFrameMousePressEvent( QMouseEvent* e ) +{ + + POSITION mPos; + transformPointToPosition(e->pos(), mPos, (mode == remove)); + + switch (mode) { + case remove: + if (!theBoard.tileAbove(mPos) && mPos.e < BoardLayout::depth && theBoard.isTileAt(mPos) ) { + theBoard.deleteTile(mPos); + numTiles--; + statusChanged(); + drawFrameMouseMovedEvent(e); + repaint(false); + } + break; + case insert: { + POSITION n = mPos; + if (n.e == 100) + n.e = 0; + else + n.e += 1; + if (canInsert(n)) { + theBoard.insertTile(n); + numTiles++; + statusChanged(); + repaint(false); + } + } + break; + default: + break; + } + +} + + +void Editor::drawCursor(POSITION &p, bool visible) +{ + int x = (tiles.width()/2)+(p.e*tiles.shadowSize())+(p.x * tiles.qWidth()); + int y = (tiles.height()/2)-(p.e*tiles.shadowSize())+(p.y * tiles.qHeight()); + int w = tiles.width(); + int h = tiles.height(); + + + if (p.e==100 || !visible) + x = -1; + drawFrame->setRect(x,y,w,h, tiles.shadowSize(), mode-remove); + drawFrame->repaint(false); + + + +} + + + +// we swallow the draw frames mouse moves and process here +void Editor::drawFrameMouseMovedEvent( QMouseEvent* e ){ + + + POSITION mPos; + transformPointToPosition(e->pos(), mPos, (mode == remove)); + + if ((mPos.x==currPos.x) && (mPos.y==currPos.y) && (mPos.e==currPos.e)) + return; + currPos = mPos; + + statusChanged(); + + switch(mode) { + case insert: { + POSITION next; + next = currPos; + if (next.e == 100) + next.e = 0; + else + next.e += 1; + + drawCursor(next, canInsert(next)); + break; + } + case remove: + drawCursor(currPos, 1); + break; + + case move: + + break; + + } + +} + +// can we inser a tile here. We can iff +// there are tiles in all positions below us (or we are a ground level) +// there are no tiles intersecting with us on this level + +bool Editor::canInsert(POSITION &p) { + + + if (p.e >= BoardLayout::depth) + return (false); + if (p.y >BoardLayout::height-2) + return false; + if (p.x >BoardLayout::width-2) + return false; + + POSITION n = p; + if (p.e != 0) { + n.e -= 1; + if (!theBoard.allFilled(n)) { + return(false); + } + } + int any = theBoard.anyFilled(p); + return(!any); + +} + + + +#include "Editor.moc" diff --git a/kmahjongg/Editor.h b/kmahjongg/Editor.h new file mode 100644 index 00000000..5ced0daf --- /dev/null +++ b/kmahjongg/Editor.h @@ -0,0 +1,68 @@ +#ifndef _EditorLoadBase_H +#define _EditorLoadBase_H + +#include +#include +#include +#include +#include + +#include "Tileset.h" +#include "BoardLayout.h" +#include "Background.h" + +#include "Preview.h" + +class Editor: public QDialog +{ + Q_OBJECT + +public: + + + Editor + ( + QWidget* parent = NULL, + const char* name = NULL + ); + + virtual ~Editor(); + + + +protected slots: + void topToolbarOption(int w); + void drawFrameMousePressEvent ( QMouseEvent* ); + void drawFrameMouseMovedEvent ( QMouseEvent *); + + +protected: + enum {remove=98, insert=99, move=100}; + void paintEvent( QPaintEvent* pa ); + void setupToolbar(); + void loadBoard(); + bool saveBoard(); + void newBoard(); + void drawBackground(QPixmap *to); + void drawTiles(QPixmap *to); + bool testSave(); + void transformPointToPosition(const QPoint &, POSITION &, bool align); + void drawCursor(POSITION &p, bool visible); + bool canInsert(POSITION &p); + void statusChanged(); + QString statusText(); +private: + int mode; + int numTiles; + KToolBar *topToolbar; + FrameImage * drawFrame; + Tileset tiles; + BoardLayout theBoard; + bool clean; + POSITION currPos; + QLabel *theLabel; +private: + +}; + +#endif diff --git a/kmahjongg/GameTimer.cpp b/kmahjongg/GameTimer.cpp new file mode 100644 index 00000000..5eb827d9 --- /dev/null +++ b/kmahjongg/GameTimer.cpp @@ -0,0 +1,94 @@ +/* + * Based upon the QT example dclock + */ + +#include +#include "GameTimer.h" +#include "GameTimer.moc" + + +// +// Constructs a GameTimer widget with a parent and a name. +// + +GameTimer::GameTimer( QWidget *parent, const char *name ) + : QLCDNumber( parent, name ) +{ + showingColon = false; + setNumDigits(7); + setFrameStyle(QFrame::Panel | QFrame::Sunken); + setFrameStyle(QFrame::NoFrame); + timerMode = stopped; + showTime(); // display the current time1 + startTimer( 500 ); // 1/2 second timer events +} + +// QObject timer call back implementation +void GameTimer::timerEvent( QTimerEvent * ) +{ + if (timerMode == running) + theTimer=theTimer.addMSecs(500); + showTime(); +} + + +// +// Shows the current time in the internal lcd widget. +// + +void GameTimer::showTime() +{ + QString s; + showingColon = !showingColon; // toggle/blink colon + + switch(timerMode) { + case paused: + case running: + s = theTimer.toString(); + break; + case stopped: + s = "00:00:00"; + break; + } + + if ( !showingColon ) + s[2] = s[5] = ' '; + display( s ); // set LCD number/text +} + +void GameTimer::start() { + theTimer.setHMS(0,0,0); + timerMode = running; +} + + +void GameTimer::fromString(const char*tim) { + int h,m,s; + sscanf(tim, "%2d:%2d:%2d\n", &h, &m, &s); + theTimer.setHMS(h,m,s); + timerMode = running; +} + + +void GameTimer::stop() { + timerMode = stopped; +} + +void GameTimer::pause() { + + if (timerMode == stopped) + return; + + if (timerMode == paused) { + timerMode = running; + } else { + timerMode = paused; + } +} + +int GameTimer::toInt() { + + return (theTimer.second()+ + theTimer.minute()*60+ + theTimer.hour()*360); +} diff --git a/kmahjongg/GameTimer.h b/kmahjongg/GameTimer.h new file mode 100644 index 00000000..6cde5d11 --- /dev/null +++ b/kmahjongg/GameTimer.h @@ -0,0 +1,53 @@ +/* ------------------------------------------------------------------------- + -- kmahjongg timer. Based on a slightly modified verion of the QT demo -- + -- program dclock. Copyright as shown below. -- + ------------------------------------------------------------------------- */ + +/**************************************************************************** +** $Id$ +** +** Copyright (C) 1992-1998 Troll Tech AS. All rights reserved. +** +** This file is part of an example program for Qt. This example +** program may be used, distributed and modified without limitation. +** +*****************************************************************************/ + +#ifndef KM_GAME_TIMER +#define KM_GAME_TIMER + +#include +#include + +enum TimerMode {running = -53 , stopped= -54 , paused = -55}; + +class GameTimer: public QLCDNumber +{ + Q_OBJECT +public: + GameTimer( QWidget *parent=0, const char *name=0 ); + + int toInt(); + QString toString() {return theTimer.toString();} + void fromString(const char *); + +protected: // event handlers + void timerEvent( QTimerEvent * ); + +public slots: + void start(); + void stop(); + void pause(); + + +private slots: // internal slots + void showTime(); + +private: // internal data + bool showingColon; + QTime theTimer; + TimerMode timerMode; +}; + + +#endif diff --git a/kmahjongg/HighScore.cpp b/kmahjongg/HighScore.cpp new file mode 100644 index 00000000..2e55c15a --- /dev/null +++ b/kmahjongg/HighScore.cpp @@ -0,0 +1,541 @@ +#include +#include "HighScore.h" +#include "HighScore.moc" + + +#include +#include +#include +#include "klocale.h" +#include +#include +#include +#include +#include +#include +#include + +static const QString highScoreMagic1_0 = "kmahjongg-scores-v1.0"; +static const QString highScoreMagic1_1 = "kmahjongg-scores-v1.1"; + +static const char * highScoreFilename = "/kmahjonggHiscores"; + +const char * defNames[numScores] = { + "David Black", + "Mathias Mueller", + "Osvaldo Stark", + "Steve Taylor", + "Clare Brizzolara", + "Angela Simpson", + "Michael O'Brien", + "Kelvin Bell", + "Jenifferi O'Keeffe", + "Phil Lamdin" +}; + +int defScores[numScores] = + {400, 350, 300, 250, 200, 150, 100, 50, 20, 10}; + +const int ages = 59+(59*60)+(2*60*60); +int defTimes[numScores] = {ages, ages-1, ages-2, ages-3, + ages-4, ages-5, ages-6, ages-7, ages-8, ages-9}; + + +HighScore::HighScore +( + QWidget* parent, + const char* name +) + : + QDialog( parent, name, true, 0 ) +{ + + // form the target name + + + filename = locateLocal("appdata", highScoreFilename); + + QFont fnt; + // Number + QLabel* qtarch_Label_3; + qtarch_Label_3 = new QLabel( this, "Label_3" ); + qtarch_Label_3->setGeometry( 10, 45, 30, 30 ); + qtarch_Label_3->setFrameStyle( 50 ); + qtarch_Label_3->setText( i18n("Pos") ); + qtarch_Label_3->setAlignment( AlignCenter ); + fnt = qtarch_Label_3->font(); + fnt.setBold(true); + qtarch_Label_3->setFont(fnt); + + + // name + + QLabel* qtarch_Label_4; + qtarch_Label_4 = new QLabel( this, "Label_4" ); + qtarch_Label_4->setGeometry( 40, 45, 150, 30 ); + qtarch_Label_4->setFrameStyle( 50 ); + qtarch_Label_4->setText( i18n("Name") ); + qtarch_Label_4->setFont(fnt); + + + // board number + QLabel* boardTitle; + boardTitle= new QLabel( this, "" ); + boardTitle->setGeometry( 190, 45, 80, 30 ); + boardTitle->setFrameStyle( 50 ); + boardTitle->setText( i18n("Board") ); + boardTitle->setFont(fnt); + + // score + QLabel* qtarch_Label_5; + qtarch_Label_5 = new QLabel( this, "Label_5" ); + qtarch_Label_5->setGeometry( 270, 45, 70, 30 ); + qtarch_Label_5->setFrameStyle( 50 ); + qtarch_Label_5->setText( i18n("Score") ); + qtarch_Label_5->setFont(fnt); + + // time + QLabel* qtarch_Label_6; + qtarch_Label_6 = new QLabel( this, "Label_6" ); + qtarch_Label_6->setGeometry( 340, 45, 70, 30 ); + qtarch_Label_6->setFrameStyle( 50 ); + qtarch_Label_6->setText( i18n("Time") ); + qtarch_Label_6->setFont(fnt); + + + + for (int row=0; rowsetGeometry( 110+35, 340+50, 100, 30 ); + qtarch_PushButton_1->setMinimumSize( 0, 0 ); + qtarch_PushButton_1->setMaximumSize( 32767, 32767 ); + qtarch_PushButton_1->setFocusPolicy( QWidget::TabFocus ); + qtarch_PushButton_1->setAutoRepeat( false ); + qtarch_PushButton_1->setAutoResize( false ); + qtarch_PushButton_1->setDefault(true); + + QPushButton* resetBtn; + resetBtn= new QPushButton( this, "resetBtn" ); + resetBtn->setGeometry( 10, 5, 25, 25); + resetBtn->setMinimumSize( 0, 0 ); + resetBtn->setMaximumSize( 32767, 32767 ); + resetBtn->setFocusPolicy( QWidget::TabFocus ); + //resetBtn->setText(i18n( "Reset" )); + resetBtn->setAutoRepeat( false ); + resetBtn->setAutoResize( false ); + + KIconLoader *loader = KGlobal::iconLoader(); + resetBtn->setPixmap(loader->loadIcon("editdelete", KIcon::Toolbar)); + + + + /* We create the ediat area for the hi score name entry and move it */ + /* off screen. it is moved over and placed in position when a */ + /* new name is added */ + + lineEdit = new QLineEdit(this, ""); + lineEdit->setGeometry( 50, 40+(20*30), 190, 30 ); + lineEdit->setFocusPolicy(QWidget::StrongFocus); + lineEdit->setFrame(true); + lineEdit->setEchoMode(QLineEdit::Normal); + lineEdit->setText(""); + + // the drop down for the board names + + combo = new QComboBox( false, this, "combo" ); + combo->setGeometry( 65, 5, 220, 25 ); + combo->setMinimumSize( 0, 0 ); + combo->setMaximumSize( 32767, 32767 ); + combo->setFocusPolicy( QWidget::StrongFocus ); + combo->setSizeLimit( 10 ); + combo->setAutoResize( false ); + connect( combo, SIGNAL(activated(int)), SLOT(selectionChanged(int)) ); + + + resize( 350+70,390+45 ); + setFixedSize(350+70,390+45); + + tables = NULL; + loadTables(); + currTable = tables; + + setCaption(kapp->makeStdCaption(i18n("Scores"))); + + selectedLine = -1; + + connect(lineEdit, SIGNAL( textChanged(const QString &)), + SLOT( nameChanged(const QString &))); + + + connect(qtarch_PushButton_1, SIGNAL(clicked()), SLOT(reject())); + connect(resetBtn, SIGNAL(clicked()), SLOT(reset())); +} + +// free up the table structures + +HighScore::~HighScore() +{ + TableInstance *t, *t1; + + if (tables != NULL) { + t = tables; + while(t != NULL) + { + t1 = t; + t = t -> next; + delete t1; + } + } + tables = NULL; +} + + + +// return a pointer to a linked list of table entries from the +// saved hi score tables file. If no such file exists then +// return a single default hiscore table. + +void HighScore::loadTables() { + char buff[1024]; + + // open the file, on error set up the default table + FILE *fp = fopen( QFile::encodeName(highScoreFile()), "r"); + if (fp == NULL) + goto error; + + // check magic + fscanf(fp, "%1023s\n", buff); + if (highScoreMagic1_1 != buff) { + goto error; + } + + int num; + fscanf(fp, "%d\n", &num); + + for (int p=0; pnext = tables; + + + tables = t; + + fgets(buff, sizeof(buff), fp); + if (buff[strlen(buff)-1] == '\n') + buff[strlen(buff)-1] = '\0'; + t->name = buff; + combo->insertItem(t->name); + setComboTo(t->name); + for (int e=0; eentries[e].score)); + fscanf(fp, "%ld\n", &(t->entries[e].elapsed)); + fscanf(fp, "%ld\n", &(t->entries[e].board)); + fgets(buff, sizeof(buff), fp); + if (buff[strlen(buff)-1] == '\n') + buff[strlen(buff)-1] = '\0'; + t->entries[e].name=QString::fromUtf8(buff,-1); + } + } + + + fclose(fp); + return; + + +error: + selectTable("default"); + saveTables(); + return; +} + +void HighScore::saveTables() { + + TableInstance *p; + int num = 0; + + // Nothing to do + if (tables == NULL) + return; + + + // open the outrput file, with naff error handling + FILE *fp = fopen( QFile::encodeName(highScoreFile()), "w"); + if (fp == NULL) + return; + + // count up the number of tables to save + for (p=tables; p != NULL; p = p->next) + num++; + + // output the file magic + fprintf(fp,"%s\n", highScoreMagic1_1.utf8().data()); + + // output the count of tables to process + fprintf(fp, "%d\n", num); + + // output each table + for (p=tables; p != NULL; p = p->next) { + fprintf(fp, "%s\n", p->name.utf8().data()); + for (int e=0; eentries[e].score, + p->entries[e].elapsed, + p->entries[e].board, + p->entries[e].name.utf8().data()); + } + } + fclose(fp); + +} + +// traverse the list of hi score tables and set the +// current table to the specified board. Create it if it does not +// exist. + +void HighScore::selectTable(const QString &board) { + + TableInstance *pos = tables; + + + while (pos != NULL) { + if (pos->name == board) + break; + pos = pos->next; + } + + + + if (pos == NULL) { + // not found, add new board to the front of the list + TableInstance *n = new TableInstance; + n->next = tables; + n->name = board; + + + + for (int p =0; p < numScores; p ++) { + n->entries[p].name = defNames[p]; + n->entries[p].score = defScores[p]; + n->entries[p].board = 928364243l+(p *3); + n->entries[p].elapsed = defTimes[p]; + } + tables = n; + currTable = n; + combo->insertItem(board); + setComboTo(board); + return; + } + currTable = pos; + setComboTo(board); + return; +} + + + +void HighScore::addRow(int num) { + QFont tmp; + + // game number + numbersWidgets[num] = new QLabel( this); + numbersWidgets[num]->setGeometry( 10, 75+(num*30), 30, 30 ); + numbersWidgets[num]->setFrameStyle( 50 ); + numbersWidgets[num]->setAlignment( AlignRight | AlignVCenter ); + numbersWidgets[num]->setNum(num+1); + + + // name + namesWidgets[num] = new QLabel( this); + namesWidgets[num]->setGeometry( 40, 75+(num*30), 150, 30 ); + namesWidgets[num]->setFrameStyle( 50 ); + namesWidgets[num]->setAlignment( 289 ); + + // board + boardWidgets[num] = new QLabel( this); + boardWidgets[num]->setGeometry( 190, 75+(num*30), 80, 30 ); + boardWidgets[num]->setFrameStyle( 50 ); + boardWidgets[num]->setAlignment( 289 ); + + // score + scoresWidgets[num] = new QLabel( this); + scoresWidgets[num]->setGeometry( 270, 75+(num*30), 70, 30 ); + scoresWidgets[num]->setFrameStyle( 50 ); + tmp = scoresWidgets[num]->font(); + tmp.setItalic(true); + scoresWidgets[num]->setFont(tmp); + + // elapsed time + elapsedWidgets[num] = new QLabel( this); + elapsedWidgets[num]->setGeometry( 270+70, 75+(num*30), 70, 30 ); + elapsedWidgets[num]->setFrameStyle( 50 ); + tmp = elapsedWidgets[num]->font(); + tmp.setItalic(true); + elapsedWidgets[num]->setFont(tmp); + + +} + +void HighScore::copyTableToScreen(const QString &name) { + char buff[256]; + QString base; + getBoardName(name, base); + selectTable(base); + for (int p=0; psetNum((int)currTable->entries[p].score); + namesWidgets[p]->setText(currTable->entries[p].name); + boardWidgets[p]->setNum((int)currTable->entries[p].board); + + int e = currTable->entries[p].elapsed; + int s = e % 60; + e = e-s; + int m = (e % (60*60)) / 60; + e = e - (e % (60*60)); + int h = (e % (60*60*60)) / (60*60); + sprintf(buff, "%2.2d:%2.2d:%2.2d", h, m , s); + elapsedWidgets[p]->setText(buff); + + } + repaint(false); +} + +int HighScore::exec(QString &layout) { + copyTableToScreen(layout); + return(QDialog::exec()); +} + +void HighScore::checkHighScore(int s, int e, long gameNum, QString &name) { + int pos; + + QString board; + getBoardName(name, board); + + // make this board name the current one! + // creates it if it does not exist + selectTable(board); + + + for (pos=0; pos currTable->entries[pos].score) { + break; + } + } + if (pos >= numScores) { + return; + } + for (int move= numScores-1; move >pos; move--) { + currTable->entries[move].score = currTable->entries[move-1].score; + currTable->entries[move].name = currTable->entries[move-1].name; + currTable->entries[move].board = currTable->entries[move-1].board; + currTable->entries[move].elapsed = currTable->entries[move-1].elapsed; + } + + currTable->entries[pos].score = s; + currTable->entries[pos].board = gameNum; + currTable->entries[pos].elapsed = e; + + lineEdit->setEnabled(true); + lineEdit->setGeometry( 40, 75+(pos*30), 150, 30 ); + lineEdit->setFocus(); + lineEdit->setText(""); + selectedLine = pos; + nameChanged(""); + + // no board change when entering data + combo->setEnabled(false); + exec(board); + combo->setEnabled(true); + + selectedLine = -1; + lineEdit->setGeometry( 40, 75+(20*30), 150, 30); + lineEdit->setEnabled(false); + + // sync the hiscore table to disk now + saveTables(); + +} + +void HighScore::nameChanged(const QString &s) { + + if (selectedLine == -1) + return; + + if (s.isEmpty()) + currTable->entries[selectedLine].name = + i18n("Anonymous"); + else + currTable->entries[selectedLine].name = s; +} + + +void HighScore::getBoardName(QString in, QString &out) { + + QFileInfo fi( in ); + out = fi.baseName(); +} + +void HighScore::setComboTo(const QString &to) { + for (int p=0; pcount(); p++) { + if (combo->text(p) == to) + combo->setCurrentItem(p); + } +} + + +void HighScore::selectionChanged(int ) { + copyTableToScreen(combo->currentText()); + +} + + +// reset the high score table. Caution the user +// before acting + + +void HighScore::reset() { + + int res=KMessageBox::warningContinueCancel(this, + i18n("Resetting the high scores will " + "remove all high score entries " + "both in memory and on disk. Do " + "you wish to proceed?"), + i18n("Reset High Scores"),i18n("Reset")); + if (res != KMessageBox::Continue) + return ; + + // delete the file + res = unlink( QFile::encodeName(highScoreFile())); + + // wipe ou the in memory list of tables + TableInstance *t, *d; + + if (tables != NULL) { + t = tables; + while (t != NULL) { + d = t; + t = t->next; + d->next=0; + delete d; + } + + } + + // set the list empty + tables = NULL; + currTable=NULL; + + // clear out the combobox + combo->clear(); + + // stick in a default + selectTable("default"); + + // make sure tha the on screen data does not + // point to deleted data + copyTableToScreen("default"); +} + +QString &HighScore::highScoreFile() { + return filename; + +} diff --git a/kmahjongg/HighScore.h b/kmahjongg/HighScore.h new file mode 100644 index 00000000..5db36819 --- /dev/null +++ b/kmahjongg/HighScore.h @@ -0,0 +1,77 @@ + +#ifndef HighScore_included +#define HighScore_included + +#include + + +class QLineEdit; +class QComboBox; +class QLabel; + +const int numScores = 10; + +typedef struct HiScoreEntry { + QString name; + long board; + long score; + long elapsed; + +}; + +typedef struct TableInstance { + QString name; + HiScoreEntry entries[numScores]; + TableInstance *next; +}; + + +class HighScore : public QDialog +{ + Q_OBJECT + +public: + + HighScore + ( + QWidget* parent = NULL, + const char* name = NULL + ); + + virtual ~HighScore(); + + int exec(QString &layout); + + + void checkHighScore(int score, int elapsed, long game, QString &board); +public slots: + void selectionChanged(int); + +protected slots: + void nameChanged(const QString &s); + void reset(); +private: + void addRow(int num); // generate one table row + void loadTables(); // initialise from saved + void saveTables(); // save to disc. + void getBoardName(QString in, QString &out); + void selectTable(const QString &name); + void setComboTo(const QString &to); + void copyTableToScreen(const QString &name); + QString &highScoreFile(); + + int selectedLine; + QLineEdit *lineEdit; + QLabel* numbersWidgets[numScores]; + QLabel* boardWidgets[numScores]; + QLabel* namesWidgets[numScores]; + QLabel* scoresWidgets[numScores]; + QLabel* elapsedWidgets[numScores]; + QComboBox* combo; + QString filename; + + TableInstance *tables; + TableInstance *currTable; +}; + +#endif // HighScore_included diff --git a/kmahjongg/KmTypes.h b/kmahjongg/KmTypes.h new file mode 100644 index 00000000..8f85978a --- /dev/null +++ b/kmahjongg/KmTypes.h @@ -0,0 +1,27 @@ +#ifndef _KM_TYPES_ +#define _KM_TYPES_ + +//---------------------------------------------------------- +// TYPEDEFS +//---------------------------------------------------------- +typedef unsigned char UCHAR; +typedef unsigned char BYTE; +typedef unsigned short USHORT; +typedef unsigned long ULONG; + + +typedef struct pos { + pos() : e(0), y(0), x(0), f(0) { } + USHORT e,y,x,f; +} POSITION; + +typedef struct dep { + int turn_dep[4]; // Turn dependencies + int place_dep[4]; // Placing dependencies + int lhs_dep[2]; // Left side dependencies, same level + int rhs_dep[2]; // Right side dependencies, same level + bool filled; // True if this tile has been placed. + bool free; // True if this tile can be removed? +} DEPENDENCY; + +#endif diff --git a/kmahjongg/Makefile.am b/kmahjongg/Makefile.am new file mode 100644 index 00000000..7eba27b1 --- /dev/null +++ b/kmahjongg/Makefile.am @@ -0,0 +1,25 @@ +SUBDIRS = pics + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +KDE_ICON = kmahjongg + +bin_PROGRAMS = kmahjongg +kmahjongg_SOURCES = main.cpp kmahjongg.cpp boardwidget.cpp \ + Tileset.cpp BoardLayout.cpp GameTimer.cpp \ + Background.cpp Preview.cpp prefs.kcfgc \ + Editor.cpp HighScore.cpp settings.ui +kmahjongg_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kmahjongg_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) +kmahjongg_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +xdg_apps_DATA = kmahjongg.desktop +kde_kcfg_DATA = kmahjongg.kcfg + +rcdir = $(kde_datadir)/kmahjongg +rc_DATA = kmahjonggui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kmahjongg.pot + diff --git a/kmahjongg/Preview.cpp b/kmahjongg/Preview.cpp new file mode 100644 index 00000000..edd8ef79 --- /dev/null +++ b/kmahjongg/Preview.cpp @@ -0,0 +1,513 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "prefs.h" +#include "Preview.h" + +static const char * themeMagicV1_0= "kmahjongg-theme-v1.0"; + +Preview::Preview(QWidget* parent) : KDialogBase(parent), m_tiles(true) +{ + KPushButton *loadButton; + QGroupBox *group; + QVBox *page; + + page = new QVBox(this); + + group = new QHGroupBox(page); + + m_combo = new QComboBox(false, group); + connect(m_combo, SIGNAL(activated(int)), SLOT(selectionChanged(int))); + + loadButton = new KPushButton(i18n("Load..."), group); + connect( loadButton, SIGNAL(clicked()), SLOT(load()) ); + + m_drawFrame = new FrameImage(page); + m_drawFrame->setFixedSize(310, 236); + + m_changed = false; + + setMainWidget(page); + setFixedSize(sizeHint()); +} + +Preview::~Preview() +{ +} + +void Preview::selectionChanged(int which) +{ + m_selectedFile = m_fileList[which]; + drawPreview(); + m_drawFrame->repaint(0,0,-1,-1,false); + markChanged(); +} + +bool Preview::isChanged() +{ + return m_changed; +} + +void Preview::markChanged() +{ + m_changed = true; +} + +void Preview::markUnchanged() +{ + m_changed = false; +} + +void Preview::initialise(const PreviewType type) +{ + QString extension; + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + // set up the concept of the current file. Initialised to the preferences + // value initially. Set the caption to indicate what we are doing + switch (type) + { + case background: + setCaption(i18n("Change Background Image")); + m_selectedFile = back; + m_fileSelector = i18n("*.bgnd|Background Image (*.bgnd)\n"); + m_fileSelector += KImageIO::pattern()+"\n"; + extension = "*.bgnd"; + break; + + case tileset: + setCaption(i18n("Change Tile Set")); + m_fileSelector = i18n("*.tileset|Tile Set File (*.tileset)\n"); + m_selectedFile = tile; + extension = "*.tileset"; + break; + + case board: + m_fileSelector = i18n("*.layout|Board Layout File (*.layout)\n"); + setCaption(i18n("Change Board Layout")); + m_selectedFile = layout; + extension = "*.layout"; + break; + + case theme: + m_fileSelector = i18n("*.theme|KMahjongg Theme File (*.theme)\n"); + setCaption(i18n("Choose Theme")); + m_selectedFile=""; + extension = "*.theme"; + + m_themeLayout=""; + m_themeBack=""; + m_themeTileset=""; + + default: + break; + } + + m_fileSelector += i18n("*|All Files"); + enableButtonApply(type != board); + + m_previewType = type; + // we start with no change indicated + markUnchanged(); + + m_fileList = kapp->dirs()->findAllResources("appdata", "pics/*"+extension, false, true); + + // get rid of files from the last invocation + m_combo->clear(); + + QStringList names; + QStringList::const_iterator it, itEnd; + it = m_fileList.begin(); + itEnd = m_fileList.end(); + for ( ; it != itEnd; ++it) + { + QFileInfo fi(*it); + names << fi.baseName(); + } + + m_combo->insertStringList(names); + m_combo->setEnabled(m_fileList.count()); + drawPreview(); +} + +void Preview::slotApply() { + if (isChanged()) { + applyChange(); + markUnchanged(); + } +} + +void Preview::slotOk() { + slotApply(); + accept(); +} + +void Preview::load() { + KURL url = KFileDialog::getOpenURL(QString::null, m_fileSelector, this, i18n("Open Board Layout" )); + if ( !url.isEmpty() ) { + m_selectedFile = url.path(); + drawPreview(); + m_drawFrame->repaint(0,0,-1,-1,false); + markChanged(); + } +} + +// Top level preview drawing method. Background, tileset and layout +// are initialised from the preferences. Depending on the type +// of preview dialog we pick up the selected file for one of these +// chaps. + +void Preview::drawPreview() +{ + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + switch (m_previewType) + { + case background: + back = m_selectedFile; + break; + + case tileset: + tile = m_selectedFile; + break; + + case board: + layout = m_selectedFile; + break; + + case theme: + // a theme is quite a bit of work. We load the + // specified bits in (layout, background and tileset + if (!m_selectedFile.isEmpty()) + { + QString backRaw, layoutRaw, tilesetRaw, magic; + + QFile in(m_selectedFile); + if (in.open(IO_ReadOnly)) + { + QTextStream stream(&in); + magic = stream.readLine(); + if (magic != themeMagicV1_0) + { + in.close(); + KMessageBox::sorry(this, i18n("That is not a valid theme file.")); + break; + } + tilesetRaw = stream.readLine(); + backRaw = stream.readLine(); + layoutRaw = stream.readLine(); + in.close(); + + tile = tilesetRaw; + tile.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(tile)) + { + tile = tilesetRaw; + tile = "pics/" + tile.right(tile.length() - tile.find(":") - 1 ); + tile = locate("appdata", tile); + } + + back = backRaw; + back.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(back)) + { + back = backRaw; + back = "pics/" + back.right(back.length() - back.find(":") - 1); + back = locate("appdata", back); + } + + layout = layoutRaw; + layout.replace(":", "/kmahjongg/pics/"); + if (!QFile::exists(layout)) + { + layout = layoutRaw; + layout = "pics/" + layout.right(layout.length() - layout.find(":") - 1); + layout = locate("appdata", layout); + } + + m_themeBack=back; + m_themeLayout=layout; + m_themeTileset=tile; + } + } + break; + } + + renderBackground(back); + renderTiles(tile, layout); +} + +void Preview::paintEvent( QPaintEvent* ){ + m_drawFrame->repaint(false); +} + +// the user selected ok, or apply. This method passes the changes +// across to the game widget and if necessary forces a board redraw +// (unnecessary on layout changes since it only effects the next game) +void Preview::applyChange() +{ + switch (m_previewType) + { + case background: + loadBackground(m_selectedFile, false); + break; + + case tileset: + loadTileset(m_selectedFile); + break; + + case board: + loadBoard(m_selectedFile); + break; + + case theme: + if (!m_themeLayout.isEmpty() && !m_themeBack.isEmpty() && !m_themeTileset.isEmpty()) + { + loadBackground(m_themeBack, false); + loadTileset(m_themeTileset); + loadBoard(m_themeLayout); + } + break; + } + + // don't redraw for a layout change + if (m_previewType == board || m_previewType == theme) layoutChange(); + else boardRedraw(true); + + // either way we mark the current value as unchanged + markUnchanged(); +} + +// Render the background to the pixmap. +void Preview::renderBackground(const QString &bg) { + QImage img; + QImage tmp; + QPixmap *p; + QPixmap *b; + p = m_drawFrame->getPreviewPixmap(); + m_back.load(bg, p->width(), p->height()); + b = m_back.getBackground(); + bitBlt( p, 0,0, + b,0,0, b->width(), b->height(), CopyROP ); +} + +// This method draws a mini-tiled board with no tiles missing. + +void Preview::renderTiles(const QString &file, const QString &layout) { + m_tiles.loadTileset(file, true); + m_boardLayout.loadBoardLayout(layout); + + QPixmap *dest = m_drawFrame->getPreviewPixmap(); + int xOffset = m_tiles.width()/2; + int yOffset = m_tiles.height()/2; + short tile = 0; + + // we iterate over the depth stacking order. Each successive level is + // drawn one indent up and to the right. The indent is the width + // of the 3d relief on the tile left (tile shadow width) + for (int z=0; z=0; x--) { + int sx = x*(m_tiles.qWidth() )+xOffset; + int sy = y*(m_tiles.qHeight() )+yOffset; + if (m_boardLayout.getBoardData(z, y, x) != '1') { + continue; + } + QPixmap *t = m_tiles.unselectedPixmaps(tile); + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + + if ((x>1) && (y>0) && m_boardLayout.getBoardData(z,y-1,x-2)=='1'){ + bitBlt( dest, sx+2, sy, + t, 2,0, t->width(), t->height()/2, CopyROP ); + bitBlt( dest, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( dest, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + tile++; + if (tile == 35) + tile++; + tile = tile % 43; + } + } + xOffset +=m_tiles.shadowSize(); + yOffset -=m_tiles.shadowSize(); + } +} + +// this really does not belong here. It will be fixed in v1.1 onwards +void Preview::saveTheme() { + QString tile = Prefs::tileSet(); + QString back = Prefs::background(); + QString layout = Prefs::layout(); + + QString with = ":"; + // we want to replace any path in the default store + // with a + + QRegExp p(locate("data_dir", "/kmahjongg/pics/")); + + back.replace(p,with); + tile.replace(p,with); + layout.replace(p,with); + + + // Get the name of the file to save + KURL url = KFileDialog::getSaveURL( + NULL, + "*.theme", + parentWidget(), + i18n("Save Theme" )); + if ( url.isEmpty() ) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return; + } + + // Are we over writing an existin file, or was a directory selected? + QFileInfo f( url.path() ); + if( f.isDir() ) + return; + if (f.exists()) { + // if it already exists, querie the user for replacement + int res=KMessageBox::warningContinueCancel(this, + i18n("A file with that name " + "already exists. Do you " + "wish to overwrite it?"),QString::null,i18n("Overwrite")); + if (res != KMessageBox::Continue) + return ; + } + FILE *outFile = fopen( QFile::encodeName(url.path()), "w" ); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not write to file. Aborting.")); + return; + } + + fprintf(outFile,"%s\n%s\n%s\n%s\n", + themeMagicV1_0, + tile.utf8().data(), + back.utf8().data(), + layout.utf8().data()); + fclose(outFile); +} + +FrameImage::FrameImage (QWidget *parent, const char *name) + : QFrame(parent, name) +{ + rx = -1; + thePixmap = new QPixmap(); +} + +FrameImage::~FrameImage() +{ + delete thePixmap; +} + +void FrameImage::setGeometry(int x, int y, int w, int h) { + QFrame::setGeometry(x,y,w,h); + + thePixmap->resize(size()); + +} + +void FrameImage::paintEvent( QPaintEvent* pa ) +{ + QFrame::paintEvent(pa); + + QPainter p(this); + + + QPen line; + line.setStyle(DotLine); + line.setWidth(2); + line.setColor(yellow); + p.setPen(line); + p.setBackgroundMode(OpaqueMode); + p.setBackgroundColor(black); + + int x = pa->rect().left(); + int y = pa->rect().top(); + int h = pa->rect().height(); + int w = pa->rect().width(); + + p.drawPixmap(x+frameWidth(),y+frameWidth(),*thePixmap,x+frameWidth(),y+frameWidth(),w-(2*frameWidth()),h-(2*frameWidth())); + if (rx >=0) { + + p.drawRect(rx, ry, rw, rh); + p.drawRect(rx+rs, ry, rw-rs, rh-rs); + p.drawLine(rx, ry+rh, rx+rs, ry+rh-rs); + + int midX = rx+rs+((rw-rs)/2); + int midY = ry+((rh-rs)/2); + switch (rt) { + case 0: // delete mode cursor + p.drawLine(rx+rs, ry, rx+rw, ry+rh-rs); + p.drawLine(rx+rw, ry, rx+rs, ry+rh-rs); + break; + case 1: // insert cursor + p.drawLine(midX, ry, midX, ry+rh-rs); + p.drawLine(rx+rs, midY, rx+rw, midY); + break; + case 2: // move mode cursor + p.drawLine(midX, ry, rx+rw, midY); + p.drawLine(rx+rw, midY, midX, ry+rh-rs); + p.drawLine(midX, ry+rh-rs, rx+rs, midY); + p.drawLine(rx+rs, midY, midX, ry); + + break; + } + + } +} + +void FrameImage::setRect(int x,int y,int w,int h, int s, int t) +{ + rx = x; + ry = y; + rw = w; + rh = h; + rt = t; + rs = s; +} + +// Pass on the mouse presed event to our owner + +void FrameImage::mousePressEvent(QMouseEvent *m) { + mousePressed(m); +} + +void FrameImage::mouseMoveEvent(QMouseEvent *e) { + mouseMoved(e); +} + +#include "Preview.moc" diff --git a/kmahjongg/Preview.h b/kmahjongg/Preview.h new file mode 100644 index 00000000..4f58e2cd --- /dev/null +++ b/kmahjongg/Preview.h @@ -0,0 +1,104 @@ +#ifndef _PreviewLoadBase_H +#define _PreviewLoadBase_H + +#include + +#include + +#include "Tileset.h" +#include "BoardLayout.h" +#include "Background.h" + +class QComboBox; +class QPixmap; + +class FrameImage: public QFrame +{ + Q_OBJECT +public: + FrameImage(QWidget *parent=NULL, const char *name = NULL); + ~FrameImage(); + void setGeometry(int x, int y, int w, int h); + QPixmap *getPreviewPixmap() {return thePixmap;} + void setRect(int x, int y, int w, int h, int ss, int type); +signals: + void mousePressed(QMouseEvent *e); + void mouseMoved(QMouseEvent *e); +protected: + void mousePressEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void paintEvent( QPaintEvent* pa ); +private: + QPixmap *thePixmap; + int rx; + int ry; + int rw; + int rh; + int rs; + int rt; +}; + + + +class Preview: public KDialogBase +{ + Q_OBJECT + +public: + enum PreviewType {background, tileset, board, theme}; + + Preview(QWidget* parent); + ~Preview(); + + void initialise(const PreviewType type); + void saveTheme(); + +protected: + void markUnchanged(); + void markChanged(); + bool isChanged(); + QPixmap *getPreviewPixmap() {return m_drawFrame->getPreviewPixmap(); } + virtual void drawPreview(); + void applyChange() ; + void renderBackground(const QString &bg); + void renderTiles(const QString &file, const QString &layout); + void paintEvent( QPaintEvent* pa ); + +signals: + void boardRedraw(bool); + void loadTileset(const QString &); + void loadBackground(const QString &, bool); + void loadBoard(const QString &); + void layoutChange(); + +public slots: + void selectionChanged(int which); + +protected slots: + void slotApply(); + void slotOk(); + +private slots: + void load(); + +protected: + FrameImage *m_drawFrame; + QComboBox *m_combo; + + QString m_selectedFile; + Tileset m_tiles; + BoardLayout m_boardLayout; + Background m_back; + +private: + QString m_fileSelector; + bool m_changed; + QStringList m_fileList; + PreviewType m_previewType; + + QString m_themeBack; + QString m_themeLayout; + QString m_themeTileset; +}; + +#endif diff --git a/kmahjongg/Tileset.cpp b/kmahjongg/Tileset.cpp new file mode 100644 index 00000000..c29f9701 --- /dev/null +++ b/kmahjongg/Tileset.cpp @@ -0,0 +1,281 @@ + +#include +#include "Tileset.h" +#include + + +#define mini_width 20 +#define mini_height 28 +static unsigned char mini_bits[] = { + 0xfc, 0xff, 0x0f, 0xfe, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0x0f, 0xff, 0xff, 0x0f, 0xff, 0xff, 0x07, 0xff, 0xff, 0x03, + }; + +#define mask_width 40 +#define mask_height 56 +static unsigned char mask_bits[] = { + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, + 0xff, 0xff, 0xff, 0x0f, }; + + +// --------------------------------------------------------- + +Tileset::Tileset(bool scale): + maskBits(mask_width, mask_height, mask_bits, true), + maskBitsMini(mini_width, mini_height, mini_bits, true) +{ + isScaled = scale; + divisor = (isScaled) ? 2 : 1; + + // set up tile metrics (fixed for now) + ss = 4; // left/bottom shadow width + bs = 1; // tile boarder width + w = 40; // tile width (inc boarder & shadow) + h = 56; // tile height (inc boarder and shadow) + s = w*h; // RGBA's required per tile + + // Allocate memory for the 9*5 tile arrays + tiles = new QRgb [9*5*s]; + selectedTiles = new QRgb [9*5*s]; + + // allocate memory for single tile storage + selectedFace = new QRgb [s]; + unselectedFace = new QRgb [s]; + + // quarter widths are used as an offset when + // overlaying tiles in 3 dimensions. + qw = ((w-ss)/2) ; + qh = ((h-ss)/2) ; + + filename = ""; +} + + +// --------------------------------------------------------- + +Tileset::~Tileset() { + + // deallocate all memory + delete [] tiles; + delete [] selectedTiles; + delete [] selectedFace; + delete [] unselectedFace; +} + +// --------------------------------------------------------- +// copy a tile from a qimage into a linear array of bytes. This +// method returns the address of the byte after the copied image +// and can be used to fill a larger array of tiles. + +QRgb *Tileset::copyTileImage(short tileX, short tileY, QRgb *to, QImage &from) { + QRgb *dest = to; + QRgb *src; + + src = (QRgb *) from.scanLine(tileY * h) + +(tileX * w); + for (short pos=0; pos < h; pos++) { + memcpy(dest, src, w*sizeof(QRgb)); + dest+=w; + src += from.width(); + } + return(dest); +} + +// ---------------------------------------------------------- +// Create a tile. Take a specified tile background a tile face +// (specified as an x,y coord) and a destination buffer (location +// in which is calculated from the x,y) and copy in the +// tile background, overlaying the foreground with transparency +// (the foregrounds top/left pixel is taken as the transparent +// color). + + +QRgb *Tileset::createTile(short x, short y, + QRgb *det, QImage &allTiles , QRgb *face) { + QRgb *image ; + QRgb *to = det; + + // Alloc the space + image = new QRgb[s]; + + // copy in the background + memcpy(to, face, s*sizeof(QRgb)); + + // get the tile gylph + copyTileImage(x, y , image, allTiles); + + // copy the image over the background using the + // top left colour as the transparency. We step over + // the shadow and the boarder + + QRgb* src = image+ // image + ss+ // past the left shadow + bs+ // then the tile border + (bs * w); // then step past the top border + + + to += (((ss+bs))+(bs*w)); + + + QRgb trans = *src; + + // start after the top border rows and go through all rows + for( short YP=0; YP < h-ss - (2*bs); YP++) { + // start after the shadow and border and iterate over x + for (short xpos=0; xpos < w-ss -(2*bs) ; xpos++) { + // skip transparent pixels + if (*src != trans) + *to = *src; + src++; + to++; + } + // step over the border to get to the next row + src += ss + (2 * bs); + to += ss + (2 * bs); + } + + // Free allocated space + delete [] image; + + // calculate the address of the next tile + return(det+s); +} + +// -------------------------------------------------------- +// create a pixmap for a tile. Optionally create a scalled +// version, which can be used for mini tile requirements. +// this gives us a small tile for previews and showing +// removed tiles. +void Tileset::createPixmap(QRgb *src, QPixmap &dest, bool scale, bool shadow) +{ + + QImage buff; + QRgb *line; + + buff.create(w, h, 32); + + for (int y=0; y + +class Tileset { + public: + Tileset(bool scaled=false); + ~Tileset(); + + bool loadTileset(const QString &filesetPath, const bool isPreview = false); + QRgb *createTile(short x, short y, QRgb *dst, QImage &src , QRgb *face); + QRgb *copyTileImage(short tileX, short tileY, QRgb *to, QImage &from); + + void setScaled(bool sc) {isScaled = sc; divisor = (sc) ? 2 : 1;} + + + QRgb *tile(short tnum); + QRgb *selectedTile(short tnum); + short width() {return w/divisor;} + short height() {return h/divisor;} + short shadowSize() {return ss/divisor;} + short size() {return s;} + short qWidth() {return qw/divisor;} + short qHeight() {return qh/divisor;} + + + QPixmap *selectedPixmaps(int num) { + if (!isScaled) + return &(selectedPix[num]); + else + return &(selectedMiniPix[num]); + } + + QPixmap *unselectedPixmaps(int num) { + if (!isScaled) + return &(unselectedPix[num]); + else + return &(unselectedMiniPix[num]); + } + + QPixmap *selectedShadowPixmaps(int num) { + if (!isScaled) + return &(selectedShadowPix[num]); + else + return &(selectedShadowMiniPix[num]); + } + + QPixmap *unselectedShadowPixmaps(int num) { + if (!isScaled) + return &(unselectedShadowPix[num]); + else + return &(unselectedShadowMiniPix[num]); + } + + protected: + + enum { maxTiles=45 }; + void createPixmap(QRgb *src, QPixmap &dest, bool scale, bool shadow); + + + private: + QBitmap maskBits; // xbm mask for the tile + QBitmap maskBitsMini; // xbm mask for the tile + QRgb* tiles; // Buffer containing all tiles (unselected glyphs) + QRgb* selectedTiles; // Buffer containing all tiles (selected glyphs) + + + // in version 0.5 we have moved ftom using images and calculating + // masks etc, to using pixmaps and letting the blt do the hard work, + // in hardware. + QPixmap selectedPix[maxTiles]; // selected tiles + QPixmap unselectedPix[maxTiles]; // unselected tiles + QPixmap selectedMiniPix[maxTiles]; // selected tiles + QPixmap unselectedMiniPix[maxTiles]; // unselected tiles + + QPixmap selectedShadowPix[maxTiles]; // selected tiles as above in shadow + QPixmap unselectedShadowPix[maxTiles]; // unselected tiles + QPixmap selectedShadowMiniPix[maxTiles]; // selected tiles + QPixmap unselectedShadowMiniPix[maxTiles]; // unselected tiles + + + + + QRgb* selectedFace; // The tile background face for a selected tile + QRgb* unselectedFace;// The tile background face for an unselected tile + + QRgb tr; // transparenct color for tile bg + + short ss; // left/bottom shadow width + short bs; // width of the border around a tile + short w; // tile width ( +border +shadow) + short h; // tile height ( +border +shadow) + short qw; // quarter tile width used in 3d rendering + short qh; // quarter tile height used in 3d rendering + short s; // buffer size for tile (width*height) + + QString filename; // cache the last file loaded to save reloading it + bool isScaled; + int divisor; +}; + + +#endif + + diff --git a/kmahjongg/boardwidget.cpp b/kmahjongg/boardwidget.cpp new file mode 100644 index 00000000..9c3355ea --- /dev/null +++ b/kmahjongg/boardwidget.cpp @@ -0,0 +1,2023 @@ +#include "boardwidget.h" +#include "prefs.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Constructor. + * Loads tileset and background bitmaps. + */ +BoardWidget::BoardWidget( QWidget* parent, const char *name ) + : QWidget( parent, name ), theTiles(false) +{ + setBackgroundColor( QColor( 0,0,0 ) ); + + timer = new QTimer(this); + connect( timer, SIGNAL(timeout()), + this, SLOT(helpMoveTimeout()) ); + + TimerState = Stop; + gamePaused = false; + iTimerStep = 0; + matchCount = 0; + showMatch = false; + showHelp = false; + MouseClickPos1.e = BoardLayout::depth; // mark tile position as invalid + MouseClickPos2.e = BoardLayout::depth; + memset( &Game.Mask, 0, sizeof( Game.Mask ) ); + Game.MaxTileNum = 0; + gameGenerationNum = 0; + + // initially we force a redraw + updateBackBuffer=true; + + // Load tileset. First try to load the last use tileset + QString tFile; + getFileOrDefault(Prefs::tileSet(), "tileset", tFile); + + if (!loadTileset(tFile)){ + KMessageBox::error(this, + i18n("An error occurred when loading the tileset file %1\n" + "KMahjongg will now terminate.").arg(tFile)); + kapp->quit(); + } + + getFileOrDefault(Prefs::background(), "bgnd", tFile); + + // Load background + if( ! loadBackground(tFile, false ) ) + { + KMessageBox::error(this, + i18n("An error occurred when loading the background image\n%1").arg(tFile)+ + i18n("KMahjongg will now terminate.")); + kapp->quit(); + } + + getFileOrDefault(Prefs::layout(), "layout", tFile); + if( ! loadBoardLayout(tFile) ) + { + KMessageBox::error(this, + i18n("An error occurred when loading the board layout %1\n" + "KMahjongg will now terminate.").arg(tFile)); + kapp->quit(); + } + setDisplayedWidth(); + loadSettings(); +} + +BoardWidget::~BoardWidget(){ + saveSettings(); +} + +void BoardWidget::loadSettings(){ + theBackground.tile = Prefs::tiledBackground(); + + setDisplayedWidth(); + tileSizeChanged(); + updateScaleMode(); + drawBoard(true); +} + +void BoardWidget::saveSettings(){ + // Preview can't handle this. TODO + //KConfig *config=kapp->config(); + //config->setGroup("General"); + + //config->writePathEntry("Tileset_file", tileFile); + //config->writePathEntry("Background_file", backgroundFile); + //config->writePathEntry("Layout_file", layout); +} + +void BoardWidget::getFileOrDefault(QString filename, QString type, QString &res) +{ + QString picsPos = "pics/"; + picsPos += "default."; + picsPos += type; + + if (QFile::exists(filename)) { + res = filename; + } + else { + res = locate("appdata", picsPos); + } + + if (res.isEmpty()) { + KMessageBox::error(this, i18n("KMahjongg could not locate the file: %1\n" + "or the default file of type: %2\n" + "KMahjongg will now terminate").arg(filename).arg(type) ); + kapp->quit(); + } +} + +void BoardWidget::setDisplayedWidth() { + if (Prefs::showRemoved()) + setFixedSize( requiredWidth() , requiredHeight()); + else + setFixedSize( requiredWidth() - ((theTiles.width())*4) + , requiredHeight()); +} + +// for a given cell x y calc how that cell is shadowed +// returnd left = width of left hand side shadow +// t = height of top shadow +// c = width and height of corner shadow + +void BoardWidget::calcShadow(int e, int y, int x, int &l, int &t, int &c) { + + l = t = c = 0; + if ((Game.shadowHeight(e,y,x) != 0) || + (Game.shadowHeight(e,y-1,x) != 0) || + (Game.shadowHeight(e,y,x-1) != 0)) { + return; + } + int a,b; + + a=Game.shadowHeight(e,y,x-2); + b=Game.shadowHeight(e,y-1,x-2); + if (a != 0 || b != 0) + l = (a>b) ? a : b; + a=Game.shadowHeight(e,y-2,x); + b=Game.shadowHeight(e,y-2,x-1); + if (a != 0 || b != 0) + t = (a>b) ? a : b; + + c = Game.shadowHeight(e, y-2, x-2); +} + +// draw a triangular shadow from the top right to the bottom left. +// one such shadow is a right hand edge of a shadow line. +// if a second shadow botton left to top right is rendered over it +// then the shadow becomes a box (ie in the middle of the run) + +void BoardWidget::shadowTopLeft(int depth, int sx, int sy, int rx, int ry, QPixmap *src, bool flag) { + if (depth) { + int shadowPixels= (depth+1) * theTiles.shadowSize(); + int xOffset=theTiles.qWidth()-shadowPixels; + for (int p=0; p 0)) + bitBlt( &backBuffer, + sx, sy, + src, + rx, ry, + theTiles.qWidth() - shadowPixels, + shadowPixels, CopyROP ); + } +} + +// Second triangular shadow generator see above +void BoardWidget::shadowBotRight(int depth, int sx, int sy, int rx, int ry, QPixmap *src, bool flag) { + if (depth) { + int shadowPixels= (depth+1) * theTiles.shadowSize(); + int xOffset=theTiles.qWidth(); + for (int p=0; p0)) + bitBlt( &backBuffer, + sx+xOffset-shadowPixels, + sy+shadowPixels, + src, + rx+xOffset-shadowPixels, + ry+shadowPixels, + shadowPixels, + theTiles.qHeight()-shadowPixels, CopyROP ); + + } +} + + + + +void BoardWidget::shadowArea(int z, int y, int x, int sx, int sy,int rx, int ry, QPixmap *src) +{ + // quick check to see if we are obscured + if (z < BoardLayout::depth-1) { + if ((x >= 0) && (yrect().left(); + int xheight = pa->rect().height(); + int xwidth = pa->rect().width(); + + back = theBackground.getBackground(); + + if (gamePaused) { + // If the game is paused, then blank out the board. + // We tolerate no cheats around here folks.. + bitBlt( this, xx, pa->rect().top(), + back, xx, pa->rect().top(), xwidth, xheight, CopyROP ); + return; + } + + // if the repaint is because of a window redraw after a move + // or a menu roll up, then just blit in the last rendered image + if (!updateBackBuffer) { + bitBlt(this, xx,pa->rect().top(), + &backBuffer, xx, pa->rect().top(), xwidth, xheight, CopyROP); + return; + } + + // update the complete drawArea + + backBuffer.resize(back->width(), back->height()); + + // erase out with the background + bitBlt( &backBuffer, xx, pa->rect().top(), + back, xx,pa->rect().top(), back->width(), back->height(), CopyROP ); + + // initial offset on the screen of tile 0,0 + int xOffset = theTiles.width()/2; + int yOffset = theTiles.height()/2; + //short tile = 0; + + // shadow the background first + if (Prefs::showShadows()) { + for (int by=0; by =0; x--) { + int sx = x*(theTiles.qWidth() )+xOffset; + int sy = y*(theTiles.qHeight() )+yOffset; + + + + // skip if no tile to display + if (!Game.tilePresent(z,y,x)) + continue; + + QPixmap *t; + QPixmap *s; + if (Game.hilighted[z][y][x]) { + t= theTiles.selectedPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + s= theTiles.selectedShadowPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + } else { + t= theTiles.unselectedPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + s= theTiles.unselectedShadowPixmaps( + Game.Board[z][y][x]-TILE_OFFSET); + } + + // Only one compilcation. Since we render top to bottom , left + // to right situations arise where...: + // there exists a tile one q height above and to the left + // in this situation we would draw our top left border over it + // we simply split the tile draw so the top half is drawn + // minus border + + if (x > 1 && y > 0 && Game.tilePresent(z, y-1, x-2)){ + bitBlt( &backBuffer, + sx+theTiles.shadowSize(), sy, + t, theTiles.shadowSize() ,0, + t->width()-theTiles.shadowSize(), + t->height()/2, CopyROP ); + bitBlt( &backBuffer, sx, sy+t->height()/2, + t, 0,t->height()/2,t->width(),t->height()/2,CopyROP); + } else { + + bitBlt( &backBuffer, sx, sy, + t, 0,0, t->width(), t->height(), CopyROP ); + } + + + if (Prefs::showShadows() && z= 0 && pos < 3) { + last = removedDragon[pos]; + tile = TILE_DRAGON+pos; + } else { + //Wind? + if (pos >= 3 && pos < 7) { + last = removedWind[pos-3]; + tile = TILE_WIND+pos-3; + } else { + if (pos == 7) { + for (int t=0; t<4;t++) { + if (removedFlower[t]) { + last++; + tile=TILE_FLOWER+t; + } + } + } else { + for (int t=0; t<4;t++) { + if (removedSeason[t]) { + last++; + tile=TILE_SEASON+t; + } + } + } + } + } + + stackTiles(tile, last, xPos, yPos); + stackTiles(TILE_ROD+pos, removedRod[pos], + xPos - (1*(theTiles.width() - theTiles.shadowSize())) , yPos); + stackTiles(TILE_BAMBOO+pos, removedBamboo[pos], + xPos - (2*(theTiles.width() - theTiles.shadowSize())) , yPos); + stackTiles(TILE_CHARACTER+pos, removedCharacter[pos], + xPos - (3*(theTiles.width() - theTiles.shadowSize())) , yPos); + + + + yPos += theTiles.height()-theTiles.shadowSize(); + } + + updateBackBuffer=false; + bitBlt(this, xx,pa->rect().top(), &backBuffer, xx, pa->rect().top(), xwidth, xheight, CopyROP); + + +} + +void BoardWidget::stackTiles(unsigned char t, unsigned short h, unsigned short x,unsigned short y) +{ + + int ss = theTiles.shadowSize(); + QPainter p(&backBuffer); + QPen line; + p.setBackgroundMode(OpaqueMode); + p.setBackgroundColor(black); + + + + + line.setWidth(1); + line.setColor(white); + p.setPen(line); + int x2 = x+theTiles.width()-ss-1; + int y2 = y+theTiles.height()-1; + p.drawLine(x, y+ss, x2, y+ss); + p.drawLine(x, y+ss, x, y2); + p.drawLine(x2, y+ss, x2, y2); + p.drawLine(x+1, y2, x2, y2); + + // p.fillRect(x+1, y+ss+1, theTiles.width()-ss-2, theTiles.height()-ss-2, QBrush(lightGray)); + + for (unsigned short pos=0; pos < h; pos++) { + QPixmap *p = theTiles.unselectedPixmaps(t-TILE_OFFSET); + bitBlt( &backBuffer, x+(pos*ss), y-(pos*ss), + p, 0,0, p->width(), p->height(), CopyROP ); + } + +} + + +void BoardWidget::pause() { + gamePaused = !gamePaused; + drawBoard(true); +} + +void BoardWidget::gameLoaded() +{ + int i; + initialiseRemovedTiles(); + i = Game.TileNum; + // use the history of moves to put in the removed tiles area the correct tiles + while (i < Game.MaxTileNum ) + { + setRemovedTilePair(Game.MoveList[i], Game.MoveList[i+1]); + i +=2; + } + drawBoard(); +} + +// --------------------------------------------------------- +int BoardWidget::undoMove() +{ + cancelUserSelectedTiles(); + + if( Game.TileNum < Game.MaxTileNum ) + { + + clearRemovedTilePair(Game.MoveList[Game.TileNum], Game.MoveList[Game.TileNum+1]); + putTile( Game.MoveList[Game.TileNum], false ); + Game.TileNum++; + putTile( Game.MoveList[Game.TileNum] ); + Game.TileNum++; + drawTileNumber(); + setStatusText( i18n("Undo operation done successfully.") ); + return 1; + } + else { + setStatusText(i18n("What do you want to undo? You have done nothing!")); + return 0; + } +} + +// --------------------------------------------------------- +void BoardWidget::helpMove() +{ + cancelUserSelectedTiles(); + if (showHelp) helpMoveStop(); + + if( findMove( TimerPos1, TimerPos2 ) ) + { + cheatsUsed++; + iTimerStep = 1; + showHelp = true; + helpMoveTimeout(); + } + else + setStatusText( i18n("Sorry, you have lost the game.") ); +} +// --------------------------------------------------------- +void BoardWidget::helpMoveTimeout() +{ + if( iTimerStep & 1 ) + { + hilightTile( TimerPos1, true, false ); + hilightTile( TimerPos2, true ); + } + else + { + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + } + // restart timer + if( iTimerStep++ < 8 ) + timer->start( ANIMSPEED , true ); + else + showHelp = false; +} +// --------------------------------------------------------- + +void BoardWidget::helpMoveStop() +{ + timer->stop(); + iTimerStep = 8; + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + showHelp = false; +} + +// --------------------------------------------------------- +void BoardWidget::startDemoMode() +{ + calculateNewGame(); + + if( TimerState == Stop ) + { + TimerState = Demo; + iTimerStep = 0; + emit demoModeChanged( true ); + setStatusText( i18n("Demo mode. Click mousebutton to stop.") ); + demoMoveTimeout(); + } +} +// --------------------------------------------------------- +void BoardWidget::stopDemoMode() +{ + TimerState = Stop; // stop demo + calculateNewGame(); + setStatusText( i18n("Now it's you again.") ); + emit demoModeChanged( false ); + emit gameCalculated(); +} +// --------------------------------------------------------- +void BoardWidget::demoMoveTimeout() +{ + if( TimerState == Demo ) + { + switch( iTimerStep++ % 6 ) + { + // at firts, find new matching tiles + case 0: + if( ! findMove( TimerPos1, TimerPos2 ) ) + { + // if computer has won + if( Game.TileNum == 0 ) + { + animateMoveList(); + } + // else computer has lost + else + { + setStatusText( i18n("Your computer has lost the game.") ); + while( Game.TileNum < Game.MaxTileNum ) + { + putTile( Game.MoveList[Game.TileNum], false ); + Game.TileNum++; + putTile( Game.MoveList[Game.TileNum] ); + Game.TileNum++; + drawTileNumber(); + } + } + TimerState = Stop; + startDemoMode(); + return; + } + break; + // hilight matching tiles two times + case 1: + case 3: + hilightTile( TimerPos1, true, false ); + hilightTile( TimerPos2, true ); + break; + + case 2: + case 4: + hilightTile( TimerPos1, false, false ); + hilightTile( TimerPos2, false ); + break; + // remove matching tiles from game board + case 5: + setRemovedTilePair(TimerPos1, TimerPos2); + removeTile( TimerPos1, false ); + removeTile( TimerPos2 ); + drawTileNumber(); + break; + } + // restart timer + QTimer::singleShot( ANIMSPEED, this, SLOT( demoMoveTimeout() ) ); + } +} + +// --------------------------------------------------------- +void BoardWidget::setShowMatch( bool show ) +{ + if( showMatch ) + stopMatchAnimation(); + showMatch = show; +} +// --------------------------------------------------------- +void BoardWidget::matchAnimationTimeout() +{ + if (matchCount == 0) + return; + + if( iTimerStep++ & 1 ) + { + for(short Pos = 0; Pos < matchCount; Pos++) + { + + + hilightTile(PosTable[Pos], true); + } + } + else + { + for(short Pos = 0; Pos < matchCount; Pos++) + { + hilightTile(PosTable[Pos], false); + } + } + if( TimerState == Match ) + QTimer::singleShot( ANIMSPEED, this, SLOT( matchAnimationTimeout() ) ); +} +// --------------------------------------------------------- +void BoardWidget::stopMatchAnimation() +{ + for(short Pos = 0; Pos < matchCount; Pos++) + { + hilightTile(PosTable[Pos], false); + } + TimerState = Stop; + matchCount = 0; +} + +void BoardWidget::redoMove() +{ + + setRemovedTilePair(Game.MoveList[Game.TileNum-1],Game.MoveList[Game.TileNum-2]); + removeTile(Game.MoveList[Game.TileNum-1], false); + removeTile(Game.MoveList[Game.TileNum-1]); + drawTileNumber(); +} + +// --------------------------------------------------------- +void BoardWidget::animateMoveList() +{ + setStatusText( i18n("Congratulations. You have won!") ); + + if (Prefs::playAnimation()) + { + while( Game.TileNum < Game.MaxTileNum ) + { + // put back all tiles + putTile(Game.MoveList[Game.TileNum]); + Game.TileNum++; + putTile(Game.MoveList[Game.TileNum], false); + Game.TileNum++; + drawTileNumber(); + } + while( Game.TileNum > 0 ) + { + // remove all tiles + removeTile(Game.MoveList[Game.TileNum-1], false); + removeTile(Game.MoveList[Game.TileNum-1]); + drawTileNumber(); + } + } + + calculateNewGame(); +} + +// --------------------------------------------------------- +void BoardWidget::calculateNewGame( int gNumber) +{ + cancelUserSelectedTiles(); + stopMatchAnimation(); + initialiseRemovedTiles(); + setStatusText( i18n("Calculating new game...") ); + + + if( !loadBoard()) + { + setStatusText( i18n("Error converting board information!") ); + return; + } + + if (gNumber == -1) { + gameGenerationNum = kapp->random(); + } else { + gameGenerationNum = gNumber; + } + + random.setSeed(gameGenerationNum); + + // Translate Game.Map to an array of POSITION data. We only need to + // do this once for each new game. + memset(tilePositions, 0, sizeof(tilePositions)); + generateTilePositions(); + + // Now use the tile position data to generate tile dependency data. + // We only need to do this once for each new game. + generatePositionDepends(); + + // Now try to position tiles on the board, 64 tries max. + for( short nr=0; nr<64; nr++ ) + { + if( generateStartPosition2() ) + { + drawBoard(); + setStatusText( i18n("Ready. Now it is your turn.") ); + cheatsUsed=0; + return; + } + } + + drawBoard(); + setStatusText( i18n("Error generating new game!") ); +} + +// --------------------------------------------------------- +// Generate the position data for the layout from contents of Game.Map. +void BoardWidget::generateTilePositions() { + + numTiles = 0; + + for (int z=0; z< BoardLayout::depth; z++) { + for (int y=0; y (numTiles*numTiles)) { + return false; // bail + } + } while (tilePositions[position].e != 0); + + // If there are no other free positions on the same apparent + // horizontal line, we can mark that position as free. + if (onlyFreeInLine(position)) { + positionDepends[position].free = true; + } + } + + // Check to make sure we really got them all. Very important for + // this algorithm. + for (int i = 0; i < numTiles; i++) { + if (tilePositions[i].e == 0 && onlyFreeInLine(i)) { + positionDepends[i].free = true; + } + } + + // Get ready to place the tiles + int lastPosition = -1; + int position = -1; + int position2 = -1; + + // For each position, + for (int i = 0; i < numTiles; i++) { + + // If this is the first tile in a 144 tile set, + if ((i % 144) == 0) { + + // Initialise the faces to allocate. For the classic + // dragon board there are 144 tiles. So we allocate and + // randomise the assignment of 144 tiles. If there are > 144 + // tiles we will reallocate and re-randomise as we run out. + // One advantage of this method is that the pairs to assign are + // non-linear. In kmahjongg 0.4, If there were > 144 the same + // allocation series was followed. So 154 = 144 + 10 rods. + // 184 = 144 + 40 rods (20 pairs) which overwhemed the board + // with rods and made deadlock games more likely. + randomiseFaces(); + } + + // If this is the first half of a pair, there is no previous + // position for the pair. + if ((i & 1) == 0) { + lastPosition = -1; + } + + // Select a position for the tile, relative to the position of + // the last tile placed. + if ((position = selectPosition(lastPosition)) < 0) { + return false; // bail + } + if (i < numTiles-1) { + if ((position2 = selectPosition(lastPosition)) < 0) { + return false; // bail + } + if (tilePositions[position2].e > tilePositions[position].e) { + position = position2; // higher is better + } + } + + // Place the tile. + placeTile(position, tilePair[i % 144]); + + // Remember the position + lastPosition = position; + } + + // The game is solvable. + return true; +} + +// --------------------------------------------------------- +// Determines whether it is ok to mark this position as "free" because +// there are no other positions marked "free" in its apparent horizontal +// line. +bool BoardWidget::onlyFreeInLine(int position) { + + int i, i0, w; + int lin, rin, out; + static int nextLeft[BoardLayout::maxTiles]; + static int nextRight[BoardLayout::maxTiles]; + + /* Check left, starting at position */ + lin = 0; + out = 0; + nextLeft[lin++] = position; + do { + w = nextLeft[out++]; + if (positionDepends[w].free || positionDepends[w].filled) { + return false; + } + if ((i = positionDepends[w].lhs_dep[0]) != -1) { + nextLeft[lin++] = i; + } + i0 = i; + if ((i = positionDepends[w].lhs_dep[1]) != -1 && i0 != i) { + nextLeft[lin++] = i; + } + } + while (lin > out) ; + + /* Check right, starting at position */ + rin = 0; + out = 0; + nextRight[rin++] = position; + do { + w = nextRight[out++]; + if (positionDepends[w].free || positionDepends[w].filled) { + return false; + } + if ((i = positionDepends[w].rhs_dep[0]) != -1) { + nextRight[rin++] = i; + } + i0 = i; + if ((i = positionDepends[w].rhs_dep[1]) != -1 && i0 != i) { + nextRight[rin++] = i; + } + } + while (rin > out) ; + + // Here, the position can be marked "free" + return true; +} + +// --------------------------------------------------------- +int BoardWidget::selectPosition(int lastPosition) { + + int position, cnt = 0; + bool goodPosition = false; + + // while a good position has not been found, + while (!goodPosition) { + + // Select a random, but free, position. + do { + position = random.getLong(numTiles); + if (cnt++ > (numTiles*numTiles)) { + return -1; // bail + } + } while (!positionDepends[position].free); + + // Found one. + goodPosition = true; + + // If there is a previous position to take into account, + if (lastPosition != -1) { + + // Check the new position against the last one. + for (int i = 0; i < 4; i++) { + if (positionDepends[position].place_dep[i] == lastPosition) { + goodPosition = false; // not such a good position + } + } + for (int i = 0; i < 2; i++) { + if ((positionDepends[position].lhs_dep[i] == lastPosition) || + (positionDepends[position].rhs_dep[i] == lastPosition)) { + goodPosition = false; // not such a good position + } + } + } + } + + return position; +} + +// --------------------------------------------------------- +void BoardWidget::placeTile(int position, int tile) { + + // Install the tile in the specified position + tilePositions[position].f = tile; + Game.putTile(tilePositions[position]); + + // Update position dependency data + positionDepends[position].filled = true; + positionDepends[position].free = false; + + // Now examine the tiles near this to see if this makes them "free". + int depend; + for (int i = 0; i < 4; i++) { + if ((depend = positionDepends[position].turn_dep[i]) != -1) { + updateDepend(depend); + } + } + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].lhs_dep[i]) != -1) { + updateDepend(depend); + } + if ((depend = positionDepends[position].rhs_dep[i]) != -1) { + updateDepend(depend); + } + } +} + +// --------------------------------------------------------- +// Updates the free indicator in the dependency data for a position +// based on whether the positions on which it depends are filled. +void BoardWidget::updateDepend(int position) { + + // If the position is valid and not filled + if (position >= 0 && !positionDepends[position].filled) { + + // Check placement depends. If they are not filled, the + // position cannot become free. + int depend; + for (int i = 0; i < 4; i++) { + if ((depend = positionDepends[position].place_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + return ; + } + } + } + + // If position is first free on apparent horizontal, it is + // now free to be filled. + if (onlyFreeInLine(position)) { + positionDepends[position].free = true; + return; + } + + // Assume no LHS positions to fill + bool lfilled = false; + + // If positions to LHS + if ((positionDepends[position].lhs_dep[0] != -1) || + (positionDepends[position].lhs_dep[1] != -1)) { + + // Assume LHS positions filled + lfilled = true; + + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].lhs_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + lfilled = false; + } + } + } + } + + // Assume no RHS positions to fill + bool rfilled = false; + + // If positions to RHS + if ((positionDepends[position].rhs_dep[0] != -1) || + (positionDepends[position].rhs_dep[1] != -1)) { + + // Assume LHS positions filled + rfilled = true; + + for (int i = 0; i < 2; i++) { + if ((depend = positionDepends[position].rhs_dep[i]) != -1) { + if (!positionDepends[depend].filled) { + rfilled = false; + } + } + } + } + + // If positions to left or right are filled, this position + // is now free to be filled. + positionDepends[position].free = (lfilled || rfilled); + } +} + +// --------------------------------------------------------- +bool BoardWidget::generateStartPosition2() { + + // For each tile, + for (int i = 0; i < numTiles; i++) { + + // Get its basic position data + int x = tilePositions[i].x; + int y = tilePositions[i].y; + int z = tilePositions[i].e; + + // Clear Game.Board at that position + Game.Board[z][y][x] = 0; + + // Clear tile placed/free indicator(s). + positionDepends[i].filled = false; + positionDepends[i].free = false; + + // Set tile face blank + tilePositions[i].f = 254; + } + + // If solvable games should be generated, + if (Prefs::solvableGames()) { + + if (generateSolvableGame()) { + Game.TileNum = Game.MaxTileNum; + return true; + } else { + return false; + } + } + + // Initialise the faces to allocate. For the classic + // dragon board there are 144 tiles. So we allocate and + // randomise the assignment of 144 tiles. If there are > 144 + // tiles we will reallocate and re-randomise as we run out. + // One advantage of this method is that the pairs to assign are + // non-linear. In kmahjongg 0.4, If there were > 144 the same + // allocation series was followed. So 154 = 144 + 10 rods. + // 184 = 144 + 40 rods (20 pairs) which overwhemed the board + // with rods and made deadlock games more likely. + + int remaining = numTiles; + randomiseFaces(); + + for (int tile=0; tile 2) { + p2 = p1 = random.getLong(remaining-2); + int bail = 0; + while (p1 == p2) { + p2 = random.getLong(remaining-2); + + if (bail >= 100) { + if (p1 != p2) { + break; + } + } + if ((tilePositions[p1].y == tilePositions[p2].y) && + (tilePositions[p1].e == tilePositions[p2].e)) { + // skip if on same y line + bail++; + p2=p1; + continue; + } + } + } else { + p1 = 0; + p2 = 1; + } + POSITION a, b; + a = tilePositions[p1]; + b = tilePositions[p2]; + tilePositions[p1] = tilePositions[remaining - 1]; + tilePositions[p2] = tilePositions[remaining - 2]; + remaining -= 2; + + getFaces(a, b); + Game.putTile(a); + Game.putTile(b); + } + + Game.TileNum = Game.MaxTileNum; + return 1; +} + +void BoardWidget::getFaces(POSITION &a, POSITION &b) { + a.f = tilePair[tilesUsed]; + b.f = tilePair[tilesUsed+1]; + tilesUsed += 2; + + if (tilesUsed >= 144) { + randomiseFaces(); + } +} + +void BoardWidget::randomiseFaces() { + int nr; + int numAlloced=0; + // stick in 144 tiles in pairsa. + + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_CHARACTER+(nr/4); // 4*9 Tiles + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_BAMBOO+(nr/4); // 4*9 Tiles + for( nr=0; nr<9*4; nr++) + tilePair[numAlloced++] = TILE_ROD+(nr/4); // 4*9 Tiles + for( nr=0; nr<4; nr++) + tilePair[numAlloced++] = TILE_FLOWER+nr; // 4 Tiles + for( nr=0; nr<4; nr++) + tilePair[numAlloced++] = TILE_SEASON+nr; // 4 Tiles + for( nr=0; nr<4*4; nr++) + tilePair[numAlloced++] = TILE_WIND+(nr/4); // 4*4 Tiles + for( nr=0; nr<3*4; nr++) + tilePair[numAlloced++] = TILE_DRAGON+(nr/4); // 3*4 Tiles + + + //randomise. Keep pairs together. Ie take two random + //odd numbers (n,x) and swap n, n+1 with x, x+1 + + int at=0; + int to=0; + for (int r=0; r<200; r++) { + + + to=at; + while (to==at) { + to = random.getLong(144); + + if ((to & 1) != 0) + to--; + + } + UCHAR tmp = tilePair[at]; + tilePair[at] = tilePair[to]; + tilePair[to] = tmp; + tmp = tilePair[at+1]; + tilePair[at+1] = tilePair[to+1]; + tilePair[to+1] = tmp; + + + at+=2; + if (at >= 144) + at =0; + } + + tilesAllocated = numAlloced; + tilesUsed = 0; +} + + +// --------------------------------------------------------- +bool isFlower( UCHAR Tile ) +{ + return( Tile >= TILE_FLOWER && Tile <=TILE_FLOWER+3 ); +} +bool isSeason( UCHAR Tile ) +{ + return( Tile >= TILE_SEASON && Tile <=TILE_SEASON+3 ); +} +bool isBamboo(UCHAR t) { + return( t >= TILE_BAMBOO && t = TILE_CHARACTER && t = TILE_ROD && t = TILE_DRAGON && t < TILE_DRAGON +3); +} +bool isWind(UCHAR t) { + return( t >= TILE_WIND && t < TILE_WIND +4); +} + + +bool BoardWidget::isMatchingTile( POSITION& Pos1, POSITION& Pos2 ) +{ + // don't compare 'equal' positions + if( memcmp( &Pos1, &Pos2, sizeof(POSITION) ) ) + { + UCHAR FA = Pos1.f; + UCHAR FB = Pos2.f; + + if( (FA == FB) + || ( isFlower( FA ) && isFlower( FB ) ) + || ( isSeason( FA ) && isSeason( FB ) ) ) + return( true ); + } + return( false ); +} + +// --------------------------------------------------------- +bool BoardWidget::findMove( POSITION& posA, POSITION& posB ) +{ + short Pos_Ende = Game.MaxTileNum; // Ende der PosTable + + for( short E=0; E=2 ) + { + random.setSeed(0); // WABA: Why is the seed reset? + short Pos = random.getLong(iPosCount) & -2; // Gerader Wert + posA = PosTable[Pos]; + posB = PosTable[Pos+1]; + + return( true ); + } + else + return( false ); +} + +int BoardWidget::moveCount( ) +{ + short Pos_Ende = Game.MaxTileNum; // end of PosTable + + for( short E=0; Ebutton() == LeftButton ) + { + if( TimerState == Demo ) + { + stopDemoMode(); + } + else if( showMatch ) + { + stopMatchAnimation(); + } + + if( showHelp ) // stop hilighting tiles + helpMoveStop(); + + if( MouseClickPos1.e == BoardLayout::depth ) // first tile + { + transformPointToPosition( event->pos(), MouseClickPos1 ); + + if( MouseClickPos1.e != BoardLayout::depth && showMatch ) + { + matchCount = findAllMatchingTiles( MouseClickPos1 ); + TimerState = Match; + iTimerStep = 1; + matchAnimationTimeout(); + cheatsUsed++; + } + } + else // second tile + { + transformPointToPosition( event->pos(), MouseClickPos2 ); + if( MouseClickPos2.e == BoardLayout::depth ) + { + cancelUserSelectedTiles(); + } + else + { + if( isMatchingTile( MouseClickPos1, MouseClickPos2 ) ) + { + // update the removed tiles (we do this before the remove below + // so that we only require 1 screen paint for both actions) + setRemovedTilePair(MouseClickPos1, MouseClickPos2); + + // now we remove the tiles from the board + removeTile(MouseClickPos1, false); + removeTile(MouseClickPos2); + + // removing a tile means redo is impossible without + // a further undo. + Game.allow_redo=false; + demoModeChanged(false); + drawTileNumber(); + + // if no tiles are left, the player has `won`, so celebrate + if( Game.TileNum == 0 ) + { + gameOver(Game.MaxTileNum,cheatsUsed); + } + // else if no more moves are possible, display the sour grapes dialog + else if( ! findMove( TimerPos1, TimerPos2 ) ) + { + KMessageBox::information(this, i18n("Game over: You have no moves left.")); + } + } + else + { + // redraw tiles in normal state + hilightTile( MouseClickPos1, false, false ); + hilightTile( MouseClickPos2, false ); + } + MouseClickPos1.e = BoardLayout::depth; // mark tile position as invalid + MouseClickPos2.e = BoardLayout::depth; + } + } + } +} + + +// ---------------------------------------------------------- +/** + Transform window point to board position. + + @param point Input: Point in window coordinates + @param MouseClickPos Output: Position in game board +*/ +void BoardWidget::transformPointToPosition( + const QPoint& point, + POSITION& MouseClickPos + ) +{ + short E,X,Y; + + // iterate over E coordinate from top to bottom + for( E=BoardLayout::depth-1; E>=0; E-- ) + { + // calculate mouse coordiantes --> position in game board + // the factor -theTiles.width()/2 must keep track with the + // offset for blitting in the print Event (FIX ME) + X = ((point.x()-theTiles.width()/2)- (E+1)*theTiles.shadowSize()) / theTiles.qWidth(); + Y = ((point.y()-theTiles.height()/2) + E*theTiles.shadowSize()) / theTiles.qHeight(); + + + // changed to allow x == 0 + // skip when position is illegal + if (X<0 || X>=BoardLayout::width || Y<0 || Y>=BoardLayout::height) + continue; + + // + switch( Game.Mask[E][Y][X] ) + { + case (UCHAR)'3': X--;Y--; + break; + + case (UCHAR)'2': X--; + break; + + case (UCHAR)'4': Y--; + break; + + case (UCHAR)'1': break; + + default : continue; + } + // if gameboard is empty, skip + if ( ! Game.Board[E][Y][X] ) continue; + // tile must be 'free' (nothing left, right or above it) + if( E < 4 ) + { + if( Game.Board[E+1][Y][X] || Game.Board[E+1][Y+1][X] || + (X 0) && (Game.Board[E][Y][X-1] || Game.Board[E][Y+1][X-1])) { + if ((X +#include +#include + + +#include "KmTypes.h" +#include "Tileset.h" +#include "Background.h" +#include "BoardLayout.h" + +typedef struct gamedata { + int allow_undo; + int allow_redo; + UCHAR Board[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + USHORT TileNum; + USHORT MaxTileNum; + UCHAR Mask[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + UCHAR hilighted[BoardLayout::depth][BoardLayout::height][BoardLayout::width]; + POSITION MoveList[BoardLayout::maxTiles]; + void putTile( short e, short y, short x, UCHAR f ) + { + + + Board[e][y][x] = Board[e][y+1][x] = + Board[e][y+1][x+1] = Board[e][y][x+1] = f; + } + void putTile( POSITION& pos ) + { + putTile( pos.e, pos.y, pos.x, pos.f ); + } + + + bool tilePresent(int z, int y, int x) { + return(Board[z][y][x]!=0 && Mask[z][y][x] == '1'); + } + + bool partTile(int z, int y, int x) { + return (Board[z][y][x] != 0); + } + + int shadowHeight(int z, int y, int x) { + + + if ((z>=BoardLayout::depth||y>=BoardLayout::height||x>=BoardLayout::width)) + return 0; + + + if ((y < 0) || (x < 0)) + return 0; + + int h=0; + for (int e=z; eX^bV;b>Dwg@4bHgdhecQ&psq)$QcerQKYy@B59J5M9Z>l z!IJIBX8gfH5Zj7i2XKHO{$j}a;5a}MTZv#KNB|kJY$=wFSb=0qf@D)9Es5d`B}!a} zUX)g;(OZ1 z*X!rJi0hj@c2#on%%$e9eds@Y`R{-vyS5Ay^n(FnQsP&?`N^OE)g6;VpB53u#zq+# z9->mI072(!_XfIA!Z#`L_k&+|8kHz6EiEy>um}PdF0Fn1{lEXE&j1}#G`K9NycJxp z89+<^z!(4Rhu(W=*W?%DIA-sjUDWFXJucc4Xk2pUfuk%eV1a_LwPuUsCr*+i2}h5g zyYFB8k4K&W+H4UdsP-^#t?#QqeebrB-vQXYYezBhYmM{*-f+L=B-e-RGJp3aa=j&p z)M-LzE%nK~2J3b9?b*%o6DK*ebMkk9U&7i0inllu^pXJ}lm4H*bKlOPdhK0}#vqNx zpdVv!6lM;=(4_TSM?!t?B7Y(B=xbjhDJOjRW}wk4Ltu3Qb?z9nmQA|h;Rb_)bp-iI zUr-$x(02s*H{+1uw_L2d#@mF7Nb_NS$ z?0yHt72X)JGa_Z_QX*jq=;rD9Wm!3dl_0M)zc8=Nt`mW%LNt9{t_(p~ZFlQHos1p#MVPJ8ZqTyRUQaBDN7z#LbjT+-n z9Yi`!FWPD|831ae%&+_5Eo%q#(gujLtJ>;x^W_XpI2~HhKcN8z1w;zN*E96|d0l~RZZQG}Uj+-j~b3}9D80t66es(c;EbybCZj~qk@7)fk->oH8IZrpckoA~IH zYW9dq1j-BpGzV-w7U(Mi6iR4`&ez1yV{8#I66Z06=(&HK_beYhJ?!}d9z$h!6C@t- z^L9d%PI1OD(n*LDA&$4evR{eo4+ALWsY|W?jU52KN;dje6I-DYL5V_%pb}*mK(7JV zOtK-G!x)}4n<6u&a>_$MWea#|)qHeGaVH{*B2={kNkUb!)km)$TgUZv6kt0^&v?sOqZh^iHrVYbBZPrEnI2+TSxI(7`X{yO8Yb#@B*DeNanLN~iqqUbuRKq)bd zB%WkTw!r5gf<^_6NCG0tXAotVeifQQMS}~~*;%4%_7MqGx(O9gR3%RH*@8--&%;0~ zP!U4>r7sbE>2FZK@(}W_yI^gV_z(Xp>i(Y~x$_Q^Bq32kx0}#O5|SiATn5Mpsg$zD zo}`j1A}FH<90rk8m1xzV(ZEQsvT7sx;WI=>j*|SsN09meJpCMeU=JUhTK9+g-rw+5v}6dhJMeFiyu9{K!VqHa7y{P1IRXQolN+(ca^ zX`DoN-j-4y=2>6Ivj zh{_lsC9F~sNV%1EBk6&^rw+n&%Es&5dpGe~ljzy!Q0LB*OmBma{47zeMv^FEB}gRT z*c4mM^%Vh(`HzKbZE0)pRM#EIBvhePdWlL`9`G7-rw&v`@8?n>SdV8?{#Cji}5=%juL(9OcC z)hgqa%6=2i7L?xNisR=#isv`bsC;O+G5H6Nee%PV&I(RU(^5^D|RvUT86tcgO_GxCZSB?Yo zLbX<9+teguW21d(mK2Nfx?z_~lOzdCOUs--bC#ij+P{0`cRxIF?RpzM&#g*!*rVqOIqtkeCKhfwWyIs=G8RV%3b?_NQY-?Yz*~*2Vs7J=-5ld z=TAeu%E-u2p7`UtVEdtL^C>}ht4~EUfd;89u&F1G`l`G=wQ9{T zYym-Se7;^?3M z#G&i4YGBi*9Q(I#-Nkt$?RrjMr8a;#6FgdFh-D0vGHFz;YV8V_sBsl$4#Gx>P3V8Q z(?%+v1`K28maB96-dSI8Pa9=dy!{5RleExWyM1Io_Q=_$(d|t`0K3Q{o?77a8IR~G z0w{|DN+C+2_Uu8%#`43qp-G4;<;2DxE{mnivT(4EYQ_%i{Cj0_f8;RI}cA@gbNpl zkA5FDU4aKCVcG4vY>mO{8K)Qt$yxaLMx-`Ow=w1_S2>w=;GjSr(KgfdcTb?Fn#EZj zbvr5R0ukiEV7jSee*<~)647(VkWL$Ua3`XK*x8S7X(8zI205Z{Kq5CPwJu_nr}6#0 zwtLme+W20UgdomLp#Lm4ji8cL!efaH0~sUGK>(*P zLIM|B@I`(OIu%NAj*)`00_G}TM+lh4&Fe~00e~Tx9Y4E z4#C|B;mbP1Ln$Dzi4($mfxjQ7vo2u*k5TO9;eU@fpl_Q(!EcYA*EazO+&`I0|6Ch6 zx(GK8BSR7DVjK1B60&mu*-C`QMw z#xSeXXL*=K8h`?#7Z7;;alBKoDMu~rc&iC_@Ad0Ip3fL;J zTdqt5z)EZ;;u$j74?cMKXQVlUdhuM`oz1OYrAC}X04 zWQF#Jj}9)gNQeMEbp72CWYF6ZzATa;&`h_J(@c$s?&KJ}umCp>!BnjvUDkOjAan^_ z+sAxo=X8D%s`%WMBl@Lgm)ZHIxx0BDm~L?E-myIN7u39$ywK;ffkmraj+6)9>T4HJ zR}Z3|UnZ$kXdgaE^~h6156`21YMLmCjO64HpsZuddea*cy2LMt@7V{|uhmt|&Nq4K z!m1&>UDxpMC+AjCqIl=tv3%Y+L-`6&0hUtPn>Yv{63{GThFg0Dw-ecv-#H^lZUpqp{U zA%N}hv~4L{Co|iqiNN1qg!gZQZ!e-64U)0Z^eDLVeE=n(wqJ!(LVV$jCpqa&a(FjF&mrZ=J4NqG6f8n0$jtizh!g!4;nr#f#O zzRUd9i!+S{gNG=a2Z0IyUr;fQ9_cP3lRNt}{II5WicGs8|NxDsD% zo1}deB0z730sY$+Zj3{$idm_jXmh3}KuD^#W+78ff*43-9XS~ktap#{) zC1s3rOD$R*#o48FGs@yxhs6vr&MmcQcGP7FKi;VF(>Kj9-l#G@=zb2ywMB}IRNZ!` zQutlA#rFdQ2w*&XhA_v!M4tpo5y2i27vkB|3|yQ?x(QMvkRU(L)GH6Gcxj+sfxQ!T z1UPWjpcf3nAAj={t9l1>mf!9B#`x|liyl+Vue2E-tXSfgp-6}*;jmQnqt|0SaeX-( zXf|m(1acRC^rVnE4V-a{kgfzKD*Z;_!1te}`tnKWt`ZG*kqPJH52Cv(%P%`cY}Jk1 zvvwgW`=~7W1*$Zkh1CuVt8H^MO<(t1H^FEgx7^+g!D^u7@^?r zV9jAqJjO&6FPvTW2&@TSuSDE)U?Laxy;F-UtaY4nafqH0TpWVqwZCkIl%DB;{xE*`T*I^9BlF{7kqrOf!;nPpG<_5I+?P^Q9! zLo>q+RLG&gH=aN5ah!!s~ zaO?#2AHDzrm6NYgdHNWOH|%Hmo;#40rd1D)Wo@iwcjJd>d6A5`USamp@sl77u`!Nc zT+LuyOnm`v*)zsOql&wk<(r;+*qBY*QS^&cLib@%`)?|l#2V8nq>Ge;ol9D&l1ukF69p!K2OWu)+v zG1|aXeEX%jLioS`;8eU%X(U--+*!LbO`&|3b$yr&R{MaD6rc z-Pg3EyVH|&_Uu46w6kY>rZba_SYnt#>s@O!x{^-;^eY4X;poCAhBH=I!oo_2XHGBW z2zvj)DJB|Kqe6@xSHd+@gE?bxwbkXwi3RUJ2t1?H?Ekm6i*KkpP3jR<`Zf`ynb{v_ z3!`88CIc_NLhHc36bZie81<)LGSZMv1UZCo89_@sJ|pCYkVazhEp9^$JcjuC^XGHK z+&)_4frC@%p_|7vC=Tx!&J7opQap0(0;cfXD8!-?${DWW!oTPudgA)30zw2B`qokE zPamUs<8@s6nFpBv=!3{Wjp}P>ypHrHiX{EV5apqRrLPC~>yEpNp?`j*#gP;99x(?h z@XL4Z@{Kkvvl)S|-PXtrAIvUt@AcaX(%}=i!0MA85+B}-sbHsZYM{pQ zoi{s1nUIVQIg*kfDlt>Wo+N7t(jDij7y^B7CR-eTlTK@2#oS7ZCo<{R*GJ#EgKM`9 z8e77hCG?O<2|Gpy3Q^$k<8y&+VuziDsDc#|M{auJ`uwF(7XfrfIuXQ<#r-t`W&a6G zxzcH}^zgu*3kW30li(nYz+XJ^YWn9CnfuV&XSi+en5VydMDZ!Je|m^xXP2Df((u{2 zRW2^Km>Sm0my(O)<|jFdZ*>&_XxOz-O|HRq>J%_ip24+|URTe95F1IsWLY-uE>{UJ zonPg}bIYbaw_iQMJqM;Tb>lX}Czu;H*{J5!hrYjfc8NQ#naK5t&xpECzN+jc0`z$* z*yntI$yURiYoXiWocUC|*wKJ8OpxX27*W1uUoiXTsTAS!-<>tEecQfqe&M#A*3QV_ z+a0UBBN?q0CWoqpD~BTDAcHdsQ3Z8%7msXjvBh@;f#LS7VUa^NEd|;t)`VA@;EBH6 zk48ZHw_sXtx*h4v1iFZ0O7zSA>NDrLxSZ|;djH|8c<{!n9KGx&FNS_yAI$)+pT3`( zTTPigtc(*U%-b*-^y&*SZ|+oZ_#p(>_%XvjXB|NQO{+zU?9;ifE#NVTDXj$h;nn#y zzVh^0M1+ssv70-to-oFRlbn$Fo|Tb>7cqc-J~g-MNjd zD*Wx!=eV%k;ew^}1%gVtc8qSG^h^WvhXE2S z0)U49jM(6CzBv7p4Hfl}F!S^NE?&hZiMRxppp$igBdIT*Tj8lw3;e75uVGhkL@N*P zZY7;|IL|tcR$Vr#ZS$$QRc_uL{;i!$XrEzh=oNwhpa~18B>jIM&j8YsJkHqwV=+5_ zA)97oCB2Aoykb3)ver)c_RAOf#QoRMs8yUlf3|rzFMcZ=1PRL!C^Z7j#E*we4g!X~ zpT>({5`F~?10mFzs@qBb+|lt94TXAs0Ei=q^aaG3gvXJdBr+LfX^WuW zGQ(gkE`(dRj`@*9L71&L0MDOUTGt3v1!MgDc;7E)D;t5j=JLYXOJ_i6Hd{gC6(k*^ zxMFPtQA8-BC?$LxrL$FW1eG}bK(taZL_!?dc>vd_G7~hmpkOdi(CH>!;2)-02O*w3 zxv;JgcyMOesHYtCo4EK)A_YZmqU#3&$RjUaJpJn{?UN$xYqeU`>-981L!wftcxTd! zN{A$(f>4cxfl5R~L|4V>yg&pHF%Y{(AcleT@Y2)#xei|^EX%7@B{=>%_|)7gUw!(F zZI{U=eQwlZ_Dl|11K2AnU@otkTN?os=^!Ba#`EX?>gR6V{jX1-KF6+IJE+xyq8%eC zil7=t)N9$_rzxSUViG;Qn)OG$k`?20s>=-C$iP&pSdz;m+|8p1Ay(U6zV^&HzWBY< z<$51@-{I{H)nZfEV&>8%UKt1=q{Qz6o&WXdiGR3p=jgi+ZXdhr#49J6oSa~&(V)>7 zbdp3wKqZc-Rbm>o3e`Abpi;>;foKCT?WPI}k&Y)L*4lQ6r>i42PN9DZB-`esZnfRz zbC1t*VX4J)XO_G?i|+fThIs$stE`PekY~tT1wggc4u`tZ-vOGy+He2G5B}$G{gVUd z@40^ZeaVHn!8D@V2^j0XtAuW%NEGSua977?oj6#pv3q*l>)2F&l#}NM^m_VI>>r?r zh|s9U9Gn^E$ceeqVV{1!VaEu+`QGbj)ZFg^lrxvcFmGhEg;hWoXaTFhC7|&~-+1ZE zfA*b|M?ZM;&ReI42c}d+6lH&?ZJQa#Q4~elEs`jXqbU7>gJ2_1Xw`ADHBpy<;gw$S>U-dOP;>JVdp5f?VI49>$mwTsuTfi zz04ALt}dJJ`k?~azzQh?3;@yjl}_>}-#WPf)Ukhss$Ep6RN^>}qiVGp#c>>0t5p*o z$8jVg6`*q8^^@1!dClYj7ZpUrt~8bo_0sAY!ulLkoTebJp<2v?gHemp000IeNklAtY&3JnO_SSCS?HqZ`kI)@RB5nL?lX*Bm$y1 zPU#<~4>3?xN)4#)$g~E1TdHha*9H<_W*}NZg=tDAZOpJUoW*mP$SbN?+|6)ME7Tsi zevAS-*grc|SVU-G5kP-V!!UtT=ASa7Owa1D8Yz-ADnz?|HLfja|3i^E7^v0U%ot#3I64R<`?(*ig&_ z?sH7a0K-5sP>t6@4QbX5yy9Qqe7?>ndkDu4x$q!mpI+r3H z8N}ozzlaIK_m~tp5};bC#BFV0>kf-tg}IK-FQ1PO-vdS{Ab5y2W%h!-suksv-g21iJYL=|@Wb-z zA;&D*4sg@-P!05XH4rFb|Bbttw9W~TL~&$r)}M&kfYjm;ri@^hVU*%wWImpoRX8Lp zW+)&2duau{84U2FIIs62XOv_jNcy?>qT$ziV|AUjsK}f>P(*MqpYrtiyn_tIXBM-v zxdn%T{d2)2P|jlCAdIiaD85bRz;%(0aao2kiT$!oer20NHs4(V3}AN%Wv_k*mRk6E zrtqXV`;kC8*RvOF-d=)oMky)ROe1B6;g40oI?mV0*(8t(@iH7%`iQNLpbKunR%^ud***02P=aH-6guIzUN0{VT#1axS13&RWwVAW1?T zRs8S*c<(ukyJ-vU2frTq8fInnmi}9x0W{iMdg*Tix^XrJz4XZLQKsN(;(a9(PQ;d=96yr8D*5?%ZI={xNX2z>M9`IoSb-87EaiVR>n zAw6-lo0Ziyrlx!%O1BMBB|UJmK^QMq3$Gu>%fiZtZo~4(w6d~BCsC`fTwGhqH{?fm zTaf`EV}M6qymg;J&R#)?prC^86I{q6CTQ5xxr;Q~S zV7T@#NVBl8$eFX}X?N7xryf4>8Pc6pT{f8kwju&(2IvCq|MSG`_ojxbpZ(y?yB<1u z>Q#ms4Ms+Wsn`9#hO>_)x*G~D`_#L{-%$y<7#q^}`7|r5Yb-4-(`>eAca!FSf9%wM zd2O*ZN4l4?i``zgDNInb>*lTQs-&Aj$AM{JC$QtE-nRX&pSgMG2lh-2U7cUS3yiBG zq7@NlqkfKmX`buWBkyh(+iV7p7JBRf_l&#W6SGBc(B~pT4%&vJ-;y1H6 zo1y}?q74B4`2uaoc1gFJm!3F!3p-6reVV#S+P1~)xln2aGxKw%I;15=nP4;v*BJF5@~ zX~e=RO(0DQ8!Ous@lO!3ku-wX1Pc`hW=1j0o%@{Yee84gW38RSdE_RSe6U~@d;M77 zx0m5SrxqvIzWwskuTLy5Erlq8LLy)wxde%WmP%u}iyHtTP_wsHBhD_2=q zh3Fxr|H~K%`+W4<*5-2oqA900B+KLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C1BFRMK~#9!l$5=XRAm&0pZC3YW@mP0*%fCORzN5;q9A?| zn>7|ojFnm#3kw=eh_Ru<5E6`l3SDNv^^@~QPSmUAi6|-nsG@+XVhkpCA^Y;D%ipxR z`F)16&P#_seDvm_Q=gyy_%!3QAQ4m_e2js-$&Shw2R`}s{3$lc2)I&8W*BRLnE>Mf zWz825FcxETCMu;pJR&3D5CvR;kQ0g=9|eg45kW#AiZT&aa3RM31=b{iKdutHu=M0! zdW(zr7;xU>ODW;kE9`7kDVJ=0XEkZmDy8wvYp2yK+K&WF2(W7gXL7nXey7}DV9$Z) zkWvk-L0^9Xtl@*hPrvp4yT=y{B%S8n?y*n5e&gC==kn&pvRcbn+-FdIoC%XLes~Vj z8c0|>T|qSktz#{d%h#*(Fa7-CwI8p11{4oB2I#gigGF#~?aX6%_#$K(Q#+C?Q1fo}qDgh_NafJvs$NvqC5- z>OHmuMg>v92Vt?3bElp0#8j1iCTycCq7aNBD`x4`6yNk@F6VZu&qA}$QZMIz&(R(@ z>Se<_FYZPX&yX;7^zwvIPRXYC5;{#{$VfHN7*n2Uq*P3xwbEs#s+^pgWu^+wJBDj9 z0v2m9x9^f%yhPtx3R^-$LB$64??||Hug&p;lPs-tx!qbJx&rarF^+;aN;W-77Qu@l zdWVUQ3pegjPb07FsbZt!+|_@u>IlIzOdbInReY^NUM-XL`*`os;OO=SwAx+XKD3jf z$eFDfuKe936gd+7)|c1My(`CpaXb?x#Ucb(5WR48?>II(dOhLKJ4>{?Ink|xbx3&x z{QZTS--$Wb@9%Ndhp;n>U56nTdA+c56r;g_$@U4IWK z0WyDN-wy=$T5u7Sh*x2{qMUejH_ivL!GH?e7?8CLHfnAN(H8_4P!UuGFG8Nl#v}$K zTN{u3V6av^Rst#!6~U;Y5)n~Ul{5vms_}5nUb^+${+Y=+7bKUB1+=&=U$=H{n)FA?)V8z`X=khhD(I-F48AA@ z#TU^R5fR^%VO|CQ4#g41H@_5wAy3hU;PQKqu@uutQ-Tg3W)BQQ-Ss}SFio^`b$6l^%sE72L|xM z>7$=wdJd*9fb$IY{uqb>rWKa*w>j{&yIlP6(nrrukDt4{(!KeC0mPIV6eHlkByR8C zwA#E;dbJ9GfiJ+-2r)ZzIyS8HrvgwjLlslORLbz0VXE5>u__=HkfMN`P*rlqp5o^K z003gdw%UM_oud~bDGO?biXf_p2r7cch^o=FHdzhloc><`0O)q{zx+A**^{(C`3c$& zmPnlrDGN!3oHN;s+-RWp7SOlu5MO)_O^LCZp9Ds#;eZ76hX!D#P%F^qFOt{S=r6oO zT)Ykw#xv5SHqpQehdGD&I-F`?v*+-WV2xJ&%8$PBnwka!=dmKi*nZ{JKmFkK8;idg z3V^CX;gF9#PhQ_7t*;U4$7$s?j?SNjqw`R2l$BssZIUuqaBkMQ83t*f0&8y{`}Tv? zAHIHL@pnLY|057B*WddWZh4V3eugmBg3yJr!!XtY5pX-Izzp-St2&BNih7e~V`Lg= z0?Y4z1b_v>xmDcJCG79ZD4=r_AYeIx<{geGzipc@jA|3!gJe?94)DVr0bnLrFJr2x z-#{#*#RRBelB#gqb{X0ZZg}Fmz_hHMy=->`)DTnbQ6F8}BCiAz8U0YgP)u?O?HEHNcMH4>Cu!yGG_8P37vz&fpbRAMn4IU zIsp$%HyfB_bRTZoorG3`peNWypx2qDCjps?=rKi7ELjpm)aZrGS~s%18F;uE`0sj; z<;}pknQ^}M!f{k(J3oesfEs3*?2pjf^7Q8F^dGDu-H4g6((bXi+Ts4A9;=;5CuF)I z(~SuaOpX@J&o+4RY!f7-Sx`9u08EjbNmGXi^*Z_XB5wH}dgutM%Ji70In&^Lt4>V9 zN+)vVA4{~xJZqi6<%=_%X^oLZ$SG3Oyl)QdDh&fHC2VB{f9p2VZW93YMVU}JQg_05 zVa!eygb>*5botzwCQcGJ?yaCXkzz!1D*MGgIj{?WI zsAME3L?WRd`SYzMPBv>icWj(XCnmUd`yubHZQ-meSrw^DbJz$7s0v#jzd-No94Q7; z45(Upo;e{oQAfG?pA|N?x_tRU3(1jFhwH3wb$Mf96>kHe8P&bdU^hFNkZYd)%wfXR zI5|X8jED)Ug62rgGWQ;CaP7`RzJ6(zR>RQ?fx``l<;bo3YlIN7oKVT=L8`!+bCv~7 zeIx{8jD(oTqUBB`)6bcuwGNjr9${v(POl&E4o01#IkC91Nw43hUU;0yV^ud4fObEI z!i1VZ-6^95jMczls7k4PB=eEkF}#C*2$(5~v9P?wqjr~Oy#bO?^VRzRMD(j0i&xK| zc=PioT2qHZ64~fwmqT)z^Mqz8EXPuwnUX7kS~$45xXx?W7nrCiUz%@GM~Ep?J7_O> zXRZJH@4x!j?|7KSLs57);{DW5zx&yj=bfHRIh9(mYCL9!Vw{^E<&~FCQ}>SX5nn|H zoaKWtA+Q4cL7|3%s!BqSJG1Yp2ttA?0#@|{GovtLvQ{>z5E7gHy+!Xx{1qhSH`uHSw4?Te>p=Zji>+c~9x8K|QH<(4-%!vS0U z%wOJG`Zs&r7u}EmYNG|8zA$&_a&vs-nSls;BDT8oRX)A*eyg80-dNoDMTToUA7(b;p1AzWLyg6iHd4DAJS^S#=^gmIJ|V4Ob};14)1Y zX^H{`iY|-*MS-HrZn`K?pql~(y2>VttW!8i7j00WMo}-)AWq@hb)CAhEhkE1eOmnH zkTdh%yZ4;a#eHvPWGmvhqH}@aeZ2Gd-ua(<&iS8nE*%q&OQ(*lTz+EtQl3Y5zwZTr zc;Itd-sz9u+x(xmZf)J1^xY2{_Z8;D<7-c!dHx%J@}+OBo|yj)i4m*6uzDI^pcu~5 zn$^pufWyr2-m$UMTl=dQfBdK4{?U8?^oawQnPKU*zj@)!3!gcAlJ*=tavGdxDBZnC z(hoB}f)YR#?cTxXJG^=2r?)Tv#dn|S!<~l#&^YLZQ*-mHop$p$jW#S_gw6t(jszXb zsOfSX1Ykp;hT)(+hurutj?c}kJahiUlRtcS?UMzdQ92@`OdWyNG=#jCcqE+Jm>Jcx z4mTvNRqH;3tVtA6Rc0PW_=x}n!&z+-DoNN%prL?6joWAk#!SiTYv(`(GeuMh(flV4 z0KhZ}U@DcIwMGrJ24h1o_uHA_nhI!rk36jW69O;;Rfao(Vj_qtB7&(7wXBtJh9%$? zAgy3}j8+*@wFkfSPyy5?u-C;p9S9}SJMz3BF9RZis-kL$YIR+KGebqtqCgE=p=6<1 z{+a<8Xy)L`75ppzLHNp-8JsyoFE1zuB~k>$lqiu{fr+As8R{LPz^kue@BNJC=bvTj z>@2Fvpp3m=3jozB1U4HG1G2tOvA9Ti>o&4|4Y3m4F|t+@UztwR6m2%q`8m{iY`0a1N7cLa)k+PfPpOgC2!l5N5rAZcg2OLp~#c;}GCrwQ#0 zDa*tUXNnoDDf9(Tt#`nBh1e;hc!d%`}l!t=Xo-K5e+4 zy^EYK(fJmLru(b{Q30v_2a}XWXgycYG{I!_{KWQF=Y|K4iFu%ARklj349GwbFUUa9 zJYXlNgw!z;8rSydcN?o2#?8mfaJZCqGZ}~p0VGXnrs&&ySYDuc1>%)0^z0P246fcK z73y56wtpKlCrCkIlq=Qd21cxQ!_Wy3OaLGzCRW1@0z7>LEm}ywMBdmzzPNz)+gPYg zM=+E7CU!~S(YgaN2boLjp%R?hj3yY9GqD*ag0;aSSP_stL3RQ*Qvs}O*qKz8VYo3T zb}oe!M=CALDBRsIx!WzsFdkc+rqRg2H0{t~0Z5spqNY$pY!C_Wc=9vnkZbG6YL3mO zbla%_iZdf~iIg2ssxT;({XDWWDB0=-e!0=-=624Yj5y$hXO}s3Y=$6*yaOqtjBw-# z`g)1@27`;|DMCS9Zy;NJ?2Mr*6lEZUh^o>ngxx%_)eEe33qI%;49Z9pqlhW#GXpi{ zvnOX*nr#qcOli3pdRR`ZVjzOeWDK7AP4a$@_H(2kh!U~91T#Vuwu+q1y@Ko818(o+ z?DR|WAgCJ6jGslZr;MJcF|L9ETj+`B@oJpGojOflSdkSc6pkkZO8V0 z&hlJ?OOH;ozT4;Ooi5IlqAH4pMD{VF(7{X`Q!1f=+t`G?J=(ASH`d!DUph{I?I>}} zVXk6Q5zHbgf=DFB$bP@%->YTg?Cc_ldL9gy{~^d-AzQh*>LJM4|X}$$@tvL3^Pqwn$5Ve zxzFpr*u-0PzouuXO2zz#1V8~|z!f>g$|B}LBqA{q%Mwu?b}e)qga}QiymfP%t-YKt zpFhf(qirzZcqgMM1213sfZcw9Gp)3r+Aj639|{07!(ycW_-S^(_#A^rm(aXOAS7Z^ zy|A`pNhZlf@(-`w;n@raZ2cjA+=0mL_~;DkcyCM6T+ortl9MZ;MxY~mpXj);t~?;UXURMb5kCt zLY@ciZ1;yIhjU}Wmze~RcXs)CBcCJEES5Gs$e0YxFnEN4Xqec#b#9ODMV9dT8>Mr zv!Icvb?Y&G8VyH3&lwaYF$Q{hfk^7RDvY{-hXBB>?c^KRZg2nc+^Li2eA8neXkE-G%f;ljy;ZomJKGfh9Rd#!k7hk>3W_LglgvX9e(R4~4B~_TU6CZ{T zl(6}?|N721_k;e~>7|87%%Bu?c~BRkxu9xc#!s7B%S13-db!m_Onq*UveVsO$&3Ga zoj2a!X0GixyE4afXXgnapi-UWh)$HqO#;Ax-nF&fzy0}l-dJzINd@2V-nUwf%r_c7 zh-82Ldr$rTvrqbqQ6&jb0~l6XsmPB8^KHi;Kex&YzqJBpbebOTpbTMDuvN|CL#oK= z1HS-vBg_C7@i%Zx#^+llX^r2sm>hgveSJGCY}U< z0{ayZ&qv!FOO1>{>PXiB6wskS^-kQoGb4d*476GD0iBS@CQPS?K<@X{(u0|$f+n>a zMrR3D?L>WTI@X=|@ClA-YM|ABKf(FO#{uxGh8t>7W97u-+8r~W)U~P(b?UW|lh=iQ z+KEFfDGwT{YmtZvd=h5tbfy+?98i5dTpwF=F5NGO#l#aJaB7A|9kmIoBItg?C&6{h z;m6F$Qpds!yg&FH`!E3LAcUAXPly3?{-ZWN)VdDaEkh@+0aC4#(xDIn-9ao4dig^i za{}?@x9+^TzOjju0vB_9$kQAPoD_H|a8lwV;56VR;56bbrr+ziT8sIB*RS5Z`}6hv z_c)-rJJJ7295Cj9r=I)N!q-3lnG?@s-Zk#&G!M0##&~?!dbM(H3^MQBTDRE#$DiE! zPPfpPfa^Tqzeo-PKm%9=J_Vcv+GFzme}(~F;08(f{RchoFaSX67|sBl>iS_v0raZ* bfy($lyo5$XTv~3gd^4Qn^{cnKYfaASk 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 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "prefs.h" +#include "kmahjongg.h" +#include "settings.h" +#include "GameTimer.h" +#include "Editor.h" + +static const char *gameMagic = "kmahjongg-game-v1.0"; + +//---------------------------------------------------------- +// Defines +//---------------------------------------------------------- +#define ID_STATUS_TILENUMBER 1 +#define ID_STATUS_MESSAGE 2 +#define ID_STATUS_GAME 3 + +int is_paused = 0; + +/** + Constructor. +*/ +KMahjongg::KMahjongg( QWidget* parent, const char *name) + : KMainWindow(parent, name) +{ + boardEditor = 0; + + // init board widget + bw = new BoardWidget( this ); + setCentralWidget( bw ); + + previewLoad = new Preview(this); + + setupStatusBar(); + setupKAction(); + + gameTimer = new GameTimer(toolBar()); + toolBar()->insertWidget(ID_GAME_TIMER, gameTimer->width() , gameTimer); + toolBar()->alignItemRight( ID_GAME_TIMER, true ); + + theHighScores = new HighScore(this); + + + bDemoModeActive = false; + + connect( bw, SIGNAL( statusTextChanged(const QString&, long) ), + SLOT( showStatusText(const QString&, long) ) ); + + connect( bw, SIGNAL( tileNumberChanged(int,int,int) ), + SLOT( showTileNumber(int,int,int) ) ); + + connect( bw, SIGNAL( demoModeChanged(bool) ), + SLOT( demoModeChanged(bool) ) ); + + connect( bw, SIGNAL( gameOver(unsigned short , unsigned short)), this, + SLOT( gameOver(unsigned short , unsigned short))); + + + connect(bw, SIGNAL(gameCalculated()), + this, SLOT(timerReset())); + + // Make connections for the preview load dialog + connect( previewLoad, SIGNAL( boardRedraw(bool) ), + bw, SLOT( drawBoard(bool) ) ); + + connect( previewLoad, SIGNAL( layoutChange() ), + this, SLOT( newGame() ) ); + + + connect( previewLoad, SIGNAL( loadBackground(const QString&, bool) ), + bw, SLOT(loadBackground(const QString&, bool) ) ); + + connect( previewLoad, SIGNAL( loadTileset(const QString &) ), + bw, SLOT(loadTileset(const QString&) ) ); + connect( previewLoad, SIGNAL( loadBoard(const QString&) ), + SLOT(loadBoardLayout(const QString&) ) ); + + startNewGame( ); + +} + +// --------------------------------------------------------- +KMahjongg::~KMahjongg() +{ + delete previewLoad; + delete theHighScores; + delete bw; +} + +// --------------------------------------------------------- +void KMahjongg::setupKAction() +{ + // game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::load(this, SLOT(loadGame()), actionCollection()); + KStdGameAction::save(this, SLOT(saveGame()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::restart(this, SLOT(restartGame()), actionCollection()); + new KAction(i18n("New Numbered Game..."), "newnum", 0, this, SLOT(startNewNumeric()), actionCollection(), "game_new_numeric"); + new KAction(i18n("Open Th&eme..."), 0, this, SLOT(openTheme()), actionCollection(), "game_open_theme"); + new KAction(i18n("Open &Tileset..."), 0, this, SLOT(openTileset()), actionCollection(), "game_open_tileset"); + new KAction(i18n("Open &Background..."), 0, this, SLOT(openBackground()), actionCollection(), "game_open_background"); + new KAction(i18n("Open La&yout..."), 0, this, SLOT(openLayout()), actionCollection(), "game_open_layout"); + new KAction(i18n("Sa&ve Theme..."), 0, this, SLOT(saveTheme()), actionCollection(), "game_save_theme"); + // originally "file" ends here + KStdGameAction::hint(bw, SLOT(helpMove()), actionCollection()); + new KAction(i18n("Shu&ffle"), "reload", 0, bw, SLOT(shuffle()), actionCollection(), "move_shuffle"); + demoAction = KStdGameAction::demo(this, SLOT(demoMode()), actionCollection()); + showMatchingTilesAction = new KToggleAction(i18n("Show &Matching Tiles"), 0, this, SLOT(showMatchingTiles()), actionCollection(), "options_show_matching_tiles"); + showMatchingTilesAction->setCheckedState(i18n("Hide &Matching Tiles")); + showMatchingTilesAction->setChecked(Prefs::showMatchingTiles()); + bw->setShowMatch( Prefs::showMatchingTiles() ); + KStdGameAction::highscores(this, SLOT(showHighscores()), actionCollection()); + pauseAction = KStdGameAction::pause(this, SLOT(pause()), actionCollection()); + + // TODO: store the background ; open on startup + // TODO: same about layout + // TODO: same about theme + + // move + undoAction = KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + redoAction = KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + + // edit + new KAction(i18n("&Board Editor"), 0, this, SLOT(slotBoardEditor()), actionCollection(), "edit_board_editor"); + + // settings + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); + + setupGUI(); +} + +// --------------------------------------------------------- +void KMahjongg::setupStatusBar() +{ + // The following isn't possible with the new KStatusBar anymore. + // The correct fix is probably to reverse the order of adding the + // widgets. :/ + // Just commenting it out for now (order is not as important + // as compilation), in case someone comes up with a better fix. + // pStatusBar->setInsertOrder( KStatusBar::RightToLeft ); + + tilesLeftLabel= new QLabel("Removed: 0000/0000", statusBar()); + tilesLeftLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(tilesLeftLabel, tilesLeftLabel->sizeHint().width(), ID_STATUS_GAME); + + + gameNumLabel = new QLabel("Game: 000000000000000000000", statusBar()); + gameNumLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(gameNumLabel, gameNumLabel->sizeHint().width(), ID_STATUS_TILENUMBER); + + + statusLabel= new QLabel("Kmahjongg", statusBar()); + statusLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + statusBar()->addWidget(statusLabel, statusLabel->sizeHint().width(), ID_STATUS_MESSAGE); + + // pStatusBar->setAlignment( ID_STATUS_TILENUMBER, AlignCenter ); +} + +void KMahjongg::setDisplayedWidth() +{ + bw->setDisplayedWidth(); +/* setFixedSize( bw->size() + + QSize( 2, (!statusBar()->isHidden() ? statusBar()->height() : 0) + + 2 + menuBar()->height() ) ); + toolBar()->setFixedWidth(bw->width());*/ + toolBar()->alignItemRight( ID_GAME_TIMER, true ); + bw->drawBoard(); +} + + +// --------------------------------------------------------- +void KMahjongg::startNewNumeric() +{ + bool ok; + int s = KInputDialog::getInteger(i18n("New Game"),i18n("Enter game number:"),0,0,INT_MAX,1,&ok,this); + if (ok) startNewGame(s); +} + +void KMahjongg::undo() +{ + bw->Game.allow_redo += bw->undoMove(); + demoModeChanged(false); +} + +void KMahjongg::redo() +{ + if (bw->Game.allow_redo >0) { + bw->Game.allow_redo--; + bw->redoMove(); + demoModeChanged(false); + } +} + +/** + * Show Configure dialog. + */ +void KMahjongg::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + dialog->addPage(new Settings(0, "General"), i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), bw, SLOT(loadSettings())); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(setDisplayedWidth())); + dialog->show(); +} + +void KMahjongg::demoMode() +{ + if( bDemoModeActive ) { + bw->stopDemoMode(); + } else { + // we assume demo mode removes tiles so we can + // disbale redo here. + bw->Game.allow_redo=false; + bw->startDemoMode(); + } + +} + +void KMahjongg::pause() +{ + is_paused = !is_paused; + demoModeChanged(false); + gameTimer->pause(); + bw->pause(); +} + +void KMahjongg::showMatchingTiles() +{ + Prefs::setShowMatchingTiles(!Prefs::showMatchingTiles()); + bw->setShowMatch( Prefs::showMatchingTiles() ); + showMatchingTilesAction->setChecked(Prefs::showMatchingTiles()); + Prefs::writeConfig(); +} + +void KMahjongg::showHighscores() +{ + theHighScores->exec(bw->getLayoutName()); +} + +void KMahjongg::openTheme() +{ + previewLoad->initialise(Preview::theme); + previewLoad->exec(); +} + +void KMahjongg::saveTheme() +{ + previewLoad->initialise(Preview::theme); + previewLoad->saveTheme(); +} + +void KMahjongg::openLayout() +{ + previewLoad->initialise(Preview::board); + previewLoad->exec(); +} + +void KMahjongg::openBackground() +{ + previewLoad->initialise(Preview::background); + previewLoad->exec(); +} + +void KMahjongg::openTileset() +{ + previewLoad->initialise(Preview::tileset); + previewLoad->exec(); +} + +void KMahjongg::slotBoardEditor() +{ + if (!boardEditor) + boardEditor = new Editor(this); + boardEditor->exec(); +} + +//---------------------------------------------------------- +// signalled from the prieview dialog to generate a new game +// we don't make startNewGame a slot because it has a default +// param. + +void KMahjongg::newGame() +{ + startNewGame(); +} + + + +// --------------------------------------------------------- +void KMahjongg::startNewGame( int item ) +{ + if( ! bDemoModeActive ) { + bw->calculateNewGame(item); + + // initialise button states + bw->Game.allow_redo = bw->Game.allow_undo = 0; + + timerReset(); + + // update the initial enabled/disabled state for + // the menu and the tool bar. + demoModeChanged(false); + } +} + +// --------------------------------------------------------- +void KMahjongg::timerReset() { + + // initialise the scoring system + gameElapsedTime = 0; + + // start the game timer + gameTimer->start(); + +} + + +// --------------------------------------------------------- + +void KMahjongg::gameOver( + unsigned short numRemoved, + unsigned short cheats) +{ + int time; + int score; + + gameTimer->pause(); + long gameNum = bw->getGameNum(); + KMessageBox::information(this, i18n("You have won!")); + bw->animateMoveList(); + int elapsed = gameTimer->toInt(); + + time = score = 0; + + // get the time in milli secs + // subtract from 20 minutes to get bonus. if longer than 20 then ignore + time = (60*20) - gameTimer->toInt(); + if (time <0) + time =0; + // conv back to secs (max bonus = 60*20 = 1200 + + // points per removed tile bonus (for deragon max = 144*10 = 1440 + score += (numRemoved * 20); + // time bonus one point per second under one hour + score += time; + // points per cheat penalty (max penalty = 1440 for dragon) + score -= (cheats *20); + if (score < 0) + score = 0; + + theHighScores->checkHighScore(score, elapsed, gameNum, bw->getBoardName()); + + timerReset(); +} + +// --------------------------------------------------------- +void KMahjongg::showStatusText( const QString &msg, long board ) +{ + statusLabel->setText(msg); + QString str = i18n("Game number: %1").arg(board); + gameNumLabel->setText(str); + +} + +// --------------------------------------------------------- +void KMahjongg::showTileNumber( int iMaximum, int iCurrent, int iLeft ) +{ + // Hmm... seems iCurrent is the number of remaining tiles, not removed ... + //QString szBuffer = i18n("Removed: %1/%2").arg(iCurrent).arg(iMaximum); + QString szBuffer = i18n("Removed: %1/%2 Combinations left: %3").arg(iMaximum-iCurrent).arg(iMaximum).arg(iLeft); + tilesLeftLabel->setText(szBuffer); + + // Update here since undo allow is effected by demo mode + // removal. However we only change the enabled state of the + // items when not in demo mode + bw->Game.allow_undo = iMaximum != iCurrent; + + // update undo menu item, if demomode is inactive + if( ! bDemoModeActive && !is_paused) + { + +// pMenuBar->setItemEnabled( ID_EDIT_UNDO, bw->Game.allow_undo); +// toolBar->setItemEnabled( ID_EDIT_UNDO, bw->Game.allow_undo); + undoAction->setEnabled(bw->Game.allow_undo); + } +} + + +// --------------------------------------------------------- +void KMahjongg::demoModeChanged( bool bActive) +{ + bDemoModeActive = bActive; + + pauseAction->setChecked(is_paused); + demoAction->setChecked(bActive || is_paused); + + if (is_paused) + stateChanged("paused"); + else if (bActive) + stateChanged("active"); + else { + stateChanged("inactive"); + undoAction->setEnabled(bw->Game.allow_undo); + redoAction->setEnabled(bw->Game.allow_redo); + } +} + +void KMahjongg::loadBoardLayout(const QString &file) { + bw->loadBoardLayout(file); +} + +void KMahjongg::tileSizeChanged() { + bw->tileSizeChanged(); + setDisplayedWidth(); +} + + +void KMahjongg::loadGame() { + GAMEDATA in; + char buffer[1024]; + QString fname; + + // Get the name of the file to load + KURL url = KFileDialog::getOpenURL( NULL, "*.kmgame", this, i18n("Load Game" ) ); + + if ( url.isEmpty() ) + return; + + KIO::NetAccess::download( url, fname, this ); + + // open the file for reading + FILE *outFile = fopen( QFile::encodeName(fname), "r"); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not read from file. Aborting.")); + return; + } + + // verify the magic + fscanf(outFile, "%1023s\n", buffer); + if (strcmp(buffer, gameMagic) != 0) { + KMessageBox::sorry(this, + i18n("File format not recognized.")); + fclose(outFile); + return; + } + + //ed the elapsed time + fscanf(outFile, "%1023s\n", buffer); + gameTimer->fromString(buffer); + + // suck out all the game data + fread(&in, sizeof(GAMEDATA), 1, outFile); + memcpy(&bw->Game, &in, sizeof(GAMEDATA)); + + // close the file before exit + fclose(outFile); + + KIO::NetAccess::removeTempFile( fname ); + + // refresh the board + bw->gameLoaded(); +} + +void KMahjongg::restartGame() { + if( ! bDemoModeActive ) { + bw->calculateNewGame(bw->getGameNum()); + + // initialise button states + bw->Game.allow_redo = bw->Game.allow_undo = 0; + + timerReset(); + + // update the initial enabled/disabled state for + // the menu and the tool bar. + demoModeChanged(false); + if (is_paused) + { + pauseAction->setChecked(false); + is_paused = false; + bw->pause(); + } + } +} + +void KMahjongg::saveGame() { + + // Get the name of the file to save + KURL url = KFileDialog::getSaveURL( NULL, "*.kmgame", this, i18n("Save Game" ) ); + + if ( url.isEmpty() ) + return; + + if( !url.isLocalFile() ) + { + KMessageBox::sorry( this, i18n( "Only saving to local files currently supported." ) ); + return; + } + + FILE *outFile = fopen( QFile::encodeName(url.path()), "w"); + if (outFile == NULL) { + KMessageBox::sorry(this, + i18n("Could not write to file. Aborting.")); + return; + } + + // stick in the magic id string + fprintf(outFile, "%s\n", gameMagic); + + // Now stick in the elapsed time for the game + fprintf(outFile, "%s\n", gameTimer->toString().utf8().data()); + + + // chuck in all the game data + fwrite(&bw->Game, sizeof(GAMEDATA), 1, outFile); + + // close the file before exit + fclose(outFile); +} + + +#include "kmahjongg.moc" diff --git a/kmahjongg/kmahjongg.desktop b/kmahjongg/kmahjongg.desktop new file mode 100644 index 00000000..4ffb95bc --- /dev/null +++ b/kmahjongg/kmahjongg.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +Name=KMahjongg +Name[af]=Kmahjong +Name[ar]=لعبة KMahjongg +Name[be]=Маджонг +Name[bn]=কে-মাহজং +Name[eo]=Mahjongo +Name[hi]=के-महजोंग +Name[ne]=केडीई माहजोङ +Name[pa]=ਕੇ-ਮਹਿਜੋਂਗ +Name[pl]=Mahjongg +Name[sv]=Kmahjongg +Name[tg]=KМаҷонг +Name[th]=มาจง - K +Name[zh_TW]=KMahjongg 麻將 +Exec=kmahjongg %i %m -caption "%c" +Type=Application +GenericName=Mahjongg-like Tile Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž маджонг +GenericName[bg]=Игра Ñ Ð¿Ð»Ð¾Ñ‡ÐºÐ¸ +GenericName[bn]=মাহজং-জাতীয় টালির খেলা +GenericName[br]=Ur c'hoari teol a seurt gant Mahjongg +GenericName[bs]=Igra nalik na Mahjongg +GenericName[ca]=Joc de mosaics a l'estil Mahjongg +GenericName[cs]=Hra s dlaždicemi podobná Mahjongg +GenericName[cy]=Gêm Deiliau sy'n debyg i Mahjongg +GenericName[da]=Mahjongg-lignende flisespil +GenericName[de]=Mahjongg-ähnliches Spiel mit Steinen +GenericName[el]=Παιχνίδι παÏόμοιο με το Mahjongg +GenericName[eo]=Mahjongg-simila ludo +GenericName[es]=Juego de fichas similar al Mahjongg +GenericName[et]=Mahjonggi moodi klotsimäng +GenericName[eu]=Mahjongg-en antzeko fitxa-jokoa +GenericName[fa]=بازی کاشی شبیه Mahjongg +GenericName[fi]=Mahjonggin kaltainen peli +GenericName[fr]=Jeu de tuiles dans le style du Mahjongg +GenericName[ga]=Cluiche Tíleanna Mar Mahjongg +GenericName[he]=חיקוי Mahjongg, משחק ××‘× ×™× (קלפי×) +GenericName[hr]=Igra s ploÄicama poput Mahjongga +GenericName[hu]=Mahjongg +GenericName[is]=Leikur sem líkist Mahjongg +GenericName[it]=Gioco di tessere simile a Mahjongg +GenericName[ja]=上海マージャン牌ゲーム +GenericName[km]=ល្បែង​ក្បឿងដូច Mahjongg +GenericName[ko]=시센-쇼 마작과 ê°™ì€ íƒ€ì¼ ê²Œìž„ +GenericName[lt]=Mahjongg primenantis žaidimas +GenericName[lv]=Mahjongg lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñо плочки Ñлична на Mahjongg +GenericName[nb]=Mahjongg-aktig brikkespill +GenericName[nds]=Mahjongg-liek Speel +GenericName[ne]=माहजोङ जसà¥à¤¤à¥ˆ टायल खेल +GenericName[nl]=Mahjongg-achtig stenenspel +GenericName[nn]=Mahjongg-aktig brikkespel +GenericName[pl]=Gra typu Mahjonng +GenericName[pt]=Jogo de Padrões tipo Mahjongg +GenericName[pt_BR]=Jogo de Ladrilhos parecido com Mahjongg +GenericName[ru]=Маджонг +GenericName[se]=Mahjongg-lágan bihttáspeallu +GenericName[sk]=Hra typu Mahjongg +GenericName[sl]=Igra s ploÅ¡Äicami, podobna Mahjonggu +GenericName[sr]=Игра Ñа пољима налик на Mahjongg +GenericName[sr@Latn]=Igra sa poljima nalik na Mahjongg +GenericName[sv]=Mahjongg-liknande brickspel +GenericName[ta]=மாஹà¯à®œà¯‹à®™à¯-போனà¯à®± ஓட௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра з плитками подібна до Mahjongg +GenericName[zh_CN]=对对碰 +GenericName[zh_TW]=麻將牌éŠæˆ² +Terminal=false +Icon=kmahjongg +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kmahjongg/kmahjongg.h b/kmahjongg/kmahjongg.h new file mode 100644 index 00000000..a4b3ac3f --- /dev/null +++ b/kmahjongg/kmahjongg.h @@ -0,0 +1,119 @@ +/* + + $Id$ + + kmahjongg, the classic mahjongg game for KDE project + + Requires the Qt widget libraries, available at no cost at + http://www.troll.no + + Copyright (C) 1997 Mathias Mueller + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#ifndef _KMAHJONGG_H +#define _KMAHJONGG_H + +#include + +#include "KmTypes.h" +#include "Tileset.h" +#include "Background.h" +#include "BoardLayout.h" +#include "Preview.h" +#include "HighScore.h" +#include "boardwidget.h" + +class GameTimer; +class Editor; + +class KToggleAction; +class QLabel; + +/** + ... + @author Mathias +*/ +class KMahjongg : public KMainWindow +{ + Q_OBJECT + + public: + KMahjongg( QWidget* parent = 0, const char *name = 0); + ~KMahjongg(); + + public slots: + void startNewGame( int num = -1 ); + void showStatusText ( const QString& , long); + void showTileNumber( int iMaximum, int iCurrent, int iLeft ); + void demoModeChanged( bool bActive ); + void gameOver( unsigned short removed, unsigned short cheats); + void loadBoardLayout(const QString&); + void setDisplayedWidth(); + void newGame(); + void timerReset(); + + void tileSizeChanged(); + + +private slots: + void showSettings(); + + void startNewNumeric(); + void saveGame(); + void loadGame(); + void restartGame(); + void undo(); + void redo(); + void pause(); + void demoMode(); + void showMatchingTiles(); + void showHighscores(); + void slotBoardEditor(); + void openTheme(); + void saveTheme(); + void openLayout(); + void openBackground(); + void openTileset(); + +protected: + void setupKAction(); + void setupStatusBar(); + +private: + // number of seconds since the start of the game + unsigned long gameElapsedTime; + BoardWidget* bw; + + QLabel *gameNumLabel; + QLabel *tilesLeftLabel; + QLabel *statusLabel; + + GameTimer *gameTimer; + HighScore *theHighScores; + Preview *previewLoad; + Editor* boardEditor; + + bool bDemoModeActive; + + KToggleAction *showMatchingTilesAction, *pauseAction, *demoAction; + KAction *undoAction, *redoAction; + +}; + +#endif + diff --git a/kmahjongg/kmahjongg.kcfg b/kmahjongg/kmahjongg.kcfg new file mode 100644 index 00000000..427b4474 --- /dev/null +++ b/kmahjongg/kmahjongg.kcfg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + true + + + + true + + + + true + + + + false + + + diff --git a/kmahjongg/kmahjonggui.rc b/kmahjongg/kmahjonggui.rc new file mode 100644 index 00000000..e8c4bd5b --- /dev/null +++ b/kmahjongg/kmahjonggui.rc @@ -0,0 +1,102 @@ + + + + + &Game + + + + + + + + &Edit + + + &Move + + + &Settings + + + + +Main Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kmahjongg/main.cpp b/kmahjongg/main.cpp new file mode 100644 index 00000000..6564a842 --- /dev/null +++ b/kmahjongg/main.cpp @@ -0,0 +1,38 @@ +#include "kmahjongg.h" +#include "version.h" + +#include +#include +#include +#include + +static const char description[] = I18N_NOOP("Mahjongg for KDE"); + +int main( int argc, char** argv ) +{ + KAboutData aboutData( "kmahjongg", I18N_NOOP("KMahjongg"), + KMAHJONGG_VERSION, description, KAboutData::License_GPL, + "(c) 1997, Mathias Mueller"); + aboutData.addAuthor("Mathias Mueller", I18N_NOOP("Original Author"), "in5y158@public.uni-hamburg.de"); + aboutData.addAuthor("Albert Astals Cid", I18N_NOOP("Current maintainer"), "astals11@terra.es"); + aboutData.addAuthor("David Black", I18N_NOOP("Rewrite and Extension"), "david.black@lutris.com"); + aboutData.addAuthor("Michael Haertjens", I18N_NOOP("Solvable game generation\nbased on algorithm by Michael Meeks in GNOME mahjongg"), "mhaertjens@modusoperandi.com"); + aboutData.addAuthor("Osvaldo Stark", I18N_NOOP("Tile set contributor and web page maintainer"), "starko@dnet.it"); + aboutData.addCredit("Benjamin Meyer", I18N_NOOP("Code cleanup"), "ben+kmahjongg@meyerhome.net"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + KImageIO::registerFormats(); + + if (a.isRestored()) + RESTORE(KMahjongg) + else { + KMahjongg *app = new KMahjongg; + a.setMainWidget(app); + app->show(); + } + return a.exec(); +} + diff --git a/kmahjongg/pics/Makefile.am b/kmahjongg/pics/Makefile.am new file mode 100644 index 00000000..1b879ee0 --- /dev/null +++ b/kmahjongg/pics/Makefile.am @@ -0,0 +1,19 @@ +theme_DATA = default.theme pirates.theme +themedir = $(kde_datadir)/kmahjongg/pics + +pics_DATA = kmahjongg.png kmahjongg_bgnd.png splash.png newnum.xpm +picsdir = $(kde_datadir)/kmahjongg/pics + +layout_DATA = default.layout cross.layout pirates.layout \ + pyramid.layout stax.layout test.layout test2.layout \ + tower.layout triangle.layout +layoutdir = $(kde_datadir)/kmahjongg/pics + +bgnd_DATA = default.bgnd haze.bgnd pirates.bgnd slate.bgnd \ + wood.bgnd +bgnddir = $(kde_datadir)/kmahjongg/pics + +tileset_DATA = default.tileset pirates.tileset runes.tileset traditional.tileset +tilesetdir = $(kde_datadir)/kmahjongg/pics + +EXTRA_DIST = $(pics_DATA) $(layout_DATA) $(bgnd_DATA) $(tileset_DATA) $(theme_DATA) diff --git a/kmahjongg/pics/cross.layout b/kmahjongg/pics/cross.layout new file mode 100644 index 00000000..39c58f1e --- /dev/null +++ b/kmahjongg/pics/cross.layout @@ -0,0 +1,81 @@ +kmahjongg-layout-v1.0 +12....12121212121212121212....12 +4312..43434343434343434343..1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....43124312..4343..12431243.... +......43124312121212431243...... +........4312434343431243........ +........1243121212124312........ +......12431243434343124312...... +....12431243..1212..43124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243..12121212121212121212..4312 +43....43434343434343434343....43 +12......1212121212121212......12 +4312....4343434343434343....1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....431243....4343....431243.... +......4312....1212....1243...... +........43....4343....43........ +........12....1212....12........ +......1243....4343....4312...... +....124312....1212....124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243....1212121212121212....4312 +43......4343434343434343......43 +12........121212121212........12 +4312......434343434343......1243 +124312........1212........124312 +43124312......4343......12431243 +..43124312....1212....12431243.. +....431243....4343....431243.... +......4312....1212....1243...... +........43....4343....43........ +........12....1212....12........ +......1243....4343....4312...... +....124312....1212....124312.... +..12431243....4343....43124312.. +12431243......1212......43124312 +431243........4343........431243 +1243......121212121212......4312 +43........434343434343........43 +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/default.bgnd b/kmahjongg/pics/default.bgnd new file mode 100644 index 0000000000000000000000000000000000000000..2f0e4612443117d82fde6b6f997a97cb0759f69d GIT binary patch literal 41434 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 Gd;dRtrnH~{ literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/default.layout b/kmahjongg/pics/default.layout new file mode 100644 index 00000000..f72ed794 --- /dev/null +++ b/kmahjongg/pics/default.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +...121212121212121212121212..... +...434343434343434343434343..... +.......1212121212121212......... +.......4343434343434343......... +.....12121212121212121212....... +.....43434343434343434343....... +...121212121212121212121212..... +.124343434343434343434343431212. +.431212121212121212121212124343. +...434343434343434343434343..... +.....12121212121212121212....... +.....43434343434343434343....... +.......1212121212121212......... +.......4343434343434343......... +...121212121212121212121212..... +...434343434343434343434343..... +# Level 1 ------------------------- +................................ +................................ +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +.........121212121212........... +.........434343434343........... +................................ +................................ +# Level 2 ------------------------- +................................ +................................ +................................ +................................ +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +...........12121212............. +...........43434343............. +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +.............1212............... +.............4343............... +.............1212............... +.............4343............... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +................................ +..............12................ +..............43................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/default.theme b/kmahjongg/pics/default.theme new file mode 100644 index 00000000..9ba994bc --- /dev/null +++ b/kmahjongg/pics/default.theme @@ -0,0 +1,4 @@ +kmahjongg-theme-v1.0 +:default.tileset +:default.bgnd +:default.layout diff --git a/kmahjongg/pics/default.tileset b/kmahjongg/pics/default.tileset new file mode 100644 index 0000000000000000000000000000000000000000..b35cda6c35b7e2dbe8630f3485e490666d99bdb1 GIT binary patch literal 45748 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 literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/kmahjongg.png b/kmahjongg/pics/kmahjongg.png new file mode 100644 index 0000000000000000000000000000000000000000..5e0ac37cb8289a25c379c854846fad4d5a2b3972 GIT binary patch literal 9711 zcmY*;0Me{qdXGot=HkYxmjNeRlVmC+Uf?KK%`@8vp=+-r(VbrvLy6__}gX zlU-BvI17ZX8*(=-BP{^nYZ~o^6UBAk_{n2)-Rl+*4T!!jfM_p(%kodR3|Ru=pgt^tAWzCQeNk}}dx{7+qjK>RYYQnJcY%JTdo z9)1$8ArM!e|4H1Jl#`U>7vsOShu_Z$;tb}Ok(6~2T?%8C1_1a01`o8%Lke~ZlVg82 zrzV_le~i^(rEyx6TipFu;%U$^*cfg=ol`O*_w324ukh*3`ZA^US*fY3Q>@p&^U_wk zbDOP>4*RW>t6hiv1I!a^Y!{N(PSX4{4G8;VS!-d}B_FvAKN&V?*ymrB zMxsmCPR^Rs>x9Szww^r8yb8L!tNrOIrc5rstd-gGPh{ouM&<#YT}2^aDI3~3{Ymm= z%9<7C>s9JNn_`uYQy%Ux^uG3_dr&mO4t~mzho;SXq)cVkwqZ6|APE-7#swluvtbMa z8{e4R%MEeOboZBQ$t2!gT@unXoLcO@ZZ1c;VAvPK6Qcq!qAm!PI$|5A=S4iD{f2q~ zuP^DD2@SGs885Gjk6t1$^I%)goCcwK2p-=h9Y8aa zZ9-CaZU*p9?BWQb6uMhLRFuWD1d<>HM<3EU=lr$>c-t)=-ZM207FUq<-2`~T1ip|P z1~FD9)2#NhQ1I#&ThB@H(Z~(QU&V$olr^k*MmPQmi>Xi5*y>ISaT^P_n02feft%1F z<>DOC*$p~&ubJLT4EXEc$mh?|+P}&bD2KOH`%KgvwSIQ~s{hP#ONwMiyUna|IakiR z637Qic?~HE>@Rr7cD9C?*I~NHlr=gm-k0}hpECz>8lKoK)E=T; zF*?-S`5WAcP*=Eelwf;i^0_U1fVpKxK^YCqxEZ0h6VL*_(*`SiY%KJ)rw*)8$Y}89ihiIvKV!E zvRn=&eu(i6lO|&`8HCGF81%8=L0Q99?%n-1S26Id0E8h8`+2gMfvX*P=ndx*a%m1( zxy?78RncrpIcLr=eiKGt66|CaM>3U&7Msa~wz8;-PsTL*T~>!p?3Xl;=RedwaPbx?aC`oN#q@!_I5M{@pXB%wUAbMSEpu zr>5{Z{scRyB2Jqu`MIi7aV9GQ;+B|Hx7`3b3v(+Z=ko4kcD(l7+~b9?~s`H z1ao+Fj015Z|Jf0qunemZw|3j1%HVSRw)Cxa6z&RVn-g=i`(Wg$tJCM}NnoL(;G+Py z#IMpnuJw*kmiEL-kG(%TJEw&h)abCs8`PBExoH2` z*{N+=sXt5A5?fbtjid(Efb*4|lU(v+8Szw`U*Pk6P)wl;k#Nm%#ng;GVaxt+4Jr|U zv-CEBJ_a)Sah_+Z=51-mc<5b}ww}%&m@EVyN9?Lj#+_liQ2;c5>`ljuwNX zRq(k`X*(aew24!8_U1*U7kjh(kVejUSpx-0Wb+ck@3bH`*Ymn=?FaJ9I*AH`~ub-&4Nh!c*Y3 z?o#wLsz086#S4Ue+#PS32$4Pzb6SVHyJmI2C?7(fxE*|mMMTLit`_peMeL3CsC3?^ zqCY{DDl5V2C_(3MgQ6pKXD2v3Pm-PX+-Q=g3?TCsGzZ$~aQE0$qjHKfU8){xxF0W* zU1L^fS6-*1&E45maTF0W6C3$s4}qC!$xYSDjN$#}@Dp+%x!~P44{h`4ehL<&8VlkW zlvpT%BAi{gefj9188;dxQ2DaUMgtDJ58Lvc95uk6Z8G=_-CBm-47nGm&|COOP>>KX z$x`m?W-xFeUn&^*6Et*nGIK}VAPi`?@ycAQmXA*6+DUFE`gf>N_!jMDLfVT*zYykF zaPi<0Hr#~F5o^{AYq1Z}w~zn^xI|1Zr{m)8Rv*&WlHfJ1ZD9?3qqyv4yzg&Et6-XK zqc^sXx%3G*79Lv)0JK2O{e;iHwUu$Hs#D?+e*=6Zlw>l?mL;u4(mj}myLE>?N}?$m z$5gn4S^gXfnTHO(6#oGViOP}{O$dDHmMyo#@o3%}*Np6*{ z*GCSQ-L-S*?!H>>aGmU|*jvtuZHSty{tL}EIK0ZKFEa@#mi$+-tiuin`ccXaR~adr zJ8D-F3(c0q>PVp#NM!AHxu1$=YA*((Vz!@$LIN&Be6b#w!c)!C(18VYX7G)HA0IOb z17U;`=FP9uIGIZB19h|RM@64%{zpBd>RDNCp<<#mV)s!TE~7i(&EzqZYJ$GF1^#D{ z+P+%&(DDXZ*htyigXSJ2HlwQvI+Pu@31ViOcc4z9Pu}b;KhA%B^%EXuwujrYi^_wj z;m=m~ej-YnDv+ka&4e(l_`y%uM;@-dnpOf4dcNqH7`#jed3HQWGg4@mSk}5+*ic&# zd(059NZG@`|$WQ)I?dA<~YmfZfs> z89uzDo@AF?ENza(leNkR|4$@SSHmx&O`nipP^tV4JjrQ1CBG^a0zjXz@O`(^`QJE} zHDp>FprIyj%x|fEiL{C&x+&-8AcVJreREy{?z-G)pVK+o z7y_K+``MJ-E*NXf^}hE?l|d`mI9&giGB3IHk#`mJbR?X1ipH0n+l#zVNyGpJ5c-iv zzp>95)kPNFUw#seSQB8AIBh&cUAQ*hwSw;j{pk{#9fD<)$t9=_qK+In&S=|zjo*Wh zb9nK)g*47I)m+c6L1O8}-=<`Gg;-;r_jd~GoLChB3wKBt2XZ%;Bm*zom<><94hiszmdCr?FD-cv0p{9$~TX^>CJ?UgnU^?n-v~ zF972eOo8c75YB`HJoX8pg|QLK9MFQ4jok53-I44svXM5B(-E&^zuWK7#SeFBXfIE( z1|U*zr9>sJhi-61U0^~3CtZt{hDIvevSiThc46_OjiuN!SWRyp>h>hjA)rv+9_4l$ z?D}>d8AwP#3-?Wko8p91*HzF^e6gCF;llYQQThqMyT7Z)%dwLOuWE`L(BD@y#(a!Y zPb9&l3uyp1x8kE{kWUj|U8#l_4fH#nh-0oXM7Jv1{mD+<`r2b|YXHyxTC8eN1gdkR z`{5~RjqviJi?*A1J)tInt%^2(AD*{qQg2^l?`N~fzfh1|;6NVoFkiJj4QVn7UpnsB zlFtbUD$9CvPB89br0P}ukk-GtKNZM3TCU%S7mg2pN*-<&Vo5NeDZswA3SYCPp`A&4 ztH#GrEXQ#`uJ##fiz`8PQllzI94vk)-7grFT($eAk!9Iss+>NH%jGNy?(a(Hp?X5e z-u@~7TPIq-b+qza>6>Da-LiuZr58Z~vlgN`8Y8ESPjMuEA^_u5&Iv%zt;-sNu7N*+ z@|8_M>MrH;s|S48SGU6mMy$+USJWDo8j)rf*Y4sx$&d6WXmQo&ipxtBEbO6^)s9dyII;* zB%s@}kSdg@yeYjfzcWRRg4$WuIAeD#yJGgC8QrZkrLFYv%}F>Jy`2eLpu6;vCzn>S z@$m9d%MJUT@n0;amz>-`%E#Tip8{%bWA;@SMsR+C$qlfW7b=_w#=pmjvX$wbJ5Sq_ zpXQk7%oIz29g6L_q5N((i_IBo&U6waE~-ZrPF|JJ?Sfd^N?Ngdx`%F)iDv@~6+LgG zBY6k1^j-@*iIl)R{Tt2<7KN^M6wDSUJsuosWxh+Q7KYArLDy2Cd}2OmZm6VEdRj!y z7(}2{sv&Gf1^ySwW?4M&UY$uSJ*0;;Sn^`va09811zIqHoiQ{VUt~dE6P3TN zlj0+`MzJ7w->@u${ms;&)3dWc>tb9+Z02(M-?8uk5K(U$E0LNVr`S7@(!BKXtG#li ziSNBk3#HDcG{lzIqJKuI(__)573D}x_617JpfZr`ZZs34!Lgm@EED`H36G7B8flrv zaX=(UwmQF+H0`Ao=$4z3UrMePW)B;pOFkTPB2qw0KZvaGWs$ZK@5Rldj`6y_7HOqz znH0E#u$>v|_}O0juAG-YPWna^pPg$p=l>*5KYib>u1dMzy6iUh^Wu?X(CoAR{Qu;5 zSNCTtTdO4ZuRfery`|S6;M5);|0VRbCli0qCWo>Af7ih^cLz@J4z{=SnPg94KK=Y8 zj&-&;nfDQD?GK(eZEP*M({R0iUt|lVybrh{ixRsio+Qd`N)hvFza)mO>n*lA+qGWF0Rl5?*^>wXX=*((g=2j~|{-yI5x>Yd#P_vYnn{KT6 zA;)0$*V5Ni-giW*v5^E&WZZ>}n5CrO0qs@Asn?a2;{v(@4RL-kc-a*aBR*Lul)Q*B z`Ap&*|1$O;{3#hloI#~luKt)NUm&J}n|V?Oy#Esnl|ZX~W4_Z)azK?^R}W`BNnzYU z)FlF?LZ82c^3q$7aj7B&Iz`s1QzG`y#J}JD&@aY>_O%@htNX$p4xxl<^;P%?0@AthZ}LsUX%cWTzEM=qC60(&eA2)UlJAa(YkReV;m;?rjbQgz_&3q@NY0O!n-)>7dMFoepO$ z&^T^M7Ud=l_M2FF@DehAH<{r|kb?dqbyb&k!COWMuUY0UjGzSO9pt6N-Rp=4 z=&Aihhmv4PKYRJ5z4N&}JX0rJ??R}}BLIr40re$=J@(4lmMK|Fq+kh{z0#aujC#Oc z@=mP*iuS;t8+=DC)6l|kb|DNus1JKS-mfoJX5*+E*Qa&}mzn`C%A~6>5kW$Jydheg z3Ezk8QPQJS7Y)=PUDpVCH-PhU$Xo9y$mI^XbuC2=kB7X=8wD0Q|0ZV&N;MsfY6gwY zFsX*0K#DGK7==$xQxE}|B9k^9F*>s(j5h=s>Gr)u>KPy>iy<^rAg*IlJZO^_gX!}~ zrnJufc3D$G-D`v7a#NF%z8NdP{gcI?gR$g0t_B^gW`-)kG-h#Ait5u{=0&B*P57OZ zQm0XN(+H+t#I?utSCC^I-e-p=!EJ;h6fwEQ;a1E=Zs8ZvDM7YK^_#N3nN-^A-X|I8 z2a^@tFa}DUzr#1YUg>>*-zhT83QH@R_`rPdT?)UA04-*r9p7Cx)1q| z6eOnRUf+dWGRX0cs?Hs^fF24nRt~E18?wE9Z&OI*;R&i3A+dYC(~iFd+Q^nwTR6_% zX1>5adnrw5jq%Bau!GxqkyMYq0zpi!BfQP?r{+rF*V}q@>kr*zb0``Ef!Y?5&t+13 zt(yDmsjL+DPw~S1K=MpVebRKdgYic^bkHl-aVCyCDe%(t(|PzeDEFuo;U=^qT?xJ~ z&HZ>UTnKnZkPD|qnpg>Wj^jVZNbB)&z-&b*1qy+ul(HUu;(0&8cWn-g;htrS@U%to zg_e{CUBVVm!<(mm-=1i(Xw!-NTVcUJ-Pa!K)tVtjV{lvo?)Br=QYP97Beb1iJa0K^Xmsuc6$6YdXL>`kTXKRk2B;Yk> zpR09WF#;b=IZ5|gI!RksGhk6&YLbu3rGe(@-Tg|JGD~>kSE3&F zHGv}>VnCHOdADGY98`e5wdokrw#Q;S{v^EU*Cmb?8Q9>Adw_{WH`XB5VAS`5PoF5g z+oPcU>SF+5*KiP;iP3V9v>#xjtlVv)a=iXLQUM{yWZotxZ_BqQq6SIA%cc%k*}Z`^ zpJqR{3W7uJ1Aw={XHojIf4Wj%25p=R@H!%@iuTZ`uxJEb0#SC)2lnx$mEH6V={^g# z2(Hqt64V|syT{H1ah-4V_~lIF@F-Q2p=^iWN@CX)6j8rM02nMGv13Kk8=M^q^`k_tD3oCKupM z_66U@ey^BLU%yOFlu&9Z(M=1;IAK_$@I^hs8HjItg`E<@q#vTXr0d0lM8$k+y(hxC z+!0~8_tG;ft??ka7JY(sCc83a5^`!sZpi=AYqFv%_kBLw@*i-N^>sjR0d=+6{5V%V z>FB)Lz4c?n3KfTJ;`1-Kt|gU-{`2}+w2wFsKcUv|HnQGHcUG-SHTDU~$x;%2!9oAq z{(Z+0{rWXI`}+@oocw4CnBmF^#p}+hOB%!ZDK4qURI|v0F07^@3TGqqg4#8Dy!Bgb ziEHq{n@<;A>H|&#f}@XOU-3kT#Bk!31cB?BdMNkqE1}tCmvSwmYK<_)KDy&k5YYI@ z(t7_BHq4jwjlg@V9$}ogi`!`UTouwsfxbXi?B1%lbZ$gP!75r?_kRB^qMEs&y}&=< zVPX#HeRYAbBVW5}?l~6y_~UPI^m5N+n2k-`M_CT_{UgN>Bpxd6>w?mzUIQU2!ng+6 zpLPwUg^$`-!qfinFr?G5yYW0eTBxdL_tc4M4!IggZ zdb-KOF;JKQ<56d<39FBqJq2ow?Y9svEuZ&-r&vO)Fq_ZUM|7$UtrkWe z%4^m-=#i>;^~(%_{QAg7XOaVHPmGDD@9}t|(axGOcr~s2ERYjyAncEe0=7ScJG^Rd zww$dYNr{+QB-Dx>i(_wKA}%lSE_NDt^i?FJ-iHFeQB8Io=6JUAV>T=7@NoNTL*t+O z6(j19@SU}%wYBb?b#UaKgN6S?_*h~t)+}xfbSTZ^}HxBh)(7U zMk^EHG)p;53Uo`d{2I*|v+-#24c=2TxP_s!!-foEMI~7jy;)RYm6&v{zd%ct0-_E5 zT%6Z2@_Y?;2|O}{n-JfDiwp7;XDVB5dB9cN+E|bDj%~N7+10?a{Ee01y3~nYmVe@v za%7tYa)H2#eT4JTzs-~2x!MLi^SR($P`kpaa^j2ZqyFtAUn=Yx=n5&G&zAW@SUc4j zQXt_@g2X%z=LMm(^a!<2_g#$MKBan_A;l z;tQvEvu)5L_@92=ab+^2WeKp>{w8fO1W18Cos~`8;(YS?JZtDMZgE*%e2W*eR3gU* zJQ^uYt^T3KSZ*$?3-nu#o5c=6zuR%jZWve#PFw%MYufD5wvMN_fGJfefZeTaR@NjT zgA7Q@P~yZUh43ke8I?o0De8G-=jw`qM6fYtVU@YJPszA71W)6z_jQ1r`PA!vgAXk= zb2c*T6qsB!2A7AR@0Bby<=A(|@|GdHE4SC<-$^c|y%y7BcJ=wel-na93?vu_{c%y- zyd(G}`g0hffT)geexK=aB5m?DjR8Fm1R@HP5N=Ll${;ZX#$vvuikt%mn@%y6F8=3X z_T&*;0Qxd_9~$woNcU)k_3)PH{W((9GE_Wcx9+RzJW@@QO+8JrQ82mD9;XY82?UE| zn1qPDW7GOzRm!lC&MUbm@t0XQxLiEdn9F+qH&ggPp>3k=oqAC-x=LQ&3oa|_9$QE4&HxX_g3ZHC;?-&)1C}6~Fsg$V+?+Tw!99x<= z-A?8e{2;lE()0tm`l1DzotVMt67j1rDla)je~SDbC--Isezk%_7s1r2q0&_7iuj<- zT)MMONK-ddob8M7&ic(yc}^AngJlM8DY-nz8JsKCqz`F|VU%}=oCw?RuZl`P-?9~^ zXXDc4E;a3&ZN1odHzf^~;@=ThQGwGkw>O=v61rZ!loj!v^ttyV*BYXo4?|y{>~~Vf zNiglw{-IQm5~F)NL(eHh?Nb>o+>MK4Ox@(jkf+%PqE$%M?nQ>(0r@?`I(}%{7RbF` z2a+A7N7ZC_MkA8M_5HJ_(&}1fWHu5bEfV`Sqz~bJS5GJ9hD~z%EO&F!Pd^7ozOC90 z4gd;DPVZ_F-1myH)CYrKxYtBJ2EjFYq)!B%5B!Rd^D$;~9=n;N*?{~+ zr`NK_P_B5dp17!~Ur=6Iu~yh3Sxyh1ZD!bc$AEZ9P^D@Dze65b@RqOO3=;7@f^P6E z#$C#8qGB)H#8Yd0K{9X-5Yf&FX(H^wBbvMDPz?FcH2OIXHU{*)=JM6t)UB0sMx(q6 z*ngDj#YeGdd5HdCw(ZoDwwaRF=dYf%Bx?VZg=u7 z=12&8Fp-l?E5|hXf+OTm&G>_+uOASWqCn{_$k-&K{M`p5?&{?FSt+&go`dPcbvDEF zf4Klv-VDR#KdoC!D#PPHQGo9G;v+G278`6BZo9;BGai8#mGtkarMT_yTe$l}XTEU=?gZ3@f+dk&NI^C17%!-yjFEJaC>r!70rFe^GN05<7?A=pyw50M}h z_xz%r1Dq5jmucX3B6ffpPUqH(HimDT)1+(L+I+UJL`kV(4)Q!~nqiZc7N(1U&@6&q z*bBw6ROBJW^0@spmgeiJ-n+dM*hcc`d_H#>wooSI&TB+9sBtTvG82>R%a6dgZ|v(U&dBLTY&958!TA^ zTkqQz6xKDZXnh^jDPmHfK}LG(RxD zkpZ`wXVHJ8;?t#tS<)?JzC>|tyfEDyF8%urer`7)-P@4CJUK-2l4g~o~l@+sW!JT@a#j6)-enXJ@yAWF}cMc{X~;R=MFr0^%sqPHgmvc zg3Qg;d$}Q@@veRp3WTB^xk7uJJD97&P-;9YqV%S00Kiv0HrE>o+_nj+MH@>Z%KTP3TtLq3T|(|ItvUV z1o_r>AeKgI-Dc@gft^twhz#)J2K%Kd!jaEE#`^lAA1S UD__FDUy}d^y2cL>+K#dR2b<>{1^@s6 literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/kmahjongg_bgnd.png b/kmahjongg/pics/kmahjongg_bgnd.png new file mode 100644 index 0000000000000000000000000000000000000000..14d9003376abc59cd0a37fc039cecf6a621ef8ef GIT binary patch literal 27876 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 TD19wJ00000NkvXXu0mjf&SuXt literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/newnum.xpm b/kmahjongg/pics/newnum.xpm new file mode 100644 index 00000000..bc0b96ee --- /dev/null +++ b/kmahjongg/pics/newnum.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char*numnew[]={ +"22 22 5 1", +"c c #c0c0c0", +"# c #000000", +". c None", +"b c #dcdcdc", +"a c #ffffff", +"......................", +"......................", +"......................", +"......................", +"......#######.........", +"......#aaaabb#........", +"......#aaaacab#.......", +"......#aaaacaab#......", +"......#aaaac####......", +"......#aaaaaccc#......", +"......#aaaaaaaa#......", +"......#a#a##a#a#......", +"......#a#aa#a#a#......", +"......#a#a##a#a#......", +"......#a#a##a#a#......", +"......#a#a##a#a#......", +"......#aaaaaaaa#......", +"......##########......", +"......................", +"......................", +"......................", +"......................"}; diff --git a/kmahjongg/pics/pirates.bgnd b/kmahjongg/pics/pirates.bgnd new file mode 100644 index 0000000000000000000000000000000000000000..2e96211ffd83c0f1f4cbb22997ecedf2262f9c53 GIT binary patch literal 286354 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kmines/kzoommainwindow.cpp b/kmines/kzoommainwindow.cpp new file mode 100644 index 00000000..115d5175 --- /dev/null +++ b/kmines/kzoommainwindow.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include +#include +#include +#include + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, const char *name) + : KMainWindow(0, name), _zoomStep(step), _minZoom(min), _maxZoom(max) +{ + installEventFilter(this); + + _zoomInAction = KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + _zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + _menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + _menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast(tlw->qt_cast("KZoomMainWindow")); + Q_ASSERT(zm); + zm->_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + +void KZoomMainWindow::widgetDestroyed() +{ + _widgets.remove(static_cast(sender())); +} + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + +void KZoomMainWindow::setZoom(uint zoom) +{ + _zoom = zoom; + writeZoomSetting(_zoom); + QPtrListIterator it(_widgets); + for (; it.current(); ++it) + (*it)->adjustSize();; + _zoomOutAction->setEnabled( _zoom>_minZoom ); + _zoomInAction->setEnabled( _zoom<_maxZoom ); +} + +void KZoomMainWindow::zoomIn() +{ + setZoom(_zoom + _zoomStep); +} + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( _zoom>=_zoomStep ); + setZoom(_zoom - _zoomStep); +} + +void KZoomMainWindow::toggleMenubar() +{ + if ( _menu->isChecked() ) menuBar()->show(); + else menuBar()->hide(); +} + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(_menu->isChecked()); + return KMainWindow::queryExit(); +} diff --git a/kmines/kzoommainwindow.h b/kmines/kzoommainwindow.h new file mode 100644 index 00000000..da8ec96c --- /dev/null +++ b/kmines/kzoommainwindow.h @@ -0,0 +1,126 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + +#include + +class KToggleAction; + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return _zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint _zoom, _zoomStep, _minZoom, _maxZoom; + QPtrList _widgets; + KAction *_zoomInAction, *_zoomOutAction; + KToggleAction *_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + +#endif diff --git a/kmines/main.cpp b/kmines/main.cpp new file mode 100644 index 00000000..52620887 --- /dev/null +++ b/kmines/main.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "main.h" +#include "main.moc" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "status.h" +#include "highscores.h" +#include "version.h" +#include "dialogs.h" + +const MainWidget::KeyData MainWidget::KEY_DATA[NB_KEYS] = { +{I18N_NOOP("Move Up"), "keyboard_moveup", Key_Up, SLOT(moveUp())}, +{I18N_NOOP("Move Down"), "keyboard_movedown", Key_Down, SLOT(moveDown())}, +{I18N_NOOP("Move Right"), "keyboard_moveright", Key_Right, SLOT(moveRight())}, +{I18N_NOOP("Move Left"), "keyboard_moveleft", Key_Left, SLOT(moveLeft())}, +{I18N_NOOP("Move at Left Edge"), "keyboard_leftedge", Key_Home, SLOT(moveLeftEdge())}, +{I18N_NOOP("Move at Right Edge"), "keyboard_rightedge", Key_End, SLOT(moveRightEdge())}, +{I18N_NOOP("Move at Top Edge"), "keyboard_topedge", Key_PageUp, SLOT(moveTop())}, +{I18N_NOOP("Move at Bottom Edge"), "keyboard_bottomedge", Key_PageDown, SLOT(moveBottom())}, +{I18N_NOOP("Reveal Mine"), "keyboard_revealmine", Key_Space, SLOT(reveal())}, +{I18N_NOOP("Mark Mine"), "keyboard_markmine", Key_W, SLOT(mark())}, +{I18N_NOOP("Automatic Reveal"), "keyboard_autoreveal", Key_Return, SLOT(autoReveal())} +}; + + +MainWidget::MainWidget() + : KZoomMainWindow(4, 100, 1, "kmines") +{ + KNotifyClient::startDaemon(); + + _status = new Status(this); + connect(_status, SIGNAL(gameStateChangedSignal(KMines::GameState)), + SLOT(gameStateChanged(KMines::GameState))); + connect(_status, SIGNAL(pause()), SLOT(pause())); + + // Game & Popup + KStdGameAction::gameNew(_status, SLOT(restartGame()), actionCollection()); + _pause = KStdGameAction::pause(_status, SLOT(pauseGame()), + actionCollection()); + KStdGameAction::highscores(this, SLOT(showHighscores()), + actionCollection()); + KStdGameAction::quit(qApp, SLOT(quit()), actionCollection()); + + // keyboard + _keybCollection = new KActionCollection(this); + for (uint i=0; isetItems(list); + connect(_levels, SIGNAL(activated(int)), _status, SLOT(newGame(int))); + + // Adviser + _advise = + KStdGameAction::hint(_status, SLOT(advise()), actionCollection()); + _solve = KStdGameAction::solve(_status, SLOT(solve()), actionCollection()); + (void)new KAction(i18n("Solving Rate..."), 0, _status, SLOT(solveRate()), + actionCollection(), "solve_rate"); + + // Log + (void)new KAction(KGuiItem(i18n("View Log"), "viewmag"), 0, + _status, SLOT(viewLog()), + actionCollection(), "log_view"); + (void)new KAction(KGuiItem(i18n("Replay Log"), "player_play"), + 0, _status, SLOT(replayLog()), + actionCollection(), "log_replay"); + (void)new KAction(KGuiItem(i18n("Save Log..."), "filesave"), 0, + _status, SLOT(saveLog()), + actionCollection(), "log_save"); + (void)new KAction(KGuiItem(i18n("Load Log..."), "fileopen"), 0, + _status, SLOT(loadLog()), + actionCollection(), "log_load"); + + setupGUI( KMainWindow::Save | Create ); + readSettings(); + setCentralWidget(_status); + init("popup"); + addWidget(_status->field()); +} + +bool MainWidget::queryExit() +{ + _status->checkBlackMark(); + return KZoomMainWindow::queryExit(); +} + +void MainWidget::readSettings() +{ + settingsChanged(); + Level::Type type = (Level::Type) Settings::level(); + _levels->setCurrentItem(type); + _status->newGame(type); +} + +void MainWidget::showHighscores() +{ + KExtHighscore::show(this); +} + +void MainWidget::focusOutEvent(QFocusEvent *e) +{ + if ( Settings::pauseFocus() && e->reason()==QFocusEvent::ActiveWindow + && _status->isPlaying() ) pause(); + KMainWindow::focusOutEvent(e); +} + +void MainWidget::configureSettings() +{ + if ( KConfigDialog::showDialog("settings") ) return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self()); + GameConfig *gc = new GameConfig; + dialog->addPage(gc, i18n("Game"), "package_system"); + dialog->addPage(new AppearanceConfig, i18n("Appearance"), "style"); + CustomConfig *cc = new CustomConfig; + dialog->addPage(cc, i18n("Custom Game"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), SLOT(settingsChanged())); + dialog->show(); + cc->init(); + gc->init(); +} + +void MainWidget::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void MainWidget::settingsChanged() +{ + bool enabled = Settings::keyboardGame(); + QValueList list = _keybCollection->actions(); + QValueList::Iterator it; + for (it = list.begin(); it!=list.end(); ++it) + (*it)->setEnabled(enabled); + _status->settingsChanged(); +} + +void MainWidget::configureKeys() +{ + KKeyDialog d(true, this); + d.insert(_keybCollection, i18n("Keyboard game")); + d.insert(actionCollection(), i18n("General")); + d.configure(); +} + +void MainWidget::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +void MainWidget::gameStateChanged(KMines::GameState state) +{ + stateChanged(KMines::STATES[state]); + if ( state==Playing ) setFocus(); +} + +void MainWidget::pause() +{ + _pause->activate(); +} + +void MainWidget::writeZoomSetting(uint zoom) +{ + Settings::setCaseSize(zoom); + Settings::writeConfig(); +} + +uint MainWidget::readZoomSetting() const +{ + return Settings::caseSize(); +} + +void MainWidget::writeMenubarVisibleSetting(bool visible) +{ + Settings::setMenubarVisible(visible); + Settings::writeConfig(); +} + +bool MainWidget::menubarVisibleSetting() const +{ + return Settings::menubarVisible(); +} + +//---------------------------------------------------------------------------- +static const char *DESCRIPTION + = I18N_NOOP("KMines is a classic mine sweeper game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kmines"); + + KAboutData aboutData("kmines", I18N_NOOP("KMines"), LONG_VERSION, + DESCRIPTION, KAboutData::License_GPL, + COPYLEFT, 0, HOMEPAGE); + aboutData.addAuthor("Nicolas Hadacek", 0, EMAIL); + aboutData.addCredit("Andreas Zehender", I18N_NOOP("Smiley pixmaps")); + aboutData.addCredit("Mikhail Kourinny", I18N_NOOP("Solver/Adviser")); + aboutData.addCredit("Thomas Capricelli", I18N_NOOP("Magic reveal mode")); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + KExtHighscore::ExtManager manager; + + if ( a.isRestored() ) RESTORE(MainWidget) + else { + MainWidget *mw = new MainWidget; + mw->show(); + } + return a.exec(); +} diff --git a/kmines/main.h b/kmines/main.h new file mode 100644 index 00000000..21ec28b1 --- /dev/null +++ b/kmines/main.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + +#include "kzoommainwindow.h" + +#include "defines.h" + +class KAction; +class KToggleAction; +class KSelectAction; +class Status; + +class MainWidget : public KZoomMainWindow, public KMines +{ + Q_OBJECT + public: + MainWidget(); + + private slots: + void configureKeys(); + void configureSettings(); + void configureNotifications(); + void configureHighscores(); + void gameStateChanged(KMines::GameState); + void showHighscores(); + void settingsChanged(); + void pause(); + + protected: + virtual void focusOutEvent(QFocusEvent *); + virtual bool queryExit(); + + private: + Status *_status; + KToggleAction *_pause; + KSelectAction *_levels; + KAction *_advise, *_solve; + KActionCollection *_keybCollection; + + struct KeyData { + const char *label, *name; + Qt::Key keycode; + const char *slot; + }; + enum Key { NB_KEYS = 11 }; + static const KeyData KEY_DATA[NB_KEYS]; + + void readSettings(); + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; +}; + +#endif diff --git a/kmines/settings.kcfgc b/kmines/settings.kcfgc new file mode 100644 index 00000000..c863f41a --- /dev/null +++ b/kmines/settings.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kmines.kcfg +IncludeFiles=defines.h +ClassName=Settings +Singleton=true +Mutators=level,MenubarVisible,CaseSize +CustomAdditions=true diff --git a/kmines/settings_addons.h b/kmines/settings_addons.h new file mode 100644 index 00000000..59be1ac1 --- /dev/null +++ b/kmines/settings_addons.h @@ -0,0 +1,5 @@ +public: + static Level customLevel() + { + return Level( self()->mCustomWidth, self()->mCustomHeight, self()->mCustomMines ); + } diff --git a/kmines/solver/Makefile.am b/kmines/solver/Makefile.am new file mode 100644 index 00000000..8a6696a7 --- /dev/null +++ b/kmines/solver/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/kmines/generic -I$(top_srcdir)/kmines -I$(top_srcdir)/libkdegames $(all_includes) + +noinst_LTLIBRARIES = libsolver.la +noinst_HEADERS = bfield.h solver.h headerP.h adviseFast.h adviseFull.h +libsolver_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined +libsolver_la_SOURCES = bfield.cpp solver.cpp advFastRules.cpp adviseFast.cpp \ + adviseFull.cpp +METASOURCES = solver.moc + +check_PROGRAMS = test testFast testSolve testRate +test_SOURCES = test.cpp +test_LDADD = ./libsolver.la $(LIB_KDECORE) +testFast_SOURCES = testFast.cpp +testFast_LDADD = ./libsolver.la $(LIB_KDECORE) +testSolve_SOURCES = testSolve.cpp +testSolve_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) +testRate_SOURCES = testRate.cpp +testRate_LDADD = ./libsolver.la $(LIB_KDECORE) $(LIB_KDEUI) diff --git a/kmines/solver/advFastRules.cpp b/kmines/solver/advFastRules.cpp new file mode 100644 index 00000000..79c42bba --- /dev/null +++ b/kmines/solver/advFastRules.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include "adviseFast.h" + +using std::set; + +AdviseFast::RuleSet::RuleSet(FactSet *f) : + facts(f) +{ + FactSet::iterator i; + for(i=facts->begin(); i!=facts->end(); ++i) + addGeneral(i->first); +} + +AdviseFast::RuleSet::~RuleSet(){ +} + +void AdviseFast::RuleSet::addRule(Entry const &entry) +{ + _rules.insert(entry); +} + +bool AdviseFast::RuleSet::getSurePoint(Coord *sp) +{ + if(_surePoints.empty()){ + if(!apply()) return false; + } + + CoordSet::iterator i = _surePoints.begin(); + *sp = *i; + _surePoints.erase(i); + + return true; +} + +bool AdviseFast::RuleSet::reveal(Coord what) +{ + CoordSet affectedFacts; + if(!facts->reveal(what, &affectedFacts)) + // OOPS :( + return false; + + CoordSet::iterator i; + for( i = affectedFacts.begin(); + i != affectedFacts.end(); + ++i) + this->addGeneral(*i); + + return true; +} + +void AdviseFast::RuleSet::solve() +{ + Coord p; + while(getSurePoint(&p)) { + bool res = reveal(p); + assert(res); + Q_UNUSED(res); + } +} + +bool AdviseFast::RuleSet::apply() +{ + while(!_rules.empty()){ + set::iterator i = _rules.begin(); + std::auto_ptr r (this->newRule(*i)); + _rules.erase(i); + + if(r->apply(&this->_surePoints)) return true; + } + + return false; +} + +AdviseFast::Rule * +AdviseFast::RuleSet::newRule(Entry const &e){ + CoordSet::const_iterator i = e.second.begin(); + Coord p, p1; + switch(e.first){ + case EMPTY: + assert(e.second.size() == 1); + return new EmptyRule(*i, this); + + case FULL: + assert(e.second.size() == 1); + return new FullRule(*i, this); + + case INCLUDE: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p, p1, this); + + case INCLUDE1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new InclusionRule(p1, p, this); + + case INTERSECT: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p, p1, this); + + case INTERSECT1: + assert(e.second.size() == 2); + p = *i; ++i; p1 = *i; + return new IntersectionRule(p1, p, this); + + case GENERAL: + assert(e.second.size() == 1); + return new GeneralRule(*i, this); + + default: + assert(false); + } + + // Make compiler happy + return 0; +} + +void AdviseFast::RuleSet::removeRef(Coord p){ + set::iterator i, j; + + for( i = j = _rules.begin(); + i != _rules.end(); + i = j) + { + ++j; + if(i->second.count(p)) _rules.erase(i); + } +} + +void AdviseFast::RuleSet::addGeneral(Coord p){ + this->removeRef(p); + Entry e; + e.first = GENERAL; + e.second.insert(p); + this->addRule(e); +} + +#if defined(DEBUG) && DEBUG >= 2 +int AdviseFast::Rule::leaks = 0; +#endif + +AdviseFast::Rule::Rule(RuleSet *parent) : + _parent(parent), + _facts(parent->facts) +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::Rule, leaks = " << ++leaks << endl; +#endif +} + +AdviseFast::Rule::~Rule() +{ +#if defined(DEBUG) && DEBUG >= 2 + cout << "Rule::~Rule, leaks = " << --leaks << endl; +#endif +} + +AdviseFast::GeneralRule::GeneralRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::GeneralRule::apply(CoordSet *) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying general rule ", + _fact) << endl; +#endif + + // Return if there's no more such fact + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // Insert intersection rules first + // relatedFacts -- facts which have non-zero intersection + CoordSet relatedFacts; + { + CoordSet::const_iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i){ + + CoordSet const & ps = + *_facts->getContainingFacts(*i); + relatedFacts.insert( + ps.begin(), ps.end()); + } + } + relatedFacts.erase(_fact); // ;) + + CoordSet::iterator i; + for( i=relatedFacts.begin(); + i!=relatedFacts.end(); + ++i) + { + RuleSet::Entry e; + e.second.insert(_fact); + e.second.insert(*i); + + e.first = RuleSet::INTERSECT1; _parent->addRule(e); + e.first = RuleSet::INTERSECT; _parent->addRule(e); + e.first = RuleSet::INCLUDE1; _parent->addRule(e); + e.first = RuleSet::INCLUDE; _parent->addRule(e); + } + + // Now simple rules, so that they appear first in the list + RuleSet::Entry e; e.second.insert(_fact); + e.first = RuleSet::FULL; _parent->addRule(e); + e.first = RuleSet::EMPTY; _parent->addRule(e); + + // No point revealed, so... + return false; +} + +AdviseFast::EmptyRule::EmptyRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::EmptyRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying empty rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact const &f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // If there are mines around, alas :( + if(f.mines) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + surePoints->insert( + f.pointSet.begin(), + f.pointSet.end()); + + _parent->removeRef(_fact); + + return true; +} + +AdviseFast::FullRule::FullRule( + Coord fact, + RuleSet *parent) : + Rule(parent), + _fact(fact) +{} + +bool AdviseFast::FullRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + operator <<( + cout << "Applying full rule ", + _fact) << endl; +#endif + + if(!_facts->count(_fact)) return false; + Fact f = (*_facts)[_fact]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << f << endl; +#endif + + // FactSet does not contain empty facts!! + assert(!f.pointSet.empty()); + + // The point set is not full of mines... :( + if(f.mines != (int)f.pointSet.size()) return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet affectedFacts; + CoordSet::iterator i; + for( i=f.pointSet.begin(); + i!=f.pointSet.end(); + ++i) + _facts->mark(*i, &affectedFacts); + + for( i=affectedFacts.begin(); + i!=affectedFacts.end(); + ++i) + _parent->addGeneral(*i); + _parent->removeRef(_fact); + + // No mines revealed + return false; +} + +AdviseFast::InclusionRule::InclusionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::InclusionRule::apply( + CoordSet */*surePoints*/) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying inclusion rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + assert(!s.pointSet.empty()); + + CoordSet diff; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diff, diff.begin())); + if(!diff.empty()) + // That is s is not included in b + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + diff.clear(); + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if(diff.empty()){ + _facts->deleteFact(_bigger); + _parent->removeRef(_bigger); + } else { + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + _parent->addGeneral(_bigger); + } + + // No points revealed + return false; +} + +AdviseFast::IntersectionRule::IntersectionRule( + Coord bigger, Coord smaller, + RuleSet *parent) : + Rule(parent), + _bigger(bigger), _smaller(smaller) +{} + +bool AdviseFast::IntersectionRule::apply( + CoordSet *surePoints) +{ + +#if defined(DEBUG) && DEBUG >= 2 + cout << "Applying intersection rule "; + operator <<(cout, _bigger) << ' '; + operator <<(cout, _smaller) << endl; +#endif + + if(!_facts->count(_bigger)) return false; + if(!_facts->count(_smaller)) return false; + + Fact b = (*_facts)[_bigger]; + Fact s = (*_facts)[_smaller]; + +#if defined(DEBUG) && DEBUG >= 2 + cout << b << endl << s << endl; +#endif + + CoordSet diff; + set_difference( + b.pointSet.begin(), + b.pointSet.end(), + s.pointSet.begin(), + s.pointSet.end(), + inserter(diff, diff.begin())); + + if((int)diff.size() != b.mines - s.mines) + // Oops :( + return false; + +#if defined(DEBUG) && DEBUG >= 2 + cout << "succeeded!" << endl; +#endif + + CoordSet cross, diffs; + set_difference( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(diffs, diffs.begin())); + set_intersection( + s.pointSet.begin(), + s.pointSet.end(), + b.pointSet.begin(), + b.pointSet.end(), + inserter(cross, cross.begin())); + + b.pointSet = diff; + b.mines -= s.mines; + _facts->addFact(_bigger, b); + + s.pointSet = cross; + _facts->addFact(_smaller, s); + + { + _parent->removeRef(_bigger); + _parent->addGeneral(_smaller); + + RuleSet::Entry e; + e.first = RuleSet::FULL; + e.second.insert(_bigger); + _parent->addRule(e); + } + + if(diffs.empty()) return false; + + // Otherwise we have something to reveal!! + surePoints->insert(diffs.begin(), diffs.end()); + return true; +} diff --git a/kmines/solver/adviseFast.cpp b/kmines/solver/adviseFast.cpp new file mode 100644 index 00000000..f15d508e --- /dev/null +++ b/kmines/solver/adviseFast.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFast.h" + +#include + + +using namespace AdviseFast; + +std::ostream &AdviseFast::operator <<(std::ostream &s, Fact const &f) +{ + return s << f.pointSet << "= " << f.mines; +} + +AdviseFast::FactSet::FactSet(BaseField *field) : + _field(field) +{ + Fact globalFact; globalFact.mines = field->nbMines(); + + int i, j; + for(i=field->height()-1; i>=0; --i) + for(j=field->width()-1; j>=0; --j){ + Coord p(j, i); + // #### hasMine implies isCovered (and the solver should not + // know if there is a mine :) [NH] + if ( field->isCovered(p) /*|| field->hasMine(p)*/ ) + globalFact.pointSet.insert(p); + else { + Fact f; + this->retrieveFact(p, &f); + this->addFact(p, f); + } + } + + this->addFact(Coord(-1,-1), globalFact); +} + +void AdviseFast::FactSet::retrieveFact( + Coord which, + Fact *where) +{ + where->mines = (_field->isCovered(which) ? -1 + : (int)_field->nbMinesAround(which)); + CoordList tmp = _field->coveredNeighbours(which); + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + where->pointSet.insert(*it); +} + +void AdviseFast::FactSet::addFact( + Coord const &point, + Fact const &fact) +{ + if(this->count(point)) this->deleteFact(point); + + Fact &f = ((*this)[point] = fact); + + + // Remove marked points + CoordSet marked; + set_intersection( + f.pointSet.begin(), + f.pointSet.end(), + _marked.begin(), + _marked.end(), + inserter(marked, marked.begin())); + + CoordSet::iterator i; + for(i=marked.begin(); i!=marked.end(); ++i) + f.pointSet.erase(*i); + f.mines -= marked.size(); + + // Don't insert empty fact + if(f.pointSet.empty()) { this->erase(point); return;} + + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i) + _containingFacts[*i].insert(point); +} + +void AdviseFast::FactSet::deleteFact( + Coord const &point) +{ + if(!this->count(point)) return; + CoordSet::iterator i; + Fact &f = (*this)[point]; + for(i=f.pointSet.begin(); i!=f.pointSet.end(); ++i){ + _containingFacts[*i].erase(point); + if(_containingFacts[*i].empty()) + _containingFacts.erase(*i); + } + this->erase(point); +} + +bool AdviseFast::FactSet::reveal( + Coord point, + CoordSet *affectedFacts) +{ + // Tolerate :) + if( !_field->isCovered(point) ) return true; // :) + + CoordList tmp; + if(_field->doReveal(point, &tmp, 0) == false) + // Blew up :( + return false; + + CoordSet autorevealed; + for (CoordList::const_iterator it = tmp.begin(); it!=tmp.end(); ++it) + autorevealed.insert(*it); + autorevealed.insert(point); + affectedFacts->insert(autorevealed.begin(), autorevealed.end()); + + CoordSet::const_iterator i; + for(i=autorevealed.begin(); i!=autorevealed.end(); ++i) + { + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(*i)){ + CoordSet const &affF = _containingFacts[*i]; + affectedFacts->insert( + affF.begin(), affF.end()); + for(CoordSet::const_iterator j=affF.begin(); + j!=affF.end(); + ++j) + { + (*this)[*j].pointSet.erase(*i); + if((*this)[*j].pointSet.empty()) + this->erase(*j); + } + _containingFacts.erase(*i); + } + + Fact f; retrieveFact(*i, &f); + this->addFact(*i, f); + } + + return true; +} + +void AdviseFast::FactSet::mark( + Coord point, + CoordSet *affectedFacts) +{ + if(_marked.count(point)) return; + _marked.insert(point); + + // I still think that each poing will belong to + // at least one fact, but don't want to waste time + // proving it :) + if(_containingFacts.count(point)){ + CoordSet const &affF = _containingFacts[point]; + affectedFacts->insert(affF.begin(), affF.end()); + for(CoordSet::const_iterator i=affF.begin(); i!=affF.end(); ++i){ + (*this)[*i].pointSet.erase(point); + (*this)[*i].mines--; + if((*this)[*i].pointSet.empty()) + this->erase(*i); + } + _containingFacts.erase(point); + } + + _field->doMark(point); +} + +CoordSet const *AdviseFast::FactSet::getContainingFacts( + Coord const &point) const +{ + if(_containingFacts.count(point)) + return &const_cast &>(_containingFacts) + [point]; + else return 0; +} + +std::ostream &AdviseFast::operator <<(std::ostream &s, FactSet const &fs) +{ + FactSet::const_iterator i; + for(i=fs.begin(); i!=fs.end(); ++i) + s << i->first << ": " << i->second << endl; + return s; +} + +bool AdviseFast::adviseFast( + Coord *, + FactSet *, + RuleSet *) +{ return false;} diff --git a/kmines/solver/adviseFast.h b/kmines/solver/adviseFast.h new file mode 100644 index 00000000..db3b1955 --- /dev/null +++ b/kmines/solver/adviseFast.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef adviseFast_h +#define adviseFast_h + +#include "headerP.h" + + +namespace AdviseFast { + + class GeneralRule : public Rule { + public: + GeneralRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class EmptyRule : public Rule { + public: + EmptyRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class FullRule : public Rule { + public: + FullRule(Coord fact, RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _fact; + }; + + class InclusionRule : public Rule { + public: + InclusionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; + + class IntersectionRule : public Rule { + public: + IntersectionRule(Coord bigger, Coord smaller, + RuleSet *rules); + virtual bool apply(CoordSet *surePoints); + private: + Coord _bigger, _smaller; + }; +} + +#endif diff --git a/kmines/solver/adviseFull.cpp b/kmines/solver/adviseFull.cpp new file mode 100644 index 00000000..815ff02c --- /dev/null +++ b/kmines/solver/adviseFull.cpp @@ -0,0 +1,655 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "adviseFull.h" +#include +#include +#include + +using std::list; +using std::map; +using std::set; +using namespace AdviseFull; + +AdviseFull::EquationSet::EquationSet() : + _maxPointSet(0) +{} + +AdviseFull::EquationSet::EquationSet( + AdviseFast::FactSet const &facts) : + _maxPointSet(0) +{ + AdviseFast::FactSet::const_iterator i; + for(i=facts.begin(); i!=facts.end(); ++i, ++_maxPointSet){ + Equation e; + e.pointSets.insert(_maxPointSet); + e.mines = i->second.mines; + _equations.push_back(e); + + _pointSets[_maxPointSet] = i->second.pointSet; + } +} + +void AdviseFull::EquationSet::normalize() +{ + short i=0; + set empty; + for(i=0; i<_maxPointSet; ++i){ + if(!_pointSets.count(i)) continue; + if(_pointSets[i].empty()){ + this->substitute(i, empty); + continue; + } + for(short j=i+1;j<_maxPointSet; ++j){ + if(!_pointSets.count(j)) continue; + if(_pointSets[j].empty()){ + this->substitute(j, empty); + continue; + } + + CoordSet intersect; + set_intersection( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()) continue; + + CoordSet _i, _j; + set_difference( + _pointSets[i].begin(), + _pointSets[i].end(), + _pointSets[j].begin(), + _pointSets[j].end(), + inserter(_i, _i.begin())); + set_difference( + _pointSets[j].begin(), + _pointSets[j].end(), + _pointSets[i].begin(), + _pointSets[i].end(), + inserter(_j, _j.begin())); + + set _ip, _jp; + _ip.insert(_maxPointSet); + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = intersect; + _ip.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _i; + _jp.insert(_maxPointSet); + _pointSets[_maxPointSet++] = _j; + + this->substitute(i, _ip); + this->substitute(j, _jp); + break; + } + } +} + +void AdviseFull::EquationSet::separate( + list *result) const +{ + result->clear(); // :) + + list equations = _equations; + + while(!equations.empty()){ + // Add a new equation set to *results + result->push_back(EquationSet()); + EquationSet &workingSet = result->back(); + workingSet._maxPointSet = _maxPointSet; + + // Start iteration process + // recentlyDeceased is a set of pointSets added on the + // last iteration + workingSet._equations.push_back(equations.front()); + set recentlyDeceased = equations.front().pointSets; + equations.pop_front(); + + // The iteration process + while(!recentlyDeceased.empty()){ + + // Temporary set + set rd; + + list::iterator i = equations.begin(); + while(i != equations.end()){ + set intersect; + set_intersection( + i->pointSets.begin(), + i->pointSets.end(), + recentlyDeceased.begin(), + recentlyDeceased.end(), + inserter(intersect, intersect.begin())); + if(intersect.empty()){ + ++i; + } else { + set_difference( + i->pointSets.begin(), + i->pointSets.end(), + intersect.begin(), + intersect.end(), + inserter(rd, rd.begin())); + workingSet._equations.push_back(*i); + i = equations.erase(i); + } + } + + // Now switch recentlyDeceased + set::iterator j; + for( j = recentlyDeceased.begin(); + j != recentlyDeceased.end(); + ++j) + { + workingSet._pointSets[*j] = + (const_cast< + map &>( + _pointSets))[*j]; + } + + recentlyDeceased = rd; + } + } +} + +map const &AdviseFull::EquationSet::solve( + list *results) const +{ + +#ifdef DEBUG + printf("Entering EquationSet::solve\n"); + prettyprint(); +#endif + + EquationSet eqs = *this; + + // Get the most evident solutions + Solution only; + list &EQS = eqs._equations; + + bool success; + do { + + success = false; + list::iterator i = EQS.begin(); + + while(i!=EQS.end()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Taking look at the equation...\n"); +#endif + // Substitute known values + { set::iterator j; + set known; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + { + if(only.count(*j)){ + i->mines -= only[*j]; + known.insert(*j); + } + } + + // STL bug ?? + for( j = known.begin(); + j != known.end(); + ++j) + i->pointSets.erase(*j); + } + // From now on the equation has no known values +#if defined(DEBUG) && DEBUG >= 2 + printf("Substituted known values.\n"); +#endif + if(i->mines < 0) + // Discrepancy + return _pointSets; + + + if(i->pointSets.empty()){ +#if defined(DEBUG) && DEBUG >= 2 + printf("Empty equation.\n"); +#endif + if(i->mines){ + // No points, non-zero mine count + // No solution + return _pointSets; + } else { + i = EQS.erase(i); + continue; + } + } + + if(i->mines == 0){ + set::iterator j; + for( + j=i->pointSets.begin(); + j!=i->pointSets.end(); + ++j) + only[*j] = 0; + + EQS.erase(i); + success = true; + break; + } + + if(i->pointSets.size() == 1){ + short j = *i->pointSets.begin(); + + if((int)eqs._pointSets[j].size() < i->mines) + // Discrepancy !! + return _pointSets; + + only[j] = i->mines; + + EQS.erase(i); + success = true; + break; + } + + ++i; + } + + } while(success); + + // If no equations left we have a unique solution + if(EQS.empty()){ +#ifdef DEBUG + printf("Got a single solution!\n"); +#endif + results->push_back(only); + return _pointSets; + } + + // Otherwise the first equation is not empty. + // Find the range for first element + short var = *EQS.begin()->pointSets.begin(); + std::pair range; + range.first = 0; + range.second = eqs._pointSets[var].size(); + + // A list of equations containing var + list::iterator> containers; + list::iterator i; + for( i = EQS.begin(); + i != EQS.end(); + ++i) + { + if(i->pointSets.count(var)){ + i->pointSets.erase(var); + containers.push_back(i); + + if(i->mines < range.second) + range.second = i->mines; + + // The total size of other point sets + // in the equation + short totalsize = 0; + set::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + totalsize += eqs._pointSets[*j].size(); + + if(range.first < i->mines - totalsize) + range.first = i->mines - totalsize; + } + } + // Found the range + + // Now set properly equation set for first recursion + list::iterator>::iterator super_iter; // ;) + short varvalue = range.first; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + (*super_iter)->mines -= varvalue; + + // Recursive calls here + while(varvalue <= range.second){ + only[var] = varvalue; + list tempResults; + eqs.solve(&tempResults); + + // Mix solutions with only and put them + // in *results + list::iterator j; + for( j=tempResults.begin(); + j!=tempResults.end(); + ++j) + { + j->insert(only.begin(), only.end()); + results->push_back(*j); + } + + // Prepare next recursive call + ++varvalue; + for( super_iter = containers.begin(); + super_iter != containers.end(); + ++super_iter) + --(*super_iter)->mines; + } + + return _pointSets; +} + +void AdviseFull::EquationSet::prettyprint() const +{ + +#if defined(DEBUG) + printf("Point Sets:\n"); + map::const_iterator i; + for(i=_pointSets.begin(); i!=_pointSets.end(); ++i){ + printf("%d:", i->first); + CoordSet::const_iterator j; + for(j=i->second.begin(); j!=i->second.end(); ++j) + printf("\t(%d,%d)\n", j->second, j->first); + } +#endif + + printf("Equations:\n"); + list::const_iterator l; + for(l=_equations.begin(); l!=_equations.end(); ++l){ + set::const_iterator j; + for(j=l->pointSets.begin(); j!=l->pointSets.end(); ++j) + printf("%d ", *j); + printf("= %d\n", l->mines); + } +} + +void AdviseFull::EquationSet::substitute( + short out, + set const &in) +{ + list::iterator i; + for( i = _equations.begin(); + i != _equations.end(); + ++i) + { + if(i->pointSets.count(out)){ + i->pointSets.erase(out); + i->pointSets.insert(in.begin(), in.end()); + } + } + + _pointSets.erase(out); +} + +bool AdviseFull::surePoints( + map const &m, + list const &l, + CoordSet *surePoints) +{ + // A set of candidates to be surePoints + list sp; + { + map::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) sp.push_back(i->first); + } + + // Scan solution list + list::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + list::iterator j = sp.begin(); + while(j != sp.end()){ + // Non-empty possibility + if((const_cast(*i))[*j]){ + j = sp.erase(j); + if(sp.empty()) // No candidates left + return false; + } else // Stay alive for now + ++j; + } + } + + // There are SOME sure points; + // Fill *surePoints + list::iterator isp; + map &mm = const_cast &>(m); + for(isp = sp.begin(); isp != sp.end(); ++isp) + surePoints->insert(mm[*isp].begin(), mm[*isp].end()); + + return true; +} + +float AdviseFull::variantNumberFraction( + map const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction) +{ + short count_difference = 0; + float quotient = 1; + + EquationSet::Solution::const_iterator i; + for(i=divisor.begin(); i!=divisor.end(); ++i){ + int j = i->first; + assert(m.count(j)); + int size = (const_cast &>(m))[j].size(); + int dvd = (const_cast(dividend))[j]; + int dvr = (const_cast(divisor))[j]; + + count_difference += dvd - dvr; + + if(dvd < dvr){ + dvr = size - dvr; + dvd = size - dvd; + } + while(dvr < dvd) { + float num = size-dvr++; + quotient *= num/dvr; + } + } + + // Sorry, expensive call, but I'm lazy :(( + if(count_difference){ + assert(fraction != 0.); +#if defined(DEBUG) + float correction = pow( fraction/(1-fraction), count_difference ); + cout << "Got into correction, " << + count_difference << ' ' << correction << endl; +#endif + quotient *= pow( (1-fraction)/fraction , -count_difference ); + } + +#if defined(DEBUG) && DEBUG >= 2 + printf("variantNumberFraction: %.02f.\n", quotient); +#endif + + return quotient; +} + +void AdviseFull::getProbabilities( + map const &m, + list const &l, + ProbabilityMap *probabilities, + float fraction) +{ + assert(!l.empty()); + EquationSet::Solution const &front = l.front(); + + float probabilitiesSum = 0; + map probs; + { map::const_iterator i; + for(i=m.begin(); i!=m.end(); ++i) + probs[i->first] = 0; + } + + list::const_iterator i; + for(i=l.begin(); i!=l.end(); ++i){ + float frac = variantNumberFraction(m, *i, front, fraction); + EquationSet::Solution::const_iterator j; + for(j=i->begin(); j!=i->end(); ++j) + probs[j->first] += j->second*frac; + probabilitiesSum += frac; + } + + probabilities->clear(); + + map::iterator j; + for(j=probs.begin(); j!= probs.end(); ++j){ + CoordSet const &ps = const_cast &>(m)[j->first]; + j->second /= ps.size() * probabilitiesSum; + CoordSet::const_iterator k; + for(k=ps.begin(); k!=ps.end(); ++k) + probabilities->insert( + std::pair(j->second, *k)); + } + + // That's it :) +} + +void AdviseFull::adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities) +{ + EquationSet eqs(*facts); + +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + eqs.normalize(); +#if defined(DEBUG) && DEBUG >= 2 + eqs.prettyprint(); +#endif + + list eqss; + eqs.separate(&eqss); +#ifdef DEBUG + {list::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + + + // OK, uneffective, but simple :( + surePoints->clear(); + probabilities->clear(); + + // Get a fraction; + float fraction; + { BaseField const *f = facts->getField(); + fraction = ((float)f->nbMines()) / (f->width() * f->height()); + } + + /* From now on the first equation set on the list includes + * the equation corresponding to "total" fact. This is the + * first equation on the set. + * + * Give it a special treatment ;) */ + if(!eqss.empty()) do { + EquationSet prime = eqss.front(); + EquationSet::Equation total = prime._equations.front(); + prime._equations.pop_front(); + + list prime_sep; + prime.separate(&prime_sep); + + // Find a pool + list::iterator i = prime._equations.begin(); + while(!prime._equations.empty()){ + set::iterator j; + for( j = i->pointSets.begin(); + j != i->pointSets.end(); + ++j) + prime._pointSets.erase(*j); + i = prime._equations.erase(i); + } + + assert(prime._pointSets.size() <= 1); + if(prime._pointSets.size() == 0) break; + + short pool = prime._pointSets.begin()->first; + CoordSet const &p = prime._pointSets[pool]; +#ifdef DEBUG + cout << "Prime equation set:" << endl << + " separated into " << prime_sep.size() << endl << + " pool size is " << p.size() << endl; +#endif + // Euristic + // if( prime_sep.size () > 6 && p.size() >= prime_sep.size() * 10){ + if(p.size() < (prime_sep.size()+1) * 10) + // No special treatment!! + break; + + + // Actually, just substitute prime (!!!) + eqss.pop_front(); + eqss.insert(eqss.begin(), + prime_sep.begin(), + prime_sep.end()); + + prime._equations.clear(); + EquationSet::Equation o; + o.pointSets.insert(pool); + // #### is the convertion right ? (NH) + o.mines = (ushort)(fraction * p.size()); + // A precaution + if(o.mines == 0) o.mines = 1; // ;) + + prime._equations.push_front(o); + eqss.push_front(prime); + + +#ifdef DEBUG + cout << "Specially treated:" << endl; + { + list::iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i) + i->prettyprint(); + } +#endif + } while (false); + + list::const_iterator i; + for(i=eqss.begin(); i!=eqss.end(); ++i){ + CoordSet sp; ProbabilityMap pb; + + list solutions; + map const &m = i->solve(&solutions); +#ifdef DEBUG + printf("Got solutions.\n"); +#if defined(DEBUG) && DEBUG >= 2 + { list::iterator i; + for( i = solutions.begin(); + i != solutions.end(); + ++i) + { + EquationSet::Solution::iterator j; + for(j=i->begin(); j!=i->end(); ++j) + printf("%d:\t%d\n", + j->first, j->second); + printf("\n"); + } + } +#endif +#endif + + //bool sure = + AdviseFull::surePoints(m, solutions, &sp); + surePoints->insert(sp.begin(), sp.end()); + + getProbabilities(m, solutions, &pb, fraction); + probabilities->insert(pb.begin(), pb.end()); + } + + // That's it + return; +} diff --git a/kmines/solver/adviseFull.h b/kmines/solver/adviseFull.h new file mode 100644 index 00000000..2b0cc97b --- /dev/null +++ b/kmines/solver/adviseFull.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ADVISE_FULL_H +#define __ADVISE_FULL_H + +#include +#include + +#include "headerP.h" + + +namespace AdviseFull { + class EquationSet { + public: // Well, why is it necessary? + struct Equation { + std::set pointSets; + short mines; + }; + typedef std::map Solution; + + public: + EquationSet(); + EquationSet(AdviseFast::FactSet const &facts); + + std::list _equations; + std::map _pointSets; + + /** Make sure no _pointSets have + * non-empty intersection */ + void normalize(); + + /** Returns in *results a set of equation sets + * which can be solved separately. + * *this assumed normalized :) */ + void separate(std::list *results) const; + + /** Solves... returns _pointSets. + * It's nice to have *this separated :) */ + std::map const &solve( + std::list *results) const; + + void prettyprint() const; + + private: + /** One more than max(_pointSets[i].first) */ + short _maxPointSet; + + /** Substitutes a pointSet in all equations */ + void substitute( + short out, + std::set const &in); + }; + + bool surePoints( + std::map const &m, + std::list const &l, + CoordSet *surePoints); + + /** The fourth argument is a fraction of mines in the "pool" */ + void getProbabilities( + std::map const &m, + std::list const &l, + ProbabilityMap *probabilities, + float fraction = 0); + + /** Get the quotient of the number of variants of + * point distribution satisfying dividend and divisor + * solutions */ + /** The fourth argument is a fraction of mines in the "pool" */ + float variantNumberFraction( + std::map const &m, + EquationSet::Solution const ÷nd, + EquationSet::Solution const &divisor, + float fraction = 0); +} + +#endif diff --git a/kmines/solver/bfield.cpp b/kmines/solver/bfield.cpp new file mode 100644 index 00000000..d6c03643 --- /dev/null +++ b/kmines/solver/bfield.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "bfield.h" + + +using namespace KGrid2D; + +BaseField::BaseField(long seed) + : _nbUncovered(0), _nbMarked(0), _nbUncertain(0), _random(seed) +{} + +CoordList BaseField::coveredNeighbours(const Coord &p) const +{ + CoordList n; + CoordList tmp = neighbours(p); + for (CoordList::const_iterator it=tmp.begin(); it!=tmp.end(); ++it) + if ( state(*it)!=Uncovered ) n.append(*it); + return n; +} + +uint BaseField::nbMinesAround(const Coord &p) const +{ + uint nb = 0; + CoordList n = neighbours(p); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + if ( hasMine(*it) ) nb++; + return nb; +} + +void BaseField::reset(uint width, uint height, uint nbMines) +{ + _firstReveal = true; + _nbMarked = 0; + _nbUncertain = 0; + _nbUncovered = 0; + _nbMines = nbMines; + + Case tmp; + tmp.mine = false; + tmp.state = Covered; + resize(width, height); + fill(tmp); +} + +bool BaseField::checkField(uint w, uint h, uint nb, const QString &s) +{ + if ( s.length()!=w*h ) return false; + uint n = 0; + unsigned int strLength(s.length()); + for (uint i=0; i + +#include +#include + +#include "defines.h" + + +class BaseField : public KGrid2D::Square, public KMines +{ + public: + // seed for KRandomSequence (used by solver check programs) + BaseField(long seed = 0); + virtual ~BaseField() {} + + void reset(uint width, uint height, uint nbMines); + static bool checkField(uint width, uint height, uint nbMines, + const QString &field); + void initReplay(const QString &field); // string == "0100011011000101..." + +// -------------------------- +// interface used by the solver + uint nbMines() const { return _nbMines; } + bool isCovered(const KGrid2D::Coord &p) const + { return ( state(p)!=KMines::Uncovered ); } + uint nbMinesAround(const KGrid2D::Coord &) const; + KGrid2D::CoordList coveredNeighbours(const KGrid2D::Coord &p) const; + bool isSolved() const { return (size() - _nbUncovered)==_nbMines; } + + // return false if the case revealed contains a mine. + virtual bool doReveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered) + { return reveal(c, autorevealed, caseUncovered); } + virtual void doMark(const KGrid2D::Coord &); +// ------------------------- + + uint nbMarked() const { return _nbMarked; } + QCString string() const; + + void showAllMines(bool won); + + protected: + bool firstReveal() const { return _firstReveal; } + KMines::CaseState state(const KGrid2D::Coord &p) const + { return (*this)[p].state; } + bool hasMine(const KGrid2D::Coord &p) const { return (*this)[p].mine; } + virtual void changeCase(const KGrid2D::Coord &, KMines::CaseState); + bool reveal(const KGrid2D::Coord &c, + KGrid2D::CoordList *autorevealed, bool *caseUncovered); + bool autoReveal(const KGrid2D::Coord &, bool *caseUncovered); + void completeReveal(); + + private: + bool _firstReveal; + uint _nbUncovered, _nbMarked, _nbUncertain, _nbMines; + KRandomSequence _random; + + void uncover(const KGrid2D::Coord &, KGrid2D::CoordList *autoreveal); + void changeState(KMines::CaseState, int increment); +}; + +#endif diff --git a/kmines/solver/headerP.h b/kmines/solver/headerP.h new file mode 100644 index 00000000..984e3113 --- /dev/null +++ b/kmines/solver/headerP.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __HEADERP_H +#define __HEADERP_H + +//#define DEBUG 2 + +#include +#include +#include +#include +#include + +#include "bfield.h" + + +using namespace KGrid2D; +using std::cout; +using std::endl; + +typedef std::set > CoordSet; + +inline std::ostream &operator <<(std::ostream &s, const Coord &c) +{ + s << '(' << c.first << ',' << c.second << ')' << endl; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const CoordSet &set) +{ + for(CoordSet::const_iterator i=set.begin(); i!=set.end(); ++i) + s << *i; + return s; +} + +inline std::ostream &operator <<(std::ostream &s, const BaseField &f) +{ + for (uint j=0; j { + public: + FactSet(BaseField *); + BaseField const *getField() const { return _field;} + + /** Reveals a point on the field underlining + * Returns false on blowup !!! */ + bool reveal( + Coord what, + CoordSet *affectedFacts); + void mark( + Coord what, + CoordSet *affectedFacts); + CoordSet const *getContainingFacts( + Coord const &) + const; + /** May be used to substitute fact */ + void addFact(Coord const &, Fact const &); + void deleteFact(Coord const &); + void retrieveFact(Coord which, Fact *where); + + private: + BaseField *_field; + std::map _containingFacts; + CoordSet _marked; + }; + std::ostream &operator <<(std::ostream &, FactSet const &); + + /** A Rule abstraction that can be applied. + * Applying the rule results in either modifyling the + * RuleSet which it belongs to or FactSet it is based on + * or both ;) + */ + class RuleSet; + struct Rule { + Rule(RuleSet *parent); + virtual ~Rule(); + virtual bool apply(CoordSet *surePoints) = 0; + + RuleSet *_parent; + FactSet *_facts; +#if defined(DEBUG) +# if DEBUG >= 2 + private: + static int leaks; +# endif +#endif + }; + + /** A set of rules */ + class RuleSet { + public: + enum RuleType { + EMPTY, + FULL, + INCLUDE, + INCLUDE1, + INTERSECT, + INTERSECT1, + GENERAL}; + + typedef std::pair Entry; + + RuleSet(FactSet *); + ~RuleSet(); + void addRule(Entry const &); + + /** A factory method */ + Rule *newRule(Entry const &); + + /** Remove all references to a point from RuleSet */ + void removeRef(Coord); + + /** removeRef + add a General Rule */ + void addGeneral(Coord); + + /** Returns false on blowup */ + bool reveal(Coord p); + + /** Returns false on failure */ + bool getSurePoint(Coord *sp); + /** Works until is stuck :) */ + void solve(); + + FactSet *facts; + + private: + std::set _rules; + CoordSet _surePoints; + + /** Fills _surePoints. + * Returns false if nothing done. */ + bool apply(); + }; + + /** Returns true on success */ + bool adviseFast( + Coord *point, + FactSet *facts, + RuleSet *rules); + +} + + +namespace AdviseFull { + typedef std::multimap ProbabilityMap; + + /** If there are sure free cells, + * sets surePoints, otherwise sets probabilities */ + void adviseFull( + AdviseFast::FactSet *facts, + CoordSet *surePoints, + ProbabilityMap *probabilities); + +} + +#endif diff --git a/kmines/solver/solver.cpp b/kmines/solver/solver.cpp new file mode 100644 index 00000000..00807fca --- /dev/null +++ b/kmines/solver/solver.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "solver.h" +#include "solver.moc" + +#include +#include + +#include +#include +#include +#include + +#include + +#include "headerP.h" + + +//----------------------------------------------------------------------------- +class SolverPrivate +{ + public: + SolverPrivate() : facts(0), rules(0) {} + ~SolverPrivate() { + delete facts; + delete rules; + } + + AdviseFast::FactSet *facts; + AdviseFast::RuleSet *rules; +#ifdef DEBUG + unsigned long t0, t; +#endif +}; + +Solver::Solver(QObject *parent) + : QObject(parent) +{ + d = new SolverPrivate; + +#ifdef DEBUG +#define PRINT_ELAPSED(purpose) \ + d->t = time(0); \ + cout << "Spent " << d->t - d->t0 << " seconds on " purpose << endl; \ + d->t0 = d->t; + +#endif +} + +Solver::~Solver() +{ + delete d; +} + +Coord Solver::advise(BaseField &field, float &probability) +{ + Coord point; + probability = 1; + delete d->facts; + d->facts = new AdviseFast::FactSet(&field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); + + if( AdviseFast::adviseFast(&point, d->facts, d->rules) ) return point; + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + + // return one of the sure point (random choice to limit the tropism) [NH] + if( !surePoints.empty() ) { + KRandomSequence r; + uint k = r.getLong(surePoints.size()); + CoordSet::iterator it = surePoints.begin(); + for (uint i=0; ifirst; + return probabilities.begin()->second; + } + + // Otherwise the Field is already solved :) + return Coord(-1,-1); +} + +void Solver::solve(BaseField &field, bool noGuess) +{ + _field = &field; + initSolve(false, noGuess); +} + +bool Solver::initSolve(bool oneStep, bool noGuess) +{ + _inOneStep = oneStep; + _noGuess = noGuess; + delete d->facts; + d->facts = new AdviseFast::FactSet(_field); + delete d->rules; + d->rules = new AdviseFast::RuleSet(d->facts); +#ifdef DEBUG + d->t0 = time(0); +#endif + return solveStep(); +} + +bool Solver::solveStep() +{ + if ( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + d->rules->solve(); + +#ifdef DEBUG + PRINT_ELAPSED("fast rules") +#endif + + if( _field->isSolved() ) { + emit solvingDone(true); + return true; + } + + CoordSet surePoints; + AdviseFull::ProbabilityMap probabilities; + AdviseFull::adviseFull(d->facts, &surePoints, &probabilities); + +#ifdef DEBUG + PRINT_ELAPSED("full rules") +#endif + + if(!surePoints.empty()){ + CoordSet::iterator i; + for(i=surePoints.begin(); i!=surePoints.end(); ++i) { + bool b = d->rules->reveal(*i); + assert(b); + } + } else if ( !_noGuess ) { +#ifdef DEBUG + cout << "Applying heuristics!" << endl; + cout << *_field << endl; +#endif + // Minimum probability logic + assert(!probabilities.empty()); +#ifdef DEBUG + AdviseFull::ProbabilityMap::iterator i=probabilities.begin(); + cout << "Probability is " << i->first << endl; +#endif + bool success = d->rules->reveal(probabilities.begin()->second); + if ( !success ) { + emit solvingDone(false); + return false; + } + } + + if (_inOneStep) return solveStep(); + else QTimer::singleShot(0, this, SLOT(solveStep())); + return false; +} + +bool Solver::solveOneStep(BaseField &field) +{ + _field = &field; + return initSolve(true, false); +} + + +//----------------------------------------------------------------------------- +SolvingRateDialog::SolvingRateDialog(const BaseField &field, QWidget *parent) + : KDialogBase(Plain, i18n("Compute Solving Rate"), Ok|Close, + Close, parent, "compute_solving_rate", true, true), + _refField(field) +{ + connect(&_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + + KGuiItem item = KStdGuiItem::ok(); + item.setText(i18n("Start")); + setButtonOK(item); + + QVBoxLayout *top = new QVBoxLayout(plainPage(), 0, spacingHint()); + QLabel *label = new QLabel(i18n("Width: %1").arg(field.width()), + plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Height: %1").arg(field.height()), plainPage()); + top->addWidget(label); + label = new QLabel(i18n("Mines: %1 (%2%)").arg(field.nbMines()) + .arg( field.nbMines() * 100.0 / field.size()), + plainPage()); + top->addWidget(label); + + top->addSpacing(spacingHint()); + + _progress = new KProgress(NB_STEPS, plainPage()); + _progress->setTextEnabled(true); + _progress->setFormat("%v"); + top->addWidget(_progress); + + _label = new QLabel(i18n("Success rate:"), plainPage()); + top->addWidget(_label); +} + +void SolvingRateDialog::slotOk() +{ + enableButtonOK(false); + _i = 0; + _success = 0; + _progress->setValue(0); + QTimer::singleShot(0, this, SLOT(step())); +} + +void SolvingRateDialog::step() +{ + if ( _i==NB_STEPS ) { + enableButtonOK(true); + return; + } + _i++; + _field.reset(_refField.width(), _refField.height(), _refField.nbMines()); + _solver.solve(_field, false); +} + +void SolvingRateDialog::solvingDone(bool success) +{ + if (success) _success++; + _label->setText(i18n("Success rate: %1%") + .arg(_success * 100.0 / _i, 0, 'f', 3)); + _progress->advance(1); + QTimer::singleShot(0, this, SLOT(step())); +} diff --git a/kmines/solver/solver.h b/kmines/solver/solver.h new file mode 100644 index 00000000..f076874e --- /dev/null +++ b/kmines/solver/solver.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __SOLVER_H +#define __SOLVER_H + +#include + +#include "bfield.h" + + +class QLabel; +class KProgress; +class SolverPrivate; + +class Solver : public QObject +{ + Q_OBJECT + public: + Solver(QObject *parent = 0); + ~Solver(); + + /** A method to advice a point placement */ + KGrid2D::Coord advise(BaseField &field, float &probability); + + /** Solve current mine field */ + void solve(BaseField &field, bool noGuess); + + /** Solve without signals/slot (for test programs) */ + bool solveOneStep(BaseField &field); + + signals: + void solvingDone(bool success); + + private slots: + bool solveStep(); + + private: + BaseField *_field; + bool _inOneStep, _noGuess; + SolverPrivate *d; + + bool initSolve(bool oneStep, bool noGuess); +}; + +class SolvingRateDialog : public KDialogBase +{ + Q_OBJECT + public: + SolvingRateDialog(const BaseField &field, QWidget *parent); + + private slots: + void step(); + void slotOk(); + void solvingDone(bool success); + + private: + const BaseField &_refField; + BaseField _field; + Solver _solver; + uint _i, _success; + QLabel *_label; + KProgress *_progress; + + static const uint NB_STEPS = 200; +}; + +#endif diff --git a/kmines/solver/test.cpp b/kmines/solver/test.cpp new file mode 100644 index 00000000..dd56d7a0 --- /dev/null +++ b/kmines/solver/test.cpp @@ -0,0 +1,45 @@ +/** A program to test advisory library */ + +#include "bfield.h" +#include "headerP.h" + +#define W 10 +#define H 10 + +int main(int argc, char *argv[]) +{ + long seed = (argc<2 ? time(0) : atoi(argv[1])); + cout << "seed = " << seed << endl; + + BaseField f(seed); + f.reset(W, H, 10); + + KRandomSequence random(seed); + Coord c(random.getLong(W), random.getLong(H)); + f.doReveal(c, 0, 0); + + CoordSet sp; + AdviseFull::ProbabilityMap pm; + + AdviseFast::FactSet facts(&f); + AdviseFull::adviseFull(&facts, &sp, &pm); + + float pic[H][W]; + + for(uint i=0; isecond.second][pmi->second.first] = pmi->first; + + QString s; + for(uint i=0;i +#include + +#include "bfield.h" +#include "solver.h" +#include "headerP.h" + +int main(int argc, char *argv[]) +{ + if ( argc!=4 ) + qFatal("Arguments: width height nbMines"); + + long seed = time(0); + cout << "seed = " << seed << endl; + + short W, H, M; + W = atoi(argv[1]); assert(W > 0); + H = atoi(argv[2]); assert(H > 0); + M = atoi(argv[3]); assert(M >= 0); // ;) + + BaseField field(seed); + field.reset(W, H, M); + + Solver solver; + if( !solver.solveOneStep(field) ) cout << "OOPS!!" << endl; + else cout << "Solved!" << endl; + + cout << field << endl; + + return 0; +} diff --git a/kmines/status.cpp b/kmines/status.cpp new file mode 100644 index 00000000..a3d5f061 --- /dev/null +++ b/kmines/status.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "status.h" +#include "status.moc" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" +#include "version.h" + + +Status::Status(QWidget *parent) + : QWidget(parent, "status"), _oldLevel(Level::Easy) +{ + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), SLOT(replayStep())); + + _solver = new Solver(this); + connect(_solver, SIGNAL(solvingDone(bool)), SLOT(solvingDone(bool))); + +// top layout + QGridLayout *top = new QGridLayout(this, 2, 5, 10, 10); + top->setColStretch(1, 1); + top->setColStretch(3, 1); + +// status bar + // mines left LCD + left = new KGameLCD(5, this); + left->setFrameStyle(QFrame::Panel | QFrame::Sunken); + left->setDefaultBackgroundColor(black); + left->setDefaultColor(white); + QWhatsThis::add(left, i18n("Mines left.
    " + "It turns red " + "when you have flagged more cases than " + "present mines.
    ")); + top->addWidget(left, 0, 0); + + // smiley + smiley = new Smiley(this); + connect(smiley, SIGNAL(clicked()), SLOT(smileyClicked())); + smiley->setFocusPolicy(QWidget::NoFocus); + QWhatsThis::add(smiley, i18n("Press to start a new game")); + top->addWidget(smiley, 0, 2); + + // digital clock LCD + dg = new DigitalClock(this); + QWhatsThis::add(dg, i18n("Time elapsed.
    " + "It turns blue " + "if it is a highscore " + "and red " + "if it is the best time.
    ")); + top->addWidget(dg, 0, 4); + +// mines field + _fieldContainer = new QWidget(this); + QGridLayout *g = new QGridLayout(_fieldContainer, 1, 1); + _field = new Field(_fieldContainer); + _field->readSettings(); + g->addWidget(_field, 0, 0, AlignCenter); + connect( _field, SIGNAL(updateStatus(bool)), SLOT(updateStatus(bool)) ); + connect(_field, SIGNAL(gameStateChanged(GameState)), + SLOT(gameStateChangedSlot(GameState)) ); + connect(_field, SIGNAL(setMood(Mood)), smiley, SLOT(setMood(Mood))); + connect(_field, SIGNAL(setCheating()), dg, SLOT(setCheating())); + connect(_field,SIGNAL(addAction(const KGrid2D::Coord &, Field::ActionType)), + SLOT(addAction(const KGrid2D::Coord &, Field::ActionType))); + QWhatsThis::add(_field, i18n("Mines field.")); + +// resume button + _resumeContainer = new QWidget(this); + g = new QGridLayout(_resumeContainer, 1, 1); + QFont f = font(); + f.setBold(true); + QPushButton *pb + = new QPushButton(i18n("Press to Resume"), _resumeContainer); + pb->setFont(f); + connect(pb, SIGNAL(clicked()), SIGNAL(pause())); + g->addWidget(pb, 0, 0, AlignCenter); + + _stack = new QWidgetStack(this); + _stack->addWidget(_fieldContainer); + _stack->addWidget(_resumeContainer); + _stack->raiseWidget(_fieldContainer); + top->addMultiCellWidget(_stack, 1, 1, 0, 4); +} + +void Status::smileyClicked() +{ + if ( _field->gameState()==Paused ) emit pause(); + else restartGame(); +} + +void Status::newGame(int t) +{ + if ( _field->gameState()==Paused ) emit pause(); + Level::Type type = (Level::Type)t; + Settings::setLevel(type); + if ( type!=Level::Custom ) newGame( Level(type) ); + else newGame( Settings::customLevel() ); +} + +void Status::newGame(const Level &level) +{ + _timer->stop(); + if ( level.type()!=Level::Custom ) + KExtHighscore::setGameType(level.type()); + _field->setLevel(level); +} + +bool Status::checkBlackMark() +{ + bool bm = ( _field->gameState()==Playing ); + if (bm) KExtHighscore::submitScore(KExtHighscore::Lost, this); + return bm; +} + +void Status::restartGame() +{ + if ( _field->gameState()==Paused ) emit pause(); + else if ( _field->gameState()==Replaying ) { + _timer->stop(); + _field->setLevel(_oldLevel); + } else { + bool bm = checkBlackMark(); + _field->reset(bm); + } +} + +void Status::settingsChanged() +{ + _field->readSettings(); + + if ( Settings::level()!=Level::Custom ) return; + Level l = Settings::customLevel(); + if ( l==_field->level() ) return; + if ( _field->gameState()==Paused ) emit pause(); + newGame(l); +} + +void Status::updateStatus(bool mine) +{ + int r = _field->nbMines() - _field->nbMarked(); + QColor color = (r<0 && !_field->isSolved() ? red : white); + left->setColor(color); + left->display(r); + + if ( _field->isSolved() && !mine ) + gameStateChanged(GameOver, true); // ends only for wins +} + +void Status::setGameOver(bool won) +{ + if ( !won ) + KNotifyClient::event(winId(), "explosion", i18n("Explosion!")); + _field->showAllMines(won); + smiley->setMood(won ? Happy : Sad); + if ( _field->gameState()==Replaying ) return; + + _field->setGameOver(); + dg->stop(); + if ( _field->level().type()!=Level::Custom && !dg->cheating() ) { + if (won) KExtHighscore::submitScore(dg->score(), this); + else KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + KNotifyClient::event(winId(), won ? "won" : "lost", + won ? i18n("Game won!") : i18n("Game lost!")); + + // game log + _logRoot.setAttribute("count", dg->nbActions()); + + if ( Settings::magicReveal() ) + _logRoot.setAttribute("complete_reveal", "true"); + QString sa = "none"; + if ( _field->solvingState()==Solved ) sa = "solving"; + else if ( _field->solvingState()==Advised ) sa = "advising"; + _logRoot.setAttribute("solver", sa); + + QDomElement f = _log.createElement("Field"); + _logRoot.appendChild(f); + QDomText data = _log.createTextNode(_field->string()); + f.appendChild(data); +} + +void Status::setStopped() +{ + smiley->setMood(Normal); + updateStatus(false); + bool custom = ( _field->level().type()==Level::Custom ); + dg->reset(custom); + _field->setSolvingState(Regular); +} + +void Status::setPlaying() +{ + smiley->setMood(Normal); + dg->start(); + if ( _field->gameState()==Paused ) return; // do not restart game log... + + // game log + const Level &level = _field->level(); + _log = QDomDocument("kmineslog"); + _logRoot = _log.createElement("kmineslog"); + _logRoot.setAttribute("version", SHORT_VERSION); + QDateTime date = QDateTime::currentDateTime(); + _logRoot.setAttribute("date", date.toString(Qt::ISODate)); + _logRoot.setAttribute("width", level.width()); + _logRoot.setAttribute("height", level.height()); + _logRoot.setAttribute("mines", level.nbMines()); + _log.appendChild(_logRoot); + _logList = _log.createElement("ActionList"); + _logRoot.appendChild(_logList); +} + +void Status::gameStateChanged(GameState state, bool won) +{ + QWidget *w = _fieldContainer; + + switch (state) { + case Playing: + setPlaying(); + break; + case GameOver: + setGameOver(won); + break; + case Paused: + smiley->setMood(Sleeping); + dg->stop(); + w = _resumeContainer; + break; + case Stopped: + case Init: + setStopped(); + break; + case Replaying: + smiley->setMood(Normal); + break; + case NB_STATES: + Q_ASSERT(false); + break; + } + + _stack->raiseWidget(w); + emit gameStateChangedSignal(state); +} + +void Status::addAction(const KGrid2D::Coord &c, Field::ActionType type) +{ + QDomElement action = _log.createElement("Action"); + action.setAttribute("time", dg->pretty()); + action.setAttribute("column", c.first); + action.setAttribute("line", c.second); + action.setAttribute("type", Field::ACTION_DATA[type].name); + _logList.appendChild(action); + dg->addAction(); +} + +void Status::advise() +{ + int res = KMessageBox::warningContinueCancel(this, + i18n("When the solver gives " + "you advice, your score will not be added to the highscores."), + QString::null, QString::null, "advice_warning"); + if ( res==KMessageBox::Cancel ) return; + dg->setCheating(); + float probability; + KGrid2D::Coord c = _solver->advise(*_field, probability); + _field->setAdvised(c, probability); +} + +void Status::solve() +{ + dg->setCheating(); + _solver->solve(*_field, false); + _field->setSolvingState(Solved); +} + +void Status::solvingDone(bool success) +{ + if ( !success ) gameStateChanged(GameOver, false); +} + +void Status::solveRate() +{ + SolvingRateDialog sd(*_field, this); + sd.exec(); +} + +void Status::viewLog() +{ + KDialogBase d(this, "view_log", true, i18n("View Game Log"), + KDialogBase::Close, KDialogBase::Close); + QTextEdit *view = new QTextEdit(&d); + view->setReadOnly(true); + view->setTextFormat(PlainText); + view->setText(_log.toString()); + d.setMainWidget(view); + d.resize(500, 400); + d.exec(); +} + +void Status::saveLog() +{ + KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + if ( KIO::NetAccess::exists(url, false, this) ) { + KGuiItem gi = KStdGuiItem::save(); + gi.setText(i18n("Overwrite")); + int res = KMessageBox::warningYesNo(this, + i18n("The file already exists. Overwrite?"), + i18n("File Exists"), gi, KStdGuiItem::cancel()); + if ( res==KMessageBox::No ) return; + } + KTempFile tmp; + (*tmp.textStream()) << _log.toString(); + tmp.close(); + KIO::NetAccess::upload(tmp.name(), url, this); + tmp.unlink(); +} + +void Status::loadLog() +{ + KURL url = KFileDialog::getOpenURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + QString tmpFile; + bool success = false; + QDomDocument doc; + if( KIO::NetAccess::download(url, tmpFile, this) ) { + QFile file(tmpFile); + if ( file.open(IO_ReadOnly) ) { + int errorLine; + bool ok = doc.setContent(&file, 0, &errorLine); + if ( !ok ) { + KMessageBox::sorry(this, i18n("Cannot read XML file on line %1") + .arg(errorLine)); + return; + } + success = true; + } + KIO::NetAccess::removeTempFile(tmpFile); + + } + if ( !success ) { + KMessageBox::sorry(this, i18n("Cannot load file.")); + return; + } + + if ( !checkLog(doc) ) + KMessageBox::sorry(this, i18n("Log file not recognized.")); + else { + _log = doc; + _logRoot = doc.namedItem("kmineslog").toElement(); + emit gameStateChangedSignal(GameOver); + } +} + +bool Status::checkLog(const QDomDocument &doc) +{ + // check root element + if ( doc.doctype().name()!="kmineslog" ) return false; + QDomElement root = doc.namedItem("kmineslog").toElement(); + if ( root.isNull() ) return false; + bool ok; + uint w = root.attribute("width").toUInt(&ok); + if ( !ok || w>CustomConfig::maxWidth || wCustomConfig::maxHeight || hLevel::maxNbMines(w, h) ) return false; + + // check field + QDomElement field = root.namedItem("Field").toElement(); + if ( field.isNull() ) return false; + QString ftext = field.text(); + if ( !BaseField::checkField(w, h, nb, ftext) ) return false; + + // check action list + QDomElement list = root.namedItem("ActionList").toElement(); + if ( list.isNull() ) return false; + QDomNodeList actions = list.elementsByTagName("Action"); + if ( actions.count()==0 ) return false; + for (uint i=0; i=h ) return false; + uint j = a.attribute("column").toUInt(&ok); + if ( !ok || j>=w ) return false; + QString type = a.attribute("type"); + uint k = 0; + for (; klevel(); + newGame(level); + _field->setReplayField(f.toElement().text()); + QString s = _logRoot.attribute("complete_reveal"); + _completeReveal = ( s=="true" ); + + f = _logRoot.namedItem("ActionList"); + _actions = f.toElement().elementsByTagName("Action"); + _index = 0; + _timer->start(500); +} + +void Status::replayStep() +{ + if ( _index>=_actions.count() ) { + _timer->stop(); + _actions = QDomNodeList(); + return; + } + + _timer->changeInterval(200); + QDomElement a = _actions.item(_index).toElement(); + dg->setTime(a.attribute("time")); + uint i = a.attribute("column").toUInt(); + uint j = a.attribute("line").toUInt(); + QString type = a.attribute("type"); + for (uint k=0; kdoAction((Field::ActionType)k, + KGrid2D::Coord(i, j), _completeReveal); + break; + } + _index++; +} diff --git a/kmines/status.h b/kmines/status.h new file mode 100644 index 00000000..6fd06a76 --- /dev/null +++ b/kmines/status.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef STATUS_H +#define STATUS_H + +#include + +#include "field.h" + +class Smiley; +class KGameLCD; +class DigitalClock; +class Solver; +class QWidgetStack; +class QTimer; + +class Status : public QWidget, public KMines +{ + Q_OBJECT + public : + Status(QWidget *parent); + + const Level ¤tLevel() const { return _field->level(); } + bool isPlaying() const { return _field->gameState()==Playing; } + void settingsChanged(); + Field *field() { return _field; } + + bool checkBlackMark(); + + signals: + void pause(); + void gameStateChangedSignal(KMines::GameState); + + public slots: + void newGame(int type); + void restartGame(); + void updateStatus(bool); + void pauseGame() { _field->pause(); } + + void moveUp() { _field->moveCursor(KGrid2D::SquareBase::Up); } + void moveDown() { _field->moveCursor(KGrid2D::SquareBase::Down); } + void moveLeft() { _field->moveCursor(KGrid2D::SquareBase::Left); } + void moveRight() { _field->moveCursor(KGrid2D::SquareBase::Right); } + void moveLeftEdge() { _field->moveToEdge(KGrid2D::SquareBase::Left); } + void moveRightEdge() { _field->moveToEdge(KGrid2D::SquareBase::Right); } + void moveTop() { _field->moveToEdge(KGrid2D::SquareBase::Up); } + void moveBottom() { _field->moveToEdge(KGrid2D::SquareBase::Down); } + void reveal() { _field->doReveal(); } + void mark() { _field->doMark(); } + void autoReveal() { _field->keyboardAutoReveal(); } + + void advise(); + void solve(); + void solveRate(); + void addAction(const KGrid2D::Coord &, Field::ActionType type); + + void viewLog(); + void replayLog(); + void saveLog(); + void loadLog(); + + private slots: + void gameStateChangedSlot(GameState state) + { gameStateChanged(state, false); } + void smileyClicked(); + void solvingDone(bool success); + void replayStep(); + + private: + Field *_field; + QWidget *_fieldContainer, *_resumeContainer; + QWidgetStack *_stack; + + Smiley *smiley; + KGameLCD *left; + DigitalClock *dg; + Solver *_solver; + + QDomDocument _log; + QDomElement _logRoot, _logList; + QDomNodeList _actions; + uint _index; + bool _completeReveal; + Level _oldLevel; + QTimer *_timer; + + void setGameOver(bool won); + void setStopped(); + void setPlaying(); + void newGame(const Level &); + void gameStateChanged(GameState, bool won); + static bool checkLog(const QDomDocument &); +}; + +#endif // STATUS_H diff --git a/kmines/version.h b/kmines/version.h new file mode 100644 index 00000000..0e8f3568 --- /dev/null +++ b/kmines/version.h @@ -0,0 +1,5 @@ +#define SHORT_VERSION "2.1.10" +#define LONG_VERSION "2.1.10 (25 Aug 2005)" +#define COPYLEFT "(c) 1996-2005, Nicolas Hadacek\n(c) 2001, Mikhail Kourinny" +#define EMAIL "hadacek@kde.org" +#define HOMEPAGE "http://kmines.sourceforge.net/" diff --git a/knetwalk/AUTHORS b/knetwalk/AUTHORS new file mode 100644 index 00000000..c856c23b --- /dev/null +++ b/knetwalk/AUTHORS @@ -0,0 +1,6 @@ +Original author: +QNetwalk, Copyright (C) 2004, Andi Peredri + +Ported to kde by: +Thomas Nagy +Cell-locking implemented by Reinhold Kainhofer diff --git a/knetwalk/Makefile.am b/knetwalk/Makefile.am new file mode 100644 index 00000000..1bfdcf48 --- /dev/null +++ b/knetwalk/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=src diff --git a/knetwalk/TODO b/knetwalk/TODO new file mode 100644 index 00000000..749f01f3 --- /dev/null +++ b/knetwalk/TODO @@ -0,0 +1,5 @@ +* Fiber lighting should be made more evident (lighted/unlighted contrast) + The contrast between on/off is not blatant +* Connect all your friends so you can talk on the phone together +* Make more obvious that even monitors or the server can change orientation +* Tiles size should be configurable (larger) - use kzoomwindow diff --git a/knetwalk/configure.in.in b/knetwalk/configure.in.in new file mode 100644 index 00000000..2c5f0ffd --- /dev/null +++ b/knetwalk/configure.in.in @@ -0,0 +1,2 @@ + +#MIN_CONFIG(3.3) diff --git a/knetwalk/src/Makefile.am b/knetwalk/src/Makefile.am new file mode 100644 index 00000000..630095f6 --- /dev/null +++ b/knetwalk/src/Makefile.am @@ -0,0 +1,71 @@ +SUBDIRS = pics sounds + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + + +bin_PROGRAMS = knetwalk +knetwalk_LDFLAGS = $(all_libraries) $(KDE_RPATH) +knetwalk_LDADD = $(LIB_KDEGAMES) $(LIB_KDECORE) $(LIB_KDEUI) $(LIB_QT) +knetwalk_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +knetwalk_SOURCES = cell.cpp highscores.cpp main.cpp mainwindow.cpp settings.kcfgc + +xdg_apps_DATA = knetwalk.desktop + +knetwalk_METASOURCES = AUTO +rcdir = $(kde_datadir)/knetwalk +rc_DATA = knetwalkui.rc + +appdatadir = $(kde_datadir)/knetwalk +appdata_DATA = eventsrc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cpp -o $(podir)/knetwalk.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/knetwalk/src/SConscript b/knetwalk/src/SConscript new file mode 100644 index 00000000..3be117e9 --- /dev/null +++ b/knetwalk/src/SConscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +Import('env') +myenv=env.Copy() +knetwalk_sources = [ +'cell.cpp', 'main.cpp', 'mainwindow.cpp', 'settings.kcfgc', 'highscores.cpp' +] +myenv.KDEprogram( "knetwalk", knetwalk_sources) +myenv.KDEaddpaths_includes( ['./', '#./']) +myenv.KDEaddlibs( ['qt-mt', 'kio', 'kdecore', 'kdegames']) +myenv.KDEinstall('KDEDATA', 'knetwalk', 'knetwalkui.rc') +myenv.KDEinstall('KDEDATA', 'knetwalk', 'eventsrc') +myenv.KDEinstall('KDEXDG', '', 'knetwalk.desktop') +myenv.KDEinstall('KDEKCFG', '', 'knetwalk.kcfg') + +soundfiles=""" +sounds/click.wav +sounds/connect.wav +sounds/start.wav +sounds/turn.wav +sounds/win.wav +""".split() + +myenv.KDEinstall('KDEDATA', 'knetwalk/sounds', soundfiles) diff --git a/knetwalk/src/cell.cpp b/knetwalk/src/cell.cpp new file mode 100644 index 00000000..4d561cec --- /dev/null +++ b/knetwalk/src/cell.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "cell.h" + +Cell::PixmapMap Cell::connectedpixmap; +Cell::PixmapMap Cell::disconnectedpixmap; + +void Cell::initPixmaps() +{ + typedef QMap NamesMap; + NamesMap names; + names[L] = "0001"; + names[D] = "0010"; + names[D|L] = "0011"; + names[R] = "0100"; + names[R|L] = "0101"; + names[R|D] = "0110"; + names[R|D|L] = "0111"; + names[U] = "1000"; + names[U|L] = "1001"; + names[U|D] = "1010"; + names[U|D|L] = "1011"; + names[U|R] = "1100"; + names[U|R|L] = "1101"; + names[U|R|D] = "1110"; + + NamesMap::ConstIterator it; + for(it = names.constBegin(); it != names.constEnd(); ++it) + { + connectedpixmap[it.key()]=new QPixmap(KGlobal::iconLoader()->loadIcon( + locate("data","knetwalk/cable"+it.data()+".png"), KIcon::NoGroup, 32) ); + + QImage image = connectedpixmap[it.key()]->convertToImage(); + for(int y = 0; y < image.height(); y++) + { + QRgb* line = (QRgb*)image.scanLine(y); + for(int x = 0; x < image.width(); x++) + { + QRgb pix = line[x]; + if(qAlpha(pix) == 255) + { + int g = (255 + 4 * qGreen(pix)) / 5; + int b = (255 + 4 * qBlue(pix)) / 5; + int r = (255 + 4 * qRed(pix)) / 5; + line[x] = qRgb(r, g, b); + } + } + } + disconnectedpixmap[it.key()] = new QPixmap(image); + } +} + +Cell::Cell(QWidget* parent, int i) : QWidget(parent, 0, WNoAutoErase) +{ + angle = 0; + light = 0; + iindex = i; + ddirs = Free; + changed = true; + connected = false; + root = false; + locked = false; +} + +int Cell::index() const +{ + return iindex; +} + +Cell::Dirs Cell::dirs() const +{ + return ddirs; +} + +bool Cell::isConnected() const +{ + return connected; +} + +bool Cell::isRotated() const +{ + return angle; +} + +bool Cell::isLocked() const +{ + return locked; +} + +void Cell::setLocked( bool newlocked ) +{ + if ( locked == newlocked ) return; + locked = newlocked; + changed = true; + update(); +} + + +void Cell::setDirs(Dirs d) +{ + if(ddirs == d) return; + ddirs = d; + changed = true; + update(); +} + +void Cell::setConnected(bool b) +{ + if(connected == b) return; + connected = b; + changed = true; + update(); +} + +void Cell::setRoot(bool b) +{ + if(root == b) return; + root = b; + changed = true; + update(); +} + +void Cell::setLight(int l) +{ + light = l; + changed = true; + update(); +} + +void Cell::paintEvent(QPaintEvent*) +{ + if(changed) + { + changed = false; + if ( locked ) { + pixmap = KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/background_locked.png"), KIcon::NoGroup, 32); + } else { + pixmap = KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/background.png"), KIcon::NoGroup, 32); + } + + QPainter paint; + paint.begin(&pixmap); + + if(light) + { + paint.setPen(QPen(white, 5)); + paint.drawLine(0, width() - light, width(), 2 * width() - light); + } + + if((ddirs != Free) && (ddirs != None)) + { + double offset = 0; + if(angle) + { + offset = width() / 2; + paint.translate(offset, offset); + paint.rotate(angle); + } + + if(connected) + paint.drawPixmap(int(-offset), int(-offset), *connectedpixmap[ddirs]); + else paint.drawPixmap(int(-offset), int(-offset), *disconnectedpixmap[ddirs]); + paint.resetXForm(); + + QPixmap pix; + + if(root) + { + pix=KGlobal::iconLoader()->loadIcon(locate("data", "knetwalk/server.png"), KIcon::NoGroup, 32); + } + else if(ddirs == U || ddirs == L || ddirs == D || ddirs == R) + { + if(connected) + pix=KGlobal::iconLoader()->loadIcon(locate("data","knetwalk/computer2.png"),KIcon::NoGroup,32); + else + pix=KGlobal::iconLoader()->loadIcon(locate("data","knetwalk/computer1.png"),KIcon::NoGroup,32); + } + paint.drawPixmap(0, 0, pix); + } + paint.end(); + } + bitBlt(this, 0, 0, &pixmap); +} + +void Cell::mousePressEvent(QMouseEvent* e) +{ + if(e->button() == LeftButton) + emit lClicked(iindex); + else if(e->button() == RightButton) + emit rClicked(iindex); + else if(e->button() == MidButton) + emit mClicked(iindex); +} + +void Cell::rotate(int a) +{ + angle += a; + changed = true; + while(angle >= 45) + { + angle -= 90; + int newdirs = Free; + if(ddirs & U) newdirs |= R; + if(ddirs & R) newdirs |= D; + if(ddirs & D) newdirs |= L; + if(ddirs & L) newdirs |= U; + setDirs(Dirs(newdirs)); + } + while(angle < -45) + { + angle += 90; + int newdirs = Free; + if(ddirs & U) newdirs |= L; + if(ddirs & R) newdirs |= U; + if(ddirs & D) newdirs |= R; + if(ddirs & L) newdirs |= D; + setDirs(Dirs(newdirs)); + } + update(); +} + +#include "cell.moc" diff --git a/knetwalk/src/cell.h b/knetwalk/src/cell.h new file mode 100644 index 00000000..2dd5009c --- /dev/null +++ b/knetwalk/src/cell.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef CELL_H +#define CELL_H + +#include +#include + +class Cell : public QWidget +{ + Q_OBJECT + public: + enum Dirs { Free = 0, U = 1, R = 2, D = 4, L = 8, None = 16 }; + Cell(QWidget* parent, int i); + int index() const; + void rotate(int a); + void setDirs(Dirs d); + void setRoot(bool b); + void setLight(int l); + void setConnected(bool b); + void setLocked( bool newlocked = true ); + bool isConnected() const; + bool isRotated() const; + bool isLocked() const; + Dirs dirs() const; + static void initPixmaps(); + signals: + void lClicked(int); + void rClicked(int); + void mClicked(int); + protected: + virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent*); + private: + typedef QMap PixmapMap; + static PixmapMap connectedpixmap; + static PixmapMap disconnectedpixmap; + int angle; + int light; + int iindex; + bool connected; + bool changed; + bool root; + bool locked; + Dirs ddirs; + QPixmap pixmap; +}; + +#endif diff --git a/knetwalk/src/defines.h b/knetwalk/src/defines.h new file mode 100644 index 00000000..ff1f122f --- /dev/null +++ b/knetwalk/src/defines.h @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright (C) 2005 Thomas Nagy * + * tnagyemail-mail@yahoo.fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef _DEFINES_H +#define _DEFINES_H + +const char *levels[4] = { +I18N_NOOP("Novice"), +I18N_NOOP("Normal"), +I18N_NOOP("Expert"), +I18N_NOOP("Master") +}; + +#endif // _DEFINES_H diff --git a/knetwalk/src/eventsrc b/knetwalk/src/eventsrc new file mode 100644 index 00000000..fbbd28c3 --- /dev/null +++ b/knetwalk/src/eventsrc @@ -0,0 +1,258 @@ +[!Global!] +IconName=knetwalk +Comment=knetwalk +Comment[bn]=কে-নেটওয়াক +Comment[fi]=Knetwalk +Comment[hu]=KNetWalk +Comment[sv]=Knetwalk + +[clicksound] +Name=Click +Name[be]=ПÑтрычка +Name[bg]=Щракване +Name[bn]=কà§à¦²à¦¿à¦• +Name[br]=Klik +Name[bs]=Klikni +Name[ca]=Clic +Name[cs]=Kliknutí +Name[cy]=Clicio +Name[da]=Klik +Name[de]=Klick +Name[el]=Κλικ +Name[eo]=Kliki +Name[es]=Clic +Name[et]=Klõps +Name[eu]=Klikatu +Name[fa]=Ùشار +Name[fi]=Napsauta +Name[fr]=Clic +Name[ga]=Cliceáil +Name[he]=לחיצה +Name[hr]=Klik +Name[hu]=Kattintás +Name[is]=Smella +Name[it]=Clic +Name[ja]=クリック +Name[km]=ចុច +Name[ko]=í´ë¦­ +Name[lt]=Paspausti +Name[lv]=KlikÅ¡Ä·is +Name[mk]=Кликање +Name[nb]=Klikk +Name[nds]=Klick +Name[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Klik +Name[nn]=Klikk +Name[pa]=ਦਬਾਓ +Name[pl]=Dobierz +Name[pt]=Carregar +Name[pt_BR]=Clicar +Name[ru]=Щелчок +Name[se]=Coahkkal +Name[sk]=Kliknutie +Name[sl]=Klik +Name[sr]=Кликни +Name[sr@Latn]=Klikni +Name[sv]=Klick +Name[ta]=சொடà¯à®•à¯à®•à¯ +Name[tg]=Ðнгуштзанӣ +Name[tr]=Tık +Name[uk]=Клац +Name[zh_CN]=å•å‡» +Name[zh_TW]=é»žé¸ +default_sound=click.wav +default_presentation=0 + +[connectsound] +Name=Connect +Name[be]=ЗлучÑнне +Name[bg]=Връзка +Name[bn]=সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করো +Name[br]=Kevreañ +Name[bs]=Spoji se +Name[cs]=Propojení +Name[da]=Forbind +Name[de]=Verbinden +Name[el]=ΣÏνδεση +Name[eo]=Konekti +Name[et]=Ãœhendus +Name[eu]=Konektatu +Name[fa]=اتصال +Name[fi]=Yhdistä +Name[fr]=Connecter +Name[ga]=Nasc +Name[he]=התחבר +Name[hr]=Poveži +Name[hu]=Csatlakozás +Name[is]=Tengjast +Name[it]=Connetti +Name[km]=ážâ€‹áž—្ជាប់ +Name[ko]=ì—°ê²° +Name[lv]=PieslÄ“gties +Name[mk]=Поврзување +Name[nds]=Tokoppeln +Name[ne]=जडान गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Verbinden +Name[pa]=ਜà©à©œà©‹ +Name[pl]=PoÅ‚Ä…cz +Name[pt]=Ligar +Name[pt_BR]=Conectar +Name[ru]=Подключение +Name[sl]=Povezava +Name[sr]=Повежи Ñе +Name[sr@Latn]=Poveži se +Name[sv]=Anslut +Name[uk]=З'єднати +Name[wa]=Raloyî +Name[zh_TW]=é€£çµ +default_sound=connect.wav +default_presentation=0 + +[winsound] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +default_sound=win.wav +default_presentation=1 + +[startsound] +Name=Connect +Name[be]=ЗлучÑнне +Name[bg]=Връзка +Name[bn]=সংযোগ সà§à¦¥à¦¾à¦ªà¦¨ করো +Name[br]=Kevreañ +Name[bs]=Spoji se +Name[cs]=Propojení +Name[da]=Forbind +Name[de]=Verbinden +Name[el]=ΣÏνδεση +Name[eo]=Konekti +Name[et]=Ãœhendus +Name[eu]=Konektatu +Name[fa]=اتصال +Name[fi]=Yhdistä +Name[fr]=Connecter +Name[ga]=Nasc +Name[he]=התחבר +Name[hr]=Poveži +Name[hu]=Csatlakozás +Name[is]=Tengjast +Name[it]=Connetti +Name[km]=ážâ€‹áž—្ជាប់ +Name[ko]=ì—°ê²° +Name[lv]=PieslÄ“gties +Name[mk]=Поврзување +Name[nds]=Tokoppeln +Name[ne]=जडान गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Verbinden +Name[pa]=ਜà©à©œà©‹ +Name[pl]=PoÅ‚Ä…cz +Name[pt]=Ligar +Name[pt_BR]=Conectar +Name[ru]=Подключение +Name[sl]=Povezava +Name[sr]=Повежи Ñе +Name[sr@Latn]=Poveži se +Name[sv]=Anslut +Name[uk]=З'єднати +Name[wa]=Raloyî +Name[zh_TW]=é€£çµ +default_sound=start.wav +default_presentation=1 + +[turnsound] +Name=Turn +Name[be]=Ход +Name[bg]=Ход +Name[bn]=ঘোরো +Name[bs]=Okreni +Name[cs]=Tah +Name[de]=Runde +Name[el]=ΓÏÏος +Name[eo]=Turni +Name[et]=Käik +Name[eu]=Biratu +Name[fa]=چرخش +Name[fi]=Käännä +Name[fr]=Tourner +Name[he]=סיבוב +Name[hr]=Potez +Name[hu]=Lépés +Name[is]=Beygja +Name[it]=Turno +Name[km]=ážœáŸáž“ +Name[ko]=회전 +Name[lt]=Ä–jimas +Name[lv]=GÄjiens +Name[mk]=Вртење +Name[nds]=Törn +Name[ne]=घà¥à¤®à¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Draaien +Name[pa]=ਵਾਰੀ +Name[pl]=Tura +Name[pt]=Jogada +Name[pt_BR]=Virar +Name[ru]=Поворот +Name[sl]=Poteza +Name[sr]=Круг +Name[sr@Latn]=Krug +Name[sv]=Vänd +Name[uk]=Хід +Name[wa]=Toû +default_sound=turn.wav +default_presentation=0 + diff --git a/knetwalk/src/highscores.cpp b/knetwalk/src/highscores.cpp new file mode 100644 index 00000000..28742662 --- /dev/null +++ b/knetwalk/src/highscores.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2005 Thomas Nagy * + * tnagyemail-mail@yahoo.fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "defines.h" +#include "highscores.h" +#include "settings.h" + +namespace KExtHighscore +{ + ExtManager::ExtManager() : Manager(4) + { + setScoreType(Normal); + /* + setWWHighscores(KURL( HOMEPAGE ), VERSION); + setShowStatistics(true); + */ + const uint RANGE[16] = { 0, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160 }; + QMemArray s; + s.duplicate(RANGE, 16); + setScoreHistogram(s, ScoreNotBound); + //Item *item = new Item((uint)0, i18n("Clicks"), Qt::AlignRight); + //addScoreItem("nb_actions", item); + } + + QString ExtManager::gameTypeLabel(uint gameType, LabelType /*type*/) const + { + /*const Level::Data &data = Level::DATA[gameType]; + switch (type) { + case Icon: + case Standard: return data.label; + case I18N: return i18n(level[gameType]); + case WW: return data.wwLabel; + } + return QString::null;*/ + return i18n(levels[gameType]); + } + + void ExtManager::convertLegacy(uint gameType) + { + QString group; + switch (gameType) + { + case Settings::EnumSkill::Novice: group = "Novice level"; break; + case Settings::EnumSkill::Normal: group = "Normal level"; break; + case Settings::EnumSkill::Expert: group = "Expert level"; break; + case Settings::EnumSkill::Master: group = "Master level"; break; + default: Q_ASSERT(false); + } + + KConfigGroupSaver cg(kapp->config(), group); + QString name = cg.config()->readEntry("Name", QString::null); + if ( name.isNull() ) return; + if ( name.isEmpty() ) name = i18n("anonymous"); + int score = cg.config()->readNumEntry("score", 0); + if ( score<=0 ) return; + Score s(Won); + s.setScore(score); + s.setData("name", name); + submitLegacyScore(s); + } + + bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const + { + if ( s1.score()==s2.score() ) + // when time is same, favour more clicks (it means auto-reveal + // didn't help so much): + return true; //s1.data("nb_actions").toUInt() +#include + +namespace KExtHighscore +{ + + class KDE_EXPORT ExtManager : public Manager + { + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + bool isStrictlyLess(const Score &s1, const Score &s2) const; + }; +} + +#endif diff --git a/knetwalk/src/knetwalk.desktop b/knetwalk/src/knetwalk.desktop new file mode 100644 index 00000000..6db50658 --- /dev/null +++ b/knetwalk/src/knetwalk.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Type=Application +Exec=knetwalk -caption "%c" %i %m +DocPath=knetwalk/index.html +Name=knetwalk +Name[bn]=কে-নেটওয়াক +Name[fi]=Knetwalk +Name[hu]=KNetWalk +Name[pa]=ਕੇ-ਨੈੱਟਵਾਕ +Name[sv]=Knetwalk +Terminal=false +Icon=knetwalk +X-KDE-StartupNotify=true +Categories=Qt;KDE;Game;StrategyGame; diff --git a/knetwalk/src/knetwalk.kcfg b/knetwalk/src/knetwalk.kcfg new file mode 100644 index 00000000..8469b0fb --- /dev/null +++ b/knetwalk/src/knetwalk.kcfg @@ -0,0 +1,23 @@ + + + + + + + + + + + Novice + + + + + + + + + diff --git a/knetwalk/src/knetwalkui.rc b/knetwalk/src/knetwalkui.rc new file mode 100644 index 00000000..fcb44181 --- /dev/null +++ b/knetwalk/src/knetwalkui.rc @@ -0,0 +1,9 @@ + + + + +Main Toolbar + + + + diff --git a/knetwalk/src/main.cpp b/knetwalk/src/main.cpp new file mode 100644 index 00000000..72aaa4f3 --- /dev/null +++ b/knetwalk/src/main.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2005, Thomas Nagy * + * tnagyemail-mail@yahoo@fr * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "highscores.h" +#include "settings.h" +#include "mainwindow.h" + +static const char description[] = +I18N_NOOP("KNetWalk, a game for system administrators."); + +static const char version[] = "1.0"; + +static KCmdLineOptions options[] = +{ + { "Novice", I18N_NOOP( "Start in novice mode" ), 0 }, + { "Normal", I18N_NOOP( "Start in normal mode" ), 0 }, + { "Expert", I18N_NOOP( "Start in expert mode" ), 0 }, + { "Master", I18N_NOOP( "Start in master mode" ), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char ** argv) +{ + KAboutData about("knetwalk", I18N_NOOP("knetwalk"), version, description, + KAboutData::License_GPL, I18N_NOOP("(C) 2004, 2005 Andi Peredri, ported to KDE by Thomas Nagy"), 0, + "tnagyemail-mail@yahoo.fr"); + about.addAuthor( "Andi Peredri", 0, "andi@ukr.net" ); + about.addAuthor( "Thomas Nagy", 0, "tnagy2^8@yahoo.fr" ); + + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + + KApplication app; + + KGlobal::locale()->insertCatalogue("libkdegames"); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("Novice")) Settings::setSkill(Settings::EnumSkill::Novice); + if (args->isSet("Normal")) Settings::setSkill(Settings::EnumSkill::Normal); + if (args->isSet("Expert")) Settings::setSkill(Settings::EnumSkill::Expert); + if (args->isSet("Master")) Settings::setSkill(Settings::EnumSkill::Master); + args->clear(); + + KHighscore::init("knetwalk"); + KExtHighscore::ExtManager manager; + + + MainWindow* wi = new MainWindow; + app.setMainWidget(wi); + wi->show(); + + return app.exec(); +} + diff --git a/knetwalk/src/mainwindow.cpp b/knetwalk/src/mainwindow.cpp new file mode 100644 index 00000000..15e615ed --- /dev/null +++ b/knetwalk/src/mainwindow.cpp @@ -0,0 +1,421 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "cell.h" +#include "mainwindow.h" + +static QMap contrdirs; + +MainWindow::MainWindow(QWidget *parent, const char* name, WFlags /*fl*/) : + KMainWindow(parent, name, WStyle_NoBorder) +{ + m_clickcount = 0; + + contrdirs[Cell::U] = Cell::D; + contrdirs[Cell::R] = Cell::L; + contrdirs[Cell::D] = Cell::U; + contrdirs[Cell::L] = Cell::R; + + KNotifyClient::startDaemon(); + + KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection()); + + KStdGameAction::highscores(this, SLOT(showHighscores()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::configureHighscores(this, SLOT(configureHighscores()), actionCollection()); + + m_levels = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList lst; + lst += i18n("Novice"); + lst += i18n("Normal"); + lst += i18n("Expert"); + lst += i18n("Master"); + m_levels->setItems(lst); + + setFixedSize(minimumSizeHint()); + + statusBar()->insertItem("abcdefghijklmnopqrst: 0 ",1); + setAutoSaveSettings(); + createGUI(); + connect(m_levels, SIGNAL(activated(int)), this, SLOT(newGame(int))); + + + QWhatsThis::add(this, i18n("

    Rules of the Game

    " + "

    You are the system administrator and your goal" + " is to connect each computer to the central server." + "

    Click the right mouse button to turn the cable" + " in a clockwise direction, and the left mouse button" + " to turn it in a counter-clockwise direction." + "

    Start the LAN with as few turns as possible!")); + + //const int cellsize = KGlobal::iconLoader()->loadIcon("knetwalk/background.png", KIcon::User, 32).width(); + const int cellsize = 32; + const int gridsize = cellsize * MasterBoardSize + 2; + + QGrid* grid = new QGrid(MasterBoardSize, this); + grid->setFrameStyle(QFrame::Panel | QFrame::Sunken); + grid->setFixedSize(gridsize, gridsize); + setCentralWidget(grid); + + Cell::initPixmaps(); + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + board[i] = new Cell(grid, i); + board[i]->setFixedSize(cellsize, cellsize); + connect(board[i], SIGNAL(lClicked(int)), SLOT(lClicked(int))); + connect(board[i], SIGNAL(rClicked(int)), SLOT(rClicked(int))); + connect(board[i], SIGNAL(mClicked(int)), SLOT(mClicked(int))); + } + srand(time(0)); + + slotNewGame(); +} + +void MainWindow::configureHighscores() +{ + KExtHighscore::configure(this); +} + +void MainWindow::showHighscores() +{ + KExtHighscore::show(this); +} + +void MainWindow::slotNewGame() +{ + newGame( Settings::skill() ); +} + +void MainWindow::newGame(int sk) +{ + if (sk==Settings::EnumSkill::Novice || sk==Settings::EnumSkill::Normal + || sk==Settings::EnumSkill::Expert || sk==Settings::EnumSkill::Master) + { + Settings::setSkill(sk); + } + + if(Settings::skill() == Settings::EnumSkill::Master) wrapped = true; + else wrapped = false; + + KExtHighscore::setGameType(Settings::skill()); + + Settings::writeConfig(); + + m_clickcount = 0; + QString clicks = i18n("Click: %1"); + statusBar()->changeItem(clicks.arg(QString::number(m_clickcount)),1); + + KNotifyClient::event(winId(), "startsound", i18n("New Game")); + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + board[i]->setDirs(Cell::None); + board[i]->setConnected(false); + board[i]->setRoot(false); + board[i]->setLocked(false); + } + + const int size = (Settings::skill() == Settings::EnumSkill::Novice) ? NoviceBoardSize : + (Settings::skill() == Settings::EnumSkill::Normal) ? NormalBoardSize : + (Settings::skill() == Settings::EnumSkill::Expert) ? ExpertBoardSize : MasterBoardSize; + + const int start = (MasterBoardSize - size) / 2; + const int rootrow = rand() % size; + const int rootcol = rand() % size; + + root = board[(start + rootrow) * MasterBoardSize + start + rootcol]; + root->setConnected(true); + root->setRoot(true); + + while(true) + { + for(int row = start; row < start + size; row++) + for(int col = start; col < start + size; col++) + board[row * MasterBoardSize + col]->setDirs(Cell::Free); + + CellList list; + list.append(root); + if(rand() % 2) addRandomDir(list); + + while(!list.isEmpty()) + { + if(rand() % 2) + { + addRandomDir(list); + if(rand() % 2) addRandomDir(list); + list.remove(list.begin()); + } + else + { + list.append(list.first()); + list.remove(list.begin()); + } + } + + int cells = 0; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + Cell::Dirs d = board[i]->dirs(); + if((d != Cell::Free) && (d != Cell::None)) cells++; + } + if(cells >= MinimumNumCells) break; + } + + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + board[i]->rotate((rand() % 4) * 90); + updateConnections(); +} + +bool MainWindow::updateConnections() +{ + bool newconnection[MasterBoardSize * MasterBoardSize]; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + newconnection[i] = false; + + CellList list; + if(!root->isRotated()) + { + newconnection[root->index()] = true; + list.append(root); + } + while(!list.isEmpty()) + { + Cell* cell = list.first(); + Cell* ucell = uCell(cell); + Cell* rcell = rCell(cell); + Cell* dcell = dCell(cell); + Cell* lcell = lCell(cell); + + if((cell->dirs() & Cell::U) && ucell && (ucell->dirs() & Cell::D) && + !newconnection[ucell->index()] && !ucell->isRotated()) + { + newconnection[ucell->index()] = true; + list.append(ucell); + } + if((cell->dirs() & Cell::R) && rcell && (rcell->dirs() & Cell::L) && + !newconnection[rcell->index()] && !rcell->isRotated()) + { + newconnection[rcell->index()] = true; + list.append(rcell); + } + if((cell->dirs() & Cell::D) && dcell && (dcell->dirs() & Cell::U) && + !newconnection[dcell->index()] && !dcell->isRotated()) + { + newconnection[dcell->index()] = true; + list.append(dcell); + } + if((cell->dirs() & Cell::L) && lcell && (lcell->dirs() & Cell::R) && + !newconnection[lcell->index()] && !lcell->isRotated()) + { + newconnection[lcell->index()] = true; + list.append(lcell); + } + list.remove(list.begin()); + } + + bool isnewconnection = false; + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + if(!board[i]->isConnected() && newconnection[i]) + isnewconnection = true; + board[i]->setConnected(newconnection[i]); + } + return isnewconnection; +} + +void MainWindow::addRandomDir(CellList& list) +{ + Cell* cell = list.first(); + Cell* ucell = uCell(cell); + Cell* rcell = rCell(cell); + Cell* dcell = dCell(cell); + Cell* lcell = lCell(cell); + + typedef QMap CellMap; + CellMap freecells; + + if(ucell && ucell->dirs() == Cell::Free) freecells[Cell::U] = ucell; + if(rcell && rcell->dirs() == Cell::Free) freecells[Cell::R] = rcell; + if(dcell && dcell->dirs() == Cell::Free) freecells[Cell::D] = dcell; + if(lcell && lcell->dirs() == Cell::Free) freecells[Cell::L] = lcell; + if(freecells.isEmpty()) return; + + CellMap::ConstIterator it = freecells.constBegin(); + for(int i = rand() % freecells.count(); i > 0; --i) ++it; + + cell->setDirs(Cell::Dirs(cell->dirs() | it.key())); + it.data()->setDirs(contrdirs[it.key()]); + list.append(it.data()); +} + +Cell* MainWindow::uCell(Cell* cell) const +{ + if(cell->index() >= MasterBoardSize) + return board[cell->index() - MasterBoardSize]; + else if(wrapped) + return board[MasterBoardSize * (MasterBoardSize - 1) + cell->index()]; + else return 0; +} + +Cell* MainWindow::dCell(Cell* cell) const +{ + if(cell->index() < MasterBoardSize * (MasterBoardSize - 1)) + return board[cell->index() + MasterBoardSize]; + else if(wrapped) + return board[cell->index() - MasterBoardSize * (MasterBoardSize - 1)]; + else return 0; +} + +Cell* MainWindow::lCell(Cell* cell) const +{ + if(cell->index() % MasterBoardSize > 0) + return board[cell->index() - 1]; + else if(wrapped) + return board[cell->index() - 1 + MasterBoardSize]; + else return 0; +} + +Cell* MainWindow::rCell(Cell* cell) const +{ + if(cell->index() % MasterBoardSize < MasterBoardSize - 1) + return board[cell->index() + 1]; + else if(wrapped) + return board[cell->index() + 1 - MasterBoardSize]; + else return 0; +} + +void MainWindow::lClicked(int index) +{ + rotate(index, true); +} + +void MainWindow::rClicked(int index) +{ + rotate(index, false); +} + +void MainWindow::mClicked(int index) +{ + board[index]->setLocked( !board[index]->isLocked() ); +} + +void MainWindow::rotate(int index, bool toleft) +{ + const Cell::Dirs d = board[index]->dirs(); + if((d == Cell::Free) || (d == Cell::None) || isGameOver() || board[index]->isLocked() ) + { + KNotifyClient::event(winId(), "clicksound"); + blink(index); + } + else + { + KNotifyClient::event(winId(), "turnsound"); + board[index]->rotate(toleft ? -6 : 6); + updateConnections(); + for(int i = 0; i < 14; i++) + { + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + QTimer::singleShot(20, board[index], SLOT(update())); + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | QEventLoop::WaitForMore); + board[index]->rotate(toleft ? -6 : 6); + } + + if (updateConnections()) + KNotifyClient::event(winId(), "connectsound"); + + m_clickcount++; + QString clicks = i18n("Click: %1"); + statusBar()->changeItem(clicks.arg(QString::number(m_clickcount)),1); + + if (isGameOver()) + { + KNotifyClient::event(winId(), "winsound"); + blink(index); + + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(m_clickcount); + KExtHighscore::submitScore(score, this); + } + } +} + +void MainWindow::blink(int index) +{ + for(int i = 0; i < board[index]->width() * 2; i += 2) + { + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput); + QTimer::singleShot(20, board[index], SLOT(update())); + kapp->eventLoop()->processEvents(QEventLoop::ExcludeUserInput | + QEventLoop::WaitForMore); + board[index]->setLight(i); + } + board[index]->setLight(0); +} + +bool MainWindow::isGameOver() +{ + for(int i = 0; i < MasterBoardSize * MasterBoardSize; i++) + { + const Cell::Dirs d = board[i]->dirs(); + if((d != Cell::Free) && (d != Cell::None) && !board[i]->isConnected()) + return false; + } + return true; +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + event->accept(); +} + +void MainWindow::configureNotifications() +{ + KNotifyDialog::configure(this); +} + +#include "mainwindow.moc" diff --git a/knetwalk/src/mainwindow.h b/knetwalk/src/mainwindow.h new file mode 100644 index 00000000..c28b6ed6 --- /dev/null +++ b/knetwalk/src/mainwindow.h @@ -0,0 +1,96 @@ +/*************************************************************************** + * Copyright (C) 2004, 2005 Andi Peredri * + * andi@ukr.net * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License version 2 * + * as published by the Free Software Foundation (see COPYING) * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + ***************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +class Cell; +class QAction; +class QSound; +class QLCDNumber; +class KSelectAction; + +class MainWindow : public KMainWindow +{ + Q_OBJECT + public: + MainWindow(QWidget *parent=0, const char* name=0, WFlags fl=0); + protected: + virtual void closeEvent(QCloseEvent*); + private: + //enum Skill { Novice, Normal, Expert, Master }; + enum BoardSize + { + NoviceBoardSize = 5, + NormalBoardSize = 7, + ExpertBoardSize = 9, + MasterBoardSize = 9 + }; + enum + { + NumHighscores = 10, + MinimumNumCells = 20 + }; + typedef QValueList CellList; + public slots: + void slotNewGame(); + void newGame(int); + + void lClicked(int index); + void rClicked(int index); + void mClicked(int index); + + void showHighscores(); + void configureHighscores(); + void configureNotifications(); + + private: + Cell* uCell(Cell* cell) const; + Cell* dCell(Cell* cell) const; + Cell* lCell(Cell* cell) const; + Cell* rCell(Cell* cell) const; + bool isGameOver(); + bool startBrowser(const QString& url); + bool updateConnections(); + void blink(int index); + void rotate(int index, bool toleft); + void addRandomDir(CellList& list); + void dialog(const QString& caption, const QString& text); + private: + bool wrapped; + Cell* root; + Cell* board[MasterBoardSize * MasterBoardSize]; + + QSound* clicksound; + QSound* connectsound; + QSound* startsound; + QSound* turnsound; + QSound* winsound; + + QString username; + QString soundpath; + QAction* soundaction; + QStringList highscores; + QLCDNumber* lcd; + QPopupMenu* gamemenu; + QPopupMenu* skillmenu; + + int m_clickcount; + KSelectAction* m_levels; +}; + +#endif // MAINWINDOW_H diff --git a/knetwalk/src/pics/Makefile.am b/knetwalk/src/pics/Makefile.am new file mode 100644 index 00000000..2eb67856 --- /dev/null +++ b/knetwalk/src/pics/Makefile.am @@ -0,0 +1,9 @@ + +pics_DATA = background.png background_locked.png cable0001.png cable0010.png cable0011.png cable0100.png cable0101.png cable0110.png cable0111.png cable1000.png cable1001.png cable1010.png cable1011.png cable1100.png cable1101.png cable1110.png computer1.png computer2.png server.png + +picsdir = $(kde_datadir)/knetwalk/ + +KDE_ICON = knetwalk + +EXTRA_DIST = $(pics_DATA) + diff --git a/knetwalk/src/pics/SConscript b/knetwalk/src/pics/SConscript new file mode 100644 index 00000000..2a9b5e7c --- /dev/null +++ b/knetwalk/src/pics/SConscript @@ -0,0 +1,31 @@ +#! /usr/bin/env python +Import( '*' ) +myenv=env.Copy() + +files=[ +'background.png', +'cable0001.png', +'cable0010.png', +'cable0011.png', +'cable0100.png', +'cable0101.png', +'cable0110.png', +'cable0111.png', +'cable1000.png', +'cable1001.png', +'cable1010.png', +'cable1011.png', +'cable1100.png', +'cable1101.png', +'cable1110.png', +'computer1.png', +'computer2.png', +'highscores.png', +'homepage.png', +'newgame.png', +'quit.png', +'server.png', +] + +myenv.KDEinstall('KDEDATA', 'knetwalk', files) +myenv.KDEicon('knetwalk') diff --git a/knetwalk/src/pics/background.png b/knetwalk/src/pics/background.png new file mode 100644 index 0000000000000000000000000000000000000000..d1484f5474675e19e023d738477f591012fe0ce4 GIT binary patch literal 1749 zcmV;`1}gc9P)+quoD02owrxXI`1-CkCGa`^u z=6?>lbJwr%uoSnuSL$6D^(?;q@s9Z^Aq9}93Cw;((~0udoXh;ZB;GlxZ3 z*A;gMf^EAXB8UjRcXG;DF;taOgi;E(+dDaBA_6y`o9kR-7T|6OLP~;`e9oQ#)zMC& z<7T)!$FUOuId|4|W#4y}Wg#3q3S+Dg;an~+Y}<-z!XwVs_tpo_o?uC7{E#$}^IQZ- zNzb;M8TB|=mX%+A`I%)|=)Ggs$yvF)T!?_j<1y#IQ%a$f!pFx4wN_edq@={e*9lE? zqqqLq&QcHw_I)QJSeAvvC~jLfTI)O>j}zUsc9vxsr|X?k3K4;-(psmrhR6W?bDtkD zAR>q=DP?Ls==*r6l!Az0W-QAxHt&sN-#KcXSP72fpp=E{^#xUBDFsnwDT(*no$K|A znUT@}?KX?egYOB|Ov%c!te82fdQ$1B2_i~um3`mGCd&c@DuSwV?2n1=jt684N9}xk z+_-EP0w|@hl!B)3Xjp5`3T?v9xmNA0fsQ3H-u|O0EtQ*_5ovjk&?1sBfkWQvd(Q-37LAS=w zdZE^XqzTor=CW>-vP?P~;iHJ}KsbcKCe3m#5aVP^SupF=+JT^(;SuDVPX!G?+#!NL z|NKVDg}1kV(VOx5`iA=eW^0WIC#6g&<&%avB?#!P;qLU_=r%b1`ZmDRru?RqhCkGL zDxe4haFdol{q!@cLY>c?pVp?@_LKIb10o>c5y)i4wr&7?`}WSh@8mr6K&MBzKfPm! zj)<+Ff@fEf3riEZ0Jgx>9(m6_wdpN1qd3MzSE zVwlqK0kv2ZsUPmC?z8j^xn@RMRl+t=M$h$ zRjsX3O5y(T&bD57?1RdslmWsiP@gO?LsMd1R%9xdnN5MxS>T|mj2W@6>#26VcUnDI z)&+N?)=HUj?$g>u1QmtB)*tP70B$4a%)amZ{r6uilQ!;^oDwM|wrx4ln^Kx|8pWnO z6!UhwVde-*DTUf9OW6qI9L;kSX8~pnk!2bMh-6d&9~&LV!6JogDb&_x=umPlSnnLQ z^7i(MNFrRPtt;#LLc~s*fhOGN{EeLTlZG!Zms55l29T_G+-;tG<#KtUn@uhcTXlE3 z8966Jbh@BVSzlR}jc~ATn00(+i-8&YUI~NnezM_lzhh>s+s4bw%XBh1@6R^wj|bg4 z*Xxy956p&$sg7s6*>F`puP2So`ey;ELd2*vBDh{(5r%r*?{|7{l(I}}jn4Eo=C4Un zP3-$lYaP{$`(V$dlp)zuVF4s55&cIN2+F!GxEY9ybQ8hH{bN$CoYH&0-w`-jux=ac zx}J6{q8!JLyHj$(V~lBJNq?j8XPFY4* zj^jW@*!P`Uk0DGcKuVN6JUY#)pIm(wq%$u@3^`tw6?Y?i@LuLRni3)-A|j~G@DOs!lNk$ULe7ca9WyXLfbot&*la73 literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/background_locked.png b/knetwalk/src/pics/background_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..323f91843f7c86acca5a59053a04d49a8ff9cbd2 GIT binary patch literal 1847 zcmV-72gvw|P)YGr&Lq1zHnx!L?#_0)caU z-GsP8&6L_`*53jF?#%reu~641Io>x>+EMw#KtzQz0f9CYZV0fV61@p}-)6rc zLuo>Y#uah_t>9;}M6@%jAL;x+`W+ER!ifP}pd#9P6bG_)<6sQv$?M?B`27Idrs0WV z5T<}oU>b?1eoT$1Y8QX}4q_+(OC~!2) z1RQZ*Q3=Hkg|HK8N3D;v=>_EzOJ3+*q)q4<^N95+tvB$VY!fik^gZs6gHKGr6}W$G zAiYDL2?F#$Q79TnRGn^~V0GH`M6Jm;Y6m?)7n?%THrQ=CDC);ppfxt@L`;$iSU z?vO=pXmtRu7hE-9$qVcCNb7e<6V4g+Pw=MOR^Je9CfqU0n7KNN8I$nYXSC-hSWb9) zM=c*{afd00`!N=sQGI+RVd%O^qfxoi`~4{H1<8~<5Qrq`pqW)O6&rDDL0piY5Dn)E zwE=g;C|vp_<^5=&3hMldKvf_mX2q4j4w<10-O>9QaUoQ!xgt(T^JdwF1(6HtrXT-G zX~z9mXuXWWh|n20gIBB>Z4Mg4h$bj-Ua%AUg0-MHaOTUw>O4~g5goChJb|y+Nd&k} zDDH4AWcw`&A-@nh6B-8~&zlUdH~tR}s3C#N=x1x@0KiUv!TADBm^O$Srnda34N;=u zGNl#v;L|s-HqZ{<+M{-$wY!ZIf!GayXa_Pr zVc`H2xr*cy%mu}c@)>zzT~>^@x%Vx^;*AP?MKKMzs3A^+ES1B((%8#}5Llvk;F_ON z>=6syLsl&=)Ox-d>@YA5U#TEkCp7XNd1J+j3$0(VFAxPxFft9)$@NGv<2=yX z3)GR`8`I0dHqJ!?Sp0VdGGZrEe#R7`g2Kw}X38?dkVm-k65r=_2 z(|kwz9q595-uN;RTCo>;?<_SD{J^q|$aTIabzt&XR@Ca2sDXgxjSOv67wm#+z(=aq zK{9=T{k%Eh30vE~04yJ|7Tg}0^b^Z9L3={rI=+(FuX}GOhKziDGXjZ{@R~S``lmFr zUGyE|f_1kQ&T*nD(hG`)JyH1tthirBuauF!34c$STp=^g069iHg|f za=o#(RO3k~*X`m?Rc#yZ9#>L%rL}4M3}oF_JXw@G$*0IUQZVLiH_SJ2924>mbB8<) z2&E0i(NIKe`&nnIyvYY@D(Yri_CWeu^R%l+`QSG zs0u0zJ4e>v3_(4gA|5GyzRf*|Hde0k8DeJfZ)ov?`~p5>H_RPb)7XxH{?#8& l{Pe@0|3?qtfBLUi{2RkzCT+sl4;=si002ovPDHLkV1lJNS>*r# literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable0001.png b/knetwalk/src/pics/cable0001.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d598755f11fb6e7c6eabb109e2f913fdff131d GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ET8$-*IFD0Tlt3Q%aBr;B4q#hkZO9J!nn1svulh_9$M zU-7wO@w!Kiz6&4|r6h@(+2L>A&&XGckXmufj16(Hl(a30F7Y zXcJh%q@+_2apJnjLynk}%lA*Xzu)_ThJeSDN75(eUWmECcB6#LCwIn#EsCe-2dq61 g;}$65n)!^?>yjS(ffX0Zfi7q8boFyt=akR{0OQqgzW@LL literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable0010.png b/knetwalk/src/pics/cable0010.png new file mode 100644 index 0000000000000000000000000000000000000000..e86490787f30628a3be9bfb1baa776c9cf9fea31 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ET8$-*JY5?XExGGK$Ji(^Q|oVV9(`3@QII6UMJNk~Xc ze#%rZPi}Ye`2$A|9O~C#UfUHg@z#pi1Nog(G`@8|)llSUQsDSE_a?{h*%DWeTdX`F zBRMVNO0juN;qk@!3v?8J@alz$L%feNkbuz8)`^miRYt;Kw%Q!Af z`#V?GEdH9C_zSCtyZr1{tPf({cU0=mm1M39ai>pKCzv=XusA(f&Z4zGeTUHl{|Q<>&kwGlZ8W^Ph0WlGzJDnZci7-kcv5Pr#X5ZF%WTxR}pK8 z80UB~QA{tzZEg3mgr#r49A9U`@0B_0&wr0u z^PSWtpG^5@{p|MLo>ChJ+4U-omm9yW^t|6FyMf*ADwEQrlzWf%#xtEiz*fO*XY#ne zZnBJn%ezMH2TT?hkC~)Ku1Y^(dA)MosxE;;Tonv*nbT5&Vx*leJbUkPmQrOp6}U$JO6%M*YPpr_D!Z+rWy^O87)h7 zHy-dle_*3Sa&S&SSHz`NH~enaPncpgA*SrP(2S`%6IlOEaq#`@yy(`ll}xpqae;H1 abmMs$?an>^_p%WfQVgE1elF{r5}E*b?VJk$ literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable0100.png b/knetwalk/src/pics/cable0100.png new file mode 100644 index 0000000000000000000000000000000000000000..6a6d277db6e691ffd68083562bff7da8f07b72fd GIT binary patch literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ET8$-*J39VF}@3>2E`>EaktG3V_SL%u@>0xlQDwI?Wv znm=H=qwenSf0R)}NyW)gMMFa;-|Cz9w5NwQC$B%Q;yFp>zu)%7ev`wlI5_qz^Tk)0 z?$W+y7qG|K-uJT5g@7iuHjl!4*TrwypD(`RY|N@2tsm|FL{O~9;_;@li+{P;6!vdP zINEf3+rD2a3$GnWUN6BXmAvD)`<>&kwGlZ8W6cJ9hsr+`8^o-U3d6?5KR*~r^qAmDtlUPJmo zfn?{>8_L}QiiIMX8UnHsocq;$RieG#9XNC5pPpa(o!FX92Uc*f37uH6b=e9AhGrv~ z1zWByVQu;+X2d$f^``X(u>?V}8%#4?Sbs?r?QOXi;dN<$dfGQx_Tzf|zm29`x(~FM N!PC{xWt~$(696AdOC10J literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable0110.png b/knetwalk/src/pics/cable0110.png new file mode 100644 index 0000000000000000000000000000000000000000..b5d97781c17b07a168e3b071a7ef83df9e30ac7e GIT binary patch literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ET8$-*J7C9Wjv4iviX>EaktG3V_RU%n;-fmVBti7z;- zz1Lq-tz!NY+1SarRj-RP!@00aZ%e-OC4=|1bGI=Eg24ZEDQ@x;!!;bZQXKBp%GU+g z@FqAh1SPDTvWnTpgZWk1!iNghO)L=_OV9m1x=6!?VWxmWd29CC{Y(Zcs=4Bss~0du z^&jW8@ML6jzs`N1Njibu0}whLa2F;*oHr8G9mN-!HV%oJX5=-nQcz5_=;?`Qle zTd?xzw!JS8tq<;6QhY%5aK&kRK7}Bb4hDPHgbk0su6?e-TEKec$MN^w?{0eA|2pk) R#RBL>22WQ%mvv4FO#rqYb|U}) literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable0111.png b/knetwalk/src/pics/cable0111.png new file mode 100644 index 0000000000000000000000000000000000000000..f526acde0f3ee97306cac782437ddf43e5a943ad GIT binary patch literal 524 zcmV+n0`vWeP)WK> zTTuHE7s=Q<&CF!VfSdV4U`XD)_sh%63skC9sZynXR&a!Uk$CYdy>WJailc*T5WDSO_PmkWzm6IE1EgJ^}KzG!C>HJ7>1z}C@nL-enr3E_cBaEv|6oHIHeT6eg*L0W2lt6i@)3Lwrf2( z)dgmx7vUCI2EeTsDx3jVz=hWOK8m8>BM8F~r4*e`=MfkIV>^bJo3P@rA8dh@0A4%+ zPJwgaGQ(yByaJI0n38u5EoGkSn4L_3_jod{_Pz9vZSXft`ael;L>m0B;mSH%0|Y^E zBc*%-Q|-3ky6jRv13oa_FtZicJON_j4Q5vp$NC(T&8fA?4PF!=hFAkj$J&Cwtv5IL zF#!7zNv+u(RqEqY7JzGaxleCd0Hvw|C=)EaktG3V_y$F4&T0&WlW0}Bny zC7vwoYnU(`?3AmZLiijG8&ra#a+*n(^{p{r`H=LaWR3c{VT7$Xc<= zD{|U~BV4N6V);)Cvp(Z2ZRR|XlhX)92P>u?NXcnD*W$p@av+-1#A0jq=d8wRMr)a0 zEtAbcifSHkbJuM9ZU5t#RQ2>co0M4!7Zx0Q7H{xpTlH4CSP$9NJG=kfxqtt`g*N%p qJ%5dvuHU--D~{iQqe+3|0ngV3DqA!DhCc*)gTd3)&t;ucLK6UoX>!Z} literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable1001.png b/knetwalk/src/pics/cable1001.png new file mode 100644 index 0000000000000000000000000000000000000000..6dff6282be9b7a44565c2044e59bbddf1604433e GIT binary patch literal 353 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o{c5N2Gfytfo6$X?><>&kwGlZ8V>MwqC<(>Dj#k})+z^h{`Rx(xJ-DqC>lbtX5bieC$agHs! ttnYs{@_hQdD~yxLd8UurCkp literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable1010.png b/knetwalk/src/pics/cable1010.png new file mode 100644 index 0000000000000000000000000000000000000000..fd599ab41ed65bf0861ca0acd000d8f23d75d112 GIT binary patch literal 238 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ET8$-=>*ym-m#TA)z1r;B4q#hka-H}W1b5MjA6z2`v5 zypFXu4wlF<1zuE~XtHo|hqFLXiF#z-zJInmKQFLn_FFndddsydQ4f2#*X>A-+MIKT z<3@6tal&;OE`wJUzv3Bm8ovMJUSQ40BH+LPBMvNN5RN;&_f`5I-Uq=4na|HFn!%V_ XBy4c>YwjMPGZ;Kw{an^LB{Ts5usKox literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable1011.png b/knetwalk/src/pics/cable1011.png new file mode 100644 index 0000000000000000000000000000000000000000..4b0e09b2a6c1a50c0bbd4626c048b09c739f409a GIT binary patch literal 556 zcmV+{0@MA8P)5K1jrKFfevPyb6!dfvrQCC|ibF>R1Gb4y^?A z!whktt=#6Eq^gkkOI8%;a`&@+w$H;|9uUTH{0WdG3EgfN;0Cw{o`5&0Gru6fL4p4& z0{{qj4d7MfwK~v=0L}7AxK#jKfDsP+KhrmOGLzs&M6@9Q0000<>&kwGlZ8XjSXEev5h(P>)5S5QV$R!Zj$VfyL>eCIXKvNF z8{tyEPAg?*az)FX!`>BBWDaR_+~lxyDShs?u<#Wz4;uHH$6oLl@ip7fxJHCo$%=G#Vr8@lS;BCduu^Iz|(J03g#*FWb)&f=GYitmJkZtbr4p0ZIQ z&AKOP{{gAnn{@6!6Owdp6P5e0B>wb`4%@Ken8$McHy&L}WN*24=W+I_o%;@mn*Du{ z9rI=9gk9BteD+2>`MW^7UDx&s_e$$b&OXbljLxh=oi0ib#be}+>;#Jr_W{F!!PC{x JWt~$(69CnJR_`0g_2X zK~#90?bf}n(=Zsu@t;e2s00H-eC@dh?tpuUxDE!ku3QSY(1EQ(#ndrFBV_~$&>1)q zsHkR0B1liu`c##QP#-CBY&p;Sw`0F{aCZfai=y}mWm#G-m+%fBu*8>Cv(b?N_mBAQ zWB@QeH$XTyz{&LA0^B<{K(bD`2*W`8$pJ>lcC=@j?U@||lB$4791vb3$0QYE*GTqh z)S9cRN}lJcsKnC76xz2+!NH+cCSg?naXXuJO4o*KX&74b}lq5&$whXoEl0&mY^0ni1dFKLZgl zNIN>aM0BN}>xTe3h_&HF6o(9kv}?+T0JdQ+y6B>d{$2eB0ak`?PV|}w00000NkvXX Hu0mjf_Ojo7 literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/cable1110.png b/knetwalk/src/pics/cable1110.png new file mode 100644 index 0000000000000000000000000000000000000000..eea56481346ebcc6b1be2f9b1698b0d0c7c31263 GIT binary patch literal 549 zcmV+=0^0qFP)MOP5VvDs_}@9+ULTqCXXqCrc5pyT~c z00T9>I~D*9JUkXaZ|WN$*lK?Tpt1g0fU*!b3Vj!V0ZvM;pWs=3?CmSrRe;lUk8q9< zr*$F9JgEYB4oQ*-!%&hW=JUDfbPBIap(i_0u(57kn zcJojWypb(R%N*j5$C0)HDAjN{lqAWfA4So}I^#M6gkdNQ!_7I2$K%@LI|FQcy{$No zp9TOXlgXq0VzDSTDNh4X7~RnYZtPFOHST%?fYE659^Y`8x39RB)r8%8u*Q9_0J-tc zNOFLqe1;oOifrEs|7C9ci!A(y3Qs`TxbhWW?-qj<{#8=7CWx-b5Nb500000NkvXXu0mjfC6ev^ literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/computer1.png b/knetwalk/src/pics/computer1.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae03fd93c1de80e67d59410a8b16f5a70fde667 GIT binary patch literal 534 zcmV+x0_pvUP)V+tZ=yW>zb{n`fIa~rpa$T#{atTmMnF3ZA{jSDR;GRU{IG2=PzQBIq zg%jW)zkt9BKLP(l?u3_sqR71k6p6Wjxqz7qa2>@XP)bc2Qk;M&is*K`g$l@&f_lB~ zY)w?dkZKrCoXd7OC14%s#c^!@#d1Ubs!iYsI0YVop(_OnAoNh50WZJ+!xOp$41j$S z_fiEmfWtDKo3+#~ixm936o~PM#CGS52SHJ_wVqRtb z)Vjdz)9I*4yNq9*pk3Uj5Z_^JyG@b?U8Iz;%%q0^-3~}8MtrIrSBd8Hp0cisJ=3$| Y2MJEaplk-`hX4Qo07*qoM6N<$f|#D<@&Et; literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/computer2.png b/knetwalk/src/pics/computer2.png new file mode 100644 index 0000000000000000000000000000000000000000..f6bd3a3ffbf4feb6599e6f17c3882280bfc1e7d4 GIT binary patch literal 641 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 literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/knetwalk.svgz b/knetwalk/src/pics/knetwalk.svgz new file mode 100644 index 0000000000000000000000000000000000000000..deefcc4a54c2bac58a190e7f2ec13ff4821528f9 GIT binary patch literal 1302 zcmV+x1?l=9iwFpU)p|kz18Z(&ba!EFYc6wkX8^rd+iu%N5PkPou=Gm;n9H5La^)xi z>ZCz|v<(ucMIRY)WwEA7fut>~U*B2YB+@rkC~Cm~IXgRh&fIqR=H2~G=H1z{N{f8% z1EIcW^F@)Q`PJP2cKK5f`Ce7Wc@k$uZs&ep`0w7Hy!qk%Zx@$;e*WN9cURu$Z$Eze z`NH#qF#LmGgyH+k_ukjvKYC~_3_twppZKe~UeCgCv)PahCq;P`UVaG~r4WAka1o$x z;9jVq!fg)GC{U8riSLdI0`H=oejJx;i6eWZ{*ed5xj_xI$S%gS#M@gA0Sl4ZMm>Z+-FH{>y}fu z7_`K8X6~~zzkcjUFvf(=4RD8USO2grLAmvXUBAt zV(~q$a5-ERH#SV`_`!M}-|Vl4*9d_@Nzjd9BDBz3=_k=F&vI*97vYx7s-wR!LuZ3^b*PClp#Dzj+#TsHTgGNm3BGz zPalT#sbIGR?qTi+l2FB&R$SYlt-mdbBO(pgj6>8*@0qJZ1HcXAyjAQ(f-0ukK>t?= z84L&wk<3J(jIep@yNJTi(jl`VngvRaD=J6bP!Y|9G@aLWJ&`bN1ax_H-^I&dyHITiokVNkk<$7wZntIj z>hwXdUjIw3lz^nzHm9uAh!rJnCx95SVuEljj1i(|B^x~|ne17$5@5_67_o@D-;*r* z5KZuQ$U3}?aLJ9PU2J;1fL#`pj0_v1zzG!*FwJ_rNEH*8@#w%P9E(>ThgTESqKxVP z8?QPFFLRoJ4O1MAm(pZG55U_fOm3SUd_9IQu@aTx>;?D;RteD5@T1`$)qvbMAosh^ zB3^gR=kb=*Nt3u*#bp_9XE_eXQy)b8K7fcI4as2~LQa4J&ZWW(DTf^b`vcQ3_&^ZQ z0ASjQWF6P5eIGd5&GAoi{+`8)>mKtiWRrkC%XI9NN$j8T20G&xPvb?T808+vCv8;Z zee$qFgJBYOvcY?fAl^_d*&^Fs75QjPt+(rD`iV>bmKM~~Oy@(P+&A>0KJ zJy>m~6p1qyCQn;XYm94MIVg%rrOX+$1qYr34p@v%f*qPE7c?!jX?1u68YR-8E;};> zZ)*<1Q$(RE@Uw^rqquT9cZuPlJE`y0D_KyVgLXD literal 0 HcmV?d00001 diff --git a/knetwalk/src/pics/server.png b/knetwalk/src/pics/server.png new file mode 100644 index 0000000000000000000000000000000000000000..92c27b34454173be766cd76bad75f5933718a9d7 GIT binary patch literal 540 zcmV+%0^|LOP)Tl|TqgfD2$+Ij#?xa_I-~ZXWkt zCj@4(_FQw|#>_r`kg$co3_xo=xbi$N=gxs!Gy6zj(xSq&Y^0Q3YFUwGS=mOwQ*|~p zD*zRSX_}IzX=m?`nSfHt+B+aPDR%-<6j}Se_)7))6Btv0 zwx3}QN+}kLMQgv^ZX3@NLRe>eQ=qy`2*G;2b{XC1?ICatJZr6;j3|!d#@D5kWLd^) zwF0;Y9)L&S1^5KVufT>OZfPY@y4_~8Y3+wx15OIKIAPOw?xKtr9}q$yrTkGY7{v$T zBq2@`e*}K(gfh>O-}eE-3H&QC=6`_~C+vdF37r%e6K5x2b;8PB?NIDZ)zVQL0SyoY zK2sTQK*32gYa>tqcO1e*1YFTSC+^L11-zQsodh(847W4S%P=v7Kw*~e%$;RN>QE1*D2gJ)x(Y=RjiM;3vVN$d z{rk`l>!$WYRcTRGMNt(^Qx!$sgrW!`#1LXBMU+sF63;Ql7-NpZoX^HSpPjQgTg$8I zzP!@?etLiJ^ZcIYnRsVx>~kFi{rbYYzr6XIJ1hx7kml7__!R`D{sBQSL_#;-zw`dB zS2dC{-Wz0%Iszpz7cEy8cQYjfHX9%)UkP-MbQ35qMFkA0sT+q}5!D7dSyECg%`Ay( z6SkqQ4>3{xLQhXg4vXGFJY3~hRqp^@cwLunw z9&XJbkPX> zaBF3MsOySLt=NacAh3Us6`~ z5!lk4uPi0w;z;+PH7YD6DgAgarC;YF#|VsSc_}X9O;%&!rCi0E%mxCDi@antGK5iA zQz0nO$q=z@rsQl5vd&fvq4fP`NtxXi>GzmNl&|%Vae5W z>g3&@T|Zw_SJP^Jyw`6@U-l zdfGeE{pk7pPN4IH*TwSRH#4Pc=Z0h*Ks=_SMRPZ?@bMa><_|!{x^+(_y(%| z?7OF_iN4!`r>U>j3G~A^Jnew1>*sIz(}lnN2Z29*JILPO0C)U;xbX6`?_kYeMsN5Y zJ^y0W%l!7|1p4IU{q>^Fw75GLzvTS*t*G!cMdKg5{_$I4S*=eE z_$>swkjPiYdnF~zZyrHV$X<9UDfSjrOX-*QmiK5pUs3Cl0&6Zg)w)=&sd5!?oUgds zj5?N7>!|kfSy)qeDVD*mWlECbWsEv|3#^f=E}EB;`AkVH2Rb20IR%RBH|HynV@tDI zM_|wuvfE7H3~T%rL0;TNU=r&$tMzo@lGE2wQ#X&oe4rC=n%_z0&4y_&jn8hQT2H&x zxRm_-dz)#z7uH0DbYa>Xvajr~?AO%MGZ1v|S*14Kd&Pxn=i)Rz)}x#*BdGuU4of_~mytKTBA;UJVIJ+G=_A=wWnNlokGmV}B3S8N@ z8l9~*b)BoJS6t54)JfzRZvs=#?+CJ&8R^bfVp(VFP#3CQN^TrFTOAZvj0#5Gq}xGl zWzq$P@G@L+F5YMaSuZ2Vi5%#Xl%-@W>uG1ntxPP-k`sLlv6|+J_7x&@@*BYEtX#oN_wUMPkc)Vctbgx(O7#W30zTZywFX33Sj3Yc`MM>J?YWE|xcs z05b?YzY`Zd?Lk(pM+Jts=%DengVi)c1Ue;U^9VyG-9uf#$r!@d0X%>!4%@)LM2;oL zdH^DTb!Th35a!K>@m_mN`cb>xrClYBpd8iTN!KDpLZvV>Z;(L{55LZ7@kZfADvB;bQ{C6Y(pthM0)ffy$M?I>Y<(s;>}xsR)2vAR#9d}nxz@%4&8CPtEM&x`xh|_Q>tvO zZQ30pW}~aD2$CRC<@O3fwoWkTVLAxG4%4FS7!Gx)PTrrrKU3w7vAynDIsw&Tbb2|X zOH}c!qU79d1<8p&2s!tx-3`wgU8Px&d^`s&Q}e8?HOPt3@$l`JAThP=^&NM|Rps;X z*B`@(7v(UDSyr$dGWJYE&5AG$)A1BO&vIK- zF|}yo$ETNm%(pvs0y6{>I^O&}M+1RE-rAObQG ztjYwN8db0aa~e+(DoUNeoW|2IDa;T}j`*!r=$J3b2il5yh!Bl~JMTm3op+}v-WMF#Gsd!z0gI$#H&WaNFoQoa)1s^!o3B?#JL;C=)KHe`fflTq7(FYXjnUU{xTtNhs#8cojc2%LhV(4E&7?0KxIwzQWMxa|af0)284_5c6? literal 0 HcmV?d00001 diff --git a/knetwalk/src/sounds/start.wav b/knetwalk/src/sounds/start.wav new file mode 100644 index 0000000000000000000000000000000000000000..6e69bf43e6b67f598ce0564c0b1a60d31a30f0e1 GIT binary patch literal 25880 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 literal 0 HcmV?d00001 diff --git a/knetwalk/src/sounds/turn.wav b/knetwalk/src/sounds/turn.wav new file mode 100644 index 0000000000000000000000000000000000000000..3f909e76390eae28500a2fcbe03810db45d18cf9 GIT binary patch literal 7804 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 literal 0 HcmV?d00001 diff --git a/knetwalk/src/sounds/win.wav b/knetwalk/src/sounds/win.wav new file mode 100644 index 0000000000000000000000000000000000000000..fc091fe593b945e4f0ff25f003ca9b79d968e9eb GIT binary patch literal 24794 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 i-+aDM*MIDmuScrhZ&9mn`?p + +Others listed in main.cpp. diff --git a/kolf/COPYING b/kolf/COPYING new file mode 100644 index 00000000..d3a8f707 --- /dev/null +++ b/kolf/COPYING @@ -0,0 +1,341 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kolf/Makefile.am b/kolf/Makefile.am new file mode 100644 index 00000000..95449e11 --- /dev/null +++ b/kolf/Makefile.am @@ -0,0 +1,49 @@ +SUBDIRS = objects sounds courses pics graphics +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +bin_PROGRAMS = +lib_LTLIBRARIES = libkolf.la +kdeinit_LTLIBRARIES = kolf.la + +libkolf_la_SOURCES = \ + kolf.cpp game.cpp canvasitem.cpp ball.cpp newgame.cpp config.cpp \ + scoreboard.cpp editor.cpp pluginloader.cpp object.cpp vector.cpp \ + printdialogpage.cpp kcomboboxdialog.cpp kvolumecontrol.cpp \ + floater.cpp slope.cpp + +libkolf_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 3:0:2 +libkolf_la_LIBADD = $(LIB_KDEGAMES) $(LIB_KDEPRINT) $(LIB_KIO) -lartskde +libkolf_la_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + + +# the installed header files +kolfinclude_HEADERS = \ + ball.h game.h statedb.h config.h canvasitem.h object.h rtti.h vector.h \ + floater.h slope.h + +kolfincludedir=$(includedir)/kolf + + +# kdeinited lib +kolf_la_SOURCES = main.cpp +kolf_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module -avoid-version +kolf_la_LIBADD = libkolf.la + + +EXTRA_DIST = kolf.desktop + +appdir = $(kde_datadir)/kolf +app_DATA = kolfui.rc intro tutorial.kolf tutorial.kolfgame + +xdg_apps_DATA = kolf.desktop + +applicationmimedir = $(kde_mimedir)/application +applicationmime_DATA = x-kourse.desktop x-kolf.desktop + +magicdir = $(kde_confdir)/magic +magic_DATA = kolf.magic + +METASOURCES = AUTO + +messages: rc.cpp game.h floater.h slope.h + $(XGETTEXT) *.cpp canvasitem.h game.h -o $(podir)/kolf.pot diff --git a/kolf/Makefile.am.fast b/kolf/Makefile.am.fast new file mode 100644 index 00000000..2d39a25e --- /dev/null +++ b/kolf/Makefile.am.fast @@ -0,0 +1,32 @@ +SUBDIRS=objects sounds courses pics graphics +bin_PROGRAMS=kolf +INCLUDES=-I$(top_srcdir)/libkdegames $(all_includes) + +kolf_SOURCES=main.cpp kolf.cpp game.cpp canvasitem.cpp ball.cpp newgame.cpp config.cpp scoreboard.cpp editor.cpp pluginloader.cpp object.cpp vector.cpp printdialogpage.cpp kcomboboxdialog.cpp kvolumecontrol.cpp floater.cpp slope.cpp +kolf_LDFLAGS=$(all_libraries) $(KDE_RPATH) -export-dynamic +kolf_LDADD=$(top_builddir)/libkdegames/libkdegames.la $(LIB_KDEPRINT) $(LIB_KIO) -lartskde + +EXTRA_DIST=kolf.desktop + +appdir=$(kde_datadir)/kolf +app_DATA=kolfui.rc intro tutorial.kolf tutorial.kolfgame + +appsdir=$(kde_appsdir)/Games/Arcade +apps_DATA=kolf.desktop + +applicationmimedir=$(kde_mimedir)/application +applicationmime_DATA=x-kourse.desktop x-kolf.desktop + +magicdir=$(kde_confdir)/magic +magic_DATA=kolf.magic + +kolfinclude_HEADERS=ball.h game.h statedb.h config.h canvasitem.h object.h rtti.h vector.h floater.h slope.h +kolfincludedir=$(includedir)/kolf + +METASOURCES=AUTO + +messages: rc.cpp game.h floater.h slope.h + $(XGETTEXT) *.cpp -o $(podir)/kolf.pot + +install-data-local: + rm -rf $(DESTDIR)$(kde_appsdir)/Games/kolf.desktop diff --git a/kolf/PLUGINS.docbook b/kolf/PLUGINS.docbook new file mode 100644 index 00000000..4da65ddb --- /dev/null +++ b/kolf/PLUGINS.docbook @@ -0,0 +1 @@ +This will include plugin writing instructions later. diff --git a/kolf/README.pool b/kolf/README.pool new file mode 100644 index 00000000..a571b1dd --- /dev/null +++ b/kolf/README.pool @@ -0,0 +1,7 @@ +Heya, + +I made a kolf pool game as an example of kolf plugin use. kolf/obects/poolball/ contains the PoolBall class, which is installed as a plugin. + +So, compile PoolBall, install it, and open pool.kolf. Kolf courses can specify any object name, and it is up to the user to install these objects that the course needs. Kolf will warn when an object is not found in the hole being loaded. + +Jason diff --git a/kolf/TODO b/kolf/TODO new file mode 100644 index 00000000..4a584e49 --- /dev/null +++ b/kolf/TODO @@ -0,0 +1 @@ +Nothing diff --git a/kolf/ball.cpp b/kolf/ball.cpp new file mode 100644 index 00000000..8adaa8cb --- /dev/null +++ b/kolf/ball.cpp @@ -0,0 +1,466 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "rtti.h" +#include "vector.h" +#include "canvasitem.h" +#include "game.h" +#include "ball.h" + +Ball::Ball(QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + m_doDetect = true; + m_collisionLock = false; + setBeginningOfHole(false); + setBlowUp(false); + setPen(black); + resetSize(); + collisionId = 0; + m_addStroke = false; + m_placeOnGround = false; + m_forceStillGoing = false; + frictionMultiplier = 1.0; + QFont font(kapp->font()); + //font.setPixelSize(10); + label = new QCanvasText("", font, canvas); + label->setColor(white); + label->setVisible(false); + + // this sets z + setState(Stopped); + label->setZ(z() - .1); +} + +void Ball::aboutToDie() +{ + delete label; +} + +void Ball::setState(BallState newState) +{ + state = newState; + if (state == Stopped) + setZ(1000); + else + setBeginningOfHole(false); +} + +void Ball::advance(int phase) +{ + // not used anymore + // can be used to make ball wobble + if (phase == 1 && m_blowUp) + { + if (blowUpCount >= 50) + { + // i should make this a config option + //setAddStroke(addStroke() + 1); + setBlowUp(false); + resetSize(); + return; + } + + const double diff = 8; + double randnum = kapp->random(); + const double width = 6 + randnum * (diff / RAND_MAX); + randnum = kapp->random(); + const double height = 6 + randnum * (diff / RAND_MAX); + setSize(width, height); + blowUpCount++; + } +} + +void Ball::friction() +{ + if (state == Stopped || state == Holed || !isVisible()) { setVelocity(0, 0); return; } + const double subtractAmount = .027 * frictionMultiplier; + if (m_vector.magnitude() <= subtractAmount) + { + state = Stopped; + setVelocity(0, 0); + game->timeout(); + return; + } + m_vector.setMagnitude(m_vector.magnitude() - subtractAmount); + setVector(m_vector); + + frictionMultiplier = 1.0; +} + +void Ball::setVelocity(double vx, double vy) +{ + QCanvasEllipse::setVelocity(vx, vy); + + if (vx == 0 && vy == 0) + { + m_vector.setDirection(0); + m_vector.setMagnitude(0); + return; + } + + double ballAngle = atan2(-vy, vx); + + m_vector.setDirection(ballAngle); + m_vector.setMagnitude(sqrt(pow(vx, 2) + pow(vy, 2))); +} + +void Ball::setVector(const Vector &newVector) +{ + m_vector = newVector; + + if (newVector.magnitude() == 0) + { + setVelocity(0, 0); + return; + } + + QCanvasEllipse::setVelocity(cos(newVector.direction()) * newVector.magnitude(), -sin(newVector.direction()) * newVector.magnitude()); +} + +void Ball::moveBy(double dx, double dy) +{ + double oldx; + double oldy; + oldx = x(); + oldy = y(); + QCanvasEllipse::moveBy(dx, dy); + + if (game && !game->isPaused()) + collisionDetect(oldx, oldy); + + if ((dx || dy) && game && game->curBall() == this) + game->ballMoved(); + + label->move(x() + width(), y() + height()); +} + +void Ball::doAdvance() +{ + QCanvasEllipse::advance(1); +} + +namespace Lines +{ + // provides a point made of doubles + + struct Line + { + Point p1, p2; + }; + + int ccw(const Point &p0, const Point &p1, const Point &p2) + { + double dx1, dx2, dy1, dy2; + dx1 = p1.x - p0.x; dy1 = p1.y - p0.y; + dx2 = p2.x - p0.x; dy2 = p2.y - p0.y; + if (dx1*dy2 > dy1*dx2) return +1; + if (dx1*dy2 < dy1*dx2) return -1; + if ((dx1*dx2 < 0) || (dy1*dy2 < 0)) return -1; + if ((dx1*dx1+dy1*dy1) < (dx2*dx2+dy2*dy2)) + return +1; + return 0; + } + + int intersects(const Line &l1, const Line &l2) + { + // Charles says, TODO: Account for vertical lines + // Jason says, in my testing vertical lines work + return ((ccw(l1.p1, l1.p2, l2.p1) + *ccw(l1.p1, l1.p2, l2.p2)) <= 0) + && ((ccw(l2.p1, l2.p2, l1.p1) + *ccw(l2.p1, l2.p2, l1.p2)) <= 0); + } + + + bool intersects( + double xa1, double ya1, double xb1, double yb1, + double xa2, double ya2, double xb2, double yb2 + ) + { + Line l1, l2; + l1.p1.x = xa1; + l1.p1.y = ya1; + l1.p2.x = xb1; + l1.p2.y = yb1; + + l2.p1.x = xa2; + l2.p1.y = ya2; + l2.p2.x = xb2; + l2.p2.y = yb2; + + return intersects(l1, l2); + } +} + +void Ball::collisionDetect(double oldx, double oldy) +{ + if (!isVisible() || state == Holed || !m_doDetect) + return; + + if (collisionId >= INT_MAX - 1) + collisionId = 0; + else + collisionId++; + + //kdDebug(12007) << "------" << endl; + //kdDebug(12007) << "Ball::collisionDetect id " << collisionId << endl; + + // every other time... + // do friction + if (collisionId % 2 && !(xVelocity() == 0 && yVelocity() == 0)) + friction(); + + const double minSpeed = .06; + + QCanvasItemList m_list = collisions(true); + + // please don't ask why QCanvas doesn't actually sort its list; + // it just doesn't. + m_list.sort(); + + this->m_list = m_list; + + for (QCanvasItemList::Iterator it = m_list.begin(); it != m_list.end(); ++it) + { + QCanvasItem *item = *it; + + if (item->rtti() == Rtti_NoCollision || item->rtti() == Rtti_Putter) + continue; + + if (item->rtti() == rtti() && !m_collisionLock) + { + // it's one of our own kind, a ball + Ball *oball = dynamic_cast(item); + if (!oball || oball->collisionLock()) + continue; + oball->setCollisionLock(true); + + if ((oball->x() - x() != 0 && oball->y() - y() != 0) && state == Rolling && oball->curState() != Holed) + { + m_collisionLock = true; + // move this ball to where it was barely touching + double ballAngle = m_vector.direction(); + while (collisions(true).contains(item) > 0) + move(x() - cos(ballAngle) / 2.0, y() + sin(ballAngle) / 2.0); + + // make a 2 pixel separation + move(x() - 2 * cos(ballAngle), y() + 2 * sin(ballAngle)); + + Vector bvector = oball->curVector(); + m_vector -= bvector; + + Vector unit1 = Vector(QPoint(x(), y()), QPoint(oball->x(), oball->y())); + unit1 = unit1.unit(); + + Vector unit2 = m_vector.unit(); + + double cos = unit1 * unit2; + + unit1 *= m_vector.magnitude() * cos; + m_vector -= unit1; + m_vector += bvector; + + bvector += unit1; + + oball->setVector(bvector); + setVector(m_vector); + + oball->setState(Rolling); + setState(Rolling); + + oball->doAdvance(); + } + + continue; + } + else if (item->rtti() == Rtti_WallPoint) + { + //kdDebug(12007) << "collided with WallPoint\n"; + // iterate through the rst + QPtrList points; + for (QCanvasItemList::Iterator pit = it; pit != m_list.end(); ++pit) + { + if ((*pit)->rtti() == Rtti_WallPoint) + { + WallPoint *point = (WallPoint *)(*pit); + if (point) + points.prepend(point); + } + } + + // ok now we have a list of wall points we are on + + WallPoint *iterpoint = 0; + WallPoint *finalPoint = 0; + + // this wont be least when we're done hopefully + double leastAngleDifference = 9999; + + for (iterpoint = points.first(); iterpoint; iterpoint = points.next()) + { + //kdDebug(12007) << "-----\n"; + const Wall *parentWall = iterpoint->parentWall(); + const QPoint qp(iterpoint->x() + parentWall->x(), iterpoint->y() + parentWall->y()); + const Point p(qp.x(), qp.y()); + const QPoint qother = QPoint(parentWall->startPoint() == qp? parentWall->endPoint() : parentWall->startPoint()) + QPoint(parentWall->x(), parentWall->y()); + const Point other(qother.x(), qother.y()); + + // vector of wall + Vector v = Vector(p, other); + + // difference between our path and the wall path + double ourDir = m_vector.direction(); + + double wallDir = M_PI - v.direction(); + + //kdDebug(12007) << "ourDir: " << rad2deg(ourDir) << endl; + //kdDebug(12007) << "wallDir: " << rad2deg(wallDir) << endl; + + const double angleDifference = fabs(M_PI - fabs(ourDir - wallDir)); + //kdDebug(12007) << "computed angleDifference: " << rad2deg(angleDifference) << endl; + + // only if this one is the least of all + if (angleDifference < leastAngleDifference) + { + leastAngleDifference = angleDifference; + finalPoint = iterpoint; + //kdDebug(12007) << "it's the one\n"; + } + } + + // this'll never happen + if (!finalPoint) + continue; + + // collide with our chosen point + finalPoint->collision(this, collisionId); + + // don't worry about colliding with walls + // wall points are ok alone + goto end; + } + + if (!isVisible() || state == Holed) + return; + + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + if (!citem->terrainCollisions()) + { + // read: if (not do terrain collisions) + if (!citem->collision(this, collisionId)) + { + // if (skip smart wall test) + if (citem->vStrut() || item->rtti() == Rtti_Wall) + goto end; + else + goto wallCheck; + } + } + break; + } + } + + for (QCanvasItemList::Iterator it = m_list.begin(); it != m_list.end(); ++it) + { + CanvasItem *citem = dynamic_cast(*it); + if (citem && citem->terrainCollisions()) + { + // slopes return false + // as only one should be processed + // however that might not always be true + + // read: if (not do terrain collisions) + if (!citem->collision(this, collisionId)) + { + break; + } + } + } + +// Charles's smart wall check: + + wallCheck: + + { // check if I went through a wall + QCanvasItemList items; + if (game) + items = game->canvas()->allItems(); + for (QCanvasItemList::Iterator i = items.begin(); i != items.end(); ++i) + { + if ((*i)->rtti() != Rtti_Wall) + continue; + + QCanvasItem *item = (*i); + Wall *wall = dynamic_cast(item); + if (!wall || !wall->isVisible()) + continue; + + if (Lines::intersects( + wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y(), + wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y(), + + oldx, oldy, x(), y() + )) + { + //kdDebug(12007) << "smart wall collision\n"; + wall->collision(this, collisionId); + break; + } + + + } + } + + end: + + if (m_vector.magnitude() < minSpeed && m_vector.magnitude()) + { + setVelocity(0, 0); + setState(Stopped); + } +} + +BallState Ball::currentState() +{ + return state; +} + +void Ball::showInfo() +{ + label->setVisible(isVisible()); +} + +void Ball::hideInfo() +{ + label->setVisible(false); +} + +void Ball::setName(const QString &name) +{ + label->setText(name); +} + +void Ball::setCanvas(QCanvas *c) +{ + QCanvasEllipse::setCanvas(c); + label->setCanvas(c); +} + +void Ball::setVisible(bool yes) +{ + QCanvasEllipse::setVisible(yes); + + label->setVisible(yes && game && game->isInfoShowing()); +} + diff --git a/kolf/ball.h b/kolf/ball.h new file mode 100644 index 00000000..098d82ef --- /dev/null +++ b/kolf/ball.h @@ -0,0 +1,114 @@ +#ifndef KOLF_BALL_H +#define KOLF_BALL_H + +#include +#include + +#include + +#include "vector.h" +#include "rtti.h" + +enum BallState { Rolling = 0, Stopped, Holed }; + +class Ball : public QCanvasEllipse, public CanvasItem +{ +public: + Ball(QCanvas *canvas); + virtual void aboutToDie(); + + BallState currentState(); + + virtual void resetSize() { setSize(7, 7); } + virtual void advance(int phase); + virtual void doAdvance(); + virtual void moveBy(double dx, double dy); + virtual void setVelocity(double vx, double vy); + + virtual bool deleteable() const { return false; } + + virtual bool canBeMovedByOthers() const { return true; } + + BallState curState() const { return state; } + void setState(BallState newState); + + QColor color() const { return m_color; } + void setColor(QColor color) { m_color = color; setBrush(color); } + + void setMoved(bool yes) { m_moved = yes; } + bool moved() const { return m_moved; } + void setBlowUp(bool yes) { m_blowUp = yes; blowUpCount = 0; } + bool blowUp() const { return m_blowUp; } + + void setFrictionMultiplier(double news) { frictionMultiplier = news; }; + void friction(); + void collisionDetect(double oldx, double oldy); + + virtual int rtti() const { return Rtti_Ball; }; + + int addStroke() const { return m_addStroke; } + bool placeOnGround(Vector &v) { v = oldVector; return m_placeOnGround; } + void setAddStroke(int newStrokes) { m_addStroke = newStrokes; } + void setPlaceOnGround(bool placeOnGround) { m_placeOnGround = placeOnGround; oldVector = m_vector; } + + bool beginningOfHole() const { return m_beginningOfHole; } + void setBeginningOfHole(bool yes) { m_beginningOfHole = yes; } + + bool forceStillGoing() const { return m_forceStillGoing; } + void setForceStillGoing(bool yes) { m_forceStillGoing = yes; } + + Vector curVector() const { return m_vector; } + void setVector(const Vector &newVector); + + bool collisionLock() const { return m_collisionLock; } + void setCollisionLock(bool yes) { m_collisionLock = yes; } + virtual void fastAdvanceDone() { setCollisionLock(false); } + + void setDoDetect(bool yes) { m_doDetect = yes; } + bool doDetect() const { return m_doDetect; } + + virtual void showInfo(); + virtual void hideInfo(); + virtual void setName(const QString &); + virtual void setCanvas(QCanvas *c); + virtual void setVisible(bool yes); + +private: + BallState state; + QColor m_color; + long int collisionId; + double frictionMultiplier; + + bool m_blowUp; + int blowUpCount; + int m_addStroke; + bool m_placeOnGround; + double m_oldvx; + double m_oldvy; + + bool m_moved; + bool m_beginningOfHole; + bool m_forceStillGoing; + + Vector m_vector; + Vector oldVector; + bool m_collisionLock; + + bool m_doDetect; + QCanvasItemList m_list; + + QCanvasText *label; +}; + + +inline int rad2deg(double theDouble) +{ + return (int)((360L / (2L * M_PI)) * theDouble); +} + +inline double deg2rad(double theDouble) +{ + return (((2L * M_PI) / 360L) * theDouble); +} + +#endif diff --git a/kolf/canvasitem.cpp b/kolf/canvasitem.cpp new file mode 100644 index 00000000..5e39257c --- /dev/null +++ b/kolf/canvasitem.cpp @@ -0,0 +1,48 @@ +#include + +#include + +#include "game.h" +#include "canvasitem.h" + +QCanvasRectangle *CanvasItem::onVStrut() +{ + QCanvasItem *qthis = dynamic_cast(this); + if (!qthis) + return 0; + QCanvasItemList l = qthis->collisions(true); + l.sort(); + bool aboveVStrut = false; + CanvasItem *item = 0; + QCanvasItem *qitem = 0; + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + item = dynamic_cast(*it); + if (item) + { + qitem = *it; + if (item->vStrut()) + { + //kdDebug(12007) << "above vstrut\n"; + aboveVStrut = true; + break; + } + } + } + + QCanvasRectangle *ritem = dynamic_cast(qitem); + + return aboveVStrut && ritem? ritem : 0; +} + +void CanvasItem::save(KConfig *cfg) +{ + cfg->writeEntry("dummykey", true); +} + +void CanvasItem::playSound(QString file, double vol) +{ + if (game) + game->playSound(file, vol); +} + diff --git a/kolf/canvasitem.h b/kolf/canvasitem.h new file mode 100644 index 00000000..e7a37cb6 --- /dev/null +++ b/kolf/canvasitem.h @@ -0,0 +1,182 @@ +#ifndef KOLF_CANVASITEM_H +#define KOLF_CANVASITEM_H + +#include + +#include "config.h" + +class Ball; +class KConfig; +class StateDB; +class KolfGame; + +class CanvasItem +{ +public: + CanvasItem() { game = 0; } + virtual ~CanvasItem() {} + /** + * load your settings from the KConfig, which represents a course. + */ + virtual void load(KConfig *) {} + /** + * load a point if you wish. Rarely necessary. + */ + virtual void loadState(StateDB * /*db*/) {} + /** + * returns a bool that is true if your item needs to load after other items + */ + virtual bool loadLast() const { return false; } + /** + * called if the item is made by user while editing, with the item that was selected on the hole; + */ + virtual void selectedItem(QCanvasItem * /*item*/) {} + /** + * called after the item is moved the very first time by the game + */ + virtual void firstMove(int /*x*/, int /*y*/) {} + /** + * save your settings. + */ + virtual void save(KConfig *cfg); + /** + * save a point if you wish. Rarely necessary. + */ + virtual void saveState(StateDB * /*db*/) {} + /** + * called for information when shot started + */ + virtual void shotStarted() {} + /** + * called right before any items are saved. + */ + virtual void aboutToSave() {} + /** + * called right after all items are saved. + */ + virtual void savingDone() {} + /** + * called when the edit mode has been changed. + */ + virtual void editModeChanged(bool /*editing*/) {} + /** + * the item should delete any other objects it's created. + * DO NOT DO THIS KIND OF STUFF IN THE DESTRUCTOR! + */ + virtual void aboutToDie() {} + /** + * returns the object to get rid of when the delete button is pressed on this item. Some sub-objects will return something other than this. + */ + virtual CanvasItem *itemToDelete() { return this; } + /** + * called when user presses delete key while editing. This is very rarely reimplemented, and generally shouldn't be. + */ + virtual void aboutToDelete() {} + /** + * returns whether this item should be able to be deleted by user while editing. + */ + virtual bool deleteable() const { return true; } + /** + * returns whether this item should get doAdvance called -- it is called in sync with ball advancing (which is twice as fast as the advance() calling rate) + */ + virtual bool fastAdvance() const { return false; } + /** + * called when all items have had their chance at a doAdvance + */ + virtual void fastAdvanceDone() {} + /** + * called if fastAdvance is enabled + */ + virtual void doAdvance() {} + /** + * if all items of this type of item (based on rtti()) that are "colliding" (ie, in the same spot) with ball should get collision() called. + */ + virtual bool terrainCollisions() const { return false; } + /** + * returns whether or not this item lifts items on top of it. + */ + virtual bool vStrut() const { return false; } + /** + * show extra item info + */ + virtual void showInfo() {}; + /** + * hide extra item info + */ + virtual void hideInfo() {}; + /** + * update your Z value (this is called by various things when perhaps the value should change) if this is called by a vStrut, it will pass 'this'. + */ + virtual void updateZ(QCanvasRectangle * /*vStrut*/ = 0) {}; + /** + * clean up for prettyness + */ + virtual void clean() {}; + /** + * scale factor changed (game->scaleFactor(), the world matrix is game->worldMatrix()) + * NOTE: not used in Kolf 1.1, which comes with KDE 3.1. + */ + virtual void scaleChanged() {}; + /** + * returns whether this item can be moved by others (if you want to move an item, you should honor this!) + */ + virtual bool canBeMovedByOthers() const { return false; } + /** + * returns a Config that can be used to configure this item by the user. + * The default implementation returns one that says 'No configuration options'. + */ + virtual Config *config(QWidget *parent) { return new DefaultConfig(parent); } + /** + * returns other items that should be moveable (besides this one of course). + */ + virtual QPtrList moveableItems() const { return QPtrList(); } + /** + * returns whether this can be moved by the user while editing. + */ + virtual bool moveable() const { return true; } + + void setId(int newId) { id = newId; } + int curId() const { return id; } + + /** + * call to play sound (ie, playSound("wall") plays kdedir/share/apps/kolf/sounds/wall.wav). + * optionally, specify vol to be between 0-1, for no sound to full volume, respectively. + */ + void playSound(QString file, double vol = 1); + + /** + * called on ball's collision. Return if terrain collisions should be processed. + */ + virtual bool collision(Ball * /*ball*/, long int /*id*/) { return true; } + + /** + * reimplement if you want extra items to have access to the game object. + * playSound() relies on having this. + */ + virtual void setGame(KolfGame *game) { this->game = game; } + + /** + * returns whether this is a corner resizer + */ + virtual bool cornerResize() const { return false; } + + QString name() const { return m_name; } + void setName(const QString &newname) { m_name = newname; } + +protected: + /** + * pointer to main KolfGame + */ + KolfGame *game; + + /** + * returns the highest vertical strut the item is on + */ + QCanvasRectangle *onVStrut(); + +private: + QString m_name; + int id; +}; + +#endif diff --git a/kolf/config.cpp b/kolf/config.cpp new file mode 100644 index 00000000..7652cf40 --- /dev/null +++ b/kolf/config.cpp @@ -0,0 +1,48 @@ +#include +#include + +#include +#include + +#include "config.h" + +Config::Config(QWidget *parent, const char *name) + : QFrame(parent, name) +{ + startedUp = false; +} + +void Config::ctorDone() +{ + startedUp = true; +} + +int Config::spacingHint() +{ + return KDialog::spacingHint() / 2; +} + +int Config::marginHint() +{ + return KDialog::marginHint(); +} + +void Config::changed() +{ + if (startedUp) + emit modified(); +} + +MessageConfig::MessageConfig(QString text, QWidget *parent, const char *name) + : Config(parent, name) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + layout->addWidget(new QLabel(text, this)); +} + +DefaultConfig::DefaultConfig(QWidget *parent, const char *name) + : MessageConfig(i18n("No configuration options"), parent, name) +{ +} + +#include "config.moc" diff --git a/kolf/config.h b/kolf/config.h new file mode 100644 index 00000000..07c68938 --- /dev/null +++ b/kolf/config.h @@ -0,0 +1,42 @@ +#ifndef KOLF_CONFIG_H +#define KOLF_CONFIG_H + +#include + +class Config : public QFrame +{ + Q_OBJECT + +public: + Config(QWidget *parent, const char *name = 0); + void ctorDone(); + +signals: + void modified(); + +protected: + int spacingHint(); + int marginHint(); + bool startedUp; + void changed(); +}; + +// this is easy to use to show a message +class MessageConfig : public Config +{ + Q_OBJECT + +public: + MessageConfig(QString text, QWidget *parent, const char *name = 0); +}; + +// internal +class DefaultConfig : public MessageConfig +{ + Q_OBJECT + +public: + DefaultConfig(QWidget *parent, const char *name = 0); +}; + +#endif diff --git a/kolf/configure.in.in b/kolf/configure.in.in new file mode 100644 index 00000000..9754545e --- /dev/null +++ b/kolf/configure.in.in @@ -0,0 +1,3 @@ +if test "x$build_arts" = "xno"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kolf" +fi diff --git a/kolf/courses.list b/kolf/courses.list new file mode 100644 index 00000000..021d608c --- /dev/null +++ b/kolf/courses.list @@ -0,0 +1,10 @@ +tutorial.kolf +pool.kolf +courses/Classic.kolf +courses/Easy.kolf +courses/Hard.kolf +courses/Medium.kolf +courses/Practice +courses/ReallyEasy +courses/Impossible +courses/USApro diff --git a/kolf/courses/ADDING_COURSES b/kolf/courses/ADDING_COURSES new file mode 100644 index 00000000..2b86c6b2 --- /dev/null +++ b/kolf/courses/ADDING_COURSES @@ -0,0 +1,8 @@ +Hi! + +To add a course to the default distribution, first email me at jason@katzbrown.com. + +If I say 'sure!', cvs add it here (with or without file extension, preferably without), add it to the Makefile.am, and add it to kolf/courses.list. + +Thanks! +Jason diff --git a/kolf/courses/Classic.kolf b/kolf/courses/Classic.kolf new file mode 100644 index 00000000..199d937a --- /dev/null +++ b/kolf/courses/Classic.kolf @@ -0,0 +1,1986 @@ +[0-course@-50,-50] +Name=Classic +Name[af]=Klasieke +Name[bg]=КлаÑичеÑко +Name[bn]=কà§à¦²à¦¾à¦¸à¦¿à¦• +Name[br]=Da gustum +Name[bs]=KlasiÄni +Name[ca]=Clàsic +Name[cs]=Klasický +Name[da]=Klassisk +Name[de]=Klassisch +Name[el]=Κλασσικό +Name[es]=Clásico +Name[et]=Klassika +Name[fi]=Klassinen +Name[fr]=Classique +Name[gl]=Clásico +Name[he]=קלסי +Name[hi]=कà¥à¤²à¤¾à¤¸à¤¿à¤• +Name[hr]=KlasiÄno +Name[hu]=Klasszikus +Name[is]=Klassísk +Name[it]=Classico +Name[ja]=クラッシック +Name[mk]=КлаÑичен +Name[nb]=Klassisk +Name[nl]=Klassiek +Name[nn]=Klassisk +Name[pl]=Klasyczny +Name[pt]=Clássico +Name[pt_BR]=Clássico +Name[ro]=Clasic +Name[ru]=КлаÑÑика +Name[sk]=Klasické +Name[sl]=KlasiÄna +Name[sr]=КлаÑичан +Name[sr@Latn]=KlasiÄan +Name[sv]=Klassisk +Name[ta]=சிறநà¯à®¤ +Name[tg]=КлаÑÑика +Name[tr]=Klasik +Name[uk]=КлаÑичний +Name[ven]=Maimbo +Name[wa]=Classike +Name[xh]=Yeyakudala +Name[xx]=xxClassicxx +Name[zh_CN]=ç»å…¸ +Name[zh_TW]=å¤å…¸ +Name[zu]=Okuhle kakhulu +author=Niklas Knutsson +name=Classic + +[1-ball@200,360] +dummykey=true + +[1-cup@197,56|3] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-sand@-49,288|16] +changeEnabled=false +changeEvery=50 +height=482 +width=332 + +[1-slope@160,19|13] +grade=4 +gradient=Elliptic +height=76 +reversed=false +stuckOnGround=false +width=76 + +[1-wall@0,0|10] +endPoint=285,66 +startPoint=247,9 + +[1-wall@0,0|11] +endPoint=285,66 +startPoint=238,128 + +[1-wall@0,0|13] +endPoint=213,190 +startPoint=185,190 + +[1-wall@0,0|26] +endPoint=238,387 +startPoint=156,387 + +[1-wall@0,0|27] +endPoint=247,9 +startPoint=149,9 + +[1-wall@0,0|4] +endPoint=156,128 +startPoint=156,387 + +[1-wall@0,0|5] +endPoint=238,127 +startPoint=238,387 + +[1-wall@0,0|6] +endPoint=180,128 +startPoint=155,129 + +[1-wall@0,0|7] +endPoint=238,128 +startPoint=212,127 + +[1-wall@0,0|8] +endPoint=111,67 +startPoint=156,128 + +[1-wall@0,0|9] +endPoint=149,9 +startPoint=111,67 + +[10-ball@327,329] +dummykey=true + +[10-cup@59,60|16] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-slope@10,11|15] +grade=4 +gradient=Elliptic +height=54 +reversed=false +stuckOnGround=false +width=54 + +[10-slope@105,66|9] +grade=3 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@116,118|13] +grade=8 +gradient=Elliptic +height=59 +reversed=false +stuckOnGround=false +width=59 + +[10-slope@132,178|12] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@16,63|17] +grade=5 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@17,247|26] +grade=4 +gradient=Elliptic +height=138 +reversed=false +stuckOnGround=false +width=138 + +[10-slope@172,124|11] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@176,232|5] +grade=4 +gradient=Elliptic +height=60 +reversed=false +stuckOnGround=false +width=60 + +[10-slope@178,174|8] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@211,174|14] +grade=3 +gradient=Elliptic +height=66 +reversed=false +stuckOnGround=false +width=66 + +[10-slope@242,6|25] +grade=4 +gradient=Elliptic +height=144 +reversed=false +stuckOnGround=false +width=144 + +[10-slope@248,224|6] +grade=4 +gradient=Elliptic +height=63 +reversed=false +stuckOnGround=false +width=63 + +[10-slope@59,63|10] +grade=7 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@66,25|18] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@72,106|8] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-wall@0,0|19] +endPoint=242,256 +startPoint=242,329 + +[10-wall@0,0|20] +endPoint=179,90 +startPoint=126,116 + +[10-wall@0,0|21] +endPoint=80,104 +startPoint=50,135 + +[10-wall@0,0|22] +endPoint=390,301 +startPoint=299,387 + +[10-wall@0,0|23] +endPoint=10,11 +startPoint=10,95 + +[10-wall@0,0|24] +endPoint=100,11 +startPoint=10,11 + +[10-wall@0,0|3] +endPoint=10,95 +startPoint=299,387 + +[10-wall@0,0|5] +endPoint=390,301 +startPoint=100,11 + +[11-ball@200,356] +dummykey=true + +[11-cup@204,194|8] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[11-slope@117,42|10] +grade=2 +gradient=Vertical +height=64 +reversed=false +stuckOnGround=false +width=169 + +[11-slope@153,144|7] +grade=5 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[11-slope@283,42|14] +grade=2 +gradient=Opposite Diagonal +height=64 +reversed=false +stuckOnGround=false +width=73 + +[11-slope@284,106|11] +grade=2 +gradient=Horizontal +height=132 +reversed=true +stuckOnGround=false +width=70 + +[11-slope@284,235|13] +grade=2 +gradient=Diagonal +height=35 +reversed=true +stuckOnGround=false +width=73 + +[11-slope@286,279|21] +grade=4 +gradient=Elliptic +height=102 +reversed=false +stuckOnGround=false +width=102 + +[11-slope@40,235|15] +grade=2 +gradient=Opposite Diagonal +height=39 +reversed=true +stuckOnGround=false +width=77 + +[11-slope@40,42|12] +grade=2 +gradient=Diagonal +height=64 +reversed=false +stuckOnGround=false +width=78 + +[11-slope@42,106|9] +grade=2 +gradient=Horizontal +height=131 +reversed=false +stuckOnGround=false +width=75 + +[11-slope@9,278|20] +grade=4 +gradient=Elliptic +height=106 +reversed=false +stuckOnGround=false +width=106 + +[11-wall@0,0|16] +endPoint=115,276 +startPoint=116,388 + +[11-wall@0,0|17] +endPoint=287,271 +startPoint=287,388 + +[11-wall@0,0|18] +endPoint=115,276 +startPoint=11,276 + +[11-wall@0,0|19] +endPoint=388,271 +startPoint=287,271 + +[11-wall@0,0|3] +endPoint=141,171 +startPoint=141,222 + +[11-wall@0,0|4] +endPoint=224,259 +startPoint=183,259 + +[11-wall@0,0|5] +endPoint=265,169 +startPoint=265,221 + +[11-wall@0,0|6] +endPoint=228,132 +startPoint=180,132 + +[12-ball@191,359] +dummykey=true + +[12-cup@190,61|4] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[12-sand@-19,297|16] +changeEnabled=false +changeEvery=50 +height=394 +width=212 + +[12-sand@27,396|17] +changeEnabled=false +changeEvery=50 +height=192 +width=186 + +[12-slope@141,119|13] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=92 + +[12-wall@0,0|14] +endPoint=230,9 +startPoint=146,9 + +[12-wall@0,0|15] +endPoint=234,387 +startPoint=143,387 + +[12-wall@0,0|3] +endPoint=142,117 +startPoint=143,387 + +[12-wall@0,0|5] +endPoint=234,118 +startPoint=234,387 + +[12-wall@0,0|6] +endPoint=111,63 +startPoint=142,117 + +[12-wall@0,0|7] +endPoint=146,9 +startPoint=111,63 + +[12-wall@0,0|8] +endPoint=266,61 +startPoint=234,118 + +[12-wall@0,0|9] +endPoint=230,9 +startPoint=266,61 + +[12-windmill@142,146|12] +botWallVisible=false +bottom=true +height=47 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=false +width=92 + +[12-windmill@142,192|11] +botWallVisible=false +bottom=true +height=65 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=92 + +[12-windmill@142,257|10] +botWallVisible=false +bottom=true +height=49 +leftWallVisible=true +rightWallVisible=true +speed=3 +topWallVisible=false +width=92 + +[13-ball@42,350] +dummykey=true + +[13-blackhole@302,95|4] +exit=133,78 +exitDeg=180 +maxspeed=5 +minspeed=2 + +[13-cup@33,77|9] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[13-puddle@209,99|13] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[13-puddle@212,77|12] +changeEnabled=false +changeEvery=50 +height=46 +width=50 + +[13-puddle@214,120|14] +changeEnabled=false +changeEvery=50 +height=38 +width=48 + +[13-puddle@221,57|11] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[13-puddle@226,143|15] +changeEnabled=false +changeEvery=50 +height=46 +width=52 + +[13-puddle@236,38|10] +changeEnabled=false +changeEvery=50 +height=36 +width=46 + +[13-slope@237,31|3] +grade=8 +gradient=Elliptic +height=130 +reversed=true +stuckOnGround=false +width=130 + +[13-wall@0,0|16] +endPoint=368,23 +startPoint=237,24 + +[13-wall@0,0|17] +endPoint=368,23 +startPoint=367,187 + +[13-wall@0,0|18] +endPoint=367,187 +startPoint=16,391 + +[13-wall@0,0|19] +endPoint=237,24 +startPoint=16,348 + +[13-wall@0,0|20] +endPoint=16,391 +startPoint=16,348 + +[13-wall@0,0|21] +endPoint=172,201 +startPoint=142,216 + +[13-wall@0,0|22] +endPoint=163,268 +startPoint=134,250 + +[13-wall@0,0|23] +endPoint=242,187 +startPoint=223,221 + +[13-wall@0,0|24] +endPoint=206,280 +startPoint=199,247 + +[13-wall@0,0|25] +endPoint=120,277 +startPoint=90,292 + +[13-wall@0,0|5] +endPoint=137,19 +startPoint=10,17 + +[13-wall@0,0|6] +endPoint=138,142 +startPoint=137,19 + +[13-wall@0,0|7] +endPoint=138,142 +startPoint=10,143 + +[13-wall@0,0|8] +endPoint=10,17 +startPoint=10,143 + +[14-ball@265,361] +dummykey=true + +[14-cup@76,93|15] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-slope@105,206|18] +grade=4 +gradient=Opposite Diagonal +height=181 +reversed=true +stuckOnGround=false +width=248 + +[14-slope@108,13|20] +grade=4 +gradient=Diagonal +height=194 +reversed=false +stuckOnGround=false +width=245 + +[14-slope@351,14|21] +grade=4 +gradient=Opposite Diagonal +height=194 +reversed=false +stuckOnGround=false +width=50 + +[14-slope@351,206|22] +grade=4 +gradient=Diagonal +height=181 +reversed=true +stuckOnGround=false +width=49 + +[14-wall@0,0|10] +endPoint=50,37 +startPoint=20,68 + +[14-wall@0,0|11] +endPoint=20,68 +startPoint=20,109 + +[14-wall@0,0|12] +endPoint=20,109 +startPoint=49,152 + +[14-wall@0,0|13] +endPoint=100,151 +startPoint=49,152 + +[14-wall@0,0|14] +endPoint=131,135 +startPoint=100,151 + +[14-wall@0,0|16] +endPoint=246,207 +startPoint=213,206 + +[14-wall@0,0|17] +endPoint=313,206 +startPoint=271,207 + +[14-wall@0,0|20] +endPoint=145,110 +startPoint=131,135 + +[14-wall@0,0|3] +endPoint=213,207 +startPoint=212,387 + +[14-wall@0,0|4] +endPoint=312,206 +startPoint=313,387 + +[14-wall@0,0|5] +endPoint=313,387 +startPoint=212,387 + +[14-wall@0,0|6] +endPoint=131,135 +startPoint=213,206 + +[14-wall@0,0|7] +endPoint=138,69 +startPoint=313,206 + +[14-wall@0,0|8] +endPoint=138,69 +startPoint=108,37 + +[14-wall@0,0|9] +endPoint=108,37 +startPoint=50,37 + +[15-ball@82,336] +dummykey=true + +[15-cup@283,95|7] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[15-slope@-1,0|16] +grade=4 +gradient=Diagonal +height=319 +reversed=true +stuckOnGround=false +width=281 + +[15-slope@1,319|19] +grade=4 +gradient=Opposite Diagonal +height=80 +reversed=false +stuckOnGround=false +width=102 + +[15-slope@119,75|17] +grade=4 +gradient=Diagonal +height=322 +reversed=false +stuckOnGround=false +width=279 + +[15-slope@251,-1|18] +grade=4 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=150 + +[15-wall@0,0|10] +endPoint=98,277 +startPoint=98,218 + +[15-wall@0,0|11] +endPoint=190,301 +startPoint=132,301 + +[15-wall@0,0|12] +endPoint=283,197 +startPoint=222,197 + +[15-wall@0,0|13] +endPoint=193,167 +startPoint=193,106 + +[15-wall@0,0|14] +endPoint=159,240 +startPoint=158,196 + +[15-wall@0,0|15] +endPoint=202,240 +startPoint=159,240 + +[15-wall@0,0|3] +endPoint=10,315 +startPoint=269,18 + +[15-wall@0,0|4] +endPoint=377,88 +startPoint=113,394 + +[15-wall@0,0|5] +endPoint=377,88 +startPoint=269,18 + +[15-wall@0,0|6] +endPoint=113,394 +startPoint=10,315 + +[15-wall@0,0|8] +endPoint=250,96 +startPoint=250,133 + +[15-wall@0,0|9] +endPoint=290,134 +startPoint=250,133 + +[16-ball@132,358] +dummykey=true + +[16-blackhole@109,200|12] +exit=110,111 +exitDeg=90 +maxspeed=5 +minspeed=3 + +[16-cup@318,52|13] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[16-sand@233,399|19] +changeEnabled=false +changeEvery=50 +height=150 +width=86 + +[16-sand@307,259|17] +changeEnabled=false +changeEvery=50 +height=116 +width=26 + +[16-sand@387,206|18] +changeEnabled=false +changeEvery=50 +height=146 +width=100 + +[16-sand@402,371|16] +changeEnabled=false +changeEvery=50 +height=404 +width=358 + +[16-slope@177,19|14] +grade=4 +gradient=Opposite Diagonal +height=101 +reversed=true +stuckOnGround=false +width=175 + +[16-slope@55,115|11] +grade=5 +gradient=Vertical +height=224 +reversed=true +stuckOnGround=false +width=107 + +[16-wall@0,0|10] +endPoint=160,117 +startPoint=56,116 + +[16-wall@0,0|16] +endPoint=241,94 +startPoint=186,59 + +[16-wall@0,0|3] +endPoint=56,78 +startPoint=57,381 + +[16-wall@0,0|4] +endPoint=160,379 +startPoint=57,381 + +[16-wall@0,0|5] +endPoint=160,117 +startPoint=160,379 + +[16-wall@0,0|6] +endPoint=114,19 +startPoint=56,78 + +[16-wall@0,0|7] +endPoint=354,19 +startPoint=114,19 + +[16-wall@0,0|8] +endPoint=354,116 +startPoint=160,117 + +[16-wall@0,0|9] +endPoint=354,19 +startPoint=354,116 + +[16-windmill@288,278|20] +botWallVisible=true +bottom=false +height=96 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=86 + +[17-ball@91,316] +dummykey=true + +[17-bumper@152,251|14] +dummykey=true + +[17-bumper@175,181|12] +dummykey=true + +[17-bumper@214,222|13] +dummykey=true + +[17-bumper@245,156|15] +dummykey=true + +[17-bumper@333,114|16] +dummykey=true + +[17-cup@321,312|10] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-slope@-3,-2|17] +grade=2 +gradient=Diagonal +height=323 +reversed=true +stuckOnGround=false +width=291 + +[17-slope@288,190|18] +grade=2 +gradient=Vertical +height=156 +reversed=false +stuckOnGround=false +width=68 + +[17-wall@0,0|11] +endPoint=310,66 +startPoint=243,66 + +[17-wall@0,0|3] +endPoint=271,32 +startPoint=32,299 + +[17-wall@0,0|4] +endPoint=289,179 +startPoint=107,375 + +[17-wall@0,0|5] +endPoint=355,103 +startPoint=271,32 + +[17-wall@0,0|6] +endPoint=107,375 +startPoint=32,299 + +[17-wall@0,0|7] +endPoint=289,347 +startPoint=289,179 + +[17-wall@0,0|8] +endPoint=355,103 +startPoint=355,347 + +[17-wall@0,0|9] +endPoint=355,347 +startPoint=289,347 + +[18-ball@173,353] +dummykey=true + +[18-bridge@172,92|28] +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=30 + +[18-cup@188,44|7] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[18-puddle@215,126|17] +changeEnabled=false +changeEvery=50 +height=44 +width=322 + +[18-puddle@31,169|14] +changeEnabled=false +changeEvery=50 +height=44 +width=80 + +[18-puddle@341,133|18] +changeEnabled=false +changeEvery=50 +height=56 +width=176 + +[18-puddle@41,155|15] +changeEnabled=false +changeEvery=50 +height=52 +width=70 + +[18-puddle@5,178|27] +changeEnabled=false +changeEvery=50 +height=48 +width=122 + +[18-puddle@53,146|16] +changeEnabled=false +changeEvery=50 +height=36 +width=76 + +[18-puddle@74,135|18] +changeEnabled=false +changeEvery=50 +height=30 +width=90 + +[18-slope@122,156|16] +grade=2 +gradient=Vertical +height=222 +reversed=true +stuckOnGround=false +width=134 + +[18-slope@172,118|9] +grade=3 +gradient=Vertical +height=30 +reversed=true +stuckOnGround=false +width=30 + +[18-slope@172,89|10] +grade=3 +gradient=Vertical +height=30 +reversed=false +stuckOnGround=false +width=30 + +[18-wall@0,0|3] +endPoint=122,16 +startPoint=122,377 + +[18-wall@0,0|4] +endPoint=258,16 +startPoint=257,377 + +[18-wall@0,0|5] +endPoint=257,377 +startPoint=122,377 + +[18-wall@0,0|6] +endPoint=258,16 +startPoint=122,16 + +[2-ball@191,371] +dummykey=true + +[2-bumper@165,269|19] +dummykey=true + +[2-cup@362,65|14] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@-43,17|23] +changeEnabled=false +changeEvery=50 +height=380 +width=288 + +[2-sand@-28,-1|22] +changeEnabled=false +changeEvery=50 +height=502 +width=336 + +[2-sand@223,221|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[2-slope@139,151|19] +grade=3 +gradient=Vertical +height=193 +reversed=true +stuckOnGround=true +width=113 + +[2-slope@180,116|20] +grade=6 +gradient=Diagonal +height=33 +reversed=false +stuckOnGround=false +width=15 + +[2-slope@193,117|9] +grade=6 +gradient=Vertical +height=33 +reversed=false +stuckOnGround=false +width=42 + +[2-slope@235,116|21] +grade=6 +gradient=Opposite Diagonal +height=34 +reversed=false +stuckOnGround=false +width=16 + +[2-wall@0,0|10] +endPoint=244,38 +startPoint=140,151 + +[2-wall@0,0|11] +endPoint=296,91 +startPoint=251,153 + +[2-wall@0,0|12] +endPoint=389,37 +startPoint=244,38 + +[2-wall@0,0|13] +endPoint=389,91 +startPoint=297,92 + +[2-wall@0,0|16] +endPoint=274,69 +startPoint=296,91 + +[2-wall@0,0|17] +endPoint=314,60 +startPoint=294,41 + +[2-wall@0,0|20] +endPoint=250,388 +startPoint=139,388 + +[2-wall@0,0|21] +endPoint=389,37 +startPoint=389,91 + +[2-wall@0,0|3] +endPoint=139,149 +startPoint=139,388 + +[2-wall@0,0|4] +endPoint=251,153 +startPoint=250,388 + +[2-wall@0,0|7] +endPoint=191,149 +startPoint=140,151 + +[2-wall@0,0|8] +endPoint=251,153 +startPoint=230,153 + +[3-ball@197,369] +dummykey=true + +[3-blackhole@200,179|6] +exit=197,147 +exitDeg=90 +maxspeed=2 +minspeed=1 + +[3-cup@364,39|13] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-slope@-2,-1|18] +grade=4 +gradient=Diagonal +height=318 +reversed=true +stuckOnGround=false +width=150 + +[3-slope@143,152|5] +grade=5 +gradient=Vertical +height=197 +reversed=true +stuckOnGround=true +width=106 + +[3-slope@176,108|10] +grade=7 +gradient=Diagonal +height=42 +reversed=false +stuckOnGround=false +width=39 + +[3-slope@257,83|19] +grade=4 +gradient=Diagonal +height=319 +reversed=false +stuckOnGround=false +width=143 + +[3-wall@0,0|11] +endPoint=178,9 +startPoint=178,150 + +[3-wall@0,0|12] +endPoint=215,71 +startPoint=214,151 + +[3-wall@0,0|14] +endPoint=385,72 +startPoint=215,71 + +[3-wall@0,0|16] +endPoint=250,388 +startPoint=144,388 + +[3-wall@0,0|17] +endPoint=393,10 +startPoint=178,9 + +[3-wall@0,0|18] +endPoint=296,38 +startPoint=236,38 + +[3-wall@0,0|20] +endPoint=393,10 +startPoint=385,72 + +[3-wall@0,0|3] +endPoint=145,151 +startPoint=144,388 + +[3-wall@0,0|4] +endPoint=250,151 +startPoint=250,388 + +[3-wall@0,0|7] +endPoint=250,151 +startPoint=145,151 + +[3-wall@0,0|8] +endPoint=198,224 +startPoint=171,201 + +[3-wall@0,0|9] +endPoint=226,201 +startPoint=198,224 + +[4-ball@181,367] +dummykey=true + +[4-cup@360,284|14] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-puddle@-2,45|24] +changeEnabled=false +changeEvery=50 +height=314 +width=236 + +[4-puddle@67,-7|25] +changeEnabled=false +changeEvery=50 +height=194 +width=348 + +[4-slope@328,206|23] +grade=3 +gradient=Diagonal +height=180 +reversed=true +stuckOnGround=false +width=63 + +[4-slope@330,200|15] +grade=3 +gradient=Diagonal +height=186 +reversed=false +stuckOnGround=false +width=60 + +[4-wall@0,0|10] +endPoint=321,83 +startPoint=206,82 + +[4-wall@0,0|11] +endPoint=389,150 +startPoint=322,82 + +[4-wall@0,0|12] +endPoint=272,145 +startPoint=329,202 + +[4-wall@0,0|13] +endPoint=329,203 +startPoint=329,386 + +[4-wall@0,0|16] +endPoint=236,386 +startPoint=124,387 + +[4-wall@0,0|17] +endPoint=389,150 +startPoint=388,386 + +[4-wall@0,0|18] +endPoint=388,386 +startPoint=329,386 + +[4-wall@0,0|19] +endPoint=321,83 +startPoint=310,96 + +[4-wall@0,0|20] +endPoint=283,129 +startPoint=271,144 + +[4-wall@0,0|21] +endPoint=343,189 +startPoint=329,202 + +[4-wall@0,0|22] +endPoint=389,150 +startPoint=373,162 + +[4-wall@0,0|3] +endPoint=124,205 +startPoint=124,387 + +[4-wall@0,0|4] +endPoint=236,201 +startPoint=236,386 + +[4-wall@0,0|6] +endPoint=144,186 +startPoint=124,205 + +[4-wall@0,0|7] +endPoint=219,186 +startPoint=236,201 + +[4-wall@0,0|8] +endPoint=206,82 +startPoint=144,144 + +[4-wall@0,0|9] +endPoint=272,145 +startPoint=219,144 + +[4-windmill@144,144|5] +botWallVisible=false +bottom=true +height=42 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=false +width=75 + +[5-ball@194,372] +dummykey=true + +[5-bumper@246,44|15] +dummykey=true + +[5-cup@347,44|9] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-slope@-1,-1|16] +grade=4 +gradient=Horizontal +height=398 +reversed=false +stuckOnGround=false +width=140 + +[5-slope@139,126|10] +grade=5 +gradient=Vertical +height=225 +reversed=true +stuckOnGround=false +width=111 + +[5-slope@139,96|10] +grade=6 +gradient=Vertical +height=32 +reversed=false +stuckOnGround=false +width=112 + +[5-slope@252,98|17] +grade=4 +gradient=Horizontal +height=296 +reversed=true +stuckOnGround=false +width=148 + +[5-slope@260,84|18] +grade=4 +gradient=Horizontal +height=14 +reversed=true +stuckOnGround=false +width=171 + +[5-slope@319,18|11] +grade=6 +gradient=Elliptic +height=57 +reversed=false +stuckOnGround=false +width=57 + +[5-slope@387,0|19] +grade=4 +gradient=Horizontal +height=84 +reversed=true +stuckOnGround=false +width=17 + +[5-wall@0,0|12] +endPoint=250,388 +startPoint=140,388 + +[5-wall@0,0|13] +endPoint=388,7 +startPoint=227,8 + +[5-wall@0,0|14] +endPoint=388,7 +startPoint=387,80 + +[5-wall@0,0|3] +endPoint=140,95 +startPoint=140,388 + +[5-wall@0,0|4] +endPoint=250,95 +startPoint=250,388 + +[5-wall@0,0|6] +endPoint=227,8 +startPoint=140,95 + +[5-wall@0,0|7] +endPoint=266,80 +startPoint=250,95 + +[5-wall@0,0|8] +endPoint=387,80 +startPoint=266,80 + +[6-ball@212,370] +dummykey=true + +[6-bridge@193,151|8] +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=35 + +[6-cup@210,61|17] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-puddle@204,177|4] +changeEnabled=false +changeEvery=50 +height=38 +width=126 + +[6-puddle@238,175|5] +changeEnabled=false +changeEvery=50 +height=34 +width=92 + +[6-sand@419,109|18] +changeEnabled=false +changeEvery=50 +height=150 +width=126 + +[6-sand@436,9|17] +changeEnabled=false +changeEvery=50 +height=276 +width=228 + +[6-slope@142,8|20] +grade=3 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=140 + +[6-slope@193,143|9] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=35 + +[6-slope@193,168|8] +grade=3 +gradient=Vertical +height=30 +reversed=true +stuckOnGround=false +width=35 + +[6-wall@0,0|12] +endPoint=140,8 +startPoint=141,387 + +[6-wall@0,0|13] +endPoint=283,8 +startPoint=284,387 + +[6-wall@0,0|14] +endPoint=211,269 +startPoint=211,280 + +[6-wall@0,0|15] +endPoint=181,252 +startPoint=181,289 + +[6-wall@0,0|16] +endPoint=240,252 +startPoint=240,288 + +[6-wall@0,0|18] +endPoint=283,8 +startPoint=140,8 + +[6-wall@0,0|19] +endPoint=284,387 +startPoint=141,387 + +[7-ball@343,355] +dummykey=true + +[7-cup@235,349|12] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@170,137|7] +changeEnabled=false +changeEvery=50 +height=38 +width=120 + +[7-puddle@204,128|23] +changeEnabled=false +changeEvery=50 +height=48 +width=56 + +[7-puddle@257,247|8] +changeEnabled=false +changeEvery=50 +height=70 +width=40 + +[7-slope@10,149|19] +grade=2 +gradient=Vertical +height=27 +reversed=false +stuckOnGround=false +width=88 + +[7-slope@113,22|22] +grade=4 +gradient=Elliptic +height=68 +reversed=false +stuckOnGround=false +width=68 + +[7-slope@12,175|20] +grade=2 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=86 + +[7-slope@123,169|17] +grade=8 +gradient=Elliptic +height=104 +reversed=false +stuckOnGround=false +width=104 + +[7-slope@128,306|15] +grade=2 +gradient=Horizontal +height=82 +reversed=false +stuckOnGround=false +width=24 + +[7-slope@148,306|16] +grade=2 +gradient=Horizontal +height=81 +reversed=true +stuckOnGround=false +width=23 + +[7-slope@205,13|21] +grade=4 +gradient=Elliptic +height=68 +reversed=true +stuckOnGround=false +width=68 + +[7-slope@291,217|18] +grade=2 +gradient=Vertical +height=171 +reversed=false +stuckOnGround=false +width=96 + +[7-wall@0,0|10] +endPoint=75,9 +startPoint=10,74 + +[7-wall@0,0|11] +endPoint=77,388 +startPoint=11,322 + +[7-wall@0,0|13] +endPoint=291,334 +startPoint=266,308 + +[7-wall@0,0|14] +endPoint=292,362 +startPoint=265,388 + +[7-wall@0,0|3] +endPoint=293,91 +startPoint=292,389 + +[7-wall@0,0|4] +endPoint=293,91 +startPoint=97,91 + +[7-wall@0,0|5] +endPoint=97,91 +startPoint=97,308 + +[7-wall@0,0|6] +endPoint=292,308 +startPoint=97,308 + +[7-wall@0,0|9] +endPoint=389,77 +startPoint=323,10 + +[8-ball@197,360] +dummykey=true + +[8-cup@197,41|5] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-puddle@320,230|19] +changeEnabled=false +changeEvery=50 +height=62 +width=82 + +[8-puddle@324,228|22] +changeEnabled=false +changeEvery=50 +height=90 +width=88 + +[8-puddle@340,216|21] +changeEnabled=false +changeEvery=50 +height=54 +width=68 + +[8-puddle@340,243|20] +changeEnabled=false +changeEvery=50 +height=88 +width=122 + +[8-slope@138,205|14] +grade=3 +gradient=Opposite Diagonal +height=46 +reversed=false +stuckOnGround=false +width=52 + +[8-slope@140,139|16] +grade=3 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=54 + +[8-slope@19,32|18] +grade=4 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[8-slope@196,256|15] +grade=3 +gradient=Diagonal +height=53 +reversed=false +stuckOnGround=false +width=56 + +[8-slope@210,85|17] +grade=3 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-wall@0,0|10] +endPoint=252,87 +startPoint=216,87 + +[8-wall@0,0|11] +endPoint=252,388 +startPoint=139,389 + +[8-wall@0,0|12] +endPoint=252,10 +startPoint=139,9 + +[8-wall@0,0|13] +endPoint=190,141 +startPoint=140,141 + +[8-wall@0,0|3] +endPoint=139,9 +startPoint=139,389 + +[8-wall@0,0|4] +endPoint=252,10 +startPoint=252,388 + +[8-wall@0,0|6] +endPoint=251,310 +startPoint=197,310 + +[8-wall@0,0|7] +endPoint=190,251 +startPoint=141,251 + +[8-wall@0,0|8] +endPoint=251,192 +startPoint=199,192 + +[8-wall@0,0|9] +endPoint=176,87 +startPoint=140,87 + +[9-ball@275,369] +dummykey=true + +[9-cup@183,50|6] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[9-puddle@280,79|19] +changeEnabled=false +changeEvery=50 +height=54 +width=68 + +[9-sand@-11,294|21] +changeEnabled=false +changeEvery=50 +height=174 +width=152 + +[9-sand@-16,398|20] +changeEnabled=false +changeEvery=50 +height=254 +width=362 + +[9-slope@141,105|15] +grade=3 +gradient=Vertical +height=24 +reversed=true +stuckOnGround=false +width=84 + +[9-slope@141,79|14] +grade=3 +gradient=Vertical +height=26 +reversed=false +stuckOnGround=false +width=84 + +[9-slope@224,204|12] +grade=4 +gradient=Vertical +height=31 +reversed=false +stuckOnGround=false +width=86 + +[9-slope@224,298|10] +grade=3 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=false +width=87 + +[9-slope@226,235|13] +grade=4 +gradient=Vertical +height=25 +reversed=true +stuckOnGround=false +width=84 + +[9-slope@226,320|11] +grade=3 +gradient=Vertical +height=21 +reversed=true +stuckOnGround=false +width=85 + +[9-wall@0,0|16] +endPoint=310,387 +startPoint=225,388 + +[9-wall@0,0|17] +endPoint=225,9 +startPoint=142,9 + +[9-wall@0,0|18] +endPoint=225,160 +startPoint=225,169 + +[9-wall@0,0|3] +endPoint=226,205 +startPoint=225,388 + +[9-wall@0,0|4] +endPoint=226,205 +startPoint=142,203 + +[9-wall@0,0|5] +endPoint=142,9 +startPoint=142,203 + +[9-wall@0,0|7] +endPoint=311,128 +startPoint=310,387 + +[9-wall@0,0|8] +endPoint=311,128 +startPoint=225,128 + +[9-wall@0,0|9] +endPoint=225,9 +startPoint=225,128 diff --git a/kolf/courses/Easy.kolf b/kolf/courses/Easy.kolf new file mode 100644 index 00000000..97f7d292 --- /dev/null +++ b/kolf/courses/Easy.kolf @@ -0,0 +1,1737 @@ +[0-course@-50,-50] +Name=Easy Course +Name[af]=Maklike Natuurlik +Name[bg]=ЛеÑно +Name[bn]=সহজ খেলা +Name[bs]=Lagani teren +Name[ca]=Camp fàcil +Name[cs]=Jednoduchý +Name[da]=Nem bane +Name[de]=Leicht +Name[el]=ΕÏκολη πίστα +Name[es]=Campo sencillo +Name[et]=Lihtne väljak +Name[fi]=Helppo kenttä +Name[fr]=Parcours facile +Name[ga]=Cúrsa Éasca +Name[gl]=Campo sinxelo +Name[he]=מסלול קל +Name[hi]=आसान कोरà¥à¤¸ +Name[hr]=Lagani teren +Name[hu]=Könnyű pálya +Name[is]=Auðveld leið +Name[it]=Percorso facile +Name[ja]=ç°¡å˜ãªã‚³ãƒ¼ã‚¹ +Name[mk]=ЛеÑен терен +Name[nb]=Enkel bane +Name[nl]=Eenvoudig parcours +Name[nn]=Enkel bane +Name[nso]=Course ye Bonolo +Name[pl]=Åatwy tor +Name[pt]=Percurso Fácil +Name[pt_BR]=Curso Fácil +Name[ro]=Cale uÅŸoară +Name[ru]=Ð›Ñ‘Ð³ÐºÐ°Ñ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Jednoduché ihrisko +Name[sl]=Lahko igriÅ¡Äe +Name[sr]=Лак терен +Name[sr@Latn]=Lak teren +Name[sv]=Enkel bana +Name[ta]=சà¯à®²à®ªà®®à®¾à®© மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Сабук +Name[tr]=Kolay Pist +Name[uk]=Легкий +Name[ven]=Thero i Leluwaho +Name[xh]=Indlela Elula +Name[xx]=xxEasy Coursexx +Name[zh_CN]=è½»æ¾çš„路线 +Name[zh_TW]=簡單的路線 +Name[zu]=Indlela elula +author=Jason Katz-Brown +name=Easy Course + +[1-ball@267,126] +dummykey=true + +[1-cup@313,325|22] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-puddle@69,72|14] +changeEnabled=false +changeEvery=50 +height=92 +width=112 + +[1-puddle@84,103|19] +changeEnabled=false +changeEvery=50 +height=144 +width=96 + +[1-sand@-6,255|19] +changeEnabled=false +changeEvery=50 +height=256 +width=112 + +[1-sand@-6,368|18] +changeEnabled=false +changeEvery=50 +height=238 +width=190 + +[1-sand@33,295|0] +changeEnabled=false +changeEvery=50 +height=116 +width=54 + +[1-sand@37,261|1] +changeEnabled=false +changeEvery=50 +height=84 +width=34 + +[1-sand@40,303|2] +changeEnabled=false +changeEvery=50 +height=88 +width=54 + +[1-slope@211,282|21] +grade=3 +gradient=Horizontal +height=83 +reversed=false +stuckOnGround=false +width=61 + +[1-wall@0,0|10] +endPoint=347,282 +startPoint=348,364 + +[1-wall@0,0|11] +endPoint=347,282 +startPoint=211,281 + +[1-wall@0,0|12] +endPoint=195,257 +startPoint=211,281 + +[1-wall@0,0|13] +endPoint=281,208 +startPoint=195,257 + +[1-wall@0,0|3] +endPoint=343,148 +startPoint=281,208 + +[1-wall@0,0|4] +endPoint=327,85 +startPoint=343,148 + +[1-wall@0,0|5] +endPoint=327,85 +startPoint=273,56 + +[1-wall@0,0|6] +endPoint=273,56 +startPoint=122,182 + +[1-wall@0,0|7] +endPoint=122,182 +startPoint=112,349 + +[1-wall@0,0|8] +endPoint=138,368 +startPoint=112,349 + +[1-wall@0,0|9] +endPoint=348,364 +startPoint=138,368 + +[10-ball@107,335] +dummykey=true + +[10-blackhole@128,259|15] +exit=227,337 +exitDeg=220 +maxspeed=2 +minspeed=1 + +[10-cup@322,331|3] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-puddle@262,-7|45] +changeEnabled=false +changeEvery=50 +height=90 +width=166 + +[10-puddle@50,-21|44] +changeEnabled=false +changeEvery=50 +height=224 +width=466 + +[10-slope@109,151|41] +grade=4 +gradient=Horizontal +height=71 +reversed=true +stuckOnGround=false +width=54 + +[10-slope@150,349|45] +grade=8 +gradient=Horizontal +height=41 +reversed=false +stuckOnGround=false +width=44 + +[10-slope@162,329|46] +grade=7 +gradient=Horizontal +height=21 +reversed=false +stuckOnGround=false +width=33 + +[10-slope@173,121|42] +grade=6 +gradient=Elliptic +height=165 +reversed=false +stuckOnGround=false +width=165 + +[10-slope@21,275|47] +grade=7 +gradient=Vertical +height=65 +reversed=false +stuckOnGround=false +width=49 + +[10-wall@0,0|10] +endPoint=139,218 +startPoint=107,225 + +[10-wall@0,0|11] +endPoint=107,225 +startPoint=80,256 + +[10-wall@0,0|12] +endPoint=80,256 +startPoint=69,302 + +[10-wall@0,0|13] +endPoint=69,302 +startPoint=75,340 + +[10-wall@0,0|14] +endPoint=91,360 +startPoint=75,340 + +[10-wall@0,0|16] +endPoint=372,358 +startPoint=335,379 + +[10-wall@0,0|17] +endPoint=335,379 +startPoint=288,374 + +[10-wall@0,0|18] +endPoint=288,374 +startPoint=248,353 + +[10-wall@0,0|19] +endPoint=246,351 +startPoint=219,321 + +[10-wall@0,0|20] +endPoint=219,321 +startPoint=194,325 + +[10-wall@0,0|22] +endPoint=194,325 +startPoint=174,314 + +[10-wall@0,0|23] +endPoint=372,358 +startPoint=386,320 + +[10-wall@0,0|24] +endPoint=386,320 +startPoint=387,260 + +[10-wall@0,0|25] +endPoint=372,197 +startPoint=355,158 + +[10-wall@0,0|26] +endPoint=355,158 +startPoint=289,111 + +[10-wall@0,0|27] +endPoint=289,111 +startPoint=232,111 + +[10-wall@0,0|28] +endPoint=232,111 +startPoint=187,128 + +[10-wall@0,0|29] +endPoint=187,128 +startPoint=158,152 + +[10-wall@0,0|30] +endPoint=158,152 +startPoint=111,153 + +[10-wall@0,0|33] +endPoint=111,153 +startPoint=34,222 + +[10-wall@0,0|35] +endPoint=34,222 +startPoint=21,286 + +[10-wall@0,0|36] +endPoint=21,286 +startPoint=21,353 + +[10-wall@0,0|37] +endPoint=21,353 +startPoint=55,393 + +[10-wall@0,0|38] +endPoint=55,393 +startPoint=201,394 + +[10-wall@0,0|39] +endPoint=201,394 +startPoint=240,374 + +[10-wall@0,0|4] +endPoint=128,367 +startPoint=91,360 + +[10-wall@0,0|40] +endPoint=240,374 +startPoint=248,353 + +[10-wall@0,0|45] +endPoint=372,197 +startPoint=387,260 + +[10-wall@0,0|5] +endPoint=128,367 +startPoint=159,343 + +[10-wall@0,0|6] +endPoint=159,343 +startPoint=176,311 + +[10-wall@0,0|7] +endPoint=174,314 +startPoint=180,266 + +[10-wall@0,0|8] +endPoint=180,266 +startPoint=164,230 + +[10-wall@0,0|9] +endPoint=164,230 +startPoint=139,218 + +[11-ball@63,369] +dummykey=true + +[11-blackhole@116,216|10] +exit=275,307 +exitDeg=140 +maxspeed=4 +minspeed=2 + +[11-blackhole@124,260|6] +exit=23,381 +exitDeg=50 +maxspeed=6 +minspeed=5 + +[11-blackhole@128,126|21] +exit=103,387 +exitDeg=90 +maxspeed=2 +minspeed=1 + +[11-blackhole@139,184|7] +exit=347,109 +exitDeg=130 +maxspeed=2 +minspeed=1 + +[11-blackhole@163,234|11] +exit=24,25 +exitDeg=309 +maxspeed=5 +minspeed=3 + +[11-blackhole@164,136|4] +exit=15,147 +exitDeg=349 +maxspeed=7 +minspeed=6 + +[11-blackhole@187,159|12] +exit=281,22 +exitDeg=329 +maxspeed=2 +minspeed=1 + +[11-blackhole@194,207|5] +exit=308,183 +exitDeg=170 +maxspeed=10 +minspeed=3 + +[11-blackhole@59,187|9] +exit=101,20 +exitDeg=289 +maxspeed=5 +minspeed=3 + +[11-blackhole@82,233|8] +exit=301,235 +exitDeg=160 +maxspeed=8 +minspeed=4 + +[11-blackhole@86,154|13] +exit=198,384 +exitDeg=110 +maxspeed=5 +minspeed=3 + +[11-cup@307,85|3] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[11-sand@383,371|16] +changeEnabled=false +changeEvery=50 +height=214 +width=252 + +[11-sand@388,295|15] +changeEnabled=false +changeEvery=50 +height=178 +width=146 + +[11-slope@-15,24|17] +grade=2 +gradient=Elliptic +height=316 +reversed=true +stuckOnGround=false +width=316 + +[11-slope@226,1|17] +grade=4 +gradient=Elliptic +height=162 +reversed=true +stuckOnGround=false +width=162 + +[11-wall@0,0|18] +endPoint=242,70 +startPoint=185,27 + +[11-wall@0,0|19] +endPoint=242,70 +startPoint=288,157 + +[11-wall@0,0|20] +endPoint=185,27 +startPoint=72,11 + +[11-wall@0,0|22] +endPoint=81,346 +startPoint=84,388 + +[12-ball@247,85] +dummykey=true + +[12-cup@79,101|3] +dummykey=true + +[12-floater@245,248|11] +botWallVisible=false +endPoint=245,248 +height=66 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=241,177 +topWallVisible=false +width=47 + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[12-slope@205,213|21] +grade=4 +gradient=Horizontal +height=170 +reversed=false +stuckOnGround=true +width=85 + +[12-wall@0,0|10] +endPoint=361,153 +startPoint=339,75 + +[12-wall@0,0|13] +endPoint=38,326 +startPoint=36,166 + +[12-wall@0,0|14] +endPoint=88,382 +startPoint=296,383 + +[12-wall@0,0|15] +endPoint=346,333 +startPoint=313,214 + +[12-wall@0,0|17] +endPoint=38,326 +startPoint=88,382 + +[12-wall@0,0|20] +endPoint=346,333 +startPoint=296,383 + +[12-wall@0,0|4] +endPoint=339,75 +startPoint=246,29 + +[12-wall@0,0|5] +endPoint=246,29 +startPoint=137,64 + +[12-wall@0,0|7] +endPoint=137,166 +startPoint=215,213 + +[12-wall@0,0|8] +endPoint=215,213 +startPoint=314,213 + +[12-wall@0,0|9] +endPoint=313,214 +startPoint=361,153 + +[12-windmill@36,64|12] +botWallVisible=false +bottom=true +height=102 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=101 + +[13-ball@356,340] +dummykey=true + +[13-cup@213,189|3] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[13-puddle@166,190|8] +changeEnabled=false +changeEvery=50 +height=86 +width=22 + +[13-puddle@234,150|9] +changeEnabled=false +changeEvery=50 +height=26 +width=140 + +[13-puddle@276,228|7] +changeEnabled=false +changeEvery=50 +height=28 +width=230 + +[13-sand@137,322|14] +changeEnabled=false +changeEvery=50 +height=96 +width=12 + +[13-sand@359,72|11] +changeEnabled=false +changeEvery=50 +height=76 +width=14 + +[13-sand@362,118|12] +changeEnabled=false +changeEvery=50 +height=72 +width=10 + +[13-sign@219,247|14] +Comment=Hit slowly... +Comment[af]=Getref stadig... +Comment[ar]=إضرب ببطء... +Comment[bg]=Леки и технични удари... +Comment[bn]=ধীরে আঘাত করà§à¦¨... +Comment[bs]=Polako pucaj... +Comment[ca]=Picar lentament... +Comment[cs]=Trefujte pomalu... +Comment[da]=SlÃ¥ langsomt... +Comment[de]=Langsam... +Comment[el]=Ρίξε αÏγά... +Comment[es]=Golpear lentamente... +Comment[et]=Löö aeglaselt... +Comment[fi]=Lyö hitaasti... +Comment[fr]=Taper doucement... +Comment[gl]=Golpear a modo +Comment[he]=חבוט ל×ט... +Comment[hi]=धीरे से मारें... +Comment[hr]=Udarite polako... +Comment[hu]=Lassan megütni... +Comment[is]=Sláðu varlega... +Comment[it]=Colpisci piano... +Comment[ja]=ゆã£ãり打ã¨ã†... +Comment[lv]=Sit lÄ“nÄm... +Comment[mk]=Удрете пополека... +Comment[nb]=SlÃ¥ forsiktig ... +Comment[nl]=Zachtjes slaan... +Comment[nn]=SlÃ¥ forsiktig ... +Comment[nso]=Betha kago nanya... +Comment[pl]=Powolne uderzenie... +Comment[pt]=Bata devagarinho... +Comment[pt_BR]=Arremessar lentamente... +Comment[ro]=LoveÅŸte încet... +Comment[ru]=Бейте не Ñпеша... +Comment[sk]=Jemné údery... +Comment[sl]=Udarite poÄasi ... +Comment[sr]=Ударите полако... +Comment[sr@Latn]=Udarite polako... +Comment[sv]=SlÃ¥ lÃ¥ngsamt... +Comment[ta]=மெதà¯à®µà®¾à®• அடிகà¯à®•à®µà¯à®®à¯... +Comment[tg]=ОҳиÑта занед... +Comment[tr]=YavaÅŸ basın... +Comment[uk]=Бийте без поÑпіху... +Comment[ven]=Irwani nga u ongolowa... +Comment[xh]=Betha ngokucothayo... +Comment[xx]=xxHit slowly...xx +Comment[zh_CN]=缓慢击打... +Comment[zh_TW]=緩慢地敲擊... +Comment[zu]=Shaya kancane... +botWallVisible=true +height=55 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=180 + +[13-slope@12,11|8] +grade=2 +gradient=Horizontal +height=124 +reversed=true +stuckOnGround=false +width=373 + +[13-slope@12,133|7] +grade=2 +gradient=Vertical +height=254 +reversed=false +stuckOnGround=false +width=139 + +[13-slope@139,266|6] +grade=2 +gradient=Horizontal +height=121 +reversed=false +stuckOnGround=false +width=248 + +[13-wall@0,0|10] +endPoint=385,132 +startPoint=365,12 + +[14-ball@94,323] +dummykey=true + +[14-bridge@261,66|13] +botWallVisible=false +height=91 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=99 + +[14-bumper@135,356|12] +dummykey=true + +[14-cup@136,355|13] +dummykey=true + +[14-cup@147,145|3] +dummykey=true + +[14-cup@293,288|8] +dummykey=true + +[14-cup@313,113|11] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-puddle@292,288|7] +changeEnabled=false +changeEvery=50 +height=102 +width=102 + +[14-sand@145,142|5] +changeEnabled=false +changeEvery=50 +height=130 +width=132 + +[14-slope@219,215|6] +grade=6 +gradient=Elliptic +height=145 +reversed=false +stuckOnGround=false +width=145 + +[14-slope@233,34|9] +grade=3 +gradient=Elliptic +height=160 +reversed=false +stuckOnGround=false +width=160 + +[14-slope@58,54|4] +grade=7 +gradient=Elliptic +height=177 +reversed=false +stuckOnGround=false +width=177 + +[15-ball@32,360] +dummykey=true + +[15-cup@357,168|3] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[15-slope@10,9|6] +grade=4 +gradient=Vertical +height=332 +reversed=true +stuckOnGround=false +width=63 + +[15-slope@34,12|4] +grade=4 +gradient=Diagonal +height=376 +reversed=false +stuckOnGround=false +width=355 + +[15-slope@71,10|5] +grade=4 +gradient=Diagonal +height=332 +reversed=true +stuckOnGround=false +width=313 + +[16-ball@299,105] +dummykey=true + +[16-blackhole@308,282|23] +exit=358,136 +exitDeg=160 +maxspeed=3 +minspeed=2 + +[16-blackhole@37,334|24] +exit=227,218 +exitDeg=239 +maxspeed=1 +minspeed=0 + +[16-cup@106,333|3] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[16-slope@183,211|22] +grade=4 +gradient=Elliptic +height=108 +reversed=true +stuckOnGround=false +width=108 + +[16-slope@48,70|20] +grade=6 +gradient=Elliptic +height=100 +reversed=false +stuckOnGround=false +width=100 + +[16-wall@0,0|10] +endPoint=371,115 +startPoint=323,59 + +[16-wall@0,0|11] +endPoint=323,59 +startPoint=64,58 + +[16-wall@0,0|12] +endPoint=64,58 +startPoint=27,141 + +[16-wall@0,0|13] +endPoint=81,374 +startPoint=61,341 + +[16-wall@0,0|14] +endPoint=27,292 +startPoint=68,281 + +[16-wall@0,0|15] +endPoint=68,281 +startPoint=170,269 + +[16-wall@0,0|16] +endPoint=170,269 +startPoint=173,249 + +[16-wall@0,0|17] +endPoint=173,249 +startPoint=155,236 + +[16-wall@0,0|18] +endPoint=155,236 +startPoint=49,225 + +[16-wall@0,0|19] +endPoint=49,225 +startPoint=27,141 + +[16-wall@0,0|25] +endPoint=61,341 +startPoint=49,366 + +[16-wall@0,0|26] +endPoint=27,368 +startPoint=13,351 + +[16-wall@0,0|27] +endPoint=13,351 +startPoint=12,312 + +[16-wall@0,0|28] +endPoint=12,312 +startPoint=27,292 + +[16-wall@0,0|29] +endPoint=27,368 +startPoint=49,366 + +[16-wall@0,0|30] +endPoint=346,277 +startPoint=335,244 + +[16-wall@0,0|4] +endPoint=319,316 +startPoint=81,374 + +[16-wall@0,0|5] +endPoint=319,316 +startPoint=346,277 + +[16-wall@0,0|6] +endPoint=335,244 +startPoint=156,185 + +[16-wall@0,0|7] +endPoint=156,185 +startPoint=160,161 + +[16-wall@0,0|8] +endPoint=160,161 +startPoint=357,159 + +[16-wall@0,0|9] +endPoint=357,159 +startPoint=371,115 + +[17-ball@37,125] +dummykey=true + +[17-cup@356,45|3] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-wall@0,0|10] +endPoint=69,156 +startPoint=136,161 + +[17-wall@0,0|11] +endPoint=136,161 +startPoint=133,359 + +[17-wall@0,0|12] +endPoint=133,359 +startPoint=117,338 + +[17-wall@0,0|13] +endPoint=134,210 +startPoint=71,208 + +[17-wall@0,0|14] +endPoint=71,247 +startPoint=138,248 + +[17-wall@0,0|15] +endPoint=243,191 +startPoint=195,152 + +[17-wall@0,0|16] +endPoint=278,190 +startPoint=326,152 + +[17-wall@0,0|17] +endPoint=312,189 +startPoint=180,199 + +[17-wall@0,0|18] +endPoint=312,189 +startPoint=355,196 + +[17-wall@0,0|19] +endPoint=322,242 +startPoint=175,248 + +[17-wall@0,0|20] +endPoint=322,242 +startPoint=348,249 + +[17-wall@0,0|21] +endPoint=264,147 +startPoint=256,208 + +[17-wall@0,0|22] +endPoint=256,208 +startPoint=226,257 + +[17-wall@0,0|23] +endPoint=226,257 +startPoint=175,295 + +[17-wall@0,0|24] +endPoint=303,260 +startPoint=271,198 + +[17-wall@0,0|25] +endPoint=265,365 +startPoint=265,266 + +[17-wall@0,0|26] +endPoint=303,260 +startPoint=355,291 + +[17-wall@0,0|27] +endPoint=265,365 +startPoint=251,351 + +[17-wall@0,0|28] +endPoint=224,287 +startPoint=245,306 + +[17-wall@0,0|29] +endPoint=242,322 +startPoint=209,343 + +[17-wall@0,0|31] +endPoint=320,352 +startPoint=271,320 + +[17-wall@0,0|4] +endPoint=277,76 +startPoint=66,83 + +[17-wall@0,0|5] +endPoint=277,76 +startPoint=323,87 + +[17-wall@0,0|6] +endPoint=121,41 +startPoint=131,118 + +[17-wall@0,0|7] +endPoint=272,44 +startPoint=248,117 + +[17-wall@0,0|8] +endPoint=68,316 +startPoint=69,156 + +[17-wall@0,0|9] +endPoint=68,316 +startPoint=47,358 + +[17-wall@3,0|30] +endPoint=316,280 +startPoint=279,302 + +[18-ball@246,335] +dummykey=true + +[18-cup@109,107|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[18-puddle@348,-8|18] +changeEnabled=false +changeEvery=50 +height=100 +width=272 + +[18-puddle@410,-2|17] +changeEnabled=false +changeEvery=50 +height=280 +width=172 + +[18-slope@164,26|16] +grade=4 +gradient=Horizontal +height=107 +reversed=false +stuckOnGround=false +width=219 + +[18-wall@0,0|10] +endPoint=367,151 +startPoint=315,47 + +[18-wall@0,0|11] +endPoint=315,47 +startPoint=209,26 + +[18-wall@0,0|12] +endPoint=209,26 +startPoint=66,47 + +[18-wall@0,0|13] +endPoint=66,47 +startPoint=41,105 + +[18-wall@0,0|14] +endPoint=41,105 +startPoint=89,175 + +[18-wall@0,0|15] +endPoint=89,175 +startPoint=168,175 + +[18-wall@0,0|19] +endPoint=140,314 +startPoint=169,361 + +[18-wall@0,0|20] +endPoint=140,314 +startPoint=167,256 + +[18-wall@0,0|6] +endPoint=299,377 +startPoint=169,361 + +[18-wall@0,0|7] +endPoint=299,377 +startPoint=362,323 + +[18-wall@0,0|8] +endPoint=362,323 +startPoint=387,252 + +[18-wall@0,0|9] +endPoint=387,252 +startPoint=367,151 + +[18-windmill@167,133|4] +botWallVisible=false +bottom=true +height=123 +leftWallVisible=true +rightWallVisible=true +speed=3 +topWallVisible=false +width=180 + +[18-windmill@167,171|5] +botWallVisible=false +bottom=true +height=85 +leftWallVisible=false +rightWallVisible=false +speed=5 +topWallVisible=false +width=179 + +[2-ball@60,82] +dummykey=true + +[2-cup@343,362|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@379,-8|10] +changeEnabled=false +changeEvery=50 +height=172 +width=206 + +[2-puddle@397,65|13] +changeEnabled=false +changeEvery=50 +height=96 +width=112 + +[2-slope@10,232|16] +grade=4 +gradient=Vertical +height=158 +reversed=false +stuckOnGround=false +width=215 + +[2-slope@224,156|9] +grade=3 +gradient=Vertical +height=76 +reversed=true +stuckOnGround=false +width=167 + +[2-wall@0,0|4] +endPoint=130,12 +startPoint=130,210 + +[2-wall@0,0|5] +endPoint=225,129 +startPoint=225,388 + +[3-ball@210,356] +dummykey=true + +[3-bumper@54,246|9] +dummykey=true + +[3-bumper@98,299|8] +dummykey=true + +[3-cup@341,351|12] +dummykey=true + +[3-cup@62,354|11] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-slope@184,11|7] +grade=4 +gradient=Vertical +height=177 +reversed=true +stuckOnGround=false +width=21 + +[3-slope@205,11|6] +grade=4 +gradient=Diagonal +height=177 +reversed=true +stuckOnGround=false +width=162 + +[3-slope@32,10|5] +grade=4 +gradient=Opposite Diagonal +height=179 +reversed=true +stuckOnGround=false +width=154 + +[3-wall@0,0|3] +endPoint=262,212 +startPoint=263,389 + +[3-wall@0,0|4] +endPoint=159,213 +startPoint=159,386 + +[3-windmill@263,288|10] +botWallVisible=false +bottom=false +height=101 +leftWallVisible=true +rightWallVisible=false +speed=2 +topWallVisible=false +width=125 + +[4-ball@91,344] +dummykey=true + +[4-cup@291,91|18] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-puddle@115,111|7] +changeEnabled=false +changeEvery=50 +height=70 +width=86 + +[4-puddle@125,133|3] +changeEnabled=false +changeEvery=50 +height=62 +width=66 + +[4-puddle@136,121|5] +changeEnabled=false +changeEvery=50 +height=60 +width=72 + +[4-puddle@277,291|15] +changeEnabled=false +changeEvery=50 +height=76 +width=90 + +[4-puddle@296,322|11] +changeEnabled=false +changeEvery=50 +height=128 +width=98 + +[4-slope@255,136|20] +grade=4 +gradient=Elliptic +height=123 +reversed=false +stuckOnGround=false +width=123 + +[4-slope@28,155|19] +grade=3 +gradient=Elliptic +height=145 +reversed=false +stuckOnGround=false +width=145 + +[5-ball@197,252] +dummykey=true + +[5-cup@85,77|33] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@150,258|42] +changeEnabled=false +changeEvery=50 +height=134 +width=62 + +[5-puddle@198,207|11] +changeEnabled=false +changeEvery=50 +height=52 +width=130 + +[5-puddle@248,182|10] +changeEnabled=false +changeEvery=50 +height=76 +width=42 + +[5-puddle@278,158|11] +changeEnabled=false +changeEvery=50 +height=44 +width=84 + +[5-slope@14,195|36] +grade=3 +gradient=Elliptic +height=106 +reversed=true +stuckOnGround=false +width=106 + +[5-slope@160,169|15] +grade=4 +gradient=Diagonal +height=220 +reversed=false +stuckOnGround=false +width=227 + +[5-slope@181,12|35] +grade=3 +gradient=Elliptic +height=128 +reversed=false +stuckOnGround=false +width=128 + +[5-wall@0,0|9] +endPoint=127,390 +startPoint=13,314 + +[6-ball@351,186] +dummykey=true + +[6-blackhole@306,352|12] +exit=16,145 +exitDeg=80 +maxspeed=2 +minspeed=2 + +[6-blackhole@367,38|11] +exit=19,10 +exitDeg=279 +maxspeed=1 +minspeed=1 + +[6-bridge@281,64|6] +botWallVisible=true +height=155 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=116 + +[6-bridge@5,2|7] +botWallVisible=true +height=149 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=96 + +[6-cup@30,67|9] +dummykey=true + +[6-floater@78,34|8] +botWallVisible=true +endPoint=78,34 +height=78 +leftWallVisible=false +rightWallVisible=false +speed=2 +startPoint=207,104 +topWallVisible=true +width=128 + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[6-puddle@198,123|6] +changeEnabled=false +changeEvery=50 +height=394 +width=316 + +[6-slope@109,429|8] +grade=4 +gradient=Elliptic +height=77 +reversed=true +stuckOnGround=false +width=77 + +[6-slope@99,35|18] +grade=3 +gradient=Elliptic +height=75 +reversed=true +stuckOnGround=false +width=75 + +[6-wall@0,0|10] +endPoint=381,9 +startPoint=392,65 + +[6-wall@0,0|13] +endPoint=381,218 +startPoint=386,329 + +[6-wall@0,0|14] +endPoint=386,329 +startPoint=299,391 + +[6-wall@0,0|15] +endPoint=299,391 +startPoint=39,345 + +[6-wall@0,0|16] +endPoint=39,345 +startPoint=14,204 + +[6-wall@0,0|17] +endPoint=14,204 +startPoint=24,152 + +[6-wall@0,0|9] +endPoint=381,9 +startPoint=317,3 + +[7-ball@65,363] +dummykey=true + +[7-cup@309,83|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@337,401|4] +changeEnabled=false +changeEvery=50 +height=504 +width=412 + +[7-slope@-2,-3|5] +grade=2 +gradient=Diagonal +height=353 +reversed=true +stuckOnGround=false +width=394 + +[7-slope@247,27|10] +grade=2 +gradient=Elliptic +height=123 +reversed=true +stuckOnGround=false +width=123 + +[7-wall@0,0|6] +endPoint=370,157 +startPoint=379,91 + +[7-wall@0,0|7] +endPoint=379,91 +startPoint=370,42 + +[7-wall@0,0|8] +endPoint=370,42 +startPoint=321,15 + +[7-wall@0,0|9] +endPoint=321,15 +startPoint=110,16 + +[8-ball@324,331] +dummykey=true + +[8-cup@171,249|32] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-slope@212,51|33] +grade=2 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[8-slope@84,231|34] +grade=4 +gradient=Elliptic +height=51 +reversed=false +stuckOnGround=false +width=51 + +[8-wall@0,0|10] +endPoint=104,210 +startPoint=84,204 + +[8-wall@0,0|11] +endPoint=84,204 +startPoint=80,183 + +[8-wall@0,0|12] +endPoint=80,183 +startPoint=105,150 + +[8-wall@0,0|13] +endPoint=105,150 +startPoint=159,137 + +[8-wall@0,0|14] +endPoint=159,137 +startPoint=222,143 + +[8-wall@0,0|15] +endPoint=222,143 +startPoint=275,180 + +[8-wall@0,0|16] +endPoint=275,180 +startPoint=305,263 + +[8-wall@0,0|17] +endPoint=305,263 +startPoint=276,308 + +[8-wall@0,0|18] +endPoint=276,308 +startPoint=275,337 + +[8-wall@0,0|19] +endPoint=275,337 +startPoint=285,369 + +[8-wall@0,0|20] +endPoint=285,369 +startPoint=310,382 + +[8-wall@0,0|21] +endPoint=310,382 +startPoint=346,377 + +[8-wall@0,0|22] +endPoint=346,377 +startPoint=377,350 + +[8-wall@0,0|23] +endPoint=377,350 +startPoint=382,302 + +[8-wall@0,0|24] +endPoint=382,302 +startPoint=382,136 + +[8-wall@0,0|25] +endPoint=382,136 +startPoint=338,61 + +[8-wall@0,0|26] +endPoint=338,61 +startPoint=243,30 + +[8-wall@0,0|27] +endPoint=243,30 +startPoint=99,43 + +[8-wall@0,0|28] +endPoint=99,43 +startPoint=35,104 + +[8-wall@0,0|29] +endPoint=35,104 +startPoint=18,230 + +[8-wall@0,0|3] +endPoint=147,351 +startPoint=83,329 + +[8-wall@0,0|30] +endPoint=18,230 +startPoint=32,285 + +[8-wall@0,0|31] +endPoint=32,285 +startPoint=83,329 + +[8-wall@0,0|4] +endPoint=147,351 +startPoint=230,330 + +[8-wall@0,0|5] +endPoint=230,330 +startPoint=271,265 + +[8-wall@0,0|6] +endPoint=271,265 +startPoint=247,193 + +[8-wall@0,0|7] +endPoint=247,193 +startPoint=202,157 + +[8-wall@0,0|8] +endPoint=202,157 +startPoint=138,165 + +[8-wall@0,0|9] +endPoint=138,165 +startPoint=104,210 + +[9-ball@342,333] +dummykey=true + +[9-blackhole@73,307|3] +exit=385,46 +exitDeg=180 +maxspeed=6 +minspeed=1 + +[9-cup@314,199|9] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-sand@155,305|6] +changeEnabled=false +changeEvery=50 +height=70 +width=48 + +[9-sand@165,309|7] +changeEnabled=false +changeEvery=50 +height=40 +width=34 + +[9-sand@174,305|8] +changeEnabled=false +changeEvery=50 +height=28 +width=44 + +[9-sand@179,290|5] +changeEnabled=false +changeEvery=50 +height=52 +width=92 + +[9-wall@0,0|10] +endPoint=386,143 +startPoint=140,120 + +[9-wall@0,0|11] +endPoint=106,12 +startPoint=10,50 + +[9-wall@0,0|4] +endPoint=388,253 +startPoint=12,223 diff --git a/kolf/courses/Hard.kolf b/kolf/courses/Hard.kolf new file mode 100644 index 00000000..47ba23bf --- /dev/null +++ b/kolf/courses/Hard.kolf @@ -0,0 +1,4403 @@ +[0-course@-50,-50] +Name=Hard Course +Name[af]=Hard Natuurlik +Name[bg]=Трудно +Name[bn]=কঠিন খেলা +Name[bs]=Težak teren +Name[ca]=Camp difícil +Name[cs]=Těžký +Name[da]=Svær bane +Name[de]=Schwer +Name[el]=ΔÏσκολη πίστα +Name[es]=Campo difícil +Name[et]=Raske väljak +Name[fi]=Vaikea kenttä +Name[fr]=Parcours difficile +Name[ga]=Cúrsa Crua +Name[gl]=Campo difícil +Name[he]=מסלול קשה +Name[hi]=कठिन कोरà¥à¤¸ +Name[hu]=Nehéz pálya +Name[is]=Erfið braut +Name[it]=Percorso difficile +Name[ja]=難ã—ã„コース +Name[mk]=Тежок терен +Name[nb]=Vanskelig bane +Name[nl]=Moeilijk parcours +Name[nn]=Vanskeleg bane +Name[nso]=Course ye Bothata +Name[pl]=Trudny tor +Name[pt]=Percurso Difícil +Name[pt_BR]=Curso Difícil +Name[ro]=Cale grea +Name[ru]=Ð¡Ð»Ð¾Ð¶Ð½Ð°Ñ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Ťažké ihrisko +Name[sl]=Težko igriÅ¡Äe +Name[sr]=Тежак терен +Name[sr@Latn]=Težak teren +Name[sv]=SvÃ¥r bana +Name[ta]=கடினமான மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Душвор +Name[tr]=Zor Pist +Name[uk]=Складна майданчик +Name[ven]=Thero i Kondaho +Name[xh]=Indlela enzima +Name[xx]=xxHard Coursexx +Name[zh_CN]=困难的路线 +Name[zh_TW]=困難的路線 +Name[zu]=Indlela elukhuni +author=Jason Katz-Brown + +[1-ball@84,73] +dummykey=true + +[1-blackhole@83,203|10] +exit=111,214 +exitDeg=330 +maxspeed=5 +minspeed=3 + +[1-cup@40,198|5] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[1-puddle@-24,361|16] +changeEnabled=false +changeEvery=50 +height=124 +width=168 + +[1-puddle@1,336|17] +changeEnabled=false +changeEvery=50 +height=60 +width=110 + +[1-puddle@34,377|22] +changeEnabled=false +changeEvery=50 +height=54 +width=90 + +[1-puddle@42,351|18] +changeEnabled=false +changeEvery=50 +height=56 +width=40 + +[1-puddle@51,352|19] +changeEnabled=false +changeEvery=50 +height=32 +width=36 + +[1-puddle@54,363|20] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[1-puddle@62,373|21] +changeEnabled=false +changeEvery=50 +height=52 +width=36 + +[1-sand@169,250|21] +changeEnabled=false +changeEvery=50 +height=60 +width=46 + +[1-sand@174,240|23] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[1-slope@10,135|15] +grade=3 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=119 + +[1-slope@10,229|6] +grade=3 +gradient=Vertical +height=60 +reversed=false +stuckOnGround=false +width=83 + +[1-slope@221,145|17] +grade=4 +gradient=Elliptic +height=77 +reversed=false +stuckOnGround=false +width=77 + +[1-slope@56,177|18] +grade=4 +gradient=Elliptic +height=52 +reversed=true +stuckOnGround=false +width=52 + +[1-slope@65,78|24] +grade=2 +gradient=Diagonal +height=309 +reversed=false +stuckOnGround=false +width=323 + +[1-slope@8,14|14] +grade=2 +gradient=Opposite Diagonal +height=105 +reversed=true +stuckOnGround=false +width=383 + +[1-wall@0,0|17] +endPoint=129,176 +startPoint=129,137 + +[1-wall@0,0|24] +endPoint=94,287 +startPoint=93,234 + +[1-wall@0,0|3] +endPoint=93,234 +startPoint=129,176 + +[1-wall@0,0|4] +endPoint=129,137 +startPoint=14,137 + +[10-ball@308,365] +dummykey=true + +[10-cup@66,343|3] +dummykey=true + +[10-floater@171,129|54] +botWallVisible=false +endPoint=171,129 +height=53 +leftWallVisible=true +rightWallVisible=false +speed=3 +startPoint=25,212 +topWallVisible=true +width=76 + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[10-puddle@111,207|40] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@115,231|25] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@136,250|28] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@139,193|58] +changeEnabled=false +changeEvery=50 +height=162 +width=122 + +[10-puddle@163,123|12] +changeEnabled=false +changeEvery=50 +height=40 +width=104 + +[10-puddle@164,260|36] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@188,144|52] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@190,268|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@196,261|35] +changeEnabled=false +changeEvery=50 +height=64 +width=60 + +[10-puddle@199,237|37] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[10-puddle@200,157|53] +changeEnabled=false +changeEvery=50 +height=94 +width=72 + +[10-puddle@200,200|54] +changeEnabled=false +changeEvery=50 +height=134 +width=72 + +[10-puddle@206,276|39] +changeEnabled=false +changeEvery=50 +height=48 +width=36 + +[10-slope@112,365|52] +grade=2 +gradient=Vertical +height=19 +reversed=true +stuckOnGround=false +width=98 + +[10-slope@12,271|46] +grade=2 +gradient=Diagonal +height=45 +reversed=false +stuckOnGround=true +width=20 + +[10-slope@12,315|53] +grade=2 +gradient=Horizontal +height=52 +reversed=false +stuckOnGround=true +width=21 + +[10-slope@134,308|48] +grade=2 +gradient=Vertical +height=23 +reversed=false +stuckOnGround=true +width=74 + +[10-slope@149,30|31] +grade=3 +gradient=Horizontal +height=85 +reversed=true +stuckOnGround=true +width=52 + +[10-slope@18,32|60] +grade=3 +gradient=Diagonal +height=59 +reversed=true +stuckOnGround=false +width=138 + +[10-slope@199,133|59] +grade=4 +gradient=Horizontal +height=69 +reversed=true +stuckOnGround=true +width=52 + +[10-slope@203,87|60] +grade=4 +gradient=Opposite Diagonal +height=58 +reversed=false +stuckOnGround=true +width=48 + +[10-slope@207,306|49] +grade=2 +gradient=Opposite Diagonal +height=28 +reversed=false +stuckOnGround=false +width=29 + +[10-slope@209,331|50] +grade=2 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=28 + +[10-slope@209,363|51] +grade=2 +gradient=Diagonal +height=19 +reversed=true +stuckOnGround=false +width=29 + +[10-slope@211,199|61] +grade=4 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=true +width=40 + +[10-slope@250,140|60] +grade=1 +gradient=Horizontal +height=243 +reversed=false +stuckOnGround=false +width=136 + +[10-slope@264,102|61] +grade=1 +gradient=Diagonal +height=39 +reversed=false +stuckOnGround=true +width=124 + +[10-slope@32,275|44] +grade=2 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=true +width=59 + +[10-slope@89,276|45] +grade=2 +gradient=Opposite Diagonal +height=55 +reversed=false +stuckOnGround=true +width=75 + +[10-slope@9,365|47] +grade=2 +gradient=Opposite Diagonal +height=23 +reversed=true +stuckOnGround=false +width=104 + +[10-wall@0,0|10] +endPoint=77,31 +startPoint=29,60 + +[10-wall@0,0|11] +endPoint=29,60 +startPoint=11,187 + +[10-wall@0,0|4] +endPoint=229,252 +startPoint=288,385 + +[10-wall@0,0|5] +endPoint=232,206 +startPoint=229,252 + +[10-wall@0,0|57] +endPoint=338,159 +startPoint=302,131 + +[10-wall@0,0|6] +endPoint=389,148 +startPoint=355,92 + +[10-wall@0,0|62] +endPoint=6,316 +startPoint=11,187 + +[10-wall@0,0|63] +endPoint=8,374 +startPoint=6,316 + +[10-wall@0,0|64] +endPoint=35,387 +startPoint=8,374 + +[10-wall@0,0|65] +endPoint=70,392 +startPoint=35,387 + +[10-wall@0,0|66] +endPoint=246,397 +startPoint=70,392 + +[10-wall@0,0|67] +endPoint=275,355 +startPoint=246,397 + +[10-wall@0,0|68] +endPoint=387,384 +startPoint=288,385 + +[10-wall@0,0|69] +endPoint=389,148 +startPoint=387,384 + +[10-wall@0,0|7] +endPoint=355,92 +startPoint=274,56 + +[10-wall@0,0|8] +endPoint=274,56 +startPoint=179,31 + +[10-wall@0,0|9] +endPoint=179,31 +startPoint=77,31 + +[11-ball@27,26] +dummykey=true + +[11-blackhole@-23,90|4] +exit=-26,293 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[11-blackhole@293,44|16] +exit=132,260 +exitDeg=0 +maxspeed=3 +minspeed=2 + +[11-cup@221,409|13] +dummykey=true + +[11-cup@223,332|15] +dummykey=true + +[11-floater@127,241|13] +botWallVisible=false +endPoint=127,241 +height=36 +leftWallVisible=true +rightWallVisible=true +speed=5 +startPoint=13,235 +topWallVisible=true +width=260 + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=4 + +[11-puddle@142,397|19] +changeEnabled=false +changeEvery=50 +height=94 +width=74 + +[11-puddle@149,362|3] +changeEnabled=false +changeEvery=50 +height=30 +width=56 + +[11-puddle@159,387|9] +changeEnabled=false +changeEvery=50 +height=72 +width=58 + +[11-puddle@170,398|18] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@173,382|20] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@192,390|10] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@210,393|44] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@231,394|12] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@258,388|11] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@272,380|45] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@284,376|8] +changeEnabled=false +changeEvery=50 +height=46 +width=46 + +[11-puddle@290,401|7] +changeEnabled=false +changeEvery=50 +height=88 +width=60 + +[11-puddle@293,-12|40] +changeEnabled=false +changeEvery=50 +height=50 +width=82 + +[11-puddle@332,7|39] +changeEnabled=false +changeEvery=50 +height=32 +width=82 + +[11-puddle@335,10|38] +changeEnabled=false +changeEvery=50 +height=34 +width=50 + +[11-puddle@349,12|37] +changeEnabled=false +changeEvery=50 +height=50 +width=56 + +[11-puddle@353,33|34] +changeEnabled=false +changeEvery=50 +height=34 +width=36 + +[11-puddle@356,12|33] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@361,32|39] +changeEnabled=false +changeEvery=50 +height=58 +width=48 + +[11-puddle@388,45|36] +changeEnabled=false +changeEvery=50 +height=48 +width=88 + +[11-puddle@391,10|35] +changeEnabled=false +changeEvery=50 +height=74 +width=102 + +[11-sand@111,152|26] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[11-sand@113,140|23] +changeEnabled=false +changeEvery=50 +height=80 +width=80 + +[11-sand@120,160|25] +changeEnabled=false +changeEvery=50 +height=56 +width=56 + +[11-sand@122,120|24] +changeEnabled=false +changeEvery=50 +height=40 +width=70 + +[11-sand@133,177|46] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[11-sand@141,155|27] +changeEnabled=false +changeEvery=50 +height=96 +width=56 + +[11-slope@11,37|22] +grade=2 +gradient=Opposite Diagonal +height=166 +reversed=false +stuckOnGround=false +width=178 + +[11-slope@13,217|41] +grade=4 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=true +width=376 + +[11-slope@168,280|40] +grade=4 +gradient=Elliptic +height=107 +reversed=true +stuckOnGround=false +width=107 + +[11-slope@343,53|44] +grade=3 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[11-slope@61,13|30] +grade=2 +gradient=Vertical +height=79 +reversed=true +stuckOnGround=true +width=286 + +[11-slope@7,271|42] +grade=4 +gradient=Vertical +height=29 +reversed=true +stuckOnGround=true +width=382 + +[11-slope@73,128|28] +grade=2 +gradient=Vertical +height=77 +reversed=false +stuckOnGround=true +width=314 + +[11-slope@9,11|43] +grade=6 +gradient=Diagonal +height=56 +reversed=true +stuckOnGround=false +width=52 + +[11-slope@9,8|32] +grade=2 +gradient=Diagonal +height=140 +reversed=true +stuckOnGround=false +width=116 + +[11-wall@0,0|14] +endPoint=392,207 +startPoint=12,207 + +[11-wall@0,0|17] +endPoint=253,280 +startPoint=10,278 + +[11-wall@0,0|42] +endPoint=199,63 +startPoint=209,113 + +[12-ball@58,341] +dummykey=true + +[12-blackhole@244,110|62] +exit=166,91 +exitDeg=280 +maxspeed=3 +minspeed=3 + +[12-blackhole@359,23|65] +exit=111,236 +exitDeg=20 +maxspeed=5 +minspeed=3 + +[12-blackhole@46,112|41] +exit=144,33 +exitDeg=350 +maxspeed=5 +minspeed=3 + +[12-cup@177,164|16] +dummykey=true + +[12-cup@212,294|38] +dummykey=true + +[12-cup@229,270|40] +dummykey=true + +[12-cup@230,315|39] +dummykey=true + +[12-cup@256,319|37] +dummykey=true + +[12-floater@329,91|50] +botWallVisible=false +endPoint=329,91 +height=46 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=277,35 +topWallVisible=false +width=52 + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[12-puddle@101,37|66] +changeEnabled=false +changeEvery=50 +height=96 +width=76 + +[12-puddle@104,63|28] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[12-puddle@114,15|25] +changeEnabled=false +changeEvery=50 +height=98 +width=60 + +[12-puddle@62,43|24] +changeEnabled=false +changeEvery=50 +height=44 +width=54 + +[12-puddle@68,-60|62] +changeEnabled=false +changeEvery=50 +height=244 +width=150 + +[12-puddle@85,65|26] +changeEnabled=false +changeEvery=50 +height=34 +width=42 + +[12-puddle@91,43|21] +changeEnabled=false +changeEvery=50 +height=72 +width=100 + +[12-puddle@95,72|27] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[12-slope@238,231|53] +grade=3 +gradient=Vertical +height=11 +reversed=false +stuckOnGround=false +width=58 + +[12-slope@238,242|36] +grade=3 +gradient=Vertical +height=41 +reversed=true +stuckOnGround=false +width=51 + +[12-slope@262,236|49] +grade=6 +gradient=Diagonal +height=151 +reversed=false +stuckOnGround=false +width=126 + +[12-slope@7,74|67] +grade=4 +gradient=Elliptic +height=78 +reversed=true +stuckOnGround=false +width=78 + +[12-slope@86,130|17] +grade=4 +gradient=Diagonal +height=69 +reversed=true +stuckOnGround=false +width=63 + +[12-slope@92,94|52] +grade=4 +gradient=Horizontal +height=38 +reversed=true +stuckOnGround=false +width=56 + +[12-wall@0,0|10] +endPoint=21,360 +startPoint=35,379 + +[12-wall@0,0|12] +endPoint=274,328 +startPoint=288,292 + +[12-wall@0,0|13] +endPoint=81,161 +startPoint=96,93 + +[12-wall@0,0|14] +endPoint=81,161 +startPoint=136,332 + +[12-wall@0,0|15] +endPoint=20,305 +startPoint=13,332 + +[12-wall@0,0|20] +endPoint=282,74 +startPoint=96,93 + +[12-wall@0,0|30] +endPoint=252,339 +startPoint=218,337 + +[12-wall@0,0|31] +endPoint=218,337 +startPoint=196,320 + +[12-wall@0,0|32] +endPoint=274,328 +startPoint=252,339 + +[12-wall@0,0|33] +endPoint=196,320 +startPoint=189,292 + +[12-wall@0,0|34] +endPoint=196,264 +startPoint=189,292 + +[12-wall@0,0|35] +endPoint=236,251 +startPoint=196,264 + +[12-wall@0,0|4] +endPoint=13,332 +startPoint=21,360 + +[12-wall@0,0|42] +endPoint=316,78 +startPoint=282,74 + +[12-wall@0,0|43] +endPoint=337,57 +startPoint=318,79 + +[12-wall@0,0|44] +endPoint=339,30 +startPoint=337,57 + +[12-wall@0,0|45] +endPoint=333,14 +startPoint=339,30 + +[12-wall@0,0|46] +endPoint=316,78 +startPoint=310,111 + +[12-wall@0,0|47] +endPoint=365,51 +startPoint=337,57 + +[12-wall@0,0|48] +endPoint=38,285 +startPoint=20,305 + +[12-wall@0,0|49] +endPoint=387,54 +startPoint=365,51 + +[12-wall@0,0|5] +endPoint=71,383 +startPoint=35,379 + +[12-wall@0,0|51] +endPoint=307,131 +startPoint=284,137 + +[12-wall@0,0|54] +endPoint=136,332 +startPoint=133,354 + +[12-wall@0,0|55] +endPoint=56,280 +startPoint=38,285 + +[12-wall@0,0|56] +endPoint=284,137 +startPoint=264,134 + +[12-wall@0,0|57] +endPoint=264,134 +startPoint=241,139 + +[12-wall@0,0|58] +endPoint=241,139 +startPoint=221,129 + +[12-wall@0,0|59] +endPoint=221,129 +startPoint=212,115 + +[12-wall@0,0|6] +endPoint=104,374 +startPoint=71,383 + +[12-wall@0,0|60] +endPoint=212,115 +startPoint=194,107 + +[12-wall@0,0|61] +endPoint=194,107 +startPoint=186,86 + +[12-wall@0,0|63] +endPoint=147,372 +startPoint=134,357 + +[12-wall@0,0|64] +endPoint=186,388 +startPoint=147,372 + +[12-wall@0,0|68] +endPoint=236,251 +startPoint=238,230 + +[12-wall@0,0|7] +endPoint=288,292 +startPoint=310,111 + +[12-wall@0,0|8] +endPoint=133,354 +startPoint=104,374 + +[12-wall@0,0|9] +endPoint=265,189 +startPoint=154,189 + +[13-ball@38,51] +dummykey=true + +[13-cup@360,359|20] +dummykey=true + +[13-floater@68,306|7] +botWallVisible=true +endPoint=68,306 +height=11 +leftWallVisible=false +rightWallVisible=false +speed=9 +startPoint=133,75 +topWallVisible=false +width=50 + +[13-floater@83,372|16] +botWallVisible=false +endPoint=83,372 +height=47 +leftWallVisible=true +rightWallVisible=false +speed=4 +startPoint=344,371 +topWallVisible=true +width=14 + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[13-sign@257,44|26] +Comment=Let the floaters push you! +Comment[bg]=Обръщане на грешките във Ваша полза! +Comment[bn]=ফà§à¦²à§‹à¦Ÿà¦¾à¦°à¦—à§à¦²à§‹ আপনাকে ধাকà§à¦•à¦¾ দিক! +Comment[bs]=Dopustite plutaÄima da vas guraju! +Comment[ca]=Permetre que els flotadors t'empenyin! +Comment[da]=Lad svæverne skubbe dig! +Comment[de]=Lassen Sie sich von den Flößen weiterschubsen. +Comment[es]=¡Dejar que los flotadores le empujen! +Comment[et]=Lase hõljukitel ennast lükata ! +Comment[fi]=Anna kelluvien painaa sinua! +Comment[fr]=Laissez les flotteurs vous pousser ! +Comment[gl]=Deixe que os flotadores o empuxen! +Comment[he]=תן ×œ×ž×©×˜×—×™× ×”×¦×¤×™× ×œ×“×—×•×£ ×ותך! +Comment[hi]=तैरने वाले आपको धकेल सकते हैं! +Comment[hu]=Csak a legjobbaknak! +Comment[is]=Láttu flotin ýta þér! +Comment[it]=Lasciati trasportare dai floater! +Comment[ja]=フローターã§ãƒœãƒ¼ãƒ«ã‚’プッシュã•ã›ã‚ˆã†! +Comment[mk]=Дозволете им на лебдачите да ве турнат! +Comment[nb]=La plattformene dytte deg! +Comment[nl]=Laat u door de drijvers duwen. +Comment[nn]=Lat plattformane dytta deg! +Comment[nso]=Dumelela di-floater digo tshofe! +Comment[pl]=Niech ta piÅ‚eczka leci! +Comment[pt]=Deixe os flutuadores empurrá-lo! +Comment[pt_BR]=Deixe as bóias levarem você! +Comment[ru]=ПуÑÑ‚ÑŒ Ð²Ð°Ñ Ð½ÐµÑут ползуны! +Comment[sk]=Nechajte sa unášaÅ¥! +Comment[sl]=Naj vas plovci porinejo! +Comment[sr]=Ðека Ð²Ð°Ñ Ð¿Ð»ÑƒÑ‚Ð°Ñ‡Ð¸ гурају! +Comment[sr@Latn]=Neka vas plutaÄi guraju! +Comment[sv]=LÃ¥t flottarna knuffa dig! +Comment[ta]=மிதவைகள௠உஙà¯à®•à®³à¯ˆà®¤à®³à¯à®³à®Ÿà¯à®Ÿà¯à®®à¯ ! +Comment[tg]=Иҷозат диҳед, ки хазандаҳо шуморо тела диҳанд! +Comment[tr]=Yüzücülerin size vurmasına izin verin! +Comment[uk]=Ðехай плавуни Ð²Ð°Ñ Ð¿Ñ–Ð´ÑˆÑ‚Ð¾Ð²Ñ…Ð½ÑƒÑ‚ÑŒ! +Comment[ven]=Irani zwipapamalisukumedzainwi! +Comment[xh]=Vumela ezidadayo tyhala wena! +Comment[xx]=xxLet the floaters push you!xx +Comment[zh_CN]=让浮å°æ‰˜èµ·æ‚¨ï¼ +Comment[zh_TW]=è®“æµ®æ¨™æŽ¨å‹•æ‚¨ï¼ +Comment[zu]=Yekela okuntantayo kukududule! +botWallVisible=true +height=196 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=111 + +[13-slope@10,10|3] +grade=6 +gradient=Vertical +height=137 +reversed=true +stuckOnGround=true +width=227 + +[13-slope@10,378|0] +grade=3 +gradient=Vertical +height=9 +reversed=false +stuckOnGround=true +width=209 + +[13-slope@11,363|1] +grade=3 +gradient=Vertical +height=17 +reversed=false +stuckOnGround=true +width=59 + +[13-slope@111,102|4] +grade=3 +gradient=Vertical +height=10 +reversed=false +stuckOnGround=true +width=27 + +[13-slope@12,147|16] +grade=5 +gradient=Vertical +height=131 +reversed=true +stuckOnGround=true +width=227 + +[13-slope@136,102|6] +grade=4 +gradient=Vertical +height=6 +reversed=false +stuckOnGround=true +width=17 + +[13-slope@183,277|25] +grade=5 +gradient=Vertical +height=111 +reversed=true +stuckOnGround=true +width=206 + +[13-slope@218,378|2] +grade=3 +gradient=Vertical +height=10 +reversed=false +stuckOnGround=true +width=105 + +[13-slope@332,330|19] +grade=6 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[13-slope@69,307|8] +grade=8 +gradient=Vertical +height=8 +reversed=false +stuckOnGround=false +width=46 + +[13-slope@82,373|13] +grade=8 +gradient=Horizontal +height=18 +reversed=true +stuckOnGround=false +width=16 + +[13-slope@9,277|17] +grade=5 +gradient=Vertical +height=100 +reversed=true +stuckOnGround=true +width=175 + +[13-wall@0,0|10] +endPoint=124,297 +startPoint=65,372 + +[13-wall@0,0|11] +endPoint=13,364 +startPoint=36,377 + +[13-wall@0,0|12] +endPoint=238,12 +startPoint=236,276 + +[13-wall@0,0|14] +endPoint=113,114 +startPoint=96,12 + +[13-wall@0,0|16] +endPoint=322,389 +startPoint=331,343 + +[13-wall@0,0|17] +endPoint=331,343 +startPoint=353,326 + +[13-wall@0,0|18] +endPoint=353,326 +startPoint=390,326 + +[13-wall@0,0|21] +endPoint=388,276 +startPoint=236,276 + +[13-wall@0,0|23] +endPoint=159,103 +startPoint=173,108 + +[13-wall@0,0|24] +endPoint=113,114 +startPoint=159,103 + +[13-wall@0,0|5] +endPoint=173,108 +startPoint=124,297 + +[13-wall@0,0|9] +endPoint=65,372 +startPoint=36,377 + +[14-ball@360,259] +dummykey=true + +[14-blackhole@87,369|7] +exit=366,49 +exitDeg=50 +maxspeed=2 +minspeed=1 + +[14-cup@315,106|10] +dummykey=true + +[14-cup@87,276|6] +dummykey=true + +[14-floater@289,36|9] +botWallVisible=true +endPoint=289,36 +height=97 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=250,24 +topWallVisible=true +width=100 + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[14-slope@10,362|8] +grade=4 +gradient=Horizontal +height=14 +reversed=true +stuckOnGround=false +width=83 + +[14-slope@11,10|4] +grade=2 +gradient=Vertical +height=338 +reversed=true +stuckOnGround=true +width=163 + +[14-slope@11,352|11] +grade=4 +gradient=Opposite Diagonal +height=11 +reversed=false +stuckOnGround=false +width=81 + +[14-slope@11,374|12] +grade=4 +gradient=Diagonal +height=12 +reversed=true +stuckOnGround=false +width=83 + +[14-slope@174,10|12] +grade=2 +gradient=Diagonal +height=339 +reversed=true +stuckOnGround=true +width=63 + +[14-slope@289,50|11] +grade=2 +gradient=Vertical +height=83 +reversed=false +stuckOnGround=false +width=101 + +[14-slope@290,78|18] +grade=4 +gradient=Elliptic +height=53 +reversed=true +stuckOnGround=false +width=53 + +[14-wall@0,0|13] +endPoint=203,205 +startPoint=181,353 + +[14-wall@0,0|14] +endPoint=170,362 +startPoint=115,361 + +[14-wall@0,0|15] +endPoint=105,365 +startPoint=115,361 + +[14-wall@0,0|16] +endPoint=98,387 +startPoint=105,365 + +[14-wall@0,0|17] +endPoint=181,353 +startPoint=170,362 + +[15-ball@20,380] +dummykey=true + +[15-blackhole@172,240|6] +exit=316,255 +exitDeg=42 +maxspeed=1 +minspeed=0 + +[15-blackhole@228,90|22] +exit=345,328 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@242,55|24] +exit=24,53 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@330,369|8] +exit=353,363 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-blackhole@375,215|46] +exit=16,128 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[15-blackhole@61,244|7] +exit=21,70 +exitDeg=10 +maxspeed=5 +minspeed=3 + +[15-bridge@206,351|38] +botWallVisible=false +height=29 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=51 + +[15-bridge@233,337|36] +botWallVisible=false +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=53 + +[15-bridge@52,20|13] +botWallVisible=true +height=79 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=153 + +[15-cup@182,78|14] +dummykey=true + +[15-cup@373,269|27] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[15-puddle@247,365|35] +changeEnabled=false +changeEvery=50 +height=88 +width=58 + +[15-puddle@252,383|37] +changeEnabled=false +changeEvery=50 +height=92 +width=68 + +[15-slope@10,311|45] +grade=6 +gradient=Opposite Diagonal +height=76 +reversed=false +stuckOnGround=false +width=84 + +[15-slope@107,329|31] +grade=7 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@116,285|30] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@117,178|38] +grade=4 +gradient=Elliptic +height=116 +reversed=true +stuckOnGround=false +width=116 + +[15-slope@14,120|37] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@14,190|43] +grade=4 +gradient=Elliptic +height=98 +reversed=true +stuckOnGround=false +width=98 + +[15-slope@147,135|35] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@149,339|45] +grade=6 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@168,296|34] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@218,265|44] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@221,182|29] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@240,238|41] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@26,293|36] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@283,9|33] +grade=4 +gradient=Opposite Diagonal +height=96 +reversed=true +stuckOnGround=false +width=105 + +[15-slope@339,275|26] +grade=4 +gradient=Opposite Diagonal +height=36 +reversed=false +stuckOnGround=false +width=39 + +[15-slope@361,304|25] +grade=4 +gradient=Vertical +height=79 +reversed=false +stuckOnGround=false +width=23 + +[15-slope@58,141|42] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@61,22|17] +grade=4 +gradient=Horizontal +height=75 +reversed=false +stuckOnGround=false +width=144 + +[15-slope@67,343|40] +grade=5 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@8,363|46] +grade=6 +gradient=Opposite Diagonal +height=25 +reversed=false +stuckOnGround=false +width=25 + +[15-slope@95,163|32] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[15-wall@0,0|10] +endPoint=257,172 +startPoint=295,241 + +[15-wall@0,0|11] +endPoint=256,173 +startPoint=201,137 + +[15-wall@0,0|12] +endPoint=201,137 +startPoint=12,110 + +[15-wall@0,0|18] +endPoint=295,241 +startPoint=331,283 + +[15-wall@0,0|19] +endPoint=363,242 +startPoint=330,281 + +[15-wall@0,0|20] +endPoint=382,237 +startPoint=363,242 + +[15-wall@0,0|23] +endPoint=273,135 +startPoint=257,172 + +[15-wall@0,0|28] +endPoint=382,237 +startPoint=390,249 + +[15-wall@0,0|9] +endPoint=331,283 +startPoint=351,387 + +[15-windmill@273,10|21] +botWallVisible=false +bottom=true +height=125 +leftWallVisible=false +rightWallVisible=false +speed=7 +topWallVisible=false +width=115 + +[16-ball@39,319] +dummykey=true + +[16-blackhole@354,290|51] +exit=319,39 +exitDeg=180 +maxspeed=4 +minspeed=3 + +[16-cup@-48,-27|3] +dummykey=true + +[16-cup@-48,-43|70] +dummykey=true + +[16-cup@-50,-40|71] +dummykey=true + +[16-cup@-51,-42|68] +dummykey=true + +[16-cup@-52,-41|69] +dummykey=true + +[16-cup@-57,-40|72] +dummykey=true + +[16-cup@87,83|73] +dummykey=true + +[16-floater@122,335|39] +botWallVisible=true +endPoint=122,335 +height=42 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=67,242 +topWallVisible=true +width=165 + +[16-floater@182,24|63] +botWallVisible=true +endPoint=182,24 +height=34 +leftWallVisible=false +rightWallVisible=true +speed=8 +startPoint=147,80 +topWallVisible=true +width=141 + +[16-floater@64,69|64] +botWallVisible=false +endPoint=64,69 +height=66 +leftWallVisible=true +rightWallVisible=false +speed=5 +startPoint=45,34 +topWallVisible=true +width=73 + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[16-puddle@110,211|60] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[16-puddle@115,229|44] +changeEnabled=false +changeEvery=50 +height=70 +width=88 + +[16-puddle@128,239|45] +changeEnabled=false +changeEvery=50 +height=62 +width=98 + +[16-puddle@14,174|39] +changeEnabled=false +changeEvery=50 +height=64 +width=162 + +[16-puddle@148,390|55] +changeEnabled=false +changeEvery=50 +height=94 +width=58 + +[16-puddle@152,246|46] +changeEnabled=false +changeEvery=50 +height=60 +width=114 + +[16-puddle@153,373|54] +changeEnabled=false +changeEvery=50 +height=86 +width=66 + +[16-puddle@160,355|53] +changeEnabled=false +changeEvery=50 +height=96 +width=74 + +[16-puddle@170,257|47] +changeEnabled=false +changeEvery=50 +height=70 +width=94 + +[16-puddle@172,334|52] +changeEnabled=false +changeEvery=50 +height=94 +width=88 + +[16-puddle@173,367|56] +changeEnabled=false +changeEvery=50 +height=82 +width=58 + +[16-puddle@177,309|51] +changeEnabled=false +changeEvery=50 +height=72 +width=80 + +[16-puddle@178,263|48] +changeEnabled=false +changeEvery=50 +height=72 +width=82 + +[16-puddle@178,271|49] +changeEnabled=false +changeEvery=50 +height=94 +width=82 + +[16-puddle@183,284|50] +changeEnabled=false +changeEvery=50 +height=122 +width=82 + +[16-puddle@189,358|57] +changeEnabled=false +changeEvery=50 +height=54 +width=36 + +[16-puddle@206,312|58] +changeEnabled=false +changeEvery=50 +height=70 +width=32 + +[16-puddle@65,186|40] +changeEnabled=false +changeEvery=50 +height=68 +width=100 + +[16-puddle@76,216|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[16-puddle@77,193|41] +changeEnabled=false +changeEvery=50 +height=74 +width=98 + +[16-puddle@89,201|42] +changeEnabled=false +changeEvery=50 +height=76 +width=80 + +[16-puddle@97,216|43] +changeEnabled=false +changeEvery=50 +height=72 +width=70 + +[16-sand@192,162|51] +changeEnabled=false +changeEvery=50 +height=58 +width=78 + +[16-sand@200,168|55] +changeEnabled=false +changeEvery=50 +height=62 +width=62 + +[16-sand@205,172|56] +changeEnabled=false +changeEvery=50 +height=62 +width=52 + +[16-sand@211,175|57] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[16-sand@220,192|58] +changeEnabled=false +changeEvery=50 +height=32 +width=56 + +[16-sand@225,182|59] +changeEnabled=false +changeEvery=50 +height=54 +width=34 + +[16-sand@235,186|51] +changeEnabled=false +changeEvery=50 +height=22 +width=20 + +[16-sand@338,126|45] +changeEnabled=false +changeEvery=50 +height=62 +width=38 + +[16-sand@348,108|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@363,121|41] +changeEnabled=false +changeEvery=50 +height=82 +width=78 + +[16-sand@366,95|50] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@374,88|49] +changeEnabled=false +changeEvery=50 +height=40 +width=38 + +[16-sand@379,142|46] +changeEnabled=false +changeEvery=50 +height=64 +width=108 + +[16-sand@396,83|48] +changeEnabled=false +changeEvery=50 +height=72 +width=58 + +[16-slope@293,269|52] +grade=4 +gradient=Diagonal +height=118 +reversed=false +stuckOnGround=true +width=93 + +[16-wall@0,0|65] +endPoint=345,264 +startPoint=251,389 + +[16-wall@0,0|67] +endPoint=389,271 +startPoint=345,264 + +[17-ball@170,349] +dummykey=true + +[17-blackhole@233,359|24] +exit=288,38 +exitDeg=50 +maxspeed=3 +minspeed=1 + +[17-bumper@248,22|51] +dummykey=true + +[17-bumper@263,33|50] +dummykey=true + +[17-bumper@278,45|52] +dummykey=true + +[17-bumper@289,59|41] +dummykey=true + +[17-bumper@297,75|49] +dummykey=true + +[17-bumper@301,93|44] +dummykey=true + +[17-bumper@302,111|45] +dummykey=true + +[17-bumper@309,128|42] +dummykey=true + +[17-bumper@318,144|43] +dummykey=true + +[17-bumper@335,151|48] +dummykey=true + +[17-bumper@355,154|43] +dummykey=true + +[17-bumper@375,154|42] +dummykey=true + +[17-cup@353,116|36] +dummykey=true + +[17-cup@365,358|15] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-puddle@243,204|45] +changeEnabled=false +changeEvery=50 +height=50 +width=52 + +[17-puddle@253,194|44] +changeEnabled=false +changeEvery=50 +height=54 +width=66 + +[17-puddle@254,183|46] +changeEnabled=false +changeEvery=50 +height=64 +width=58 + +[17-puddle@262,213|47] +changeEnabled=false +changeEvery=50 +height=56 +width=88 + +[17-sand@-19,263|16] +changeEnabled=false +changeEvery=50 +height=138 +width=94 + +[17-sand@1,214|52] +changeEnabled=false +changeEvery=50 +height=82 +width=64 + +[17-sand@102,219|56] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@109,197|12] +changeEnabled=false +changeEvery=50 +height=44 +width=58 + +[17-sand@115,216|4] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@126,200|5] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@136,201|29] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@155,202|30] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@160,189|32] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@166,172|17] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@167,162|3] +changeEnabled=false +changeEvery=50 +height=48 +width=44 + +[17-sand@178,156|54] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[17-sand@187,166|55] +changeEnabled=false +changeEvery=50 +height=48 +width=60 + +[17-sand@193,175|31] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[17-sand@5,238|8] +changeEnabled=false +changeEvery=50 +height=98 +width=64 + +[17-sand@85,225|34] +changeEnabled=false +changeEvery=50 +height=60 +width=34 + +[17-sand@91,232|9] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[17-sand@92,208|6] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[17-slope@103,219|40] +grade=2 +gradient=Elliptic +height=108 +reversed=false +stuckOnGround=false +width=108 + +[17-slope@11,8|42] +grade=4 +gradient=Diagonal +height=160 +reversed=true +stuckOnGround=false +width=124 + +[17-slope@204,241|25] +grade=2 +gradient=Opposite Diagonal +height=268 +reversed=true +stuckOnGround=false +width=181 + +[17-slope@205,331|41] +grade=4 +gradient=Elliptic +height=57 +reversed=true +stuckOnGround=false +width=57 + +[17-slope@210,240|38] +grade=2 +gradient=Vertical +height=103 +reversed=true +stuckOnGround=false +width=63 + +[17-slope@238,332|40] +grade=2 +gradient=Horizontal +height=52 +reversed=false +stuckOnGround=false +width=66 + +[17-slope@78,12|39] +grade=3 +gradient=Vertical +height=68 +reversed=true +stuckOnGround=false +width=308 + +[17-slope@9,269|41] +grade=4 +gradient=Elliptic +height=109 +reversed=false +stuckOnGround=false +width=109 + +[17-wall@0,0|18] +endPoint=206,196 +startPoint=203,387 + +[17-wall@0,0|19] +endPoint=46,59 +startPoint=11,123 + +[17-wall@0,0|23] +endPoint=89,12 +startPoint=46,59 + +[17-windmill@322,91|53] +botWallVisible=false +bottom=false +height=43 +leftWallVisible=false +rightWallVisible=false +speed=5 +topWallVisible=false +width=67 + +[18-ball@174,371] +dummykey=true + +[18-cup@340,103|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[18-slope@308,71|19] +grade=4 +gradient=Elliptic +height=65 +reversed=true +stuckOnGround=false +width=65 + +[18-wall@0,0|10] +endPoint=388,174 +startPoint=250,151 + +[18-wall@0,0|11] +endPoint=324,257 +startPoint=320,270 + +[18-wall@0,0|12] +endPoint=342,336 +startPoint=339,348 + +[18-wall@0,0|13] +endPoint=228,98 +startPoint=239,90 + +[18-wall@0,0|14] +endPoint=15,367 +startPoint=12,143 + +[18-wall@0,0|15] +endPoint=219,390 +startPoint=36,391 + +[18-wall@0,0|16] +endPoint=36,391 +startPoint=15,367 + +[18-wall@0,0|17] +endPoint=388,12 +startPoint=275,11 + +[18-wall@0,0|18] +endPoint=388,12 +startPoint=390,212 + +[18-wall@0,0|4] +endPoint=238,238 +startPoint=75,312 + +[18-wall@0,0|5] +endPoint=228,98 +startPoint=12,143 + +[18-wall@0,0|6] +endPoint=275,11 +startPoint=239,90 + +[18-wall@0,0|7] +endPoint=390,212 +startPoint=324,257 + +[18-wall@0,0|8] +endPoint=320,270 +startPoint=342,336 + +[18-wall@0,0|9] +endPoint=339,348 +startPoint=219,390 + +[2-ball@112,41] +dummykey=true + +[2-blackhole@142,61|20] +exit=261,80 +exitDeg=305 +maxspeed=4 +minspeed=3 + +[2-blackhole@178,367|17] +exit=17,31 +exitDeg=349 +maxspeed=2 +minspeed=2 + +[2-blackhole@28,148|15] +exit=238,26 +exitDeg=0 +maxspeed=5 +minspeed=1 + +[2-blackhole@374,89|7] +exit=308,84 +exitDeg=244 +maxspeed=3 +minspeed=2 + +[2-cup@23,188|21] +dummykey=true + +[2-cup@375,52|6] +dummykey=true + +[2-floater@43,240|24] +botWallVisible=false +endPoint=43,240 +height=64 +leftWallVisible=false +rightWallVisible=true +speed=5 +startPoint=33,173 +topWallVisible=true +width=81 + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[2-puddle@10,241|50] +changeEnabled=false +changeEvery=50 +height=42 +width=142 + +[2-puddle@48,248|29] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[2-puddle@57,233|51] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@60,244|31] +changeEnabled=false +changeEvery=50 +height=32 +width=58 + +[2-puddle@68,232|52] +changeEnabled=false +changeEvery=50 +height=50 +width=40 + +[2-puddle@72,241|32] +changeEnabled=false +changeEvery=50 +height=30 +width=44 + +[2-puddle@75,225|34] +changeEnabled=false +changeEvery=50 +height=54 +width=38 + +[2-puddle@75,235|33] +changeEnabled=false +changeEvery=50 +height=36 +width=38 + +[2-sand@147,241|43] +changeEnabled=false +changeEvery=50 +height=54 +width=48 + +[2-sand@160,261|45] +changeEnabled=false +changeEvery=50 +height=22 +width=52 + +[2-sand@169,242|44] +changeEnabled=false +changeEvery=50 +height=52 +width=54 + +[2-slope@11,97|51] +grade=4 +gradient=Diagonal +height=21 +reversed=true +stuckOnGround=false +width=43 + +[2-slope@12,198|36] +grade=3 +gradient=Vertical +height=44 +reversed=false +stuckOnGround=true +width=35 + +[2-slope@12,75|50] +grade=4 +gradient=Horizontal +height=22 +reversed=true +stuckOnGround=false +width=42 + +[2-slope@13,35|49] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=42 + +[2-slope@190,89|58] +grade=3 +gradient=Diagonal +height=30 +reversed=false +stuckOnGround=false +width=47 + +[2-slope@192,119|57] +grade=3 +gradient=Opposite Diagonal +height=127 +reversed=true +stuckOnGround=true +width=43 + +[2-slope@210,22|58] +grade=2 +gradient=Opposite Diagonal +height=39 +reversed=true +stuckOnGround=false +width=20 + +[2-slope@224,11|46] +grade=2 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=92 + +[2-slope@232,243|48] +grade=3 +gradient=Diagonal +height=29 +reversed=true +stuckOnGround=true +width=135 + +[2-slope@233,119|22] +grade=3 +gradient=Vertical +height=126 +reversed=true +stuckOnGround=true +width=153 + +[2-slope@234,243|23] +grade=3 +gradient=Horizontal +height=144 +reversed=false +stuckOnGround=true +width=154 + +[2-slope@235,88|59] +grade=3 +gradient=Vertical +height=31 +reversed=false +stuckOnGround=false +width=153 + +[2-slope@314,9|47] +grade=2 +gradient=Diagonal +height=49 +reversed=true +stuckOnGround=false +width=19 + +[2-slope@334,13|16] +grade=4 +gradient=Opposite Diagonal +height=31 +reversed=true +stuckOnGround=false +width=52 + +[2-slope@43,112|18] +grade=8 +gradient=Opposite Diagonal +height=84 +reversed=false +stuckOnGround=true +width=68 + +[2-slope@7,174|59] +grade=4 +gradient=Elliptic +height=33 +reversed=true +stuckOnGround=true +width=33 + +[2-wall@0,0|10] +endPoint=86,192 +startPoint=54,196 + +[2-wall@0,0|11] +endPoint=33,173 +startPoint=54,196 + +[2-wall@0,0|12] +endPoint=11,167 +startPoint=33,173 + +[2-wall@0,0|13] +endPoint=48,144 +startPoint=33,173 + +[2-wall@0,0|14] +endPoint=28,113 +startPoint=50,133 + +[2-wall@0,0|3] +endPoint=233,12 +startPoint=104,130 + +[2-wall@0,0|37] +endPoint=116,82 +startPoint=141,96 + +[2-wall@0,0|38] +endPoint=104,85 +startPoint=89,115 + +[2-wall@0,0|4] +endPoint=275,63 +startPoint=86,192 + +[2-wall@0,0|49] +endPoint=116,82 +startPoint=104,85 + +[2-wall@0,0|5] +endPoint=387,117 +startPoint=275,63 + +[2-wall@0,0|52] +endPoint=104,130 +startPoint=89,115 + +[2-wall@0,0|54] +endPoint=131,11 +startPoint=170,36 + +[2-wall@0,0|55] +endPoint=172,66 +startPoint=171,36 + +[2-wall@0,0|56] +endPoint=198,44 +startPoint=170,36 + +[2-wall@0,0|60] +endPoint=48,144 +startPoint=50,133 + +[3-ball@346,322] +dummykey=true + +[3-blackhole@192,132|23] +exit=47,35 +exitDeg=310 +maxspeed=3.75 +minspeed=1.78 + +[3-blackhole@208,286|7] +exit=147,367 +exitDeg=180 +maxspeed=6.63 +minspeed=2.33 + +[3-cup@341,43|16] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-puddle@260,299|41] +changeEnabled=false +changeEvery=50 +height=22 +width=22 + +[3-puddle@261,283|40] +changeEnabled=false +changeEvery=50 +height=20 +width=26 + +[3-puddle@262,312|10] +changeEnabled=false +changeEvery=50 +height=36 +width=54 + +[3-puddle@267,272|8] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[3-puddle@276,290|9] +changeEnabled=false +changeEvery=50 +height=68 +width=50 + +[3-sand@269,142|45] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@270,146|30] +changeEnabled=false +changeEvery=50 +height=32 +width=46 + +[3-sand@272,135|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@277,127|43] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[3-sand@282,122|42] +changeEnabled=false +changeEvery=50 +height=22 +width=32 + +[3-sand@284,133|29] +changeEnabled=false +changeEvery=50 +height=46 +width=32 + +[3-slope@11,10|25] +grade=3 +gradient=Diagonal +height=326 +reversed=true +stuckOnGround=false +width=128 + +[3-slope@112,381|41] +grade=8 +gradient=Opposite Diagonal +height=15 +reversed=true +stuckOnGround=false +width=35 + +[3-slope@113,334|40] +grade=8 +gradient=Diagonal +height=18 +reversed=false +stuckOnGround=false +width=35 + +[3-slope@113,351|14] +grade=8 +gradient=Horizontal +height=30 +reversed=false +stuckOnGround=false +width=30 + +[3-slope@150,302|11] +grade=6 +gradient=Opposite Diagonal +height=86 +reversed=false +stuckOnGround=false +width=81 + +[3-slope@170,248|46] +grade=4 +gradient=Elliptic +height=81 +reversed=true +stuckOnGround=false +width=81 + +[3-slope@174,112|47] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[3-slope@281,117|41] +grade=2 +gradient=Diagonal +height=56 +reversed=false +stuckOnGround=false +width=53 + +[3-slope@282,172|40] +grade=2 +gradient=Opposite Diagonal +height=53 +reversed=true +stuckOnGround=false +width=53 + +[3-slope@304,10|41] +grade=4 +gradient=Opposite Diagonal +height=48 +reversed=true +stuckOnGround=false +width=84 + +[3-slope@334,119|24] +grade=2 +gradient=Opposite Diagonal +height=54 +reversed=false +stuckOnGround=false +width=52 + +[3-slope@334,173|33] +grade=2 +gradient=Diagonal +height=54 +reversed=true +stuckOnGround=false +width=48 + +[3-slope@99,36|39] +grade=3 +gradient=Elliptic +height=190 +reversed=true +stuckOnGround=false +width=190 + +[3-wall@0,0|13] +endPoint=90,390 +startPoint=14,350 + +[3-wall@0,0|19] +endPoint=301,42 +startPoint=323,62 + +[3-wall@0,0|20] +endPoint=274,39 +startPoint=261,46 + +[3-wall@0,0|22] +endPoint=255,24 +startPoint=242,41 + +[3-wall@0,0|28] +endPoint=100,175 +startPoint=13,132 + +[3-wall@0,0|29] +endPoint=78,206 +startPoint=14,175 + +[3-wall@0,0|3] +endPoint=390,233 +startPoint=224,228 + +[3-wall@0,0|30] +endPoint=150,198 +startPoint=224,228 + +[3-wall@0,0|31] +endPoint=192,250 +startPoint=128,227 + +[3-wall@0,0|34] +endPoint=255,24 +startPoint=250,13 + +[3-wall@0,0|35] +endPoint=192,252 +startPoint=192,250 + +[3-wall@0,0|36] +endPoint=192,250 +startPoint=189,264 + +[3-wall@0,0|38] +endPoint=261,46 +startPoint=242,41 + +[3-wall@0,0|42] +endPoint=292,10 +startPoint=301,42 + +[3-wall@0,0|43] +endPoint=301,42 +startPoint=274,39 + +[3-wall@0,0|5] +endPoint=189,264 +startPoint=151,310 + +[3-wall@0,0|6] +endPoint=151,390 +startPoint=151,310 + +[3-windmill@323,62|31] +botWallVisible=false +bottom=true +height=60 +leftWallVisible=true +rightWallVisible=false +speed=2 +topWallVisible=false +width=61 + +[4-ball@356,151] +dummykey=true + +[4-blackhole@160,36|45] +exit=156,218 +exitDeg=350 +maxspeed=2 +minspeed=1 + +[4-blackhole@360,332|116] +exit=122,234 +exitDeg=290 +maxspeed=5 +minspeed=3 + +[4-bridge@184,22|136] +botWallVisible=true +height=25 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=81 + +[4-cup@105,116|3] +dummykey=true + +[4-cup@187,330|136] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[4-puddle@113,399|146] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@136,397|147] +changeEnabled=false +changeEvery=50 +height=48 +width=74 + +[4-puddle@141,184|76] +changeEnabled=false +changeEvery=50 +height=66 +width=100 + +[4-puddle@151,162|116] +changeEnabled=false +changeEvery=50 +height=54 +width=84 + +[4-puddle@159,395|60] +changeEnabled=false +changeEvery=50 +height=60 +width=108 + +[4-puddle@178,382|53] +changeEnabled=false +changeEvery=50 +height=34 +width=84 + +[4-puddle@188,158|117] +changeEnabled=false +changeEvery=50 +height=100 +width=106 + +[4-puddle@197,136|81] +changeEnabled=false +changeEvery=50 +height=154 +width=92 + +[4-puddle@207,397|144] +changeEnabled=false +changeEvery=50 +height=48 +width=40 + +[4-puddle@211,189|75] +changeEnabled=false +changeEvery=50 +height=40 +width=232 + +[4-puddle@214,67|139] +changeEnabled=false +changeEvery=50 +height=24 +width=66 + +[4-puddle@217,78|124] +changeEnabled=false +changeEvery=50 +height=42 +width=50 + +[4-puddle@218,2|143] +changeEnabled=false +changeEvery=50 +height=74 +width=38 + +[4-puddle@226,139|118] +changeEnabled=false +changeEvery=50 +height=82 +width=60 + +[4-puddle@226,149|120] +changeEnabled=false +changeEvery=50 +height=92 +width=116 + +[4-puddle@233,255|55] +changeEnabled=false +changeEvery=50 +height=30 +width=64 + +[4-puddle@236,65|123] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[4-puddle@240,313|128] +changeEnabled=false +changeEvery=50 +height=26 +width=36 + +[4-puddle@241,3|140] +changeEnabled=false +changeEvery=50 +height=90 +width=42 + +[4-puddle@244,26|139] +changeEnabled=false +changeEvery=50 +height=82 +width=32 + +[4-puddle@244,315|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@247,257|56] +changeEnabled=false +changeEvery=50 +height=32 +width=54 + +[4-puddle@251,320|52] +changeEnabled=false +changeEvery=50 +height=24 +width=32 + +[4-puddle@266,258|58] +changeEnabled=false +changeEvery=50 +height=24 +width=24 + +[4-puddle@267,157|119] +changeEnabled=false +changeEvery=50 +height=42 +width=46 + +[4-puddle@273,261|51] +changeEnabled=false +changeEvery=50 +height=26 +width=24 + +[4-puddle@275,323|130] +changeEnabled=false +changeEvery=50 +height=22 +width=70 + +[4-puddle@279,176|121] +changeEnabled=false +changeEvery=50 +height=62 +width=98 + +[4-puddle@281,264|50] +changeEnabled=false +changeEvery=50 +height=26 +width=22 + +[4-puddle@287,267|54] +changeEnabled=false +changeEvery=50 +height=24 +width=22 + +[4-puddle@293,189|122] +changeEnabled=false +changeEvery=50 +height=42 +width=66 + +[4-puddle@295,273|57] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[4-puddle@296,320|131] +changeEnabled=false +changeEvery=50 +height=22 +width=30 + +[4-puddle@302,312|127] +changeEnabled=false +changeEvery=50 +height=30 +width=24 + +[4-puddle@305,278|125] +changeEnabled=false +changeEvery=50 +height=24 +width=14 + +[4-puddle@307,291|128] +changeEnabled=false +changeEvery=50 +height=40 +width=24 + +[4-puddle@310,302|126] +changeEnabled=false +changeEvery=50 +height=32 +width=18 + +[4-puddle@311,185|99] +changeEnabled=false +changeEvery=50 +height=44 +width=52 + +[4-puddle@321,198|96] +changeEnabled=false +changeEvery=50 +height=32 +width=48 + +[4-puddle@333,195|123] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[4-puddle@354,202|154] +changeEnabled=false +changeEvery=50 +height=40 +width=62 + +[4-puddle@376,208|115] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[4-puddle@397,211|114] +changeEnabled=false +changeEvery=50 +height=62 +width=54 + +[4-slope@12,188|117] +grade=4 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=71 + +[4-slope@12,213|118] +grade=4 +gradient=Vertical +height=24 +reversed=true +stuckOnGround=false +width=82 + +[4-slope@146,60|125] +grade=4 +gradient=Diagonal +height=83 +reversed=true +stuckOnGround=false +width=48 + +[4-slope@169,225|124] +grade=1 +gradient=Vertical +height=32 +reversed=false +stuckOnGround=false +width=128 + +[4-slope@205,22|162] +grade=4 +gradient=Horizontal +height=25 +reversed=false +stuckOnGround=false +width=22 + +[4-slope@215,-1|137] +grade=3 +gradient=Opposite Diagonal +height=209 +reversed=true +stuckOnGround=false +width=181 + +[4-slope@261,193|138] +grade=3 +gradient=Opposite Diagonal +height=23 +reversed=true +stuckOnGround=false +width=12 + +[4-slope@273,196|125] +grade=3 +gradient=Vertical +height=21 +reversed=true +stuckOnGround=false +width=43 + +[4-slope@296,258|130] +grade=4 +gradient=Horizontal +height=47 +reversed=true +stuckOnGround=false +width=25 + +[4-slope@297,225|137] +grade=1 +gradient=Opposite Diagonal +height=35 +reversed=false +stuckOnGround=false +width=24 + +[4-slope@328,302|145] +grade=2 +gradient=Elliptic +height=64 +reversed=true +stuckOnGround=false +width=64 + +[4-slope@40,280|111] +grade=4 +gradient=Diagonal +height=32 +reversed=false +stuckOnGround=false +width=32 + +[4-slope@41,309|113] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=true +stuckOnGround=false +width=30 + +[4-slope@70,281|112] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=false +stuckOnGround=false +width=30 + +[4-slope@70,308|114] +grade=4 +gradient=Diagonal +height=30 +reversed=true +stuckOnGround=false +width=30 + +[4-slope@99,9|130] +grade=3 +gradient=Horizontal +height=43 +reversed=false +stuckOnGround=false +width=37 + +[4-wall@0,0|10] +endPoint=95,237 +startPoint=141,225 + +[4-wall@0,0|101] +endPoint=249,280 +startPoint=220,278 + +[4-wall@0,0|103] +endPoint=267,277 +startPoint=249,280 + +[4-wall@0,0|104] +endPoint=288,293 +startPoint=267,277 + +[4-wall@0,0|105] +endPoint=242,292 +startPoint=216,291 + +[4-wall@0,0|106] +endPoint=267,299 +startPoint=242,292 + +[4-wall@0,0|107] +endPoint=285,303 +startPoint=273,307 + +[4-wall@0,0|108] +endPoint=288,293 +startPoint=285,303 + +[4-wall@0,0|11] +endPoint=97,154 +startPoint=76,195 + +[4-wall@0,0|111] +endPoint=216,291 +startPoint=189,299 + +[4-wall@0,0|115] +endPoint=138,131 +startPoint=120,137 + +[4-wall@0,0|119] +endPoint=389,274 +startPoint=342,227 + +[4-wall@0,0|12] +endPoint=136,101 +startPoint=146,113 + +[4-wall@0,0|13] +endPoint=146,113 +startPoint=138,131 + +[4-wall@0,0|131] +endPoint=28,49 +startPoint=34,27 + +[4-wall@0,0|132] +endPoint=267,299 +startPoint=273,307 + +[4-wall@0,0|133] +endPoint=61,25 +startPoint=34,27 + +[4-wall@0,0|134] +endPoint=80,40 +startPoint=61,25 + +[4-wall@0,0|138] +endPoint=243,98 +startPoint=245,83 + +[4-wall@0,0|139] +endPoint=255,44 +startPoint=277,62 + +[4-wall@0,0|14] +endPoint=77,75 +startPoint=93,90 + +[4-wall@0,0|140] +endPoint=277,62 +startPoint=271,76 + +[4-wall@0,0|141] +endPoint=314,12 +startPoint=96,8 + +[4-wall@0,0|142] +endPoint=13,336 +startPoint=9,263 + +[4-wall@0,0|143] +endPoint=395,355 +startPoint=350,388 + +[4-wall@0,0|144] +endPoint=350,388 +startPoint=230,394 + +[4-wall@0,0|145] +endPoint=389,274 +startPoint=395,355 + +[4-wall@0,0|15] +endPoint=74,58 +startPoint=77,75 + +[4-wall@0,0|152] +endPoint=383,162 +startPoint=378,176 + +[4-wall@0,0|153] +endPoint=345,178 +startPoint=378,176 + +[4-wall@0,0|162] +endPoint=153,355 +startPoint=121,366 + +[4-wall@0,0|17] +endPoint=12,189 +startPoint=28,49 + +[4-wall@0,0|18] +endPoint=9,263 +startPoint=12,189 + +[4-wall@0,0|19] +endPoint=39,353 +startPoint=13,336 + +[4-wall@0,0|20] +endPoint=69,367 +startPoint=39,353 + +[4-wall@0,0|21] +endPoint=190,299 +startPoint=164,241 + +[4-wall@0,0|22] +endPoint=379,147 +startPoint=383,162 + +[4-wall@0,0|23] +endPoint=345,178 +startPoint=322,148 + +[4-wall@0,0|24] +endPoint=275,113 +startPoint=243,98 + +[4-wall@0,0|25] +endPoint=291,137 +startPoint=275,113 + +[4-wall@0,0|26] +endPoint=182,55 +startPoint=158,60 + +[4-wall@0,0|27] +endPoint=80,40 +startPoint=79,24 + +[4-wall@0,0|28] +endPoint=208,51 +startPoint=182,55 + +[4-wall@0,0|29] +endPoint=132,51 +startPoint=103,51 + +[4-wall@0,0|30] +endPoint=230,49 +startPoint=208,51 + +[4-wall@0,0|31] +endPoint=245,83 +startPoint=271,76 + +[4-wall@0,0|32] +endPoint=103,51 +startPoint=80,40 + +[4-wall@0,0|33] +endPoint=322,148 +startPoint=291,137 + +[4-wall@0,0|34] +endPoint=96,8 +startPoint=79,24 + +[4-wall@0,0|38] +endPoint=379,147 +startPoint=385,136 + +[4-wall@0,0|39] +endPoint=385,136 +startPoint=383,113 + +[4-wall@0,0|4] +endPoint=121,366 +startPoint=69,367 + +[4-wall@0,0|40] +endPoint=383,113 +startPoint=379,83 + +[4-wall@0,0|41] +endPoint=379,83 +startPoint=385,60 + +[4-wall@0,0|42] +endPoint=385,60 +startPoint=383,43 + +[4-wall@0,0|43] +endPoint=383,43 +startPoint=365,22 + +[4-wall@0,0|44] +endPoint=365,22 +startPoint=314,12 + +[4-wall@0,0|5] +endPoint=189,299 +startPoint=153,355 + +[4-wall@0,0|7] +endPoint=141,225 +startPoint=220,278 + +[4-wall@0,0|8] +endPoint=76,195 +startPoint=95,237 + +[4-wall@0,0|9] +endPoint=120,137 +startPoint=97,154 + +[4-wall@0,0|94] +endPoint=136,101 +startPoint=93,90 + +[4-wall@0,0|95] +endPoint=158,60 +startPoint=132,51 + +[5-ball@364,360] +dummykey=true + +[5-cup@32,41|3] +dummykey=true + +[5-floater@288,52|16] +botWallVisible=false +endPoint=288,52 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=7 +startPoint=101,37 +topWallVisible=false +width=80 + +[5-floater@294,95|10] +botWallVisible=false +endPoint=294,95 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=18 +startPoint=88,293 +topWallVisible=false +width=80 + +[5-floater@33,319|15] +botWallVisible=false +endPoint=33,319 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=64,200 +topWallVisible=false +width=80 + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[5-sand@137,111|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@137,118|17] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[5-sand@145,132|19] +changeEnabled=false +changeEvery=50 +height=20 +width=26 + +[5-sand@152,120|18] +changeEnabled=false +changeEvery=50 +height=44 +width=28 + +[5-sand@152,258|13] +changeEnabled=false +changeEvery=50 +height=64 +width=62 + +[5-sand@155,265|15] +changeEnabled=false +changeEvery=50 +height=60 +width=56 + +[5-sand@158,279|19] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@159,244|11] +changeEnabled=false +changeEvery=50 +height=26 +width=44 + +[5-sand@162,269|20] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-sand@165,257|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-slope@12,11|17] +grade=2 +gradient=Diagonal +height=162 +reversed=true +stuckOnGround=true +width=77 + +[5-slope@271,268|16] +grade=4 +gradient=Diagonal +height=120 +reversed=false +stuckOnGround=true +width=118 + +[5-wall@0,0|4] +endPoint=345,237 +startPoint=122,68 + +[5-wall@0,0|5] +endPoint=148,359 +startPoint=68,99 + +[6-ball@36,37] +dummykey=true + +[6-blackhole@331,316|12] +exit=199,62 +exitDeg=0 +maxspeed=6 +minspeed=2 + +[6-blackhole@374,374|17] +exit=17,32 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[6-bridge@166,277|10] +botWallVisible=true +height=74 +leftWallVisible=false +rightWallVisible=true +topWallVisible=true +width=219 + +[6-bridge@223,134|16] +botWallVisible=true +height=27 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=29 + +[6-bridge@310,72|15] +botWallVisible=true +height=32 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=28 + +[6-cup@307,154|14] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-slope@10,187|5] +grade=2 +gradient=Diagonal +height=87 +reversed=true +stuckOnGround=false +width=67 + +[6-slope@11,11|8] +grade=2 +gradient=Diagonal +height=130 +reversed=false +stuckOnGround=false +width=64 + +[6-slope@11,140|3] +grade=2 +gradient=Opposite Diagonal +height=51 +reversed=false +stuckOnGround=false +width=64 + +[6-slope@11,148|11] +grade=3 +gradient=Opposite Diagonal +height=241 +reversed=false +stuckOnGround=false +width=181 + +[6-slope@12,12|6] +grade=2 +gradient=Diagonal +height=127 +reversed=true +stuckOnGround=false +width=62 + +[6-slope@12,140|7] +grade=2 +gradient=Opposite Diagonal +height=48 +reversed=true +stuckOnGround=false +width=63 + +[6-slope@231,275|18] +grade=4 +gradient=Horizontal +height=77 +reversed=true +stuckOnGround=false +width=23 + +[6-wall@0,0|18] +endPoint=76,231 +startPoint=76,13 + +[6-windmill@194,42|19] +botWallVisible=false +bottom=true +height=149 +leftWallVisible=true +rightWallVisible=true +speed=6 +topWallVisible=true +width=166 + +[7-ball@211,293] +dummykey=true + +[7-blackhole@236,39|38] +exit=83,55 +exitDeg=40 +maxspeed=2 +minspeed=1 + +[7-blackhole@248,83|20] +exit=82,30 +exitDeg=30 +maxspeed=2 +minspeed=1 + +[7-cup@340,62|4] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@-30,384|21] +changeEnabled=false +changeEvery=50 +height=288 +width=384 + +[7-puddle@123,354|35] +changeEnabled=false +changeEvery=50 +height=136 +width=96 + +[7-puddle@147,390|14] +changeEnabled=false +changeEvery=50 +height=170 +width=64 + +[7-puddle@15,318|13] +changeEnabled=false +changeEvery=50 +height=116 +width=278 + +[7-puddle@271,143|22] +changeEnabled=false +changeEvery=50 +height=30 +width=62 + +[7-puddle@294,150|21] +changeEnabled=false +changeEvery=50 +height=32 +width=80 + +[7-puddle@341,161|15] +changeEnabled=false +changeEvery=50 +height=38 +width=86 + +[7-sand@132,70|14] +changeEnabled=false +changeEvery=50 +height=46 +width=72 + +[7-sand@185,132|19] +changeEnabled=false +changeEvery=50 +height=38 +width=60 + +[7-sand@311,116|18] +changeEnabled=false +changeEvery=50 +height=40 +width=80 + +[7-sand@329,119|17] +changeEnabled=false +changeEvery=50 +height=42 +width=68 + +[7-sand@349,123|16] +changeEnabled=false +changeEvery=50 +height=52 +width=60 + +[7-sand@364,134|24] +changeEnabled=false +changeEvery=50 +height=52 +width=30 + +[7-slope@11,10|5] +grade=4 +gradient=Vertical +height=209 +reversed=true +stuckOnGround=false +width=378 + +[7-slope@11,238|17] +grade=4 +gradient=Vertical +height=46 +reversed=false +stuckOnGround=false +width=118 + +[7-slope@127,237|16] +grade=4 +gradient=Opposite Diagonal +height=41 +reversed=false +stuckOnGround=false +width=63 + +[7-slope@129,278|15] +grade=4 +gradient=Horizontal +height=121 +reversed=true +stuckOnGround=false +width=62 + +[7-slope@226,238|41] +grade=4 +gradient=Diagonal +height=149 +reversed=false +stuckOnGround=false +width=163 + +[7-wall@0,0|23] +endPoint=284,123 +startPoint=240,117 + +[8-ball@40,30] +dummykey=true + +[8-bridge@151,203|21] +botWallVisible=true +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=66 + +[8-cup@198,167|10] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=6 + +[8-puddle@170,236|22] +changeEnabled=false +changeEvery=50 +height=36 +width=38 + +[8-puddle@186,239|41] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-puddle@207,240|43] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-puddle@268,292|17] +changeEnabled=false +changeEvery=50 +height=44 +width=48 + +[8-puddle@268,307|42] +changeEnabled=false +changeEvery=50 +height=34 +width=90 + +[8-puddle@279,280|19] +changeEnabled=false +changeEvery=50 +height=42 +width=40 + +[8-puddle@296,280|13] +changeEnabled=false +changeEvery=50 +height=82 +width=50 + +[8-slope@133,253|37] +grade=4 +gradient=Opposite Diagonal +height=15 +reversed=true +stuckOnGround=false +width=16 + +[8-slope@134,206|38] +grade=4 +gradient=Horizontal +height=47 +reversed=false +stuckOnGround=false +width=15 + +[8-slope@14,287|20] +grade=8 +gradient=Opposite Diagonal +height=101 +reversed=false +stuckOnGround=false +width=109 + +[8-slope@146,253|24] +grade=4 +gradient=Vertical +height=13 +reversed=true +stuckOnGround=false +width=78 + +[8-slope@150,129|27] +grade=3 +gradient=Horizontal +height=69 +reversed=false +stuckOnGround=false +width=28 + +[8-slope@221,204|39] +grade=4 +gradient=Horizontal +height=49 +reversed=true +stuckOnGround=false +width=18 + +[8-slope@223,253|36] +grade=4 +gradient=Diagonal +height=13 +reversed=true +stuckOnGround=false +width=15 + +[8-slope@233,91|35] +grade=3 +gradient=Vertical +height=66 +reversed=true +stuckOnGround=false +width=27 + +[8-slope@256,156|28] +grade=3 +gradient=Opposite Diagonal +height=20 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@257,105|25] +grade=3 +gradient=Vertical +height=49 +reversed=true +stuckOnGround=false +width=27 + +[8-slope@284,122|26] +grade=3 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=31 + +[8-slope@291,10|12] +grade=8 +gradient=Opposite Diagonal +height=98 +reversed=true +stuckOnGround=false +width=97 + +[8-wall@0,0|11] +endPoint=11,161 +startPoint=30,90 + +[8-wall@0,0|29] +endPoint=318,125 +startPoint=238,83 + +[8-wall@0,0|3] +endPoint=238,83 +startPoint=61,70 + +[8-wall@0,0|30] +endPoint=300,226 +startPoint=320,150 + +[8-wall@0,0|31] +endPoint=90,126 +startPoint=70,145 + +[8-wall@0,0|32] +endPoint=61,70 +startPoint=30,90 + +[8-wall@0,0|33] +endPoint=227,129 +startPoint=241,143 + +[8-wall@0,0|34] +endPoint=241,181 +startPoint=225,196 + +[8-wall@0,0|4] +endPoint=300,226 +startPoint=185,332 + +[8-wall@0,0|40] +endPoint=318,125 +startPoint=320,150 + +[8-wall@0,0|41] +endPoint=185,332 +startPoint=171,331 + +[8-wall@0,0|5] +endPoint=171,331 +startPoint=75,228 + +[8-wall@0,0|6] +endPoint=70,145 +startPoint=75,228 + +[8-wall@0,0|7] +endPoint=90,126 +startPoint=227,129 + +[8-wall@0,0|8] +endPoint=241,143 +startPoint=241,181 + +[8-wall@0,0|9] +endPoint=225,196 +startPoint=141,196 + +[9-ball@43,342] +dummykey=true + +[9-blackhole@189,342|22] +exit=44,144 +exitDeg=0 +maxspeed=3 +minspeed=1 + +[9-cup@195,165|20] +dummykey=true + +[9-cup@274,316|10] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[9-puddle@260,356|30] +changeEnabled=false +changeEvery=50 +height=34 +width=72 + +[9-puddle@284,362|3] +changeEnabled=false +changeEvery=50 +height=28 +width=60 + +[9-puddle@295,281|6] +changeEnabled=false +changeEvery=50 +height=36 +width=48 + +[9-puddle@298,286|31] +changeEnabled=false +changeEvery=50 +height=38 +width=38 + +[9-puddle@303,367|4] +changeEnabled=false +changeEvery=50 +height=28 +width=62 + +[9-puddle@308,294|7] +changeEnabled=false +changeEvery=50 +height=38 +width=46 + +[9-puddle@314,301|32] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[9-puddle@316,364|5] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@321,307|8] +changeEnabled=false +changeEvery=50 +height=42 +width=40 + +[9-puddle@328,355|29] +changeEnabled=false +changeEvery=50 +height=36 +width=34 + +[9-puddle@329,320|28] +changeEnabled=false +changeEvery=50 +height=42 +width=38 + +[9-puddle@333,336|9] +changeEnabled=false +changeEvery=50 +height=62 +width=36 + +[9-sand@87,234|26] +changeEnabled=false +changeEvery=50 +height=36 +width=62 + +[9-slope@11,11|19] +grade=2 +gradient=Diagonal +height=513 +reversed=true +stuckOnGround=false +width=499 + +[9-slope@38,337|28] +grade=5 +gradient=Diagonal +height=10 +reversed=true +stuckOnGround=false +width=8 + +[9-slope@93,148|27] +grade=4 +gradient=Elliptic +height=72 +reversed=false +stuckOnGround=false +width=72 + +[9-wall@0,0|11] +endPoint=88,205 +startPoint=68,172 + +[9-wall@0,0|12] +endPoint=222,186 +startPoint=166,193 + +[9-wall@0,0|13] +endPoint=265,251 +startPoint=217,229 + +[9-wall@0,0|14] +endPoint=295,205 +startPoint=228,133 + +[9-wall@0,0|15] +endPoint=178,146 +startPoint=107,128 + +[9-wall@0,0|16] +endPoint=164,219 +startPoint=133,239 + +[9-wall@0,0|17] +endPoint=209,325 +startPoint=167,326 + +[9-wall@0,0|18] +endPoint=166,351 +startPoint=123,311 + +[9-wall@0,0|21] +endPoint=180,278 +startPoint=184,242 + +[9-wall@0,0|23] +endPoint=54,276 +startPoint=35,245 + +[9-wall@0,0|28] +endPoint=93,345 +startPoint=61,369 diff --git a/kolf/courses/Impossible b/kolf/courses/Impossible new file mode 100644 index 00000000..40e72e4e --- /dev/null +++ b/kolf/courses/Impossible @@ -0,0 +1,3573 @@ +[0-course@-50,-50] +Name=Impossible Course +Name[af]=Onmoontlik Natuurlik +Name[bg]=Ðевъзможно +Name[bn]=অসমà§à¦­à¦¬ কঠিন খেলা +Name[bs]=Nemoguć teren +Name[ca]=Cursa impossible +Name[cs]=Nesplnitelný +Name[da]=Umulig bane +Name[de]=Unmöglich +Name[es]=Campo imposible +Name[et]=Võimatu väljak +Name[fi]=Mahdoton kenttä +Name[fr]=Parcours impossible +Name[gl]=Campo imposible +Name[he]=מסלול בלתי ×פשרי +Name[hi]=असंभव कोरà¥à¤¸ +Name[hr]=Nemogući smjer +Name[hu]=Különlegesen nehéz pálya +Name[is]=Ómöguleg leið +Name[it]=Percorso impossibile +Name[ja]=ä¸å¯èƒ½ãªã‚³ãƒ¼ã‚¹ +Name[mk]=Ðевозможен терен +Name[nb]=Umulig bane +Name[nl]=Onmogelijk parcours +Name[nn]=Umogleg bane +Name[nso]=Course yeo esa Kgonegego +Name[pl]=Niemożliwy do wykonania +Name[pt]=Curso Impossível +Name[pt_BR]=Curso Impossível +Name[ro]=Cale imposibilă +Name[ru]=МиÑÑÐ¸Ñ Ð½ÐµÐ²Ñ‹Ð¿Ð¾Ð»Ð½Ð¸Ð¼Ð° +Name[sk]=Nezvládnuteľné ihrisko +Name[sl]=NemogoÄe igriÅ¡Äe +Name[sr]=Ðемогућ терен +Name[sr@Latn]=Nemoguć teren +Name[sv]=Omöjlig bana +Name[ta]=சாதà¯à®¤à®¿à®¯à®®à®¿à®²à¯à®²à®¾à®¤ மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Имконнопазир +Name[tr]=Ä°mkansız Pist +Name[uk]=ÐайÑкладніший майданчик +Name[ven]=Thero i sa koneiho +Name[xh]=Indlela Engenzekiyo +Name[xx]=xxImpossible Coursexx +Name[zh_CN]=无法完æˆçš„路线 +Name[zh_TW]=ä¸å¯èƒ½çš„路線 +Name[zu]=Indlela engenzeki +author=Jason Katz-Brown + +[1-ball@31,330] +dummykey=true + +[1-cup@328,318|16] +dummykey=true + +[1-floater@227,64|8] +botWallVisible=false +endPoint=227,64 +height=85 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=105,76 +topWallVisible=true +width=93 + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[1-puddle@-5,99|20] +changeEnabled=false +changeEvery=50 +height=210 +width=60 + +[1-puddle@108,17|39] +changeEnabled=false +changeEvery=50 +height=32 +width=36 + +[1-puddle@11,15|22] +changeEnabled=false +changeEvery=50 +height=30 +width=32 + +[1-puddle@117,33|45] +changeEnabled=false +changeEvery=50 +height=32 +width=46 + +[1-puddle@124,27|25] +changeEnabled=false +changeEvery=50 +height=36 +width=78 + +[1-puddle@127,31|42] +changeEnabled=false +changeEvery=50 +height=36 +width=76 + +[1-puddle@131,37|43] +changeEnabled=false +changeEvery=50 +height=38 +width=58 + +[1-puddle@131,46|46] +changeEnabled=false +changeEvery=50 +height=26 +width=38 + +[1-puddle@139,46|44] +changeEnabled=false +changeEvery=50 +height=40 +width=46 + +[1-puddle@145,42|24] +changeEnabled=false +changeEvery=50 +height=52 +width=54 + +[1-puddle@163,41|40] +changeEnabled=false +changeEvery=50 +height=26 +width=30 + +[1-puddle@172,45|41] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[1-puddle@177,56|6] +changeEnabled=false +changeEvery=50 +height=46 +width=104 + +[1-puddle@188,207|1] +changeEnabled=false +changeEvery=50 +height=68 +width=28 + +[1-puddle@19,9|5] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[1-puddle@2,22|7] +changeEnabled=false +changeEvery=50 +height=38 +width=34 + +[1-puddle@216,158|4] +changeEnabled=false +changeEvery=50 +height=232 +width=82 + +[1-puddle@216,256|3] +changeEnabled=false +changeEvery=50 +height=184 +width=98 + +[1-puddle@22,8|0] +changeEnabled=false +changeEvery=50 +height=24 +width=44 + +[1-puddle@247,201|2] +changeEnabled=false +changeEvery=50 +height=84 +width=26 + +[1-puddle@40,-3|21] +changeEnabled=false +changeEvery=50 +height=42 +width=124 + +[1-puddle@7,24|0] +changeEnabled=false +changeEvery=50 +height=38 +width=28 + +[1-puddle@88,5|0] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[1-puddle@98,9|26] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[1-sand@224,10|27] +changeEnabled=false +changeEvery=50 +height=46 +width=124 + +[1-sand@247,19|31] +changeEnabled=false +changeEvery=50 +height=30 +width=68 + +[1-sand@277,19|30] +changeEnabled=false +changeEvery=50 +height=40 +width=94 + +[1-sand@292,10|33] +changeEnabled=false +changeEvery=50 +height=16 +width=58 + +[1-sand@304,26|32] +changeEnabled=false +changeEvery=50 +height=32 +width=66 + +[1-sand@314,26|29] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[1-sand@349,114|37] +changeEnabled=false +changeEvery=50 +height=62 +width=22 + +[1-sand@358,92|36] +changeEnabled=false +changeEvery=50 +height=80 +width=58 + +[1-sand@367,123|35] +changeEnabled=false +changeEvery=50 +height=96 +width=54 + +[1-sand@368,133|34] +changeEnabled=false +changeEvery=50 +height=82 +width=56 + +[1-sand@369,150|15] +changeEnabled=false +changeEvery=50 +height=86 +width=58 + +[1-sand@383,101|38] +changeEnabled=false +changeEvery=50 +height=38 +width=14 + +[1-slope@-42,10|22] +grade=2 +gradient=Horizontal +height=387 +reversed=true +stuckOnGround=false +width=135 + +[1-slope@135,310|11] +grade=4 +gradient=Opposite Diagonal +height=76 +reversed=true +stuckOnGround=true +width=61 + +[1-slope@136,82|10] +grade=4 +gradient=Horizontal +height=229 +reversed=false +stuckOnGround=true +width=60 + +[1-slope@192,301|12] +grade=4 +gradient=Vertical +height=90 +reversed=true +stuckOnGround=true +width=66 + +[1-slope@211,6|28] +grade=4 +gradient=Opposite Diagonal +height=82 +reversed=false +stuckOnGround=true +width=75 + +[1-slope@244,89|14] +grade=4 +gradient=Horizontal +height=213 +reversed=true +stuckOnGround=true +width=47 + +[1-slope@257,301|13] +grade=4 +gradient=Diagonal +height=88 +reversed=true +stuckOnGround=true +width=35 + +[1-slope@286,276|29] +grade=4 +gradient=Elliptic +height=82 +reversed=true +stuckOnGround=false +width=82 + +[1-slope@29,-3|9] +grade=4 +gradient=Opposite Diagonal +height=149 +reversed=true +stuckOnGround=true +width=173 + +[1-wall@0,0|17] +endPoint=358,358 +startPoint=298,362 + +[1-wall@0,0|18] +endPoint=358,358 +startPoint=380,331 + +[1-wall@0,0|19] +endPoint=380,331 +startPoint=383,206 + +[1-wall@0,0|23] +endPoint=280,345 +startPoint=298,362 + +[10-ball@351,365] +dummykey=true + +[10-blackhole@255,206|4] +exit=15,39 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[10-cup@44,38|3] +dummykey=true + +[10-floater@220,184|2] +botWallVisible=true +endPoint=220,184 +height=43 +leftWallVisible=true +rightWallVisible=true +speed=7 +startPoint=69,209 +topWallVisible=true +width=71 + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[10-puddle@194,199|0] +changeEnabled=false +changeEvery=50 +height=60 +width=72 + +[10-puddle@201,188|0] +changeEnabled=false +changeEvery=50 +height=56 +width=66 + +[10-puddle@201,213|0] +changeEnabled=false +changeEvery=50 +height=32 +width=56 + +[10-puddle@214,197|0] +changeEnabled=false +changeEvery=50 +height=60 +width=48 + +[10-slope@35,30|0] +grade=6 +gradient=Elliptic +height=338 +reversed=false +stuckOnGround=true +width=338 + +[10-wall@0,0|0] +endPoint=387,198 +startPoint=307,198 + +[10-wall@0,0|1] +endPoint=73,196 +startPoint=12,196 + +[11-ball@182,47] +dummykey=true + +[11-bridge@158,36|17] +botWallVisible=false +height=27 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=43 + +[11-cup@97,297|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=1 + +[11-sand@204,336|18] +changeEnabled=false +changeEvery=50 +height=30 +width=34 + +[11-sand@213,327|21] +changeEnabled=false +changeEvery=50 +height=18 +width=22 + +[11-sand@213,331|19] +changeEnabled=false +changeEvery=50 +height=24 +width=26 + +[11-sand@222,324|20] +changeEnabled=false +changeEvery=50 +height=26 +width=30 + +[11-slope@129,150|2] +grade=4 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=99 + +[11-slope@130,148|1] +grade=4 +gradient=Opposite Diagonal +height=93 +reversed=false +stuckOnGround=false +width=98 + +[11-slope@130,242|15] +grade=4 +gradient=Diagonal +height=75 +reversed=true +stuckOnGround=false +width=97 + +[11-slope@130,63|3] +grade=4 +gradient=Diagonal +height=88 +reversed=false +stuckOnGround=false +width=99 + +[11-slope@130,64|4] +grade=4 +gradient=Diagonal +height=87 +reversed=true +stuckOnGround=false +width=97 + +[11-slope@73,241|22] +grade=5 +gradient=Opposite Diagonal +height=73 +reversed=true +stuckOnGround=false +width=58 + +[11-wall@0,0|10] +endPoint=118,365 +startPoint=69,296 + +[11-wall@0,0|11] +endPoint=69,296 +startPoint=81,244 + +[11-wall@0,0|12] +endPoint=81,244 +startPoint=129,242 + +[11-wall@0,0|13] +endPoint=129,242 +startPoint=130,63 + +[11-wall@0,0|14] +endPoint=130,63 +startPoint=158,36 + +[11-wall@0,0|16] +endPoint=201,36 +startPoint=231,64 + +[11-wall@0,0|5] +endPoint=229,242 +startPoint=231,64 + +[11-wall@0,0|6] +endPoint=229,242 +startPoint=261,319 + +[11-wall@0,0|7] +endPoint=261,319 +startPoint=252,353 + +[11-wall@0,0|8] +endPoint=252,353 +startPoint=201,376 + +[11-wall@0,0|9] +endPoint=201,376 +startPoint=118,365 + +[12-ball@196,183] +dummykey=true + +[12-cup@107,101|2] +dummykey=true + +[12-cup@107,271|1] +dummykey=true + +[12-cup@285,103|0] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[12-slope@177,164|7] +grade=6 +gradient=Elliptic +height=37 +reversed=false +stuckOnGround=true +width=37 + +[12-slope@197,54|4] +grade=8 +gradient=Opposite Diagonal +height=128 +reversed=false +stuckOnGround=true +width=124 + +[12-slope@198,182|6] +grade=8 +gradient=Diagonal +height=123 +reversed=true +stuckOnGround=true +width=122 + +[12-slope@288,-2|15] +grade=3 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[12-slope@347,68|8] +grade=2 +gradient=Elliptic +height=79 +reversed=false +stuckOnGround=false +width=79 + +[12-slope@68,183|5] +grade=8 +gradient=Opposite Diagonal +height=123 +reversed=true +stuckOnGround=true +width=130 + +[12-slope@69,52|3] +grade=8 +gradient=Diagonal +height=129 +reversed=false +stuckOnGround=true +width=128 + +[12-windmill@234,300|9] +botWallVisible=true +bottom=false +height=83 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=121 + +[13-ball@72,345] +dummykey=true + +[13-cup@296,86|0] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[13-puddle@338,3|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[13-puddle@357,-16|15] +changeEnabled=false +changeEvery=50 +height=62 +width=132 + +[13-puddle@358,21|13] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[13-puddle@367,103|2] +changeEnabled=false +changeEvery=50 +height=64 +width=38 + +[13-puddle@367,55|12] +changeEnabled=false +changeEvery=50 +height=64 +width=38 + +[13-puddle@369,0|7] +changeEnabled=false +changeEvery=50 +height=80 +width=94 + +[13-puddle@370,145|4] +changeEnabled=false +changeEvery=50 +height=120 +width=78 + +[13-puddle@376,81|5] +changeEnabled=false +changeEvery=50 +height=76 +width=50 + +[13-puddle@380,31|6] +changeEnabled=false +changeEvery=50 +height=82 +width=80 + +[13-puddle@394,99|14] +changeEnabled=false +changeEvery=50 +height=68 +width=46 + +[13-puddle@428,179|8] +changeEnabled=false +changeEvery=50 +height=88 +width=164 + +[13-sand@103,64|10] +changeEnabled=false +changeEvery=50 +height=94 +width=130 + +[13-sand@106,102|26] +changeEnabled=false +changeEvery=50 +height=86 +width=48 + +[13-sand@106,172|20] +changeEnabled=false +changeEvery=50 +height=62 +width=36 + +[13-sand@107,91|25] +changeEnabled=false +changeEvery=50 +height=50 +width=64 + +[13-sand@120,149|21] +changeEnabled=false +changeEvery=50 +height=74 +width=14 + +[13-sand@35,210|18] +changeEnabled=false +changeEvery=50 +height=82 +width=46 + +[13-sand@43,201|27] +changeEnabled=false +changeEvery=50 +height=40 +width=48 + +[13-sand@60,148|23] +changeEnabled=false +changeEvery=50 +height=36 +width=30 + +[13-sand@66,96|9] +changeEnabled=false +changeEvery=50 +height=134 +width=72 + +[13-sand@67,181|11] +changeEnabled=false +changeEvery=50 +height=38 +width=92 + +[13-sand@72,194|19] +changeEnabled=false +changeEvery=50 +height=40 +width=90 + +[13-sand@73,171|24] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[13-sand@76,164|22] +changeEnabled=false +changeEvery=50 +height=70 +width=56 + +[13-sand@96,126|17] +changeEnabled=false +changeEvery=50 +height=138 +width=66 + +[13-sign@142,193|1] +Comment=Reprieve +Comment[bg]=ОтмÑна +Comment[bn]=সাময়িকভাবে সà§à¦¥à¦—িত +Comment[bs]=Odgodi +Comment[ca]=Respir +Comment[da]=Red dig selv +Comment[de]=Aufschub +Comment[es]=Respiro +Comment[et]=Kergendus +Comment[fi]=Armonaika +Comment[fr]=Sursis +Comment[gl]=Respiro +Comment[he]=הקלה +Comment[hi]=विराम +Comment[hr]=Odgodi +Comment[hu]=Mázli +Comment[is]=Milda +Comment[it]=Pausa +Comment[ja]=カンタンãªä¸€çž¬ +Comment[mk]=Одложување +Comment[nb]=NÃ¥de +Comment[nl]=Gratie +Comment[nn]=NÃ¥de +Comment[pl]=Odroczenie +Comment[pt]=Tortura +Comment[pt_BR]=Suspensão +Comment[ru]=ОтÑрочка +Comment[sk]=Omilostnenie +Comment[sl]=Odložitev +Comment[sr]=Предах +Comment[sr@Latn]=Predah +Comment[sv]=Lättnad +Comment[ta]=ஒதà¯à®¤à®¿ வை +Comment[tg]=Таъхир +Comment[tr]=Ceza Ertele +Comment[uk]=Ð’Ñ–Ð´ÐºÐ»Ð°Ð´Ð°Ð½Ð½Ñ +Comment[ven]=U vhofhololwa +Comment[xh]=Urhoxiso lwethutyana +Comment[xx]=xxReprievexx +Comment[zh_CN]=暂时缓解 +Comment[zh_TW]=æš«ç·© +Comment[zu]=Phumuza +botWallVisible=false +height=47 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=121 + +[13-slope@212,12|3] +grade=4 +gradient=Elliptic +height=117 +reversed=false +stuckOnGround=false +width=117 + +[14-ball@63,341] +dummykey=true + +[14-blackhole@137,332|12] +exit=22,383 +exitDeg=40 +maxspeed=5 +minspeed=3 + +[14-blackhole@317,208|10] +exit=17,377 +exitDeg=40 +maxspeed=5 +minspeed=3 + +[14-bumper@179,215|4] +dummykey=true + +[14-bumper@290,116|6] +dummykey=true + +[14-cup@351,37|0] +dummykey=true + +[14-floater@113,315|3] +botWallVisible=true +endPoint=113,315 +height=39 +leftWallVisible=true +rightWallVisible=true +speed=7 +startPoint=29,195 +topWallVisible=true +width=49 + +[14-floater@281,189|5] +botWallVisible=true +endPoint=281,189 +height=39 +leftWallVisible=true +rightWallVisible=true +speed=19 +startPoint=112,79 +topWallVisible=true +width=74 + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=3 + +[14-puddle@334,315|9] +changeEnabled=false +changeEvery=50 +height=60 +width=96 + +[14-puddle@372,404|8] +changeEnabled=false +changeEvery=50 +height=242 +width=332 + +[14-puddle@391,360|7] +changeEnabled=false +changeEvery=50 +height=218 +width=226 + +[14-slope@-2,-2|2] +grade=4 +gradient=Diagonal +height=357 +reversed=true +stuckOnGround=true +width=360 + +[14-slope@36,37|1] +grade=4 +gradient=Diagonal +height=362 +reversed=false +stuckOnGround=true +width=364 + +[14-windmill@283,12|11] +botWallVisible=false +bottom=true +height=71 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=92 + +[15-ball@202,250] +dummykey=true + +[15-blackhole@122,62|42] +exit=107,219 +exitDeg=180 +maxspeed=5 +minspeed=3 + +[15-bridge@129,134|62] +botWallVisible=false +height=20 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=74 + +[15-bridge@129,135|63] +botWallVisible=false +height=37 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=16 + +[15-bridge@161,134|59] +botWallVisible=false +height=46 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=87 + +[15-bridge@180,152|61] +botWallVisible=false +height=42 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=34 + +[15-bridge@202,120|60] +botWallVisible=false +height=45 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=32 + +[15-bridge@222,152|64] +botWallVisible=false +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=47 + +[15-cup@185,340|0] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[15-slope@102,285|56] +grade=5 +gradient=Opposite Diagonal +height=25 +reversed=true +stuckOnGround=false +width=45 + +[15-slope@105,251|47] +grade=6 +gradient=Horizontal +height=16 +reversed=false +stuckOnGround=false +width=27 + +[15-slope@108,313|57] +grade=4 +gradient=Opposite Diagonal +height=55 +reversed=false +stuckOnGround=false +width=39 + +[15-slope@132,215|58] +grade=6 +gradient=Vertical +height=20 +reversed=true +stuckOnGround=false +width=34 + +[15-slope@145,308|53] +grade=4 +gradient=Horizontal +height=61 +reversed=true +stuckOnGround=false +width=27 + +[15-slope@167,212|46] +grade=4 +gradient=Horizontal +height=77 +reversed=true +stuckOnGround=false +width=118 + +[15-slope@172,308|54] +grade=4 +gradient=Vertical +height=25 +reversed=true +stuckOnGround=false +width=28 + +[15-slope@174,345|55] +grade=4 +gradient=Vertical +height=22 +reversed=false +stuckOnGround=false +width=27 + +[15-slope@304,150|45] +grade=4 +gradient=Horizontal +height=105 +reversed=false +stuckOnGround=false +width=59 + +[15-slope@65,251|52] +grade=4 +gradient=Opposite Diagonal +height=60 +reversed=false +stuckOnGround=false +width=40 + +[15-slope@81,207|51] +grade=5 +gradient=Horizontal +height=24 +reversed=false +stuckOnGround=false +width=30 + +[15-slope@90,51|65] +grade=4 +gradient=Diagonal +height=51 +reversed=true +stuckOnGround=false +width=25 + +[15-wall@0,0|10] +endPoint=216,102 +startPoint=191,102 + +[15-wall@0,0|11] +endPoint=191,102 +startPoint=191,87 + +[15-wall@0,0|12] +endPoint=191,87 +startPoint=153,87 + +[15-wall@0,0|13] +endPoint=153,87 +startPoint=153,111 + +[15-wall@0,0|14] +endPoint=153,111 +startPoint=115,111 + +[15-wall@0,0|15] +endPoint=115,111 +startPoint=115,78 + +[15-wall@0,0|16] +endPoint=115,78 +startPoint=134,76 + +[15-wall@0,0|17] +endPoint=134,76 +startPoint=134,49 + +[15-wall@0,0|18] +endPoint=134,49 +startPoint=87,49 + +[15-wall@0,0|19] +endPoint=87,49 +startPoint=87,184 + +[15-wall@0,0|2] +endPoint=305,151 +startPoint=363,151 + +[15-wall@0,0|20] +endPoint=87,184 +startPoint=130,184 + +[15-wall@0,0|21] +endPoint=130,184 +startPoint=130,239 + +[15-wall@0,0|22] +endPoint=123,252 +startPoint=86,252 + +[15-wall@0,0|23] +endPoint=86,252 +startPoint=86,232 + +[15-wall@0,0|24] +endPoint=86,232 +startPoint=111,232 + +[15-wall@0,0|25] +endPoint=111,232 +startPoint=111,205 + +[15-wall@0,0|26] +endPoint=111,205 +startPoint=64,205 + +[15-wall@0,0|27] +endPoint=64,205 +startPoint=64,314 + +[15-wall@0,0|28] +endPoint=64,314 +startPoint=106,314 + +[15-wall@0,0|29] +endPoint=106,314 +startPoint=106,369 + +[15-wall@0,0|3] +endPoint=305,151 +startPoint=305,115 + +[15-wall@0,0|30] +endPoint=106,369 +startPoint=199,369 + +[15-wall@0,0|31] +endPoint=199,369 +startPoint=199,307 + +[15-wall@0,0|32] +endPoint=199,307 +startPoint=146,307 + +[15-wall@0,0|33] +endPoint=146,307 +startPoint=146,285 + +[15-wall@0,0|34] +endPoint=146,285 +startPoint=105,285 + +[15-wall@0,0|35] +endPoint=105,285 +startPoint=105,268 + +[15-wall@0,0|36] +endPoint=105,268 +startPoint=166,268 + +[15-wall@0,0|37] +endPoint=166,268 +startPoint=166,291 + +[15-wall@0,0|38] +endPoint=166,291 +startPoint=305,291 + +[15-wall@0,0|39] +endPoint=305,291 +startPoint=305,256 + +[15-wall@0,0|4] +endPoint=305,115 +startPoint=270,115 + +[15-wall@0,0|40] +endPoint=305,256 +startPoint=363,256 + +[15-wall@0,0|41] +endPoint=363,256 +startPoint=363,151 + +[15-wall@0,0|43] +endPoint=166,268 +startPoint=166,212 + +[15-wall@0,0|44] +endPoint=166,212 +startPoint=285,211 + +[15-wall@0,0|49] +endPoint=165,236 +startPoint=133,270 + +[15-wall@0,0|5] +endPoint=270,115 +startPoint=270,70 + +[15-wall@0,0|50] +endPoint=123,252 +startPoint=130,239 + +[15-wall@0,0|6] +endPoint=270,70 +startPoint=238,70 + +[15-wall@0,0|7] +endPoint=238,70 +startPoint=238,89 + +[15-wall@0,0|8] +endPoint=238,89 +startPoint=216,89 + +[15-wall@0,0|9] +endPoint=216,89 +startPoint=216,102 + +[15-windmill@133,212|48] +botWallVisible=false +bottom=false +height=11 +leftWallVisible=false +rightWallVisible=false +speed=2 +topWallVisible=false +width=33 + +[16-ball@56,49] +dummykey=true + +[16-cup@212,321|1] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[16-puddle@266,315|25] +changeEnabled=false +changeEvery=50 +height=18 +width=10 + +[16-puddle@274,301|22] +changeEnabled=false +changeEvery=50 +height=22 +width=22 + +[16-puddle@276,314|28] +changeEnabled=false +changeEvery=50 +height=8 +width=12 + +[16-puddle@280,328|26] +changeEnabled=false +changeEvery=50 +height=12 +width=20 + +[16-puddle@286,315|24] +changeEnabled=false +changeEvery=50 +height=14 +width=10 + +[16-puddle@290,309|29] +changeEnabled=false +changeEvery=50 +height=16 +width=14 + +[16-puddle@292,327|31] +changeEnabled=false +changeEvery=50 +height=12 +width=18 + +[16-puddle@296,300|23] +changeEnabled=false +changeEvery=50 +height=16 +width=10 + +[16-puddle@300,321|27] +changeEnabled=false +changeEvery=50 +height=16 +width=14 + +[16-puddle@306,308|21] +changeEnabled=false +changeEvery=50 +height=16 +width=10 + +[16-puddle@329,382|8] +changeEnabled=false +changeEvery=50 +height=26 +width=36 + +[16-puddle@336,400|4] +changeEnabled=false +changeEvery=50 +height=52 +width=390 + +[16-puddle@351,359|7] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[16-puddle@355,386|6] +changeEnabled=false +changeEvery=50 +height=74 +width=72 + +[16-puddle@409,335|5] +changeEnabled=false +changeEvery=50 +height=336 +width=150 + +[16-puddle@422,231|9] +changeEnabled=false +changeEvery=50 +height=228 +width=114 + +[16-puddle@58,420|0] +changeEnabled=false +changeEvery=50 +height=96 +width=550 + +[16-sand@306,84|19] +changeEnabled=false +changeEvery=50 +height=28 +width=52 + +[16-sand@312,-7|16] +changeEnabled=false +changeEvery=50 +height=102 +width=84 + +[16-sand@322,78|18] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[16-sand@332,65|17] +changeEnabled=false +changeEvery=50 +height=58 +width=54 + +[16-sand@342,76|20] +changeEnabled=false +changeEvery=50 +height=32 +width=76 + +[16-sand@387,22|15] +changeEnabled=false +changeEvery=50 +height=142 +width=196 + +[16-slope@-2,-3|14] +grade=7 +gradient=Diagonal +height=260 +reversed=true +stuckOnGround=false +width=267 + +[16-slope@2,360|3] +grade=4 +gradient=Vertical +height=30 +reversed=false +stuckOnGround=false +width=293 + +[16-slope@202,43|13] +grade=2 +gradient=Elliptic +height=140 +reversed=true +stuckOnGround=false +width=140 + +[16-slope@271,313|10] +grade=4 +gradient=Diagonal +height=66 +reversed=false +stuckOnGround=false +width=74 + +[16-slope@319,91|12] +grade=4 +gradient=Diagonal +height=139 +reversed=false +stuckOnGround=false +width=81 + +[16-slope@320,230|11] +grade=4 +gradient=Horizontal +height=108 +reversed=false +stuckOnGround=false +width=44 + +[16-slope@69,154|2] +grade=3 +gradient=Elliptic +height=154 +reversed=false +stuckOnGround=false +width=154 + +[17-ball@55,55] +dummykey=true + +[17-blackhole@269,363|4] +exit=198,384 +exitDeg=90 +maxspeed=3 +minspeed=2 + +[17-cup@146,232|21] +dummykey=true + +[17-cup@88,233|1] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-sand@333,344|15] +changeEnabled=false +changeEvery=50 +height=40 +width=54 + +[17-sand@340,343|16] +changeEnabled=false +changeEvery=50 +height=28 +width=56 + +[17-sand@351,351|14] +changeEnabled=false +changeEvery=50 +height=38 +width=52 + +[17-sand@359,354|13] +changeEnabled=false +changeEvery=50 +height=44 +width=50 + +[17-sign@23,152|20] +Comment=Luck +Comment[ar]=حظ +Comment[bg]=КъÑмет +Comment[bn]=ভাগà§à¦¯ +Comment[br]=Chañs +Comment[bs]=Sreća +Comment[ca]=Sort +Comment[cs]=Å tÄ›stí +Comment[da]=Held +Comment[de]=Glück +Comment[el]=ΤÏχη +Comment[es]=Suerte +Comment[et]=Õnn +Comment[fi]=Onni +Comment[fr]=Chance +Comment[gl]=Sorte +Comment[he]=מזל +Comment[hi]=भागà¥à¤¯ +Comment[hr]=Sreća +Comment[hu]=Szerencse +Comment[is]=Lukka +Comment[it]=Fortunato +Comment[ja]=é‹ +Comment[lv]=Veiksme +Comment[mk]=Среќа +Comment[nb]=Flaks +Comment[nl]=Geluk +Comment[nn]=Flaks +Comment[nso]=Mahlatse +Comment[pl]=Szczęście +Comment[pt]=Sorte +Comment[pt_BR]=Sorte +Comment[ro]=Noroc +Comment[ru]=Удача +Comment[sk]=Šťastie +Comment[sl]=SreÄa +Comment[sr]=Срећа +Comment[sr@Latn]=Sreća +Comment[sv]=Tur +Comment[ta]=அதிரà¯à®·à¯à®Ÿà®®à¯ +Comment[tg]=Барор +Comment[tr]=Åžans +Comment[uk]=удача +Comment[ven]=Mashudu +Comment[wa]=Tchance +Comment[xh]=Ithamsanqa +Comment[xx]=xxLuckxx +Comment[zh_CN]=å¹¸è¿ +Comment[zh_TW]=å¹¸é‹ +Comment[zu]=Inhlanhla +botWallVisible=false +height=51 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=105 + +[17-slope@113,18|9] +grade=2 +gradient=Elliptic +height=183 +reversed=false +stuckOnGround=false +width=183 + +[17-slope@237,229|5] +grade=5 +gradient=Vertical +height=109 +reversed=false +stuckOnGround=false +width=153 + +[17-slope@289,85|19] +grade=2 +gradient=Elliptic +height=146 +reversed=false +stuckOnGround=false +width=146 + +[17-slope@43,331|11] +grade=8 +gradient=Horizontal +height=58 +reversed=true +stuckOnGround=false +width=27 + +[17-slope@71,246|10] +grade=2 +gradient=Vertical +height=43 +reversed=false +stuckOnGround=false +width=91 + +[17-slope@77,297|18] +grade=3 +gradient=Diagonal +height=89 +reversed=false +stuckOnGround=false +width=84 + +[17-wall@0,0|0] +endPoint=235,141 +startPoint=12,141 + +[17-wall@0,0|12] +endPoint=70,330 +startPoint=44,330 + +[17-wall@0,0|17] +endPoint=43,389 +startPoint=13,332 + +[17-wall@0,0|2] +endPoint=373,113 +startPoint=311,33 + +[17-wall@0,0|22] +endPoint=131,289 +startPoint=119,298 + +[17-wall@0,0|23] +endPoint=119,298 +startPoint=109,288 + +[17-wall@0,0|3] +endPoint=235,141 +startPoint=235,387 + +[17-wall@0,0|6] +endPoint=162,217 +startPoint=70,217 + +[17-wall@0,0|7] +endPoint=162,217 +startPoint=162,388 + +[17-wall@0,0|8] +endPoint=70,217 +startPoint=70,330 + +[18-ball@134,349] +dummykey=true + +[18-blackhole@362,297|7] +exit=207,44 +exitDeg=0 +maxspeed=2 +minspeed=1 + +[18-cup@309,150|0] +dummykey=true + +[18-floater@55,53|1] +botWallVisible=false +endPoint=55,53 +height=119 +leftWallVisible=false +rightWallVisible=false +speed=8 +startPoint=42,235 +topWallVisible=false +width=28 + +[18-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[18-puddle@104,229|32] +changeEnabled=false +changeEvery=50 +height=32 +width=174 + +[18-puddle@238,220|2] +changeEnabled=false +changeEvery=50 +height=52 +width=404 + +[18-puddle@279,199|5] +changeEnabled=false +changeEvery=50 +height=26 +width=100 + +[18-puddle@307,201|4] +changeEnabled=false +changeEvery=50 +height=26 +width=244 + +[18-puddle@33,222|33] +changeEnabled=false +changeEvery=50 +height=28 +width=42 + +[18-puddle@50,218|34] +changeEnabled=false +changeEvery=50 +height=22 +width=52 + +[18-puddle@605,204|3] +changeEnabled=false +changeEvery=50 +height=90 +width=750 + +[18-sand@-35,168|17] +changeEnabled=false +changeEvery=50 +height=40 +width=152 + +[18-sand@17,105|16] +changeEnabled=false +changeEvery=50 +height=152 +width=106 + +[18-sand@36,11|15] +changeEnabled=false +changeEvery=50 +height=272 +width=86 + +[18-sand@43,152|19] +changeEnabled=false +changeEvery=50 +height=50 +width=104 + +[18-sand@51,106|20] +changeEnabled=false +changeEvery=50 +height=60 +width=54 + +[18-sand@58,167|26] +changeEnabled=false +changeEvery=50 +height=26 +width=48 + +[18-sand@63,83|23] +changeEnabled=false +changeEvery=50 +height=36 +width=24 + +[18-sand@65,164|25] +changeEnabled=false +changeEvery=50 +height=38 +width=50 + +[18-sand@69,119|22] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[18-sand@69,139|21] +changeEnabled=false +changeEvery=50 +height=70 +width=60 + +[18-sand@69,149|24] +changeEnabled=false +changeEvery=50 +height=82 +width=56 + +[18-slope@145,-5|8] +grade=7 +gradient=Horizontal +height=409 +reversed=false +stuckOnGround=true +width=129 + +[18-slope@269,244|28] +grade=4 +gradient=Opposite Diagonal +height=185 +reversed=true +stuckOnGround=false +width=132 + +[18-slope@30,233|18] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=true +width=98 + +[18-slope@31,323|12] +grade=6 +gradient=Vertical +height=43 +reversed=false +stuckOnGround=true +width=52 + +[18-slope@32,243|14] +grade=5 +gradient=Horizontal +height=125 +reversed=true +stuckOnGround=true +width=20 + +[18-slope@50,241|10] +grade=5 +gradient=Horizontal +height=163 +reversed=false +stuckOnGround=true +width=76 + +[18-wall@0,0|11] +endPoint=84,367 +startPoint=35,369 + +[18-wall@0,0|13] +endPoint=84,328 +startPoint=84,367 + +[18-wall@0,0|27] +endPoint=385,123 +startPoint=385,166 + +[18-wall@0,0|29] +endPoint=422,377 +startPoint=226,377 + +[18-wall@0,0|30] +endPoint=91,380 +startPoint=84,328 + +[18-wall@0,0|31] +endPoint=91,380 +startPoint=35,369 + +[18-wall@0,0|9] +endPoint=31,244 +startPoint=31,400 + +[18-windmill@201,-2|6] +botWallVisible=false +bottom=true +height=121 +leftWallVisible=true +rightWallVisible=false +speed=5 +topWallVisible=false +width=201 + +[2-ball@62,329] +dummykey=true + +[2-cup@71,229|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[2-puddle@285,-3|2] +changeEnabled=false +changeEvery=50 +height=42 +width=20 + +[2-puddle@315,13|12] +changeEnabled=false +changeEvery=50 +height=32 +width=70 + +[2-puddle@331,-5|1] +changeEnabled=false +changeEvery=50 +height=68 +width=76 + +[2-puddle@364,0|9] +changeEnabled=false +changeEvery=50 +height=58 +width=132 + +[2-sand@15,163|19] +changeEnabled=false +changeEvery=50 +height=86 +width=66 + +[2-sand@31,76|16] +changeEnabled=false +changeEvery=50 +height=58 +width=42 + +[2-sand@76,33|17] +changeEnabled=false +changeEvery=50 +height=36 +width=52 + +[2-slope@1,124|14] +grade=4 +gradient=Vertical +height=146 +reversed=false +stuckOnGround=false +width=179 + +[2-slope@111,100|4] +grade=3 +gradient=Diagonal +height=301 +reversed=false +stuckOnGround=false +width=291 + +[2-slope@181,130|0] +grade=4 +gradient=Vertical +height=32 +reversed=true +stuckOnGround=false +width=119 + +[2-slope@255,9|19] +grade=4 +gradient=Opposite Diagonal +height=34 +reversed=true +stuckOnGround=false +width=36 + +[2-slope@256,-12|20] +grade=4 +gradient=Horizontal +height=25 +reversed=false +stuckOnGround=false +width=30 + +[2-slope@290,10|18] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=111 + +[2-wall@0,0|10] +endPoint=298,169 +startPoint=278,188 + +[2-wall@0,0|11] +endPoint=181,200 +startPoint=191,188 + +[2-wall@0,0|15] +endPoint=179,178 +startPoint=191,186 + +[2-wall@0,0|18] +endPoint=278,20 +startPoint=115,20 + +[2-wall@0,0|5] +endPoint=180,270 +startPoint=3,272 + +[2-wall@0,0|6] +endPoint=179,127 +startPoint=180,270 + +[2-wall@0,0|7] +endPoint=191,188 +startPoint=278,188 + +[2-wall@0,0|8] +endPoint=298,169 +startPoint=298,103 + +[3-ball@55,341] +dummykey=true + +[3-blackhole@85,70|9] +exit=166,79 +exitDeg=269 +maxspeed=4 +minspeed=1 + +[3-cup@252,186|3] +dummykey=true + +[3-floater@87,154|13] +botWallVisible=true +endPoint=87,154 +height=14 +leftWallVisible=true +rightWallVisible=true +speed=8 +startPoint=29,78 +topWallVisible=true +width=15 + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[3-sand@42,34|11] +changeEnabled=false +changeEvery=50 +height=50 +width=40 + +[3-sand@50,28|14] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[3-sand@59,20|12] +changeEnabled=false +changeEvery=50 +height=28 +width=64 + +[3-sand@79,26|0] +changeEnabled=false +changeEvery=50 +height=28 +width=26 + +[3-slope@0,166|10] +grade=3 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=134 + +[3-slope@136,74|4] +grade=4 +gradient=Horizontal +height=234 +reversed=true +stuckOnGround=true +width=205 + +[3-wall@0,0|6] +endPoint=338,308 +startPoint=132,307 + +[3-wall@0,0|7] +endPoint=132,307 +startPoint=136,75 + +[3-wall@0,0|8] +endPoint=343,77 +startPoint=338,308 + +[3-wall@0,0|9] +endPoint=343,77 +startPoint=136,75 + +[4-ball@125,336] +dummykey=true + +[4-cup@321,206|3] +dummykey=true + +[4-floater@286,190|4] +botWallVisible=true +endPoint=286,190 +height=40 +leftWallVisible=true +rightWallVisible=true +speed=19 +startPoint=78,89 +topWallVisible=false +width=80 + +[4-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=true +maxstrokes=10 +par=2 + +[4-puddle@-22,215|6] +changeEnabled=false +changeEvery=50 +height=228 +width=204 + +[4-slope@-1,280|9] +grade=4 +gradient=Diagonal +height=89 +reversed=true +stuckOnGround=false +width=102 + +[4-slope@-4,77|7] +grade=4 +gradient=Opposite Diagonal +height=74 +reversed=false +stuckOnGround=false +width=102 + +[4-slope@-81,6|5] +grade=4 +gradient=Opposite Diagonal +height=295 +reversed=true +stuckOnGround=true +width=482 + +[4-slope@37,150|8] +grade=4 +gradient=Horizontal +height=131 +reversed=true +stuckOnGround=false +width=60 + +[5-ball@106,356] +dummykey=true + +[5-blackhole@167,37|21] +exit=212,97 +exitDeg=339 +maxspeed=2 +minspeed=1 + +[5-bridge@156,148|17] +botWallVisible=true +height=23 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=46 + +[5-cup@237,229|3] +dummykey=true + +[5-cup@249,34|33] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-puddle@213,14|39] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[5-puddle@218,15|15] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[5-puddle@223,13|13] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[5-puddle@248,1|37] +changeEnabled=false +changeEvery=50 +height=42 +width=76 + +[5-puddle@262,57|38] +changeEnabled=false +changeEvery=50 +height=22 +width=48 + +[5-puddle@271,59|30] +changeEnabled=false +changeEvery=50 +height=22 +width=38 + +[5-puddle@279,22|41] +changeEnabled=true +changeEvery=28 +height=50 +width=26 + +[5-sand@-20,411|36] +changeEnabled=false +changeEvery=50 +height=172 +width=274 + +[5-sand@-28,349|37] +changeEnabled=false +changeEvery=50 +height=312 +width=146 + +[5-sand@23,345|42] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[5-sand@40,354|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[5-slope@156,152|19] +grade=4 +gradient=Horizontal +height=15 +reversed=true +stuckOnGround=false +width=48 + +[5-slope@203,195|34] +grade=2 +gradient=Vertical +height=23 +reversed=true +stuckOnGround=true +width=70 + +[5-slope@204,239|35] +grade=2 +gradient=Vertical +height=24 +reversed=false +stuckOnGround=true +width=68 + +[5-slope@284,-2|32] +grade=3 +gradient=Opposite Diagonal +height=140 +reversed=true +stuckOnGround=false +width=114 + +[5-slope@294,186|27] +grade=2 +gradient=Vertical +height=74 +reversed=true +stuckOnGround=true +width=36 + +[5-slope@295,301|31] +grade=8 +gradient=Diagonal +height=99 +reversed=false +stuckOnGround=false +width=106 + +[5-slope@56,225|19] +grade=4 +gradient=Vertical +height=58 +reversed=true +stuckOnGround=true +width=99 + +[5-slope@57,39|16] +grade=6 +gradient=Vertical +height=33 +reversed=false +stuckOnGround=true +width=97 + +[5-slope@58,112|17] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=true +width=99 + +[5-slope@59,152|18] +grade=2 +gradient=Horizontal +height=10 +reversed=true +stuckOnGround=false +width=95 + +[5-slope@59,163|18] +grade=5 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=true +width=95 + +[5-slope@61,23|20] +grade=2 +gradient=Horizontal +height=16 +reversed=true +stuckOnGround=true +width=118 + +[5-wall@0,0|10] +endPoint=330,96 +startPoint=330,362 + +[5-wall@0,0|11] +endPoint=330,362 +startPoint=201,316 + +[5-wall@0,0|12] +endPoint=201,316 +startPoint=202,170 + +[5-wall@0,0|14] +endPoint=156,170 +startPoint=154,318 + +[5-wall@0,0|22] +endPoint=293,175 +startPoint=202,148 + +[5-wall@0,0|23] +endPoint=293,175 +startPoint=294,286 + +[5-wall@0,0|24] +endPoint=294,286 +startPoint=264,301 + +[5-wall@0,0|25] +endPoint=270,263 +startPoint=270,188 + +[5-wall@0,0|26] +endPoint=270,188 +startPoint=202,170 + +[5-wall@0,0|28] +endPoint=157,54 +startPoint=185,53 + +[5-wall@0,0|29] +endPoint=185,53 +startPoint=182,21 + +[5-wall@0,0|4] +endPoint=57,21 +startPoint=57,321 + +[5-wall@0,0|5] +endPoint=57,21 +startPoint=182,21 + +[5-wall@0,0|6] +endPoint=157,54 +startPoint=156,148 + +[5-wall@0,0|8] +endPoint=202,148 +startPoint=200,56 + +[5-wall@0,0|9] +endPoint=200,56 +startPoint=330,96 + +[5-windmill@332,149|40] +botWallVisible=false +bottom=false +height=71 +leftWallVisible=false +rightWallVisible=false +speed=9 +topWallVisible=false +width=75 + +[6-ball@82,356] +dummykey=true + +[6-cup@258,84|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-puddle@200,8|6] +changeEnabled=false +changeEvery=50 +height=166 +width=88 + +[6-puddle@221,61|25] +changeEnabled=false +changeEvery=50 +height=24 +width=28 + +[6-puddle@229,53|2] +changeEnabled=false +changeEvery=50 +height=28 +width=30 + +[6-puddle@240,58|24] +changeEnabled=false +changeEvery=50 +height=14 +width=46 + +[6-puddle@256,23|8] +changeEnabled=false +changeEvery=50 +height=80 +width=150 + +[6-puddle@274,57|1] +changeEnabled=false +changeEvery=50 +height=16 +width=50 + +[6-puddle@283,55|0] +changeEnabled=false +changeEvery=50 +height=26 +width=32 + +[6-puddle@307,10|7] +changeEnabled=false +changeEvery=50 +height=166 +width=74 + +[6-sand@102,98|26] +changeEnabled=false +changeEvery=50 +height=46 +width=38 + +[6-sand@331,215|9] +changeEnabled=false +changeEvery=50 +height=62 +width=74 + +[6-sand@333,148|11] +changeEnabled=false +changeEvery=50 +height=36 +width=46 + +[6-sand@333,158|34] +changeEnabled=false +changeEvery=50 +height=20 +width=22 + +[6-sand@335,184|32] +changeEnabled=false +changeEvery=50 +height=24 +width=26 + +[6-sand@338,141|27] +changeEnabled=false +changeEvery=50 +height=38 +width=38 + +[6-sand@342,207|28] +changeEnabled=false +changeEvery=50 +height=66 +width=78 + +[6-sand@343,133|10] +changeEnabled=false +changeEvery=50 +height=54 +width=38 + +[6-sand@344,161|33] +changeEnabled=false +changeEvery=50 +height=32 +width=34 + +[6-sand@348,198|29] +changeEnabled=false +changeEvery=50 +height=54 +width=66 + +[6-sand@350,148|35] +changeEnabled=false +changeEvery=50 +height=28 +width=18 + +[6-sand@350,168|36] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[6-sand@350,179|31] +changeEnabled=false +changeEvery=50 +height=42 +width=46 + +[6-sand@361,188|30] +changeEnabled=false +changeEvery=50 +height=52 +width=42 + +[6-sand@59,94|23] +changeEnabled=false +changeEvery=50 +height=94 +width=64 + +[6-sand@73,100|22] +changeEnabled=false +changeEvery=50 +height=94 +width=90 + +[6-sand@77,84|21] +changeEnabled=false +changeEvery=50 +height=84 +width=102 + +[6-slope@-1,115|4] +grade=3 +gradient=Vertical +height=74 +reversed=false +stuckOnGround=false +width=404 + +[6-slope@-2,215|5] +grade=3 +gradient=Vertical +height=81 +reversed=true +stuckOnGround=false +width=407 + +[6-slope@140,61|16] +grade=4 +gradient=Opposite Diagonal +height=43 +reversed=true +stuckOnGround=false +width=43 + +[6-slope@142,-1|17] +grade=4 +gradient=Horizontal +height=64 +reversed=false +stuckOnGround=false +width=36 + +[6-slope@182,79|14] +grade=4 +gradient=Vertical +height=26 +reversed=true +stuckOnGround=false +width=32 + +[6-slope@208,61|15] +grade=4 +gradient=Diagonal +height=46 +reversed=true +stuckOnGround=false +width=49 + +[6-slope@242,48|12] +grade=4 +gradient=Vertical +height=27 +reversed=true +stuckOnGround=false +width=29 + +[6-slope@270,64|13] +grade=4 +gradient=Opposite Diagonal +height=44 +reversed=true +stuckOnGround=false +width=30 + +[6-slope@299,59|18] +grade=4 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=25 + +[6-slope@322,57|19] +grade=4 +gradient=Diagonal +height=50 +reversed=true +stuckOnGround=false +width=37 + +[6-slope@324,-2|20] +grade=4 +gradient=Horizontal +height=59 +reversed=true +stuckOnGround=false +width=35 + +[7-ball@53,349] +dummykey=true + +[7-blackhole@111,125|20] +exit=31,3 +exitDeg=270 +maxspeed=5 +minspeed=3 + +[7-blackhole@146,160|19] +exit=197,312 +exitDeg=90 +maxspeed=3 +minspeed=2 + +[7-blackhole@99,169|21] +exit=3,359 +exitDeg=0 +maxspeed=4 +minspeed=2 + +[7-cup@118,151|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-sand@306,70|11] +changeEnabled=false +changeEvery=50 +height=30 +width=36 + +[7-sand@307,45|10] +changeEnabled=false +changeEvery=50 +height=72 +width=78 + +[7-sand@322,78|9] +changeEnabled=false +changeEvery=50 +height=102 +width=50 + +[7-sand@330,103|8] +changeEnabled=false +changeEvery=50 +height=70 +width=74 + +[7-sand@331,56|13] +changeEnabled=false +changeEvery=50 +height=26 +width=28 + +[7-sand@339,84|12] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[7-sand@40,238|16] +changeEnabled=false +changeEvery=50 +height=70 +width=56 + +[7-sand@49,222|18] +changeEnabled=false +changeEvery=50 +height=26 +width=38 + +[7-sand@53,257|19] +changeEnabled=false +changeEvery=50 +height=46 +width=64 + +[7-sand@56,243|15] +changeEnabled=false +changeEvery=50 +height=60 +width=54 + +[7-sand@62,239|17] +changeEnabled=false +changeEvery=50 +height=42 +width=52 + +[7-sand@69,254|14] +changeEnabled=false +changeEvery=50 +height=62 +width=70 + +[7-slope@159,316|23] +grade=4 +gradient=Horizontal +height=85 +reversed=true +stuckOnGround=true +width=46 + +[7-slope@242,228|7] +grade=4 +gradient=Diagonal +height=173 +reversed=false +stuckOnGround=true +width=158 + +[7-slope@43,12|18] +grade=2 +gradient=Elliptic +height=214 +reversed=false +stuckOnGround=true +width=214 + +[7-slope@86,317|22] +grade=4 +gradient=Horizontal +height=84 +reversed=false +stuckOnGround=true +width=48 + +[7-wall@0,0|6] +endPoint=248,316 +startPoint=-3,316 + +[8-ball@272,147] +dummykey=true + +[8-blackhole@296,38|9] +exit=254,371 +exitDeg=139 +maxspeed=5 +minspeed=3 + +[8-cup@296,64|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[8-puddle@345,350|12] +changeEnabled=false +changeEvery=50 +height=32 +width=52 + +[8-puddle@354,401|11] +changeEnabled=false +changeEvery=50 +height=124 +width=168 + +[8-puddle@400,8|30] +changeEnabled=false +changeEvery=50 +height=40 +width=34 + +[8-puddle@402,-8|30] +changeEnabled=false +changeEvery=50 +height=50 +width=56 + +[8-puddle@405,106|30] +changeEnabled=false +changeEvery=50 +height=78 +width=32 + +[8-puddle@405,135|30] +changeEnabled=false +changeEvery=50 +height=48 +width=20 + +[8-puddle@405,86|30] +changeEnabled=false +changeEvery=50 +height=64 +width=36 + +[8-puddle@411,49|30] +changeEnabled=false +changeEvery=50 +height=122 +width=62 + +[8-puddle@423,170|30] +changeEnabled=false +changeEvery=50 +height=154 +width=58 + +[8-puddle@447,406|10] +changeEnabled=false +changeEvery=50 +height=318 +width=268 + +[8-sand@144,184|15] +changeEnabled=false +changeEvery=50 +height=42 +width=32 + +[8-sand@145,176|14] +changeEnabled=false +changeEvery=50 +height=36 +width=30 + +[8-sand@75,139|35] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[8-sand@77,135|13] +changeEnabled=false +changeEvery=50 +height=28 +width=38 + +[8-sand@81,138|28] +changeEnabled=false +changeEvery=50 +height=32 +width=30 + +[8-slope@188,16|7] +grade=2 +gradient=Horizontal +height=73 +reversed=true +stuckOnGround=false +width=81 + +[8-slope@214,112|23] +grade=4 +gradient=Elliptic +height=171 +reversed=false +stuckOnGround=false +width=171 + +[8-slope@253,11|22] +grade=3 +gradient=Elliptic +height=80 +reversed=false +stuckOnGround=false +width=80 + +[8-slope@317,14|8] +grade=3 +gradient=Horizontal +height=76 +reversed=true +stuckOnGround=false +width=53 + +[8-slope@41,253|41] +grade=4 +gradient=Opposite Diagonal +height=132 +reversed=false +stuckOnGround=false +width=145 + +[8-slope@45,89|34] +grade=3 +gradient=Vertical +height=142 +reversed=true +stuckOnGround=false +width=132 + +[8-wall@0,0|10] +endPoint=379,154 +startPoint=364,135 + +[8-wall@0,0|11] +endPoint=364,135 +startPoint=346,123 + +[8-wall@0,0|12] +endPoint=346,123 +startPoint=324,111 + +[8-wall@0,0|13] +endPoint=324,111 +startPoint=292,109 + +[8-wall@0,0|14] +endPoint=292,109 +startPoint=262,116 + +[8-wall@0,0|15] +endPoint=262,116 +startPoint=243,128 + +[8-wall@0,0|16] +endPoint=243,128 +startPoint=225,144 + +[8-wall@0,0|17] +endPoint=225,144 +startPoint=218,158 + +[8-wall@0,0|18] +endPoint=218,158 +startPoint=212,180 + +[8-wall@0,0|19] +endPoint=212,180 +startPoint=211,202 + +[8-wall@0,0|2] +endPoint=41,388 +startPoint=246,386 + +[8-wall@0,0|20] +endPoint=211,202 +startPoint=218,229 + +[8-wall@0,0|21] +endPoint=218,229 +startPoint=231,250 + +[8-wall@0,0|22] +endPoint=231,250 +startPoint=246,264 + +[8-wall@0,0|24] +endPoint=384,218 +startPoint=378,238 + +[8-wall@0,0|25] +endPoint=378,238 +startPoint=368,256 + +[8-wall@0,0|26] +endPoint=368,256 +startPoint=351,268 + +[8-wall@0,0|27] +endPoint=256,274 +startPoint=246,264 + +[8-wall@0,0|29] +endPoint=369,90 +startPoint=177,89 + +[8-wall@0,0|3] +endPoint=247,289 +startPoint=224,294 + +[8-wall@0,0|31] +endPoint=369,14 +startPoint=41,14 + +[8-wall@0,0|32] +endPoint=41,14 +startPoint=41,388 + +[8-wall@0,0|33] +endPoint=246,386 +startPoint=351,268 + +[8-wall@0,0|34] +endPoint=175,256 +startPoint=177,89 + +[8-wall@0,0|4] +endPoint=224,294 +startPoint=203,287 + +[8-wall@0,0|45] +endPoint=247,289 +startPoint=256,274 + +[8-wall@0,0|5] +endPoint=203,287 +startPoint=186,275 + +[8-wall@0,0|6] +endPoint=186,275 +startPoint=175,256 + +[8-wall@0,0|7] +endPoint=384,218 +startPoint=387,196 + +[8-wall@0,0|8] +endPoint=387,196 +startPoint=384,176 + +[8-wall@0,0|9] +endPoint=384,176 +startPoint=379,154 + +[9-ball@25,24] +dummykey=true + +[9-blackhole@118,60|0] +exit=269,90 +exitDeg=270 +maxspeed=5 +minspeed=5 + +[9-blackhole@149,72|7] +exit=364,381 +exitDeg=120 +maxspeed=8 +minspeed=6 + +[9-blackhole@273,274|18] +exit=116,237 +exitDeg=20 +maxspeed=5 +minspeed=3 + +[9-blackhole@311,345|4] +exit=196,54 +exitDeg=169 +maxspeed=3 +minspeed=3 + +[9-blackhole@68,112|12] +exit=373,150 +exitDeg=269 +maxspeed=5 +minspeed=4 + +[9-blackhole@81,56|8] +exit=385,373 +exitDeg=160 +maxspeed=8 +minspeed=6 + +[9-bridge@125,329|0] +botWallVisible=true +height=38 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=195 + +[9-bridge@191,260|9] +botWallVisible=false +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=32 + +[9-bridge@319,329|0] +botWallVisible=false +height=36 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=30 + +[9-bridge@323,138|0] +botWallVisible=false +height=192 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=27 + +[9-bumper@129,201|8] +dummykey=true + +[9-bumper@152,151|4] +dummykey=true + +[9-bumper@374,137|14] +dummykey=true + +[9-cup@146,175|17] +dummykey=true + +[9-cup@156,193|18] +dummykey=true + +[9-cup@168,176|3] +dummykey=true + +[9-cup@207,272|9] +dummykey=true + +[9-floater@54,43|21] +botWallVisible=false +endPoint=54,43 +height=1 +leftWallVisible=false +rightWallVisible=false +speed=4 +startPoint=42,69 +topWallVisible=true +width=19 + +[9-floater@80,296|9] +botWallVisible=false +endPoint=80,296 +height=28 +leftWallVisible=false +rightWallVisible=true +speed=2 +startPoint=191,299 +topWallVisible=false +width=30 + +[9-floater@82,99|0] +botWallVisible=false +endPoint=82,99 +height=1 +leftWallVisible=false +rightWallVisible=false +speed=9 +startPoint=49,98 +topWallVisible=true +width=10 + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[9-puddle@179,312|33] +changeEnabled=false +changeEvery=50 +height=70 +width=22 + +[9-puddle@193,296|32] +changeEnabled=false +changeEvery=50 +height=82 +width=60 + +[9-puddle@217,292|0] +changeEnabled=false +changeEvery=50 +height=94 +width=114 + +[9-puddle@256,317|34] +changeEnabled=false +changeEvery=50 +height=78 +width=38 + +[9-puddle@257,325|35] +changeEnabled=false +changeEvery=50 +height=42 +width=42 + +[9-puddle@265,297|37] +changeEnabled=false +changeEvery=50 +height=58 +width=22 + +[9-puddle@266,333|36] +changeEnabled=false +changeEvery=50 +height=28 +width=32 + +[9-puddle@65,133|30] +changeEnabled=false +changeEvery=50 +height=30 +width=22 + +[9-puddle@73,140|29] +changeEnabled=false +changeEvery=50 +height=36 +width=32 + +[9-puddle@75,129|13] +changeEnabled=false +changeEvery=50 +height=30 +width=48 + +[9-puddle@79,142|31] +changeEnabled=false +changeEvery=50 +height=22 +width=26 + +[9-puddle@84,133|28] +changeEnabled=false +changeEvery=50 +height=32 +width=32 + +[9-sand@100,369|27] +changeEnabled=false +changeEvery=50 +height=26 +width=52 + +[9-sand@108,343|24] +changeEnabled=false +changeEvery=50 +height=60 +width=96 + +[9-sand@130,350|7] +changeEnabled=false +changeEvery=50 +height=62 +width=90 + +[9-sand@83,363|26] +changeEnabled=false +changeEvery=50 +height=56 +width=62 + +[9-sand@91,353|25] +changeEnabled=false +changeEvery=50 +height=66 +width=66 + +[9-sign@146,329|23] +Comment=Chaos +Comment[ar]=Ùوضى +Comment[bg]=Ð¥Ð°Ð¾Ñ +Comment[bn]=গোলমাল +Comment[bs]=Haos +Comment[ca]=Caos +Comment[cy]=Anhrefn +Comment[da]=Kaos +Comment[el]=Χάος +Comment[es]=Caos +Comment[et]=Kaos +Comment[fi]=Kaaos +Comment[gl]=Caos +Comment[he]=תוהו ובוהו +Comment[hi]=अवà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾ +Comment[hr]=Kaos +Comment[hu]=Káosz +Comment[is]=Kaos +Comment[it]=Caos +Comment[ja]=カオス +Comment[mk]=Ð¥Ð°Ð¾Ñ +Comment[nb]=Kaos +Comment[nn]=Kaos +Comment[pt]=Caos +Comment[pt_BR]=Caos +Comment[ro]=Haos +Comment[ru]=Ð¥Ð°Ð¾Ñ +Comment[sl]=Kaos +Comment[sr]=Ð¥Ð°Ð¾Ñ +Comment[sr@Latn]=Haos +Comment[sv]=Kaos +Comment[ta]= கà¯à®´à®ªà¯à®ªà®®à¯ +Comment[tg]=БеÑаруÑомонӣ +Comment[tr]=KarmaÅŸa +Comment[uk]=Ð¥Ð°Ð¾Ñ +Comment[ven]=Thaidzo +Comment[xh]=Ingxwaba ngxwaba +Comment[xx]=xxChaosxx +Comment[zh_CN]=混沌 +Comment[zh_TW]=混沌 +Comment[zu]=Inyakanyaka +botWallVisible=true +height=38 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=151 + +[9-slope@11,355|22] +grade=4 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=35 + +[9-slope@12,325|16] +grade=4 +gradient=Opposite Diagonal +height=31 +reversed=false +stuckOnGround=false +width=33 + +[9-slope@127,48|12] +grade=4 +gradient=Elliptic +height=45 +reversed=true +stuckOnGround=false +width=45 + +[9-slope@320,43|3] +grade=7 +gradient=Horizontal +height=41 +reversed=false +stuckOnGround=false +width=32 + +[9-slope@321,83|8] +grade=4 +gradient=Horizontal +height=53 +reversed=false +stuckOnGround=false +width=30 + +[9-slope@326,9|17] +grade=7 +gradient=Opposite Diagonal +height=72 +reversed=true +stuckOnGround=false +width=62 + +[9-slope@50,94|15] +grade=5 +gradient=Elliptic +height=35 +reversed=true +stuckOnGround=false +width=35 + +[9-slope@93,119|9] +grade=8 +gradient=Elliptic +height=125 +reversed=true +stuckOnGround=true +width=125 + +[9-wall@0,0|0] +endPoint=350,330 +startPoint=396,361 + +[9-wall@0,0|1] +endPoint=350,394 +startPoint=320,356 + +[9-wall@0,0|10] +endPoint=225,151 +startPoint=205,239 + +[9-wall@0,0|11] +endPoint=225,151 +startPoint=147,108 + +[9-wall@0,0|12] +endPoint=147,108 +startPoint=77,170 + +[9-wall@0,0|13] +endPoint=77,170 +startPoint=118,254 + +[9-wall@0,0|16] +endPoint=205,239 +startPoint=118,254 + +[9-wall@0,0|18] +endPoint=122,83 +startPoint=82,99 + +[9-wall@0,0|19] +endPoint=352,43 +startPoint=73,43 + +[9-wall@0,0|20] +endPoint=49,98 +startPoint=46,352 + +[9-wall@0,0|3] +endPoint=392,85 +startPoint=177,85 + +[9-wall@0,0|38] +endPoint=177,85 +startPoint=158,93 + +[9-wall@0,0|39] +endPoint=158,93 +startPoint=136,93 + +[9-wall@0,0|40] +endPoint=136,93 +startPoint=122,83 + +[9-windmill@235,171|3] +botWallVisible=true +bottom=false +height=119 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=75 diff --git a/kolf/courses/Makefile.am b/kolf/courses/Makefile.am new file mode 100644 index 00000000..d9953953 --- /dev/null +++ b/kolf/courses/Makefile.am @@ -0,0 +1,4 @@ +coursedir = $(kde_datadir)/kolf/courses +course_DATA = Classic.kolf Hard.kolf Medium.kolf Easy.kolf ReallyEasy Practice Impossible USApro + +EXTRA_DIST=$(course_DATA) diff --git a/kolf/courses/Medium.kolf b/kolf/courses/Medium.kolf new file mode 100644 index 00000000..b232a4d7 --- /dev/null +++ b/kolf/courses/Medium.kolf @@ -0,0 +1,2557 @@ +[0-course@-50,-50] +Name=Medium Course +Name[af]=Medium Natuurlik +Name[bg]=Средно +Name[bn]=মধà§à¦¯à¦® কোরà§à¦¸ +Name[bs]=Srednji teren +Name[ca]=Camp medi +Name[cs]=StÅ™ední kurz +Name[da]=Middelsvær bane +Name[de]=Mittel +Name[el]=ΜέτÏια πίστα +Name[es]=Campo medio +Name[et]=Keskmiselt raske väljak +Name[fi]=Keskivaikea kenttä +Name[fr]=Parcours moyen +Name[gl]=Campo medio +Name[he]=מסלול בינוני +Name[hi]=साधारण कोरà¥à¤¸ +Name[hu]=Közepes nehézségű pálya +Name[is]=Meðal erfið leið +Name[it]=Percorso medio +Name[ja]=普通ã®ã‚³ãƒ¼ã‚¹ +Name[mk]=Среден терен +Name[nb]=Middels vanskelig bane +Name[nl]=Gemiddeld parcours +Name[nn]=Middels vanskeleg bane +Name[nso]=Course ya Magareng +Name[pl]=Åšredni tor +Name[pt]=Percurso Médio +Name[pt_BR]=Curso Médio +Name[ro]=Cale medie +Name[ru]=СреднÑÑ Ð¿Ð»Ð¾Ñ‰Ð°Ð´ÐºÐ° +Name[sk]=Stredne Å¥ažké ihrisko +Name[sl]=Srednje igriÅ¡Äe +Name[sr]=Средњи терен +Name[sr@Latn]=Srednji teren +Name[sv]=Normal bana +Name[ta]=நடà¯à®¤à¯à®¤à®°à®®à®¾à®© மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=Майдони Миёна +Name[tr]=Orta Seviye Pist +Name[uk]=Середній майданчик +Name[ven]= Thero yo Linganelaho +Name[xh]=Indlela ephakathi +Name[xx]=xxMedium Coursexx +Name[zh_CN]=中级路线 +Name[zh_TW]=中等路線 +Name[zu]=Indlela ephakathi +author=Jason Katz-Brown +name=Medium Course + +[1-ball@342,322] +dummykey=true + +[1-cup@325,52|14] +dummykey=true + +[1-floater@100,208|24] +botWallVisible=true +endPoint=100,208 +height=10 +leftWallVisible=true +rightWallVisible=true +speed=2 +startPoint=16,189 +topWallVisible=true +width=19 + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=3 + +[1-puddle@282,195|9] +changeEnabled=false +changeEvery=50 +height=38 +width=66 + +[1-puddle@350,189|8] +changeEnabled=false +changeEvery=50 +height=40 +width=192 + +[1-puddle@378,206|11] +changeEnabled=false +changeEvery=50 +height=32 +width=232 + +[1-slope@177,86|16] +grade=4 +gradient=Diagonal +height=115 +reversed=false +stuckOnGround=false +width=54 + +[1-slope@193,240|22] +grade=4 +gradient=Diagonal +height=148 +reversed=false +stuckOnGround=false +width=194 + +[1-slope@231,90|15] +grade=4 +gradient=Vertical +height=108 +reversed=false +stuckOnGround=false +width=156 + +[1-slope@291,17|19] +grade=2 +gradient=Elliptic +height=67 +reversed=true +stuckOnGround=false +width=67 + +[1-wall@0,0|13] +endPoint=208,200 +startPoint=180,199 + +[1-wall@0,0|17] +endPoint=180,199 +startPoint=148,213 + +[1-wall@0,0|21] +endPoint=389,236 +startPoint=307,234 + +[1-wall@0,0|22] +endPoint=307,234 +startPoint=255,218 + +[1-wall@0,0|23] +endPoint=255,218 +startPoint=229,215 + +[1-wall@0,0|24] +endPoint=208,200 +startPoint=229,215 + +[1-wall@0,0|3] +endPoint=103,388 +startPoint=11,304 + +[1-wall@0,0|4] +endPoint=126,11 +startPoint=10,128 + +[10-ball@343,254] +dummykey=true + +[10-bumper@114,160|14] +dummykey=true + +[10-bumper@151,79|11] +dummykey=true + +[10-bumper@166,233|9] +dummykey=true + +[10-bumper@179,304|15] +dummykey=true + +[10-bumper@258,99|20] +dummykey=true + +[10-bumper@356,86|19] +dummykey=true + +[10-bumper@43,206|13] +dummykey=true + +[10-bumper@54,90|10] +dummykey=true + +[10-bumper@67,284|12] +dummykey=true + +[10-cup@310,114|16] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[10-slope@11,11|7] +grade=4 +gradient=Vertical +height=47 +reversed=true +stuckOnGround=false +width=379 + +[10-slope@11,121|3] +grade=4 +gradient=Vertical +height=217 +reversed=false +stuckOnGround=false +width=213 + +[10-slope@221,118|6] +grade=2 +gradient=Opposite Diagonal +height=63 +reversed=false +stuckOnGround=false +width=63 + +[10-wall@0,0|17] +endPoint=390,148 +startPoint=354,183 + +[10-wall@0,0|18] +endPoint=355,182 +startPoint=388,251 + +[10-wall@0,0|4] +endPoint=223,335 +startPoint=223,182 + +[10-wall@0,0|5] +endPoint=354,183 +startPoint=223,182 + +[10-wall@0,0|8] +endPoint=388,251 +startPoint=309,389 + +[11-ball@36,295] +dummykey=true + +[11-cup@74,372|72] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[11-sand@270,47|71] +changeEnabled=false +changeEvery=50 +height=40 +width=50 + +[11-sand@279,52|70] +changeEnabled=false +changeEvery=50 +height=46 +width=42 + +[11-slope@12,103|73] +grade=6 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@121,106|62] +grade=1 +gradient=Elliptic +height=222 +reversed=true +stuckOnGround=false +width=222 + +[11-slope@31,91|73] +grade=6 +gradient=Opposite Diagonal +height=41 +reversed=false +stuckOnGround=false +width=35 + +[11-slope@47,225|59] +grade=6 +gradient=Vertical +height=24 +reversed=false +stuckOnGround=false +width=37 + +[11-slope@50,88|61] +grade=6 +gradient=Horizontal +height=42 +reversed=true +stuckOnGround=false +width=36 + +[11-slope@55,352|74] +grade=4 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@8,153|60] +grade=6 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=48 + +[11-wall@0,0|10] +endPoint=168,354 +startPoint=132,383 + +[11-wall@0,0|11] +endPoint=193,351 +startPoint=168,354 + +[11-wall@0,0|12] +endPoint=239,335 +startPoint=193,351 + +[11-wall@0,0|13] +endPoint=276,340 +startPoint=239,335 + +[11-wall@0,0|14] +endPoint=311,328 +startPoint=276,340 + +[11-wall@0,0|15] +endPoint=335,299 +startPoint=311,328 + +[11-wall@0,0|16] +endPoint=345,270 +startPoint=335,299 + +[11-wall@0,0|17] +endPoint=358,234 +startPoint=345,270 + +[11-wall@0,0|18] +endPoint=374,200 +startPoint=358,234 + +[11-wall@0,0|19] +endPoint=374,200 +startPoint=370,166 + +[11-wall@0,0|20] +endPoint=370,166 +startPoint=358,136 + +[11-wall@0,0|21] +endPoint=358,136 +startPoint=343,109 + +[11-wall@0,0|22] +endPoint=343,109 +startPoint=340,67 + +[11-wall@0,0|23] +endPoint=166,92 +startPoint=168,109 + +[11-wall@0,0|24] +endPoint=166,92 +startPoint=153,82 + +[11-wall@0,0|25] +endPoint=153,82 +startPoint=112,94 + +[11-wall@0,0|26] +endPoint=112,94 +startPoint=80,119 + +[11-wall@0,0|27] +endPoint=80,119 +startPoint=55,128 + +[11-wall@0,0|28] +endPoint=55,128 +startPoint=44,147 + +[11-wall@0,0|29] +endPoint=55,180 +startPoint=44,147 + +[11-wall@0,0|3] +endPoint=159,119 +startPoint=139,125 + +[11-wall@0,0|30] +endPoint=74,196 +startPoint=55,180 + +[11-wall@0,0|32] +endPoint=74,196 +startPoint=86,265 + +[11-wall@0,0|33] +endPoint=86,265 +startPoint=80,290 + +[11-wall@0,0|34] +endPoint=80,290 +startPoint=58,312 + +[11-wall@0,0|35] +endPoint=58,312 +startPoint=31,324 + +[11-wall@0,0|36] +endPoint=168,109 +startPoint=159,119 + +[11-wall@0,0|37] +endPoint=16,318 +startPoint=31,324 + +[11-wall@0,0|38] +endPoint=7,298 +startPoint=16,318 + +[11-wall@0,0|39] +endPoint=17,280 +startPoint=7,298 + +[11-wall@0,0|4] +endPoint=96,273 +startPoint=99,233 + +[11-wall@0,0|40] +endPoint=41,270 +startPoint=17,280 + +[11-wall@0,0|41] +endPoint=51,250 +startPoint=41,270 + +[11-wall@0,0|42] +endPoint=46,234 +startPoint=51,250 + +[11-wall@0,0|43] +endPoint=38,216 +startPoint=46,234 + +[11-wall@0,0|44] +endPoint=21,193 +startPoint=38,216 + +[11-wall@0,0|45] +endPoint=7,164 +startPoint=21,193 + +[11-wall@0,0|46] +endPoint=9,128 +startPoint=7,164 + +[11-wall@0,0|47] +endPoint=26,105 +startPoint=9,128 + +[11-wall@0,0|48] +endPoint=60,89 +startPoint=26,105 + +[11-wall@0,0|49] +endPoint=88,70 +startPoint=60,89 + +[11-wall@0,0|5] +endPoint=112,204 +startPoint=109,160 + +[11-wall@0,0|50] +endPoint=129,47 +startPoint=88,70 + +[11-wall@0,0|51] +endPoint=158,15 +startPoint=129,47 + +[11-wall@0,0|52] +endPoint=177,7 +startPoint=158,15 + +[11-wall@0,0|53] +endPoint=203,7 +startPoint=177,7 + +[11-wall@0,0|54] +endPoint=203,7 +startPoint=244,16 + +[11-wall@0,0|55] +endPoint=279,16 +startPoint=244,16 + +[11-wall@0,0|56] +endPoint=305,19 +startPoint=279,16 + +[11-wall@0,0|57] +endPoint=326,35 +startPoint=305,19 + +[11-wall@0,0|58] +endPoint=326,35 +startPoint=340,67 + +[11-wall@0,0|6] +endPoint=139,125 +startPoint=109,160 + +[11-wall@0,0|63] +endPoint=88,336 +startPoint=64,342 + +[11-wall@0,0|64] +endPoint=23,369 +startPoint=34,357 + +[11-wall@0,0|65] +endPoint=64,342 +startPoint=34,357 + +[11-wall@0,0|66] +endPoint=27,384 +startPoint=23,369 + +[11-wall@0,0|67] +endPoint=55,395 +startPoint=27,384 + +[11-wall@0,0|68] +endPoint=103,391 +startPoint=55,395 + +[11-wall@0,0|69] +endPoint=132,383 +startPoint=103,391 + +[11-wall@0,0|7] +endPoint=112,204 +startPoint=99,233 + +[11-wall@0,0|8] +endPoint=100,309 +startPoint=96,273 + +[11-wall@0,0|9] +endPoint=88,336 +startPoint=100,309 + +[12-ball@47,53] +dummykey=true + +[12-blackhole@375,294|12] +exit=137,36 +exitDeg=30 +maxspeed=5 +minspeed=3 + +[12-bumper@189,94|17] +dummykey=true + +[12-cup@216,137|14] +dummykey=true + +[12-cup@374,244|8] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[12-sand@258,131|15] +changeEnabled=false +changeEvery=50 +height=54 +width=42 + +[12-sand@260,123|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[12-slope@10,166|3] +grade=4 +gradient=Vertical +height=221 +reversed=false +stuckOnGround=false +width=381 + +[12-slope@238,7|13] +grade=4 +gradient=Opposite Diagonal +height=124 +reversed=true +stuckOnGround=false +width=150 + +[12-wall@0,0|4] +endPoint=387,219 +startPoint=290,274 + +[12-wall@0,0|5] +endPoint=389,266 +startPoint=284,324 + +[12-wall@0,0|6] +endPoint=117,12 +startPoint=200,165 + +[12-wall@0,0|7] +endPoint=386,165 +startPoint=200,165 + +[13-ball@332,39] +dummykey=true + +[13-cup@44,365|9] +dummykey=true + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[13-puddle@2,77|18] +changeEnabled=false +changeEvery=50 +height=86 +width=138 + +[13-puddle@33,3|17] +changeEnabled=false +changeEvery=50 +height=134 +width=310 + +[13-puddle@49,53|16] +changeEnabled=false +changeEvery=50 +height=64 +width=98 + +[13-sand@352,347|10] +changeEnabled=false +changeEvery=50 +height=72 +width=104 + +[13-sand@361,324|17] +changeEnabled=false +changeEvery=50 +height=34 +width=56 + +[13-sand@370,408|11] +changeEnabled=false +changeEvery=50 +height=172 +width=290 + +[13-sand@374,321|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[13-sand@389,336|13] +changeEnabled=false +changeEvery=50 +height=98 +width=62 + +[13-slope@11,232|23] +grade=1 +gradient=Opposite Diagonal +height=155 +reversed=false +stuckOnGround=false +width=165 + +[13-slope@128,111|5] +grade=4 +gradient=Elliptic +height=94 +reversed=true +stuckOnGround=false +width=94 + +[13-slope@177,207|6] +grade=3 +gradient=Elliptic +height=113 +reversed=false +stuckOnGround=false +width=113 + +[13-slope@235,79|7] +grade=5 +gradient=Elliptic +height=83 +reversed=false +stuckOnGround=false +width=83 + +[13-slope@282,173|8] +grade=4 +gradient=Elliptic +height=65 +reversed=true +stuckOnGround=false +width=65 + +[13-slope@47,105|4] +grade=3 +gradient=Elliptic +height=70 +reversed=false +stuckOnGround=false +width=70 + +[13-slope@72,198|3] +grade=6 +gradient=Elliptic +height=96 +reversed=false +stuckOnGround=false +width=96 + +[14-ball@154,39] +dummykey=true + +[14-blackhole@236,51|5] +exit=384,316 +exitDeg=180 +maxspeed=4 +minspeed=1 + +[14-cup@55,38|8] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[14-slope@10,160|9] +grade=4 +gradient=Opposite Diagonal +height=227 +reversed=false +stuckOnGround=false +width=243 + +[14-slope@257,12|11] +grade=2 +gradient=Opposite Diagonal +height=91 +reversed=true +stuckOnGround=false +width=130 + +[14-slope@31,67|10] +grade=1 +gradient=Elliptic +height=81 +reversed=false +stuckOnGround=false +width=81 + +[14-wall@0,0|4] +endPoint=387,251 +startPoint=174,251 + +[14-wall@0,0|6] +endPoint=174,251 +startPoint=115,199 + +[14-wall@0,0|7] +endPoint=115,199 +startPoint=115,9 + +[14-windmill@192,12|3] +botWallVisible=false +height=166 +leftWallVisible=true +rightWallVisible=false +speed=8 +topWallVisible=false +width=195 + +[15-ball@358,361] +dummykey=true + +[15-cup@43,43|4] +dummykey=true + +[15-floater@180,204|3] +botWallVisible=false +endPoint=180,204 +height=118 +leftWallVisible=false +rightWallVisible=false +speed=5 +startPoint=40,103 +topWallVisible=false +width=149 + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[15-puddle@176,217|8] +changeEnabled=false +changeEvery=50 +height=134 +width=190 + +[15-puddle@192,185|10] +changeEnabled=false +changeEvery=50 +height=136 +width=168 + +[15-puddle@210,196|9] +changeEnabled=false +changeEvery=50 +height=154 +width=132 + +[15-slope@18,274|5] +grade=3 +gradient=Elliptic +height=101 +reversed=false +stuckOnGround=true +width=101 + +[15-slope@93,16|6] +grade=7 +gradient=Elliptic +height=83 +reversed=false +stuckOnGround=true +width=83 + +[15-wall@0,0|34] +endPoint=255,11 +startPoint=391,102 + +[16-ball@214,366] +dummykey=true + +[16-cup@213,190|41] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[16-puddle@116,184|40] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[16-puddle@117,179|39] +changeEnabled=false +changeEvery=50 +height=36 +width=34 + +[16-puddle@127,161|37] +changeEnabled=false +changeEvery=50 +height=62 +width=48 + +[16-puddle@140,140|33] +changeEnabled=false +changeEvery=50 +height=46 +width=54 + +[16-puddle@158,378|25] +changeEnabled=false +changeEvery=50 +height=100 +width=58 + +[16-puddle@165,328|27] +changeEnabled=false +changeEvery=50 +height=92 +width=58 + +[16-puddle@187,296|23] +changeEnabled=false +changeEvery=50 +height=72 +width=72 + +[16-puddle@196,297|26] +changeEnabled=false +changeEvery=50 +height=60 +width=98 + +[16-puddle@217,270|24] +changeEnabled=false +changeEvery=50 +height=40 +width=88 + +[16-puddle@220,124|32] +changeEnabled=false +changeEvery=50 +height=44 +width=190 + +[16-puddle@237,289|22] +changeEnabled=false +changeEvery=50 +height=46 +width=40 + +[16-puddle@238,269|21] +changeEnabled=false +changeEvery=50 +height=74 +width=70 + +[16-puddle@257,257|28] +changeEnabled=false +changeEvery=50 +height=54 +width=82 + +[16-puddle@262,134|34] +changeEnabled=false +changeEvery=50 +height=26 +width=34 + +[16-puddle@275,253|41] +changeEnabled=false +changeEvery=50 +height=52 +width=84 + +[16-puddle@283,136|35] +changeEnabled=false +changeEvery=50 +height=48 +width=48 + +[16-puddle@291,238|31] +changeEnabled=false +changeEvery=50 +height=56 +width=58 + +[16-puddle@294,156|36] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[16-puddle@309,170|29] +changeEnabled=false +changeEvery=50 +height=104 +width=46 + +[16-puddle@310,202|30] +changeEnabled=false +changeEvery=50 +height=88 +width=42 + +[16-sand@-2,322|58] +changeEnabled=false +changeEvery=50 +height=72 +width=94 + +[16-sand@31,334|41] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[16-sand@41,399|59] +changeEnabled=false +changeEvery=50 +height=150 +width=196 + +[16-slope@11,12|45] +grade=5 +gradient=Diagonal +height=123 +reversed=true +stuckOnGround=false +width=179 + +[16-slope@129,64|50] +grade=4 +gradient=Vertical +height=69 +reversed=false +stuckOnGround=false +width=171 + +[16-slope@174,261|54] +grade=4 +gradient=Vertical +height=48 +reversed=true +stuckOnGround=false +width=137 + +[16-slope@178,153|53] +grade=2 +gradient=Elliptic +height=74 +reversed=true +stuckOnGround=false +width=74 + +[16-slope@278,257|54] +grade=7 +gradient=Diagonal +height=129 +reversed=false +stuckOnGround=false +width=110 + +[16-slope@299,64|51] +grade=4 +gradient=Opposite Diagonal +height=69 +reversed=false +stuckOnGround=false +width=55 + +[16-slope@309,263|53] +grade=4 +gradient=Diagonal +height=42 +reversed=true +stuckOnGround=false +width=48 + +[16-slope@311,133|52] +grade=4 +gradient=Horizontal +height=130 +reversed=true +stuckOnGround=false +width=44 + +[16-slope@87,65|55] +grade=4 +gradient=Diagonal +height=73 +reversed=false +stuckOnGround=false +width=42 + +[16-slope@89,136|38] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[16-wall@0,0|42] +endPoint=177,152 +startPoint=154,181 + +[16-wall@0,0|43] +endPoint=278,188 +startPoint=269,165 + +[16-wall@0,0|44] +endPoint=269,165 +startPoint=244,155 + +[16-wall@0,0|52] +endPoint=200,239 +startPoint=161,256 + +[16-wall@0,0|56] +endPoint=387,77 +startPoint=339,14 + +[17-ball@44,230] +dummykey=true + +[17-cup@307,67|3] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[17-slope@131,218|12] +grade=2 +gradient=Elliptic +height=156 +reversed=true +stuckOnGround=false +width=156 + +[17-slope@32,8|4] +grade=1 +gradient=Opposite Diagonal +height=349 +reversed=true +stuckOnGround=false +width=356 + +[17-slope@58,127|11] +grade=3 +gradient=Elliptic +height=81 +reversed=false +stuckOnGround=false +width=81 + +[17-wall@0,0|10] +endPoint=323,221 +startPoint=260,219 + +[17-wall@0,0|13] +endPoint=296,81 +startPoint=278,12 + +[17-wall@0,0|14] +endPoint=335,85 +startPoint=296,81 + +[17-wall@0,0|5] +endPoint=185,40 +startPoint=190,111 + +[17-wall@0,0|6] +endPoint=236,121 +startPoint=190,111 + +[17-wall@0,0|7] +endPoint=304,135 +startPoint=313,164 + +[17-wall@0,0|8] +endPoint=389,183 +startPoint=313,164 + +[17-wall@0,0|9] +endPoint=260,219 +startPoint=247,173 + +[18-ball@246,341] +dummykey=true + +[18-cup@50,355|3] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[18-slope@-183,-169|5] +grade=2 +gradient=Elliptic +height=400 +reversed=false +stuckOnGround=false +width=400 + +[18-slope@11,297|7] +grade=3 +gradient=Vertical +height=91 +reversed=false +stuckOnGround=false +width=163 + +[18-slope@4,10|8] +grade=2 +gradient=Elliptic +height=125 +reversed=true +stuckOnGround=false +width=125 + +[18-windmill@9,179|6] +botWallVisible=false +bottom=false +height=212 +leftWallVisible=false +rightWallVisible=true +speed=7 +topWallVisible=false +width=166 + +[2-ball@68,357] +dummykey=true + +[2-blackhole@100,268|14] +exit=19,50 +exitDeg=0 +maxspeed=6 +minspeed=3 + +[2-cup@277,370|59] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-puddle@133,167|39] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@144,202|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@145,190|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@163,163|53] +changeEnabled=false +changeEvery=50 +height=40 +width=88 + +[2-puddle@170,203|40] +changeEnabled=false +changeEvery=50 +height=52 +width=62 + +[2-puddle@182,219|56] +changeEnabled=false +changeEvery=50 +height=44 +width=42 + +[2-puddle@184,190|43] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@188,233|36] +changeEnabled=false +changeEvery=50 +height=44 +width=34 + +[2-puddle@190,366|38] +changeEnabled=false +changeEvery=50 +height=60 +width=34 + +[2-puddle@192,387|44] +changeEnabled=false +changeEvery=50 +height=72 +width=54 + +[2-puddle@194,288|45] +changeEnabled=false +changeEvery=50 +height=272 +width=42 + +[2-puddle@195,212|51] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@204,149|54] +changeEnabled=false +changeEvery=50 +height=40 +width=90 + +[2-puddle@205,200|37] +changeEnabled=false +changeEvery=50 +height=32 +width=32 + +[2-puddle@206,391|46] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@207,185|55] +changeEnabled=false +changeEvery=50 +height=34 +width=34 + +[2-puddle@211,154|35] +changeEnabled=false +changeEvery=50 +height=70 +width=92 + +[2-puddle@211,399|47] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[2-puddle@215,180|33] +changeEnabled=false +changeEvery=50 +height=30 +width=36 + +[2-puddle@79,180|31] +changeEnabled=false +changeEvery=50 +height=68 +width=272 + +[2-sand@275,277|65] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[2-sand@363,352|66] +changeEnabled=false +changeEvery=50 +height=64 +width=32 + +[2-sand@366,341|72] +changeEnabled=false +changeEvery=50 +height=44 +width=30 + +[2-slope@113,9|57] +grade=4 +gradient=Horizontal +height=105 +reversed=true +stuckOnGround=false +width=81 + +[2-slope@255,324|74] +grade=4 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=54 + +[2-slope@263,112|72] +grade=6 +gradient=Diagonal +height=22 +reversed=false +stuckOnGround=false +width=123 + +[2-slope@269,134|71] +grade=6 +gradient=Horizontal +height=31 +reversed=false +stuckOnGround=false +width=117 + +[2-slope@269,163|73] +grade=6 +gradient=Opposite Diagonal +height=36 +reversed=true +stuckOnGround=false +width=117 + +[2-slope@308,322|75] +grade=4 +gradient=Opposite Diagonal +height=30 +reversed=false +stuckOnGround=false +width=27 + +[2-slope@308,349|76] +grade=4 +gradient=Horizontal +height=38 +reversed=true +stuckOnGround=false +width=27 + +[2-slope@313,228|77] +grade=4 +gradient=Elliptic +height=74 +reversed=false +stuckOnGround=false +width=74 + +[2-slope@49,235|74] +grade=4 +gradient=Elliptic +height=107 +reversed=false +stuckOnGround=false +width=107 + +[2-wall@0,0|10] +endPoint=131,367 +startPoint=107,390 + +[2-wall@0,0|11] +endPoint=41,390 +startPoint=13,361 + +[2-wall@0,0|12] +endPoint=55,243 +startPoint=36,272 + +[2-wall@0,0|13] +endPoint=13,288 +startPoint=36,272 + +[2-wall@0,0|15] +endPoint=108,115 +startPoint=88,133 + +[2-wall@0,0|16] +endPoint=88,133 +startPoint=56,134 + +[2-wall@0,0|17] +endPoint=56,134 +startPoint=30,121 + +[2-wall@0,0|18] +endPoint=13,76 +startPoint=30,121 + +[2-wall@0,0|19] +endPoint=45,9 +startPoint=13,27 + +[2-wall@0,0|20] +endPoint=189,114 +startPoint=108,115 + +[2-wall@0,0|21] +endPoint=221,109 +startPoint=189,114 + +[2-wall@0,0|22] +endPoint=244,112 +startPoint=221,109 + +[2-wall@0,0|23] +endPoint=273,139 +startPoint=244,112 + +[2-wall@0,0|24] +endPoint=267,168 +startPoint=273,139 + +[2-wall@0,0|25] +endPoint=261,180 +startPoint=238,197 + +[2-wall@0,0|26] +endPoint=226,238 +startPoint=226,282 + +[2-wall@0,0|27] +endPoint=252,328 +startPoint=226,282 + +[2-wall@0,0|28] +endPoint=262,356 +startPoint=252,328 + +[2-wall@0,0|29] +endPoint=262,356 +startPoint=255,377 + +[2-wall@0,0|30] +endPoint=263,387 +startPoint=255,377 + +[2-wall@0,0|4] +endPoint=87,224 +startPoint=55,243 + +[2-wall@0,0|5] +endPoint=124,224 +startPoint=87,224 + +[2-wall@0,0|57] +endPoint=228,9 +startPoint=388,116 + +[2-wall@0,0|58] +endPoint=238,197 +startPoint=226,238 + +[2-wall@0,0|6] +endPoint=165,255 +startPoint=124,224 + +[2-wall@0,0|64] +endPoint=267,168 +startPoint=261,180 + +[2-wall@0,0|67] +endPoint=107,390 +startPoint=41,390 + +[2-wall@0,0|68] +endPoint=13,361 +startPoint=13,288 + +[2-wall@0,0|69] +endPoint=13,27 +startPoint=13,76 + +[2-wall@0,0|7] +endPoint=165,255 +startPoint=165,314 + +[2-wall@0,0|70] +endPoint=228,9 +startPoint=45,9 + +[2-wall@0,0|71] +endPoint=388,116 +startPoint=386,356 + +[2-wall@0,0|72] +endPoint=372,386 +startPoint=263,387 + +[2-wall@0,0|73] +endPoint=386,356 +startPoint=372,386 + +[2-wall@0,0|8] +endPoint=165,314 +startPoint=134,342 + +[2-wall@0,0|9] +endPoint=134,342 +startPoint=131,367 + +[3-ball@59,317] +dummykey=true + +[3-cup@299,103|5] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-puddle@169,234|11] +changeEnabled=false +changeEvery=50 +height=34 +width=10 + +[3-puddle@188,265|6] +changeEnabled=false +changeEvery=50 +height=50 +width=54 + +[3-puddle@196,252|10] +changeEnabled=false +changeEvery=50 +height=72 +width=72 + +[3-puddle@203,227|12] +changeEnabled=false +changeEvery=50 +height=86 +width=78 + +[3-sand@255,57|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-sand@345,154|15] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[3-slope@170,168|4] +grade=7 +gradient=Diagonal +height=221 +reversed=false +stuckOnGround=false +width=219 + +[3-windmill@12,12|13] +botWallVisible=false +bottom=true +height=198 +leftWallVisible=false +rightWallVisible=false +speed=4 +topWallVisible=false +width=180 + +[4-ball@93,349] +dummykey=true + +[4-bumper@262,91|20] +dummykey=true + +[4-cup@338,325|5] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-sand@100,150|11] +changeEnabled=false +changeEvery=50 +height=72 +width=46 + +[4-sand@316,139|18] +changeEnabled=false +changeEvery=50 +height=74 +width=50 + +[4-sand@324,153|14] +changeEnabled=false +changeEvery=50 +height=34 +width=38 + +[4-sand@329,163|14] +changeEnabled=false +changeEvery=50 +height=30 +width=54 + +[4-sand@72,127|10] +changeEnabled=false +changeEvery=50 +height=48 +width=78 + +[4-sand@90,138|12] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@93,135|8] +changeEnabled=false +changeEvery=50 +height=50 +width=46 + +[4-slope@152,20|21] +grade=1 +gradient=Elliptic +height=162 +reversed=true +stuckOnGround=false +width=162 + +[4-slope@278,234|19] +grade=5 +gradient=Elliptic +height=57 +reversed=false +stuckOnGround=false +width=57 + +[4-slope@290,14|20] +grade=2 +gradient=Elliptic +height=105 +reversed=false +stuckOnGround=false +width=105 + +[4-wall@0,0|22] +endPoint=257,355 +startPoint=257,387 + +[4-wall@0,0|3] +endPoint=257,355 +startPoint=221,223 + +[4-wall@0,0|4] +endPoint=10,10 +startPoint=136,109 + +[5-ball@327,224] +dummykey=true + +[5-cup@344,304|42] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@210,199|33] +changeEnabled=false +changeEvery=50 +height=112 +width=110 + +[5-puddle@212,227|35] +changeEnabled=false +changeEvery=50 +height=80 +width=74 + +[5-puddle@224,220|34] +changeEnabled=false +changeEvery=50 +height=148 +width=86 + +[5-puddle@306,368|46] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-puddle@331,361|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-puddle@365,367|45] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[5-sand@188,99|19] +changeEnabled=true +changeEvery=50 +height=40 +width=45 + +[5-slope@21,134|48] +grade=2 +gradient=Vertical +height=84 +reversed=false +stuckOnGround=false +width=207 + +[5-wall@0,0|10] +endPoint=390,257 +startPoint=393,221 + +[5-wall@0,0|11] +endPoint=393,221 +startPoint=368,191 + +[5-wall@0,0|12] +endPoint=368,191 +startPoint=349,113 + +[5-wall@0,0|13] +endPoint=349,113 +startPoint=267,51 + +[5-wall@0,0|14] +endPoint=267,51 +startPoint=232,66 + +[5-wall@0,0|15] +endPoint=232,66 +startPoint=195,52 + +[5-wall@0,0|16] +endPoint=195,52 +startPoint=125,47 + +[5-wall@0,0|17] +endPoint=125,47 +startPoint=36,138 + +[5-wall@0,0|18] +endPoint=19,213 +startPoint=36,138 + +[5-wall@0,0|28] +endPoint=39,240 +startPoint=19,213 + +[5-wall@0,0|3] +endPoint=272,159 +startPoint=223,130 + +[5-wall@0,0|36] +endPoint=44,259 +startPoint=39,240 + +[5-wall@0,0|37] +endPoint=72,267 +startPoint=44,259 + +[5-wall@0,0|38] +endPoint=73,316 +startPoint=72,267 + +[5-wall@0,0|39] +endPoint=132,375 +startPoint=73,316 + +[5-wall@0,0|4] +endPoint=372,267 +startPoint=306,251 + +[5-wall@0,0|40] +endPoint=229,378 +startPoint=132,375 + +[5-wall@0,0|41] +endPoint=259,361 +startPoint=229,378 + +[5-wall@0,0|43] +endPoint=180,313 +startPoint=150,328 + +[5-wall@0,0|5] +endPoint=278,203 +startPoint=272,159 + +[5-wall@0,0|6] +endPoint=223,130 +startPoint=190,135 + +[5-wall@0,0|8] +endPoint=306,251 +startPoint=278,203 + +[5-wall@0,0|9] +endPoint=390,257 +startPoint=372,267 + +[5-windmill@259,284|44] +botWallVisible=false +bottom=true +height=77 +leftWallVisible=false +rightWallVisible=true +speed=5 +topWallVisible=true +width=126 + +[6-ball@28,86] +dummykey=true + +[6-blackhole@169,41|36] +exit=381,349 +exitDeg=200 +maxspeed=5 +minspeed=1 + +[6-blackhole@266,293|43] +exit=26,239 +exitDeg=290 +maxspeed=2 +minspeed=1 + +[6-cup@103,354|44] +dummykey=true + +[6-cup@104,110|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-puddle@108,218|32] +changeEnabled=false +changeEvery=50 +height=56 +width=118 + +[6-puddle@149,331|40] +changeEnabled=false +changeEvery=50 +height=92 +width=44 + +[6-puddle@151,223|28] +changeEnabled=false +changeEvery=50 +height=44 +width=84 + +[6-puddle@157,369|37] +changeEnabled=false +changeEvery=50 +height=90 +width=62 + +[6-puddle@161,305|41] +changeEnabled=false +changeEvery=50 +height=98 +width=70 + +[6-puddle@167,382|39] +changeEnabled=false +changeEvery=50 +height=52 +width=64 + +[6-puddle@177,232|35] +changeEnabled=false +changeEvery=50 +height=30 +width=42 + +[6-puddle@178,261|36] +changeEnabled=false +changeEvery=50 +height=104 +width=64 + +[6-puddle@178,285|38] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[6-puddle@183,389|42] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[6-puddle@41,208|67] +changeEnabled=false +changeEvery=50 +height=56 +width=222 + +[6-sand@290,48|63] +changeEnabled=false +changeEvery=50 +height=60 +width=58 + +[6-sand@315,31|52] +changeEnabled=false +changeEvery=50 +height=34 +width=142 + +[6-sand@331,73|62] +changeEnabled=false +changeEvery=50 +height=96 +width=76 + +[6-sand@360,99|61] +changeEnabled=false +changeEvery=50 +height=172 +width=54 + +[6-sand@369,24|64] +changeEnabled=false +changeEvery=50 +height=26 +width=24 + +[6-sand@372,25|67] +changeEnabled=false +changeEvery=50 +height=28 +width=34 + +[6-sand@373,40|65] +changeEnabled=false +changeEvery=50 +height=30 +width=24 + +[6-sand@383,36|66] +changeEnabled=false +changeEvery=50 +height=18 +width=12 + +[6-slope@10,11|63] +grade=4 +gradient=Diagonal +height=66 +reversed=true +stuckOnGround=false +width=142 + +[6-slope@11,314|62] +grade=4 +gradient=Opposite Diagonal +height=74 +reversed=false +stuckOnGround=false +width=89 + +[6-slope@13,145|66] +grade=4 +gradient=Vertical +height=67 +reversed=false +stuckOnGround=false +width=133 + +[6-slope@141,14|60] +grade=2 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[6-slope@147,145|71] +grade=4 +gradient=Opposite Diagonal +height=82 +reversed=false +stuckOnGround=false +width=57 + +[6-slope@182,222|25] +grade=6 +gradient=Vertical +height=168 +reversed=false +stuckOnGround=false +width=208 + +[6-slope@21,275|61] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[6-slope@223,45|72] +grade=4 +gradient=Opposite Diagonal +height=158 +reversed=true +stuckOnGround=false +width=121 + +[6-slope@344,181|73] +grade=4 +gradient=Vertical +height=22 +reversed=true +stuckOnGround=false +width=43 + +[6-slope@58,69|75] +grade=2 +gradient=Elliptic +height=91 +reversed=true +stuckOnGround=false +width=91 + +[6-wall@0,0|10] +endPoint=362,260 +startPoint=388,272 + +[6-wall@0,0|11] +endPoint=344,292 +startPoint=290,254 + +[6-wall@0,0|12] +endPoint=309,302 +startPoint=282,312 + +[6-wall@0,0|13] +endPoint=102,60 +startPoint=70,74 + +[6-wall@0,0|15] +endPoint=360,284 +startPoint=344,292 + +[6-wall@0,0|17] +endPoint=121,66 +startPoint=102,60 + +[6-wall@0,0|18] +endPoint=258,309 +startPoint=229,298 + +[6-wall@0,0|20] +endPoint=176,78 +startPoint=150,88 + +[6-wall@0,0|21] +endPoint=150,88 +startPoint=132,81 + +[6-wall@0,0|22] +endPoint=132,81 +startPoint=121,66 + +[6-wall@0,0|23] +endPoint=200,310 +startPoint=189,350 + +[6-wall@0,0|29] +endPoint=217,80 +startPoint=197,83 + +[6-wall@0,0|30] +endPoint=203,11 +startPoint=225,46 + +[6-wall@0,0|31] +endPoint=38,114 +startPoint=11,114 + +[6-wall@0,0|4] +endPoint=227,46 +startPoint=226,63 + +[6-wall@0,0|45] +endPoint=309,356 +startPoint=277,364 + +[6-wall@0,0|5] +endPoint=38,114 +startPoint=57,98 + +[6-wall@0,0|58] +endPoint=226,63 +startPoint=217,80 + +[6-wall@0,0|59] +endPoint=254,347 +startPoint=231,353 + +[6-wall@0,0|6] +endPoint=197,83 +startPoint=176,78 + +[6-wall@0,0|63] +endPoint=213,388 +startPoint=189,350 + +[6-wall@0,0|66] +endPoint=213,285 +startPoint=200,310 + +[6-wall@0,0|7] +endPoint=57,98 +startPoint=70,74 + +[6-wall@0,0|74] +endPoint=251,10 +startPoint=225,46 + +[6-wall@0,0|8] +endPoint=232,273 +startPoint=213,285 + +[6-wall@0,0|9] +endPoint=116,271 +startPoint=76,296 + +[7-ball@37,357] +dummykey=true + +[7-blackhole@263,340|15] +exit=323,359 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[7-cup@333,50|3] +dummykey=true + +[7-cup@367,355|16] +dummykey=true + +[7-floater@163,347|13] +botWallVisible=true +endPoint=163,347 +height=41 +leftWallVisible=true +rightWallVisible=true +speed=6 +startPoint=158,314 +topWallVisible=true +width=10 + +[7-floater@197,284|14] +botWallVisible=true +endPoint=197,284 +height=37 +leftWallVisible=true +rightWallVisible=true +speed=3 +startPoint=189,222 +topWallVisible=true +width=9 + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[7-slope@111,21|6] +grade=2 +gradient=Opposite Diagonal +height=97 +reversed=false +stuckOnGround=false +width=151 + +[7-slope@112,117|5] +grade=2 +gradient=Diagonal +height=141 +reversed=true +stuckOnGround=false +width=143 + +[7-slope@193,145|10] +grade=3 +gradient=Vertical +height=57 +reversed=true +stuckOnGround=false +width=197 + +[7-slope@239,326|17] +grade=4 +gradient=Diagonal +height=63 +reversed=false +stuckOnGround=false +width=73 + +[7-slope@27,117|8] +grade=2 +gradient=Opposite Diagonal +height=136 +reversed=true +stuckOnGround=false +width=84 + +[7-slope@27,18|7] +grade=2 +gradient=Diagonal +height=100 +reversed=false +stuckOnGround=false +width=87 + +[7-wall@0,0|11] +endPoint=387,326 +startPoint=313,326 + +[7-wall@0,0|12] +endPoint=312,327 +startPoint=312,387 + +[7-wall@0,0|4] +endPoint=313,326 +startPoint=194,204 + +[7-wall@0,0|9] +endPoint=194,148 +startPoint=194,204 + +[8-ball@318,46] +dummykey=true + +[8-blackhole@33,33|9] +exit=384,321 +exitDeg=195 +maxspeed=3 +minspeed=3 + +[8-cup@226,360|11] +dummykey=true + +[8-floater@207,105|3] +botWallVisible=true +endPoint=207,105 +height=105 +leftWallVisible=false +rightWallVisible=true +speed=5 +startPoint=54,225 +topWallVisible=false +width=168 + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[8-puddle@274,284|14] +changeEnabled=false +changeEvery=50 +height=36 +width=106 + +[8-puddle@279,290|17] +changeEnabled=false +changeEvery=50 +height=44 +width=150 + +[8-puddle@311,275|12] +changeEnabled=false +changeEvery=50 +height=58 +width=138 + +[8-puddle@338,267|13] +changeEnabled=false +changeEvery=50 +height=54 +width=114 + +[8-puddle@366,254|15] +changeEnabled=false +changeEvery=50 +height=58 +width=96 + +[8-sand@236,61|20] +changeEnabled=false +changeEvery=50 +height=50 +width=74 + +[8-sand@243,47|19] +changeEnabled=false +changeEvery=50 +height=64 +width=70 + +[8-slope@12,70|10] +grade=3 +gradient=Vertical +height=195 +reversed=false +stuckOnGround=true +width=180 + +[8-slope@131,25|22] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-slope@59,19|21] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[8-wall@0,0|18] +endPoint=389,335 +startPoint=312,388 + +[8-wall@0,0|22] +endPoint=387,211 +startPoint=360,252 + +[8-wall@0,0|5] +endPoint=193,9 +startPoint=193,390 + +[9-ball@38,290] +dummykey=true + +[9-cup@318,61|3] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@100,33|8] +changeEnabled=false +changeEvery=50 +height=32 +width=28 + +[9-puddle@112,20|23] +changeEnabled=false +changeEvery=50 +height=10 +width=34 + +[9-puddle@133,37|18] +changeEnabled=false +changeEvery=50 +height=46 +width=88 + +[9-puddle@166,43|19] +changeEnabled=false +changeEvery=50 +height=40 +width=84 + +[9-puddle@178,33|22] +changeEnabled=false +changeEvery=50 +height=38 +width=92 + +[9-puddle@192,40|21] +changeEnabled=false +changeEvery=50 +height=30 +width=48 + +[9-slope@156,-24|24] +grade=4 +gradient=Elliptic +height=160 +reversed=false +stuckOnGround=false +width=160 + +[9-slope@189,216|7] +grade=6 +gradient=Elliptic +height=72 +reversed=false +stuckOnGround=false +width=72 + +[9-wall@0,0|5] +endPoint=220,214 +startPoint=145,131 + +[9-wall@0,0|6] +endPoint=219,293 +startPoint=219,389 + +[9-wall@0,0|9] +endPoint=74,11 +startPoint=11,40 + +[9-windmill@249,13|4] +botWallVisible=false +bottom=true +height=113 +leftWallVisible=true +rightWallVisible=false +speed=5 +topWallVisible=false +width=137 diff --git a/kolf/courses/Practice b/kolf/courses/Practice new file mode 100644 index 00000000..8ff077ec --- /dev/null +++ b/kolf/courses/Practice @@ -0,0 +1,376 @@ +[0-course@-50,-50] +Name=Slope Practice +Name[bg]=Ðаклонено +Name[bn]=ঢালà§à¦¤à§‡ অনà§à¦¶à§€à¦²à¦¨ +Name[bs]=Vježba sa nagibom +Name[ca]=Pràctica de baixada +Name[da]=Træn skrÃ¥ninger +Name[de]=Hügelpraxis +Name[es]=Práctica de pendiente +Name[et]=Kallakute treening +Name[fi]=Kaltevuusharjoitus +Name[fr]=Entraînement sur la pente +Name[gl]=Práctica de pendente +Name[he]=×ימון ×ž×“×¨×•× ×™× +Name[hi]=ढाल अभà¥à¤¯à¤¾à¤¸ +Name[hr]=Nagibna vježba TODO +Name[hu]=Gyakorlás lejtÅ‘n +Name[is]=Æfing í halla +Name[it]=Esercizio su pendio +Name[ja]=æ–œé¢ã®ç·´ç¿’ +Name[lv]=TrennÄ“Å¡anÄs uz nogÄzes +Name[mk]=Вежбање на наклони +Name[nb]=SkrÃ¥ningsøvelse +Name[nl]=Heuveloefening +Name[nn]=Bakkeøving +Name[pl]=Ćwiczenia na stoku +Name[pt]=Praticar Inclinações +Name[pt_BR]=Praticar Ladeira +Name[ru]=Практика на горках +Name[sk]=Nácvik slope +Name[sl]=Vaja za strmine +Name[sr]=Вежба нагиба +Name[sr@Latn]=Vežba nagiba +Name[sv]=Sluttningsövning +Name[ta]=சரிவ௠பயிறà¯à®šà®¿ +Name[tg]=Таҷриба дар Теппачаҳо +Name[tr]=EÄŸim Çalışması +Name[uk]=Ð¢Ñ€ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð° Ñхилах +Name[ven]=Ndowendowe ya Mutshoni +Name[xh]=Induli yokuzilolonga +Name[xx]=xxSlope Practicexx +Name[zh_CN]=æ–œå¡ç»ƒä¹  +Name[zh_TW]=æ–œå¡ç·´ç¿’ +Name[zu]=Ukuzejwayeza kokusantaba +author=Jason Katz-Brown +name=Slope Practice + +[1-ball@68,257] +dummykey=true + +[1-cup@289,186|3] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=4 +par=2 + +[1-sand@327,8|5] +changeEnabled=false +changeEvery=50 +height=132 +width=310 + +[1-sand@368,64|7] +changeEnabled=false +changeEvery=50 +height=96 +width=98 + +[1-sand@426,127|6] +changeEnabled=false +changeEvery=50 +height=418 +width=162 + +[1-slope@-1,88|4] +grade=2 +gradient=Vertical +height=247 +reversed=false +stuckOnGround=false +width=402 + +[2-ball@306,324] +dummykey=true + +[2-cup@262,69|3] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-sand@-62,187|5] +changeEnabled=false +changeEvery=50 +height=300 +width=376 + +[2-sand@11,-10|6] +changeEnabled=false +changeEvery=50 +height=174 +width=400 + +[2-sand@41,74|7] +changeEnabled=false +changeEvery=50 +height=136 +width=158 + +[2-slope@7,-6|4] +grade=2 +gradient=Opposite Diagonal +height=323 +reversed=true +stuckOnGround=false +width=395 + +[3-ball@39,167] +dummykey=true + +[3-cup@340,113|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-slope@160,-1|4] +grade=3 +gradient=Vertical +height=237 +reversed=true +stuckOnGround=false +width=90 + +[3-slope@247,-1|6] +grade=3 +gradient=Diagonal +height=237 +reversed=true +stuckOnGround=false +width=60 + +[3-slope@87,-1|5] +grade=3 +gradient=Opposite Diagonal +height=236 +reversed=true +stuckOnGround=false +width=74 + +[3-wall@0,0|7] +endPoint=306,13 +startPoint=380,75 + +[3-wall@0,0|8] +endPoint=380,75 +startPoint=386,130 + +[3-wall@0,0|9] +endPoint=386,130 +startPoint=366,246 + +[4-ball@344,292] +dummykey=true + +[4-cup@74,281|3] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[4-puddle@205,406|5] +changeEnabled=false +changeEvery=50 +height=444 +width=112 + +[4-slope@11,43|4] +grade=4 +gradient=Vertical +height=155 +reversed=false +stuckOnGround=false +width=377 + +[5-ball@328,312] +dummykey=true + +[5-cup@96,73|3] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-slope@-119,167|4] +grade=2 +gradient=Elliptic +height=345 +reversed=false +stuckOnGround=false +width=345 + +[5-wall@0,0|10] +endPoint=272,380 +startPoint=362,366 + +[5-wall@0,0|11] +endPoint=231,95 +startPoint=202,45 + +[5-wall@0,0|5] +endPoint=231,95 +startPoint=176,214 + +[5-wall@0,0|6] +endPoint=202,45 +startPoint=68,29 + +[5-wall@0,0|7] +endPoint=68,29 +startPoint=30,65 + +[5-wall@0,0|8] +endPoint=30,65 +startPoint=31,264 + +[5-wall@0,0|9] +endPoint=272,380 +startPoint=103,367 + +[6-ball@338,348] +dummykey=true + +[6-cup@80,52|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[6-slope@11,226|5] +grade=4 +gradient=Vertical +height=66 +reversed=true +stuckOnGround=false +width=378 + +[6-slope@12,125|4] +grade=4 +gradient=Vertical +height=70 +reversed=false +stuckOnGround=false +width=376 + +[7-ball@370,174] +dummykey=true + +[7-cup@161,231|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-slope@8,73|4] +grade=4 +gradient=Elliptic +height=311 +reversed=true +stuckOnGround=false +width=311 + +[7-wall@0,0|5] +endPoint=330,174 +startPoint=330,297 + +[7-wall@0,0|6] +endPoint=330,297 +startPoint=406,297 + +[8-ball@331,91] +dummykey=true + +[8-cup@94,267|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-sand@-8,157|5] +changeEnabled=false +changeEvery=50 +height=142 +width=372 + +[8-slope@11,145|4] +grade=6 +gradient=Vertical +height=254 +reversed=false +stuckOnGround=false +width=391 + +[8-wall@0,0|6] +endPoint=13,398 +startPoint=13,228 + +[9-ball@68,330] +dummykey=true + +[9-cup@320,101|3] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@210,356|6] +changeEnabled=false +changeEvery=50 +height=312 +width=170 + +[9-puddle@305,237|5] +changeEnabled=false +changeEvery=50 +height=120 +width=270 + +[9-puddle@394,360|7] +changeEnabled=false +changeEvery=50 +height=264 +width=300 + +[9-slope@0,0|4] +grade=6 +gradient=Diagonal +height=230 +reversed=true +stuckOnGround=false +width=310 + +[9-wall@0,0|8] +endPoint=366,26 +startPoint=276,24 + +[9-wall@0,0|9] +endPoint=366,26 +startPoint=366,201 diff --git a/kolf/courses/ReallyEasy b/kolf/courses/ReallyEasy new file mode 100644 index 00000000..5e044f97 --- /dev/null +++ b/kolf/courses/ReallyEasy @@ -0,0 +1,2404 @@ +[0-course@-50,-50] +Name=Really Easy +Name[af]=Rerig Maklike +Name[ar]=سهل جدا +Name[bg]=Елементарно +Name[bn]=খà§à¦¬à¦‡ সহজ +Name[br]=Aes-tre +Name[bs]=Stvarno lagano +Name[ca]=Realment fàcil +Name[cs]=Opravdu jednoduchý +Name[da]=Meget nem +Name[de]=Sehr leicht +Name[el]=ΠÏαγματικά εÏκολο +Name[es]=Realmente fácil +Name[et]=Imelihtne +Name[fi]=Todella helppo +Name[fr]=Vraiment facile +Name[gl]=Moi fácil +Name[he]=ממש קל +Name[hi]=सचमà¥à¤š आसान +Name[hr]=Stvarno lagano +Name[hu]=Nagyon könnyű +Name[is]=Verulega auðveld +Name[it]=Facilissimo +Name[ja]=ホントã«ã‚«ãƒ³ã‚¿ãƒ³ +Name[lv]=TieÅ¡Äm viegli +Name[mk]=ÐавиÑтина леÑен +Name[nb]=Veldig lett +Name[nl]=Heel eenvoudig +Name[nn]=Kjempelett +Name[nso]=Bonolo Kannete +Name[pl]=NaprawdÄ™ Å‚atwy tor +Name[pt]=Realmente Fácil +Name[pt_BR]=Realmente Fácil +Name[ro]=Foarte uÅŸor +Name[ru]=Проще некуда +Name[sk]=Naozaj jednoduché +Name[sl]=Zelo lahko +Name[sr]=Баш лако +Name[sr@Latn]=BaÅ¡ lako +Name[sv]=Mycket enkel +Name[ta]=உணà¯à®®à¯ˆà®¯à®¿à®²à¯‡à®¯à¯‡ சà¯à®²à®ªà®®à®¾à®©à®¤à¯ +Name[tg]=Дар Ҳақиқат ОÑон +Name[tr]=Çok Kolay +Name[uk]=Дуже проÑто +Name[ven]=Zwi leluwa nga Maanda +Name[wa]=Vormint Ã¥jhey +Name[xh]=Ilula kakhulu +Name[xx]=xxReally Easyxx +Name[zh_CN]=真的很容易 +Name[zh_TW]=真的簡單 +Name[zu]=Kulula kakhulu +author=Neil Stevens +name=Really Easy + +[1-ball@79,203] +dummykey=true + +[1-cup@301,184|7] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=2 + +[1-slope@-3,238|9] +grade=4 +gradient=Vertical +height=170 +reversed=true +stuckOnGround=false +width=407 + +[1-slope@-5,-4|8] +grade=4 +gradient=Vertical +height=146 +reversed=false +stuckOnGround=false +width=399 + +[1-wall@0,0|3] +endPoint=322,148 +startPoint=63,170 + +[1-wall@0,0|4] +endPoint=324,218 +startPoint=63,236 + +[1-wall@0,0|5] +endPoint=63,170 +startPoint=63,236 + +[1-wall@0,0|6] +endPoint=322,148 +startPoint=324,218 + +[10-ball@171,205] +dummykey=true + +[10-cup@209,158|0] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=5 + +[10-puddle@239,231|3] +changeEnabled=false +changeEvery=50 +height=60 +width=70 + +[10-sand@137,128|4] +changeEnabled=false +changeEvery=50 +height=78 +width=90 + +[10-wall@0,0|1] +endPoint=190,39 +startPoint=190,349 + +[10-wall@0,0|2] +endPoint=324,183 +startPoint=72,183 + +[11-ball@189,342] +dummykey=true + +[11-bridge@109,184|4] +botWallVisible=true +height=172 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=167 + +[11-cup@190,138|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=3 + +[11-wall@0,0|1] +endPoint=308,48 +startPoint=71,48 + +[11-wall@0,0|2] +endPoint=190,51 +startPoint=190,124 + +[11-windmill@109,119|3] +botWallVisible=false +bottom=true +height=64 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=165 + +[12-ball@281,101] +dummykey=true + +[12-cup@208,252|0] +dummykey=true + +[12-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=7 +par=4 + +[12-slope@170,111|2] +grade=6 +gradient=Vertical +height=226 +reversed=true +stuckOnGround=false +width=46 + +[12-slope@80,43|1] +grade=6 +gradient=Diagonal +height=67 +reversed=true +stuckOnGround=false +width=208 + +[12-slope@92,178|3] +grade=6 +gradient=Horizontal +height=42 +reversed=true +stuckOnGround=false +width=200 + +[12-wall@0,0|4] +endPoint=291,111 +startPoint=293,43 + +[12-wall@0,0|6] +endPoint=291,111 +startPoint=218,111 + +[12-wall@0,0|7] +endPoint=293,43 +startPoint=81,43 + +[13-ball@48,40] +dummykey=true + +[13-blackhole@348,38|8] +exit=209,298 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[13-cup@207,143|0] +dummykey=true + +[13-floater@276,198|9] +botWallVisible=true +endPoint=276,198 +height=2 +leftWallVisible=false +rightWallVisible=false +speed=11 +startPoint=45,199 +topWallVisible=false +width=90 + +[13-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=6 +par=3 + +[13-wall@0,0|1] +endPoint=348,72 +startPoint=48,72 + +[13-wall@0,0|2] +endPoint=150,75 +startPoint=151,125 + +[13-wall@0,0|3] +endPoint=150,123 +startPoint=120,291 + +[13-wall@0,0|4] +endPoint=120,291 +startPoint=54,359 + +[13-wall@0,0|5] +endPoint=151,125 +startPoint=281,123 + +[13-wall@0,0|6] +endPoint=281,123 +startPoint=244,332 + +[13-wall@0,0|7] +endPoint=244,332 +startPoint=221,359 + +[14-ball@129,183] +dummykey=true + +[14-cup@308,172|0] +dummykey=true + +[14-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=5 + +[14-puddle@1,10|7] +changeEnabled=false +changeEvery=50 +height=1242 +width=32 + +[14-puddle@11,4|9] +changeEnabled=false +changeEvery=50 +height=24 +width=1062 + +[14-puddle@158,394|8] +changeEnabled=false +changeEvery=50 +height=36 +width=846 + +[14-puddle@407,192|10] +changeEnabled=false +changeEvery=50 +height=602 +width=38 + +[14-wall@-15,1|4] +endPoint=257,100 +startPoint=220,144 + +[14-wall@0,0|1] +endPoint=192,121 +startPoint=41,121 + +[14-wall@0,0|2] +endPoint=150,85 +startPoint=150,300 + +[14-wall@0,0|3] +endPoint=151,120 +startPoint=68,224 + +[14-wall@0,0|5] +endPoint=239,121 +startPoint=357,157 + +[14-wall@0,0|6] +endPoint=357,157 +startPoint=289,302 + +[15-ball@208,165] +dummykey=true + +[15-cup@140,158|0] +dummykey=true + +[15-cup@341,156|10] +dummykey=true + +[15-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=9 +par=4 + +[15-puddle@306,15|14] +changeEnabled=false +changeEvery=50 +height=140 +width=626 + +[15-puddle@386,81|15] +changeEnabled=false +changeEvery=50 +height=82 +width=190 + +[15-puddle@386,97|16] +changeEnabled=false +changeEvery=50 +height=130 +width=114 + +[15-puddle@404,165|17] +changeEnabled=false +changeEvery=50 +height=170 +width=100 + +[15-puddle@408,273|18] +changeEnabled=false +changeEvery=50 +height=260 +width=76 + +[15-puddle@420,348|19] +changeEnabled=false +changeEvery=50 +height=272 +width=98 + +[15-wall@0,0|1] +endPoint=169,30 +startPoint=169,295 + +[15-wall@0,0|2] +endPoint=169,295 +startPoint=93,342 + +[15-wall@0,0|3] +endPoint=167,175 +startPoint=109,213 + +[15-wall@0,0|4] +endPoint=155,141 +startPoint=115,110 + +[15-wall@0,0|5] +endPoint=235,29 +startPoint=239,337 + +[15-wall@0,0|6] +endPoint=239,337 +startPoint=314,336 + +[15-wall@0,0|7] +endPoint=314,336 +startPoint=314,291 + +[15-wall@0,0|8] +endPoint=290,213 +startPoint=237,175 + +[15-wall@0,0|9] +endPoint=237,175 +startPoint=292,122 + +[15-windmill@10,105|13] +botWallVisible=false +bottom=true +height=108 +leftWallVisible=false +rightWallVisible=false +speed=7 +topWallVisible=true +width=99 + +[16-ball@198,27] +dummykey=true + +[16-cup@240,130|0] +dummykey=true + +[16-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[16-puddle@197,81|2] +changeEnabled=false +changeEvery=50 +height=34 +width=14 + +[16-puddle@197,93|1] +changeEnabled=false +changeEvery=50 +height=16 +width=236 + +[16-wall@-10,-21|12] +endPoint=289,249 +startPoint=229,210 + +[16-wall@0,0|10] +endPoint=199,332 +startPoint=179,330 + +[16-wall@0,0|9] +endPoint=199,197 +startPoint=199,332 + +[16-wall@3,-18|11] +endPoint=88,284 +startPoint=166,219 + +[16-windmill@142,112|3] +botWallVisible=false +bottom=true +height=81 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=true +width=113 + +[17-ball@21,143] +dummykey=true + +[17-cup@255,163|0] +dummykey=true + +[17-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=6 + +[17-sand@100,136|3] +changeEnabled=false +changeEvery=50 +height=48 +width=18 + +[17-sand@104,322|12] +changeEnabled=false +changeEvery=50 +height=32 +width=52 + +[17-sand@112,173|6] +changeEnabled=false +changeEvery=50 +height=26 +width=50 + +[17-sand@119,319|4] +changeEnabled=false +changeEvery=50 +height=28 +width=66 + +[17-sand@126,173|10] +changeEnabled=false +changeEvery=50 +height=26 +width=42 + +[17-sand@134,177|9] +changeEnabled=false +changeEvery=50 +height=32 +width=42 + +[17-sand@137,315|11] +changeEnabled=false +changeEvery=50 +height=24 +width=54 + +[17-sand@65,323|14] +changeEnabled=false +changeEvery=50 +height=20 +width=46 + +[17-sand@75,329|5] +changeEnabled=false +changeEvery=50 +height=28 +width=86 + +[17-sand@88,301|15] +changeEnabled=false +changeEvery=50 +height=78 +width=20 + +[17-sand@90,319|13] +changeEnabled=false +changeEvery=50 +height=20 +width=72 + +[17-sand@93,219|2] +changeEnabled=false +changeEvery=50 +height=238 +width=32 + +[17-sand@94,182|1] +changeEnabled=false +changeEvery=50 +height=38 +width=114 + +[17-sand@96,133|7] +changeEnabled=false +changeEvery=50 +height=44 +width=28 + +[17-sand@97,122|8] +changeEnabled=false +changeEvery=50 +height=46 +width=26 + +[17-slope@206,262|27] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@215,253|28] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@226,244|29] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@234,236|30] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@243,228|31] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@250,219|32] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@259,209|33] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-slope@268,202|34] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[17-wall@0,0|16] +endPoint=266,213 +startPoint=201,273 + +[17-wall@0,0|17] +endPoint=266,213 +startPoint=292,174 + +[17-wall@0,0|18] +endPoint=289,253 +startPoint=200,344 + +[17-wall@0,0|19] +endPoint=289,253 +startPoint=328,202 + +[17-wall@0,0|20] +endPoint=347,324 +startPoint=279,264 + +[17-wall@0,0|21] +endPoint=347,324 +startPoint=366,347 + +[17-wall@0,0|22] +endPoint=205,168 +startPoint=252,111 + +[17-wall@0,0|23] +endPoint=205,168 +startPoint=252,222 + +[17-wall@0,0|24] +endPoint=295,108 +startPoint=163,113 + +[17-wall@0,0|25] +endPoint=295,108 +startPoint=374,114 + +[17-wall@0,0|26] +endPoint=263,110 +startPoint=243,62 + +[17-wall@0,0|35] +endPoint=200,344 +startPoint=170,355 + +[17-wall@0,0|36] +endPoint=328,202 +startPoint=333,166 + +[17-wall@0,0|37] +endPoint=201,273 +startPoint=172,283 + +[18-ball@277,299] +dummykey=true + +[18-cup@210,119|0] +dummykey=true + +[18-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=7 +par=4 + +[18-wall@-1,2|18] +endPoint=152,106 +startPoint=89,106 + +[18-wall@0,-3|9] +endPoint=152,134 +startPoint=90,134 + +[18-wall@0,0|1] +endPoint=315,45 +startPoint=83,45 + +[18-wall@0,0|10] +endPoint=51,85 +startPoint=51,138 + +[18-wall@0,0|11] +endPoint=305,105 +startPoint=235,105 + +[18-wall@0,0|12] +endPoint=304,135 +startPoint=235,135 + +[18-wall@0,0|13] +endPoint=267,229 +startPoint=123,229 + +[18-wall@0,0|14] +endPoint=298,277 +startPoint=98,277 + +[18-wall@0,0|15] +endPoint=298,277 +startPoint=298,327 + +[18-wall@0,0|16] +endPoint=172,279 +startPoint=172,345 + +[18-wall@0,0|17] +endPoint=298,327 +startPoint=254,327 + +[18-wall@0,0|2] +endPoint=196,45 +startPoint=196,175 + +[18-wall@0,0|3] +endPoint=192,174 +startPoint=68,248 + +[18-wall@0,0|4] +endPoint=196,175 +startPoint=313,244 + +[18-wall@0,0|5] +endPoint=347,84 +startPoint=51,85 + +[18-wall@0,0|6] +endPoint=347,84 +startPoint=347,136 + +[18-wall@0,0|7] +endPoint=51,85 +startPoint=51,138 + +[18-wall@0,0|8] +endPoint=51,85 +startPoint=51,138 + +[2-ball@252,137] +dummykey=true + +[2-blackhole@155,137|12] +exit=75,308 +exitDeg=0 +maxspeed=4 +minspeed=4 + +[2-cup@353,305|7] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=5 +par=2 + +[2-puddle@-16,98|58] +changeEnabled=false +changeEvery=50 +height=52 +width=128 + +[2-puddle@324,230|73] +changeEnabled=false +changeEvery=50 +height=40 +width=590 + +[2-puddle@36,159|28] +changeEnabled=false +changeEvery=50 +height=148 +width=52 + +[2-puddle@47,196|62] +changeEnabled=false +changeEvery=50 +height=42 +width=28 + +[2-puddle@49,215|53] +changeEnabled=false +changeEvery=50 +height=38 +width=44 + +[2-puddle@67,223|29] +changeEnabled=false +changeEvery=50 +height=22 +width=36 + +[2-sand@13,296|15] +changeEnabled=false +changeEvery=50 +height=48 +width=64 + +[2-sand@29,318|17] +changeEnabled=false +changeEvery=50 +height=84 +width=48 + +[2-slope@283,1|20] +grade=3 +gradient=Elliptic +height=103 +reversed=false +stuckOnGround=false +width=103 + +[2-wall@0,0|10] +endPoint=60,355 +startPoint=58,283 + +[2-wall@0,0|11] +endPoint=374,263 +startPoint=372,338 + +[2-wall@0,0|3] +endPoint=265,101 +startPoint=138,110 + +[2-wall@0,0|4] +endPoint=279,161 +startPoint=133,171 + +[2-wall@0,0|5] +endPoint=133,171 +startPoint=138,110 + +[2-wall@0,0|6] +endPoint=265,101 +startPoint=279,161 + +[2-wall@0,0|8] +endPoint=374,263 +startPoint=58,283 + +[2-wall@0,0|9] +endPoint=372,338 +startPoint=60,355 + +[3-ball@331,335] +dummykey=true + +[3-cup@205,81|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=8 +par=5 + +[3-puddle@-5,168|49] +changeEnabled=false +changeEvery=50 +height=44 +width=126 + +[3-puddle@373,294|31] +changeEnabled=false +changeEvery=50 +height=42 +width=76 + +[3-puddle@38,156|32] +changeEnabled=false +changeEvery=50 +height=50 +width=72 + +[3-puddle@397,277|33] +changeEnabled=false +changeEvery=50 +height=68 +width=56 + +[3-puddle@52,143|27] +changeEnabled=false +changeEvery=50 +height=56 +width=52 + +[3-puddle@57,133|19] +changeEnabled=false +changeEvery=50 +height=52 +width=50 + +[3-puddle@64,114|25] +changeEnabled=false +changeEvery=50 +height=68 +width=40 + +[3-puddle@67,111|21] +changeEnabled=false +changeEvery=50 +height=42 +width=48 + +[3-puddle@74,34|41] +changeEnabled=false +changeEvery=50 +height=190 +width=58 + +[3-slope@-188,220|24] +grade=5 +gradient=Elliptic +height=307 +reversed=false +stuckOnGround=false +width=307 + +[3-wall@0,0|4] +endPoint=348,359 +startPoint=59,359 + +[3-wall@0,0|40] +endPoint=123,54 +startPoint=117,109 + +[3-wall@0,0|5] +endPoint=325,298 +startPoint=98,298 + +[3-wall@0,0|6] +endPoint=264,222 +startPoint=153,225 + +[3-wall@0,0|7] +endPoint=264,165 +startPoint=151,172 + +[3-wall@0,0|8] +endPoint=295,51 +startPoint=123,54 + +[3-wall@0,0|9] +endPoint=313,113 +startPoint=117,109 + +[4-ball@257,315] +dummykey=true + +[4-cup@35,38|9] +dummykey=true + +[4-floater@107,209|10] +botWallVisible=false +endPoint=107,209 +height=55 +leftWallVisible=false +rightWallVisible=false +speed=1 +startPoint=71,156 +topWallVisible=false +width=59 + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=6 +par=3 + +[4-puddle@406,415|54] +changeEnabled=false +changeEvery=50 +height=222 +width=356 + +[4-sand@105,164|35] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@112,151|22] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@115,129|38] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@116,136|34] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@117,119|11] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@118,119|45] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@120,101|10] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@121,84|42] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@122,90|46] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@123,62|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@123,72|9] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@124,48|8] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@125,31|7] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@125,36|33] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@238,32|20] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@238,99|29] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,118|26] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,67|25] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@239,80|18] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@240,50|24] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@241,137|21] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@242,157|23] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@242,174|22] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@245,196|19] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@251,190|36] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@264,199|30] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@278,195|28] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@290,199|14] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@299,194|40] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@313,196|27] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@323,197|31] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@33,209|39] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@332,192|47] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@334,198|12] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@347,192|21] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@364,196|37] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@364,200|15] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@367,194|32] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@41,208|13] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@55,204|41] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@67,198|17] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@77,189|43] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@84,190|48] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-sand@91,180|16] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[4-wall@0,0|49] +endPoint=128,13 +startPoint=119,134 + +[4-wall@0,0|50] +endPoint=119,134 +startPoint=54,207 + +[4-wall@0,0|51] +endPoint=54,207 +startPoint=12,208 + +[4-wall@0,0|52] +endPoint=240,15 +startPoint=241,201 + +[4-wall@0,0|53] +endPoint=241,201 +startPoint=382,197 + +[5-ball@352,289] +dummykey=true + +[5-cup@203,277|3] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=6 +par=3 + +[5-wall@0,0|4] +endPoint=331,331 +startPoint=43,331 + +[5-wall@0,0|5] +endPoint=267,205 +startPoint=102,204 + +[5-wall@0,0|6] +endPoint=267,205 +startPoint=262,327 + +[5-wall@0,0|7] +endPoint=307,59 +startPoint=82,62 + +[5-wall@0,0|8] +endPoint=164,66 +startPoint=146,329 + +[5-windmill@170,186|9] +botWallVisible=false +bottom=false +height=39 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=88 + +[6-ball@182,57] +dummykey=true + +[6-blackhole@201,82|19] +exit=312,177 +exitDeg=183 +maxspeed=2.32 +minspeed=2.32 + +[6-bridge@90,199|21] +botWallVisible=false +height=79 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=22 + +[6-cup@314,309|20] +dummykey=true + +[6-cup@95,295|3] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[6-wall@0,0|10] +endPoint=323,159 +startPoint=327,193 + +[6-wall@0,0|11] +endPoint=141,261 +startPoint=73,339 + +[6-wall@0,0|12] +endPoint=219,69 +startPoint=205,105 + +[6-wall@0,0|13] +endPoint=205,105 +startPoint=181,84 + +[6-wall@0,0|14] +endPoint=163,32 +startPoint=219,69 + +[6-wall@0,0|15] +endPoint=324,334 +startPoint=293,314 + +[6-wall@0,0|16] +endPoint=323,159 +startPoint=61,169 + +[6-wall@0,0|17] +endPoint=327,193 +startPoint=74,203 + +[6-wall@0,0|18] +endPoint=61,169 +startPoint=74,203 + +[6-wall@0,0|4] +endPoint=256,248 +startPoint=293,314 + +[6-wall@0,0|5] +endPoint=256,248 +startPoint=331,297 + +[6-wall@0,0|6] +endPoint=141,261 +startPoint=101,247 + +[6-wall@0,0|7] +endPoint=324,334 +startPoint=331,297 + +[6-wall@0,0|8] +endPoint=163,32 +startPoint=181,84 + +[6-wall@0,0|9] +endPoint=101,247 +startPoint=73,339 + +[7-ball@31,371] +dummykey=true + +[7-bridge@139,19|134] +botWallVisible=true +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=94 + +[7-bridge@332,221|135] +botWallVisible=false +height=119 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=50 + +[7-cup@368,30|3] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=5 + +[7-puddle@-1,72|6] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@-10,81|4] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@10,11|15] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@108,11|12] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@11,37|28] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@113,1|10] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@12,39|27] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@128,3|30] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@146,4|17] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@15,31|11] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@15,48|32] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@162,4|19] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@176,19|20] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@179,4|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@185,26|77] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@189,192|133] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@190,185|119] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@191,201|132] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@191,37|83] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@193,18|22] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@194,47|23] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,175|118] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,31|62] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@195,53|109] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@196,211|121] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,222|120] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,230|90] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@198,234|105] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@199,243|68] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@199,62|21] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@200,167|69] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@201,66|80] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@202,255|82] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,261|100] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,272|67] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@204,313|74] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,156|58] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,278|104] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,285|76] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,292|101] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,300|66] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,305|102] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@205,79|54] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,307|115] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,320|103] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@206,87|107] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@207,145|106] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@207,151|56] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,122|61] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,4|111] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@208,95|59] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,111|63] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,135|57] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@209,327|64] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@210,105|79] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@214,-4|60] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@216,321|117] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@219,7|112] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@223,332|99] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@236,330|116] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@242,335|72] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@247,0|65] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@25,36|35] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@263,334|78] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@265,340|98] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@267,-2|110] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@272,332|71] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@282,338|97] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@288,-8|108] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@297,320|96] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@300,332|55] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@303,323|95] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@305,314|93] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@306,-12|114] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@314,308|92] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@317,320|81] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@319,316|70] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@32,32|33] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@322,-14|113] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@323,305|89] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@325,301|88] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@328,306|75] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@330,295|73] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@335,284|94] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@344,281|87] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@350,273|91] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@361,269|86] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@37,5|18] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@385,263|85] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@4,63|7] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@403,260|84] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@43,26|31] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@49,16|13] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@54,22|34] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@59,-1|14] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@63,18|26] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@76,11|5] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@77,17|25] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@8,50|8] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@8,58|29] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@81,20|24] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-puddle@98,24|9] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[7-sand@114,219|45] +changeEnabled=false +changeEvery=50 +height=44 +width=62 + +[7-sand@136,211|44] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@151,207|67] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@165,204|78] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@183,202|76] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|122] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|123] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|124] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|125] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|126] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|127] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|129] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|130] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|131] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|72] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|74] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@200,200|77] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@208,193|128] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@220,194|75] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@234,189|79] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@247,188|37] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@260,182|53] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@282,176|54] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@302,169|52] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@317,166|70] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@328,162|55] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@71,231|36] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[7-sand@90,226|64] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[8-ball@78,251] +dummykey=true + +[8-blackhole@111,212|14] +exit=32,30 +exitDeg=0 +maxspeed=1 +minspeed=1 + +[8-blackhole@346,29|15] +exit=96,252 +exitDeg=139 +maxspeed=1 +minspeed=0 + +[8-blackhole@46,280|13] +exit=203,114 +exitDeg=308 +maxspeed=3 +minspeed=3 + +[8-cup@346,301|3] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=6 +par=3 + +[8-slope@23,11|16] +grade=2 +gradient=Horizontal +height=37 +reversed=true +stuckOnGround=false +width=342 + +[8-wall@0,0|10] +endPoint=345,272 +startPoint=212,101 + +[8-wall@0,0|11] +endPoint=388,311 +startPoint=345,272 + +[8-wall@0,0|12] +endPoint=388,311 +startPoint=335,328 + +[8-wall@0,0|4] +endPoint=146,204 +startPoint=54,312 + +[8-wall@0,0|5] +endPoint=19,283 +startPoint=54,312 + +[8-wall@0,0|6] +endPoint=104,195 +startPoint=19,283 + +[8-wall@0,0|7] +endPoint=146,204 +startPoint=104,195 + +[8-wall@0,0|8] +endPoint=335,328 +startPoint=163,88 + +[8-wall@0,0|9] +endPoint=212,101 +startPoint=163,88 + +[9-ball@54,55] +dummykey=true + +[9-bumper@103,164|8] +dummykey=true + +[9-bumper@114,230|11] +dummykey=true + +[9-bumper@128,197|9] +dummykey=true + +[9-bumper@141,161|5] +dummykey=true + +[9-bumper@152,123|0] +dummykey=true + +[9-bumper@156,85|2] +dummykey=true + +[9-bumper@162,25|13] +dummykey=true + +[9-bumper@187,157|6] +dummykey=true + +[9-bumper@221,153|12] +dummykey=true + +[9-bumper@242,329|23] +dummykey=true + +[9-bumper@243,290|18] +dummykey=true + +[9-bumper@248,219|14] +dummykey=true + +[9-bumper@248,254|17] +dummykey=true + +[9-bumper@254,187|16] +dummykey=true + +[9-bumper@257,149|3] +dummykey=true + +[9-bumper@277,339|21] +dummykey=true + +[9-bumper@317,338|20] +dummykey=true + +[9-bumper@346,321|22] +dummykey=true + +[9-bumper@371,295|24] +dummykey=true + +[9-bumper@38,348|19] +dummykey=true + +[9-bumper@58,324|15] +dummykey=true + +[9-bumper@66,170|7] +dummykey=true + +[9-bumper@77,296|10] +dummykey=true + +[9-bumper@97,261|4] +dummykey=true + +[9-cup@281,294|1] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=6 +par=3 diff --git a/kolf/courses/USApro b/kolf/courses/USApro new file mode 100644 index 00000000..bdc48566 --- /dev/null +++ b/kolf/courses/USApro @@ -0,0 +1,1982 @@ +[0-course@-50,-50] +Name=USA Pro +Name[bg]=Ю Ð•Ñ Ð•Ð¹ Про (USA Pro) +Name[bn]=ইউà¦à¦¸à¦ পà§à¦°à§‹ +Name[hi]=यूà¤à¤¸à¤ पà¥à¤°à¥‹ +Name[hu]=Amerikai profi +Name[ja]=USAプロ +Name[mk]=СÐД Профи +Name[pt_BR]=Pro EUA +Name[sr]=СÐД про +Name[sr@Latn]=SAD pro +Name[sv]=USA-proffs +Name[xx]=xxUSA Proxx +Name[zh_TW]=美國è·æ¥­é«˜çƒ +author=Rob Kaper + +[1-ball@22,350] +dummykey=true + +[1-bridge@306,87|2] +botWallVisible=false +height=212 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=68 + +[1-cup@78,38|9] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[1-puddle@-4,79|10] +changeEnabled=false +changeEvery=50 +height=218 +width=98 + +[1-puddle@171,196|1] +changeEnabled=false +changeEvery=50 +height=130 +width=596 + +[1-sand@14,207|0] +changeEnabled=false +changeEvery=50 +height=216 +width=840 + +[1-sign@4,371|14] +Comment=Daytona Beach, FL +Comment[bg]=Дейтона бийч, Флорида +Comment[bn]=ডেটোনা বীচ, ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ +Comment[hi]=डायटोना बीच, फà¥à¤²à¥‹. +Comment[hu]=Daytona Beach, Florida +Comment[ja]=デイトナビーãƒ, FL +Comment[lv]=Daitonas pludmale, Florida +Comment[mk]=Дејтона Бич, Флорида +Comment[nb]=Daytona Beach, Florida +Comment[nn]=Daytona Beach, Florida +Comment[pt]=Praia de Daytona, FL +Comment[pt_BR]=Praia de Daytona, FL +Comment[ru]=Дайтон Бич +Comment[sr]=Дејтона бич, Флорида +Comment[sr@Latn]=Dejtona biÄ, Florida +Comment[sv]=Daytona Beach, Florida +Comment[ta]=டேடோனா கடலà¯, FL +Comment[tg]=Дайтон Бич, FL +Comment[tr]=Daytona Plajı, FL +Comment[uk]=Дайтона Біч +Comment[wa]=Daytona Beach, Floride, USA +Comment[xx]=xxDaytona Beach, FLxx +Comment[zh_CN]=代托纳滩,佛罗里达州 +Comment[zh_TW]=Daytona æµ·ç˜ï¼Œä½›ç¾…里é”å·ž +botWallVisible=false +height=34 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=411 + +[1-slope@-3,-4|11] +grade=5 +gradient=Horizontal +height=131 +reversed=true +stuckOnGround=false +width=222 + +[1-slope@139,282|7] +grade=6 +gradient=Vertical +height=117 +reversed=true +stuckOnGround=false +width=53 + +[1-slope@215,273|8] +grade=4 +gradient=Vertical +height=132 +reversed=false +stuckOnGround=false +width=53 + +[1-slope@247,2|12] +grade=5 +gradient=Horizontal +height=152 +reversed=false +stuckOnGround=false +width=56 + +[1-slope@60,281|6] +grade=4 +gradient=Vertical +height=119 +reversed=false +stuckOnGround=false +width=54 + +[1-wall@0,0|13] +endPoint=328,387 +startPoint=38,387 + +[1-wall@0,0|3] +endPoint=374,299 +startPoint=328,387 + +[1-wall@0,0|4] +endPoint=297,3 +startPoint=374,87 + +[1-wall@0,0|5] +endPoint=297,3 +startPoint=32,3 + +[10-ball@364,364] +dummykey=true + +[10-blackhole@142,54|9] +exit=372,376 +exitDeg=170 +maxspeed=5 +minspeed=5 + +[10-blackhole@193,248|7] +exit=196,175 +exitDeg=220 +maxspeed=1 +minspeed=1 + +[10-bridge@169,235|11] +botWallVisible=false +height=95 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=49 + +[10-bumper@122,163|17] +dummykey=true + +[10-bumper@127,179|16] +dummykey=true + +[10-bumper@135,151|19] +dummykey=true + +[10-bumper@136,195|15] +dummykey=true + +[10-bumper@145,212|14] +dummykey=true + +[10-bumper@151,140|20] +dummykey=true + +[10-bumper@157,227|13] +dummykey=true + +[10-bumper@167,130|21] +dummykey=true + +[10-bumper@175,228|12] +dummykey=true + +[10-bumper@195,226|8] +dummykey=true + +[10-bumper@214,227|22] +dummykey=true + +[10-bumper@219,130|28] +dummykey=true + +[10-bumper@232,227|34] +dummykey=true + +[10-bumper@233,143|27] +dummykey=true + +[10-bumper@242,212|18] +dummykey=true + +[10-bumper@246,156|26] +dummykey=true + +[10-bumper@253,198|23] +dummykey=true + +[10-bumper@260,168|25] +dummykey=true + +[10-bumper@262,184|24] +dummykey=true + +[10-cup@193,121|32] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[10-puddle@14,152|30] +changeEnabled=false +changeEvery=50 +height=302 +width=184 + +[10-puddle@140,286|29] +changeEnabled=false +changeEvery=50 +height=86 +width=622 + +[10-puddle@357,89|31] +changeEnabled=false +changeEvery=50 +height=422 +width=130 + +[10-sand@101,111|10] +changeEnabled=false +changeEvery=50 +height=90 +width=146 + +[10-sand@258,141|41] +changeEnabled=false +changeEvery=50 +height=38 +width=36 + +[10-sign@-1,360|42] +Comment=Washington DC (Pentagon) +Comment[bg]=Вашингтон, окръг ÐšÐ¾Ð»ÑƒÐ¼Ð±Ð¸Ñ (Пентагона) +Comment[bn]=ওয়াশিংটন ডিসি (পেনà§à¦Ÿà¦¾à¦—ন) +Comment[es]=Washington DC (Pentágono) +Comment[fr]=Washington DC (Pentagone) +Comment[gl]=Washington DC (Pentágono) +Comment[he]=הפנטגון בוושינגטון העיר +Comment[hi]=वाशिंगटन डीसी (पेंटागन) +Comment[it]=Washington DC (Pentagono) +Comment[ja]=ワシントンDC (ペンタゴン) +Comment[lv]=WaÅ¡ingtona, Kolumbijas apgabals (Pentagons) +Comment[mk]=Вашингтон ДЦ (Пентагон) +Comment[pt]=Washington DC (Pentágono) +Comment[pt_BR]=Washington DC (Pentágono) +Comment[ru]=Пентагон +Comment[sr]=Вашингтон ДЦ (Пентагон) +Comment[sr@Latn]=VaÅ¡ington DC (Pentagon) +Comment[ta]= வாஷிஙà¯à®Ÿà®©à¯ DC (பெனà¯à®Ÿà¯à®Ÿà®•à®©à¯) +Comment[tg]=Вашингтон DC (Пентагон) +Comment[uk]=Пентагон +Comment[wa]=Washington DC (Pentagone), USA +Comment[xx]=xxWashington DC (Pentagon)xx +Comment[zh_CN]=åŽç››é¡¿ç‰¹åŒº(五角大楼) +Comment[zh_TW]=è¯ç››é “特å€(五角大廈) +botWallVisible=true +height=49 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=189 + +[10-slope@136,173|4] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@138,131|5] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@177,173|2] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@178,133|1] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[10-slope@217,174|3] +grade=4 +gradient=Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-slope@218,133|6] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=true +stuckOnGround=false +width=40 + +[10-wall@0,0|33] +endPoint=259,167 +startPoint=283,136 + +[10-wall@0,0|35] +endPoint=283,136 +startPoint=130,25 + +[10-wall@0,0|36] +endPoint=118,161 +startPoint=130,25 + +[10-wall@0,0|37] +endPoint=169,330 +startPoint=215,392 + +[10-wall@0,0|38] +endPoint=215,392 +startPoint=384,392 + +[10-wall@0,0|39] +endPoint=384,392 +startPoint=384,331 + +[10-wall@0,0|40] +endPoint=384,331 +startPoint=218,330 + +[11-ball@36,41] +dummykey=true + +[11-blackhole@140,181|15] +exit=114,265 +exitDeg=30 +maxspeed=3 +minspeed=1 + +[11-blackhole@225,184|13] +exit=77,64 +exitDeg=0 +maxspeed=9 +minspeed=4 + +[11-blackhole@256,272|18] +exit=195,249 +exitDeg=110 +maxspeed=3 +minspeed=3 + +[11-bridge@18,22|16] +botWallVisible=false +height=41 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=36 + +[11-cup@183,170|0] +dummykey=true + +[11-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=5 + +[11-puddle@182,144|3] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[11-puddle@289,3|10] +changeEnabled=false +changeEvery=50 +height=58 +width=242 + +[11-sand@17,316|17] +changeEnabled=false +changeEvery=50 +height=286 +width=318 + +[11-sand@60,174|4] +changeEnabled=false +changeEvery=50 +height=174 +width=84 + +[11-sign@-2,370|24] +Comment=Palm Springs, CO +Comment[bg]=Палм СпрингÑ, Колорадо +Comment[bn]=পাম সà§à¦ªà§à¦°à¦¿à¦™à§â€Œà¦¸, কলোরাডো +Comment[hi]=पॉम सà¥à¤ªà¥à¤°à¤¿à¤‚गà¥à¤¸, सीओ +Comment[hu]=Palm Springs, Connecticut +Comment[ja]=パームスプリング, CO +Comment[lv]=Palmspringsa, Colorado +Comment[mk]=Палм СпрингÑ, Колорадо +Comment[nb]=Palm Springs, Colorado +Comment[nn]=Palm Springs, Colorado +Comment[ru]=Палм Ð¡Ð¿Ñ€Ð¸Ð½Ð³Ñ +Comment[sr]=Палм ÑпрингÑ, Конектикат +Comment[sr@Latn]=Palm springs, Konektikat +Comment[sv]=Palm Springs, Colorado +Comment[ta]=பாம௠ஸà¯à®ªà®¿à®°à®¿à®™à¯à®¸à¯, CO +Comment[tg]=Палм СпрингÑ, CO +Comment[uk]=Палм Ð¡Ð¿Ñ€Ñ–Ð½Ò‘Ñ +Comment[wa]=Palm Springs, Colorado, USA +Comment[xx]=xxPalm Springs, COxx +Comment[zh_CN]=棕榈泉,科罗拉多州 +Comment[zh_TW]=棕櫚泉,科羅拉多州 +botWallVisible=true +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=146 + +[11-slope@109,150|2] +grade=4 +gradient=Elliptic +height=63 +reversed=true +stuckOnGround=false +width=63 + +[11-slope@120,-2|12] +grade=4 +gradient=Opposite Diagonal +height=51 +reversed=true +stuckOnGround=false +width=46 + +[11-slope@163,191|23] +grade=2 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=false +width=40 + +[11-slope@165,8|11] +grade=4 +gradient=Vertical +height=40 +reversed=true +stuckOnGround=false +width=246 + +[11-slope@195,152|1] +grade=4 +gradient=Elliptic +height=61 +reversed=true +stuckOnGround=false +width=61 + +[11-slope@212,226|19] +grade=8 +gradient=Elliptic +height=91 +reversed=false +stuckOnGround=false +width=91 + +[11-wall@0,0|14] +endPoint=383,96 +startPoint=344,32 + +[11-wall@0,0|20] +endPoint=106,147 +startPoint=84,95 + +[11-wall@0,0|21] +endPoint=84,95 +startPoint=53,81 + +[11-wall@0,0|22] +endPoint=53,81 +startPoint=-1,88 + +[11-wall@0,0|5] +endPoint=189,108 +startPoint=106,147 + +[11-wall@0,0|6] +endPoint=189,108 +startPoint=277,180 + +[11-wall@0,0|7] +endPoint=277,180 +startPoint=181,283 + +[11-wall@0,0|8] +endPoint=181,283 +startPoint=345,375 + +[11-wall@0,0|9] +endPoint=345,375 +startPoint=383,96 + +[2-ball@66,149] +dummykey=true + +[2-bridge@-21,-1|1] +botWallVisible=true +height=19 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=428 + +[2-bumper@103,124|33] +dummykey=true + +[2-bumper@118,101|32] +dummykey=true + +[2-bumper@275,32|23] +dummykey=true + +[2-bumper@276,81|18] +dummykey=true + +[2-bumper@307,139|19] +dummykey=true + +[2-bumper@349,141|27] +dummykey=true + +[2-bumper@88,146|34] +dummykey=true + +[2-cup@272,132|0] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[2-puddle@257,212|11] +changeEnabled=false +changeEvery=50 +height=50 +width=52 + +[2-sand@161,120|9] +changeEnabled=false +changeEvery=50 +height=38 +width=40 + +[2-sand@162,201|8] +changeEnabled=false +changeEvery=50 +height=40 +width=38 + +[2-sign@26,345|31] +Comment=Las Vegas, NV +Comment[bg]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[bn]=লাস ভেগাস, নেভাডা +Comment[el]=Λας Βέγκας, NV +Comment[he]=ל×ס ווגס, NV +Comment[hi]=लास वेगास, à¤à¤¨à¤µà¥€. +Comment[hu]=Las Vegas, Nevada +Comment[ja]=ラスベガス, NV +Comment[lv]=Lasvegasa, Nevada +Comment[mk]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[nb]=Las Vegas, Nevada +Comment[nn]=Las Vegas, Nevada +Comment[ru]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ +Comment[sr]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, Ðевада +Comment[sr@Latn]=Las Vegas, Nevada +Comment[sv]=Las Vegas, Nevada +Comment[ta]= லாஸ௠வேகாஸà¯,NV +Comment[tg]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ, NV +Comment[uk]=Ð›Ð°Ñ Ð’ÐµÐ³Ð°Ñ +Comment[wa]=Las Vegas, Nevada, USA +Comment[xx]=xxLas Vegas, NVxx +Comment[zh_CN]=拉斯维加斯,内åŽè¾¾å·ž +Comment[zh_TW]=拉斯維加斯,內è¯é”å·ž +botWallVisible=true +height=34 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=157 + +[2-slope@-1,296|25] +grade=4 +gradient=Vertical +height=109 +reversed=false +stuckOnGround=false +width=404 + +[2-slope@140,140|4] +grade=4 +gradient=Elliptic +height=41 +reversed=true +stuckOnGround=true +width=41 + +[2-slope@140,18|21] +grade=6 +gradient=Horizontal +height=84 +reversed=true +stuckOnGround=false +width=114 + +[2-slope@181,98|5] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=true +width=40 + +[2-slope@182,140|3] +grade=2 +gradient=Elliptic +height=40 +reversed=false +stuckOnGround=true +width=40 + +[2-slope@182,181|7] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=true +width=40 + +[2-slope@219,107|6] +grade=4 +gradient=Elliptic +height=73 +reversed=true +stuckOnGround=true +width=73 + +[2-slope@25,19|30] +grade=4 +gradient=Diagonal +height=152 +reversed=true +stuckOnGround=false +width=43 + +[2-slope@252,18|17] +grade=4 +gradient=Opposite Diagonal +height=64 +reversed=true +stuckOnGround=false +width=113 + +[2-slope@27,166|24] +grade=8 +gradient=Vertical +height=28 +reversed=false +stuckOnGround=false +width=78 + +[2-wall@0,0|10] +endPoint=105,193 +startPoint=58,224 + +[2-wall@0,0|12] +endPoint=290,103 +startPoint=145,97 + +[2-wall@0,0|13] +endPoint=145,97 +startPoint=105,168 + +[2-wall@0,0|14] +endPoint=58,224 +startPoint=248,350 + +[2-wall@0,0|15] +endPoint=248,350 +startPoint=366,224 + +[2-wall@0,0|16] +endPoint=366,224 +startPoint=366,19 + +[2-wall@0,0|2] +endPoint=290,220 +startPoint=290,103 + +[2-wall@0,0|20] +endPoint=24,19 +startPoint=24,193 + +[2-wall@0,0|22] +endPoint=290,220 +startPoint=260,251 + +[2-wall@0,0|26] +endPoint=221,223 +startPoint=260,251 + +[2-wall@0,0|28] +endPoint=105,168 +startPoint=105,193 + +[2-wall@0,0|29] +endPoint=105,193 +startPoint=24,193 + +[3-ball@334,12] +dummykey=true + +[3-cup@366,146|1] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=2 + +[3-sign@-4,358|9] +Comment=San Francisco, CA +Comment[bg]=Сан ФранциÑко, ÐšÐ°Ð»Ð¸Ñ„Ð¾Ñ€Ð½Ð¸Ñ +Comment[bn]=সান ফà§à¦°à¦¾à¦¨à§à¦¸à¦¿à¦¸à¦•à§‹, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾ +Comment[el]=Σαν ΦÏανσίσκο, CA +Comment[he]=סן פרנסיסקו,CA +Comment[hi]=सान फà¥à¤°à¤¾à¤‚सिसà¥à¤•à¥‹, सीठ+Comment[hu]=San Francisco, Kalifornia +Comment[ja]=サンフランシスコ, CA +Comment[lv]=Sanfrancisko, Kalifornija +Comment[mk]=Сан ФранциÑко, Калифорнија +Comment[nn]=San Francisco, California +Comment[pt]=São Francisco, CA +Comment[pt_BR]=São Francisco, CA +Comment[ru]=Сан-ФранциÑко +Comment[sr]=Сан ФранциÑко, Калифорнија +Comment[sr@Latn]=San Francisko, Kalifornija +Comment[sv]=San Francisco, Kalifornien +Comment[ta]= சான௠ஃபிரானà¯à®¸à®¿à®¸à¯à®•à¯‹,CA +Comment[tg]=Сан ФрантÑиÑко, CA +Comment[uk]=Сан-ФранциÑко +Comment[wa]=San Francisco, Californeye, USA +Comment[xx]=xxSan Francisco, CAxx +Comment[zh_CN]=æ´›æ‰çŸ¶ï¼ŒåŠ åˆ©ç¦å°¼äºšå·ž +Comment[zh_TW]=舊金山,加州 +botWallVisible=true +height=48 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=436 + +[3-slope@-163,-252|8] +grade=4 +gradient=Elliptic +height=384 +reversed=false +stuckOnGround=false +width=384 + +[3-slope@1,139|6] +grade=4 +gradient=Vertical +height=104 +reversed=false +stuckOnGround=false +width=394 + +[3-slope@195,-158|10] +grade=4 +gradient=Elliptic +height=273 +reversed=false +stuckOnGround=false +width=273 + +[3-slope@32,280|7] +grade=4 +gradient=Horizontal +height=49 +reversed=true +stuckOnGround=false +width=232 + +[3-wall@0,0|0] +endPoint=389,383 +startPoint=389,27 + +[3-wall@0,0|2] +endPoint=389,27 +startPoint=261,279 + +[3-wall@0,0|3] +endPoint=261,279 +startPoint=99,279 + +[3-wall@0,0|4] +endPoint=32,329 +startPoint=261,329 + +[3-wall@0,0|5] +endPoint=32,329 +startPoint=32,24 + +[4-ball@354,354] +dummykey=true + +[4-bridge@-11,172|2] +botWallVisible=false +height=20 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=472 + +[4-cup@182,182|0] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-sign@201,171|8] +Comment=Grand Canyon +Comment[ar]=الوادي العظيم +Comment[bg]=Гранд Каньон +Comment[bn]=গà§à¦°à§à¦¯à¦¾à¦¨à§à¦¡ কà§à¦¯à¦¾à¦¨à¦¿à§Ÿà¦¨ +Comment[el]=ΓÏαντ Κάνυον +Comment[es]=Gran Cañón +Comment[gl]=Gran Cañón +Comment[he]=גרנד קניון +Comment[hi]=गà¥à¤°à¤¾à¤‚ड केनà¥à¤¯à¥‰à¤¨ +Comment[ja]=グランドキャニオン +Comment[lv]=Lielais kanjons +Comment[mk]=Гранд кањон +Comment[ru]=Большой Каньон +Comment[sr]=Велики кањон +Comment[sr@Latn]=Veliki kanjon +Comment[ta]= கிராணà¯à®Ÿà¯ கேணà¯à®¯à®¾à®©à¯ +Comment[tg]=Канёни Калон +Comment[tr]=Büyük Kanyon +Comment[uk]=Великий каньйон +Comment[wa]=Grand Canyon, USA +Comment[xx]=xxGrand Canyonxx +Comment[zh_CN]=大峡谷 +Comment[zh_TW]=大峽谷 +botWallVisible=true +height=23 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=223 + +[4-slope@-1,129|1] +grade=4 +gradient=Vertical +height=61 +reversed=true +stuckOnGround=false +width=416 + +[4-slope@-14,192|3] +grade=4 +gradient=Vertical +height=38 +reversed=false +stuckOnGround=false +width=422 + +[4-slope@-2,273|6] +grade=4 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=445 + +[4-slope@-2,44|7] +grade=4 +gradient=Vertical +height=53 +reversed=true +stuckOnGround=false +width=502 + +[4-slope@-45,230|5] +grade=4 +gradient=Vertical +height=42 +reversed=true +stuckOnGround=false +width=454 + +[4-slope@0,87|4] +grade=4 +gradient=Vertical +height=42 +reversed=false +stuckOnGround=false +width=432 + +[5-ball@23,348] +dummykey=true + +[5-bridge@107,258|9] +botWallVisible=true +height=26 +leftWallVisible=true +rightWallVisible=false +topWallVisible=true +width=112 + +[5-cup@120,271|0] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[5-puddle@117,135|2] +changeEnabled=false +changeEvery=50 +height=84 +width=110 + +[5-puddle@165,297|8] +changeEnabled=false +changeEvery=50 +height=214 +width=102 + +[5-puddle@173,168|7] +changeEnabled=false +changeEvery=50 +height=108 +width=62 + +[5-puddle@180,140|6] +changeEnabled=false +changeEvery=50 +height=50 +width=144 + +[5-sand@65,346|1] +changeEnabled=false +changeEvery=50 +height=438 +width=48 + +[5-sign@219,344|10] +Comment=Lake Tahoe, CA/NV +Comment[bg]=Езерото Тахо, КалифорниÑ/Ðевада +Comment[bn]=লেক টাহো, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾/নেভাডা +Comment[el]=Λίμνη Tahoe, CA/NV +Comment[es]=Lago Tahoe, CA/NV +Comment[fr]=Lac Tahoe, CA/NV +Comment[gl]=Lago Tahoe, CA/NV +Comment[hi]=लेक ताहोà¤, सीà¤/à¤à¤¨à¤µà¥€ +Comment[hu]=Lake Tahoe, Kalifornia/Nevada +Comment[it]=Lago Tahoe, CA/NV +Comment[ja]=レイク・タホ, CA/NV +Comment[lv]=Taho ezers, Kalifornija/Nevada +Comment[mk]=Езеро Тахо, Калифорнија/Сев. Вирџинија +Comment[nn]=Lake Tahoe, California/Nevada +Comment[pt]=Lago Tahoe, CA/NV +Comment[pt_BR]=Lago Tahoe, CA/NV +Comment[ru]=Озеро Ð¢Ð°Ñ…Ð¾Ñ +Comment[sr]=Језеро Тахо, Кал./Ðев. +Comment[sr@Latn]=Jezero Taho, Kal./Nev. +Comment[sv]=Lake Tahoe, Kalifornien/Nevada +Comment[ta]= லேக௠தஹோ,CA/NV +Comment[tg]=Кӯли ТаҳоÑ, CA/NV +Comment[uk]=Озеро Тахое +Comment[wa]=Lak Tahoe, Californeye/Nevada, USA +Comment[xx]=xxLake Tahoe, CA/NVxx +Comment[zh_CN]=å¡”éœæ¹–,加利ç¦å°¼äºšå·ž/内åŽè¾¾å·ž +Comment[zh_TW]=太浩湖,加州/å…§è¯é”å·ž +botWallVisible=true +height=27 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=202 + +[5-slope@10,9|4] +grade=4 +gradient=Diagonal +height=110 +reversed=true +stuckOnGround=false +width=113 + +[5-slope@171,135|5] +grade=4 +gradient=Horizontal +height=268 +reversed=true +stuckOnGround=false +width=81 + +[5-slope@261,9|11] +grade=4 +gradient=Opposite Diagonal +height=112 +reversed=true +stuckOnGround=false +width=129 + +[5-slope@69,134|3] +grade=5 +gradient=Horizontal +height=281 +reversed=false +stuckOnGround=false +width=102 + +[5-wall@0,0|12] +endPoint=77,99 +startPoint=57,133 + +[5-wall@0,0|13] +endPoint=77,99 +startPoint=119,88 + +[6-ball@102,35] +dummykey=true + +[6-bridge@138,328|27] +botWallVisible=false +height=24 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=23 + +[6-bridge@168,127|8] +botWallVisible=false +height=92 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=21 + +[6-bridge@215,240|13] +botWallVisible=false +height=66 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=21 + +[6-bumper@182,182|15] +dummykey=true + +[6-bumper@182,182|16] +dummykey=true + +[6-cup@107,376|1] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=4 + +[6-puddle@-5,251|2] +changeEnabled=false +changeEvery=50 +height=296 +width=342 + +[6-puddle@186,179|4] +changeEnabled=false +changeEvery=50 +height=68 +width=192 + +[6-puddle@215,274|3] +changeEnabled=false +changeEvery=50 +height=64 +width=222 + +[6-puddle@222,374|7] +changeEnabled=false +changeEvery=50 +height=76 +width=148 + +[6-puddle@230,341|6] +changeEnabled=false +changeEvery=50 +height=20 +width=226 + +[6-puddle@292,354|5] +changeEnabled=false +changeEvery=50 +height=152 +width=144 + +[6-puddle@497,225|0] +changeEnabled=false +changeEvery=50 +height=508 +width=494 + +[6-sand@86,349|34] +changeEnabled=true +changeEvery=51 +height=188 +width=368 + +[6-sign@3,101|17] +Comment=Florida Keys, FL +Comment[bg]=Флорида КийÑ, Флорида +Comment[bn]=ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ কিইস, ফà§à¦²à§‹à¦°à¦¿à¦¡à¦¾ +Comment[hi]=फà¥à¤²à¥‹à¤°à¤¿à¤¡à¤¾ कीस, à¤à¤«à¤à¤² +Comment[hu]=Florida Keys, Florida +Comment[ja]=フロリダキーズ, FL +Comment[mk]=Гребени на Флорида, Флорида +Comment[nn]=Florida Keys, Florida +Comment[pt_BR]=Flórida Keys, FL +Comment[ru]=Флорида +Comment[sr]=Флорида киз, Флорида +Comment[sr@Latn]=Florida kiz, Florida +Comment[sv]=Florida Keys, Florida +Comment[ta]=ஃபà¯à®³à¯‹à®°à®¿à®Ÿà®¾ கீஸà¯, FL +Comment[tg]=Флорида, FL +Comment[uk]=Флорида Кіз +Comment[wa]=Florida Keys, Floride, USA +Comment[xx]=xxFlorida Keys, FLxx +Comment[zh_CN]=佛罗里达链岛,佛罗里达州 +Comment[zh_TW]=佛羅里é”群島,佛羅里é”å·ž +botWallVisible=true +height=30 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=117 + +[6-slope@128,300|14] +grade=4 +gradient=Horizontal +height=42 +reversed=false +stuckOnGround=false +width=115 + +[6-slope@158,216|23] +grade=4 +gradient=Horizontal +height=32 +reversed=true +stuckOnGround=false +width=99 + +[6-slope@165,23|21] +grade=4 +gradient=Vertical +height=104 +reversed=false +stuckOnGround=false +width=28 + +[6-slope@193,23|22] +grade=4 +gradient=Horizontal +height=31 +reversed=false +stuckOnGround=false +width=149 + +[6-slope@193,53|35] +grade=4 +gradient=Elliptic +height=101 +reversed=false +stuckOnGround=false +width=101 + +[6-wall@0,0|10] +endPoint=177,244 +startPoint=215,240 + +[6-wall@0,0|11] +endPoint=236,240 +startPoint=224,213 + +[6-wall@0,0|12] +endPoint=224,213 +startPoint=189,219 + +[6-wall@0,0|18] +endPoint=338,24 +startPoint=189,127 + +[6-wall@0,0|19] +endPoint=58,19 +startPoint=168,127 + +[6-wall@0,0|20] +endPoint=58,19 +startPoint=338,24 + +[6-wall@0,0|24] +endPoint=236,306 +startPoint=221,332 + +[6-wall@0,0|25] +endPoint=215,306 +startPoint=152,310 + +[6-wall@0,0|26] +endPoint=221,332 +startPoint=161,328 + +[6-wall@0,0|28] +endPoint=152,310 +startPoint=138,328 + +[6-wall@0,0|29] +endPoint=121,396 +startPoint=161,352 + +[6-wall@0,0|30] +endPoint=121,396 +startPoint=39,396 + +[6-wall@0,0|31] +endPoint=39,396 +startPoint=94,373 + +[6-wall@0,0|32] +endPoint=94,373 +startPoint=127,347 + +[6-wall@0,0|33] +endPoint=127,347 +startPoint=138,352 + +[6-wall@0,0|9] +endPoint=177,244 +startPoint=168,219 + +[7-ball@89,370] +dummykey=true + +[7-cup@375,24|0] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[7-puddle@215,236|1] +changeEnabled=false +changeEvery=50 +height=340 +width=224 + +[7-puddle@245,87|3] +changeEnabled=false +changeEvery=50 +height=44 +width=150 + +[7-puddle@68,170|2] +changeEnabled=false +changeEvery=50 +height=78 +width=178 + +[7-sign@11,116|7] +Comment=Golden Gate +Comment[ar]=البوابة الذهبية +Comment[bg]=Голдън Гейт +Comment[bn]=গোলà§à¦¡à§‡à¦¨ গেট +Comment[cs]=Zlatá brána +Comment[el]=ΧÏυσή Ï€Ïλη +Comment[gl]=Ponte de Rande +Comment[he]=שער הזהב +Comment[hi]=गोलà¥à¤¡à¤¨ गेट +Comment[ja]=ゴールデンゲート +Comment[lv]=Zelta vÄrti +Comment[mk]=Голден Гејт +Comment[ru]=Золотые Ворота +Comment[sr]=Голден гејт +Comment[sr@Latn]=Golden gejt +Comment[ta]=கோலà¯à®Ÿà®©à¯ கேட௠+Comment[tg]=Дарвозаҳои Тиллоӣ +Comment[tr]=Altın Kapı +Comment[uk]=Золоті ворота +Comment[wa]=Golden Gate, USA +Comment[xx]=xxGolden Gatexx +Comment[zh_CN]=金门大桥 +Comment[zh_TW]=金門大橋 +botWallVisible=false +height=97 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=40 + +[7-sign@135,364|25] +Comment=San Francisco, CA +Comment[bg]=Сан ФранциÑко, ÐšÐ°Ð»Ð¸Ñ„Ð¾Ñ€Ð½Ð¸Ñ +Comment[bn]=সান ফà§à¦°à¦¾à¦¨à§à¦¸à¦¿à¦¸à¦•à§‹, কà§à¦¯à¦¾à¦²à¦¿à¦«à§‹à¦°à§à¦¨à¦¿à§Ÿà¦¾ +Comment[el]=Σαν ΦÏανσίσκο, CA +Comment[he]=סן פרנסיסקו,CA +Comment[hi]=सान फà¥à¤°à¤¾à¤‚सिसà¥à¤•à¥‹, सीठ+Comment[hu]=San Francisco, Kalifornia +Comment[ja]=サンフランシスコ, CA +Comment[lv]=Sanfrancisko, Kalifornija +Comment[mk]=Сан ФранциÑко, Калифорнија +Comment[nn]=San Francisco, California +Comment[pt]=São Francisco, CA +Comment[pt_BR]=São Francisco, CA +Comment[ru]=Сан-ФранциÑко +Comment[sr]=Сан ФранциÑко, Калифорнија +Comment[sr@Latn]=San Francisko, Kalifornija +Comment[sv]=San Francisco, Kalifornien +Comment[ta]= சான௠ஃபிரானà¯à®¸à®¿à®¸à¯à®•à¯‹,CA +Comment[tg]=Сан ФрантÑиÑко, CA +Comment[uk]=Сан-ФранциÑко +Comment[wa]=San Francisco, Californeye, USA +Comment[xx]=xxSan Francisco, CAxx +Comment[zh_CN]=æ´›æ‰çŸ¶ï¼ŒåŠ åˆ©ç¦å°¼äºšå·ž +Comment[zh_TW]=舊金山,加州 +botWallVisible=true +height=40 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=159 + +[7-sign@98,209|24] +Comment=Bay Bridge +Comment[ar]=جسر الخليج (باي( +Comment[bg]=Бей Бридж +Comment[bn]=বে বà§à¦°à¦¿à¦œ +Comment[de]=Bay-Brücke +Comment[es]=Puente de la Bahía +Comment[gl]=Ponte da Ría +Comment[he]=גשר המפרץ +Comment[hi]=बे-बà¥à¤°à¤¿à¤œ +Comment[ja]=ベイブリッジ +Comment[lv]=Beibridža +Comment[mk]=Беј Бриџ +Comment[pt_BR]=Ponte da Baía +Comment[ru]=Залив Сан-ФрациÑко +Comment[sr]=Беј бриџ +Comment[sr@Latn]=Bej bridž +Comment[ta]=விரிகà¯à®Ÿà®¾ பாலம௠+Comment[tg]=БÑй Бриҷ +Comment[tr]=Bay Köprüsü +Comment[uk]=Бей-бридж +Comment[xx]=xxBay Bridgexx +Comment[zh_CN]=海湾大桥 +Comment[zh_TW]=æµ·ç£å¤§æ©‹ +botWallVisible=true +height=24 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=241 + +[7-slope@11,212|4] +grade=6 +gradient=Elliptic +height=74 +reversed=false +stuckOnGround=false +width=74 + +[7-slope@13,313|23] +grade=4 +gradient=Vertical +height=34 +reversed=true +stuckOnGround=false +width=150 + +[7-slope@17,23|5] +grade=2 +gradient=Elliptic +height=108 +reversed=false +stuckOnGround=false +width=108 + +[7-slope@293,9|22] +grade=6 +gradient=Opposite Diagonal +height=94 +reversed=true +stuckOnGround=false +width=95 + +[7-slope@333,49|21] +grade=5 +gradient=Vertical +height=339 +reversed=false +stuckOnGround=false +width=56 + +[7-slope@55,211|6] +grade=4 +gradient=Horizontal +height=73 +reversed=true +stuckOnGround=false +width=78 + +[7-wall@0,0|10] +endPoint=88,294 +startPoint=153,386 + +[7-wall@0,0|11] +endPoint=120,132 +startPoint=51,116 + +[7-wall@0,0|12] +endPoint=120,132 +startPoint=169,71 + +[7-wall@0,0|13] +endPoint=169,71 +startPoint=249,59 + +[7-wall@0,0|14] +endPoint=249,59 +startPoint=322,74 + +[7-wall@0,0|15] +endPoint=322,74 +startPoint=321,99 + +[7-wall@0,0|16] +endPoint=321,99 +startPoint=292,107 + +[7-wall@0,0|17] +endPoint=292,107 +startPoint=322,165 + +[7-wall@0,0|18] +endPoint=339,209 +startPoint=322,165 + +[7-wall@0,0|19] +endPoint=339,232 +startPoint=317,325 + +[7-wall@0,0|20] +endPoint=317,325 +startPoint=270,388 + +[7-wall@0,0|8] +endPoint=98,209 +startPoint=51,213 + +[7-wall@0,0|9] +endPoint=98,232 +startPoint=88,294 + +[8-ball@202,333] +dummykey=true + +[8-cup@203,88|1] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-puddle@205,213|0] +changeEnabled=false +changeEvery=50 +height=210 +width=76 + +[8-sand@201,35|3] +changeEnabled=false +changeEvery=50 +height=40 +width=45 + +[8-sand@202,363|2] +changeEnabled=false +changeEvery=50 +height=34 +width=98 + +[8-sign@147,370|19] +Comment=Washington DC +Comment[bg]=Вашингтон, окръг ÐšÐ¾Ð»ÑƒÐ¼Ð±Ð¸Ñ +Comment[bn]=ওয়াশিংটন ডিসি +Comment[el]=Ουάσινγκτον +Comment[he]=וושינגטון הבירה +Comment[hi]=वाशिंगटन डीसी +Comment[ja]=ワシントンDC +Comment[lv]=VaÅ¡ingtona, Kolumbijas apgabals +Comment[mk]=Вашингтон ДЦ +Comment[ru]=Вашингтон +Comment[sr]=Вашингтон ДЦ +Comment[sr@Latn]=VaÅ¡ington DC +Comment[ta]=வாஷிஙà¯à®Ÿà®©à¯ DC +Comment[tg]=Вашингтон DC +Comment[uk]=Вашингтон +Comment[wa]=Washington DC, USA +Comment[xx]=xxWashington DCxx +Comment[zh_CN]=åŽç››é¡¿ç‰¹åŒº +Comment[zh_TW]=è¯ç››é “ç‰¹å€ +botWallVisible=true +height=32 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=111 + +[8-slope@101,291|8] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@11,11|4] +grade=2 +gradient=Vertical +height=376 +reversed=false +stuckOnGround=false +width=88 + +[8-slope@136,8|18] +grade=5 +gradient=Elliptic +height=137 +reversed=true +stuckOnGround=false +width=137 + +[8-slope@174,46|10] +grade=5 +gradient=Elliptic +height=61 +reversed=false +stuckOnGround=false +width=61 + +[8-slope@244,9|7] +grade=3 +gradient=Vertical +height=381 +reversed=true +stuckOnGround=false +width=65 + +[8-slope@245,291|9] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=false +width=59 + +[8-slope@311,11|5] +grade=2 +gradient=Vertical +height=378 +reversed=false +stuckOnGround=false +width=75 + +[8-slope@99,11|6] +grade=3 +gradient=Vertical +height=377 +reversed=true +stuckOnGround=false +width=65 + +[8-wall@0,0|11] +endPoint=140,296 +startPoint=175,108 + +[8-wall@0,0|12] +endPoint=172,108 +startPoint=235,108 + +[8-wall@0,0|13] +endPoint=237,108 +startPoint=264,297 + +[8-wall@0,0|14] +endPoint=70,54 +startPoint=33,91 + +[8-wall@0,0|15] +endPoint=175,108 +startPoint=142,123 + +[8-wall@0,0|16] +endPoint=271,128 +startPoint=235,108 + +[8-wall@0,0|17] +endPoint=368,128 +startPoint=326,182 + +[9-ball@361,37] +dummykey=true + +[9-bridge@184,29|27] +botWallVisible=false +height=26 +leftWallVisible=false +rightWallVisible=false +topWallVisible=true +width=80 + +[9-bridge@218,278|26] +botWallVisible=true +height=28 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=80 + +[9-cup@125,313|0] +dummykey=true + +[9-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=10 +par=3 + +[9-puddle@128,389|12] +changeEnabled=false +changeEvery=50 +height=26 +width=28 + +[9-puddle@166,379|13] +changeEnabled=false +changeEvery=50 +height=10 +width=88 + +[9-puddle@216,370|14] +changeEnabled=false +changeEvery=50 +height=22 +width=30 + +[9-puddle@220,8|25] +changeEnabled=false +changeEvery=50 +height=40 +width=18 + +[9-puddle@222,46|24] +changeEnabled=false +changeEvery=50 +height=58 +width=20 + +[9-puddle@223,356|15] +changeEnabled=false +changeEvery=50 +height=28 +width=18 + +[9-puddle@225,82|23] +changeEnabled=false +changeEvery=50 +height=44 +width=18 + +[9-puddle@227,121|22] +changeEnabled=false +changeEvery=50 +height=74 +width=14 + +[9-puddle@231,152|21] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@240,335|16] +changeEnabled=false +changeEvery=50 +height=30 +width=45 + +[9-puddle@254,171|20] +changeEnabled=false +changeEvery=50 +height=52 +width=36 + +[9-puddle@256,302|17] +changeEnabled=false +changeEvery=50 +height=100 +width=24 + +[9-puddle@257,205|19] +changeEnabled=false +changeEvery=50 +height=42 +width=22 + +[9-puddle@262,246|18] +changeEnabled=false +changeEvery=50 +height=66 +width=20 + +[9-sign@291,368|28] +Comment=Niagara Falls, NY +Comment[bg]=Ðиагара, ÐÑŽ Йорк +Comment[bn]=নায়েগà§à¦°à¦¾ জলপà§à¦°à¦ªà¦¾à¦¤, নিউ ইয়রà§à¦• +Comment[de]=Niagara-Fälle, NY +Comment[el]=ΚαταÏÏάχτες ÎιαγάÏα, NY +Comment[es]=Cataratas del Niágara, NY +Comment[fr]=Chutes du Niagara, NY +Comment[gl]=Fervenzas do Niagara, NY +Comment[he]=מפלי × ×™×גרה, NY +Comment[hi]=नियागà¥à¤°à¤¾ फालà¥à¤¸, à¤à¤¨à¤µà¤¾à¤¯ +Comment[hu]=Niagara Falls, New York +Comment[ja]=ナイアガラã®æ», NY +Comment[lv]=NiagÄras Å«denskritums, Å…ujorka +Comment[mk]=Ðијагарини водопади, ÐЈ +Comment[nn]=Niagara Falls, New York +Comment[pt]=Catarata do Niagara, NY +Comment[pt_BR]=Cataratas do Niágara, NY +Comment[ru]=ÐиагарÑкий водопад +Comment[sk]=Niagarské vodopády, NY +Comment[sr]=Ðијагарини водопади, Њујорк +Comment[sr@Latn]=Nijagarini vodopadi, Njujork +Comment[sv]=Niagarafallen, Staten New York +Comment[ta]=நயாகரா நீரà¯à®µà¯€à®´à¯à®šà¯à®šà®¿,NY +Comment[tg]=Шаршараи Ðиагара, NY +Comment[tr]=Niagara Åželalesi, NY +Comment[uk]=ÐіагарÑький водоÑпад +Comment[wa]=Niagara, Noû York, USA +Comment[xx]=xxNiagara Falls, NYxx +Comment[zh_CN]=尼亚加拉大瀑布,纽约州 +Comment[zh_TW]=尼加拉瀑布,ç´ç´„å·ž +botWallVisible=true +height=36 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=112 + +[9-slope@-3,148|11] +grade=4 +gradient=Vertical +height=57 +reversed=false +stuckOnGround=false +width=191 + +[9-slope@-5,92|1] +grade=4 +gradient=Vertical +height=45 +reversed=false +stuckOnGround=false +width=232 + +[9-slope@103,215|5] +grade=4 +gradient=Vertical +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@143,214|6] +grade=4 +gradient=Opposite Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@144,254|2] +grade=4 +gradient=Horizontal +height=136 +reversed=true +stuckOnGround=false +width=42 + +[9-slope@189,147|10] +grade=4 +gradient=Opposite Diagonal +height=63 +reversed=false +stuckOnGround=false +width=70 + +[9-slope@226,93|7] +grade=4 +gradient=Opposite Diagonal +height=44 +reversed=false +stuckOnGround=false +width=184 + +[9-slope@252,171|9] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-slope@291,172|8] +grade=4 +gradient=Vertical +height=41 +reversed=false +stuckOnGround=false +width=117 + +[9-slope@333,9|29] +grade=4 +gradient=Elliptic +height=59 +reversed=true +stuckOnGround=true +width=59 + +[9-slope@62,254|3] +grade=4 +gradient=Opposite Diagonal +height=133 +reversed=true +stuckOnGround=false +width=46 + +[9-slope@63,214|4] +grade=4 +gradient=Diagonal +height=40 +reversed=false +stuckOnGround=false +width=40 + +[9-wall@0,0|30] +endPoint=138,14 +startPoint=4,65 + +[9-wall@0,0|31] +endPoint=138,14 +startPoint=184,29 + +[9-wall@0,0|32] +endPoint=264,29 +startPoint=361,8 + +[9-wall@0,0|33] +endPoint=361,8 +startPoint=393,22 + +[9-wall@0,0|34] +endPoint=393,22 +startPoint=393,129 + +[9-wall@0,0|35] +endPoint=393,129 +startPoint=393,171 + +[9-wall@0,0|36] +endPoint=393,171 +startPoint=392,214 + +[9-wall@0,0|37] +endPoint=337,305 +startPoint=392,214 + +[9-wall@0,0|38] +endPoint=337,305 +startPoint=298,305 + +[9-wall@0,0|39] +endPoint=4,208 +startPoint=4,65 + +[9-wall@0,0|40] +endPoint=4,208 +startPoint=46,394 + +[9-wall@0,0|41] +endPoint=46,394 +startPoint=108,385 + +[9-wall@0,0|42] +endPoint=108,385 +startPoint=124,371 + +[9-wall@0,0|43] +endPoint=124,371 +startPoint=142,370 + +[9-wall@0,0|44] +endPoint=190,371 +startPoint=218,305 diff --git a/kolf/editor.cpp b/kolf/editor.cpp new file mode 100644 index 00000000..4cf6e61a --- /dev/null +++ b/kolf/editor.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include +#include +#include +#include + +#include "editor.h" +#include "game.h" + +Editor::Editor(ObjectList *list, QWidget *parent, const char *name) + : QWidget(parent, name) +{ + this->list = list; + config = 0; + + hlayout = new QHBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + + QVBoxLayout *vlayout = new QVBoxLayout(hlayout, KDialog::spacingHint()); + vlayout->addWidget(new QLabel(i18n("Add object:"), this)); + listbox = new KListBox(this, "Listbox"); + vlayout->addWidget(listbox); + hlayout->setStretchFactor(vlayout, 2); + + QStringList items; + Object *obj = 0; + for (obj = list->first(); obj; obj = list->next()) + items.append(obj->name()); + + listbox->insertStringList(items); + + connect(listbox, SIGNAL(executed(QListBoxItem *)), SLOT(listboxExecuted(QListBoxItem *))); +} + +void Editor::listboxExecuted(QListBoxItem * /*item*/) +{ + int curItem = listbox->currentItem(); + if (curItem < 0) + return; + + emit addNewItem(list->at(curItem)); +} + +void Editor::setItem(CanvasItem *item) +{ + delete config; + config = item->config(this); + if (!config) + return; + config->ctorDone(); + hlayout->addWidget(config); + hlayout->setStretchFactor(config, 2); + config->setFrameStyle(QFrame::Box | QFrame::Raised); + config->setLineWidth(1); + config->show(); + connect(config, SIGNAL(modified()), this, SIGNAL(changed())); +} + +#include "editor.moc" diff --git a/kolf/editor.h b/kolf/editor.h new file mode 100644 index 00000000..4d5a3ac5 --- /dev/null +++ b/kolf/editor.h @@ -0,0 +1,37 @@ +#ifndef EDITOR_H_INCLUDED +#define EDITOR_H_INCLUDED + +#include + +#include "game.h" + +class KListBox; +class QHBoxLayout; +class QListBoxItem; +class Config; + +class Editor : public QWidget +{ + Q_OBJECT + +public: + Editor(ObjectList *list, QWidget * = 0, const char * = 0); + +signals: + void changed(); + void addNewItem(Object *); + +public slots: + void setItem(CanvasItem *); + +private slots: + void listboxExecuted(QListBoxItem *); + +private: + ObjectList *list; + QHBoxLayout *hlayout; + KListBox *listbox; + Config *config; +}; + +#endif diff --git a/kolf/floater.cpp b/kolf/floater.cpp new file mode 100644 index 00000000..73612bc8 --- /dev/null +++ b/kolf/floater.cpp @@ -0,0 +1,277 @@ +#include +#include + +#include + +#include "floater.h" + +void FloaterGuide::aboutToDelete() +{ + game->removeItem(floater); + aboutToDie(); + floater->aboutToDie(); + delete floater; + almostDead = true; +} + +void FloaterGuide::aboutToDie() +{ + if (almostDead) + return; + else + Wall::aboutToDie(); +} + +void FloaterGuide::moveBy(double dx, double dy) +{ + Wall::moveBy(dx, dy); + if (floater) + floater->reset(); +} + +void FloaterGuide::setPoints(int xa, int ya, int xb, int yb) +{ + if (fabs(xa - xb) > 0 || fabs(ya - yb) > 0) + { + Wall::setPoints(xa, ya, xb, yb); + if (floater) + floater->reset(); + } +} + +Config *FloaterGuide::config(QWidget *parent) +{ + return floater->config(parent); +} + +///////////////////////// + +Floater::Floater(QRect rect, QCanvas *canvas) + : Bridge(rect, canvas), speedfactor(16) +{ + wall = 0; + setEnabled(true); + noUpdateZ = false; + haventMoved = true; + wall = new FloaterGuide(this, canvas); + wall->setPoints(100, 100, 200, 200); + wall->setPen(QPen(wall->pen().color().light(), wall->pen().width() - 1)); + move(wall->endPoint().x(), wall->endPoint().y()); + + setTopWallVisible(false); + setBotWallVisible(false); + setLeftWallVisible(false); + setRightWallVisible(false); + + newSize(width(), height()); + moveBy(0, 0); + setSpeed(0); + + editModeChanged(false); + reset(); +} + +void Floater::setGame(KolfGame *game) +{ + Bridge::setGame(game); + + wall->setGame(game); +} + +void Floater::editModeChanged(bool changed) +{ + if (changed) + wall->editModeChanged(true); + Bridge::editModeChanged(changed); + wall->setVisible(changed); +} + +void Floater::advance(int phase) +{ + if (!isEnabled()) + return; + + Bridge::advance(phase); + + if (phase == 1 && (xVelocity() || yVelocity())) + { + if (Vector(origin, QPoint(x(), y())).magnitude() > vector.magnitude()) + { + vector.setDirection(vector.direction() + M_PI); + origin = (origin == wall->startPoint()? wall->endPoint() : wall->startPoint()); + + setVelocity(-xVelocity(), -yVelocity()); + } + } +} + +void Floater::reset() +{ + QPoint start = wall->startPoint() + QPoint(wall->x(), wall->y()); + QPoint end = wall->endPoint() + QPoint(wall->x(), wall->y()); + + vector = Vector(end, start); + origin = end; + + move(origin.x(), origin.y()); + setSpeed(speed); +} + +QPtrList Floater::moveableItems() const +{ + QPtrList ret(wall->moveableItems()); + ret.append(wall); + ret.append(point); + return ret; +} + +void Floater::aboutToDie() +{ + if (wall) + wall->setVisible(false); + Bridge::aboutToDie(); + setEnabled(false); +} + +void Floater::setSpeed(int news) +{ + if (!wall || news < 0) + return; + speed = news; + + if (news == 0) + { + setVelocity(0, 0); + return; + } + + const double factor = (double)speed / 3.5; + setVelocity(-cos(vector.direction()) * factor, -sin(vector.direction()) * factor); +} + +void Floater::aboutToSave() +{ + setVelocity(0, 0); + noUpdateZ = true; + move(wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y()); + noUpdateZ = false; +} + +void Floater::savingDone() +{ + setSpeed(speed); +} + +void Floater::moveBy(double dx, double dy) +{ + if (!isEnabled()) + return; + + QCanvasItemList l = collisions(false); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + CanvasItem *item = dynamic_cast(*it); + + if (!noUpdateZ && item && item->canBeMovedByOthers()) + item->updateZ(this); + + if ((*it)->z() >= z()) + { + if (item && item->canBeMovedByOthers() && collidesWith(*it)) + { + if ((*it)->rtti() == Rtti_Ball) + { + //((Ball *)(*it))->setState(Rolling); + (*it)->moveBy(dx, dy); + if (game && game->hasFocus() && !game->isEditing() && game->curBall() == (Ball *)(*it)) + game->ballMoved(); + } + else if ((*it)->rtti() != Rtti_Putter) + (*it)->moveBy(dx, dy); + } + } + } + + point->dontMove(); + point->move(x() + width(), y() + height()); + + // this call must come after we have tested for collisions, otherwise we skip them when saving! + // that's a bad thing + QCanvasRectangle::moveBy(dx, dy); + + // because we don't do Bridge::moveBy(); + topWall->move(x(), y()); + botWall->move(x(), y() - 1); + leftWall->move(x(), y()); + rightWall->move(x(), y()); + + if (game && game->isEditing()) + game->updateHighlighter(); +} + +void Floater::saveState(StateDB *db) +{ + db->setPoint(QPoint(x(), y())); +} + +void Floater::loadState(StateDB *db) +{ + const QPoint moveTo = db->point(); + move(moveTo.x(), moveTo.y()); +} + +void Floater::save(KConfig *cfg) +{ + cfg->writeEntry("speed", speed); + cfg->writeEntry("startPoint", QPoint(wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y())); + cfg->writeEntry("endPoint", QPoint(wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y())); + + doSave(cfg); +} + +void Floater::load(KConfig *cfg) +{ + move(firstPoint.x(), firstPoint.y()); + + QPoint start(wall->startPoint() + QPoint(wall->x(), wall->y())); + start = cfg->readPointEntry("startPoint", &start); + QPoint end(wall->endPoint() + QPoint(wall->x(), wall->y())); + end = cfg->readPointEntry("endPoint", &end); + wall->setPoints(start.x(), start.y(), end.x(), end.y()); + wall->move(0, 0); + + setSpeed(cfg->readNumEntry("speed", -1)); + + doLoad(cfg); + reset(); +} + +void Floater::firstMove(int x, int y) +{ + firstPoint = QPoint(x, y); +} + +///////////////////////// + +FloaterConfig::FloaterConfig(Floater *floater, QWidget *parent) + : BridgeConfig(floater, parent) +{ + this->floater = floater; + m_vlayout->addStretch(); + + m_vlayout->addWidget(new QLabel(i18n("Moving speed"), this)); + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Slow"), this)); + QSlider *slider = new QSlider(0, 20, 2, floater->curSpeed(), Qt::Horizontal, this); + hlayout->addWidget(slider); + hlayout->addWidget(new QLabel(i18n("Fast"), this)); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(speedChanged(int))); +} + +void FloaterConfig::speedChanged(int news) +{ + floater->setSpeed(news); + changed(); +} + +#include "floater.moc" diff --git a/kolf/floater.h b/kolf/floater.h new file mode 100644 index 00000000..486f3bc0 --- /dev/null +++ b/kolf/floater.h @@ -0,0 +1,81 @@ +#ifndef FLOATER_H +#define FLOATER_H + +#include "game.h" + +class Floater; +class FloaterConfig : public BridgeConfig +{ + Q_OBJECT + +public: + FloaterConfig(Floater *floater, QWidget *parent); + +private slots: + void speedChanged(int news); + +private: + Floater *floater; +}; + +class FloaterGuide : public Wall +{ +public: + FloaterGuide(Floater *floater, QCanvas *canvas) : Wall(canvas) { this->floater = floater; almostDead = false; } + virtual void setPoints(int xa, int ya, int xb, int yb); + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent); + virtual void aboutToDelete(); + virtual void aboutToDie(); + +private: + Floater *floater; + bool almostDead; +}; + +class Floater : public Bridge +{ +public: + Floater(QRect rect, QCanvas *canvas); + virtual bool collision(Ball *ball, long int id) { Bridge::collision(ball, id); return false; } + virtual void saveState(StateDB *db); + virtual void loadState(StateDB *db); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual bool loadLast() const { return true; } + virtual void firstMove(int x, int y); + virtual void aboutToSave(); + virtual void aboutToDie(); + virtual void savingDone(); + virtual void setGame(KolfGame *game); + virtual void editModeChanged(bool changed); + virtual bool moveable() const { return false; } + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent) { return new FloaterConfig(this, parent); } + virtual QPtrList moveableItems() const; + virtual void advance(int phase); + void setSpeed(int news); + int curSpeed() const { return speed; } + + // called by floaterguide when changed; + void reset(); + +private: + int speedfactor; + int speed; + FloaterGuide *wall; + QPoint origin; + Vector vector; + bool noUpdateZ; + bool haventMoved; + QPoint firstPoint; +}; + +class FloaterObj : public Object +{ +public: + FloaterObj() { m_name = i18n("Floater"); m__name = "floater"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Floater(QRect(0, 0, 80, 40), canvas); } +}; + +#endif diff --git a/kolf/game.cpp b/kolf/game.cpp new file mode 100644 index 00000000..55b324ce --- /dev/null +++ b/kolf/game.cpp @@ -0,0 +1,4302 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kcomboboxdialog.h" +#include "kvolumecontrol.h" +#include "vector.h" +#include "game.h" + + +inline QString makeGroup(int id, int hole, QString name, int x, int y) +{ + return QString("%1-%2@%3,%4|%5").arg(hole).arg(name).arg(x).arg(y).arg(id); +} + +inline QString makeStateGroup(int id, const QString &name) +{ + return QString("%1|%2").arg(name).arg(id); +} + +///////////////////////// + +RectPoint::RectPoint(QColor color, RectItem *rect, QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + setZ(9999); + setSize(10, 10); + this->rect = rect; + setBrush(QBrush(color)); + setSizeFactor(1.0); + dontmove = false; +} + +void RectPoint::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + + if (dontmove) + { + dontmove = false; + return; + } + + QCanvasItem *qitem = dynamic_cast(rect); + if (!qitem) + return; + + double nw = m_sizeFactor * fabs(x() - qitem->x()); + double nh = m_sizeFactor * fabs(y() - qitem->y()); + if (nw <= 0 || nh <= 0) + return; + + rect->newSize(nw, nh); +} + +Config *RectPoint::config(QWidget *parent) +{ + CanvasItem *citem = dynamic_cast(rect); + if (citem) + return citem->config(parent); + else + return CanvasItem::config(parent); +} + +///////////////////////// + +Arrow::Arrow(QCanvas *canvas) + : QCanvasLine(canvas) +{ + line1 = new QCanvasLine(canvas); + line2 = new QCanvasLine(canvas); + + m_angle = 0; + m_length = 20; + m_reversed = false; + + setPen(black); + + updateSelf(); + setVisible(false); +} + +void Arrow::setPen(QPen p) +{ + QCanvasLine::setPen(p); + line1->setPen(p); + line2->setPen(p); +} + +void Arrow::setZ(double newz) +{ + QCanvasLine::setZ(newz); + line1->setZ(newz); + line2->setZ(newz); +} + +void Arrow::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + line1->setVisible(yes); + line2->setVisible(yes); +} + +void Arrow::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + line1->moveBy(dx, dy); + line2->moveBy(dx, dy); +} + +void Arrow::aboutToDie() +{ + delete line1; + delete line2; +} + +void Arrow::updateSelf() +{ + QPoint start = startPoint(); + QPoint end(m_length * cos(m_angle), m_length * sin(m_angle)); + + if (m_reversed) + { + QPoint tmp(start); + start = end; + end = tmp; + } + + setPoints(start.x(), start.y(), end.x(), end.y()); + + const double lineLen = m_length / 2; + + const double angle1 = m_angle - M_PI / 2 - 1; + line1->move(end.x() + x(), end.y() + y()); + start = end; + end = QPoint(lineLen * cos(angle1), lineLen * sin(angle1)); + line1->setPoints(0, 0, end.x(), end.y()); + + const double angle2 = m_angle + M_PI / 2 + 1; + line2->move(start.x() + x(), start.y() + y()); + end = QPoint(lineLen * cos(angle2), lineLen * sin(angle2)); + line2->setPoints(0, 0, end.x(), end.y()); +} + +///////////////////////// + +BridgeConfig::BridgeConfig(Bridge *bridge, QWidget *parent) + : Config(parent) +{ + this->bridge = bridge; + + m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint()); + QGridLayout *layout = new QGridLayout(m_vlayout, 2, 3, spacingHint()); + layout->addWidget(new QLabel(i18n("Walls on:"), this), 0, 0); + top = new QCheckBox(i18n("&Top"), this); + layout->addWidget(top, 0, 1); + connect(top, SIGNAL(toggled(bool)), this, SLOT(topWallChanged(bool))); + top->setChecked(bridge->topWallVisible()); + bot = new QCheckBox(i18n("&Bottom"), this); + layout->addWidget(bot, 1, 1); + connect(bot, SIGNAL(toggled(bool)), this, SLOT(botWallChanged(bool))); + bot->setChecked(bridge->botWallVisible()); + left = new QCheckBox(i18n("&Left"), this); + layout->addWidget(left, 1, 0); + connect(left, SIGNAL(toggled(bool)), this, SLOT(leftWallChanged(bool))); + left->setChecked(bridge->leftWallVisible()); + right = new QCheckBox(i18n("&Right"), this); + layout->addWidget(right, 1, 2); + connect(right, SIGNAL(toggled(bool)), this, SLOT(rightWallChanged(bool))); + right->setChecked(bridge->rightWallVisible()); +} + +void BridgeConfig::topWallChanged(bool yes) +{ + bridge->setTopWallVisible(yes); + changed(); +} + +void BridgeConfig::botWallChanged(bool yes) +{ + bridge->setBotWallVisible(yes); + changed(); +} + +void BridgeConfig::leftWallChanged(bool yes) +{ + bridge->setLeftWallVisible(yes); + changed(); +} + +void BridgeConfig::rightWallChanged(bool yes) +{ + bridge->setRightWallVisible(yes); + changed(); +} + +///////////////////////// + +Bridge::Bridge(QRect rect, QCanvas *canvas) + : QCanvasRectangle(rect, canvas) +{ + QColor color("#92772D"); + setBrush(QBrush(color)); + setPen(NoPen); + setZ(998); + + topWall = new Wall(canvas); + topWall->setAlwaysShow(true); + botWall = new Wall(canvas); + botWall->setAlwaysShow(true); + leftWall = new Wall(canvas); + leftWall->setAlwaysShow(true); + rightWall = new Wall(canvas); + rightWall->setAlwaysShow(true); + + setWallZ(z() + 0.01); + setWallColor(color); + + topWall->setVisible(false); + botWall->setVisible(false); + leftWall->setVisible(false); + rightWall->setVisible(false); + + point = new RectPoint(color, this, canvas); + editModeChanged(false); + + newSize(width(), height()); +} + +bool Bridge::collision(Ball *ball, long int /*id*/) +{ + ball->setFrictionMultiplier(.63); + return false; +} + +void Bridge::setWallZ(double newz) +{ + topWall->setZ(newz); + botWall->setZ(newz); + leftWall->setZ(newz); + rightWall->setZ(newz); +} + +void Bridge::setGame(KolfGame *game) +{ + CanvasItem::setGame(game); + topWall->setGame(game); + botWall->setGame(game); + leftWall->setGame(game); + rightWall->setGame(game); +} + +void Bridge::setWallColor(QColor color) +{ + topWall->setPen(QPen(color.dark(), 3)); + botWall->setPen(topWall->pen()); + leftWall->setPen(topWall->pen()); + rightWall->setPen(topWall->pen()); +} + +void Bridge::aboutToDie() +{ + delete point; + topWall->aboutToDie(); + delete topWall; + botWall->aboutToDie(); + delete botWall; + leftWall->aboutToDie(); + delete leftWall; + rightWall->aboutToDie(); + delete rightWall; +} + +void Bridge::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Bridge::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width(), y() + height()); + + topWall->move(x(), y()); + botWall->move(x(), y() - 1); + leftWall->move(x(), y()); + rightWall->move(x(), y()); + + QCanvasItemList list = collisions(true); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + CanvasItem *citem = dynamic_cast(*it); + if (citem) + citem->updateZ(); + } +} + +void Bridge::load(KConfig *cfg) +{ + doLoad(cfg); +} + +void Bridge::doLoad(KConfig *cfg) +{ + newSize(cfg->readNumEntry("width", width()), cfg->readNumEntry("height", height())); + setTopWallVisible(cfg->readBoolEntry("topWallVisible", topWallVisible())); + setBotWallVisible(cfg->readBoolEntry("botWallVisible", botWallVisible())); + setLeftWallVisible(cfg->readBoolEntry("leftWallVisible", leftWallVisible())); + setRightWallVisible(cfg->readBoolEntry("rightWallVisible", rightWallVisible())); +} + +void Bridge::save(KConfig *cfg) +{ + doSave(cfg); +} + +void Bridge::doSave(KConfig *cfg) +{ + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); + cfg->writeEntry("topWallVisible", topWallVisible()); + cfg->writeEntry("botWallVisible", botWallVisible()); + cfg->writeEntry("leftWallVisible", leftWallVisible()); + cfg->writeEntry("rightWallVisible", rightWallVisible()); +} + +QPtrList Bridge::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Bridge::newSize(int width, int height) +{ + setSize(width, height); +} + +void Bridge::setSize(int width, int height) +{ + QCanvasRectangle::setSize(width, height); + + topWall->setPoints(0, 0, width, 0); + botWall->setPoints(0, height, width, height); + leftWall->setPoints(0, 0, 0, height); + rightWall->setPoints(width, 0, width, height); + + moveBy(0, 0); +} + +///////////////////////// + +WindmillConfig::WindmillConfig(Windmill *windmill, QWidget *parent) + : BridgeConfig(windmill, parent) +{ + this->windmill = windmill; + m_vlayout->addStretch(); + + QCheckBox *check = new QCheckBox(i18n("Windmill on bottom"), this); + check->setChecked(windmill->bottom()); + connect(check, SIGNAL(toggled(bool)), this, SLOT(endChanged(bool))); + m_vlayout->addWidget(check); + + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Slow"), this)); + QSlider *slider = new QSlider(1, 10, 1, windmill->curSpeed(), Qt::Horizontal, this); + hlayout->addWidget(slider); + hlayout->addWidget(new QLabel(i18n("Fast"), this)); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(speedChanged(int))); + + endChanged(check->isChecked()); +} + +void WindmillConfig::speedChanged(int news) +{ + windmill->setSpeed(news); + changed(); +} + +void WindmillConfig::endChanged(bool bottom) +{ + windmill->setBottom(bottom); + changed(); + + bot->setEnabled(!bottom); + if (startedUp) + { + bot->setChecked(!bottom); + botWallChanged(bot->isChecked()); + } + top->setEnabled(bottom); + if (startedUp) + { + top->setChecked(bottom); + topWallChanged(top->isChecked()); + } +} + +///////////////////////// + +Windmill::Windmill(QRect rect, QCanvas *canvas) + : Bridge(rect, canvas), speedfactor(16), m_bottom(true) +{ + guard = new WindmillGuard(canvas); + guard->setPen(QPen(black, 5)); + guard->setVisible(true); + guard->setAlwaysShow(true); + setSpeed(5); + guard->setZ(wallZ() + .1); + + left = new Wall(canvas); + left->setPen(wallPen()); + left->setAlwaysShow(true); + right = new Wall(canvas); + right->setPen(wallPen()); + right->setAlwaysShow(true); + left->setZ(wallZ()); + right->setZ(wallZ()); + left->setVisible(true); + right->setVisible(true); + + setTopWallVisible(false); + setBotWallVisible(false); + setLeftWallVisible(true); + setRightWallVisible(true); + + newSize(width(), height()); + moveBy(0, 0); +} + +void Windmill::aboutToDie() +{ + Bridge::aboutToDie(); + guard->aboutToDie(); + delete guard; + left->aboutToDie(); + delete left; + right->aboutToDie(); + delete right; +} + +void Windmill::setSpeed(int news) +{ + if (news < 0) + return; + speed = news; + guard->setXVelocity(((double)news / (double)3) * (guard->xVelocity() > 0? 1 : -1)); +} + +void Windmill::setGame(KolfGame *game) +{ + Bridge::setGame(game); + guard->setGame(game); + left->setGame(game); + right->setGame(game); +} + +void Windmill::save(KConfig *cfg) +{ + cfg->writeEntry("speed", speed); + cfg->writeEntry("bottom", m_bottom); + + doSave(cfg); +} + +void Windmill::load(KConfig *cfg) +{ + setSpeed(cfg->readNumEntry("speed", -1)); + + doLoad(cfg); + + left->editModeChanged(false); + right->editModeChanged(false); + guard->editModeChanged(false); + + setBottom(cfg->readBoolEntry("bottom", true)); +} + +void Windmill::moveBy(double dx, double dy) +{ + Bridge::moveBy(dx, dy); + + left->move(x(), y()); + right->move(x(), y()); + + guard->moveBy(dx, dy); + guard->setBetween(x(), x() + width()); + + update(); +} + +void Windmill::setSize(int width, int height) +{ + newSize(width, height); +} + +void Windmill::setBottom(bool yes) +{ + m_bottom = yes; + newSize(width(), height()); +} + +void Windmill::newSize(int width, int height) +{ + Bridge::newSize(width, height); + + const int indent = width / 4; + + double indentY = m_bottom? height : 0; + left->setPoints(0, indentY, indent, indentY); + right->setPoints(width - indent, indentY, width, indentY); + + guard->setBetween(x(), x() + width); + double guardY = m_bottom? height + 4 : -4; + guard->setPoints(0, guardY, (double)indent / (double)1.07 - 2, guardY); +} + +///////////////////////// + +void WindmillGuard::advance(int phase) +{ + Wall::advance(phase); + + if (phase == 1) + { + if (x() + startPoint().x() <= min) + setXVelocity(fabs(xVelocity())); + else if (x() + endPoint().x() >= max) + setXVelocity(-fabs(xVelocity())); + } +} + +///////////////////////// + +Sign::Sign(QCanvas *canvas) + : Bridge(QRect(0, 0, 110, 40), canvas) +{ + setZ(998.8); + m_text = m_untranslatedText = i18n("New Text"); + setBrush(QBrush(white)); + setWallColor(black); + setWallZ(z() + .01); + + setTopWallVisible(true); + setBotWallVisible(true); + setLeftWallVisible(true); + setRightWallVisible(true); +} + +void Sign::load(KConfig *cfg) +{ + m_text = cfg->readEntry("Comment", m_text); + m_untranslatedText = cfg->readEntryUntranslated("Comment", m_untranslatedText); + + doLoad(cfg); +} + +void Sign::save(KConfig *cfg) +{ + cfg->writeEntry("Comment", m_untranslatedText); + + doSave(cfg); +} + +void Sign::setText(const QString &text) +{ + m_text = text; + m_untranslatedText = text; + + update(); +} + +void Sign::draw(QPainter &painter) +{ + Bridge::draw(painter); + + painter.setPen(QPen(black, 1)); + QSimpleRichText txt(m_text, kapp->font()); + const int indent = wallPen().width() + 3; + txt.setWidth(width() - 2*indent); + QColorGroup colorGroup; + colorGroup.setColor(QColorGroup::Foreground, black); + colorGroup.setColor(QColorGroup::Text, black); + colorGroup.setColor(QColorGroup::Background, black); + colorGroup.setColor(QColorGroup::Base, black); + txt.draw(&painter, x() + indent, y(), QRect(x() + indent, y(), width() - indent, height() - indent), colorGroup); +} + +///////////////////////// + +SignConfig::SignConfig(Sign *sign, QWidget *parent) + : BridgeConfig(sign, parent) +{ + this->sign = sign; + m_vlayout->addStretch(); + m_vlayout->addWidget(new QLabel(i18n("Sign HTML:"), this)); + KLineEdit *name = new KLineEdit(sign->text(), this); + m_vlayout->addWidget(name); + connect(name, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &))); +} + +void SignConfig::textChanged(const QString &text) +{ + sign->setText(text); + changed(); +} + +///////////////////////// + +EllipseConfig::EllipseConfig(Ellipse *ellipse, QWidget *parent) + : Config(parent), slow1(0), fast1(0), slow2(0), fast2(0), slider1(0), slider2(0) +{ + this->ellipse = ellipse; + + m_vlayout = new QVBoxLayout(this, marginHint(), spacingHint()); + + QCheckBox *check = new QCheckBox(i18n("Enable show/hide"), this); + m_vlayout->addWidget(check); + connect(check, SIGNAL(toggled(bool)), this, SLOT(check1Changed(bool))); + check->setChecked(ellipse->changeEnabled()); + + QHBoxLayout *hlayout = new QHBoxLayout(m_vlayout, spacingHint()); + slow1 = new QLabel(i18n("Slow"), this); + hlayout->addWidget(slow1); + slider1 = new QSlider(1, 100, 5, 100 - ellipse->changeEvery(), Qt::Horizontal, this); + hlayout->addWidget(slider1); + fast1 = new QLabel(i18n("Fast"), this); + hlayout->addWidget(fast1); + + connect(slider1, SIGNAL(valueChanged(int)), this, SLOT(value1Changed(int))); + + check1Changed(ellipse->changeEnabled()); + + // TODO add slider2 and friends and make it possible for ellipses to grow and contract + + m_vlayout->addStretch(); +} + +void EllipseConfig::value1Changed(int news) +{ + ellipse->setChangeEvery(100 - news); + changed(); +} + +void EllipseConfig::value2Changed(int /*news*/) +{ + changed(); +} + +void EllipseConfig::check1Changed(bool on) +{ + ellipse->setChangeEnabled(on); + if (slider1) + slider1->setEnabled(on); + if (slow1) + slow1->setEnabled(on); + if (fast1) + fast1->setEnabled(on); + + changed(); +} + +void EllipseConfig::check2Changed(bool on) +{ + //ellipse->setChangeEnabled(on); + if (slider2) + slider2->setEnabled(on); + if (slow2) + slow2->setEnabled(on); + if (fast2) + fast2->setEnabled(on); + + changed(); +} + +///////////////////////// + +Ellipse::Ellipse(QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + savingDone(); + setChangeEnabled(false); + setChangeEvery(50); + count = 0; + setVisible(true); + + point = new RectPoint(black, this, canvas); + point->setSizeFactor(2.0); +} + +void Ellipse::aboutToDie() +{ + delete point; +} + +void Ellipse::setChangeEnabled(bool changeEnabled) +{ + m_changeEnabled = changeEnabled; + setAnimated(m_changeEnabled); + + if (!m_changeEnabled) + setVisible(true); +} + +QPtrList Ellipse::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Ellipse::newSize(int width, int height) +{ + QCanvasEllipse::setSize(width, height); +} + +void Ellipse::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width() / 2, y() + height() / 2); +} + +void Ellipse::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Ellipse::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + if (phase == 1 && m_changeEnabled && !dontHide) + { + if (count > (m_changeEvery + 10) * 1.8) + count = 0; + if (count == 0) + setVisible(!isVisible()); + + count++; + } +} + +void Ellipse::load(KConfig *cfg) +{ + setChangeEnabled(cfg->readBoolEntry("changeEnabled", changeEnabled())); + setChangeEvery(cfg->readNumEntry("changeEvery", changeEvery())); + double newWidth = width(), newHeight = height(); + newWidth = cfg->readNumEntry("width", newWidth); + newHeight = cfg->readNumEntry("height", newHeight); + newSize(newWidth, newHeight); +} + +void Ellipse::save(KConfig *cfg) +{ + cfg->writeEntry("changeEvery", changeEvery()); + cfg->writeEntry("changeEnabled", changeEnabled()); + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); +} + +Config *Ellipse::config(QWidget *parent) +{ + return new EllipseConfig(this, parent); +} + +void Ellipse::aboutToSave() +{ + setVisible(true); + dontHide = true; +} + +void Ellipse::savingDone() +{ + dontHide = false; +} + +///////////////////////// + +Puddle::Puddle(QCanvas *canvas) + : Ellipse(canvas) +{ + setSize(45, 30); + + QBrush brush; + QPixmap pic; + + if (!QPixmapCache::find("puddle", pic)) + { + pic.load(locate("appdata", "pics/puddle.png")); + QPixmapCache::insert("puddle", pic); + } + + brush.setPixmap(pic); + setBrush(brush); + + KPixmap pointPic(pic); + KPixmapEffect::intensity(pointPic, .45); + brush.setPixmap(pointPic); + point->setBrush(brush); + + setZ(-25); +} + +bool Puddle::collision(Ball *ball, long int /*id*/) +{ + if (ball->isVisible()) + { + QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas()); + i.setVisible(true); + + // is center of ball in? + if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/) + { + playSound("puddle"); + ball->setAddStroke(ball->addStroke() + 1); + ball->setPlaceOnGround(true); + ball->setVisible(false); + ball->setState(Stopped); + ball->setVelocity(0, 0); + if (game && game->curBall() == ball) + game->stoppedBall(); + } + else + return true; + } + + return false; +} + +///////////////////////// + +Sand::Sand(QCanvas *canvas) + : Ellipse(canvas) +{ + setSize(45, 40); + + QBrush brush; + QPixmap pic; + + if (!QPixmapCache::find("sand", pic)) + { + pic.load(locate("appdata", "pics/sand.png")); + QPixmapCache::insert("sand", pic); + } + + brush.setPixmap(pic); + setBrush(brush); + + KPixmap pointPic(pic); + KPixmapEffect::intensity(pointPic, .45); + brush.setPixmap(pointPic); + point->setBrush(brush); + + setZ(-26); +} + +bool Sand::collision(Ball *ball, long int /*id*/) +{ + QCanvasRectangle i(QRect(ball->x(), ball->y(), 1, 1), canvas()); + i.setVisible(true); + + // is center of ball in? + if (i.collidesWith(this)/* && ball->curVector().magnitude() < 4*/) + { + if (ball->curVector().magnitude() > 0) + ball->setFrictionMultiplier(7); + else + { + ball->setVelocity(0, 0); + ball->setState(Stopped); + } + } + + return true; +} + +///////////////////////// + +Putter::Putter(QCanvas *canvas) + : QCanvasLine(canvas) +{ + m_showGuideLine = true; + oneDegree = M_PI / 180; + len = 9; + angle = 0; + + guideLine = new QCanvasLine(canvas); + guideLine->setPen(QPen(white, 1, QPen::DotLine)); + guideLine->setZ(998.8); + + setPen(QPen(black, 4)); + putterWidth = 11; + maxAngle = 2 * M_PI; + + hideInfo(); + + // this also sets Z + resetAngles(); +} + +void Putter::showInfo() +{ + guideLine->setVisible(isVisible()); +} + +void Putter::hideInfo() +{ + guideLine->setVisible(m_showGuideLine? isVisible() : false); +} + +void Putter::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + guideLine->move(x(), y()); +} + +void Putter::setShowGuideLine(bool yes) +{ + m_showGuideLine = yes; + setVisible(isVisible()); +} + +void Putter::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + guideLine->setVisible(m_showGuideLine? yes : false); +} + +void Putter::setOrigin(int _x, int _y) +{ + setVisible(true); + move(_x, _y); + len = 9; + finishMe(); +} + +void Putter::setAngle(Ball *ball) +{ + angle = angleMap.contains(ball)? angleMap[ball] : 0; + finishMe(); +} + +void Putter::go(Direction d, Amount amount) +{ + double addition = (amount == Amount_More? 6 * oneDegree : amount == Amount_Less? .5 * oneDegree : 2 * oneDegree); + + switch (d) + { + case Forwards: + len -= 1; + guideLine->setVisible(false); + break; + case Backwards: + len += 1; + guideLine->setVisible(false); + break; + case D_Left: + angle += addition; + if (angle > maxAngle) + angle -= maxAngle; + break; + case D_Right: + angle -= addition; + if (angle < 0) + angle = maxAngle - fabs(angle); + break; + } + + finishMe(); +} + +void Putter::finishMe() +{ + midPoint.setX(cos(angle) * len); + midPoint.setY(-sin(angle) * len); + + QPoint start; + QPoint end; + + if (midPoint.y() || !midPoint.x()) + { + start.setX(midPoint.x() - putterWidth * sin(angle)); + start.setY(midPoint.y() - putterWidth * cos(angle)); + end.setX(midPoint.x() + putterWidth * sin(angle)); + end.setY(midPoint.y() + putterWidth * cos(angle)); + } + else + { + start.setX(midPoint.x()); + start.setY(midPoint.y() + putterWidth); + end.setY(midPoint.y() - putterWidth); + end.setX(midPoint.x()); + } + + guideLine->setPoints(midPoint.x(), midPoint.y(), -cos(angle) * len * 4, sin(angle) * len * 4); + + setPoints(start.x(), start.y(), end.x(), end.y()); +} + +///////////////////////// + +Bumper::Bumper(QCanvas *canvas) + : QCanvasEllipse(20, 20, canvas) +{ + setZ(-25); + + firstColor = QColor("#E74804"); + secondColor = firstColor.light(); + + count = 0; + setBrush(firstColor); + setAnimated(false); + + inside = new Inside(this, canvas); + inside->setBrush(firstColor.light(109)); + inside->setSize(width() / 2.6, height() / 2.6); + inside->show(); +} + +void Bumper::aboutToDie() +{ + delete inside; +} + +void Bumper::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + //const double insideLen = (double)(width() - inside->width()) / 2.0; + inside->move(x(), y()); +} + +void Bumper::editModeChanged(bool changed) +{ + inside->setVisible(!changed); +} + +void Bumper::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + if (phase == 1) + { + count++; + if (count > 2) + { + count = 0; + setBrush(firstColor); + update(); + setAnimated(false); + } + } +} + +bool Bumper::collision(Ball *ball, long int /*id*/) +{ + setBrush(secondColor); + + double speed = 1.8 + ball->curVector().magnitude() * .9; + if (speed > 8) + speed = 8; + + const QPoint start(x(), y()); + const QPoint end(ball->x(), ball->y()); + + Vector betweenVector(start, end); + betweenVector.setMagnitude(speed); + + // add some randomness so we don't go indefinetely + betweenVector.setDirection(betweenVector.direction() + deg2rad((kapp->random() % 3) - 1)); + + ball->setVector(betweenVector); + // for some reason, x is always switched... + ball->setXVelocity(-ball->xVelocity()); + ball->setState(Rolling); + + setAnimated(true); + + return true; +} + +///////////////////////// + +Hole::Hole(QColor color, QCanvas *canvas) + : QCanvasEllipse(15, 15, canvas) +{ + setZ(998.1); + setPen(black); + setBrush(color); +} + +bool Hole::collision(Ball *ball, long int /*id*/) +{ + bool wasCenter = false; + + switch (result(QPoint(ball->x(), ball->y()), ball->curVector().magnitude(), &wasCenter)) + { + case Result_Holed: + place(ball, wasCenter); + return false; + + default: + break; + } + + return true; +} + +HoleResult Hole::result(QPoint p, double s, bool * /*wasCenter*/) +{ + const double longestRadius = width() > height()? width() : height(); + if (s > longestRadius / 5.0) + return Result_Miss; + + QCanvasRectangle i(QRect(p, QSize(1, 1)), canvas()); + i.setVisible(true); + + // is center of ball in cup? + if (i.collidesWith(this)) + { + return Result_Holed; + } + else + return Result_Miss; +} + +///////////////////////// + +Cup::Cup(QCanvas *canvas) + : Hole(QColor("#808080"), canvas) +{ + if (!QPixmapCache::find("cup", pixmap)) + { + pixmap.load(locate("appdata", "pics/cup.png")); + QPixmapCache::insert("cup", pixmap); + } +} + +void Cup::draw(QPainter &p) +{ + p.drawPixmap(QPoint(x() - width() / 2, y() - height() / 2), pixmap); +} + +bool Cup::place(Ball *ball, bool /*wasCenter*/) +{ + ball->setState(Holed); + playSound("holed"); + + // the picture's center is a little different + ball->move(x() - 1, y()); + ball->setVelocity(0, 0); + if (game && game->curBall() == ball) + game->stoppedBall(); + return true; +} + +void Cup::save(KConfig *cfg) +{ + cfg->writeEntry("dummykey", true); +} + +///////////////////////// + +BlackHole::BlackHole(QCanvas *canvas) + : Hole(black, canvas), exitDeg(0) +{ + infoLine = 0; + m_minSpeed = 3.0; + m_maxSpeed = 5.0; + runs = 0; + + const QColor myColor((QRgb)(kapp->random() % 0x01000000)); + + outside = new QCanvasEllipse(canvas); + outside->setZ(z() - .001); + + outside->setBrush(QBrush(myColor)); + setBrush(black); + + exitItem = new BlackHoleExit(this, canvas); + exitItem->setPen(QPen(myColor, 6)); + exitItem->setX(300); + exitItem->setY(100); + + setSize(width(), width() / .8); + const float factor = 1.3; + outside->setSize(width() * factor, height() * factor); + outside->setVisible(true); + + moveBy(0, 0); + + finishMe(); +} + +void BlackHole::showInfo() +{ + delete infoLine; + infoLine = new QCanvasLine(canvas()); + infoLine->setVisible(true); + infoLine->setPen(QPen(exitItem->pen().color(), 2)); + infoLine->setZ(10000); + infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y()); + + exitItem->showInfo(); +} + +void BlackHole::hideInfo() +{ + delete infoLine; + infoLine = 0; + + exitItem->hideInfo(); +} + +void BlackHole::aboutToDie() +{ + Hole::aboutToDie(); + delete outside; + exitItem->aboutToDie(); + delete exitItem; +} + +void BlackHole::updateInfo() +{ + if (infoLine) + { + infoLine->setVisible(true); + infoLine->setPoints(x(), y(), exitItem->x(), exitItem->y()); + exitItem->showInfo(); + } +} + +void BlackHole::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + outside->move(x(), y()); + updateInfo(); +} + +void BlackHole::setExitDeg(int newdeg) +{ + exitDeg = newdeg; + if (game && game->isEditing() && game->curSelectedItem() == exitItem) + game->updateHighlighter(); + + exitItem->updateArrowAngle(); + finishMe(); +} + +QPtrList BlackHole::moveableItems() const +{ + QPtrList ret; + ret.append(exitItem); + return ret; +} + +BlackHoleTimer::BlackHoleTimer(Ball *ball, double speed, int msec) + : m_speed(speed), m_ball(ball) +{ + QTimer::singleShot(msec, this, SLOT(mySlot())); + QTimer::singleShot(msec / 2, this, SLOT(myMidSlot())); +} + +void BlackHoleTimer::mySlot() +{ + emit eject(m_ball, m_speed); + delete this; +} + +void BlackHoleTimer::myMidSlot() +{ + emit halfway(); +} + +bool BlackHole::place(Ball *ball, bool /*wasCenter*/) +{ + // most number is 10 + if (runs > 10 && game && game->isInPlay()) + return false; + + playSound("blackholeputin"); + + const double diff = (m_maxSpeed - m_minSpeed); + const double speed = m_minSpeed + ball->curVector().magnitude() * (diff / 3.75); + + ball->setVelocity(0, 0); + ball->setState(Stopped); + ball->setVisible(false); + ball->setForceStillGoing(true); + + double magnitude = Vector(QPoint(x(), y()), QPoint(exitItem->x(), exitItem->y())).magnitude(); + BlackHoleTimer *timer = new BlackHoleTimer(ball, speed, magnitude * 2.5 - speed * 35 + 500); + + connect(timer, SIGNAL(eject(Ball *, double)), this, SLOT(eject(Ball *, double))); + connect(timer, SIGNAL(halfway()), this, SLOT(halfway())); + + playSound("blackhole"); + return false; +} + +void BlackHole::eject(Ball *ball, double speed) +{ + ball->move(exitItem->x(), exitItem->y()); + + Vector v; + v.setMagnitude(10); + v.setDirection(deg2rad(exitDeg)); + ball->setVector(v); + + // advance ball 10 + ball->doAdvance(); + + v.setMagnitude(speed); + ball->setVector(v); + + ball->setForceStillGoing(false); + ball->setVisible(true); + ball->setState(Rolling); + + runs++; + + playSound("blackholeeject"); +} + +void BlackHole::halfway() +{ + playSound("blackhole"); +} + +void BlackHole::load(KConfig *cfg) +{ + QPoint exit = cfg->readPointEntry("exit", &exit); + exitItem->setX(exit.x()); + exitItem->setY(exit.y()); + exitDeg = cfg->readNumEntry("exitDeg", exitDeg); + m_minSpeed = cfg->readDoubleNumEntry("minspeed", m_minSpeed); + m_maxSpeed = cfg->readDoubleNumEntry("maxspeed", m_maxSpeed); + exitItem->updateArrowAngle(); + exitItem->updateArrowLength(); + + finishMe(); +} + +void BlackHole::finishMe() +{ + double radians = deg2rad(exitDeg); + QPoint midPoint(0, 0); + QPoint start; + QPoint end; + const int width = 15; + + if (midPoint.y() || !midPoint.x()) + { + start.setX(midPoint.x() - width*sin(radians)); + start.setY(midPoint.y() - width*cos(radians)); + end.setX(midPoint.x() + width*sin(radians)); + end.setY(midPoint.y() + width*cos(radians)); + } + else + { + start.setX(midPoint.x()); + start.setY(midPoint.y() + width); + end.setY(midPoint.y() - width); + end.setX(midPoint.x()); + } + + exitItem->setPoints(start.x(), start.y(), end.x(), end.y()); + exitItem->setVisible(true); +} + +void BlackHole::save(KConfig *cfg) +{ + cfg->writeEntry("exit", QPoint(exitItem->x(), exitItem->y())); + cfg->writeEntry("exitDeg", exitDeg); + cfg->writeEntry("minspeed", m_minSpeed); + cfg->writeEntry("maxspeed", m_maxSpeed); +} + +///////////////////////// + +BlackHoleExit::BlackHoleExit(BlackHole *blackHole, QCanvas *canvas) + : QCanvasLine(canvas) +{ + this->blackHole = blackHole; + arrow = new Arrow(canvas); + setZ(blackHole->z()); + arrow->setZ(z() - .00001); + updateArrowLength(); + arrow->setVisible(false); +} + +void BlackHoleExit::aboutToDie() +{ + arrow->aboutToDie(); + delete arrow; +} + +void BlackHoleExit::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + arrow->move(x(), y()); + blackHole->updateInfo(); +} + +void BlackHoleExit::setPen(QPen p) +{ + QCanvasLine::setPen(p); + arrow->setPen(QPen(p.color(), 1)); +} + +void BlackHoleExit::updateArrowAngle() +{ + // arrows work in a different angle system + arrow->setAngle(-deg2rad(blackHole->curExitDeg())); + arrow->updateSelf(); +} + +void BlackHoleExit::updateArrowLength() +{ + arrow->setLength(10.0 + 5.0 * (double)(blackHole->minSpeed() + blackHole->maxSpeed()) / 2.0); + arrow->updateSelf(); +} + +void BlackHoleExit::editModeChanged(bool editing) +{ + if (editing) + showInfo(); + else + hideInfo(); +} + +void BlackHoleExit::showInfo() +{ + arrow->setVisible(true); +} + +void BlackHoleExit::hideInfo() +{ + arrow->setVisible(false); +} + +Config *BlackHoleExit::config(QWidget *parent) +{ + return blackHole->config(parent); +} + +///////////////////////// + +BlackHoleConfig::BlackHoleConfig(BlackHole *blackHole, QWidget *parent) + : Config(parent) +{ + this->blackHole = blackHole; + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + layout->addWidget(new QLabel(i18n("Exiting ball angle:"), this)); + QSpinBox *deg = new QSpinBox(0, 359, 10, this); + deg->setSuffix(QString(" ") + i18n("degrees")); + deg->setValue(blackHole->curExitDeg()); + deg->setWrapping(true); + layout->addWidget(deg); + connect(deg, SIGNAL(valueChanged(int)), this, SLOT(degChanged(int))); + + layout->addStretch(); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Minimum exit speed:"), this)); + KDoubleNumInput *min = new KDoubleNumInput(this); + min->setRange(0, 8, 1, true); + hlayout->addWidget(min); + connect(min, SIGNAL(valueChanged(double)), this, SLOT(minChanged(double))); + min->setValue(blackHole->minSpeed()); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Maximum:"), this)); + KDoubleNumInput *max = new KDoubleNumInput(this); + max->setRange(1, 10, 1, true); + hlayout->addWidget(max); + connect(max, SIGNAL(valueChanged(double)), this, SLOT(maxChanged(double))); + max->setValue(blackHole->maxSpeed()); +} + +void BlackHoleConfig::degChanged(int newdeg) +{ + blackHole->setExitDeg(newdeg); + changed(); +} + +void BlackHoleConfig::minChanged(double news) +{ + blackHole->setMinSpeed(news); + changed(); +} + +void BlackHoleConfig::maxChanged(double news) +{ + blackHole->setMaxSpeed(news); + changed(); +} + +///////////////////////// + +WallPoint::WallPoint(bool start, Wall *wall, QCanvas *canvas) + : QCanvasEllipse(canvas) +{ + this->wall = wall; + this->start = start; + alwaysShow = false; + editing = false; + visible = true; + lastId = INT_MAX - 10; + dontmove = false; + + move(0, 0); + QPoint p; + if (start) + p = wall->startPoint(); + else + p = wall->endPoint(); + setX(p.x()); + setY(p.y()); +} + +void WallPoint::clean() +{ + int oldWidth = width(); + setSize(7, 7); + update(); + + QCanvasItem *onPoint = 0; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + if ((*it)->rtti() == rtti()) + onPoint = (*it); + + if (onPoint) + move(onPoint->x(), onPoint->y()); + + setSize(oldWidth, oldWidth); +} + +void WallPoint::moveBy(double dx, double dy) +{ + QCanvasEllipse::moveBy(dx, dy); + if (!editing) + updateVisible(); + + if (dontmove) + { + dontmove = false; + return; + } + + if (!wall) + return; + + if (start) + { + wall->setPoints(x(), y(), wall->endPoint().x() + wall->x(), wall->endPoint().y() + wall->y()); + } + else + { + wall->setPoints(wall->startPoint().x() + wall->x(), wall->startPoint().y() + wall->y(), x(), y()); + } + wall->move(0, 0); +} + +void WallPoint::updateVisible() +{ + if (!wall->isVisible()) + { + visible = false; + return; + } + + if (alwaysShow) + visible = true; + else + { + visible = true; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + if ((*it)->rtti() == rtti()) + visible = false; + } +} + +void WallPoint::editModeChanged(bool changed) +{ + editing = changed; + setVisible(true); + if (!editing) + updateVisible(); +} + +bool WallPoint::collision(Ball *ball, long int id) +{ + if (ball->curVector().magnitude() <= 0) + return false; + + long int tempLastId = lastId; + lastId = id; + QCanvasItemList l = collisions(true); + for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) + { + if ((*it)->rtti() == rtti()) + { + WallPoint *point = (WallPoint *)(*it); + point->lastId = id; + } + } + + //kdDebug(12007) << "WallPoint::collision id: " << id << ", tempLastId: " << tempLastId << endl; + Vector ballVector(ball->curVector()); + + //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl; + int allowableDifference = 1; + if (ballVector.magnitude() < .30) + allowableDifference = 8; + else if (ballVector.magnitude() < .50) + allowableDifference = 6; + else if (ballVector.magnitude() < .65) + allowableDifference = 4; + else if (ballVector.magnitude() < .95) + allowableDifference = 2; + + if (abs(id - tempLastId) <= allowableDifference) + { + //kdDebug(12007) << "WallPoint::collision - SKIP\n"; + } + else + { + bool weirdBounce = visible; + + QPoint relStart(start? wall->startPoint() : wall->endPoint()); + QPoint relEnd(start? wall->endPoint() : wall->startPoint()); + Vector wallVector(relStart, relEnd); + wallVector.setDirection(-wallVector.direction()); + + // find the angle between vectors, between 0 and PI + { + double difference = fabs(wallVector.direction() - ballVector.direction()); + while (difference > 2 * M_PI) + difference -= 2 * M_PI; + + if (difference < M_PI / 2 || difference > 3 * M_PI / 2) + weirdBounce = false; + } + + playSound("wall", ball->curVector().magnitude() / 10.0); + + ballVector /= wall->dampening; + const double ballAngle = ballVector.direction(); + + double wallAngle = wallVector.direction(); + + // opposite bounce, because we're the endpoint + if (weirdBounce) + wallAngle += M_PI / 2; + + const double collisionAngle = ballAngle - wallAngle; + const double leavingAngle = wallAngle - collisionAngle; + + ballVector.setDirection(leavingAngle); + ball->setVector(ballVector); + wall->lastId = id; + + //kdDebug(12007) << "WallPoint::collision - NOT skip, weirdBounce is " << weirdBounce << endl; + } // end if that skips + + wall->lastId = id; + return false; +} + +///////////////////////// + +Wall::Wall(QCanvas *canvas) + : QCanvasLine(canvas) +{ + editing = false; + lastId = INT_MAX - 10; + + dampening = 1.2; + + startItem = 0; + endItem = 0; + + moveBy(0, 0); + setZ(50); + + startItem = new WallPoint(true, this, canvas); + endItem = new WallPoint(false, this, canvas); + startItem->setVisible(true); + endItem->setVisible(true); + setPen(QPen(darkRed, 3)); + + setPoints(-15, 10, 15, -5); + + moveBy(0, 0); + + editModeChanged(false); +} + +void Wall::selectedItem(QCanvasItem *item) +{ + if (item->rtti() == Rtti_WallPoint) + { + WallPoint *wallPoint = dynamic_cast(item); + if (wallPoint) { + setPoints(startPoint().x(), startPoint().y(), wallPoint->x() - x(), wallPoint->y() - y()); + } + } +} + +void Wall::clean() +{ + startItem->clean(); + endItem->clean(); +} + +void Wall::setAlwaysShow(bool yes) +{ + startItem->setAlwaysShow(yes); + endItem->setAlwaysShow(yes); +} + +void Wall::setVisible(bool yes) +{ + QCanvasLine::setVisible(yes); + + startItem->setVisible(yes); + endItem->setVisible(yes); + startItem->updateVisible(); + endItem->updateVisible(); +} + +void Wall::setZ(double newz) +{ + QCanvasLine::setZ(newz); + if (startItem) + startItem->setZ(newz + .002); + if (endItem) + endItem->setZ(newz + .001); +} + +void Wall::setPen(QPen p) +{ + QCanvasLine::setPen(p); + + if (startItem) + startItem->setBrush(QBrush(p.color())); + if (endItem) + endItem->setBrush(QBrush(p.color())); +} + +void Wall::aboutToDie() +{ + delete startItem; + delete endItem; +} + +void Wall::setGame(KolfGame *game) +{ + CanvasItem::setGame(game); + startItem->setGame(game); + endItem->setGame(game); +} + +QPtrList Wall::moveableItems() const +{ + QPtrList ret; + ret.append(startItem); + ret.append(endItem); + return ret; +} + +void Wall::moveBy(double dx, double dy) +{ + QCanvasLine::moveBy(dx, dy); + + if (!startItem || !endItem) + return; + + startItem->dontMove(); + endItem->dontMove(); + startItem->move(startPoint().x() + x(), startPoint().y() + y()); + endItem->move(endPoint().x() + x(), endPoint().y() + y()); +} + +void Wall::setVelocity(double vx, double vy) +{ + QCanvasLine::setVelocity(vx, vy); + /* + startItem->setVelocity(vx, vy); + endItem->setVelocity(vx, vy); + */ +} + +QPointArray Wall::areaPoints() const +{ + // editing we want full width for easy moving + if (editing) + return QCanvasLine::areaPoints(); + + // lessen width, for QCanvasLine::areaPoints() likes + // to make lines _very_ fat :( + // from qcanvas.cpp, only the stuff for a line width of 1 taken + + // it's all squished because I don't want my + // line counts to count code I didn't write! + QPointArray p(4); const int xi = int(x()); const int yi = int(y()); const QPoint start = startPoint(); const QPoint end = endPoint(); const int x1 = start.x(); const int x2 = end.x(); const int y1 = start.y(); const int y2 = end.y(); const int dx = QABS(x1-x2); const int dy = QABS(y1-y2); if ( dx > dy ) { p[0] = QPoint(x1+xi,y1+yi-1); p[1] = QPoint(x2+xi,y2+yi-1); p[2] = QPoint(x2+xi,y2+yi+1); p[3] = QPoint(x1+xi,y1+yi+1); } else { p[0] = QPoint(x1+xi-1,y1+yi); p[1] = QPoint(x2+xi-1,y2+yi); p[2] = QPoint(x2+xi+1,y2+yi); p[3] = QPoint(x1+xi+1,y1+yi); } return p; +} + +void Wall::editModeChanged(bool changed) +{ + // make big for debugging? + const bool debugPoints = false; + + editing = changed; + + startItem->setZ(z() + .002); + endItem->setZ(z() + .001); + startItem->editModeChanged(editing); + endItem->editModeChanged(editing); + + int neww = 0; + if (changed || debugPoints) + neww = 10; + else + neww = pen().width(); + + startItem->setSize(neww, neww); + endItem->setSize(neww, neww); + + moveBy(0, 0); +} + +bool Wall::collision(Ball *ball, long int id) +{ + if (ball->curVector().magnitude() <= 0) + return false; + + long int tempLastId = lastId; + lastId = id; + startItem->lastId = id; + endItem->lastId = id; + + //kdDebug(12007) << "Wall::collision id: " << id << ", tempLastId: " << tempLastId << endl; + Vector ballVector(ball->curVector()); + + //kdDebug(12007) << "Wall::collision ball speed: " << ball->curVector().magnitude() << endl; + int allowableDifference = 1; + if (ballVector.magnitude() < .30) + allowableDifference = 8; + else if (ballVector.magnitude() < .50) + allowableDifference = 6; + else if (ballVector.magnitude() < .75) + allowableDifference = 4; + else if (ballVector.magnitude() < .95) + allowableDifference = 2; + //kdDebug(12007) << "Wall::collision allowableDifference is " << allowableDifference << endl; + if (abs(id - tempLastId) <= allowableDifference) + { + //kdDebug(12007) << "Wall::collision - SKIP\n"; + return false; + } + + playSound("wall", ball->curVector().magnitude() / 10.0); + + ballVector /= dampening; + const double ballAngle = ballVector.direction(); + + const double wallAngle = -Vector(startPoint(), endPoint()).direction(); + const double collisionAngle = ballAngle - wallAngle; + const double leavingAngle = wallAngle - collisionAngle; + + ballVector.setDirection(leavingAngle); + ball->setVector(ballVector); + + //kdDebug(12007) << "Wall::collision - NOT skip\n"; + return false; +} + +void Wall::load(KConfig *cfg) +{ + QPoint start(startPoint()); + start = cfg->readPointEntry("startPoint", &start); + QPoint end(endPoint()); + end = cfg->readPointEntry("endPoint", &end); + + setPoints(start.x(), start.y(), end.x(), end.y()); + + moveBy(0, 0); + startItem->move(start.x(), start.y()); + endItem->move(end.x(), end.y()); +} + +void Wall::save(KConfig *cfg) +{ + cfg->writeEntry("startPoint", QPoint(startItem->x(), startItem->y())); + cfg->writeEntry("endPoint", QPoint(endItem->x(), endItem->y())); +} + +///////////////////////// + +HoleConfig::HoleConfig(HoleInfo *holeInfo, QWidget *parent) + : Config(parent) +{ + this->holeInfo = holeInfo; + + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Course name: "), this)); + KLineEdit *nameEdit = new KLineEdit(holeInfo->untranslatedName(), this); + hlayout->addWidget(nameEdit); + connect(nameEdit, SIGNAL(textChanged(const QString &)), this, SLOT(nameChanged(const QString &))); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Course author: "), this)); + KLineEdit *authorEdit = new KLineEdit(holeInfo->author(), this); + hlayout->addWidget(authorEdit); + connect(authorEdit, SIGNAL(textChanged(const QString &)), this, SLOT(authorChanged(const QString &))); + + layout->addStretch(); + + hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Par:"), this)); + QSpinBox *par = new QSpinBox(1, 15, 1, this); + par->setValue(holeInfo->par()); + hlayout->addWidget(par); + connect(par, SIGNAL(valueChanged(int)), this, SLOT(parChanged(int))); + hlayout->addStretch(); + + hlayout->addWidget(new QLabel(i18n("Maximum:"), this)); + QSpinBox *maxstrokes = new QSpinBox(holeInfo->lowestMaxStrokes(), 30, 1, this); + QWhatsThis::add(maxstrokes, i18n("Maximum number of strokes player can take on this hole.")); + QToolTip::add(maxstrokes, i18n("Maximum number of strokes")); + maxstrokes->setSpecialValueText(i18n("Unlimited")); + maxstrokes->setValue(holeInfo->maxStrokes()); + hlayout->addWidget(maxstrokes); + connect(maxstrokes, SIGNAL(valueChanged(int)), this, SLOT(maxStrokesChanged(int))); + + QCheckBox *check = new QCheckBox(i18n("Show border walls"), this); + check->setChecked(holeInfo->borderWalls()); + layout->addWidget(check); + connect(check, SIGNAL(toggled(bool)), this, SLOT(borderWallsChanged(bool))); +} + +void HoleConfig::authorChanged(const QString &newauthor) +{ + holeInfo->setAuthor(newauthor); + changed(); +} + +void HoleConfig::nameChanged(const QString &newname) +{ + holeInfo->setName(newname); + holeInfo->setUntranslatedName(newname); + changed(); +} + +void HoleConfig::parChanged(int newpar) +{ + holeInfo->setPar(newpar); + changed(); +} + +void HoleConfig::maxStrokesChanged(int newms) +{ + holeInfo->setMaxStrokes(newms); + changed(); +} + +void HoleConfig::borderWallsChanged(bool yes) +{ + holeInfo->borderWallsChanged(yes); + changed(); +} + +///////////////////////// + +StrokeCircle::StrokeCircle(QCanvas *canvas) + : QCanvasItem(canvas) +{ + dvalue = 0; + dmax = 360; + iwidth = 100; + iheight = 100; + ithickness = 8; + setZ(10000); +} + +void StrokeCircle::setValue(double v) +{ + dvalue = v; + if (dvalue > dmax) + dvalue = dmax; + + update(); +} + +double StrokeCircle::value() +{ + return dvalue; +} + +bool StrokeCircle::collidesWith(const QCanvasItem*) const { return false; } + +bool StrokeCircle::collidesWith(const QCanvasSprite*, const QCanvasPolygonalItem*, const QCanvasRectangle*, const QCanvasEllipse*, const QCanvasText*) const { return false; } + +QRect StrokeCircle::boundingRect() const { return QRect(x(), y(), iwidth, iheight); } + +void StrokeCircle::setMaxValue(double m) +{ + dmax = m; + if (dvalue > dmax) + dvalue = dmax; + + update(); +} +void StrokeCircle::setSize(int w, int h) +{ + if (w > 0) + iwidth = w; + if (h > 0) + iheight = h; + + update(); +} +void StrokeCircle::setThickness(int t) +{ + if (t > 0) + ithickness = t; + + update(); +} + +int StrokeCircle::thickness() const +{ + return ithickness; +} + +int StrokeCircle::width() const +{ + return iwidth; +} + +int StrokeCircle::height() const +{ + return iheight; +} + +void StrokeCircle::draw(QPainter &p) +{ + int al = (int)((dvalue * 360 * 16) / dmax); + int length, deg; + if (al < 0) + { + deg = 270 * 16; + length = -al; + } + else if (al <= (270 * 16)) + { + deg = 270 * 16 - al; + length = al; + } + else + { + deg = (360 * 16) - (al - (270 * 16)); + length = al; + } + + p.setBrush(QBrush(black, Qt::NoBrush)); + p.setPen(QPen(white, ithickness / 2)); + p.drawEllipse(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness); + p.setPen(QPen(QColor((int)(0xff * dvalue) / dmax, 0, 0xff - (int)(0xff * dvalue) / dmax), ithickness)); + p.drawArc(x() + ithickness / 2, y() + ithickness / 2, iwidth - ithickness, iheight - ithickness, deg, length); + + p.setPen(QPen(white, 1)); + p.drawEllipse(x(), y(), iwidth, iheight); + p.drawEllipse(x() + ithickness, y() + ithickness, iwidth - ithickness * 2, iheight - ithickness * 2); + p.setPen(QPen(white, 3)); + p.drawLine(x() + iwidth / 2, y() + iheight - ithickness * 1.5, x() + iwidth / 2, y() + iheight); + p.drawLine(x() + iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 - iheight / 20); + p.drawLine(x() + iwidth - iwidth / 4 + iwidth / 20, y() + iheight - iheight / 4 + iheight / 20, x() + iwidth - iwidth / 4 - iwidth / 20, y() + iheight - iheight / 4 - iheight / 20); +} + +///////////////////////////////////////// + +KolfGame::KolfGame(ObjectList *obj, PlayerList *players, QString filename, QWidget *parent, const char *name ) + : QCanvasView(parent, name) +{ + // for mouse control + setMouseTracking(true); + viewport()->setMouseTracking(true); + setFrameShape(NoFrame); + + regAdv = false; + curHole = 0; // will get ++'d + cfg = 0; + setFilename(filename); + this->players = players; + this->obj = obj; + curPlayer = players->end(); + curPlayer--; // will get ++'d to end and sent back + // to beginning + paused = false; + modified = false; + inPlay = false; + putting = false; + stroking = false; + editing = false; + strict = false; + lastDelId = -1; + m_showInfo = false; + ballStateList.canUndo = false; + fastAdvancedExist = false; + soundDir = locate("appdata", "sounds/"); + dontAddStroke = false; + addingNewHole = false; + scoreboardHoles = 0; + infoShown = false; + m_useMouse = true; + m_useAdvancedPutting = false; + m_useAdvancedPutting = true; + m_sound = true; + m_ignoreEvents = false; + soundedOnce = false; + oldPlayObjects.setAutoDelete(true); + highestHole = 0; + recalcHighestHole = false; + + holeInfo.setGame(this); + holeInfo.setAuthor(i18n("Course Author")); + holeInfo.setName(i18n("Course Name")); + holeInfo.setUntranslatedName(i18n("Course Name")); + holeInfo.setMaxStrokes(10); + holeInfo.borderWallsChanged(true); + + // width and height are the width and height of the canvas + // in easy storage + width = 400; + height = 400; + grass = QColor("#35760D"); + + margin = 10; + + setFocusPolicy(QWidget::StrongFocus); + setFixedSize(width + 2 * margin, height + 2 * margin); + + setMargins(margin, margin, margin, margin); + + course = new QCanvas(this); + course->setBackgroundColor(white); + course->resize(width, height); + + QPixmap pic; + if (!QPixmapCache::find("grass", pic)) + { + pic.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", pic); + } + course->setBackgroundPixmap(pic); + + setCanvas(course); + move(0, 0); + adjustSize(); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setCanvas(course); + + // highlighter shows current item + highlighter = new QCanvasRectangle(course); + highlighter->setPen(QPen(yellow, 1)); + highlighter->setBrush(QBrush(NoBrush)); + highlighter->setVisible(false); + highlighter->setZ(10000); + + // shows some info about hole + infoText = new QCanvasText(course); + infoText->setText(""); + infoText->setColor(white); + QFont font = kapp->font(); + font.setPixelSize(12); + infoText->move(15, width/2); + infoText->setZ(10001); + infoText->setFont(font); + infoText->setVisible(false); + + // create the advanced putting indicator + strokeCircle = new StrokeCircle(course); + strokeCircle->move(width - 90, height - 90); + strokeCircle->setSize(80, 80); + strokeCircle->setThickness(8); + strokeCircle->setVisible(false); + strokeCircle->setValue(0); + strokeCircle->setMaxValue(360); + + // whiteBall marks the spot of the whole whilst editing + whiteBall = new Ball(course); + whiteBall->setGame(this); + whiteBall->setColor(white); + whiteBall->setVisible(false); + whiteBall->setDoDetect(false); + + int highestLog = 0; + + // if players have scores from loaded game, move to last hole + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if ((int)(*it).scores().count() > highestLog) + highestLog = (*it).scores().count(); + + (*it).ball()->setGame(this); + (*it).ball()->setAnimated(true); + } + + // here only for saved games + if (highestLog) + curHole = highestLog; + + putter = new Putter(course); + + // border walls: + + // horiz + addBorderWall(QPoint(margin, margin), QPoint(width - margin, margin)); + addBorderWall(QPoint(margin, height - margin - 1), QPoint(width - margin, height - margin - 1)); + + // vert + addBorderWall(QPoint(margin, margin), QPoint(margin, height - margin)); + addBorderWall(QPoint(width - margin - 1, margin), QPoint(width - margin - 1, height - margin)); + + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timerMsec = 300; + + fastTimer = new QTimer(this); + connect(fastTimer, SIGNAL(timeout()), this, SLOT(fastTimeout())); + fastTimerMsec = 11; + + autoSaveTimer = new QTimer(this); + connect(autoSaveTimer, SIGNAL(timeout()), this, SLOT(autoSaveTimeout())); + autoSaveMsec = 5 * 1000 * 60; // 5 min autosave + + // setUseAdvancedPutting() sets maxStrength! + setUseAdvancedPutting(false); + + putting = false; + putterTimer = new QTimer(this); + connect(putterTimer, SIGNAL(timeout()), this, SLOT(putterTimeout())); + putterTimerMsec = 20; +} + +void KolfGame::startFirstHole(int hole) +{ + if (curHole > 0) // if there was saved game, sync scoreboard + // with number of holes + { + for (; scoreboardHoles < curHole; ++scoreboardHoles) + { + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1)); + emit newHole(cfg->readNumEntry("par", 3)); + } + + // lets load all of the scores from saved game if there are any + for (int hole = 1; hole <= curHole; ++hole) + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + emit scoreChanged((*it).id(), hole, (*it).score(hole)); + } + + curHole = hole - 1; + + // this increments curHole, etc + recalcHighestHole = true; + startNextHole(); + paused = true; + unPause(); +} + +void KolfGame::setFilename(const QString &filename) +{ + this->filename = filename; + delete cfg; + cfg = new KConfig(filename, false, false); +} + +KolfGame::~KolfGame() +{ + oldPlayObjects.clear(); + delete cfg; +} + +void KolfGame::setModified(bool mod) +{ + modified = mod; + emit modifiedChanged(mod); +} + +void KolfGame::pause() +{ + if (paused) + { + // play along with people who call pause() again, instead of unPause() + unPause(); + return; + } + + paused = true; + timer->stop(); + fastTimer->stop(); + putterTimer->stop(); +} + +void KolfGame::unPause() +{ + if (!paused) + return; + + paused = false; + + timer->start(timerMsec); + fastTimer->start(fastTimerMsec); + + if (putting || stroking) + putterTimer->start(putterTimerMsec); +} + +void KolfGame::addBorderWall(QPoint start, QPoint end) +{ + Wall *wall = new Wall(course); + wall->setPoints(start.x(), start.y(), end.x(), end.y()); + wall->setVisible(true); + wall->setGame(this); + wall->setZ(998.7); + borderWalls.append(wall); +} + +void KolfGame::updateHighlighter() +{ + if (!selectedItem) + return; + QRect rect = selectedItem->boundingRect(); + highlighter->move(rect.x() + 1, rect.y() + 1); + highlighter->setSize(rect.width(), rect.height()); +} + +void KolfGame::handleMouseDoubleClickEvent(QMouseEvent *e) +{ + // allow two fast single clicks + handleMousePressEvent(e); +} + +void KolfGame::handleMousePressEvent(QMouseEvent *e) +{ + if (m_ignoreEvents) + return; + + if (editing) + { + if (inPlay) + return; + + storedMousePos = e->pos(); + + QCanvasItemList list = course->collisions(e->pos()); + if (list.first() == highlighter) + list.pop_front(); + + moving = false; + highlighter->setVisible(false); + selectedItem = 0; + movingItem = 0; + + if (list.count() < 1) + { + emit newSelectedItem(&holeInfo); + return; + } + // only items we keep track of + if ((!(items.containsRef(list.first()) || list.first() == whiteBall || extraMoveable.containsRef(list.first())))) + { + emit newSelectedItem(&holeInfo); + return; + } + + CanvasItem *citem = dynamic_cast(list.first()); + if (!citem || !citem->moveable()) + { + emit newSelectedItem(&holeInfo); + return; + } + + switch (e->button()) + { + // select AND move now :) + case LeftButton: + { + selectedItem = list.first(); + movingItem = selectedItem; + moving = true; + + if (citem->cornerResize()) + setCursor(KCursor::sizeFDiagCursor()); + else + setCursor(KCursor::sizeAllCursor()); + + emit newSelectedItem(citem); + highlighter->setVisible(true); + QRect rect = selectedItem->boundingRect(); + highlighter->move(rect.x() + 1, rect.y() + 1); + highlighter->setSize(rect.width(), rect.height()); + } + break; + + default: + break; + } + } + else + { + if (m_useMouse) + { + if (!inPlay && e->button() == LeftButton) + puttPress(); + else if (e->button() == RightButton) + toggleShowInfo(); + } + } + + setFocus(); +} + +QPoint KolfGame::viewportToViewport(const QPoint &p) +{ + // for some reason viewportToContents doesn't work right + return p - QPoint(margin, margin); +} + +// the following four functions are needed to handle both +// border presses and regular in-course presses + +void KolfGame::mouseReleaseEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonRelease, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseReleaseEvent(&fixedEvent); +} + +void KolfGame::mousePressEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonPress, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMousePressEvent(&fixedEvent); +} + +void KolfGame::mouseDoubleClickEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseButtonDblClick, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseDoubleClickEvent(&fixedEvent); +} + +void KolfGame::mouseMoveEvent(QMouseEvent * e) +{ + QMouseEvent fixedEvent (QEvent::MouseMove, viewportToViewport(viewportToContents(e->pos())), e->button(), e->state()); + handleMouseMoveEvent(&fixedEvent); +} + +void KolfGame::handleMouseMoveEvent(QMouseEvent *e) +{ + if (inPlay || !putter || m_ignoreEvents) + return; + + QPoint mouse = e->pos(); + + // mouse moving of putter + if (!editing) + { + updateMouse(); + return; + } + + if (!moving) + { + // lets change the cursor to a hand + // if we're hovering over something + + QCanvasItemList list = course->collisions(e->pos()); + if (list.count() > 0) + setCursor(KCursor::handCursor()); + else + setCursor(KCursor::arrowCursor()); + return; + } + + int moveX = storedMousePos.x() - mouse.x(); + int moveY = storedMousePos.y() - mouse.y(); + + // moving counts as modifying + if (moveX || moveY) + setModified(true); + + highlighter->moveBy(-(double)moveX, -(double)moveY); + movingItem->moveBy(-(double)moveX, -(double)moveY); + QRect brect = movingItem->boundingRect(); + emit newStatusText(QString("%1x%2").arg(brect.x()).arg(brect.y())); + storedMousePos = mouse; +} + +void KolfGame::updateMouse() +{ + // don't move putter if in advanced putting sequence + if (!m_useMouse || ((stroking || putting) && m_useAdvancedPutting)) + return; + + const QPoint cursor = viewportToViewport(viewportToContents(mapFromGlobal(QCursor::pos()))); + const QPoint ball((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + putter->setAngle(-Vector(cursor, ball).direction()); +} + +void KolfGame::handleMouseReleaseEvent(QMouseEvent *e) +{ + setCursor(KCursor::arrowCursor()); + + if (editing) + { + emit newStatusText(QString::null); + moving = false; + } + + if (m_ignoreEvents) + return; + + if (!editing && m_useMouse) + { + if (!inPlay && e->button() == LeftButton) + puttRelease(); + else if (e->button() == RightButton) + toggleShowInfo(); + } + + setFocus(); +} + +void KolfGame::keyPressEvent(QKeyEvent *e) +{ + if (inPlay || editing || m_ignoreEvents) + return; + + switch (e->key()) + { + case Key_Up: + if (!e->isAutoRepeat()) + toggleShowInfo(); + break; + + case Key_Escape: + putting = false; + stroking = false; + finishStroking = false; + strokeCircle->setVisible(false); + putterTimer->stop(); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + break; + + case Key_Left: + case Key_Right: + // don't move putter if in advanced putting sequence + if ((!stroking && !putting) || !m_useAdvancedPutting) + putter->go(e->key() == Key_Left? D_Left : D_Right, e->state() & ShiftButton? Amount_More : e->state() & ControlButton? Amount_Less : Amount_Normal); + break; + + case Key_Space: case Key_Down: + puttPress(); + break; + + default: + break; + } +} + +void KolfGame::toggleShowInfo() +{ + setShowInfo(!m_showInfo); +} + +void KolfGame::updateShowInfo() +{ + setShowInfo(m_showInfo); +} + +void KolfGame::setShowInfo(bool yes) +{ + m_showInfo = yes; + + if (m_showInfo) + { + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->showInfo(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->showInfo(); + + showInfo(); + } + else + { + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->hideInfo(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->hideInfo(); + + hideInfo(); + } +} + +void KolfGame::puttPress() +{ + // Advanced putting: 1st click start putting sequence, 2nd determine strength, 3rd determine precision + + if (!putting && !stroking && !inPlay) + { + puttCount = 0; + puttReverse = false; + putting = true; + stroking = false; + strength = 0; + if (m_useAdvancedPutting) + { + strokeCircle->setValue(0); + int pw = putter->endPoint().x() - putter->startPoint().x(); + if (pw < 0) pw = -pw; + int px = (int)putter->x() + pw / 2; + int py = (int)putter->y(); + if (px > width / 2 && py < height / 2) + strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py + 10); + else if (px > width / 2) + strokeCircle->move(px - pw / 2 - 10 - strokeCircle->width(), py - 10 - strokeCircle->height()); + else if (py < height / 2) + strokeCircle->move(px + pw / 2 + 10, py + 10); + else + strokeCircle->move(px + pw / 2 + 10, py - 10 - strokeCircle->height()); + strokeCircle->setVisible(true); + } + putterTimer->start(putterTimerMsec); + } + else if (m_useAdvancedPutting && putting && !editing) + { + putting = false; + stroking = true; + puttReverse = false; + finishStroking = false; + } + else if (m_useAdvancedPutting && stroking) + { + finishStroking = true; + putterTimeout(); + } +} + +void KolfGame::keyReleaseEvent(QKeyEvent *e) +{ + if (e->isAutoRepeat() || m_ignoreEvents) + return; + + if (e->key() == Key_Space || e->key() == Key_Down) + puttRelease(); + else if ((e->key() == Key_Backspace || e->key() == Key_Delete) && !(e->state() & ControlButton)) + { + if (editing && !moving && selectedItem) + { + CanvasItem *citem = dynamic_cast(selectedItem); + if (!citem) + return; + citem = citem->itemToDelete(); + if (!citem) + return; + QCanvasItem *item = dynamic_cast(citem); + if (citem && citem->deleteable()) + { + lastDelId = citem->curId(); + + highlighter->setVisible(false); + items.removeRef(item); + citem->hideInfo(); + citem->aboutToDelete(); + citem->aboutToDie(); + delete citem; + selectedItem = 0; + emit newSelectedItem(&holeInfo); + + setModified(true); + } + } + } + else if (e->key() == Key_I || e->key() == Key_Up) + toggleShowInfo(); +} + +void KolfGame::puttRelease() +{ + if (!m_useAdvancedPutting && putting && !editing) + { + putting = false; + stroking = true; + } +} + +void KolfGame::stoppedBall() +{ + if (!inPlay) + { + inPlay = true; + dontAddStroke = true; + } +} + +void KolfGame::timeout() +{ + Ball *curBall = (*curPlayer).ball(); + + // test if the ball is gone + // in this case we want to stop the ball and + // later undo the shot + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if (!course->rect().contains(QPoint((*it).ball()->x(), (*it).ball()->y()))) + { + (*it).ball()->setState(Stopped); + + // don't do it if he's past maxStrokes + if ((*it).score(curHole) < holeInfo.maxStrokes() - 1 || !holeInfo.hasMaxStrokes()) + { + loadStateList(); + } + shotDone(); + + return; + } + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + if ((*it).ball()->forceStillGoing() || ((*it).ball()->curState() == Rolling && (*it).ball()->curVector().magnitude() > 0 && (*it).ball()->isVisible())) + return; + + int curState = curBall->curState(); + if (curState == Stopped && inPlay) + { + inPlay = false; + QTimer::singleShot(0, this, SLOT(shotDone())); + } + + if (curState == Holed && inPlay) + { + emit inPlayEnd(); + emit playerHoled(&(*curPlayer)); + + int curScore = (*curPlayer).score(curHole); + if (!dontAddStroke) + curScore++; + + if (curScore == 1) + { + playSound("holeinone"); + } + else if (curScore <= holeInfo.par()) + { + // I don't have a sound!! + // *sob* + // playSound("woohoo"); + } + + (*curPlayer).ball()->setZ((*curPlayer).ball()->z() + .1 - (.1)/(curScore)); + + if (allPlayersDone()) + { + inPlay = false; + + if (curHole > 0 && !dontAddStroke) + { + (*curPlayer).addStrokeToHole(curHole); + emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole)); + } + QTimer::singleShot(600, this, SLOT(holeDone())); + } + else + { + inPlay = false; + QTimer::singleShot(0, this, SLOT(shotDone())); + } + } +} + +void KolfGame::fastTimeout() +{ + // do regular advance every other time + if (regAdv) + course->advance(); + regAdv = !regAdv; + + if (!editing) + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->doAdvance(); + + if (fastAdvancedExist) + { + CanvasItem *citem = 0; + for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next()) + citem->doAdvance(); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->fastAdvanceDone(); + + if (fastAdvancedExist) + { + CanvasItem *citem = 0; + for (citem = fastAdvancers.first(); citem; citem = fastAdvancers.next()) + citem->fastAdvanceDone(); + } + } +} + +void KolfGame::ballMoved() +{ + if (putter->isVisible()) + { + putter->move((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + } +} + +void KolfGame::putterTimeout() +{ + if (inPlay || editing) + return; + + if (m_useAdvancedPutting) + { + if (putting) + { + const float base = 2.0; + + if (puttReverse && strength <= 0) + { + // aborted + putting = false; + strokeCircle->setVisible(false); + } + else if (strength > maxStrength || puttReverse) + { + // decreasing strength as we've reached the top + puttReverse = true; + strength -= pow(base, strength / maxStrength) - 1.8; + if ((int)strength < puttCount * 2) + { + puttCount--; + if (puttCount >= 0) + putter->go(Forwards); + } + } + else + { + // make the increase at high strength faster + strength += pow(base, strength / maxStrength) - .3; + if ((int)strength > puttCount * 2) + { + putter->go(Backwards); + puttCount++; + } + } + // make the visible steps at high strength smaller + strokeCircle->setValue(pow(strength / maxStrength, 0.8) * 360); + } + else if (stroking) + { + double al = strokeCircle->value(); + if (al >= 45) + al -= 0.2 + strength / 50 + al / 100; + else + al -= 0.2 + strength / 50; + + if (puttReverse) + { + // show the stroke + puttCount--; + if (puttCount >= 0) + putter->go(Forwards); + else + { + strokeCircle->setVisible(false); + finishStroking = false; + putterTimer->stop(); + putting = false; + stroking = false; + shotStart(); + } + } + else if (al < -45 || finishStroking) + { + strokeCircle->setValue(al); + int deg; + // if > 45 or < -45 then bad stroke + if (al > 45) + { + deg = putter->curDeg() - 45 + rand() % 90; + strength -= rand() % (int)strength; + } + else if (!finishStroking) + { + deg = putter->curDeg() - 45 + rand() % 90; + strength -= rand() % (int)strength; + } + else + deg = putter->curDeg() + (int)(strokeCircle->value() / 3); + + if (deg < 0) + deg += 360; + else if (deg > 360) + deg -= 360; + + putter->setDeg(deg); + puttReverse = true; + } + else + { + strokeCircle->setValue(al); + putterTimer->changeInterval(putterTimerMsec/10); + } + } + + } + else + { + if (putting) + { + putter->go(Backwards); + puttCount++; + strength += 1.5; + if (strength > maxStrength) + { + putting = false; + stroking = true; + } + } + else if (stroking) + { + if (putter->curLen() < (*curPlayer).ball()->height() + 2) + { + stroking = false; + putterTimer->stop(); + putting = false; + stroking = false; + shotStart(); + } + + putter->go(Forwards); + putterTimer->changeInterval(putterTimerMsec/10); + } + } +} + +void KolfGame::autoSaveTimeout() +{ + // this should be a config option + // until it is i'll disable it + if (editing) + { + //save(); + } +} + +void KolfGame::recreateStateList() +{ + stateDB.clear(); + + QCanvasItem *item = 0; + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + stateDB.setName(makeStateGroup(citem->curId(), citem->name())); + citem->saveState(&stateDB); + } + } + + ballStateList.clear(); + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + ballStateList.append((*it).stateInfo(curHole)); + + ballStateList.canUndo = true; +} + +void KolfGame::undoShot() +{ + if (ballStateList.canUndo) + loadStateList(); +} + +void KolfGame::loadStateList() +{ + QCanvasItem *item = 0; + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + stateDB.setName(makeStateGroup(citem->curId(), citem->name())); + citem->loadState(&stateDB); + } + } + + for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it) + { + BallStateInfo info = (*it); + Player &player = (*players->at(info.id - 1)); + player.ball()->move(info.spot.x(), info.spot.y()); + player.ball()->setBeginningOfHole(info.beginningOfHole); + if ((*curPlayer).id() == info.id) + ballMoved(); + else + player.ball()->setVisible(!info.beginningOfHole); + player.setScoreForHole(info.score, curHole); + player.ball()->setState(info.state); + emit scoreChanged(info.id, curHole, info.score); + } +} + +void KolfGame::shotDone() +{ + inPlay = false; + emit inPlayEnd(); + setFocus(); + + Ball *ball = (*curPlayer).ball(); + double oldx = ball->x(), oldy = ball->y(); + + if (!dontAddStroke && (*curPlayer).numHoles()) + (*curPlayer).addStrokeToHole(curHole); + + dontAddStroke = false; + + // do hack stuff, shouldn't be done here + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if ((*it).ball()->addStroke()) + { + for (int i = 1; i <= (*it).ball()->addStroke(); ++i) + (*it).addStrokeToHole(curHole); + + // emit that we have a new stroke count + emit scoreChanged((*it).id(), curHole, (*it).score(curHole)); + } + (*it).ball()->setAddStroke(0); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + Ball *ball = (*it).ball(); + + if (ball->curState() == Holed) + continue; + + Vector v; + if (ball->placeOnGround(v)) + { + ball->setPlaceOnGround(false); + + QStringList options; + const QString placeOutside = i18n("Drop Outside of Hazard"); + const QString rehit = i18n("Rehit From Last Location"); + options << placeOutside << rehit; + const QString choice = KComboBoxDialog::getItem(i18n("What would you like to do for your next shot?"), i18n("%1 is in a Hazard").arg((*it).name()), options, placeOutside, "hazardOptions"); + + if (choice == placeOutside) + { + (*it).ball()->setDoDetect(false); + + double x = ball->x(), y = ball->y(); + + while (1) + { + QCanvasItemList list = ball->collisions(true); + bool keepMoving = false; + while (!list.isEmpty()) + { + QCanvasItem *item = list.first(); + if (item->rtti() == Rtti_DontPlaceOn) + keepMoving = true; + + list.pop_front(); + } + if (!keepMoving) + break; + + const float movePixel = 3.0; + x -= cos(v.direction()) * movePixel; + y += sin(v.direction()) * movePixel; + + ball->move(x, y); + } + + // move another two pixels away + x -= cos(v.direction()) * 2; + y += sin(v.direction()) * 2; + } + else if (choice == rehit) + { + for (BallStateList::Iterator it = ballStateList.begin(); it != ballStateList.end(); ++it) + { + if ((*it).id == (*curPlayer).id()) + { + if ((*it).beginningOfHole) + ball->move(whiteBall->x(), whiteBall->y()); + else + ball->move((*it).spot.x(), (*it).spot.y()); + + break; + } + } + } + + ball->setVisible(true); + ball->setState(Stopped); + + (*it).ball()->setDoDetect(true); + ball->collisionDetect(oldx, oldy); + } + } + + // emit again + emit scoreChanged((*curPlayer).id(), curHole, (*curPlayer).score(curHole)); + + ball->setVelocity(0, 0); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + Ball *ball = (*it).ball(); + + int curStrokes = (*it).score(curHole); + if (curStrokes >= holeInfo.maxStrokes() && holeInfo.hasMaxStrokes()) + { + ball->setState(Holed); + ball->setVisible(false); + + // move to center in case he/she hit out + ball->move(width / 2, height / 2); + playerWhoMaxed = (*it).name(); + + if (allPlayersDone()) + { + startNextHole(); + QTimer::singleShot(100, this, SLOT(emitMax())); + return; + } + + QTimer::singleShot(100, this, SLOT(emitMax())); + } + } + + // change player to next player + // skip player if he's Holed + do + { + curPlayer++; + if (curPlayer == players->end()) + curPlayer = players->begin(); + } + while ((*curPlayer).ball()->curState() == Holed); + + emit newPlayersTurn(&(*curPlayer)); + + (*curPlayer).ball()->setVisible(true); + + putter->setAngle((*curPlayer).ball()); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + + inPlay = false; + (*curPlayer).ball()->collisionDetect(oldx, oldy); +} + +void KolfGame::emitMax() +{ + emit maxStrokesReached(playerWhoMaxed); +} + +void KolfGame::startBall(const Vector &vector) +{ + playSound("hit"); + + emit inPlayStart(); + putter->setVisible(false); + + (*curPlayer).ball()->setState(Rolling); + (*curPlayer).ball()->setVector(vector); + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->shotStarted(); + } + + inPlay = true; +} + +void KolfGame::shotStart() +{ + // ensure we never hit the ball back into the hole which + // can cause hole skippage + if ((*curPlayer).ball()->curState() == Holed) + return; + + // save state + recreateStateList(); + + putter->saveAngle((*curPlayer).ball()); + strength /= 8; + if (!strength) + strength = 1; + + startBall(Vector(strength, putter->curAngle() + M_PI)); + + addHoleInfo(ballStateList); +} + +void KolfGame::addHoleInfo(BallStateList &list) +{ + list.player = (*curPlayer).id(); + list.vector = (*curPlayer).ball()->curVector(); + list.hole = curHole; +} + +void KolfGame::sayWhosGoing() +{ + if (players->count() >= 2) + { + KMessageBox::information(this, i18n("%1 will start off.").arg((*curPlayer).name()), i18n("New Hole"), "newHole"); + } +} + +void KolfGame::holeDone() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + startNextHole(); + sayWhosGoing(); +} + +// this function is WAY too smart for it's own good +// ie, bad design :-( +void KolfGame::startNextHole() +{ + setFocus(); + + bool reset = true; + if (askSave(true)) + { + if (allPlayersDone()) + { + // we'll reload this hole, but not reset + curHole--; + reset = false; + } + else + return; + } + else + setModified(false); + + pause(); + + dontAddStroke = false; + + inPlay = false; + timer->stop(); + putter->resetAngles(); + + int oldCurHole = curHole; + curHole++; + emit currentHole(curHole); + + if (reset) + { + whiteBall->move(width/2, height/2); + holeInfo.borderWallsChanged(true); + } + + int leastScore = INT_MAX; + + // to get the first player to go first on every hole, + // don't do the score stuff below + curPlayer = players->begin(); + double oldx=(*curPlayer).ball()->x(), oldy=(*curPlayer).ball()->y(); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + if (curHole > 1) + { + bool ahead = false; + if ((*it).lastScore() != 0) + { + if ((*it).lastScore() < leastScore) + ahead = true; + else if ((*it).lastScore() == leastScore) + { + for (int i = curHole - 1; i > 0; --i) + { + const int thisScore = (*it).score(i); + const int thatScore = (*curPlayer).score(i); + if (thisScore < thatScore) + { + ahead = true; + break; + } + else if (thisScore > thatScore) + break; + } + } + } + + if (ahead) + { + curPlayer = it; + leastScore = (*it).lastScore(); + } + } + + if (reset) + (*it).ball()->move(width / 2, height / 2); + else + (*it).ball()->move(whiteBall->x(), whiteBall->y()); + + (*it).ball()->setState(Stopped); + + // this gets set to false when the ball starts + // to move by the Mr. Ball himself. + (*it).ball()->setBeginningOfHole(true); + if ((int)(*it).scores().count() < curHole) + (*it).addHole(); + (*it).ball()->setVelocity(0, 0); + (*it).ball()->setVisible(false); + } + + emit newPlayersTurn(&(*curPlayer)); + + if (reset) + openFile(); + + inPlay = false; + timer->start(timerMsec); + + // if (false) { we're done with the round! } + if (oldCurHole != curHole) + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setPlaceOnGround(false); + + // here we have to make sure the scoreboard shows + // all of the holes up until now; + + for (; scoreboardHoles < curHole; ++scoreboardHoles) + { + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(scoreboardHoles + 1)); + emit newHole(cfg->readNumEntry("par", 3)); + } + + resetHoleScores(); + updateShowInfo(); + + // this is from shotDone() + (*curPlayer).ball()->setVisible(true); + putter->setOrigin((*curPlayer).ball()->x(), (*curPlayer).ball()->y()); + updateMouse(); + + ballStateList.canUndo = false; + + (*curPlayer).ball()->collisionDetect(oldx, oldy); + } + + unPause(); +} + +void KolfGame::showInfo() +{ + QString text = i18n("Hole %1: par %2, maximum %3 strokes").arg(curHole).arg(holeInfo.par()).arg(holeInfo.maxStrokes()); + infoText->move((width - QFontMetrics(infoText->font()).width(text)) / 2, infoText->y()); + infoText->setText(text); + // I hate this text! Let's not show it + //infoText->setVisible(true); + + emit newStatusText(text); +} + +void KolfGame::showInfoDlg(bool addDontShowAgain) +{ + KMessageBox::information(parentWidget(), + i18n("Course name: %1").arg(holeInfo.name()) + QString("\n") + + i18n("Created by %1").arg(holeInfo.author()) + QString("\n") + + i18n("%1 holes").arg(highestHole), + i18n("Course Information"), + addDontShowAgain? holeInfo.name() + QString(" ") + holeInfo.author() : QString::null); +} + +void KolfGame::hideInfo() +{ + infoText->setText(""); + infoText->setVisible(false); + + emit newStatusText(QString::null); +} + +void KolfGame::openFile() +{ + Object *curObj = 0; + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + // sometimes info is still showing + citem->hideInfo(); + citem->aboutToDie(); + } + } + + items.setAutoDelete(true); + items.clear(); + items.setAutoDelete(false); + + extraMoveable.setAutoDelete(false); + extraMoveable.clear(); + fastAdvancers.setAutoDelete(false); + fastAdvancers.clear(); + selectedItem = 0; + + // will tell basic course info + // we do this here for the hell of it. + // there is no fake id, by the way, + // because it's old and when i added ids i forgot to change it. + cfg->setGroup("0-course@-50,-50"); + holeInfo.setAuthor(cfg->readEntry("author", holeInfo.author())); + holeInfo.setName(cfg->readEntry("Name", holeInfo.name())); + holeInfo.setUntranslatedName(cfg->readEntryUntranslated("Name", holeInfo.untranslatedName())); + emit titleChanged(holeInfo.name()); + + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole)); + curPar = cfg->readNumEntry("par", 3); + holeInfo.setPar(curPar); + holeInfo.borderWallsChanged(cfg->readBoolEntry("borderWalls", holeInfo.borderWalls())); + holeInfo.setMaxStrokes(cfg->readNumEntry("maxstrokes", 10)); + bool hasFinalLoad = cfg->readBoolEntry("hasFinalLoad", true); + + QStringList missingPlugins; + QStringList groups = cfg->groupList(); + + int numItems = 0; + int _highestHole = 0; + + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + // [-@,|] + cfg->setGroup(*it); + + const int len = (*it).length(); + const int dashIndex = (*it).find("-"); + const int holeNum = (*it).left(dashIndex).toInt(); + if (holeNum > _highestHole) + _highestHole = holeNum; + + const int atIndex = (*it).find("@"); + const QString name = (*it).mid(dashIndex + 1, atIndex - (dashIndex + 1)); + + if (holeNum != curHole) + { + // if we've had one, break, cause list is sorted + // erps, no, cause we need to know highest hole! + if (numItems && !recalcHighestHole) + break; + continue; + } + numItems++; + + + const int commaIndex = (*it).find(","); + const int pipeIndex = (*it).find("|"); + const int x = (*it).mid(atIndex + 1, commaIndex - (atIndex + 1)).toInt(); + const int y = (*it).mid(commaIndex + 1, pipeIndex - (commaIndex + 1)).toInt(); + + // will tell where ball is + if (name == "ball") + { + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->move(x, y); + whiteBall->move(x, y); + continue; + } + + const int id = (*it).right(len - (pipeIndex + 1)).toInt(); + + bool loaded = false; + + for (curObj = obj->first(); curObj; curObj = obj->next()) + { + if (name != curObj->_name()) + continue; + + QCanvasItem *newItem = curObj->newObject(course); + items.append(newItem); + + CanvasItem *canvasItem = dynamic_cast(newItem); + if (!canvasItem) + continue; + + canvasItem->setId(id); + canvasItem->setGame(this); + canvasItem->editModeChanged(editing); + canvasItem->setName(curObj->_name()); + addItemsToMoveableList(canvasItem->moveableItems()); + if (canvasItem->fastAdvance()) + addItemToFastAdvancersList(canvasItem); + + newItem->move(x, y); + canvasItem->firstMove(x, y); + + newItem->setVisible(true); + + // make things actually show + if (!hasFinalLoad) + { + cfg->setGroup(makeGroup(id, curHole, canvasItem->name(), x, y)); + canvasItem->load(cfg); + course->update(); + } + + // we don't allow multiple items for the same thing in + // the file! + + loaded = true; + break; + } + + if (!loaded && name != "hole" && missingPlugins.contains(name) <= 0) + missingPlugins.append(name); + } + + if (!missingPlugins.empty()) + { + KMessageBox::informationList(this, QString("

    <http://katzbrown.com/kolf/Plugins/>

    ") + i18n("This hole uses the following plugins, which you do not have installed:") + QString("

    "), missingPlugins, QString::null, QString("%1 warning").arg(holeInfo.untranslatedName() + QString::number(curHole))); + } + + lastDelId = -1; + + // if it's the first hole let's not + if (!numItems && curHole > 1 && !addingNewHole && curHole >= _highestHole) + { + // we're done, let's quit + curHole--; + pause(); + emit holesDone(); + + // tidy things up + setBorderWalls(false); + clearHole(); + setModified(false); + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + + return; + } + + // do it down here; if !hasFinalLoad, do it up there! + QCanvasItem *qcanvasItem = 0; + QPtrList todo; + QPtrList qtodo; + if (hasFinalLoad) + { + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *item = dynamic_cast(qcanvasItem); + if (item) + { + if (item->loadLast()) + { + qtodo.append(qcanvasItem); + todo.append(item); + } + else + { + QString group = makeGroup(item->curId(), curHole, item->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y()); + cfg->setGroup(group); + item->load(cfg); + } + } + } + + CanvasItem *citem = 0; + qcanvasItem = qtodo.first(); + for (citem = todo.first(); citem; citem = todo.next()) + { + cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)qcanvasItem->x(), (int)qcanvasItem->y())); + citem->load(cfg); + + qcanvasItem = qtodo.next(); + } + } + + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *citem = dynamic_cast(qcanvasItem); + if (citem) + citem->updateZ(); + } + + if (curHole > _highestHole) + _highestHole = curHole; + + if (recalcHighestHole) + { + highestHole = _highestHole; + recalcHighestHole = false; + emit largestHole(highestHole); + } + + if (curHole == 1 && !filename.isNull() && !infoShown) + { + // let's not now, because they see it when they choose course + //showInfoDlg(true); + infoShown = true; + } + + setModified(false); +} + +void KolfGame::addItemsToMoveableList(QPtrList list) +{ + QCanvasItem *item = 0; + for (item = list.first(); item; item = list.next()) + extraMoveable.append(item); +} + +void KolfGame::addItemToFastAdvancersList(CanvasItem *item) +{ + fastAdvancers.append(item); + fastAdvancedExist = fastAdvancers.count() > 0; +} + +void KolfGame::addNewObject(Object *newObj) +{ + QCanvasItem *newItem = newObj->newObject(course); + items.append(newItem); + newItem->setVisible(true); + + CanvasItem *canvasItem = dynamic_cast(newItem); + if (!canvasItem) + return; + + // we need to find a number that isn't taken + int i = lastDelId > 0? lastDelId : items.count() - 30; + if (i <= 0) + i = 0; + + for (;; ++i) + { + bool found = false; + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + if (citem->curId() == i) + { + found = true; + break; + } + } + } + + + if (!found) + break; + } + canvasItem->setId(i); + + canvasItem->setGame(this); + + if (m_showInfo) + canvasItem->showInfo(); + else + canvasItem->hideInfo(); + + canvasItem->editModeChanged(editing); + + canvasItem->setName(newObj->_name()); + addItemsToMoveableList(canvasItem->moveableItems()); + + if (canvasItem->fastAdvance()) + addItemToFastAdvancersList(canvasItem); + + newItem->move(width/2 - 18, height / 2 - 18); + + if (selectedItem) + canvasItem->selectedItem(selectedItem); + + setModified(true); +} + +bool KolfGame::askSave(bool noMoreChances) +{ + if (!modified) + // not cancel, don't save + return false; + + int result = KMessageBox::warningYesNoCancel(this, i18n("There are unsaved changes to current hole. Save them?"), i18n("Unsaved Changes"), KStdGuiItem::save(), noMoreChances? KStdGuiItem::discard() : i18n("Save &Later"), noMoreChances? "DiscardAsk" : "SaveAsk", true); + switch (result) + { + case KMessageBox::Yes: + save(); + // fallthrough + + case KMessageBox::No: + return false; + break; + + case KMessageBox::Cancel: + return true; + break; + + default: + break; + } + + return false; +} + +void KolfGame::addNewHole() +{ + if (askSave(true)) + return; + + // either it's already false + // because it was saved by askSave(), + // or the user pressed the 'discard' button + setModified(false); + + // find highest hole num, and create new hole + // now openFile makes highest hole for us + + addingNewHole = true; + curHole = highestHole; + recalcHighestHole = true; + startNextHole(); + addingNewHole = false; + emit currentHole(curHole); + + // make sure even the current player isn't showing + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + (*it).ball()->setVisible(false); + + whiteBall->setVisible(editing); + highlighter->setVisible(false); + putter->setVisible(!editing); + inPlay = false; + + // add default objects + Object *curObj = 0; + for (curObj = obj->first(); curObj; curObj = obj->next()) + if (curObj->addOnNewHole()) + addNewObject(curObj); + + save(); +} + +// kantan deshou ;-) +void KolfGame::resetHole() +{ + if (askSave(true)) + return; + setModified(false); + curHole--; + startNextHole(); + resetHoleScores(); +} + +void KolfGame::resetHoleScores() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + (*it).resetScore(curHole); + emit scoreChanged((*it).id(), curHole, 0); + } +} + +void KolfGame::clearHole() +{ + QCanvasItem *qcanvasItem = 0; + for (qcanvasItem = items.first(); qcanvasItem; qcanvasItem = items.next()) + { + CanvasItem *citem = dynamic_cast(qcanvasItem); + if (citem) + citem->aboutToDie(); + } + items.setAutoDelete(true); + items.clear(); + items.setAutoDelete(false); + emit newSelectedItem(&holeInfo); + + // add default objects + Object *curObj = 0; + for (curObj = obj->first(); curObj; curObj = obj->next()) + if (curObj->addOnNewHole()) + addNewObject(curObj); + + setModified(true); +} + +void KolfGame::switchHole(int hole) +{ + if (inPlay) + return; + if (hole < 1 || hole > highestHole) + return; + + bool wasEditing = editing; + if (editing) + toggleEditMode(); + + if (askSave(true)) + return; + setModified(false); + + curHole = hole; + resetHole(); + + if (wasEditing) + toggleEditMode(); +} + +void KolfGame::switchHole(const QString &holestring) +{ + bool ok; + int hole = holestring.toInt(&ok); + if (!ok) + return; + switchHole(hole); +} + +void KolfGame::nextHole() +{ + switchHole(curHole + 1); +} + +void KolfGame::prevHole() +{ + switchHole(curHole - 1); +} + +void KolfGame::firstHole() +{ + switchHole(1); +} + +void KolfGame::lastHole() +{ + switchHole(highestHole); +} + +void KolfGame::randHole() +{ + int newHole = 1 + (int)((double)kapp->random() * ((double)(highestHole - 1) / (double)RAND_MAX)); + switchHole(newHole); +} + +void KolfGame::save() +{ + if (filename.isNull()) + { + QString newfilename = KFileDialog::getSaveFileName(":kourses", "application/x-kourse", this, i18n("Pick Kolf Course to Save To")); + if (newfilename.isNull()) + return; + + setFilename(newfilename); + } + + emit parChanged(curHole, holeInfo.par()); + emit titleChanged(holeInfo.name()); + + // we use this bool for optimization + // in openFile(). + bool hasFinalLoad = false; + fastAdvancedExist = false; + + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + citem->aboutToSave(); + if (citem->loadLast()) + hasFinalLoad = true; + } + } + + QStringList groups = cfg->groupList(); + + // wipe out all groups from this hole + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + int holeNum = (*it).left((*it).find("-")).toInt(); + if (holeNum == curHole) + cfg->deleteGroup(*it); + } + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + { + citem->clean(); + + cfg->setGroup(makeGroup(citem->curId(), curHole, citem->name(), (int)item->x(), (int)item->y())); + citem->save(cfg); + } + } + + // save where ball starts (whiteBall tells all) + cfg->setGroup(QString("%1-ball@%2,%3").arg(curHole).arg((int)whiteBall->x()).arg((int)whiteBall->y())); + cfg->writeEntry("dummykey", true); + + cfg->setGroup("0-course@-50,-50"); + cfg->writeEntry("author", holeInfo.author()); + cfg->writeEntry("Name", holeInfo.untranslatedName()); + + // save hole info + cfg->setGroup(QString("%1-hole@-50,-50|0").arg(curHole)); + cfg->writeEntry("par", holeInfo.par()); + cfg->writeEntry("maxstrokes", holeInfo.maxStrokes()); + cfg->writeEntry("borderWalls", holeInfo.borderWalls()); + cfg->writeEntry("hasFinalLoad", hasFinalLoad); + + cfg->sync(); + + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->savingDone(); + } + + setModified(false); +} + +void KolfGame::toggleEditMode() +{ + // won't be editing anymore, and user wants to cancel, we return + // this is pretty useless. when the person leaves the hole, + // he gets asked again + /* + if (editing && modified) + { + if (askSave(false)) + { + emit checkEditing(); + return; + } + } + */ + + moving = false; + selectedItem = 0; + + editing = !editing; + + if (editing) + { + emit editingStarted(); + emit newSelectedItem(&holeInfo); + } + else + { + emit editingEnded(); + setCursor(KCursor::arrowCursor()); + } + + // alert our items + QCanvasItem *item = 0; + for (item = items.first(); item; item = items.next()) + { + CanvasItem *citem = dynamic_cast(item); + if (citem) + citem->editModeChanged(editing); + } + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + // curplayer shouldn't be hidden no matter what + if ((*it).ball()->beginningOfHole() && it != curPlayer) + (*it).ball()->setVisible(false); + else + (*it).ball()->setVisible(!editing); + } + + whiteBall->setVisible(editing); + highlighter->setVisible(false); + + // shouldn't see putter whilst editing + putter->setVisible(!editing); + + if (editing) + autoSaveTimer->start(autoSaveMsec); + else + autoSaveTimer->stop(); + + inPlay = false; +} + +void KolfGame::playSound(QString file, double vol) +{ + if (m_sound) + { + KPlayObject *oldPlayObject = 0; + for (oldPlayObject = oldPlayObjects.first(); oldPlayObject; oldPlayObject = oldPlayObjects.next()) + { + if (oldPlayObject && oldPlayObject->state() != Arts::posPlaying) + { + oldPlayObjects.remove(); + + // because we will go to next() next time + // and after remove current item is one after + // removed item + (void) oldPlayObjects.prev(); + } + } + + file = soundDir + file + QString::fromLatin1(".wav"); + + // not needed when all of the files are in the distribution + //if (!QFile::exists(file)) + //return; + + KPlayObjectFactory factory(artsServer.server()); + KPlayObject *playObject = factory.createPlayObject(KURL(file), true); + + if (playObject && !playObject->isNull()) + { + if (vol > 1) + vol = 1; + else if (vol <= .01) + { + delete playObject; + return; + } + + if (vol < .99) + { + //new KVolumeControl(vol, artsServer.server(), playObject); + } + + playObject->play(); + oldPlayObjects.append(playObject); + } + } +} + +void HoleInfo::borderWallsChanged(bool yes) +{ + m_borderWalls = yes; + game->setBorderWalls(yes); +} + +void KolfGame::print(KPrinter &pr) +{ + QPainter p(&pr); + + QPaintDeviceMetrics metrics(&pr); + + // translate to center + p.translate(metrics.width() / 2 - course->rect().width() / 2, metrics.height() / 2 - course->rect().height() / 2); + + QPixmap pix(width, height); + QPainter pixp(&pix); + course->drawArea(course->rect(), &pixp); + p.drawPixmap(0, 0, pix); + + p.setPen(QPen(black, 2)); + p.drawRect(course->rect()); + + p.resetXForm(); + + if (pr.option("kde-kolf-title") == "true") + { + QString text = i18n("%1 - Hole %2; by %3").arg(holeInfo.name()).arg(curHole).arg(holeInfo.author()); + QFont font(kapp->font()); + font.setPointSize(18); + QRect rect = QFontMetrics(font).boundingRect(text); + p.setFont(font); + + p.drawText(metrics.width() / 2 - rect.width() / 2, metrics.height() / 2 - course->rect().height() / 2 -20 - rect.height(), text); + } +} + +bool KolfGame::allPlayersDone() +{ + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + if ((*it).ball()->curState() != Holed) + return false; + + return true; +} + +void KolfGame::setBorderWalls(bool showing) +{ + Wall *wall = 0; + for (wall = borderWalls.first(); wall; wall = borderWalls.next()) + wall->setVisible(showing); +} + +void KolfGame::setUseAdvancedPutting(bool yes) +{ + m_useAdvancedPutting = yes; + + // increase maxStrength in advanced putting mode + if (yes) + maxStrength = 65; + else + maxStrength = 55; +} + +void KolfGame::setShowGuideLine(bool yes) +{ + putter->setShowGuideLine(yes); +} + +void KolfGame::setSound(bool yes) +{ + m_sound = yes; +} + +void KolfGame::courseInfo(CourseInfo &info, const QString& filename) +{ + KConfig cfg(filename); + cfg.setGroup("0-course@-50,-50"); + info.author = cfg.readEntry("author", info.author); + info.name = cfg.readEntry("Name", cfg.readEntry("name", info.name)); + info.untranslatedName = cfg.readEntryUntranslated("Name", cfg.readEntryUntranslated("name", info.name)); + + unsigned int hole = 1; + unsigned int par= 0; + while (1) + { + QString group = QString("%1-hole@-50,-50|0").arg(hole); + if (!cfg.hasGroup(group)) + { + hole--; + break; + } + + cfg.setGroup(group); + par += cfg.readNumEntry("par", 3); + + hole++; + } + + info.par = par; + info.holes = hole; +} + +void KolfGame::scoresFromSaved(KConfig *config, PlayerList &players) +{ + config->setGroup("0 Saved Game"); + int numPlayers = config->readNumEntry("Players", 0); + if (numPlayers <= 0) + return; + + for (int i = 1; i <= numPlayers; ++i) + { + // this is same as in kolf.cpp, but we use saved game values + config->setGroup(QString::number(i)); + players.append(Player()); + players.last().ball()->setColor(config->readEntry("Color", "#ffffff")); + players.last().setName(config->readEntry("Name")); + players.last().setId(i); + + QStringList scores(config->readListEntry("Scores")); + QValueList intscores; + for (QStringList::Iterator it = scores.begin(); it != scores.end(); ++it) + intscores.append((*it).toInt()); + + players.last().setScores(intscores); + } +} + +void KolfGame::saveScores(KConfig *config) +{ + // wipe out old player info + QStringList groups = config->groupList(); + for (QStringList::Iterator it = groups.begin(); it != groups.end(); ++it) + { + // this deletes all int groups, ie, the player info groups + bool ok = false; + (*it).toInt(&ok); + if (ok) + config->deleteGroup(*it); + } + + config->setGroup("0 Saved Game"); + config->writeEntry("Players", players->count()); + config->writeEntry("Course", filename); + config->writeEntry("Current Hole", curHole); + + for (PlayerList::Iterator it = players->begin(); it != players->end(); ++it) + { + config->setGroup(QString::number((*it).id())); + config->writeEntry("Name", (*it).name()); + config->writeEntry("Color", (*it).ball()->color().name()); + + QStringList scores; + QValueList intscores = (*it).scores(); + for (QValueList::Iterator it = intscores.begin(); it != intscores.end(); ++it) + scores.append(QString::number(*it)); + + config->writeEntry("Scores", scores); + } +} + +CourseInfo::CourseInfo() +: name(i18n("Course Name")), author(i18n("Course Author")), holes(0), par(0) +{ +} + +#include "game.moc" diff --git a/kolf/game.h b/kolf/game.h new file mode 100644 index 00000000..afb13b3c --- /dev/null +++ b/kolf/game.h @@ -0,0 +1,1026 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "object.h" +#include "config.h" +#include "canvasitem.h" +#include "ball.h" +#include "statedb.h" +#include "rtti.h" +#include + +class QLabel; +class QSlider; +class QCheckBox; +class QTimer; +class QKeyEvent; +class QMouseEvent; +class QPainter; +class KConfig; +class KPrinter; +class KolfGame; + +enum Direction { D_Left, D_Right, Forwards, Backwards }; +enum Amount { Amount_Less, Amount_Normal, Amount_More }; +enum HoleResult { Result_Holed, Result_Miss, Result_LipOut }; + +class Player; + +class BallStateInfo +{ +public: + void saveState(KConfig *cfg); + void loadState(KConfig *cfg); + + int id; + QPoint spot; + BallState state; + bool beginningOfHole; + int score; +}; +class BallStateList : public QValueList +{ +public: + int hole; + int player; + bool canUndo; + Vector vector; +}; + +class Player +{ +public: + Player() : m_ball(new Ball(0)) {}; + Ball *ball() const { return m_ball; } + void setBall(Ball *ball) { m_ball = ball; } + BallStateInfo stateInfo(int hole) const { BallStateInfo ret; ret.spot = QPoint(m_ball->x(), m_ball->y()); ret.state = m_ball->curState(); ret.score = score(hole); ret.beginningOfHole = m_ball->beginningOfHole(); ret.id = m_id; return ret; } + + QValueList scores() const { return m_scores; } + void setScores(const QValueList &newScores) { m_scores = newScores; } + int score(int hole) const { return (*m_scores.at(hole - 1)); } + int lastScore() const { return m_scores.last(); } + int firstScore() const { return m_scores.first(); } + + void addStrokeToHole(int hole) { (*m_scores.at(hole - 1))++; } + void setScoreForHole(int score, int hole) { (*m_scores.at(hole - 1)) = score; } + void subtractStrokeFromHole(int hole) { (*m_scores.at(hole - 1))--; } + void resetScore(int hole) { (*m_scores.at(hole - 1)) = 0; } + void addHole() { m_scores.append(0); } + unsigned int numHoles() const { return m_scores.count(); } + + QString name() const { return m_name; } + void setName(const QString &name) { m_name = name; m_ball->setName(name); } + + void setId(int id) { m_id = id; } + int id() const { return m_id; } + +private: + Ball *m_ball; + QValueList m_scores; + QString m_name; + int m_id; +}; +typedef QValueList PlayerList; + +class Arrow : public QCanvasLine +{ +public: + Arrow(QCanvas *canvas); + void setAngle(double newAngle) { m_angle = newAngle; } + double angle() const { return m_angle; } + void setLength(double newLength) { m_length = newLength; } + double length() const { return m_length; } + void setReversed(bool yes) { m_reversed = yes; } + bool reversed() const { return m_reversed; } + virtual void setVisible(bool); + virtual void setPen(QPen p); + void aboutToDie(); + virtual void moveBy(double, double); + void updateSelf(); + virtual void setZ(double newz); + +private: + double m_angle; + double m_length; + bool m_reversed; + QCanvasLine *line1; + QCanvasLine *line2; +}; + +class RectPoint; +class RectItem +{ +public: + virtual void newSize(int /*width*/, int /*height*/) {}; +}; + +class RectPoint : public QCanvasEllipse, public CanvasItem +{ +public: + RectPoint(QColor color, RectItem *, QCanvas *canvas); + void dontMove() { dontmove = true; } + virtual void moveBy(double dx, double dy); + virtual Config *config(QWidget *parent); + virtual bool deleteable() const { return false; } + virtual bool cornerResize() const { return true; } + virtual CanvasItem *itemToDelete() { return dynamic_cast(rect); } + void setSizeFactor(double newFactor) { m_sizeFactor = newFactor; } + +protected: + RectItem *rect; + double m_sizeFactor; + +private: + bool dontmove; +}; + +class Ellipse : public QCanvasEllipse, public CanvasItem, public RectItem +{ +public: + Ellipse(QCanvas *canvas); + virtual void advance(int phase); + + int changeEvery() const { return m_changeEvery; } + void setChangeEvery(int news) { m_changeEvery = news; } + bool changeEnabled() const { return m_changeEnabled; } + void setChangeEnabled(bool news); + + virtual void aboutToDie(); + virtual void aboutToSave(); + virtual void savingDone(); + + virtual QPtrList moveableItems() const; + + virtual void newSize(int width, int height); + virtual void moveBy(double dx, double dy); + + virtual void editModeChanged(bool changed); + + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + + virtual Config *config(QWidget *parent); + +protected: + RectPoint *point; + int m_changeEvery; + bool m_changeEnabled; + +private: + int count; + bool dontHide; +}; +class EllipseConfig : public Config +{ + Q_OBJECT + +public: + EllipseConfig(Ellipse *ellipse, QWidget *); + +private slots: + void value1Changed(int news); + void value2Changed(int news); + void check1Changed(bool on); + void check2Changed(bool on); + +protected: + QVBoxLayout *m_vlayout; + +private: + QLabel *slow1; + QLabel *fast1; + QLabel *slow2; + QLabel *fast2; + QSlider *slider1; + QSlider *slider2; + Ellipse *ellipse; +}; + +class Puddle : public Ellipse +{ +public: + Puddle(QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); + virtual int rtti() const { return Rtti_DontPlaceOn; } +}; +class PuddleObj : public Object +{ +public: + PuddleObj() { m_name = i18n("Puddle"); m__name = "puddle"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Puddle(canvas); } +}; + +class Sand : public Ellipse +{ +public: + Sand(QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); +}; +class SandObj : public Object +{ +public: + SandObj() { m_name = i18n("Sand"); m__name = "sand"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Sand(canvas); } +}; + +class Inside : public QCanvasEllipse, public CanvasItem +{ +public: + Inside(CanvasItem *item, QCanvas *canvas) : QCanvasEllipse(canvas) { this->item = item; } + virtual bool collision(Ball *ball, long int id) { return item->collision(ball, id); } + +protected: + CanvasItem *item; +}; + +class Bumper : public QCanvasEllipse, public CanvasItem +{ +public: + Bumper(QCanvas *canvas); + + virtual void advance(int phase); + virtual void aboutToDie(); + virtual void moveBy(double dx, double dy); + virtual void editModeChanged(bool changed); + + virtual bool collision(Ball *ball, long int id); + +protected: + QColor firstColor; + QColor secondColor; + Inside *inside; + +private: + int count; +}; +class BumperObj : public Object +{ +public: + BumperObj() { m_name = i18n("Bumper"); m__name = "bumper"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Bumper(canvas); } +}; + +class Hole : public QCanvasEllipse, public CanvasItem +{ +public: + Hole(QColor color, QCanvas *canvas); + virtual bool place(Ball * /*ball*/, bool /*wasCenter*/) { return true; }; + + virtual bool collision(Ball *ball, long int id); + +protected: + virtual HoleResult result(const QPoint, double, bool *wasCenter); +}; + +class Cup : public Hole +{ +public: + Cup(QCanvas *canvas); + virtual bool place(Ball *ball, bool wasCenter); + virtual void save(KConfig *cfg); + virtual bool canBeMovedByOthers() const { return true; } + virtual void draw(QPainter &painter); + +protected: + QPixmap pixmap; +}; +class CupObj : public Object +{ +public: + CupObj() { m_name = i18n("Cup"); m__name = "cup"; m_addOnNewHole = true; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Cup(canvas); } +}; + +class BlackHole; +class BlackHoleConfig : public Config +{ + Q_OBJECT + +public: + BlackHoleConfig(BlackHole *blackHole, QWidget *parent); + +private slots: + void degChanged(int); + void minChanged(double); + void maxChanged(double); + +private: + BlackHole *blackHole; +}; +class BlackHoleExit : public QCanvasLine, public CanvasItem +{ +public: + BlackHoleExit(BlackHole *blackHole, QCanvas *canvas); + virtual int rtti() const { return Rtti_NoCollision; } + virtual void aboutToDie(); + virtual void moveBy(double dx, double dy); + virtual bool deleteable() const { return false; } + virtual bool canBeMovedByOthers() const { return true; } + virtual void editModeChanged(bool editing); + virtual void setPen(QPen p); + virtual void showInfo(); + virtual void hideInfo(); + void updateArrowAngle(); + void updateArrowLength(); + virtual Config *config(QWidget *parent); + BlackHole *blackHole; + +protected: + Arrow *arrow; +}; +class BlackHoleTimer : public QObject +{ +Q_OBJECT + +public: + BlackHoleTimer(Ball *ball, double speed, int msec); + +signals: + void eject(Ball *ball, double speed); + void halfway(); + +protected slots: + void mySlot(); + void myMidSlot(); + +protected: + double m_speed; + Ball *m_ball; +}; +class BlackHole : public QObject, public Hole +{ +Q_OBJECT + +public: + BlackHole(QCanvas *canvas); + virtual bool canBeMovedByOthers() const { return true; } + virtual void aboutToDie(); + virtual void showInfo(); + virtual void hideInfo(); + virtual bool place(Ball *ball, bool wasCenter); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual Config *config(QWidget *parent) { return new BlackHoleConfig(this, parent); } + virtual QPtrList moveableItems() const; + double minSpeed() const { return m_minSpeed; } + double maxSpeed() const { return m_maxSpeed; } + void setMinSpeed(double news) { m_minSpeed = news; exitItem->updateArrowLength(); } + void setMaxSpeed(double news) { m_maxSpeed = news; exitItem->updateArrowLength(); } + + int curExitDeg() const { return exitDeg; } + void setExitDeg(int newdeg); + + virtual void editModeChanged(bool editing) { exitItem->editModeChanged(editing); } + void updateInfo(); + + virtual void shotStarted() { runs = 0; }; + + virtual void moveBy(double dx, double dy); + +public slots: + void eject(Ball *ball, double speed); + void halfway(); + +protected: + int exitDeg; + BlackHoleExit *exitItem; + double m_minSpeed; + double m_maxSpeed; + +private: + int runs; + QCanvasLine *infoLine; + QCanvasEllipse *outside; + void finishMe(); +}; +class BlackHoleObj : public Object +{ +public: + BlackHoleObj() { m_name = i18n("Black Hole"); m__name = "blackhole"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new BlackHole(canvas); } +}; + +class WallPoint; +class Wall : public QCanvasLine, public CanvasItem +{ +public: + Wall(QCanvas *canvas); + virtual void aboutToDie(); + double dampening; + + void setAlwaysShow(bool yes); + virtual void setZ(double newz); + virtual void setPen(QPen p); + virtual bool collision(Ball *ball, long int id); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual void selectedItem(QCanvasItem *item); + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual void setVelocity(double vx, double vy); + virtual void clean(); + + // must reimp because we gotta move the end items, + // and we do that in moveBy() + virtual void setPoints(int xa, int ya, int xb, int yb) { QCanvasLine::setPoints(xa, ya, xb, yb); moveBy(0, 0); } + + virtual int rtti() const { return Rtti_Wall; } + virtual QPtrList moveableItems() const; + virtual void setGame(KolfGame *game); + virtual void setVisible(bool); + + virtual QPointArray areaPoints() const; + +protected: + WallPoint *startItem; + WallPoint *endItem; + bool editing; + +private: + long int lastId; + + friend class WallPoint; +}; +class WallPoint : public QCanvasEllipse, public CanvasItem +{ +public: + WallPoint(bool start, Wall *wall, QCanvas *canvas); + void setAlwaysShow(bool yes) { alwaysShow = yes; updateVisible(); } + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual int rtti() const { return Rtti_WallPoint; } + virtual bool deleteable() const { return false; } + virtual bool collision(Ball *ball, long int id); + virtual CanvasItem *itemToDelete() { return wall; } + virtual void clean(); + virtual Config *config(QWidget *parent) { return wall->config(parent); } + void dontMove() { dontmove = true; }; + void updateVisible(); + + Wall *parentWall() { return wall; } + +protected: + Wall *wall; + bool editing; + bool visible; + +private: + bool alwaysShow; + bool start; + bool dontmove; + long int lastId; + + friend class Wall; +}; +class WallObj : public Object +{ +public: + WallObj() { m_name = i18n("Wall"); m__name = "wall"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Wall(canvas); } +}; + +class Putter : public QCanvasLine, public CanvasItem +{ +public: + Putter(QCanvas *canvas); + void go(Direction, Amount amount = Amount_Normal); + void setOrigin(int x, int y); + int curLen() const { return len; } + double curAngle() const { return angle; } + int curDeg() const { return rad2deg(angle); } + virtual void showInfo(); + virtual void hideInfo(); + void setAngle(double news) { angle = news; finishMe(); } + void setDeg(int news) { angle = deg2rad(news); finishMe(); } + double curMaxAngle() const { return maxAngle; } + virtual int rtti() const { return Rtti_Putter; } + virtual void setVisible(bool yes); + void saveAngle(Ball *ball) { angleMap[ball] = angle; } + void setAngle(Ball *ball); + void resetAngles() { angleMap.clear(); setZ(999999); } + virtual bool canBeMovedByOthers() const { return true; } + virtual void moveBy(double dx, double dy); + void setShowGuideLine(bool yes); + +private: + QPoint midPoint; + double maxAngle; + double angle; + double oneDegree; + QMap angleMap; + int len; + void finishMe(); + int putterWidth; + QCanvasLine *guideLine; + bool m_showGuideLine; +}; + +class Bridge; +class BridgeConfig : public Config +{ + Q_OBJECT + +public: + BridgeConfig(Bridge *bridge, QWidget *); + +protected slots: + void topWallChanged(bool); + void botWallChanged(bool); + void leftWallChanged(bool); + void rightWallChanged(bool); + +protected: + QVBoxLayout *m_vlayout; + QCheckBox *top; + QCheckBox *bot; + QCheckBox *left; + QCheckBox *right; + +private: + Bridge *bridge; +}; +class Bridge : public QCanvasRectangle, public CanvasItem, public RectItem +{ +public: + Bridge(QRect rect, QCanvas *canvas); + virtual bool collision(Ball *ball, long int id); + virtual void aboutToDie(); + virtual void editModeChanged(bool changed); + virtual void moveBy(double dx, double dy); + virtual void load(KConfig *cfg); + virtual void save(KConfig *cfg); + virtual bool vStrut() const { return true; } + void doLoad(KConfig *cfg); + void doSave(KConfig *cfg); + virtual void newSize(int width, int height); + virtual void setGame(KolfGame *game); + virtual Config *config(QWidget *parent) { return new BridgeConfig(this, parent); } + void setSize(int width, int height); + virtual QPtrList moveableItems() const; + + void setWallColor(QColor color); + QPen wallPen() const { return topWall->pen(); } + + double wallZ() const { return topWall->z(); } + void setWallZ(double); + + void setTopWallVisible(bool yes) { topWall->setVisible(yes); } + void setBotWallVisible(bool yes) { botWall->setVisible(yes); } + void setLeftWallVisible(bool yes) { leftWall->setVisible(yes); } + void setRightWallVisible(bool yes) { rightWall->setVisible(yes); } + bool topWallVisible() const { return topWall->isVisible(); } + bool botWallVisible() const { return botWall->isVisible(); } + bool leftWallVisible() const { return leftWall->isVisible(); } + bool rightWallVisible() const { return rightWall->isVisible(); } + +protected: + Wall *topWall; + Wall *botWall; + Wall *leftWall; + Wall *rightWall; + RectPoint *point; +}; +class BridgeObj : public Object +{ +public: + BridgeObj() { m_name = i18n("Bridge"); m__name = "bridge"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Bridge(QRect(0, 0, 80, 40), canvas); } +}; + +class Sign; +class SignConfig : public BridgeConfig +{ + Q_OBJECT + +public: + SignConfig(Sign *sign, QWidget *parent); + +private slots: + void textChanged(const QString &); + +private: + Sign *sign; +}; +class Sign : public Bridge +{ +public: + Sign(QCanvas *canvas); + void setText(const QString &text); + QString text() const { return m_text; } + virtual void draw(QPainter &painter); + virtual bool vStrut() const { return false; } + virtual Config *config(QWidget *parent) { return new SignConfig(this, parent); } + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + +protected: + QString m_text; + QString m_untranslatedText; +}; +class SignObj : public Object +{ +public: + SignObj() { m_name = i18n("Sign"); m__name = "sign"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Sign(canvas); } +}; + +class Windmill; +class WindmillGuard : public Wall +{ +public: + WindmillGuard(QCanvas *canvas) : Wall(canvas) {}; + void setBetween(int newmin, int newmax) { max = newmax; min = newmin; } + virtual void advance(int phase); + +protected: + int max; + int min; +}; +class WindmillConfig : public BridgeConfig +{ + Q_OBJECT + +public: + WindmillConfig(Windmill *windmill, QWidget *parent); + +private slots: + void speedChanged(int news); + void endChanged(bool yes); + +private: + Windmill *windmill; +}; +class Windmill : public Bridge +{ +public: + Windmill(QRect rect, QCanvas *canvas); + virtual void aboutToDie(); + virtual void newSize(int width, int height); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + virtual void setGame(KolfGame *game); + virtual Config *config(QWidget *parent) { return new WindmillConfig(this, parent); } + void setSize(int width, int height); + virtual void moveBy(double dx, double dy); + void setSpeed(int news); + int curSpeed() const { return speed; } + void setBottom(bool yes); + bool bottom() const { return m_bottom; } + +protected: + WindmillGuard *guard; + Wall *left; + Wall *right; + int speedfactor; + int speed; + bool m_bottom; +}; +class WindmillObj : public Object +{ +public: + WindmillObj() { m_name = i18n("Windmill"); m__name = "windmill"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Windmill(QRect(0, 0, 80, 40), canvas); } +}; + +class HoleInfo; +class HoleConfig : public Config +{ + Q_OBJECT + +public: + HoleConfig(HoleInfo *holeInfo, QWidget *); + +private slots: + void authorChanged(const QString &); + void parChanged(int); + void maxStrokesChanged(int); + void nameChanged(const QString &); + void borderWallsChanged(bool); + +private: + HoleInfo *holeInfo; +}; +class HoleInfo : public CanvasItem +{ +public: + HoleInfo() { m_lowestMaxStrokes = 4; } + virtual ~HoleInfo() {} + void setPar(int newpar) { m_par = newpar; } + int par() const { return m_par; } + void setMaxStrokes(int newMaxStrokes) { m_maxStrokes = newMaxStrokes; } + int lowestMaxStrokes() const { return m_lowestMaxStrokes; } + int maxStrokes() const { return m_maxStrokes; } + bool hasMaxStrokes() const { return m_maxStrokes != m_lowestMaxStrokes; } + void setAuthor(QString newauthor) { m_author = newauthor; } + QString author() const { return m_author; } + + void setName(QString newname) { m_name = newname; } + void setUntranslatedName(QString newname) { m_untranslatedName = newname; } + QString name() const { return m_name; } + QString untranslatedName() const { return m_untranslatedName; } + + virtual Config *config(QWidget *parent) { return new HoleConfig(this, parent); } + void borderWallsChanged(bool yes); + bool borderWalls() const { return m_borderWalls; } + +private: + QString m_author; + QString m_name; + QString m_untranslatedName; + bool m_borderWalls; + int m_par; + int m_maxStrokes; + int m_lowestMaxStrokes; +}; + +class StrokeCircle : public QCanvasItem +{ +public: + StrokeCircle(QCanvas *canvas); + + void setValue(double v); + double value(); + void setMaxValue(double m); + void setSize(int w, int h); + void setThickness(int t); + int thickness() const; + int width() const; + int height() const; + virtual void draw(QPainter &p); + virtual QRect boundingRect() const; + virtual bool collidesWith(const QCanvasItem*) const; + virtual bool collidesWith(const QCanvasSprite*, const QCanvasPolygonalItem*, const QCanvasRectangle*, const QCanvasEllipse*, const QCanvasText*) const; + +private: + double dvalue, dmax; + int ithickness, iwidth, iheight; +}; + +struct KDE_EXPORT CourseInfo +{ + CourseInfo(const QString &_name, const QString &_untranslatedName, const QString &_author, unsigned int _holes, unsigned int _par) { name = _name; untranslatedName = _untranslatedName, author = _author; holes = _holes; par = _par; } + CourseInfo(); + + QString name; + QString untranslatedName; + QString author; + unsigned int holes; + unsigned int par; +}; + +class KDE_EXPORT KolfGame : public QCanvasView +{ + Q_OBJECT + +public: + KolfGame(ObjectList *obj, PlayerList *players, QString filename, QWidget *parent=0, const char *name=0 ); + ~KolfGame(); + void setObjects(ObjectList *obj) { this->obj = obj; } + void setFilename(const QString &filename); + QString curFilename() const { return filename; } + void emitLargestHole() { emit largestHole(highestHole); } + QCanvas *canvas() const { return course; } + void removeItem(QCanvasItem *item) { items.setAutoDelete(false); items.removeRef(item); } + // returns whether it was a cancel + bool askSave(bool); + bool isEditing() const { return editing; } + int currentHole() { return curHole; } + void setStrict(bool yes) { strict = yes; } + // returns true when you shouldn't do anything + bool isPaused() const { return paused; } + Ball *curBall() const { return (*curPlayer).ball(); } + void updateMouse(); + void ballMoved(); + void updateHighlighter(); + void updateCourse() { course->update(); } + QCanvasItem *curSelectedItem() const { return selectedItem; } + void setBorderWalls(bool); + void setInPlay(bool yes) { inPlay = yes; } + bool isInPlay() { return inPlay; } + bool isInfoShowing() { return m_showInfo; } + void stoppedBall(); + QString courseName() const { return holeInfo.name(); } + void hidePutter() { putter->setVisible(false); } + void ignoreEvents(bool ignore) { m_ignoreEvents = ignore; } + + static void scoresFromSaved(KConfig *, PlayerList &players); + static void courseInfo(CourseInfo &info, const QString &filename); + +public slots: + void pause(); + void unPause(); + void save(); + void toggleEditMode(); + void setModified(bool mod = true); + void addNewObject(Object *newObj); + void addNewHole(); + void switchHole(int); + void switchHole(const QString &); + void nextHole(); + void prevHole(); + void firstHole(); + void lastHole(); + void randHole(); + void playSound(QString file, double vol = 1); + void showInfoDlg(bool = false); + void resetHole(); + void clearHole(); + void print(KPrinter &); + void setShowInfo(bool yes); + void toggleShowInfo(); + void updateShowInfo(); + void setUseMouse(bool yes) { m_useMouse = yes; } + void setUseAdvancedPutting(bool yes); + void setShowGuideLine(bool yes); + void setSound(bool yes); + void undoShot(); + void timeout(); + void saveScores(KConfig *); + void startFirstHole(int hole); + void sayWhosGoing(); + +signals: + void holesDone(); + void newHole(int); + void parChanged(int, int); + void titleChanged(const QString &); + void largestHole(int); + void scoreChanged(int, int, int); + void newPlayersTurn(Player *); + void playerHoled(Player *); + void newSelectedItem(CanvasItem *); + void checkEditing(); + void editingStarted(); + void editingEnded(); + void inPlayStart(); + void inPlayEnd(); + void maxStrokesReached(const QString &); + void currentHole(int); + void modifiedChanged(bool); + void newStatusText(const QString &); + +private slots: + void shotDone(); + void holeDone(); + void startNextHole(); + void fastTimeout(); + void putterTimeout(); + void autoSaveTimeout(); + void addItemsToMoveableList(QPtrList); + void addItemToFastAdvancersList(CanvasItem *); + void hideInfo(); + + void emitMax(); + +protected: + void mouseMoveEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + + void handleMousePressEvent(QMouseEvent *e); + void handleMouseDoubleClickEvent(QMouseEvent *e); + void handleMouseMoveEvent(QMouseEvent *e); + void handleMouseReleaseEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + + QPoint viewportToViewport(const QPoint &p); + +private: + QCanvas *course; + Putter *putter; + PlayerList *players; + PlayerList::Iterator curPlayer; + Ball *whiteBall; + StrokeCircle *strokeCircle; + + QTimer *timer; + QTimer *autoSaveTimer; + QTimer *fastTimer; + QTimer *putterTimer; + bool regAdv; + + ObjectList *obj; + QPtrList items; + QPtrList extraMoveable; + QPtrList borderWalls; + + int timerMsec; + int autoSaveMsec; + int fastTimerMsec; + int putterTimerMsec; + + void puttPress(); + void puttRelease(); + bool inPlay; + bool putting; + bool stroking; + bool finishStroking; + double strength; + double maxStrength; + int puttCount; + bool puttReverse; + + int curHole; + int highestHole; + int curPar; + + int wallWidth; + int height; + int width; + int margin; + QColor grass; + + int advancePeriod; + + int lastDelId; + + bool paused; + + QString filename; + bool recalcHighestHole; + void openFile(); + + bool strict; + + bool editing; + QPoint storedMousePos; + bool moving; + bool dragging; + QCanvasItem *movingItem; + QCanvasItem *selectedItem; + QCanvasRectangle *highlighter; + + // sound + KArtsDispatcher artsDispatcher; + KArtsServer artsServer; + QPtrList oldPlayObjects; + bool m_sound; + bool soundedOnce; + QString soundDir; + + bool m_ignoreEvents; + + HoleInfo holeInfo; + QCanvasText *infoText; + void showInfo(); + StateDB stateDB; + + BallStateList ballStateList; + void loadStateList(); + void recreateStateList(); + void addHoleInfo(BallStateList &list); + + bool dontAddStroke; + + bool addingNewHole; + int scoreboardHoles; + inline void resetHoleScores(); + + bool m_showInfo; + + bool infoShown; + + KConfig *cfg; + + inline void addBorderWall(QPoint start, QPoint end); + void shotStart(); + void startBall(const Vector &vector); + + bool modified; + + inline bool allPlayersDone(); + + bool m_useMouse; + bool m_useAdvancedPutting; + + QPtrList fastAdvancers; + bool fastAdvancedExist; + + QString playerWhoMaxed; +}; + +#endif diff --git a/kolf/graphics/Makefile.am b/kolf/graphics/Makefile.am new file mode 100644 index 00000000..45bda9e0 --- /dev/null +++ b/kolf/graphics/Makefile.am @@ -0,0 +1,4 @@ +picdir = $(kde_datadir)/kolf/pics/ +pic_DATA = cup.png grass.png puddle.png sand.png + +EXTRA_DIST=$(pic_DATA) diff --git a/kolf/graphics/cup.png b/kolf/graphics/cup.png new file mode 100644 index 0000000000000000000000000000000000000000..2da6c70a9c2979fc710635110a98d5389c16a850 GIT binary patch literal 876 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 M07*qoM6N<$f>m+$y8r+H literal 0 HcmV?d00001 diff --git a/kolf/graphics/puddle.png b/kolf/graphics/puddle.png new file mode 100644 index 0000000000000000000000000000000000000000..f8ecd7f7664241177abda14efddd1c3272b09767 GIT binary patch literal 622 zcmV-!0+IcRP)^!#Wvy}H5}Vb#etp3z&$HON)^nfN=^RnQv1k@9AGxzKhqgl& zu17nX4Ve>@bA3C52Z}Kprl-su0W>QlYEOk2LEK@%bS_$<&?!@TI)|FIaCJIobhX(? z42H*}`7y6w)QXfq+t$RTq>!xkv=^Oi_Dwd~okPBQE`W1tL5(h5g?j<+5M{O>3awOT zE;=gOV#Js=bG!}O-VkAbTJ^&>YqxLY-YTZ$y3Q->k^SJa27ydxcMg<-3lQt-ym6@& zdXfOW-sHAvIhx&hm#A>KhjhI-hrH@jpWHY&(>l3jvKG2Kcj}vto#Xn#!GehHHXyu9 z;2l#d-L9qHPMAqIypM$i0|j(sm*6lDfWc&&1<=EsY2o^ekaLr13)RX3Nnq)e?)!LW zI_1{6-2#&PI!kAYm51wQ8vaKG@(o`%B-n8M=^;JkqPE|i-}>(*gwh%MWLCwIv0>E2}Y>Z^qThX)wbaI513YH!ZuHjrT_o{07*qo IM6N<$f)G3(wg3PC literal 0 HcmV?d00001 diff --git a/kolf/graphics/sand.png b/kolf/graphics/sand.png new file mode 100644 index 0000000000000000000000000000000000000000..860e10996ef257aa4ba909925fdb87c32beb4cce GIT binary patch literal 968 zcmV;(12_DMP)u$NK;P164^x zK~#901;JZ(+cppYP|OToeA|}d*v&_OyZ>c$16@FU8pn<$Q6xct0A_kG{PXu;HJ&cM zGz^gfXX>0vsRZBh`#w%^)_gO4nRh>8tZO<|ScJ$SRbT<2e- z&kB1DU!T(_2YpjMHuEoEa|h-qOe#SZNGPt>3HG#V|zjO6S62yfi9aYWIZQMSWFM5>2%dxY{Zv{z=f+ znr+h<6)ME@{*(qM5ofQ=p4O|>GdBk;eSI2J$~a3}%~1kUwdfT0>O~?bxd^wP%76`b zjeT{gS&QIfwAqQ-H2}ZnqrEFa(RCre!1N+cdq*nZH(P zwp$EuZ)qiIYc(ma`FNNGaTCn=i%q{+Tk>$=HG@SZM_`|-vQIBUNhu+JMmDa}X1Hk@ z`VUm=_UskDQrRANq;B_GDqxHkXxCP%SC>K^w}&Zar(-E`oN{ZwY8+023%#rs@9{`C z3S9I?pN*>S@keelwz?W~UUxcb!d{3@@Hh+FqXo-Z{Z{hH8>XYRsyczFW^bva?Va}X zd|W~7)ipJCo&Ht#E)|`tkFcjwq(W&qRy?=yGG&TjS}j{CA{_`f^rzk@@z{YVPWP_q zKy;a*=%cMkRY_CTcsJdYe4HA606oYbq)(lsz9eOiannD|^*8Es`QUJKq8Aw`c7wJB zmy^+Y%aM?Dum$f24i6Gn8iNlmwKMUF+8aC-E@7AnJC|oi@WEoMd=~sLeo`8Wy;pvP zT7Ki;?}UJ=y^smEdTEn!MbEci&gVO0000 +// Copyright (C) 2002 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "kcomboboxdialog.h" + +KComboBoxDialog::KComboBoxDialog( const QString &_text, const QStringList &_items, const QString& _value, bool showDontAskAgain, QWidget *parent ) + : KDialogBase( Plain, QString::null, Ok, Ok, parent, 0L, true, true ) +{ + QVBoxLayout *topLayout = new QVBoxLayout( plainPage(), marginHint(), spacingHint() ); + QLabel *label = new QLabel(_text, plainPage() ); + topLayout->addWidget( label, 1 ); + + combo = new KHistoryCombo( plainPage() ); + combo->setEditable(false); + combo->insertStringList( _items ); + topLayout->addWidget( combo, 1 ); + + if (showDontAskAgain) + { + dontAskAgainCheckBox = new QCheckBox( i18n("&Do not ask again"), plainPage() ); + topLayout->addWidget( dontAskAgainCheckBox, 1 ); + } + else + dontAskAgainCheckBox = 0; + + if ( !_value.isNull() ) + combo->setCurrentText( _value ); + combo->setFocus(); +} + +KComboBoxDialog::~KComboBoxDialog() +{ +} + +QString KComboBoxDialog::text() const +{ + return combo->currentText(); +} + +bool KComboBoxDialog::dontAskAgainChecked() +{ + if (dontAskAgainCheckBox) + return dontAskAgainCheckBox->isChecked(); + + return false; +} + +QString KComboBoxDialog::getItem( const QString &_text, const QStringList &_items, const QString& _value, const QString &dontAskAgainName, QWidget *parent ) +{ + return getItem( _text, QString::null, _items, _value, dontAskAgainName, parent ); +} + +QString KComboBoxDialog::getItem( const QString &_text, const QString &_caption, const QStringList &_items, const QString& _value, const QString &dontAskAgainName, QWidget *parent ) +{ + QString prevAnswer; + if ( !dontAskAgainName.isEmpty() ) + { + KConfig *config = KGlobal::config(); + config->setGroup( "Notification Messages" ); + prevAnswer = config->readEntry( dontAskAgainName ); + if ( !prevAnswer.isEmpty() ) + if ( _items.contains( prevAnswer ) > 0 ) + return prevAnswer; + } + + KComboBoxDialog dlg( _text, _items, _value, !dontAskAgainName.isNull(), parent ); + if ( !_caption.isNull() ) + dlg.setCaption( _caption ); + + dlg.exec(); + + const QString text = dlg.text(); + + if (dlg.dontAskAgainChecked()) + { + if ( !dontAskAgainName.isEmpty() && !text.isEmpty() ) + { + KConfig *config = KGlobal::config(); + config->setGroup ( "Notification Messages" ); + config->writeEntry( dontAskAgainName, text ); + } + } + + return text; +} + +QString KComboBoxDialog::getText(const QString &_caption, const QString &_text, const QString &_value, bool *ok, QWidget *parent, const QString &configName, KConfig *config) +{ + KComboBoxDialog dlg(_text, QStringList(), _value, false, parent); + if ( !_caption.isNull() ) + dlg.setCaption( _caption ); + + KHistoryCombo * const box = dlg.comboBox(); + box->setEditable(true); + + const QString historyItem = QString("%1History").arg(configName); + const QString completionItem = QString("%1Completion").arg(configName); + + if(!configName.isNull()) + { + config->setGroup("KComboBoxDialog"); + box->setHistoryItems(config->readListEntry(historyItem)); + box->completionObject()->setItems(config->readListEntry(completionItem)); + } + + bool result = dlg.exec(); + if(ok) *ok = result; + + if(!configName.isNull() && result) + { + box->addToHistory(dlg.text()); + box->completionObject()->addItem(dlg.text()); + config->setGroup("KComboBoxDialog"); + config->writeEntry(historyItem, box->historyItems()); + config->writeEntry(completionItem, box->completionObject()->items()); + } + + return dlg.text(); +} + +#include "kcomboboxdialog.moc" diff --git a/kolf/kcomboboxdialog.h b/kolf/kcomboboxdialog.h new file mode 100644 index 00000000..e835c0aa --- /dev/null +++ b/kolf/kcomboboxdialog.h @@ -0,0 +1,114 @@ +// Copyright (C) 2002 Jason Katz-Brown +// Copyright (C) 2002 Neil Stevens +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the author(s) shall not be +// used in advertising or otherwise to promote the sale, use or other dealings +// in this Software without prior written authorization from the author(s). + +#ifndef KCOMBOBOX_DIALOG_H +#define KCOMBOBOX_DIALOG_H + +#include + +#include +#include + +class QCheckBox; +class KHistoryCombo; + +/** + * Dialog for user to choose an item from a QStringList. + */ + +class KComboBoxDialog : public KDialogBase +{ +Q_OBJECT + +public: + /** + * Create a dialog that asks for a single line of text. _value is + * the initial value of the line. _text appears as the current text + * of the combobox. + * + * @param _items Items in the combobox + * @param _text Text of the label + * @param _value Initial value of the combobox + */ + KComboBoxDialog( const QString &_text, const QStringList& _items, const QString& _value = QString::null, bool showDontAskAgain = false, QWidget *parent = 0 ); + virtual ~KComboBoxDialog(); + + /** + * @return the value the user chose + */ + QString text() const; + + /** + * @return the line edit widget + */ + KHistoryCombo *comboBox() const { return combo; } + + /** + * Static convenience function to get input from the user. + * + * @param _text Text of the label + * @param _items Items in the combobox + * @param _value Initial value of the inputline + * @param dontAskAgainName Name for saving whether the user doesn't want to be asked again; use QString::null to disable + */ + static QString getItem( const QString &_text, const QStringList &_items, const QString& _value = QString::null, const QString &dontAskAgainName = QString::null, QWidget *parent = 0 ); + + /** + * Static convenience function to get input from the user. + * This method includes a caption. + * + * @param _caption Caption of the dialog + * @param _text Text of the label + * @param _items Items in the combobox + * @param _value Initial value of the inputline + * @param dontAskAgainName Name for saving whether the user doesn't want to be asked again; use QString::null to disable + */ + static QString getItem( const QString &_text, const QString &_caption, const QStringList &_items, const QString& _value = QString::null, const QString &dontAskAgainName = QString::null, QWidget *parent = 0 ); + + /** + * Static convenience method. + * This method is meant as a replacement for KLineEditDlg::getText() for cases + * when a history and autocompletion are desired. + * + * @param _caption Caption of the dialog + * @param _text Text of the label + * @param _value Initial value of the inputline + * @param ok Variable to store whether the user hit OK + * @param parent Parent widget for the dialog + * @param configName Name of the dialog for saving the completion and history + * @parma config KConfig for saving the completion and history + */ + static QString getText(const QString &_caption, const QString &_text, + const QString &_value = QString::null, + bool *ok = 0, QWidget *parent = 0, + const QString &configName = QString::null, + KConfig *config = KGlobal::config()); + +protected: + KHistoryCombo *combo; + QCheckBox *dontAskAgainCheckBox; + bool dontAskAgainChecked(); +}; + +#endif diff --git a/kolf/kolf.cpp b/kolf/kolf.cpp new file mode 100644 index 00000000..33155b6a --- /dev/null +++ b/kolf/kolf.cpp @@ -0,0 +1,815 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "game.h" +#include "floater.h" +#include "slope.h" +#include "newgame.h" +#include "scoreboard.h" +#include "editor.h" +#include "pluginloader.h" +#include "printdialogpage.h" +#include "kolf.h" + +Kolf::Kolf() + : KMainWindow(0, "Kolf") +{ + competition = false; + game = 0; + editor = 0; + spacer = 0; + scoreboard = 0; + isTutorial = false; + + initGUI(); + + obj = new ObjectList; + initPlugins(); + + filename = QString::null; + dummy = new QWidget(this); + setCentralWidget(dummy); + layout = new QGridLayout(dummy, 3, 1); + + resize(420, 480); +} + +Kolf::~Kolf() +{ + // wipe out our objects + obj->setAutoDelete(true); + delete obj; +} + +void Kolf::initGUI() +{ + newAction = KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + newAction->setText(newAction->text() + QString("...")); + + endAction = KStdGameAction::end(this, SLOT(closeGame()), actionCollection()); + printAction = KStdGameAction::print(this, SLOT(print()), actionCollection()); + + (void) KStdGameAction::quit(this, SLOT(close()), actionCollection()); + saveAction = KStdAction::save(this, SLOT(save()), actionCollection(), "game_save"); + saveAction->setText(i18n("Save &Course")); + saveAsAction = KStdAction::saveAs(this, SLOT(saveAs()), actionCollection(), "game_save_as"); + saveAsAction->setText(i18n("Save &Course As...")); + + saveGameAction = new KAction(i18n("&Save Game"), 0, this, SLOT(saveGame()), actionCollection(), "savegame"); + saveGameAsAction = new KAction(i18n("&Save Game As..."), 0, this, SLOT(saveGameAs()), actionCollection(), "savegameas"); + + loadGameAction = KStdGameAction::load(this, SLOT(loadGame()), actionCollection()); + loadGameAction->setText(i18n("Load Saved Game...")); + + highScoreAction = KStdGameAction::highscores(this, SLOT(showHighScores()), actionCollection()); + + editingAction = new KToggleAction(i18n("&Edit"), "pencil", CTRL+Key_E, this, SLOT(emptySlot()), actionCollection(), "editing"); + newHoleAction = new KAction(i18n("&New"), "filenew", CTRL+SHIFT+Key_N, this, SLOT(emptySlot()), actionCollection(), "newhole"); + clearHoleAction = new KAction(KStdGuiItem::clear().text(), "locationbar_erase", CTRL+Key_Delete, this, SLOT(emptySlot()), actionCollection(), "clearhole"); + resetHoleAction = new KAction(i18n("&Reset"), CTRL+Key_R, this, SLOT(emptySlot()), actionCollection(), "resethole"); + undoShotAction = KStdAction::undo(this, SLOT(emptySlot()), actionCollection(), "undoshot"); + undoShotAction->setText(i18n("&Undo Shot")); + //replayShotAction = new KAction(i18n("&Replay Shot"), 0, this, SLOT(emptySlot()), actionCollection(), "replay"); + + holeAction = new KListAction(i18n("Switch to Hole"), 0, this, SLOT(emptySlot()), actionCollection(), "switchhole"); + nextAction = new KAction(i18n("&Next Hole"), "forward", KStdAccel::shortcut(KStdAccel::Forward), this, SLOT(emptySlot()), actionCollection(), "nexthole"); + prevAction = new KAction(i18n("&Previous Hole"), "back", KStdAccel::shortcut(KStdAccel::Back), this, SLOT(emptySlot()), actionCollection(), "prevhole"); + firstAction = new KAction(i18n("&First Hole"), "gohome", KStdAccel::shortcut(KStdAccel::Home), this, SLOT(emptySlot()), actionCollection(), "firsthole"); + lastAction = new KAction(i18n("&Last Hole"), CTRL+SHIFT+Key_End, this, SLOT(emptySlot()), actionCollection(), "lasthole"); + randAction = new KAction(i18n("&Random Hole"), "goto", 0, this, SLOT(emptySlot()), actionCollection(), "randhole"); + + useMouseAction = new KToggleAction(i18n("Enable &Mouse for Moving Putter"), 0, this, SLOT(emptySlot()), actionCollection(), "usemouse"); + useMouseAction->setCheckedState(i18n("Disable &Mouse for Moving Putter")); + connect(useMouseAction, SIGNAL(toggled(bool)), this, SLOT(useMouseChanged(bool))); + KConfig *config = kapp->config(); + config->setGroup("Settings"); + useMouseAction->setChecked(config->readBoolEntry("useMouse", true)); + + useAdvancedPuttingAction = new KToggleAction(i18n("Enable &Advanced Putting"), 0, this, SLOT(emptySlot()), actionCollection(), "useadvancedputting"); + useAdvancedPuttingAction->setCheckedState(i18n("Disable &Advanced Putting")); + connect(useAdvancedPuttingAction, SIGNAL(toggled(bool)), this, SLOT(useAdvancedPuttingChanged(bool))); + useAdvancedPuttingAction->setChecked(config->readBoolEntry("useAdvancedPutting", false)); + + showInfoAction = new KToggleAction(i18n("Show &Info"), "info", CTRL+Key_I, this, SLOT(emptySlot()), actionCollection(), "showinfo"); + showInfoAction->setCheckedState(i18n("Hide &Info")); + connect(showInfoAction, SIGNAL(toggled(bool)), this, SLOT(showInfoChanged(bool))); + showInfoAction->setChecked(config->readBoolEntry("showInfo", false)); + + showGuideLineAction = new KToggleAction(i18n("Show Putter &Guideline"), 0, this, SLOT(emptySlot()), actionCollection(), "showguideline"); + showGuideLineAction->setCheckedState(i18n("Hide Putter &Guideline")); + connect(showGuideLineAction, SIGNAL(toggled(bool)), this, SLOT(showGuideLineChanged(bool))); + showGuideLineAction->setChecked(config->readBoolEntry("showGuideLine", true)); + + KToggleAction *act=new KToggleAction(i18n("Enable All Dialog Boxes"), 0, this, SLOT(enableAllMessages()), actionCollection(), "enableAll"); + act->setCheckedState(i18n("Disable All Dialog Boxes")); + + soundAction = new KToggleAction(i18n("Play &Sounds"), 0, this, SLOT(emptySlot()), actionCollection(), "sound"); + connect(soundAction, SIGNAL(toggled(bool)), this, SLOT(soundChanged(bool))); + soundAction->setChecked(config->readBoolEntry("sound", true)); + + (void) new KAction(i18n("&Reload Plugins"), 0, this, SLOT(initPlugins()), actionCollection(), "reloadplugins"); + (void) new KAction(i18n("Show &Plugins"), 0, this, SLOT(showPlugins()), actionCollection(), "showplugins"); + + aboutAction = new KAction(i18n("&About Course"), 0, this, SLOT(emptySlot()), actionCollection(), "aboutcourse"); + tutorialAction = new KAction(i18n("&Tutorial"), 0, this, SLOT(tutorial()), actionCollection(), "tutorial"); + + statusBar(); + setupGUI(); +} + +bool Kolf::queryClose() +{ + if (game) + if (game->askSave(true)) + return false; + return true; +} + +void Kolf::startNewGame() +{ + NewGameDialog *dialog = 0; + int firstHole = 1; + + if (loadedGame.isNull()) + { + dialog = new NewGameDialog(filename.isNull(), dummy, "New Game Dialog"); + if (dialog->exec() != QDialog::Accepted) + goto end; + } + + players.clear(); + delete scoreboard; + scoreboard = new ScoreBoard(dummy, "Score Board"); + layout->addWidget(scoreboard, 1, 0); + scoreboard->show(); + + if (loadedGame.isNull()) + { + PlayerEditor *curEditor = 0; + int newId = 1; + for (curEditor = dialog->players()->first(); curEditor; curEditor = dialog->players()->next(), ++newId) + { + players.append(Player()); + players.last().ball()->setColor(curEditor->color()); + players.last().setName(curEditor->name()); + players.last().setId(newId); + } + + competition = dialog->competition(); + filename = filename.isNull()? dialog->course() : filename; + } + else + { + KConfig config(loadedGame); + config.setGroup("0 Saved Game"); + + if (isTutorial) + filename = KGlobal::dirs()->findResource("appdata", "tutorial.kolf"); + else + filename = config.readEntry("Course", QString::null); + + if (filename.isNull()) + return; + + competition = config.readBoolEntry("Competition", false); + firstHole = config.readNumEntry("Current Hole", 1); + + players.clear(); + KolfGame::scoresFromSaved(&config, players); + } + + for (PlayerList::Iterator it = players.begin(); it != players.end(); ++it) + scoreboard->newPlayer((*it).name()); + + delete spacer; + spacer = 0; + delete game; + game = new KolfGame(obj, &players, filename, dummy); + game->setStrict(competition); + + connect(game, SIGNAL(newHole(int)), scoreboard, SLOT(newHole(int))); + connect(game, SIGNAL(scoreChanged(int, int, int)), scoreboard, SLOT(setScore(int, int, int))); + connect(game, SIGNAL(parChanged(int, int)), scoreboard, SLOT(parChanged(int, int))); + connect(game, SIGNAL(modifiedChanged(bool)), this, SLOT(updateModified(bool))); + connect(game, SIGNAL(newPlayersTurn(Player *)), this, SLOT(newPlayersTurn(Player *))); + connect(game, SIGNAL(holesDone()), this, SLOT(gameOver())); + connect(game, SIGNAL(checkEditing()), this, SLOT(checkEditing())); + connect(game, SIGNAL(editingStarted()), this, SLOT(editingStarted())); + connect(game, SIGNAL(editingEnded()), this, SLOT(editingEnded())); + connect(game, SIGNAL(inPlayStart()), this, SLOT(inPlayStart())); + connect(game, SIGNAL(inPlayEnd()), this, SLOT(inPlayEnd())); + connect(game, SIGNAL(maxStrokesReached(const QString &)), this, SLOT(maxStrokesReached(const QString &))); + connect(game, SIGNAL(largestHole(int)), this, SLOT(updateHoleMenu(int))); + connect(game, SIGNAL(titleChanged(const QString &)), this, SLOT(titleChanged(const QString &))); + connect(game, SIGNAL(newStatusText(const QString &)), this, SLOT(newStatusText(const QString &))); + connect(game, SIGNAL(currentHole(int)), this, SLOT(setCurrentHole(int))); + connect(holeAction, SIGNAL(activated(const QString &)), game, SLOT(switchHole(const QString &))); + connect(nextAction, SIGNAL(activated()), game, SLOT(nextHole())); + connect(prevAction, SIGNAL(activated()), game, SLOT(prevHole())); + connect(firstAction, SIGNAL(activated()), game, SLOT(firstHole())); + connect(lastAction, SIGNAL(activated()), game, SLOT(lastHole())); + connect(randAction, SIGNAL(activated()), game, SLOT(randHole())); + connect(editingAction, SIGNAL(activated()), game, SLOT(toggleEditMode())); + connect(newHoleAction, SIGNAL(activated()), game, SLOT(addNewHole())); + connect(clearHoleAction, SIGNAL(activated()), game, SLOT(clearHole())); + connect(resetHoleAction, SIGNAL(activated()), game, SLOT(resetHole())); + connect(undoShotAction, SIGNAL(activated()), game, SLOT(undoShot())); + //connect(replayShotAction, SIGNAL(activated()), game, SLOT(replay())); + connect(aboutAction, SIGNAL(activated()), game, SLOT(showInfoDlg())); + connect(useMouseAction, SIGNAL(toggled(bool)), game, SLOT(setUseMouse(bool))); + connect(useAdvancedPuttingAction, SIGNAL(toggled(bool)), game, SLOT(setUseAdvancedPutting(bool))); + connect(soundAction, SIGNAL(toggled(bool)), game, SLOT(setSound(bool))); + connect(showGuideLineAction, SIGNAL(toggled(bool)), game, SLOT(setShowGuideLine(bool))); + connect(showInfoAction, SIGNAL(toggled(bool)), game, SLOT(setShowInfo(bool))); + + game->setUseMouse(useMouseAction->isChecked()); + game->setUseAdvancedPutting(useAdvancedPuttingAction->isChecked()); + game->setShowInfo(showInfoAction->isChecked()); + game->setShowGuideLine(showGuideLineAction->isChecked()); + game->setSound(soundAction->isChecked()); + + layout->addWidget(game, 0, 0, AlignCenter); + + game->show(); + game->setFocus(); + + setEditingEnabled(true); + endAction->setEnabled(true); + setHoleMovementEnabled(true); + setHoleOtherEnabled(true); + aboutAction->setEnabled(true); + highScoreAction->setEnabled(true); + printAction->setEnabled(true); + saveAction->setEnabled(true); + saveAsAction->setEnabled(true); + saveGameAction->setEnabled(true); + saveGameAsAction->setEnabled(true); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + newAction->setEnabled(false); + loadGameAction->setEnabled(false); + tutorialAction->setEnabled(false); + + + // so game can do stuff that needs to be done + // after things above are connected + game->startFirstHole(firstHole); + + end: + delete dialog; +} + +void Kolf::newGame() +{ + isTutorial = false; + filename = QString::null; + startNewGame(); +} + +void Kolf::tutorial() +{ + QString newfilename = KGlobal::dirs()->findResource("appdata", "tutorial.kolfgame"); + if (newfilename.isNull()) + return; + + filename = QString::null; + loadedGame = newfilename; + isTutorial = true; + + startNewGame(); + + loadedGame = QString::null; +} + +void Kolf::closeGame() +{ + if (game) + { + if (game->askSave(true)) + return; + game->pause(); + } + + filename = QString::null; + + editingEnded(); + delete game; + game = 0; + loadedGame = QString::null; + + editingAction->setChecked(false); + setEditingEnabled(false); + endAction->setEnabled(false); + aboutAction->setEnabled(false); + highScoreAction->setEnabled(false); + printAction->setEnabled(false); + saveAction->setEnabled(false); + saveAsAction->setEnabled(false); + saveGameAction->setEnabled(false); + saveGameAsAction->setEnabled(false); + setHoleMovementEnabled(false); + setHoleOtherEnabled(false); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + newAction->setEnabled(true); + loadGameAction->setEnabled(true); + tutorialAction->setEnabled(true); + + titleChanged(QString::null); + updateModified(false); + + QTimer::singleShot(100, this, SLOT(createSpacer())); +} + +void Kolf::createSpacer() +{ + // make a player to play the spacer hole + spacerPlayers.clear(); + spacerPlayers.append(Player()); + spacerPlayers.last().ball()->setColor(yellow); + spacerPlayers.last().setName("player"); + spacerPlayers.last().setId(1); + + delete spacer; + spacer = new KolfGame(obj, &spacerPlayers, KGlobal::dirs()->findResource("appdata", "intro"), dummy); + spacer->setSound(false); + spacer->startFirstHole(1); + layout->addWidget(spacer, 0, 0, AlignCenter); + spacer->hidePutter(); + spacer->ignoreEvents(true); + + spacer->show(); +} + +void Kolf::gameOver() +{ + int curPar = 0; + int lowScore = INT_MAX; // let's hope it doesn't stay this way! + int curScore = 1; + + // names of people who had the lowest score + QStringList names; + + HighScoreList highScores; + int scoreBoardIndex = 1; + + while (curScore != 0) + { + QString curName; + + // name taken as a reference and filled out + curScore = scoreboard->total(scoreBoardIndex, curName); + + scoreBoardIndex++; + + if (curName == i18n("Par")) + { + curPar = curScore; + continue; + } + + if (curScore == 0) + continue; + + // attempt to add everybody to the highscore list + // (ignored if we aren't competing down below) + highScores.append(HighScore(curName, curScore)); + + if (curScore < lowScore) + { + names.clear(); + lowScore = curScore; + names.append(curName); + } + else if (curScore == lowScore) + names.append(curName); + } + + // only announce a winner if more than two entries + // (player and par) are on the scoreboard + one to go past end + // + 1 for koodoo + if (scoreBoardIndex > 4) + { + if (names.count() > 1) + { + QString winners = names.join(i18n(" and ")); + KMessageBox::information(this, i18n("%1 tied").arg(winners)); + } + else + KMessageBox::information(this, i18n("%1 won!").arg(names.first())); + } + + if (competition) + { + // deal with highscores + // KScoreDialog makes it very easy :-)) + + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + + CourseInfo courseInfo; + game->courseInfo(courseInfo, game->curFilename()); + + scoreDialog->setConfigGroup(courseInfo.untranslatedName + QString(" Highscores")); + + for (HighScoreList::Iterator it = highScores.begin(); it != highScores.end(); ++it) + { + KScoreDialog::FieldInfo info; + info[KScoreDialog::Name] = (*it).name; + info[KScoreDialog::Custom1] = QString::number(curPar); + + scoreDialog->addScore((*it).score, info, false, true); + } + + scoreDialog->setComment(i18n("High Scores for %1").arg(courseInfo.name)); + scoreDialog->show(); + } + + QTimer::singleShot(700, this, SLOT(closeGame())); +} + +void Kolf::showHighScores() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + + CourseInfo courseInfo; + game->courseInfo(courseInfo, game->curFilename()); + + scoreDialog->setConfigGroup(courseInfo.untranslatedName + QString(" Highscores")); + scoreDialog->setComment(i18n("High Scores for %1").arg(courseInfo.name)); + scoreDialog->show(); +} + +void Kolf::save() +{ + if (filename.isNull()) + { + saveAs(); + return; + } + + if (game) + game->save(); + + game->setFocus(); +} + +void Kolf::saveAs() +{ + QString newfilename = KFileDialog::getSaveFileName(":kourses", "application/x-kourse", this, i18n("Pick Kolf Course to Save To")); + if (!newfilename.isNull()) + { + filename = newfilename; + game->setFilename(filename); + game->save(); + game->setFocus(); + } +} + +void Kolf::saveGameAs() +{ + QString newfilename = KFileDialog::getSaveFileName(":savedkolf", "application/x-kolf", this, i18n("Pick Saved Game to Save To")); + if (newfilename.isNull()) + return; + + loadedGame = newfilename; + + saveGame(); +} + +void Kolf::saveGame() +{ + if (loadedGame.isNull()) + { + saveGameAs(); + return; + } + + KConfig config(loadedGame); + config.setGroup("0 Saved Game"); + + config.writeEntry("Competition", competition); + config.writeEntry("Course", filename); + + game->saveScores(&config); + + config.sync(); +} + +void Kolf::loadGame() +{ + loadedGame = KFileDialog::getOpenFileName(":savedkolf", QString::fromLatin1("application/x-kolf"), this, i18n("Pick Kolf Saved Game")); + + if (loadedGame.isNull()) + return; + + isTutorial = false; + startNewGame(); +} + +// called by main for commmand line files +void Kolf::openURL(KURL url) +{ + QString target; + if (KIO::NetAccess::download(url, target, this)) + { + isTutorial = false; + QString mimeType = KMimeType::findByPath(target)->name(); + if (mimeType == "application/x-kourse") + filename = target; + else if (mimeType == "application/x-kolf") + loadedGame = target; + else + { + closeGame(); + return; + } + + QTimer::singleShot(10, this, SLOT(startNewGame())); + } + else + closeGame(); +} + +void Kolf::newPlayersTurn(Player *player) +{ + tempStatusBarText = i18n("%1's turn").arg(player->name()); + + if (showInfoAction->isChecked()) + statusBar()->message(tempStatusBarText, 5 * 1000); + else + statusBar()->message(tempStatusBarText); + + scoreboard->setCurrentCell(player->id() - 1, game->currentHole() - 1); +} + +void Kolf::newStatusText(const QString &text) +{ + if (text.isEmpty()) + statusBar()->message(tempStatusBarText); + else + statusBar()->message(text); +} + +void Kolf::editingStarted() +{ + delete editor; + editor = new Editor(obj, dummy, "Editor"); + connect(editor, SIGNAL(addNewItem(Object *)), game, SLOT(addNewObject(Object *))); + connect(editor, SIGNAL(changed()), game, SLOT(setModified())); + connect(editor, SIGNAL(addNewItem(Object *)), this, SLOT(setHoleFocus())); + connect(game, SIGNAL(newSelectedItem(CanvasItem *)), editor, SLOT(setItem(CanvasItem *))); + + scoreboard->hide(); + + layout->addWidget(editor, 1, 0); + editor->show(); + + clearHoleAction->setEnabled(true); + newHoleAction->setEnabled(true); + setHoleOtherEnabled(false); + + game->setFocus(); +} + +void Kolf::editingEnded() +{ + delete editor; + editor = 0; + + if (scoreboard) + scoreboard->show(); + + clearHoleAction->setEnabled(false); + newHoleAction->setEnabled(false); + setHoleOtherEnabled(true); + + if (game) + game->setFocus(); +} + +void Kolf::inPlayStart() +{ + setEditingEnabled(false); + setHoleOtherEnabled(false); + setHoleMovementEnabled(false); +} + +void Kolf::inPlayEnd() +{ + setEditingEnabled(true); + setHoleOtherEnabled(true); + setHoleMovementEnabled(true); +} + +void Kolf::maxStrokesReached(const QString &name) +{ + KMessageBox::sorry(this, i18n("%1's score has reached the maximum for this hole.").arg(name)); +} + +void Kolf::updateHoleMenu(int largest) +{ + QStringList items; + for (int i = 1; i <= largest; ++i) + items.append(QString::number(i)); + + // setItems for some reason enables the action + bool shouldbe = holeAction->isEnabled(); + holeAction->setItems(items); + holeAction->setEnabled(shouldbe); +} + +void Kolf::setHoleMovementEnabled(bool yes) +{ + if (competition) + yes = false; + + holeAction->setEnabled(yes); + + nextAction->setEnabled(yes); + prevAction->setEnabled(yes); + firstAction->setEnabled(yes); + lastAction->setEnabled(yes); + randAction->setEnabled(yes); +} + +void Kolf::setHoleOtherEnabled(bool yes) +{ + if (competition) + yes = false; + + resetHoleAction->setEnabled(yes); + undoShotAction->setEnabled(yes); + //replayShotAction->setEnabled(yes); +} + +void Kolf::setEditingEnabled(bool yes) +{ + editingAction->setEnabled(competition? false : yes); +} + +void Kolf::checkEditing() +{ + editingAction->setChecked(true); +} + +void Kolf::print() +{ + KPrinter pr; + pr.addDialogPage(new PrintDialogPage()); + + if (pr.setup(this, i18n("Print %1 - Hole %2").arg(game->courseName()).arg(game->currentHole()))) + { + pr.newPage(); + if (game) + game->print(pr); + } +} + +void Kolf::updateModified(bool mod) +{ + courseModified = mod; + titleChanged(title); +} + +void Kolf::titleChanged(const QString &newTitle) +{ + title = newTitle; + setCaption(title, courseModified); +} + +void Kolf::useMouseChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("useMouse", yes); config->sync(); +} + +void Kolf::useAdvancedPuttingChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("useAdvancedPutting", yes); config->sync(); +} + +void Kolf::showInfoChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("showInfo", yes); config->sync(); +} + +void Kolf::showGuideLineChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("showGuideLine", yes); config->sync(); +} + +void Kolf::soundChanged(bool yes) +{ + KConfig *config = kapp->config(); config->setGroup("Settings"); config->writeEntry("sound", yes); config->sync(); +} + +void Kolf::initPlugins() +{ + //kdDebug(12007) << "initPlugins" << endl; + if (game) + game->pause(); + + obj->setAutoDelete(true); + obj->clear(); + plugins.setAutoDelete(false); + plugins.clear(); + + // add prefab objects + obj->append(new SlopeObj()); + obj->append(new PuddleObj()); + obj->append(new WallObj()); + obj->append(new CupObj()); + obj->append(new SandObj()); + obj->append(new WindmillObj()); + obj->append(new BlackHoleObj()); + obj->append(new FloaterObj()); + obj->append(new BridgeObj()); + obj->append(new SignObj()); + obj->append(new BumperObj()); + + ObjectList *other = PluginLoader::loadAll(); + Object *object = 0; + for (object = other->first(); object; object = other->next()) + { + obj->append(object); + plugins.append(object); + } + + if (game) + { + game->setObjects(obj); + game->unPause(); + } + + //kdDebug(12007) << "end of initPlugins" << endl; +} + +void Kolf::showPlugins() +{ + QString text = QString("

    %1

      ").arg(i18n("Currently Loaded Plugins")); + Object *object = 0; + for (object = plugins.first(); object; object = plugins.next()) + { + text.append("
    1. "); + text.append(object->name()); + text.append(" - "); + text.append(i18n("by %1").arg(object->author())); + text.append("
    2. "); + } + text.append("
    "); + KMessageBox::information(this, text, i18n("Plugins")); +} + +void Kolf::enableAllMessages() +{ + KMessageBox::enableAllMessages(); +} + +void Kolf::setCurrentHole(int hole) +{ + if (!holeAction) + return; + // Golf is 1-based, KListAction is 0-based + holeAction->setCurrentItem(hole - 1); +} + +#include "kolf.moc" diff --git a/kolf/kolf.desktop b/kolf/kolf.desktop new file mode 100644 index 00000000..454dcfda --- /dev/null +++ b/kolf/kolf.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +DocPath=kolf/index.html +Name=Kolf +Name[ar]=لعبة الجول٠(Kolf) +Name[be]=Гольф +Name[bn]=কলà§â€Œà¦« +Name[hi]=कोलà¥à¤« +Name[ne]=कोलà¥à¤« +Name[pa]=ਕੇ-ਗੋਲਫ਼ +Name[ta]=காலà¯à®ƒà®ªà¯ +Name[tg]=Колф + +Type=Application +Exec=kolf %U +Icon=kolf +MimeType=application/x-kourse;application/x-kolf; + +GenericName=Miniature Golf +GenericName[be]=Маленькі гольф +GenericName[bg]=Миниатюрен голф +GenericName[bn]=ছোটখাটো গলফ +GenericName[bs]=Minijaturni golf +GenericName[ca]=Golf en miniatura +GenericName[cs]=Miniaturní golf +GenericName[cy]=Golff Bach +GenericName[da]=Miniaturegolf +GenericName[de]=Minigolf +GenericName[el]=Μίνι γκολφ +GenericName[eo]=Eta golfludo +GenericName[es]=Golf en miniatura +GenericName[et]=Miniatuurne golf +GenericName[eu]=Golf txikia +GenericName[fi]=Pienoisgolf +GenericName[fr]=Golf miniature +GenericName[gl]=Golf en miniatura +GenericName[he]=מיני־גולף +GenericName[hi]=लघॠगोलà¥à¤« +GenericName[hr]=Mini-golf +GenericName[hu]=Minigolf +GenericName[is]=Minigolf +GenericName[it]=Minigolf +GenericName[ja]=ミニãƒãƒ¥ã‚¢ã‚´ãƒ«ãƒ• +GenericName[ko]=모형 골프 +GenericName[lt]=MiniatiÅ«rinis golfas +GenericName[lv]=MiniatÅ«rs golfs +GenericName[mk]=Минијатурен голф +GenericName[nb]=Minigolf +GenericName[nds]=Minigolf +GenericName[ne]=सानो गलà¥à¤« +GenericName[nl]=Miniatuurgolf +GenericName[nn]=Minigolf +GenericName[pa]=ਛੋਟੀ ਗੋਲਫ਼ ਭੇਜੋ +GenericName[pl]=Miniaturowy Golf +GenericName[pt]=Golfe em Miniatura +GenericName[pt_BR]=Mini-Golf +GenericName[ro]=Golf în miniatură +GenericName[ru]=Гольф +GenericName[se]=Minigolfa +GenericName[sk]=Miniatúrny golf +GenericName[sl]=Mini golf +GenericName[sr]=Минијатурни голф +GenericName[sr@Latn]=Minijaturni golf +GenericName[sv]=Minigolf +GenericName[ta]=சிறிய காலà¯à®ƒà®ªà¯ +GenericName[tg]=Колф дар шакли хурд +GenericName[tr]=Minyatür Golf +GenericName[uk]=Мініатюрний гольф +GenericName[wa]=Golf miniateure +GenericName[xh]=Igalufa encinci +GenericName[zh_CN]=微型高尔夫 +GenericName[zh_TW]=迷你高爾夫 +GenericName[zu]=Igalofu elincanyana +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kolf/kolf.h b/kolf/kolf.h new file mode 100644 index 00000000..8a2c6d78 --- /dev/null +++ b/kolf/kolf.h @@ -0,0 +1,147 @@ +#ifndef KOLF_H +#define KOLF_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include "game.h" + +class KolfGame; +class KToggleAction; +class KListAction; +class KAction; +class QGridLayout; +class ScoreBoard; +class QCloseEvent; +class QEvent; +class Player; +class QWidget; +class Editor; + +class KDE_EXPORT Kolf : public KMainWindow +{ + Q_OBJECT + +public: + Kolf(); + ~Kolf(); + + void openURL(KURL url); + +public slots: + void closeGame(); + void updateModified(bool); + +protected: + virtual bool queryClose(); + +protected slots: + void startNewGame(); + void loadGame(); + void tutorial(); + void newGame(); + void save(); + void saveAs(); + void saveGame(); + void saveGameAs(); + void print(); + void newPlayersTurn(Player *); + void gameOver(); + void editingStarted(); + void editingEnded(); + void checkEditing(); + void setHoleFocus() { game->setFocus(); } + void inPlayStart(); + void inPlayEnd(); + void maxStrokesReached(const QString &); + void updateHoleMenu(int); + void titleChanged(const QString &); + void newStatusText(const QString &); + void showInfoChanged(bool); + void useMouseChanged(bool); + void useAdvancedPuttingChanged(bool); + void showGuideLineChanged(bool); + void soundChanged(bool); + void initPlugins(); + void showPlugins(); + void showHighScores(); + void enableAllMessages(); + void createSpacer(); + + void emptySlot() {}; + + void setCurrentHole(int); + +private: + QWidget *dummy; + KolfGame *game; + Editor *editor; + KolfGame *spacer; + void initGUI(); + QString filename; + PlayerList players; + PlayerList spacerPlayers; + QGridLayout *layout; + ScoreBoard *scoreboard; + KToggleAction *editingAction; + KAction *newHoleAction; + KAction *resetHoleAction; + KAction *undoShotAction; + //KAction *replayShotAction; + KAction *clearHoleAction; + KAction *tutorialAction; + KAction *newAction; + KAction *endAction; + KAction *printAction; + KAction *saveAction; + KAction *saveAsAction; + KAction *saveGameAction; + KAction *saveGameAsAction; + KAction *loadGameAction; + KAction *aboutAction; + KListAction *holeAction; + KAction *highScoreAction; + KAction *nextAction; + KAction *prevAction; + KAction *firstAction; + KAction *lastAction; + KAction *randAction; + KToggleAction *showInfoAction; + KToggleAction *useMouseAction; + KToggleAction *useAdvancedPuttingAction; + KToggleAction *showGuideLineAction; + KToggleAction *soundAction; + void setHoleMovementEnabled(bool); + void setHoleOtherEnabled(bool); + inline void setEditingEnabled(bool); + bool competition; + + // contains everything + ObjectList *obj; + // contains subset of obj + ObjectList plugins; + + QString loadedGame; + + bool isTutorial; + bool courseModified; + QString title; + QString tempStatusBarText; +}; + +struct HighScore +{ + HighScore() {} + HighScore(const QString &name, int score) { this->name = name; this->score = score; } + QString name; + int score; +}; +typedef QValueList HighScoreList; + +#endif diff --git a/kolf/kolf.magic b/kolf/kolf.magic new file mode 100644 index 00000000..01bc5070 --- /dev/null +++ b/kolf/kolf.magic @@ -0,0 +1,2 @@ +0 string [0-course@-50,-50] application/x-kourse +0 string [0\ Saved\ Game] application/x-kolf diff --git a/kolf/kolfui.rc b/kolf/kolfui.rc new file mode 100644 index 00000000..b38d243d --- /dev/null +++ b/kolf/kolfui.rc @@ -0,0 +1,71 @@ + + + + + + + + + + + + Ho&le + + + + + + + + + + + + + + &Go + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kolf/kvolumecontrol.cpp b/kolf/kvolumecontrol.cpp new file mode 100644 index 00000000..11d0be15 --- /dev/null +++ b/kolf/kvolumecontrol.cpp @@ -0,0 +1,67 @@ +#include + +#include +#include + +#include "kvolumecontrol.h" + +KVolumeControl::KVolumeControl(Arts::SoundServerV2 server, KPlayObject *parent) + : QObject(parent) +{ + init(server); +} + +KVolumeControl::KVolumeControl(double vol, Arts::SoundServerV2 server, KPlayObject *parent) + : QObject(parent) +{ + init(server); + setVolume(vol); +} + +KVolumeControl::~KVolumeControl() +{ + manager.stop(); + volumeControl.stop(); +} + +void KVolumeControl::init(Arts::SoundServerV2 server) +{ + manager = Arts::DynamicCast(server.createObject("Arts::Synth_AMAN_PLAY")); + if (manager.isNull()) + { + kdError() << "Your OS is broken. Get an OS that installs KDE decently." << endl; + return; + } + manager.start(); + + volumeControl = Arts::DynamicCast(server.createObject("Arts::StereoVolumeControl")); + if (volumeControl.isNull()) + { + kdError() << "Your OS is broken. Get an OS that installs KDE decently." << endl; + return; + } + volumeControl.start(); + + Arts::connect((static_cast(parent()))->object(), "left", volumeControl, "inleft"); + Arts::connect((static_cast(parent()))->object(), "right", volumeControl, "inright"); + + Arts::connect(volumeControl, manager); +} + +void KVolumeControl::setVolume(double d) +{ + if (volumeControl.isNull()) + return; + + volumeControl.scaleFactor(d); +} + +double KVolumeControl::volume(void) +{ + if (volumeControl.isNull()) + return -1; + + return volumeControl.scaleFactor(); +} + +#include "kvolumecontrol.moc" diff --git a/kolf/kvolumecontrol.h b/kolf/kvolumecontrol.h new file mode 100644 index 00000000..3f0306a8 --- /dev/null +++ b/kolf/kvolumecontrol.h @@ -0,0 +1,28 @@ +#ifndef KVOLUMECONTROL_H +#define KVOLUMECONTROL_H + +#include +#include +#include +#include + +class KVolumeControl : public QObject +{ +Q_OBJECT + +public: + KVolumeControl(Arts::SoundServerV2 server, KPlayObject *parent); + KVolumeControl(double vol, Arts::SoundServerV2 server, KPlayObject *parent); + ~KVolumeControl(); + + void setVolume(double); + double volume(void); + + void init(Arts::SoundServerV2 server); + +private: + Arts::StereoVolumeControl volumeControl; + Arts::Synth_AMAN_PLAY manager; +}; + +#endif diff --git a/kolf/main.cpp b/kolf/main.cpp new file mode 100644 index 00000000..3a63c1f1 --- /dev/null +++ b/kolf/main.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "kolf.h" + +#include +#include +using namespace std; + +static const char description[] = +I18N_NOOP("KDE Minigolf Game"); + +static const char version[] = "1.1.1"; + +static KCmdLineOptions options[] = +{ + { "+file", I18N_NOOP("File"), 0 }, + { "course-info ", I18N_NOOP("Print course information and exit"), 0 }, + KCmdLineLastOption +}; + + +extern "C" KDE_EXPORT int kdemain(int argc, char **argv) +{ + KAboutData aboutData( "kolf", I18N_NOOP("Kolf"), version, description, KAboutData::License_GPL, "(c) 2002-2005, Jason Katz-Brown", 0, "http://www.katzbrown.com/kolf/"); + + aboutData.addAuthor("Jason Katz-Brown", I18N_NOOP("Main author"), "jason@katzbrown.com"); + aboutData.addAuthor("Niklas Knutsson", I18N_NOOP("Advanced putting mode"), 0); + aboutData.addAuthor("Rik Hemsley", I18N_NOOP("Border around course"), 0); + aboutData.addAuthor("Ryan Cumming", I18N_NOOP("Vector class"), 0); + aboutData.addAuthor("Daniel Matza-Brown", I18N_NOOP("Working wall-bouncing algorithm"), 0); + aboutData.addAuthor("Timo A. Hummel", I18N_NOOP("Some good sound effects"), "timo.hummel@gmx.net"); + + aboutData.addCredit("Rob Renaud", I18N_NOOP("Wall-bouncing help"), 0); + aboutData.addCredit("Aaron Seigo", I18N_NOOP("Suggestions, bug reports"), 0); + + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineArgs::addCmdLineOptions(options); + + // I've actually added this for my web site uploaded courses display + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + if (args->isSet("course-info")) + { + KCmdLineArgs::enable_i18n(); + + QString filename(QFile::decodeName(args->getOption("course-info"))); + if (QFile::exists(filename)) + { + CourseInfo info; + KolfGame::courseInfo(info, filename); + + cout << info.name.latin1() + << " - " << i18n("By %1").arg(info.author).latin1() + << " - " << i18n("%1 holes").arg(info.holes).latin1() + << " - " << i18n("par %1").arg(info.par).latin1() + << endl; + + return 0; + } + else + { + KCmdLineArgs::usage(i18n("Course %1 does not exist.").arg(filename.latin1())); + } + } + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + Kolf *top = new Kolf; + + if (args->count() >= 1) + { + KURL url = args->url(args->count() - 1); + top->openURL(url); + args->clear(); + } + else + top->closeGame(); + + a.setMainWidget(top); + top->show(); + + return a.exec(); +} + diff --git a/kolf/newgame.cpp b/kolf/newgame.cpp new file mode 100644 index 00000000..d038185e --- /dev/null +++ b/kolf/newgame.cpp @@ -0,0 +1,347 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "newgame.h" +#include "game.h" + +NewGameDialog::NewGameDialog(bool enableCourses, QWidget *parent, const char *_name) + : KDialogBase(KDialogBase::TreeList, i18n("New Game"), Ok | Cancel, Ok, parent, _name) +{ + this->enableCourses = enableCourses; + + editors.setAutoDelete(true); + KConfig *config = kapp->config(); + + // lots o' colors :) + startColors << yellow << blue << red << lightGray << cyan << darkBlue << magenta << darkGray << darkMagenta << darkYellow; + + playerPage = addPage(i18n("Players")); + QVBoxLayout *bigLayout = new QVBoxLayout(playerPage, marginHint(), spacingHint()); + + addButton = new KPushButton(i18n("&New Player"), playerPage); + bigLayout->addWidget(addButton); + + connect(addButton, SIGNAL(clicked()), this, SLOT(addPlayer())); + + scroller = new QScrollView(playerPage); + bigLayout->addWidget(scroller); + layout = new QVBox(scroller->viewport()); + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + scroller->viewport()->setBackgroundPixmap(grass); + scroller->addChild(layout); + + QMap entries = config->entryMap("New Game Dialog"); + unsigned int i = 0; + for (QMap::Iterator it = entries.begin(); it != entries.end(); ++it) + { + if (i > startColors.count()) + return; + + addPlayer(); + editors.last()->setName(it.key().right(it.key().length() - 1)); + editors.last()->setColor(QColor(it.data())); + ++i; + } + + if (editors.isEmpty()) + { + addPlayer(); + addPlayer(); + } + + enableButtons(); + + if (enableCourses) + { + coursePage = addPage(i18n("Course"), i18n("Choose Course to Play")); + QVBoxLayout *coursePageLayout = new QVBoxLayout(coursePage, marginHint(), spacingHint()); + + KURLLabel *coursesLink = new KURLLabel("http://web.mit.edu/~jasonkb/www/kolf/", "http://web.mit.edu/~jasonkb/www/kolf/", coursePage); + connect(coursesLink, SIGNAL(leftClickedURL(const QString &)), kapp, SLOT(invokeBrowser(const QString &))); + coursePageLayout->addWidget(coursesLink); + + QHBoxLayout *hlayout = new QHBoxLayout(coursePageLayout, spacingHint()); + + // following use this group + config->setGroup("New Game Dialog Mode"); + + // find other courses + externCourses = config->readListEntry("extra"); + + /// course loading + QStringList items = externCourses + KGlobal::dirs()->findAllResources("appdata", "courses/*"); + QStringList nameList; + const QString lastCourse(config->readEntry("course", "")); + int curItem = 0; + i = 0; + for (QStringList::Iterator it = items.begin(); it != items.end(); ++it, ++i) + { + QString file = *it; + CourseInfo curinfo; + KolfGame::courseInfo(curinfo, file); + info[file] = curinfo; + names.append(file); + nameList.append(curinfo.name); + + if (lastCourse == file) + curItem = i; + } + + const QString newName(i18n("Create New")); + info[QString::null] = CourseInfo(newName, newName, i18n("You"), 0, 0); + names.append(QString::null); + nameList.append(newName); + + courseList = new KListBox(coursePage); + hlayout->addWidget(courseList); + courseList->insertStringList(nameList); + courseList->setCurrentItem(curItem); + connect(courseList, SIGNAL(highlighted(int)), this, SLOT(courseSelected(int))); + connect(courseList, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + + QVBoxLayout *detailLayout = new QVBoxLayout(hlayout, spacingHint()); + name = new QLabel(coursePage); + detailLayout->addWidget(name); + author = new QLabel(coursePage); + detailLayout->addWidget(author); + + QHBoxLayout *minorLayout = new QHBoxLayout(detailLayout, spacingHint()); + par = new QLabel(coursePage); + minorLayout->addWidget(par); + holes = new QLabel(coursePage); + minorLayout->addWidget(holes); + + detailLayout->addStretch(); + KPushButton *scores = new KPushButton(i18n("Highscores"), coursePage); + connect(scores, SIGNAL(clicked()), this, SLOT(showHighscores())); + detailLayout->addWidget(scores); + + detailLayout->addStretch(); + detailLayout->addWidget(new KSeparator(coursePage)); + + minorLayout = new QHBoxLayout(detailLayout, spacingHint()); + + KPushButton *addCourseButton = new KPushButton(i18n("Add..."), coursePage); + minorLayout->addWidget(addCourseButton); + connect(addCourseButton, SIGNAL(clicked()), this, SLOT(addCourse())); + + remove = new KPushButton(i18n("Remove"), coursePage); + minorLayout->addWidget(remove); + connect(remove, SIGNAL(clicked()), this, SLOT(removeCourse())); + + courseSelected(curItem); + selectionChanged(); + } + + // options page + optionsPage = addPage(i18n("Options"), i18n("Game Options")); + QVBoxLayout *vlayout = new QVBoxLayout(optionsPage, marginHint(), spacingHint()); + + mode = new QCheckBox(i18n("&Strict mode"), optionsPage); + vlayout->addWidget(mode); + mode->setChecked(config->readBoolEntry("competition", false)); + + QLabel *desc = new QLabel(i18n("In strict mode, undo, editing, and switching holes is not allowed. This is generally for competition. Only in strict mode are highscores kept."), optionsPage); + desc->setTextFormat(RichText); + vlayout->addWidget(desc); +} + +void NewGameDialog::slotOk() +{ + KConfig *config = kapp->config(); + + config->setGroup("New Game Dialog Mode"); + config->writeEntry("competition", mode->isChecked()); + if (enableCourses) + { + config->writeEntry("course", currentCourse); + config->writeEntry("extra", externCourses); + } + + config->deleteGroup("New Game Dialog"); + config->setGroup("New Game Dialog"); + + PlayerEditor *curEditor = 0; + int i = 0; + for (curEditor = editors.first(); curEditor; curEditor = editors.next(), ++i) + config->writeEntry(QString::number(i) + curEditor->name(), curEditor->color().name()); + + config->sync(); + + KDialogBase::slotOk(); +} + +void NewGameDialog::courseSelected(int index) +{ + currentCourse = *names.at(index); + + CourseInfo &curinfo = info[currentCourse]; + + name->setText(QString("%1").arg(curinfo.name)); + + author->setText(i18n("By %1").arg(curinfo.author)); + par->setText(i18n("Par %1").arg(curinfo.par)); + holes->setText(i18n("%1 Holes").arg(curinfo.holes)); +} + +void NewGameDialog::showHighscores() +{ + KScoreDialog *scoreDialog = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Custom1 | KScoreDialog::Score, this); + scoreDialog->addField(KScoreDialog::Custom1, i18n("Par"), "Par"); + scoreDialog->setConfigGroup(info[currentCourse].untranslatedName + QString(" Highscores")); + scoreDialog->setComment(i18n("High Scores for %1").arg(info[currentCourse].name)); + scoreDialog->show(); +} + +void NewGameDialog::removeCourse() +{ + int curItem = courseList->currentItem(); + if (curItem < 0) + return; + + QString file = *names.at(curItem); + if (externCourses.contains(file) < 1) + return; + + names.remove(file); + externCourses.remove(file); + courseList->removeItem(curItem); + + selectionChanged(); +} + +void NewGameDialog::selectionChanged() +{ + const int curItem = courseList->currentItem(); + remove->setEnabled(!(curItem < 0 || externCourses.contains(*names.at(curItem)) < 1)); +} + +void NewGameDialog::addCourse() +{ + QStringList files = KFileDialog::getOpenFileNames(":kourses", QString::fromLatin1("application/x-kourse"), this, i18n("Pick Kolf Course")); + + bool hasDuplicates = false; + + for (QStringList::Iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) + { + if (names.contains(*fileIt) > 0) + { + hasDuplicates = true; + continue; + } + + CourseInfo curinfo; + KolfGame::courseInfo(curinfo, *fileIt); + info[*fileIt] = curinfo; + names.prepend(*fileIt); + externCourses.prepend(*fileIt); + + courseList->insertItem(curinfo.name, 0); + } + + if (hasDuplicates) + KMessageBox::information(this, i18n("Chosen course is already on course list.")); + + courseList->setCurrentItem(0); + courseSelected(0); + selectionChanged(); +} + +void NewGameDialog::addPlayer() +{ + if (editors.count() >= startColors.count()) + return; + + editors.append(new PlayerEditor(i18n("Player %1").arg(editors.count() + 1), *startColors.at(editors.count()), layout)); + editors.last()->show(); + connect(editors.last(), SIGNAL(deleteEditor(PlayerEditor *)), this, SLOT(deleteEditor(PlayerEditor *))); + + enableButtons(); +} + +void NewGameDialog::deleteEditor(PlayerEditor *editor) +{ + if (editors.count() < 2) + return; + + editors.removeRef(editor); + + enableButtons(); +} + +void NewGameDialog::enableButtons() +{ + addButton->setEnabled(!(editors.count() >= startColors.count())); +} + +///////////////////////// + +PlayerEditor::PlayerEditor(QString startName, QColor startColor, QWidget *parent, const char *_name) + : QWidget(parent, _name) +{ + QHBoxLayout *layout = new QHBoxLayout(this, KDialogBase::spacingHint()); + + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + setBackgroundPixmap(grass); + + editor = new KLineEdit(this); + layout->addWidget(editor); + editor->setFrame(false); + editor->setText(startName); + layout->addStretch(); + layout->addWidget(colorButton = new KColorButton(startColor, this)); + colorButton->setAutoMask(true); + colorButton->setBackgroundPixmap(grass); + + KPushButton *remove = new KPushButton(i18n("Remove"), this); + remove->setAutoMask(true); + layout->addWidget(remove); + remove->setBackgroundPixmap(grass); + connect(remove, SIGNAL(clicked()), this, SLOT(removeMe())); +} + +void PlayerEditor::removeMe() +{ + emit deleteEditor(this); +} + +#include "newgame.moc" diff --git a/kolf/newgame.h b/kolf/newgame.h new file mode 100644 index 00000000..b9770a80 --- /dev/null +++ b/kolf/newgame.h @@ -0,0 +1,106 @@ +#ifndef NEWGAME_H +#define NEWGAME_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "game.h" + +class KLineEdit; +class QFrame; +class QVBoxLayout; +class QVBox; +class QPainter; +class KListBox; +class QEvent; + +class PlayerEditor : public QWidget +{ + Q_OBJECT + +public: + PlayerEditor(QString name = QString::null, QColor = red, QWidget *parent = 0, const char *_name = 0); + QColor color() { return colorButton->color(); } + QString name() { return editor->text(); } + void setColor(QColor col) { colorButton->setColor(col); } + void setName(const QString &newname) { editor->setText(newname); } + +signals: + void deleteEditor(PlayerEditor *editor); + +private slots: + void removeMe(); + +private: + KLineEdit *editor; + KColorButton *colorButton; + QPixmap grass; +}; + +class NewGameDialog : public KDialogBase +{ + Q_OBJECT + +public: + NewGameDialog(bool enableCourses, QWidget *parent, const char *_name = 0); + QPtrList *players() { return &editors; } + bool competition() { return mode->isChecked(); } + QString course() { return currentCourse; } + +public slots: + void deleteEditor(PlayerEditor *); + +protected slots: + void slotOk(); + +private slots: + void addPlayer(); + void courseSelected(int); + void addCourse(); + void removeCourse(); + void selectionChanged(); + void showHighscores(); + +private: + QVBox *layout; + KPushButton *addButton; + QFrame *playerPage; + QScrollView *scroller; + QFrame *coursePage; + QFrame *optionsPage; + QValueList startColors; + QPtrList editors; + KPushButton *remove; + QCheckBox *mode; + + QPixmap grass; + + QStringList names; + QStringList externCourses; + QMap info; + + QStringList extraCourses; + + KListBox *courseList; + QLabel *name; + QLabel *author; + QLabel *par; + QLabel *holes; + + QString currentCourse; + + void enableButtons(); + bool enableCourses; +}; + +#endif diff --git a/kolf/object.cpp b/kolf/object.cpp new file mode 100644 index 00000000..aea33e01 --- /dev/null +++ b/kolf/object.cpp @@ -0,0 +1,2 @@ +#include "object.h" +#include "object.moc" diff --git a/kolf/object.h b/kolf/object.h new file mode 100644 index 00000000..a2bf2ef1 --- /dev/null +++ b/kolf/object.h @@ -0,0 +1,30 @@ +// it seems that OBJECT_H is used by something else + +#ifndef KOLF_OBJECT_H +#define KOLF_OBJECT_H + +#include +#include +#include + +class Object : public QObject +{ + Q_OBJECT + +public: + Object(QObject *parent = 0, const char *name = 0) : QObject(parent, name) { m_addOnNewHole = false; } + virtual QCanvasItem *newObject(QCanvas * /*canvas*/) { return 0; } + QString name() { return m_name; } + QString _name() { return m__name; } + QString author() { return m_author; } + bool addOnNewHole() { return m_addOnNewHole; } + +protected: + QString m_name; + QString m__name; + QString m_author; + bool m_addOnNewHole; +}; +typedef QPtrList ObjectList; + +#endif diff --git a/kolf/objects/Makefile.am b/kolf/objects/Makefile.am new file mode 100644 index 00000000..6410a006 --- /dev/null +++ b/kolf/objects/Makefile.am @@ -0,0 +1 @@ +# SUBDIRS = test poolball diff --git a/kolf/objects/poolball/Makefile.am b/kolf/objects/poolball/Makefile.am new file mode 100644 index 00000000..0388f4dd --- /dev/null +++ b/kolf/objects/poolball/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES= $(all_includes) +lib_LTLIBRARIES = libkolfpoolball.la + +libkolfpoolball_la_SOURCES = poolball.cpp + +libkolfpoolball_la_LDFLAGS = $(all_libraries) $(LIB_KIO) -lkolf -module -avoid-version + +libkolfpoolball_la_METASOURCES = AUTO + +noinst_HEADERS = poolball.h + +kolf_DATA = poolball.plugin +kolfdir = $(kde_datadir)/kolf diff --git a/kolf/objects/poolball/poolball.cpp b/kolf/objects/poolball/poolball.cpp new file mode 100644 index 00000000..a5ca80ec --- /dev/null +++ b/kolf/objects/poolball/poolball.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "poolball.h" + +K_EXPORT_COMPONENT_FACTORY(libkolfpoolball, PoolBallFactory) +QObject *PoolBallFactory::createObject (QObject *, const char *, const char *, const QStringList &) { return new PoolBallObj; } + +PoolBall::PoolBall(QCanvas *canvas) + : Ball(canvas) +{ + setBrush(black); + m_number = 1; +} + +void PoolBall::save(KConfig *cfg) +{ + cfg->writeEntry("number", number()); +} + +void PoolBall::saveState(StateDB *db) +{ + db->setPoint(QPoint(x(), y())); +} + +void PoolBall::load(KConfig *cfg) +{ + setNumber(cfg->readNumEntry("number", 1)); +} + +void PoolBall::loadState(StateDB *db) +{ + move(db->point().x(), db->point().y()); + setVelocity(0, 0); + setState(Stopped); +} + +void PoolBall::draw(QPainter &p) +{ + // we should draw the number here + Ball::draw(p); +} + +PoolBallConfig::PoolBallConfig(PoolBall *poolBall, QWidget *parent) + : Config(parent), m_poolBall(poolBall) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + layout->addStretch(); + + QLabel *num = new QLabel(i18n("Number:"), this); + layout->addWidget(num); + KIntNumInput *slider = new KIntNumInput(m_poolBall->number(), this); + slider->setRange(1, 15); + layout->addWidget(slider); + + layout->addStretch(); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(numberChanged(int))); +} + +void PoolBallConfig::numberChanged(int newNumber) +{ + m_poolBall->setNumber(newNumber); + changed(); +} + +Config *PoolBall::config(QWidget *parent) +{ + return new PoolBallConfig(this, parent); +} + +#include "poolball.moc" diff --git a/kolf/objects/poolball/poolball.h b/kolf/objects/poolball/poolball.h new file mode 100644 index 00000000..eeb851b2 --- /dev/null +++ b/kolf/objects/poolball/poolball.h @@ -0,0 +1,63 @@ +#ifndef KOLFPOOLBALL_H +#define KOLFPOOLBALL_H + +#include +#include +#include + +#include + +#include +#include +#include +#include + +class StateDB; +class KConfig; + +class PoolBallFactory : KLibFactory { Q_OBJECT public: QObject *createObject(QObject *, const char *, const char *, const QStringList & = QStringList()); }; + +class PoolBall : public Ball +{ +public: + PoolBall(QCanvas *canvas); + + virtual bool deleteable() const { return true; } + + virtual Config *config(QWidget *parent); + virtual void saveState(StateDB *); + virtual void save(KConfig *cfg); + virtual void loadState(StateDB *); + virtual void load(KConfig *cfg); + virtual void draw(QPainter &); + virtual bool fastAdvance() const { return true; } + + int number() const { return m_number; } + void setNumber(int newNumber) { m_number = newNumber; update(); } + +private: + int m_number; +}; + +class PoolBallConfig : public Config +{ + Q_OBJECT + +public: + PoolBallConfig(PoolBall *poolBall, QWidget *parent); + +private slots: + void numberChanged(int); + +private: + PoolBall *m_poolBall; +}; + +class PoolBallObj : public Object +{ +public: + PoolBallObj() { m_name = i18n("Pool Ball"); m__name = "poolball"; m_author = "Jason Katz-Brown"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new PoolBall(canvas); } +}; + +#endif diff --git a/kolf/objects/poolball/poolball.plugin b/kolf/objects/poolball/poolball.plugin new file mode 100644 index 00000000..c305251f --- /dev/null +++ b/kolf/objects/poolball/poolball.plugin @@ -0,0 +1 @@ +Filename=libkolfpoolball diff --git a/kolf/objects/test/Makefile.am b/kolf/objects/test/Makefile.am new file mode 100644 index 00000000..4e9bb33f --- /dev/null +++ b/kolf/objects/test/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES= $(all_includes) +lib_LTLIBRARIES = libkolftest.la + +libkolftest_la_SOURCES = test.cpp + +libkolftest_la_LDFLAGS = $(all_libraries) $(LIB_KIO) -lkolf -module -avoid-version + +libkolftest_la_METASOURCES = AUTO + +noinst_HEADERS = test.h + +kolf_DATA = test.plugin +kolfdir = $(kde_datadir)/kolf diff --git a/kolf/objects/test/test.cpp b/kolf/objects/test/test.cpp new file mode 100644 index 00000000..2c3d564f --- /dev/null +++ b/kolf/objects/test/test.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "test.h" + +K_EXPORT_COMPONENT_FACTORY(libkolftest, TestFactory) +QObject *TestFactory::createObject (QObject * /*parent*/, const char * /*name*/, const char * /*classname*/, const QStringList & /*args*/) +{ return new TestObj; } + +Test::Test(QCanvas *canvas) + : QCanvasEllipse(60, 40, canvas), count(0), m_switchEvery(20) +{ + // force to the bottom of other objects + setZ(-100000); + + // we want calls to advance() even though we have no velocity + setAnimated(true); +} + +void Test::advance(int phase) +{ + QCanvasEllipse::advance(phase); + + // phase is either 0 or 1, only calls with 1 should be handled + if (phase == 1) + { + // this makes it so the body is called every + // m_switchEvery times + if (count % m_switchEvery == 0) + { + // random color + const QColor myColor((QRgb)(kapp->random() % 0x01000000)); + + // set the brush, so our shape is drawn + // with the random color + setBrush(QBrush(myColor)); + + count = 0; + } + + count++; + } +} + +void Test::save(KConfig *cfg) +{ + // save our option from the course + // (courses are represented as KConfig files) + cfg->writeEntry("switchEvery", switchEvery()); +} + +void Test::load(KConfig *cfg) +{ + // load our option + setSwitchEvery(cfg->readNumEntry("switchEvery", 50)); +} + +TestConfig::TestConfig(Test *test, QWidget *parent) + : Config(parent), m_test(test) +{ + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + + layout->addStretch(); + + layout->addWidget(new QLabel(i18n("Flash speed"), this)); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + QLabel *slow = new QLabel(i18n("Slow"), this); + hlayout->addWidget(slow); + QSlider *slider = new QSlider(1, 100, 5, 101 - m_test->switchEvery(), Qt::Horizontal, this); + hlayout->addWidget(slider); + QLabel *fast = new QLabel(i18n("Fast"), this); + hlayout->addWidget(fast); + + layout->addStretch(); + + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(switchEveryChanged(int))); +} + +void TestConfig::switchEveryChanged(int news) +{ + // update our object + m_test->setSwitchEvery((101 - news)); + + // tells Kolf the hole was modified + changed(); +} + +Config *Test::config(QWidget *parent) +{ + return new TestConfig(this, parent); +} + +#include "test.moc" diff --git a/kolf/objects/test/test.h b/kolf/objects/test/test.h new file mode 100644 index 00000000..3086a578 --- /dev/null +++ b/kolf/objects/test/test.h @@ -0,0 +1,56 @@ +#ifndef KOLFTEST_H +#define KOLFTEST_H + +#include +#include + +#include + +#include +#include + +class KConfig; + +class TestFactory : KLibFactory { Q_OBJECT public: QObject *createObject(QObject *, const char *, const char *, const QStringList & = QStringList()); }; + +class Test : public QCanvasEllipse, public CanvasItem +{ +public: + Test(QCanvas *canvas); + + virtual Config *config(QWidget *parent); + virtual void save(KConfig *cfg); + virtual void load(KConfig *cfg); + + virtual void advance(int phase); + + int switchEvery() const { return m_switchEvery / 2; } + void setSwitchEvery(int news) { m_switchEvery = news * 2; } + +private: + int count; + int m_switchEvery; +}; + +class TestConfig : public Config +{ + Q_OBJECT + +public: + TestConfig(Test *test, QWidget *parent); + +private slots: + void switchEveryChanged(int news); + +private: + Test *m_test; +}; + +class TestObj : public Object +{ +public: + TestObj() { m_name = i18n("Flash"); m__name = "flash"; m_author = "Jason Katz-Brown"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Test(canvas); } +}; + +#endif diff --git a/kolf/objects/test/test.plugin b/kolf/objects/test/test.plugin new file mode 100644 index 00000000..c58bec40 --- /dev/null +++ b/kolf/objects/test/test.plugin @@ -0,0 +1 @@ +Filename=libkolftest diff --git a/kolf/pics/Makefile.am b/kolf/pics/Makefile.am new file mode 100644 index 00000000..e5515a85 --- /dev/null +++ b/kolf/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = AUTO diff --git a/kolf/pics/hi128-app-kolf.png b/kolf/pics/hi128-app-kolf.png new file mode 100644 index 0000000000000000000000000000000000000000..e11f930c6d246f3a795b2bf73b0ffdb13cc03223 GIT binary patch literal 9779 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 BZ-xK> literal 0 HcmV?d00001 diff --git a/kolf/pics/hi16-app-kolf.png b/kolf/pics/hi16-app-kolf.png new file mode 100644 index 0000000000000000000000000000000000000000..3dfe8c8638ac391eaabe016c314970055434491a GIT binary patch literal 665 zcmV;K0%rY*P)T2{ppYk?a<BZQu91jYdN_ zjw6z|CgpP3JUBQQUtWIl*!TUOlu8RBHBU}A4Ef7qvG`$QW4hI9rK{Ddg(Ebn)oN#! zmhLSEL03zuH0MGyE;Qp(GbS|WQpb$xrzXs-0uVxAS=RX| zigZ4o*XF>~Y&L(k+ij%NX>iWLIEQJNP`bZHk5a@Dg;EOp``)i02sD5r!RF@Xr>(86 zFScz@M^OaE7zhCj!vN~UUV<;2~&nuP6>L7GnfM(O%sMqT{m&;wXENfV){{C?sV|RC#t*xyUN~O|601*HU z00bwHN~IQT+kP=IF<}6J=Xt1BtBbzxKN|%9#4v!jNwEg-5y1H01dnt3nBeZeh5=v- z{aqP=i>DQtr+onE0O%!x*8d17fHwem@;;H@eN9*tA1n*$00000NkvXXu0mjfT`w$@ literal 0 HcmV?d00001 diff --git a/kolf/pics/hi22-app-kolf.png b/kolf/pics/hi22-app-kolf.png new file mode 100644 index 0000000000000000000000000000000000000000..0a06094cfd981ca5cd71137eacd6c98a5f64cf49 GIT binary patch literal 926 zcmV;P17ZA$P)z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v&6iGxuRCwB~l+R0JR}{y;_x&I-LCfGQTu3bx7d1u{ zp_{SLKcFj@EmFGau3hF|5O-aO8%tfen1OBtmz6*lRcz4)#!|3VB1F@KL|^W^Ki<9f zoo-CXJj_fM^M%8`T+aD$IOp zH=E5aG8$%qhz2pN#l^+)!NI|yQp!?BH#axUN~KZ>!!XE#2dt;SYinyS>h*e;-l5&y z-B&>ngkyo{=H@=G)oN|6H5p^bT1(D3A|guDl$>*9j3Fr{MNvdsTU$Sl1>V`&dD-c7 zh;vR-N>WOZwRRAFXs_3!R;xua?wH8T| zz!-xhNuafcF$P*|2qC~2!}|LAx7kI$G5q=Y`A;V&Cks+aOiWB53`6+755^cg&x0`r zzV9PR5=bebwMHDrD3{Bh=W@9z0MV1+nVFdn0l+zjPN#!nv4~tQhad>xoI{$XaL&P6 z3n?YyIELqWV2l;t3ckC$i>$Q>f&g(G0|1OMAR<_65r!dz5J=N>@UE0X6h%gBZAXsc z^78U^qtT#3p}_L_JUHhF!?52Kgb?sN4@r{1S_>fr^7%Y2E-wDkTDON;Z!E9fZofV~ zJ^dpLLnKLpBuSu@0_Pk;2oMo^y&mE?2Im~kIY=pSczE~{fE@`2!1wm{erUB?kWzwk z4y`qmQh0cHK%>!sQVK!{NGSmTM@L70o}Zupoc$G9Fs`q!Uu|w~ZuL9rcDv|yyHHBO zIfp2UAf<%w`#3o{x!K;{{tm!@Sq>XvLWPBeh04ap#`m+cv(JjfBGNP+Zrc^5ZR4SD)H8llmE$a0;YPH%gx3{-nW;-3r-5&KEz!_tV zeBUR=7y%#v8&4&_)7>)q>JiqDDu9Xi0sa8MKSSWJM AB>(^b literal 0 HcmV?d00001 diff --git a/kolf/pics/hi32-app-kolf.png b/kolf/pics/hi32-app-kolf.png new file mode 100644 index 0000000000000000000000000000000000000000..06bf8809b00730419b032e5649e4b67ce59a56cb GIT binary patch literal 1986 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 Hu0mjfJHwWN literal 0 HcmV?d00001 diff --git a/kolf/pics/hi64-app-kolf.png b/kolf/pics/hi64-app-kolf.png new file mode 100644 index 0000000000000000000000000000000000000000..e6e7bccb846e842c9eb4c4c49d898f077a7e8c15 GIT binary patch literal 4216 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pluginloader.h" + +ObjectList *PluginLoader::loadAll() +{ + ObjectList *ret = new ObjectList; + + QStringList libs; + QStringList files = KGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true); + + for (QStringList::Iterator it = files.begin(); it != files.end(); ++it) + { + KSimpleConfig cfg(*it); + QString filename(cfg.readEntry("Filename", "")); + + libs.append(filename); + } + + for (QStringList::Iterator it = libs.begin(); it != libs.end(); ++it) + { + Object *newObject = load(*it); + if (newObject) + ret->append(newObject); + } + + return ret; +} + +Object *PluginLoader::load(const QString &filename) +{ + KLibFactory *factory = KLibLoader::self()->factory(filename.latin1()); + + if (!factory) + { + kdWarning() << "no factory for " << filename << "!" << endl; + return 0; + } + + QObject *newObject = factory->create(0, "objectInstance", "Object"); + + if (!newObject) + { + kdWarning() << "no newObject for " << filename << "!" << endl; + return 0; + } + + Object *ret = dynamic_cast(newObject); + + if (!ret) + kdWarning() << "no ret for " << filename << "!" << endl; + + return ret; +} + diff --git a/kolf/pluginloader.h b/kolf/pluginloader.h new file mode 100644 index 00000000..8d43db66 --- /dev/null +++ b/kolf/pluginloader.h @@ -0,0 +1,13 @@ +#ifndef PLUGINLOADER_H +#define PLUGINLOADER_H + +#include +#include + +namespace PluginLoader +{ + ObjectList *loadAll(); + Object *load(const QString &); +} + +#endif diff --git a/kolf/pool.kolf b/kolf/pool.kolf new file mode 100644 index 00000000..bd63df93 --- /dev/null +++ b/kolf/pool.kolf @@ -0,0 +1,303 @@ +[0-course@-50,-50] +author=Jason Katz-Brown +name=Pool + +[1-ball@205,302] +dummykey=true + +[1-blackhole@103,191|25] +exit=67,195 +exitDeg=90 +maxspeed=3 +minspeed=3 + +[1-blackhole@110,336|22] +exit=66,395 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[1-blackhole@115,54|21] +exit=129,25 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[1-blackhole@223,26|55] +exit=121,97 +exitDeg=0 +maxspeed=2 +minspeed=2 + +[1-blackhole@332,335|23] +exit=382,393 +exitDeg=90 +maxspeed=4 +minspeed=4 + +[1-blackhole@335,53|20] +exit=329,19 +exitDeg=180 +maxspeed=2 +minspeed=2 + +[1-blackhole@348,189|24] +exit=381,170 +exitDeg=90 +maxspeed=3 +minspeed=3 + +[1-hole@-50,-50|0] +borderWalls=false +hasFinalLoad=false +maxstrokes=4 +par=3 + +[1-poolball@-215,24|33] +number=1 + +[1-poolball@225,138|31] +number=1 + +[1-poolball@229,133|65] +number=1 + +[1-poolball@230,143|28] +number=1 + +[1-poolball@233,149|35] +number=1 + +[1-poolball@234,128|64] +number=1 + +[1-poolball@235,138|3] +number=1 + +[1-poolball@238,144|30] +number=1 + +[1-poolball@239,133|34] +number=1 + +[1-poolball@243,139|29] +number=1 + +[1-poolball@851,-273|32] +number=1 + +[1-slope@-2,0|57] +grade=5 +gradient=Diagonal +height=120 +reversed=true +stuckOnGround=false +width=127 + +[1-slope@194,2|54] +grade=4 +gradient=Elliptic +height=50 +reversed=true +stuckOnGround=false +width=50 + +[1-slope@314,315|61] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@318,32|60] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@329,170|59] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@332,0|56] +grade=8 +gradient=Opposite Diagonal +height=66 +reversed=true +stuckOnGround=false +width=68 + +[1-slope@78,168|58] +grade=4 +gradient=Elliptic +height=44 +reversed=true +stuckOnGround=false +width=44 + +[1-slope@88,315|63] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-slope@94,32|62] +grade=4 +gradient=Elliptic +height=40 +reversed=true +stuckOnGround=false +width=40 + +[1-wall@0,0|10] +endPoint=352,56 +startPoint=337,70 + +[1-wall@0,0|11] +endPoint=114,72 +startPoint=114,174 + +[1-wall@0,0|12] +endPoint=124,40 +startPoint=135,55 + +[1-wall@0,0|13] +endPoint=337,206 +startPoint=337,317 + +[1-wall@0,0|14] +endPoint=315,332 +startPoint=326,350 + +[1-wall@0,0|15] +endPoint=129,332 +startPoint=117,349 + +[1-wall@0,0|16] +endPoint=114,72 +startPoint=100,65 + +[1-wall@0,0|17] +endPoint=347,330 +startPoint=337,317 + +[1-wall@0,0|18] +endPoint=116,316 +startPoint=91,332 + +[1-wall@0,0|19] +endPoint=337,206 +startPoint=355,213 + +[1-wall@0,0|26] +endPoint=116,206 +startPoint=101,211 + +[1-wall@0,0|27] +endPoint=114,174 +startPoint=99,174 + +[1-wall@0,0|36] +endPoint=336,352 +startPoint=326,350 + +[1-wall@0,0|37] +endPoint=347,330 +startPoint=349,343 + +[1-wall@0,0|38] +endPoint=117,349 +startPoint=102,353 + +[1-wall@0,0|39] +endPoint=93,347 +startPoint=91,332 + +[1-wall@0,0|4] +endPoint=324,43 +startPoint=318,55 + +[1-wall@0,0|40] +endPoint=100,65 +startPoint=101,47 + +[1-wall@0,0|41] +endPoint=110,38 +startPoint=101,47 + +[1-wall@0,0|42] +endPoint=352,42 +startPoint=352,56 + +[1-wall@0,0|43] +endPoint=324,43 +startPoint=343,36 + +[1-wall@0,0|44] +endPoint=352,42 +startPoint=343,36 + +[1-wall@0,0|45] +endPoint=124,40 +startPoint=110,38 + +[1-wall@0,0|46] +endPoint=102,353 +startPoint=93,347 + +[1-wall@0,0|47] +endPoint=336,352 +startPoint=349,343 + +[1-wall@0,0|48] +endPoint=99,174 +startPoint=83,185 + +[1-wall@0,0|49] +endPoint=101,211 +startPoint=84,201 + +[1-wall@0,0|5] +endPoint=116,206 +startPoint=116,316 + +[1-wall@0,0|50] +endPoint=84,201 +startPoint=83,185 + +[1-wall@0,0|51] +endPoint=365,178 +startPoint=351,167 + +[1-wall@0,0|52] +endPoint=368,195 +startPoint=355,213 + +[1-wall@0,0|53] +endPoint=365,178 +startPoint=368,195 + +[1-wall@0,0|6] +endPoint=315,332 +startPoint=129,332 + +[1-wall@0,0|7] +endPoint=337,70 +startPoint=337,175 + +[1-wall@0,0|8] +endPoint=351,167 +startPoint=337,175 + +[1-wall@0,0|9] +endPoint=318,55 +startPoint=135,55 diff --git a/kolf/printdialogpage.cpp b/kolf/printdialogpage.cpp new file mode 100644 index 00000000..1fc6ad37 --- /dev/null +++ b/kolf/printdialogpage.cpp @@ -0,0 +1,35 @@ +#include +#include +#include + +#include +#include +#include + +#include "printdialogpage.h" + +PrintDialogPage::PrintDialogPage(QWidget *parent, const char *name) + : KPrintDialogPage( parent, name ) +{ + setTitle(i18n("Kolf Options")); + + QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + + titleCheck = new QCheckBox(i18n("Draw title text"), this); + titleCheck->setChecked(true); + layout->addWidget(titleCheck); +} + +void PrintDialogPage::getOptions(QMap &opts, bool /*incldef*/) +{ + opts["kde-kolf-title"] = titleCheck->isChecked()? "true" : "false"; +} + +void PrintDialogPage::setOptions(const QMap &opts) +{ + QString setting = opts["kde-kolf-title"]; + if (!!setting) + titleCheck->setChecked(setting == "true"); +} + +#include "printdialogpage.moc" diff --git a/kolf/printdialogpage.h b/kolf/printdialogpage.h new file mode 100644 index 00000000..047454a2 --- /dev/null +++ b/kolf/printdialogpage.h @@ -0,0 +1,27 @@ +#ifndef PRINTDIALOGPAGE_H +#define PRINTDIALOGPAGE_H + +#include +#include +#include + +class QCheckBox; +class QWidget; + +class PrintDialogPage : public KPrintDialogPage +{ + Q_OBJECT + + public: + PrintDialogPage(QWidget *parent = 0, const char *name = 0); + + //reimplement virtual functions + void getOptions(QMap &opts, bool incldef = false); + void setOptions(const QMap &opts); + + private: + QCheckBox *bgCheck; + QCheckBox *titleCheck; +}; + +#endif diff --git a/kolf/rtti.h b/kolf/rtti.h new file mode 100644 index 00000000..00f58536 --- /dev/null +++ b/kolf/rtti.h @@ -0,0 +1,6 @@ +#ifndef KOLF_RTTI_H +#define KOLF_RTTI_H + +enum RttiCodes { Rtti_NoCollision = 1001, Rtti_DontPlaceOn = 1002, Rtti_Ball = 1003, Rtti_Putter = 1004, Rtti_WallPoint = 1005, Rtti_Wall = 1006 }; + +#endif diff --git a/kolf/scoreboard.cpp b/kolf/scoreboard.cpp new file mode 100644 index 00000000..a750156f --- /dev/null +++ b/kolf/scoreboard.cpp @@ -0,0 +1,94 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "scoreboard.h" + +ScoreBoard::ScoreBoard(QWidget *parent, const char *name) + : QTable(1, 1, parent, name) +{ + vh = verticalHeader(); + hh = horizontalHeader(); + vh->setLabel(numRows() - 1, i18n("Par")); + hh->setLabel(numCols() - 1, i18n("Total")); + + setFocusPolicy(QWidget::NoFocus); + setRowReadOnly(0, true); + setRowReadOnly(1, true); +} + +void ScoreBoard::newHole(int par) +{ + int _numCols = numCols(); + insertColumns(_numCols - 1); + hh->setLabel(numCols() - 2, QString::number(numCols() - 1)); + setText(numRows() - 1, numCols() - 2, QString::number(par)); + setColumnWidth(numCols() - 2, 40); + + // update total + int tot = 0; + for (int i = 0; i < numCols() - 1; ++i) + tot += text(numRows() - 1, i).toInt(); + setText(numRows() - 1, numCols() - 1, QString::number(tot)); + + // shrink cell... + setColumnWidth(numCols() - 2, 3); + // and make it big enough for the numbers + adjustColumn(numCols() - 2); +} + +void ScoreBoard::newPlayer(const QString &name) +{ + //kdDebug(12007) << "name of new player is " << name << endl; + insertRows(numRows() - 1); + vh->setLabel(numRows() - 2, name); + setRowReadOnly(numRows() - 2, true); +} + +void ScoreBoard::setScore(int id, int hole, int score) +{ + //kdDebug(12007) << "set score\n"; + setText(id - 1, hole - 1, score > 0? QString::number(score) : QString("")); + + QString name; + setText(id - 1, numCols() - 1, QString::number(total(id, name))); + if (hole >= numCols() - 2) + ensureCellVisible(id - 1, numCols() - 1); + else + ensureCellVisible(id - 1, hole - 1); + + // shrink cell... + setColumnWidth(hole - 1, 3); + // and make it big enough for the numbers + adjustColumn(hole - 1); + + setCurrentCell(id - 1, hole - 1); +} + +void ScoreBoard::parChanged(int hole, int par) +{ + setText(numRows() - 1, hole - 1, QString::number(par)); + + // update total + int tot = 0; + for (int i = 0; i < numCols() - 1; ++i) + tot += text(numRows() - 1, i).toInt(); + setText(numRows() - 1, numCols() - 1, QString::number(tot)); +} + +int ScoreBoard::total(int id, QString &name) +{ + int tot = 0; + for (int i = 0; i < numCols() - 1; i++) + tot += text(id - 1, i).toInt(); + name = vh->label(id - 1); + //kdDebug(12007) << "tot is " << tot << endl; + return tot; +} + +#include "scoreboard.moc" diff --git a/kolf/scoreboard.h b/kolf/scoreboard.h new file mode 100644 index 00000000..b9dc78f4 --- /dev/null +++ b/kolf/scoreboard.h @@ -0,0 +1,29 @@ +#ifndef SCOREBOARD_H +#define SCOREBOARD_H + +#include + +class QWidget; +class QHeader; + +class ScoreBoard : public QTable +{ + Q_OBJECT + +public: + ScoreBoard(QWidget *parent = 0, const char *name = 0); + int total(int id, QString &name); + +public slots: + void newHole(int); + void newPlayer(const QString &name); + void setScore(int id, int hole, int score); + void parChanged(int hole, int par); + +private: + QTable *table; + QHeader *vh; + QHeader *hh; +}; + +#endif diff --git a/kolf/slope.cpp b/kolf/slope.cpp new file mode 100644 index 00000000..e2becea1 --- /dev/null +++ b/kolf/slope.cpp @@ -0,0 +1,585 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "slope.h" + +Slope::Slope(QRect rect, QCanvas *canvas) + : QCanvasRectangle(rect, canvas), type(KImageEffect::VerticalGradient), grade(4), reversed(false), color(QColor("#327501")) +{ + stuckOnGround = false; + showingInfo = false; + + gradientKeys[KImageEffect::VerticalGradient] = "Vertical"; + gradientKeys[KImageEffect::HorizontalGradient] = "Horizontal"; + gradientKeys[KImageEffect::DiagonalGradient] = "Diagonal"; + gradientKeys[KImageEffect::CrossDiagonalGradient] = "Opposite Diagonal"; + gradientKeys[KImageEffect::EllipticGradient] = "Elliptic"; + + gradientI18nKeys[KImageEffect::VerticalGradient] = i18n("Vertical"); + gradientI18nKeys[KImageEffect::HorizontalGradient] = i18n("Horizontal"); + gradientI18nKeys[KImageEffect::DiagonalGradient] = i18n("Diagonal"); + gradientI18nKeys[KImageEffect::CrossDiagonalGradient] = i18n("Opposite Diagonal"); + gradientI18nKeys[KImageEffect::EllipticGradient] = i18n("Circular"); + + setZ(-50); + + if (!QPixmapCache::find("grass", grass)) + { + grass.load(locate("appdata", "pics/grass.png")); + QPixmapCache::insert("grass", grass); + } + + point = new RectPoint(color.light(), this, canvas); + + QFont font(kapp->font()); + font.setPixelSize(18); + text = new QCanvasText(canvas); + text->setZ(99999.99); + text->setFont(font); + text->setColor(white); + + editModeChanged(false); + hideInfo(); + + // this does updatePixmap + setGradient("Vertical"); +} + +bool Slope::terrainCollisions() const +{ + // we are a terrain collision + return true; +} + +void Slope::showInfo() +{ + showingInfo = true; + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + { + arrow->setZ(z() + .01); + arrow->setVisible(true); + } + text->setVisible(true); +} + +void Slope::hideInfo() +{ + showingInfo = false; + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + arrow->setVisible(false); + text->setVisible(false); +} + +void Slope::aboutToDie() +{ + delete point; + clearArrows(); + delete text; +} + +void Slope::clearArrows() +{ + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + { + arrow->setVisible(false); + arrow->aboutToDie(); + } + arrows.setAutoDelete(true); + arrows.clear(); + arrows.setAutoDelete(false); +} + +QPtrList Slope::moveableItems() const +{ + QPtrList ret; + ret.append(point); + return ret; +} + +void Slope::setGrade(double newGrade) +{ + if (newGrade >= 0 && newGrade < 11) + { + grade = newGrade; + updatePixmap(); + } +} + +void Slope::setSize(int width, int height) +{ + newSize(width, height); +} + +void Slope::newSize(int width, int height) +{ + if (type == KImageEffect::EllipticGradient) + { + QCanvasRectangle::setSize(width, width); + // move point back to good spot + moveBy(0, 0); + + if (game && game->isEditing()) + game->updateHighlighter(); + } + else + QCanvasRectangle::setSize(width, height); + + updatePixmap(); + updateZ(); +} + +void Slope::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + + point->dontMove(); + point->move(x() + width(), y() + height()); + + moveArrow(); + updateZ(); +} + +void Slope::moveArrow() +{ + int xavg = 0, yavg = 0; + QPointArray r = areaPoints(); + for (unsigned int i = 0; i < r.size(); ++i) + { + xavg += r[i].x(); + yavg += r[i].y(); + } + xavg /= r.size(); + yavg /= r.size(); + + Arrow *arrow = 0; + for (arrow = arrows.first(); arrow; arrow = arrows.next()) + arrow->move((double)xavg, (double)yavg); + + if (showingInfo) + showInfo(); + else + hideInfo(); + + text->move((double)xavg - text->boundingRect().width() / 2, (double)yavg - text->boundingRect().height() / 2); +} + +void Slope::editModeChanged(bool changed) +{ + point->setVisible(changed); + moveBy(0, 0); +} + +void Slope::updateZ(QCanvasRectangle *vStrut) +{ + const int area = (height() * width()); + const int defaultz = -50; + + double newZ = 0; + + QCanvasRectangle *rect = 0; + if (!stuckOnGround) + rect = vStrut? vStrut : onVStrut(); + + if (rect) + { + if (area > (rect->width() * rect->height())) + newZ = defaultz; + else + newZ = rect->z(); + } + else + newZ = defaultz; + + setZ(((double)1 / (area == 0? 1 : area)) + newZ); +} + +void Slope::load(KConfig *cfg) +{ + stuckOnGround = cfg->readBoolEntry("stuckOnGround", stuckOnGround); + grade = cfg->readDoubleNumEntry("grade", grade); + reversed = cfg->readBoolEntry("reversed", reversed); + + // bypass updatePixmap which newSize normally does + QCanvasRectangle::setSize(cfg->readNumEntry("width", width()), cfg->readNumEntry("height", height())); + updateZ(); + + QString gradientType = cfg->readEntry("gradient", gradientKeys[type]); + setGradient(gradientType); +} + +void Slope::save(KConfig *cfg) +{ + cfg->writeEntry("reversed", reversed); + cfg->writeEntry("width", width()); + cfg->writeEntry("height", height()); + cfg->writeEntry("gradient", gradientKeys[type]); + cfg->writeEntry("grade", grade); + cfg->writeEntry("stuckOnGround", stuckOnGround); +} + +void Slope::draw(QPainter &painter) +{ + painter.drawPixmap(x(), y(), pixmap); +} + +QPointArray Slope::areaPoints() const +{ + switch (type) + { + case KImageEffect::CrossDiagonalGradient: + { + QPointArray ret(3); + ret[0] = QPoint((int)x(), (int)y()); + ret[1] = QPoint((int)x() + width(), (int)y() + height()); + ret[2] = reversed? QPoint((int)x() + width(), y()) : QPoint((int)x(), (int)y() + height()); + + return ret; + } + + case KImageEffect::DiagonalGradient: + { + QPointArray ret(3); + ret[0] = QPoint((int)x() + width(), (int)y()); + ret[1] = QPoint((int)x(), (int)y() + height()); + ret[2] = !reversed? QPoint((int)x() + width(), y() + height()) : QPoint((int)x(), (int)y()); + + return ret; + } + + case KImageEffect::EllipticGradient: + { + QPointArray ret; + ret.makeEllipse((int)x(), (int)y(), width(), height()); + return ret; + } + + default: + return QCanvasRectangle::areaPoints(); + } +} + +bool Slope::collision(Ball *ball, long int /*id*/) +{ + if (grade <= 0) + return false; + + double vx = ball->xVelocity(); + double vy = ball->yVelocity(); + double addto = 0.013 * grade; + + const bool diag = type == KImageEffect::DiagonalGradient || type == KImageEffect::CrossDiagonalGradient; + const bool circle = type == KImageEffect::EllipticGradient; + + double slopeAngle = 0; + + if (diag) + slopeAngle = atan((double)width() / (double)height()); + else if (circle) + { + const QPoint start(x() + (int)width() / 2.0, y() + (int)height() / 2.0); + const QPoint end((int)ball->x(), (int)ball->y()); + + Vector betweenVector(start, end); + const double factor = betweenVector.magnitude() / ((double)width() / 2.0); + slopeAngle = betweenVector.direction(); + + // this little bit by Daniel + addto *= factor * M_PI / 2; + addto = sin(addto); + } + + switch (type) + { + case KImageEffect::HorizontalGradient: + reversed? vx += addto : vx -= addto; + break; + + case KImageEffect::VerticalGradient: + reversed? vy += addto : vy -= addto; + break; + + case KImageEffect::DiagonalGradient: + case KImageEffect::EllipticGradient: + reversed? vx += cos(slopeAngle) * addto : vx -= cos(slopeAngle) * addto; + reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto; + break; + + case KImageEffect::CrossDiagonalGradient: + reversed? vx -= cos(slopeAngle) * addto : vx += cos(slopeAngle) * addto; + reversed? vy += sin(slopeAngle) * addto : vy -= sin(slopeAngle) * addto; + break; + + default: + break; + } + + ball->setVelocity(vx, vy); + // check if the ball is at the center of a pit or mound + // or has otherwise stopped. + if (vx == 0 && vy ==0) + ball->setState(Stopped); + else + ball->setState(Rolling); + + // do NOT do terrain collisions + return false; +} + +void Slope::setGradient(QString text) +{ + for (QMap::Iterator it = gradientKeys.begin(); it != gradientKeys.end(); ++it) + { + if (it.data() == text) + { + setType(it.key()); + return; + } + } + + // extra forgiveness ;-) (note it's i18n keys) + for (QMap::Iterator it = gradientI18nKeys.begin(); it != gradientI18nKeys.end(); ++it) + { + if (it.data() == text) + { + setType(it.key()); + return; + } + } +} + +void Slope::setType(KImageEffect::GradientType type) +{ + this->type = type; + + if (type == KImageEffect::EllipticGradient) + { + // calls updatePixmap + newSize(width(), height()); + } + else + updatePixmap(); +} + +void Slope::updatePixmap() +{ + // make a gradient, make grass that's bright or dim + // merge into this->pixmap. This is drawn in draw() + + // we update the arrows in this function + clearArrows(); + + const bool diag = type == KImageEffect::DiagonalGradient || type == KImageEffect::CrossDiagonalGradient; + const bool circle = type == KImageEffect::EllipticGradient; + + const QColor darkColor = color.dark(100 + grade * (circle? 20 : 10)); + const QColor lightColor = diag || circle? color.light(110 + (diag? 5 : .5) * grade) : color; + // hack only for circles + const bool _reversed = circle? !reversed : reversed; + QImage gradientImage = KImageEffect::gradient(QSize(width(), height()), _reversed? darkColor : lightColor, _reversed? lightColor : darkColor, type); + + QPixmap qpixmap(width(), height()); + QPainter p(&qpixmap); + p.drawTiledPixmap(QRect(0, 0, width(), height()), grass); + p.end(); + + const double length = sqrt(width() * width() + height() * height()) / 4; + + if (circle) + { + const QColor otherLightColor = color.light(110 + 15 * grade); + const QColor otherDarkColor = darkColor.dark(110 + 20 * grade); + QImage otherGradientImage = KImageEffect::gradient(QSize(width(), height()), reversed? otherDarkColor : otherLightColor, reversed? otherLightColor : otherDarkColor, KImageEffect::DiagonalGradient); + + QImage grassImage(qpixmap.convertToImage()); + + QImage finalGradientImage = KImageEffect::blend(otherGradientImage, gradientImage, .60); + pixmap.convertFromImage(KImageEffect::blend(grassImage, finalGradientImage, .40)); + + // make arrows + double angle = 0; + for (int i = 0; i < 4; ++i) + { + angle += M_PI / 2; + Arrow *arrow = new Arrow(canvas()); + arrow->setLength(length); + arrow->setAngle(angle); + arrow->setReversed(reversed); + arrow->updateSelf(); + arrows.append(arrow); + } + } + else + { + Arrow *arrow = new Arrow(canvas()); + + float ratio = 0; + float factor = 1; + + double angle = 0; + + switch (type) + { + case KImageEffect::HorizontalGradient: + angle = 0; + factor = .32; + break; + + case KImageEffect::VerticalGradient: + angle = M_PI / 2; + factor = .32; + break; + + case KImageEffect::DiagonalGradient: + angle = atan((double)width() / (double)height()); + + factor = .45; + break; + + case KImageEffect::CrossDiagonalGradient: + angle = atan((double)width() / (double)height()); + angle = M_PI - angle; + + factor = .05; + break; + + default: + break; + } + + float factorPart = factor * 2; + // gradePart is out of 1 + float gradePart = grade / 8.0; + + ratio = factorPart * gradePart; + + // reverse the reversed ones + if (reversed) + ratio *= -1; + else + angle += M_PI; + + KPixmap kpixmap = qpixmap; + (void) KPixmapEffect::intensity(kpixmap, ratio); + + QImage grassImage(kpixmap.convertToImage()); + + // okay, now we have a grass image that's + // appropriately lit, and a gradient; + // lets blend.. + pixmap.convertFromImage(KImageEffect::blend(gradientImage, grassImage, .42)); + arrow->setAngle(angle); + arrow->setLength(length); + arrow->updateSelf(); + + arrows.append(arrow); + } + + text->setText(QString::number(grade)); + + if (diag || circle) + { + // make cleared bitmap + QBitmap bitmap(pixmap.width(), pixmap.height(), true); + QPainter bpainter(&bitmap); + bpainter.setBrush(color1); + QPointArray r = areaPoints(); + + // shift all the points + for (unsigned int i = 0; i < r.count(); ++i) + { + QPoint &p = r[i]; + p.setX(p.x() - x()); + p.setY(p.y() - y()); + } + bpainter.drawPolygon(r); + + // mask is drawn + pixmap.setMask(bitmap); + } + + moveArrow(); + update(); +} + +///////////////////////// + +SlopeConfig::SlopeConfig(Slope *slope, QWidget *parent) + : Config(parent) +{ + this->slope = slope; + QVBoxLayout *layout = new QVBoxLayout(this, marginHint(), spacingHint()); + KComboBox *gradient = new KComboBox(this); + QStringList items; + QString curText; + for (QMap::Iterator it = slope->gradientI18nKeys.begin(); it != slope->gradientI18nKeys.end(); ++it) + { + if (it.key() == slope->curType()) + curText = it.data(); + items.append(it.data()); + } + gradient->insertStringList(items); + gradient->setCurrentText(curText); + layout->addWidget(gradient); + connect(gradient, SIGNAL(activated(const QString &)), this, SLOT(setGradient(const QString &))); + + layout->addStretch(); + + QCheckBox *reversed = new QCheckBox(i18n("Reverse direction"), this); + reversed->setChecked(slope->isReversed()); + layout->addWidget(reversed); + connect(reversed, SIGNAL(toggled(bool)), this, SLOT(setReversed(bool))); + + QHBoxLayout *hlayout = new QHBoxLayout(layout, spacingHint()); + hlayout->addWidget(new QLabel(i18n("Grade:"), this)); + KDoubleNumInput *grade = new KDoubleNumInput(this); + grade->setRange(0, 8, 1, true); + grade->setValue(slope->curGrade()); + hlayout->addWidget(grade); + connect(grade, SIGNAL(valueChanged(double)), this, SLOT(gradeChanged(double))); + + QCheckBox *stuck = new QCheckBox(i18n("Unmovable"), this); + QWhatsThis::add(stuck, i18n("Whether or not this slope can be moved by other objects, like floaters.")); + stuck->setChecked(slope->isStuckOnGround()); + layout->addWidget(stuck); + connect(stuck, SIGNAL(toggled(bool)), this, SLOT(setStuckOnGround(bool))); +} + +void SlopeConfig::setGradient(const QString &text) +{ + slope->setGradient(text); + changed(); +} + +void SlopeConfig::setReversed(bool yes) +{ + slope->setReversed(yes); + changed(); +} + +void SlopeConfig::setStuckOnGround(bool yes) +{ + slope->setStuckOnGround(yes); + changed(); +} + +void SlopeConfig::gradeChanged(double newgrade) +{ + slope->setGrade(newgrade); + changed(); +} + +#include "slope.moc" diff --git a/kolf/slope.h b/kolf/slope.h new file mode 100644 index 00000000..d638354f --- /dev/null +++ b/kolf/slope.h @@ -0,0 +1,98 @@ +#ifndef SLOPE_H +#define SLOPE_H + +#include + +#include "game.h" + +class Slope; +class SlopeConfig : public Config +{ + Q_OBJECT + +public: + SlopeConfig(Slope *slope, QWidget *parent); + +private slots: + void setGradient(const QString &text); + void setReversed(bool); + void setStuckOnGround(bool); + void gradeChanged(double); + +private: + Slope *slope; +}; + +class Slope : public QCanvasRectangle, public CanvasItem, public RectItem +{ +public: + Slope(QRect rect, QCanvas *canvas); + virtual void aboutToDie(); + virtual int rtti() const { return 1031; } + + virtual void showInfo(); + virtual void hideInfo(); + virtual void editModeChanged(bool changed); + virtual bool canBeMovedByOthers() const { return !stuckOnGround; } + virtual QPtrList moveableItems() const; + virtual Config *config(QWidget *parent) { return new SlopeConfig(this, parent); } + void setSize(int, int); + virtual void newSize(int width, int height); + + virtual void moveBy(double dx, double dy); + + virtual void draw(QPainter &painter); + virtual QPointArray areaPoints() const; + + void setGradient(QString text); + KImageEffect::GradientType curType() const { return type; } + void setGrade(double grade); + + double curGrade() const { return grade; } + void setColor(QColor color) { this->color = color; updatePixmap(); } + void setReversed(bool reversed) { this->reversed = reversed; updatePixmap(); } + bool isReversed() const { return reversed; } + + bool isStuckOnGround() const { return stuckOnGround; } + void setStuckOnGround(bool yes) { stuckOnGround = yes; updateZ(); } + + virtual void load(KConfig *cfg); + virtual void save(KConfig *cfg); + + virtual bool collision(Ball *ball, long int id); + virtual bool terrainCollisions() const; + + QMap gradientI18nKeys; + QMap gradientKeys; + + virtual void updateZ(QCanvasRectangle *vStrut = 0); + + void moveArrow(); + +private: + KImageEffect::GradientType type; + inline void setType(KImageEffect::GradientType type); + bool showingInfo; + double grade; + bool reversed; + QColor color; + QPixmap pixmap; + void updatePixmap(); + bool stuckOnGround; + QPixmap grass; + + void clearArrows(); + + QPtrList arrows; + QCanvasText *text; + RectPoint *point; +}; + +class SlopeObj : public Object +{ +public: + SlopeObj() { m_name = i18n("Slope"); m__name = "slope"; } + virtual QCanvasItem *newObject(QCanvas *canvas) { return new Slope(QRect(0, 0, 40, 40), canvas); } +}; + +#endif diff --git a/kolf/sounds/FROM b/kolf/sounds/FROM new file mode 100644 index 00000000..6c472d5b --- /dev/null +++ b/kolf/sounds/FROM @@ -0,0 +1,11 @@ +blackhole.wav, blackholeputin.wav, blackholeeject.wav, and hit.wav: + Timo A. Hummel + +holed.wav + Jason Katz-Brown (drop ball into mug :) + +holeinone.wav + KReversi (kdegames/kreversi/sounds/reversi-won.wav) + +puddle.wav, wall.wav + Free sounds sites diff --git a/kolf/sounds/Makefile.am b/kolf/sounds/Makefile.am new file mode 100644 index 00000000..48609f33 --- /dev/null +++ b/kolf/sounds/Makefile.am @@ -0,0 +1,4 @@ +sounddir = $(kde_datadir)/kolf/sounds +sound_DATA = wall.wav puddle.wav holeinone.wav holed.wav blackhole.wav blackholeputin.wav blackholeeject.wav hit.wav + +EXTRA_DIST=$(sound_DATA) diff --git a/kolf/sounds/blackhole.wav b/kolf/sounds/blackhole.wav new file mode 100644 index 0000000000000000000000000000000000000000..309971107f35ff675413975d03127981afcb02b7 GIT binary patch literal 391724 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%0D!u?oQ_ivrGuEL6pf&DP+m(Kc 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

    SCsz;P3mZ%%?8Gw^_8G&`Kq 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 yfUZ;#Kc4e)Zx)T)z|)^gBQk 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

    ?3{thMS9*kjKXnl1lw3zyc>faZ|FB3<(f#L>c zSd{)Uew6YuGBhr{nFnG{=n7FQw7m$1WEb;8zSyiGJM4DY)j55|?MgSq#&d6&AtZ&p zR((xXaFWg7Cb7uI>Lw!fb^D4L<4wH?Ms*bGqQQD4y0z(gkk_A{pb5QrNu4YBKYc9_ z;SCI2^RfljdvE+Bz4iXaULSt}uc|*Te9F(kn0`DRB6q>8T@ri}IX(C=azgNFJp$3cV-jZ9K+p;w4 z5)+_W>MEWT=VW<#loXjAG6z$$739Gswf#vdX)cqP3UUS9sr)*&yz2cFJ=iCbd#A*O z;8rm#xBxEhXz@GHRqSIHIy6v1R1Po)!u8>+|Bb!kKV|p$m)bS{{&tzamR;e`WH%G7 ze$;=*-1BcS5&nrLMWCap0xmi@P}J-R=c^okq5SH;xV z4LCj8n`mZ$X<{as^(5SVG22W|m{YCEQkiMLn0+?H-mxjs_vN>d>;M)&APF*IY$qmt zT~YmYW;bbP_k&?Awsq`KuB)c(1|``O(pbgcen%g9$viMy%{fx^_M5&uYeaHaIkV2> z;W3@rVUn9;CYiYor|XT$VT>tFMn*%B(*gLFE;7UTc~+CxbeZq?8~h}#n8Foy){X!( z*@?^fD=}ImlBY#Q86x}33Ua+1BOg-pL{~xBD!J((TBuoSnmVHPs<-Ms+Jc{)B~hKm zPGV;yo(OAM=bc1&>O^;ZuE#m>P8Fi3s0pgn(p{o@y10wG9n?6trkc#mYpR<;O>q;d z@orQ;FZpvVTe()&aeXMjVyP@{QWb}<`R?RZSDaF6g;V`Mzt?5L1g;p~jUM#xd-ri^Yj$pxf+=hqEMLd+v4dr@RH@0m#Ep<@ot z#^0?cjIuf+JeW_oUJ{Yfv$i%arXz5L*zj1{ z<{rPtJ8?@)0=F1t_lZ7CDx2HE)J=b}m!t=``fBf+y(Bmcv?Fkjs$(qg_GuHLXTacV zZ8pNVo}$y4-a3wHsYA`*?4)%y&T~2gy>bcDRW~(b^(ee%Hkk{|$bM-DceC8K5m&|R z|9ozD*@X6qZD2#hGMiJpgKw2b3=xCqSWb(Rur~gSGmn;PaWA<=B6j<=H{1M=uzx-5I*;0i%Zk~AWA$+^?4pDkqF%_cFv-R;2Op}&<6+od z*1;D)kIDpE5h5SR*J2%aY%jSAC!$ewmMz77QAVsMjc*9OXkGEfW~VlZDz@48cAmX# z$JkxAk6j9eIu(}ZNZZ&Bu#K3d!YH(@dESZ7^=7_01cY_0J;1YDc7gpyjT1-gC13oa zts!1<>L~V#3}T_E4zGKF*e}+Jqx{RqMGSsV&?D;I^<=-y7Z>T0f6AV)+!~1zsBfF$ zyf{=P67!T4+tep}nmYQPI%nU)9Qy$Z=U4MtQ|)&(*1l4s>{T@ePt>V)3fNZ*waezh zBlWlZK~7jw(N~rMh3X{Uplnzm_RweFhAa4$S3g8HrGkz@n$&)iQ?@mEWqi_Mj&WXe z(CNUV_+?si z$NUYuZaOvOC6mj>B(bO#`~JT$?C#rjHU(8;b8(v)_#2p#KW!puY&Gcp9PV@i|8s67GJI(E5gT%f?X0nI&vOoi)e4ki+an*&Z*rx^@;*uii4JttV50d&GsW z6+#ta%p2inhV{!FV6BCPHIL6tqCy!-g8X2Sg1DL}&4fM4njmI9cR#&WtGFPB}nHYRj#%m%Jfo%3nN70uQIKdWDZ(K=x2E z)L5?ZIV!DMt}?4Ntko*LT1tOB8*iO4%Cd`xqwKd;a znUJ~QS8&SHWDfpoO35ZXPt7&*1axD8jwk=pK@mqk6PHm>O!1b9y53M6l^Tif!9qA| zB@+9CzwMUbUArxKkhH7i_FixjzHa?(ey_dl<2AO&y?Qng9s}K(J6*;DGn;8^$D2O( zg&7B;x0r;@UEGP6xGP`T60m+ciTGj$spH#3X;8ht(bfHVZHJ2}|K%pF17$rZ%E+st z0k1RcD01|qTqR>s86}pFWC|I{im#H$Y$~QKp*&Gly%u$Gv#pDlaCJ3Mlv34kpGX1k zD%^gd9y}|@+Xc*@+S5;l;eal2K)(w6b}1)S5BPl*#XFOhBt@4B_7mw^k8LY`(Cs*yYZYIs+L$!`k9nB&Tn&Rc<>F)1G_P+koXRh(+96gans+x*Uco5C4m zgE+TeQK{@4_tU$tZ3HM{d`BBUkGF zk<<0)$o_g>WFx&ZvZ%fdAI3!f@^avW-_pMoPStepslTt6G0@WM0jK?VpthHQxnZYZ zEAKQ;+3CE6-e~W%_s)y-YUq3*!Tod!b3%_X2~9Zs``YXZyXhVhfiU+GA3>VSi_CJK z7%X3jOEMGpQA_y5v*l8pDDSEFGP)B}6>xH?hE8qO&*`bAIy2Q0XNOwvTvuD2aJ9p6 zob6705=c@~m!-q2DvdLnI&FgEa|Sq{R0roOK2{r5IcKEG>C`8$Bn!q@0X(iQ%ZqBc z+=^T7EOJfyt33R>31mfl(DJG`U>mnYLawb?>Ja$pE)iXA6>-!?_V;!47VAU-wE--B zE12qT(ODf~ufHg!s|R8^Oy?cSFOG9+-B7XQOBD}}UR)XKM3c!JkN5p8?=y}P&PLJR z87ulaO~h~~5B;nwMml%lWNosYoRPL3SzSf&j8EpIv%ghrt_5jVs|eh2zMGn)z@%mU zWGZt~2Fz3@{q?xM6Uo%Po?+yg*R=UKpC8*G{NP)O28U>Gv{{`2HXMss#U7(%l9VjNs2cm;x-?G}jz`pi3 zv3LAw?PdQvbJ@Sc-0}}IFa5Pmq(7rc7!amF;E8S&IHpGh*67WFnfh*Eq;`S>b?#t) z-6S{!Tnfjh-~wC&w(0oZC0)wG$GNCm-Knd+am6zAMuHD8w>JVr01$D+g z0D}rw$#ADAhhKkJl6vOAnLo^X_(CjjLg3D3VWwS0#&X-s0`4f;#9c_D`!>1JJq|u` zRR-LLG7Y-TintAS_Wgtj>z7-6T3+x8^@3D{Up}95L!zh{A<XT8T1VQ4z(9FD_F*{2M%O8>3js2sitCpsGC{$ZTf^LT&fJ z2U9n2&6ElpAkA^TDHK>_$_HkfW`UVzOyFPo&4uP$V6_Pg?l!}N=gk$IdQy5GYRV+` zpjW{9^k23L>GI?BWKx}1F}FTI^2rr=K2PWkK5{+$vV%;}PK2>C*J!(%%5=B+ZBcE4 zT6`q?=rz~=Q|ga!{0*`pf=Myqbs|e?_ zzgP?Z^)VBxq_8;~s7>QD1pzB8TWQ&h0`$&$N`+UcfgggXk(@=X4(Qpa5} z#q}j~6zywe@3eUsJZky|_u>k)*_;k6Hv>r03JZ)het$PKPmRn%e|gj2pTjiqCp1<3 zL0!!MT!(SKg!$L$qW%fGqQ8S~=&wwIN)|oCA6@VEf8qSN=fw`3^2!GGdP4%+yaOzM zV4qhEFQ4JTm)=S6DW8{LSMWOOG2S|TlKsJYVbq(WOiz8AJ}|3&p@-ST<~mxf)Nqe=}gK=C()5r1wBi4J|7ni zgI^34Z<$j*zzgjLIKx%`yC%L;t0$6=L@9d9Zt|m8F29Mp%v)ls2vG(9n{n!gIIX@2 z2S!pE=ZzTZJcDcbKzxFepUORhVtudZ3D0(xJ6*uP#u=rFchK!AlHifUQjpi$5zr(1%>Ef5{X13$qoN#)J+B|M`>$1f&_=wkVUVgTK>jFP^>R z-6EZCfm!dhWxiR+%*DBDruP+A?Oi?HJEf<2`}7nXWhPM1jPefZ!ML^bf`!!4gWFGb za0BqEy53)=hS!%xqMmoy)c2m^Ic!X4^q&K9o}T1Yv5To!w|O1xDX)jU=k-C~-OIW# zKmMoN*aG?=TSu3tj?HOj>Uegy{!T@81y0O*lLa38OZj!MOw ziCw8I^R}iacKW0I{MViZJ$a;=cAzFtVdBC4EeIE?8SJ|W|Ea!`qX?;E6SCh_mWS;y zrU84oD!<8c0#6{(Nlj*^fuo2z1;+gVhFZ8-?nI)t6*8Wy;4KK*4`-ZJaIG&hBmM3^ z5^;UkL^k$<(muRye7o5TcH%(0M|AKV5p8_Te0@*x4TvDQF9Ev1F!{`FBsX(5^>Mdw zMm?0@ooH%7Tjavp>HWY0t` z=e)@0>=tpH)i~wN75m_-k5$9y{ClB#Xd_<9`lMu+M>Cg)PC2QF#yxmXJcIFk*!B~1 zZDrJUDPhe21e3V}s<45*dlCt_-EA$?fLE`AeXk2!JP7P0ogHt`Y_=57Gw`{%IDv-n zo;kXUjjG337~AB49=28WBimVPu&b0}k1m1orHK$`fJn^*w5VArYNNwxiT18DtGn4I zx|mg>4V;z+d`(r;h_5LuvYQ0BYkz{{aMmUT?MP_`*{rajijj&`&m^@y!K7xG_h68B z%nh!FZy=O0L98-z`V@n^Tb+}rp`A%xKHYSH3)2x!Wm{O5&Fo53kF2@MOjU}(=*(@u z@^@m{)J#ZHktUj+8A&dBrZSvQP3Sy^fqt)t<@=DCN<2|t)DW%36!a6<=|vK=3pJJP z#2VR1gv)9;=@u2;R2H!wyx}pLo@mY&n+pfY>dpn*lDwhr)RcXkWjxLzDSn)-;S8{O zaH)VFa~X~x`k~=pIBCxwrP1ET^$D( z+8=OWZ*tCTH`~3bxIlL{EAR?m?&XFlotXVVnq5q`PkT@FV|a@}?+lp=$4CgLS7Gho z`6pCd+07kTDDU+)pPF!LFe)iL&^}{P7gYMD>^m2LPoi+i;lYGW_1w&0EGys)lH*(3ll9k?_g!8^8C`NL!&Z2kTVD^bKW*MyF z@l1Pqz&30sn$RJShk3XQoc*bdFQSSlbjucO)o=TYU#J^?;XxNcXX4qn!sQi@Nync= z#785SROSNPD9rg;Qusi*eu4}f23saEklyHWlGJJH~i+EV*S)?38OeOlNrrA-djEVPfF>gu zbzm`2!)C0JJYPl6bP8qCOJ z&fbbpeNJD^+6 zVK0KN#pGJ9DQB4(vV*xM%c3$&hYuTWtR#1=#$oj$zLf9OZSCPT72-VAKC&CJW#n^l zQJ-cqt5pdz09UXIsuRAiBg|d!s>O0O_r^|>RUYA$IE}L59Iw)$HR7}@#8sXM#O@c$@H?P$2j~-)gVs)_PZ&hTYF8Amt!-iY zhmx|fEhn4Uin2L%K^xp|JF|M*s$iT|`TDAI9vJBw+(vfOqnxJKzik`J*Hl}-Z3h_z zJwy`llgwyR3*sYHRxII6*nn1bA0C8fWi31iE8uTjKm=tPaEwq+=l67gWFxATwwM}f zOVJIMRC#P+6_dQvk0!ag0N%9Gyp>bUDenITIIFdnwc!1hHD5#_vtHyzO_0^Z!Ik(Z z6PB)Y=P8*GU1b*chdW$?NrlHe<|vNM<48F8OaEgsFuw_Uz42=uYqoid%v5i;=|Y9hv{^kO`7i!o|1PFthkMN}mP&Mzj$MhY1=f2y2@gN(c)6p*!M%`Wo$LW9YJZ>d$@<8$4MNHA1 z&{4O+k+q39sB4Q0x{SDkD*mZXDqfR#_g24xZE=S0xz1kKU#jUob>^UY)AE}IDvS;QHxCC%gx5f8%D5uyA&X(mO z4$~xRRFOr%gkYLfkojX#0U|8Uvo7-dnkXdhiG1R<$S!_!5=NKFm;}TT<={57kpVD? zFP!JE>=XHb8OaS>gi50`_w*8V+TOsWAjCNXes~dIhwFBQbKl-}UfK}%6S%-{+txL9 zy6cL)=yV>q(L}(-f57L%V@%s(zVEg&J!Ku=HMo<9cwEEBQ*B{iUz>*crSw&>_uX7} zotqTxSg6h6Mw-`7xS8#|BJc7!+?Q8oEc(IpU~I?Xb2WiM9zdxyU-aTkTmd3;4^%R~ zsATKF6PYc}n}=L6X=Q76n4&OdlHeX3&^gs}&DDti&LMqXZNourEn4tpI+L>qcfAF= z2&;gz5N7{E9goj_R|_~}=ISMCrtYjJ=mKh}HY6gRk|>WiLD7HM9}*n>o1#+vp`+e>+Iz z#kmTtbC`K-+nCpM3LnvSez9-NS3L4R(*wM>HS9~uMUc)@_B3Alk3g_~ zGE<2uw$mAGwdGhXnRpHd%Ua4wbwnJXGCXRPJcR=6JoVodQeSV{4e~BLx`)iro`HS7 zwju7IA->RPH?^+#{qJtTDTt8DpV$ZrmWXggjjwR5r{0XD4*kK5`@NcLACoO}MHS&Xi(@ya z4`wzs#W1ziv{Q>rbv47}QB%D=4CW~%#<$Im!; ze5RHQ=g;24J$cI4-4+Wt)tA}*@D6wDiZtX2=rZ9y(C&a^juOn1`(?5L#~ z!=FsY33-ue2al--y#2xEC@0E!vj7}nqj_Wwo3~U@Kg2mAQh;iIM(_0}rTM%`#+{TN#3=^}4SB_CSR@-+2TX1{ z_&=RHoLu(b5^yxT!N^)`>5^gGrNcL%EqllYP=_yKF*x=NJAjGuA!=2F*LiW(9*s#e zyI0k--&9qb*(qfkJ7GBXXR-U86!wJ^8z*LI)4AVFQTLgtN=o}*?g7>sQ_Y=aisLzy z(d}VEN#T9!G%#D8+9ZnAFa>a;{i^<=188FEtIozz!?_j~m!RJ4=10?()&HlVK9Ob$D$qeNFH zDe0&=#2lxXSmL0*aBA?pmKf($7d@N`qMlO}JS)41!PmW1ihJS>-{&k@cPr?w1~NUW zY2VX5pOfFs26@a(qdM--%%r8vY^uxOy0AQ})5-Zd8r+L7qO!g&GU+WMs-7UeddX2z+FSDNU{3jEDsDF5JoIJ&0`!=%0>=2uWzB;|Vz*Qds3pfK@ ziu&ly!MDv(9L0ZeMP&uOXu^9njcnJ$av-yXP9~InJ`L|@e)6wN;>KN?&aRj$Y;vjm z=6@dH~KwQ+$r%;FJleic_Z;o7ZpEs3gH?h z5~D=QX|A!m?zOGaLye$&Tgp`M2*1S>Q_C7^u@w9c#ZXQ*Vxm8gYHXgZ3}R4^`A=&b zPF3cLu{JsPcs}-wYMh*HIWJ4_73`9L z$tZ8a>Lr^^j54Q16N7HWv=PZb6ewh2)nBv^^Y33Zr>RHILI$e%U-p7NYq#q~cCqe@XGJ|bQ5OLX zPH$)F_;wYQ{9$d)6O!h9?7n%;Z+6{C`nRCD3`*;RO+30R>#|IJzd0-Wd~NYQq%qB)`# z7*Y*1ZZ)X&Yl1b^#?Rm{w7q|`|27aKY$I^B#_W5IMS7mcVL7&m2r^5EM4KOAb|>gf zqk*I+v_nK%xUG3O!AilXt_4rJIVWNdyAYP;a`KqgaJsFBS-nAAvFpSOFwoz2j)*44 zffM$IkJFT1qdfm*<0pwnP2%Sad|_L2CARyob2$KG>j1CY30n!rg)mqfDMfV7;XE+^ zn}|!C&718hdYvFOem-{Lj^IX1WhQ$aPJ)$@CNp#P>guBDs&<=+YN=VNCZZYcW3H;^ z<|+HcM^zC2vGk+>#WfL1m~ZNf#@Ak7QP=b~bzDzWyKw1Tr?aWW`n#O1cgU$YN={<8 zouqGxX?g^eUUspD?s17dSVDn*+6biE;p z)UUDb&|%%dL;NPL8kcNI=LF}?ZX3f{jW_iyIx!gQsx@v~m25v8U>lO>R8qYoIqNJ5 zd~5Nso(Rg)4c6!1bW3G9`SO{0G84FPO4EiNvLY)3I#VWZAc8p!x>hqm)Byu)&wex9 zgo=6Qsoh}?z^a+g3c3IfF(IvXp2X;48hf`DpH}RkOgX4Z+&;Q0r`x~S$!nUW{8pS)mR6JpCx&b19cO8=vt^_ zB#ExYD|?!5NH#z%oK{QWw3%Ua+^x)L$a72D~ssLvak-3h4djD%Db>qur4#X z8)EY_waY{La!#}rS@2Rw58o)YeuGEkZq`JdMz__ONuP!$+pQ0xqI!mo z-WA_;7GaoghT_5!6C^h_SY>Rm`{=xH?E73-1>`q=gIDN;Z|TM)R5d2;yB?0=mE{~= zkgGVA97Te_aQ#}0)F;Ix()kwXkz$)}EiTi;eAoFzDwt|@P*YE0qH!L+bu{}w>e4LY zU*4eyV3p}a%l~lc{Y(K|xl+`Gn>tsXgMV;Oq*Cw5hK(fgJ&HW9lF0~_6AoGhGGbfe z{ykc@b2iCt&I7p3u}CJVq?$ToRCVW+Dhh`xvs2zl;EZ;Ja}+jJg!2Hl`gv8~-LD3@ z8`V5_soF&T#2$AFYn<9bR@y>$xEkh?-^^onca-{#GU^~{k^`LCD!;QpeS`Hg!<$p}!#YMV=!8=DJ!Y9;g=Lg~}-2)1!Vux&8^3`#ZGZPgHVxiVtR&I$&n8 zhqPCXO+J;&{FaW{M&e6r7%TDQczsy3(rra)9b2T;hinw4gg?CW_NDjI-1pXlK=wCx zyc)1rvzzZ;T$rp{=hUBdeYDGC^b>tRKST-h5a-w@x-*mBjW|Yzn{b`Oe&;(y=#AFX zZ>`m7gf#z<-E{gbBeE{9Klp zZep9MDo!%lxCaOSlX=8>w})zNI=w*$CL`sz_tSE>`)n8b>B06X)785Mr6IGiN6bmy znuj)Ee%Ls83xSV=co)U3urB>pXX^j4aH&>^>tG7+xz`Q4(YR>&(s8nd!2qZz>dJrc zc54p{q90t%vD7Ft;VCaBzhDh(GyIpGVj$PxFxGgU&*O7jOE!P%XJqLU-UdK~>wJ@2-$FWj0WYZf9sF%4K$ zRIBMXez^C|bN7Tf?~>f&&NQRlLFON~jmhBFHXof5W}}ncw02V9$Qc_PLXqMgNe6P4DS!>wW)JLGI!JNv7t`fm~h!c;LGpA?lZ@}qZA zp7Peo<@i_+_gcv|URhbwOD{`!Au_M`?!QFbY^axWc~i-zhWUlxawBx8J-xW_e7}*O zhEB@cYJYjtNayZOMoL{v7Lc8fFUUz96Go{q@##bhqr_>-8fku-)l^iRSzI+~(C8GR zGpIvt-NiQKMCrx(F&PftYI@PbD1GkOq*SkYne$boSFaCmwmB^QmP}lmpdzmgAEr3_ zQ+hE8jyDOgI8+?5W$4(3qB-5K%EO6I!6_PH{&sGf5zbb#+L?+g3s=AMmwD$D#UV0- z`QgMhU-9*L?|jpbo!9z`gOdra!pof-dX#frH+Qaq6yDIvxrL+m9X*N(RAFAx_wq6M zfRA-?)?4v__x-LeC9dfoa5pwlPj;l<%t>wOH}mvG)F!LUXg%5tV<#BK*)mGkGE;Rm zvxK#a=T~%nc7`S>|_*D)9Alunm>L$pbg-E zi{Py-rB7dGs)(iB%L}=uXW|V!7N3p2%)44Kd97hGlPpz|ZmBMNNGn`Rx{>(am$Zq2 zXu<}t)Agp;>?HqYuPKEOMGnrARPg>2XeDFoS8%(}h{W(b)5G^Hh@WaLa&fxqvN*@) z6Z>^$@lvP7lQa#^Z|OxhJf@bJFs71az-4QT0@Qr%$#kC&x_FKX?-yE~)MAUM%;c*Z z==3a9RJ%lgIeG#aF7m+*`Ulra_BFXpX8BJ&lT_scH7dYdqA(2T;_{;^3j?-_EbG+e z#A!x`Nf)`m86wv>Q{)DmfL5c(p648s!<}p7@;;XZo$r6x((0B1b;LeHOYupyKx0wJx=uVi4?lC)UFJ^P zY+SCVpJ0%WRULd`@*7+TIZFaMAK45Mbn*KnTxhvO=flnOlk#SM<~G z?QnhGPSsm+2wzOiIm6z8q3G9BY+5r5_QN7(&ui&PcY_w4wgo_r`hx22McMcp>^4lK zpz6*pc8DU(56hvFu1XJ4lU=eV`oC&Sm&%I>Sp*h)R=8-1MSc_{#nn4o8icE`%>vqiPWj{IXY<#yN zc^lP9!zy8`fnQg}6QZ;mn>mIt@!ZesDNoE9=PHSF$2l|hl190eU3r7qsn(HsyB-{4 zGyIsHa3>Ct`F55>&RcL}Ug9DCleD=gHkL>QnpPO)QGIIeeyAH4qJ})lZ~vJoQF73e z>f)9eA@0DWcn;6whsl9{r-RIfCcK7y&eS)hT4WnB!I{H*eoaLcp-x6o#3>;dKPBiGLX5xwu@8ER&r9?RL&26i!fsSQ6;#oHg@4C5qnc2x4?S^h{e(G|hHs>(KO;Yy6XsARbDq^GW@EF0z z2|?*|F_Eue%NP=u;Ykg^17;qRdz7B`f$499sQ;3K{TGGJ@cXgpd+I|N5*k4 zjY3!2A01I!I2F~=5ahJ0!3b}QSKx&QQMb&%%b*<@cwAV?%IyJpr_7Ff}BfreuHX!K1nfT+v?XMNELU@2wC z&AW;z$*j4xnP+f80t=1=|IJ|?+m^m>pE=XNEKR?+zQjvAPO(gQPrljFhS)>?z01C3yk$$$c{gC&?bD z4gQfSZE0B?)^t;l`r#-KR)CkCv@u~N#N<@}#yV-~~xxG2eR zMNw)Lg_Bi`d3OofT$MsASXvfkC1?GRrBLFOkW={{b!1^YItt3`B1}%@l&T`~z*@~E z&#-@tWkyyB=l(y8;aes?bBO4&qmlTU{T8vzNAXHO6-U4q7U}b1FiMGLdKZ(V^&*>I zf;0JaVZ0IIqt{j3$HD!AS3(?NinxdA(su75Ow-+Vmp2&}dn_36td z4bH74^$OFRYi6{b4Vt?EH=ngem_v9Y-7#&zv6h&m+zTbR4_Z^fPo%Qi#{O}esZo&K zF%>MpvLXd~u=Z5mTj}<`%fkZ2AQSv&C=NfOc>ls&CtSGBOOy(?V3Qsdx#4N%bH?G; z+*V|B%8JwulK@!1is|q-ru>i51)PQrv_Ull_b$c6KdUXP5}^|aweMiP?}hz7Qhqbl z(3B+r1Aj>J@(R>b9jO=cgQ;11k1N!LOPEh|r~4>}<3lo7T0hK4d(|w2Be@$sE|sC@3i%=}Chz*p>V$$Y{ayWeaL8dqSl>V}Z8Ng{bKz1>GWM{gr&Ug=Y!Ih~y4ja8-CiMqN8D>iJcT2KA zl!WhAR<42PwvEc_0AF{KF6@FCEw7mQ@~YX+<9R-Q!RG_~d0e%{WFpO^FmtO4%qLK1 zdMUA-CRAp|Iz$mbUTy`7y)L1g-#87#_7Q+I1T65 zb)0znZCUq}t?Qn%joq`XDor(k>3F9Di^NOKGp-0M0ebN!T?xIl5r~=4d3O2oS132*Ryk06hyUN zhEt-t-VFB8ABS;Le&D$FMe8t9zs4KnmbXk_g!g`l9L`(bEB(q-aHujeAF5%B>VfPa z8_an96iYvcdKIXc5MDpQ%Ro&d7hPbJU zg5rjm3m|)^x$chexR>m>%?h@>ssKv!ml;hebRRX+w513B2Nbxf@|#jBhb^euQp2t! zC+G!iv~<+EtwegYoQ38U-BLzXlRbYVGw|&)r+N&h$yNDK(&krHR2Uu&VQK_xJ|Ayp z9b;YNRA9UWApszBOq4durf?iZd8pH&C-N_AEDRCjfO$3uLVO{#%fh+b+u z-?=ycLQ{2DR$}^{4+VNkFsW$x3`NLA;x#T$x8T~HhPS_)9LUu&3sZrY)ERVdk~}XE zoifa?GErwlkwfS$YcR)3qnC7U6k)Z6 z#Wst&~QhV%N{317^Qs2+}bxf7D$LY=wbHDH8y`f8iL``6Zi9Adf z>%h?M%gL|=Ud#m$ssOY0?5OiwaEi=hf4Bzj5CuMWDNdXI>?m8sd-IB^cVavwD^rUP zkVo+<-fwPGpBua-)2mhdGz(1|(EJfZybj8RTu+<)`Y zALjmVMdx`4ID_E|{>5+hQMEPC_$_a<|6iaNIEEf~uX=2@pvPRRVo_D4w~O)2TBz_- zhEKl$ta2e07S|Qei|`6&qXHw{UMW2I-<#g*nrXo-q!QJ5R_?4&I5AJ)S8k(b9SRy) z3A8_sOu;#r!Ymf)NN>(%GK(^(5*nNBuw471B`C_73T2PiDa_6y!KNu3*sSnZ{Th!X z`uvqtD8uwQsunHE>OACcwUo)2Z^Q&q57Dp5KZ~KFk(r%9*HvlBr^&BpsVaJ_YN0QH z#J^N?;Og$s@tw;gO})|O98KnST+@@x*zry_T=VjnO{{%9Kf>pZ@Mn8*TV3zGBjxzI z9_Sq4>sRVR&NTR0gY`$%g-o^PIe9-HmIdw#aMpajhg2z0vwDAUpmP)8L=XKQc4nXhprW zin+%Jn~9SqCX?Ntq&+?|Y1s9;aNh5hi}9_U0&YB<9jU*W&Kznx>#X8jQA5pLHP&2L z)6FTao9${1*USzo<0GauyJcl{o&N8xNrGO~Q4h>F>c;2P3Afoz5^F;{e-&mBzW~YQA$tcu@CQ9 zQ&B-z;Lmb`+9V|N(r;Vn2dG>2+5vhNEXFQOvMR%t%wYH9a&nRR#}g3BZ|ERGaOFwC zogW4Rr3Upy2Qa9y=3fxJRrDJB%m?@;u|b3j;LXsK`)eHi#tyoyr)Y&^(QlL%oA7ix zV7I`yc`we;bDV<3cgX(BK5-TX^KUtgRKo#a5iRkKt!#U%oOY~=Z)Y+qn@|6+h*t|l z2FA}|7+(#z^9r#4#{*&cOnq{ZEWM4oxSGr!(L<+F4RstNKQfP3`Hk5r3)%flm5;!LKgBHe0{$S^ z4^EGss-EhlO7NJG&&S~FzR4!)4jS7-vItz;RCt#d*-hTT z!C^hlLVfU8ElOYGi8Oc{Bm*l?gc>S=e2;VcG8CnmtHu7cVlHXh4& z^-i&him$is50m5{&XGbwdr8DYFJKRQPwYzXjGYXNWPrEKcJgMEBs3Y;;aJuP(hi4O zSZQ_$?6#S#_1*wfZv*UeZ;+MD%hKo(wu~NUTTx|>hIzY6ug5j~kbS9dQbByOX-o|E znjBORwU{*Y5_`!Nc+2E5IcXzx?F;6-U&UoK)-iC3tquY>6(;-@v^3G>EoR_H#2`@q zO>zvbfGtVeEW-3DwkV>W(wA=Hdg*Q3sG_zVJ7FvCsD|8WHB>`W0u4ZRQ2AsE-Yt{f zuV9<^>C#U~bn57jr^ssH>eX~z_KEy-=HDqmIoiyU?@#Wn-XIwj!nd4mSWTlX)O+n#(veA7e7K z1NPTSu>w?iCB4-q(?uNOguP`-pw>taMimvFz!y8--hx@W--d#$kp%>60NyEok_P_} z(vA2@VxTe!n#9!hm6_uASNE9S-$b)<9oE(j6W6(8aygGpRp&Km*%vd=@tY}*vWuOl zc0G)+T~1tbwqx6qPBeSg5%!$(oz&Z>=A?7Z90ZBo3=%uv>12jEN|z5AXDokGL!x&^6KNFBs2V)x~FKaYk<&Z z=emd~7VEFfXm8sqT-hJ=DjUsAwb?UKsvU25O9WU@pcqupi81=3^(| z{`+B=<`atz6%ty9zW7$RGAY?{>X9uwL1v{-NXot+4UL4>sZqFusgJrex%TDN8|K$< z!I8stNbg&SUylils z{>OwMx;ab=5Hrg)8-@dy2V5~j>0MWz3gP_vi-91;8A_&fsbvEVCd<=?U#J6>D%+wpR_ z+#t8eOXOL7VZ~9K@N!zhJYXVxR`ShNT^ylu$;B|2R;!0%9d4rQ&|I%l)!-E+XHxwR z=h5A`fDdP{sER*%60of|@G|zm9-csF*9?tNQFBlxr-PI5B|hsm@+I^1r}$~U0LKj1 zjA!9>Br*5lXI((&d6?|yKlzIr;6JX$muw{*uN62DtrQi+YTRAclPR*5G)EGgd!HVn&f# zy_FWV#Y6d-l)KMzI?vm378Qk88&f{U*XJe^(Q|T$I3RIUm#M_RxIGPmwcU&}wzwRE z;-m@ZZy7kM*}(vl!F-K@22Q{Wk6>T>g5uyEc>Qx&qYum`&h(yeNsF2zyx-f-Ye6zz$IYt+lsRXWX>Ha61<$*{+%6YBf2F!>B!)n=z^) z=|y3tw#o(~keOXKI|(?2%sp9!%B&?CwP7SguK*1?4?gjW1n~?^7ye=5Fb%fVS&KKd z2*9eifV=5JI8$BibLJF@VE-Pb}!)~zC(WV z=E|UlCzfu;j#oy46@c`F$bR|_%9lSC@pAD|52hNfkGFnq&gy8K)i2q%4%=&H4r#XC znbKAPf6D+HK-q%uJWDW*DPz~Lrt^HLDTtOir|oFcfkY*?olSIjqL#Vk4|r1_VNAW| zI(lV}bA8=F<^0S@zH1V$z5laxC*U@fZyUhZ+WSysNFgGGzmh0q$Pf)^5}C&gkxYrq zAt9QOA(A;$h6W)C5k+aBM2b?$&>)ITowe5YJFf5QTJ3#~%8&e|Wa5l}Q$?o8iq)3=Od01v!>N@Uuh18`__&LVa8^ zX^lBNJbg3eL3KHzPV!y4e)5mgY;&0KU*oDkVNfH(oPcXV{frx3S0{BdGMo|kCnoV% zaX#*OXVWjo-=+t~@1$GC&!wx!ZPQoBH#qU(j3~T0wHp@pF;>y0=qlGw;kIb0`mulb zTT}xdXD~PZi06erTMv8VKu28ObibvZVFa{ep}zAIYR2D9TrN$wiA%|Hx2A8BEepr3 zn43LB1^G~VU(_=FaddBb4Moe6=<4+B=$!6rXKGS34=<{htFq^3!YTL)(`CPzQKfJ$ zhVg6B3e`^!m&q-^fteTylht?Q^aVqstCF6urnb?#_&zhcT2UwbsH$9kb3=~~NBN@< zqbrsa4 z6vFiEZ(93hcz1ApssYc8dxG73%S)zPI7_oruq@rzL~cxwH)AG`VxD~&dxEwZXMzqH zS38&Sw($Op&S8~|v0B)FyUL%gojL@+dOLNG9;Jc* z*Gc7vfBg`a;?{q8s%Lnf_t?Sk4A;f=!E3ywM_}c&bV_OYa380$g}Cv3x!K*0xmN%e z<4=5pPfVj1#}kur9142I*Qtg6#qsz!E`c+fL!WpmsvZ9zGi;}5?R zSPLDazR?3w_vkijqoNg4HhL`jUo?c@*t2+buUa{8@r~T(q`8C9_~?Rj=L*I@MU}BI zoA8x+JbqeK`G9+Crhl&{E#Occ`+FkDKc)uPi@6fAb=^;_`bl2#Xbh9X5x+7UwfcLjI{ZssAA91@lYQ}NmFKlwC7R%!jHN2rs2e*ItfMr3(*$Qh zJYSE$4_cE8eta9Wrk6-M4F6N0c+ygj5_fR8D-+~8M)$e8GlEmGQN%SXM7L^ z8H=OC*2UKJ@MvcGv8Z>t6P0AEC?nkjqSrW@=xUm3DigNSNp*;3hmS-Z!`|wOfzj{5 zh-hUnCK?hvi_P|2bb0W6bP!AWtz+t4LakuCpXG+%L z$n?K>Sai8bM5tao&olOHw2_ne?&wnU@S<@BB&vk2qZZfb*71UPFsG^MDvh<&_}^J2 znd+4iG_&>0^?T9zOr;FnhzD{c$tvCv86tr)*&&R3jXKyZaoL zz#^aGC(*9pbhMT#ey)!3IUV=_z8#&#;Kmp%b)C+7Gv5ET{4cM>rM#4zZJg|);V+l^ z$1C|e*_}F_6iZ*=*^-SqsX(wRT{bwMzC9?M@la49&@ohom3K2O$nWCl?1_)PC1*Tl319`^9py-to=uk zP@KcpZQ+rmS$JIKc?w=~KI!JOZ5L()_t4K&;KPtREXkegCf+Z%iPMdPmV7&k(O~?- z^>ratM3>~lAnSybL)0jXaPPXsWy5kNI;UXLo80dS(I||O$zjcCj_Kc$Ff(&`_={_2 z=A!V!%$ec3%<06B;XMO|y;Bx@0l$7{I5f$nT%)4SliZfhjO(S3#tqWD;ycstii)q|FR9bg55?Kib>lxVOTSEIGo3pY zIgdMfIJH&}vcRNfYV?;?{AoBUT8tkvIvgE!GV@fOS>3NkF*WUhU{ACjM|MVVCETp4 zzM~yg;V7<>OL2(5ig(KrtL2Sp=Kez<2pv>@^(n~8W9<}7zA~x!Rct(s)ez_wa|GMs zykh+o!6$yhZr5RM5)u7kArqdeNrrV727}!3X>f%u!TCx`7adxlSD)(hum77ur1o)6Av z?heXFZvs(%;$rn5b!nmOj(95cN=hFyGApYdy428XRy`kVOv^fnyY)p7gu93JCi zT#tIr$KTYZVfwPTPWtz#f_GUcy&{VB89%1_N9$7UqnAu(I;U#DFe~6mm7$_9MMYm4 zC+dc%L|9p*s~fE`ml%a9_Fymo6aKmAG*!f>*bDQxEezqh&@?$1Rm3FDm;4)@ilgY; zI7Q)li5Wyr`#hIikVoH;kF$S%6=I>d5mpqgd)y8`s<({s46JG)p44Y?3+$+_RE+~5 zcjIXOW>L6vBkr0QhS2~yq$i) zSpSy8@Mo#w!FtO1C8|GaUqHhb9w(VxE4R6cDzsx@lr4*9ud41_X>XEM|KtGh$cxb49N2O zr()Lo!nJC`F%FOU|_w8eKptxd*Hxa zA>1$PE)6c%vqk*J`QyU;&#P*_qMcqwfi@bqqaEj@sxo*U-epJm8EnQycs1Uf43smP zs2y*{f4)M0c#e15FHr-lq*}5qD$Dny2xK#_m6Ht%`#-wYf8vADZ>Cv4%N6@gv%cjh zvj-n_4}SHx@yqcK=2%B@=1$2I8SvYyAeQB+J?mjgbr5Z6mXqmxomS~9cIw&xFzLPo zW4R<3?OGIi&2>t>^>3qs@w}(!npiB?g}w#%a|Ti{PdEA^AL_p0*Fj@bjq5SiE^zzV z&iQ>(u%ANiM;zW?s5Fnt9mg>dPY2h_M1MkdSH(KjIG_Hq5S>Rc`J-g`MN}HUvXly( z^G{SH>|uWK5SBnIYoH~JvMuev!_jtL1*?LQ(d=NF`0y5$)8|n$45FGMZmFOaghz+w z^ZXIQ^C{eAjd!~MMlpqI>lqPqFdweRk}=%DKICbYNgZ9;eCd&kD#qsgqFN+}eVv`x zPf6FUblmr)nwawQaY)@ID&A@$Q!Z(jy4u-K*?9u}Z8rTKZj-a9!$mauFIdfe;%CEV z<|8%mvCN6A>Yq$ySJP@fYeL&zj;I)Xjq9|B6W@Vo6>jACm^@dZrCqUws;n8Oco?;7E8mcHS=WGWLpU`&8v z%r|Wer_N!)=UnxEql3&5-b3MX6LrFsd<#z{v+yB4;-K~?H{txLu1ReZ{)cg&o=rva z#{v0+YM-JpOpm56=^mG*X0MXIC9bItsg=GERZAZ*Ki?1)=1~%+pNxJHT|dy7y_~us z>Yge@k#JdbNh*7USrW|;^O+Er!YZmo3Ed74ceGmaZ4=>NqHaN9y!<9{F`Zf_t>1oL zA@4gs=~dZcD2I@CvSwYMLK$^)UVaJxnOXb^-TVp9ZD0HtE#Awn_hD2A;FnQ&D82^U z6I&D`Clg+oRuSjKo7Sg5={E6!`WWkPTPntNI1#Tg?_QMJ5>JvXho+uUDR++Vr0d4g z5$#Sz2lbTi=`AN%;hppo74;WMaG&X1E%ibrDz+QB^V|^qht2j$QaPHI)QP$zjUt`~ zG`vHi1My^>$Ty;i@h8z^@gcg3II0#Gj7!P|mvi4q%MAZToz<0nTtn56qp^vfr|X0| zi3a6(DBO-S*%lr(#GHFJ-_C9E8<^}X2$hVgUHPGbaGAjCoi!h)XP65 z3;Dt9gcO|*_wsz)o779~Gif=HJd-+v75qoCCUwRdNI5$+U$8fIEf2X$!Oqm3n5E4i zZe4>JTt0_6g{D<%9OfwvTa=r;A4vqbTl z=TbuSp%uDA*Hg$XQl83YJ|G2)@plQEfH`>3b|_g9en1gB9s96P@>*C=Ou5E% z_YBweUG#t}OqyR%AN0hlZVdCN9(Tb0X^uH^FJ}Y{LoD=vqif}nd^{!`zhLHhX}niF z*c4QVUk@6n8M?%S)eRk~=tvokgHANOEt2}stiIu0$*bXQaz*vzNO+5Vz;``W!P+P-)qo)r{vuJo00Rh;W=s-Zne9!#bJ5T_DxBV5_*=>#(5j6<|2+wA!_;>FN|t?EP5 zXe`=3d=+|_XY`2MnPCi0o`6RUqi}pyj+`E2$;TIx^?Hk4*v5zXyGAsV1>Dn{IDp@) zzwK%tJ?+z&V^6(r7PyDnf^ybLxnz>(FIp#0r}8;VGDzCvzTKJn0+;%YIJ>Uv z5LN3YoVOWaL972C?_wAA+REsZ3TPWl>Xo2Z)ClB0L@kNXmHXI zSvQ|-9wAEXTGId?Xf8pz(pbD>8QWBzkxBbyezcHSF z;~UfupP1PG7=E1mExslYlpNw@Zt*c!YGIH)^)27UoLpsZ3%?Efg`1pcvcQaUl#b&e z9xm!-A8G)Hn_$7qPBN(chcKwH|?$Y#(L`pypZY! z^Ys_&O*_AJ{mcEdfJj|U_GrzCby#pmFvqpY^Pj};*yN@NMo(4VmW_B#cH@OO+~Z{5 zTSU{jHoQIf)D&W0_&{(ZY{A#MeQ+V{f(6vW)hEalEl-90gQL)V6Tf|L)5$tRXzU`*+TraUsRT##1(lU z6v6qqgu>uoythM8rQNim%S7eil6O$LwzD1vUYvCDu;MGTRj2VC*7|X{Zj32$8NGD=V4&IHi%^M0t}T?W zzxZAnJF1NT-(!;8EBIS{If_qrK>q$(9^Ym@^S-G2Mp7Z1>jcAzkgKQgZF+N1XrrU6 zpN!){GTs@06T^TH$ehp4iF8z%k^KbMPp6zT9;CNAXI9BOn7^NM8`f&G-m{3o}wah%e-$u&%Gu-Pte6T(QIU`KkUsBjx`XEC*mkki^eHB!^X8LCMdCa=J@(U>cX45+SLr(Zd zZE%ib<9~L`1+&hxL9g&c(8p;219X=obezwIuc;YE+X)?lb#~O3PRu?^;}xWqnjQ_} z(t2m|fC&0X>ZkZgbIkE(Av060WZIkK*HgK0u#fXS{2FSrJ{pcY*VWwW?$l(=v=^fo zs`m{fZwV#qpm1{30FGTSoR3AYTyL?KH`O*>+qd+7+G{HXq@#U~t^hO0J4WRUoK>6QZhiZ0` zrauax4gm#rI=&MVtg@=GjQXM|FR6k_ZF|14$1W<(q2B2vO1#(2C^ut??Snb}WvZ3N zKQG9A@%rQd4tj_SP!>zBDHhRF>ZfH8fIZaG3HID|;iIbdu4<`=p^qK)6z$d7ZIj>Z z>@d}qnyN!`gG#?f>Iq(q&*-&h>A~OQ4D+4GC~2DdpT46p-q8qWS-t7(kb`RLe9i~E z9h>TLxXf()s_kKq@VGNWooIt6b2g}x+8H!TEq2Pt2p;4uxqVa$TIgc#GgYXS+LM$` zZA`8(iKJvmz7Hqk%Z@i6ebK9&$Z7ax9N$^8+hXtHZT*GQ@F44bFS-$ZZ+3s!+~TB| za6v3c+X=Z%@vjn}O9t0LVk>eFqU06png-3N{5z@odj+FK@0a!2OLW-pIWPVb{r0!P z_~4N1PYC=e-#Z(O4bJ(=bG*yX`q_VBx&Qck$MxvP?699P&Gz|yPBF0S9^{c&A5W{M zzj2c~tQ7X?Rs02VinTF)_21(2VQSlb+!jBxw>OE{t4x^}>z8Mkd%uv3iJx?rImGev zuM*ei1AU9R#kFRad6L2=(*#F zD4BUKTxxx?nm6nlI*r%F*D1j*;!1Px-t5J@y-gij2JV!Lhtz4)@B_T6K9)1yqJEy4 zbTu952j6}Ydvuhj`i%Z@td8~>{#8$7hd!x~?5kqwqAqBmkF4k9no4rYHR0*_QW5uW z6W{NHFR83P#EV-KPYRaD1F=Ti1v5lgyVmZ?>)o8^9d|_Uv{yX%M4$FSvKHt5vv`&K zyUN6Pji~xQ&yg+WHJ^*4`^3=0STz6GD+wGfC(NusFv`Sbg)E})cF)V}peh8_!t$bJ zS<$g%FeEG_8eXQJ$V^_LZk?ewogQxDytM=ucD#4PM-!gaF086&FO*c}i+*jmpKdeD zA$$RMlovR$4AN6{6%$&TQQYr7+!bdJ>c&5qRK1hjMd^N@ny*=0Bk6$6)En|N0=H>` z6KChbr&qe`Tg{;M#_w1Q?;FW%cgMe%IZlckxf5sH$rKHfi4=~@uw1r@35PK95*_W; zc1BgT(}VtVI*fHc623H<%kO;s!<(GK*9F<>UrUDDNEP7E8^chkC`Snp1Sz6Zio<8&r2*&26b6LH$%;XAL~!%z-vR zr_{YpBC8ql|?e_9XYuIB;KiAxSZ$Sgpiho>N^+5+b^@rp6 z!K1oj?gyA%-GgB`ilgEh`1RL%7umUN{03Fr!HHvq75xI#u{Vc~2a>T6hmrEYP?2B& z_FF%5>c0AiCsdCERgS|sB8*i-P7)F3T#O1+)Rr@>kcIIcE9a1& zj7NIKV4=r1!~B?1g;fgIVvd$qbKk}_qNx?s6YB7+)wC=aN&WB`CRH1!TGWT&S2Xc0 z5>9l2&#Le*XCQqWoDMfR+3s~`9!&}=nrqashH9sp1h=N@SYs8exnkI_`8f4l5`4k+ zd^yMIXT!7N&AH@uyR%rBf`R0Lg%>qZtq{!AC5#ZIJB!?R2hXdN7MND7hxmLgt{vkg zmI*D#dlh+pq`m$nsb+uQVfKALK2Njo3a|Crpi@{;rBqjy^gz&4cilVe>U})!a^?bt zQ?GE0KN}Zx@jf5&*uvNM!dk_{B>PW$ai7S!*`#nukdl2)Ld$-D z3V*7yd)I_wAzt5P5o=U3i#lKso=X!exB|}0W%}b|@m>0g+A4vnD#@FY$Kgk-bn zJnBAiV$BDb>F-5*ogZ}EV-RfCt?x8TI|M7uG#M;nI$Q@@(T(D1D$H_=8s`*$_3NCu z(I#od8>5*y@dLpLom?^4)V*--0b`TOjWjodj7Tev|YT%D_tXBObN!u{eq_{NS?arQ+H+XjF>SYJ}7@cJ>&Ojw%*{_ zxj+n=W1q|vKW0(+%@2VFZnUqu7_sxvJ{ChOH?i*#2n_yqcfxqK=oU{NsP za4pu+9It+y`0<3;@vt7FC1;)oFhXgy^&AJ}jdk+=ba{RdSJgJ=k#(V!PaSd(Bci@7Xr1v3Q()$ylK#- z>3fr_(shzD>B^kJODDBB=iHf&;`-^sm<6Ak7_W*erC*6lq@RLcb;oRNNjY~9f2+E@ zBWlOBJznDLoo1iY;>tN~Gw?)y>r zWJ^=eyOXXEixH5EmmwI-&38AMZG6QG^H{cI&db`|IE3OyZ zsuQltX|IZDOBHI=D%fh(R64hsO4Z~!dt2NW58kOA@jXd>^SI#h>lB0sR2EI)P%Y)1wxUhP*tAYA>Wy79NL@KbPdgPR zMTM=;It7!+LAz5NdRfI8pZEEkIzw@W1h1&p*CtC%h(8qLzO#N#s?9UPDtuQPh>5LC zv>pj=hPPC*Qf>;Lht0oinmbS5{)UQVy}J8zb@@RP-G9~UdG({^IJDgpKEYq2or>&k z?z6WzNu-ps^R94iUWU5lJk9nA8tx-JA`T|^q`v37yjMrFn?KQ4NhRyXK_pW1<%0yAH_Dg z*Yx5>**Uj5{KQ2(Y9nUK0(0i)%$xhFeB0eJ8Mf_Gn91#lI{d~TYe>^kTa*X57{ zScdu{huSlr^;1-zdIQZ}EzS>(c#l28f$gbeAWdGg=)I(p+;9b6#p%q;d5qi`e3)4; zSdrN_cr~+E@IvPBU}WZV!O+Y}!Qjm4!LZC(!I;cB!PLxo!BW>pnRA1qnX`ku5#%p= z*<^V#f7J2(l%K^o87;OA52j!t?TmZEe>((snv@RF>AjKMBu5r@f>YYrM}NpNX6eD| zWR|JHx?pJXP0(ImsY^3n+Rn>i*PS#8{ywg6hc&Uo+Q|c5bo)KIH9Q`UjQewI80bW) zL7X@Si3bDCIr@cQdACamh@=DXvXL3^`~K{Vr2s1@H)>Foe9=Y6TYeP80p(R(%dgOmXX`vd25aFSz-B6f_Ula^+f#CooliGQw`) zrVpRIF06zFQP4`cV6PpZ9NCCvJx8n>i2>2s47NT7L^<`v70F52?OVFCO>)BPruI`* z6eDH6$DNe+u&Y(_ar_`0r6Dc%1J+3s8L=&#>Jga3Kzc0x8}Vz_(MEaX0Pn2Kq>v80 zg!^>8_*gL+=QOaT{=HA>GLOI_lVAXT^z+I8XiF=H>ylcg*LBocbyRD&hDGfCTjb7$ zR(n@`hT;0_mxGV3_Qn4H3{Jzt-qr2?w3rC-pE{3IfcoKa^}{pa)4H)ooh0#~eOxQq zA>!`SfgK4i#k4sWhg>=H;#6Hji*o~3YqivSJcO1z?O~1y^JEj_F>&YAK&todR5EQ$ z4es~nb@d!o^v zYC>L$>7cU(ultn0;2Lr=*@+#Nh1zX!_uq9dRww;kV?CdCakBFP1ok66_h)j+H~fnE zutQ}(6*G50H$PK@@6fA$>e}x4HsAZuH1&OW?#hdtypQCfRZ#T#R@6(}kVhtU_?4A$ z&vOUOl7Ev1$qz{_YRZao+I4FEYp^o%!yHJ68V5Ay-ytq1D44$V)St7r@fm^vs=6K)9 zZ68`mJ4EbnAQuO9z<=lm&SG9<3*pA9@}gl4QM@9J!VPxY|NMp$B6?Aeg?)XM?-%gr zSK#sIQRDDFlWQ)xBY)a$KdAn`DD=n7eZ%p62mMUw8EVyRbN}H-XA& z?tff2xih)Le7YUHda&vFG$_Qo>dL*=)>-SO5H@Lb_(v-zTn?rqozB^D4OR5l)X`26 zjna6X1u#H_Q3*IwP& z+Go++XVO?5d7o2a?t(+q;t+H*^tq(!{YqF>7>uM~=m&LpgnvkTJw|iv@&|AW?z5}z zi?|gHHUG*D5VS02ywk8n|9AAI%c%Mm$p{O)zN~AZ z=SzHVg+F@-KEDnQw;4*kEvYIhHgM)a8!O~-`rUD&(_HHJEWGL~zxy|S_@UTW*si(> zep(+tuB})27*uy;P}#Jjff}ft%BwHF;W&GGj%$^?`H6h-gRlM$rmC67*%yQ5i$`Uc zmcf1DT{`odG3SavEwXcC?xm=tc~`tniyP`H(z z^J8ey4%30J@gshS->^F@De*F1c&In)X>f-|~zQ%7^}aa6}6 zz0RRj$o*y9zGj_$oF~vHcrSTe{_Jlj4~6wSZRI>~eNW{GJ6GIa?&QO@s*NrB)6dMg zb_Y{nPRq^3ws0f=#@B~@{{+1AoXnAg3xf>U<|V1oFwZA~oT;v^R<4G=zRMZiwf$L* zAY1BIlaQMFk2)Ng>iPTkb7gAobi;>&&r~$)AwWy@EYrAbJdLN?)79K{TR22c87NEj z(>p)reD0oj9o?Q0_VX+=&gJ&R`#82cRFC^iY!9m?PVyc- zA52tdJ*5KfSHmDl)6* z{aXA!#Vs#CHcl016*Y6(5jGJWaxF+edFQ%jDNV0Tz6f&Yd_!!t3s&8~a>YOKNABSJ z9^dr!0xR!j>wK*EI55bUbP0;8=F29vy_z!q^}O!fzt;VMveG@?Y?we6ZHzN|WZ5 zw5V%L6hEL5_%hlR{1koUXVcOYnGcXzowFysuvuke&~%A&^#!HS5-m1P!nQUFFcks6jPeu_%{pZczj+z zIn)HYH`T<$`2TI80nN>|8qv|;%Q03=4eCH5ZO0k~ z{;sE4&{&G<`TF<|)N9`*Svm>lxr#BR#fw{m?8*JStlGKT-Gf##-vGV*BztYS>mz&X zd#~y*9ZW(G#gWBlRMFh6nE!HOADCYeY`1r-280_aqBqRN+e}oK>uvJ=7oy77 z7pGnOMUON!XIWT4qwq-bgll?uSa0zQcEAr#oRaxq2zR8m;oELV#-vvAEnS>!H7z}u znhdKN=k)4f$+hV|PP%q7R=QbIJ6+#ZJ*h!gUYR21de~LbbdDrdu*&pELV7kNJARsZ&}0g;87}mBc5*6#iv)I7My_-L;I%#Wk(EvZq8WXTz&}>Z$7bS^M)ad%B4R_Zb`pyO{`4nrYWk@OOw0O%RIj6>(xok^hSTa zJ-H$Mb#hJm$0QH`&%9RB`;NhxLv`=`-EA@=c3sPx#a~pvX}n)0X0=6ebH6sff2>ouj1YpddszD z!LNCo=<5;AS$4h%bab+qFq-|R{VeFgL${&aa7*+{P>e5PF8ZN3^UuKPyG};?&2`xGgF%wHm&@sAyskEL zoL(I?^LqwHvpD%o4nB>Z3(nCS-C)gkG~0c}zxS2vGBNWm@p2f1=N-?F$bf}>=6CD% z2Dk_F)ZsMzxH_fyQ{`5t{tMUGpS0w6*d0%Eu)HzGRC&C7F$E)On#uC?@Ou1QUWw-A zFLDnZEfx$hH}C0c@4DaQpoZ%@-_Pf|z-8`d*T=5Kv@c`ilMc|IYI>GDYEPURRd+Ex zL`gCCdNaYBU<1|EYPZWQ4dFs<;e);P`1(fO%DeHKdj9RE>p$Zr{-?exh&ynzjCBw0 zKqpQ=6xqR1~7UF8&jVpd5RO2rDnEbs0m zHO*L9%ahh-UnSEC?k`x!56L%h^v&|2nmU;sFEV?b zm5k$BIx2oCc^cj^hRXcKcpSFsGxFsapTlUl_h>%)qkU#$C|<_OJkP6|CaT`2>cVE| zo91FPFOXXnIjv-|N^FrS<7@8I>|`iz%jw8!Gu~S>c3~@eu7u=y~zA0G$)jw3(BxJ z!j0}w^S6cxJ?_4bOWuO}yh2Yo&XoO0_dpojpO2~{pH*Ki=3uc^*8Ks3dkW_}!#c@lom_356mwkz^DYRz$p!U@ zlDBo{E5zg_I`f6pm-E#nbH$&zL3!MjOMUMUMD_!7vRCD$ftUeJIV@F*kJTE4#!3v`PTFadqu-S%16p4yDgeT;WC*A;v`<{FHlvF=CwYA1AT8U ze;v3kG{yz48~zbhPy2E^CIA{4&-N}Ed{u{$NCI^!#yvCTK_t!&oHVbbURK}fXo+=hR zk;>pV^c%;9&v7N3y_=ev>~#L)x74@$sil74==lRi{QgvF-z(0ezZj)V$z(&SZ1QI6 zmSj%qc5}7|lYyzuNe6Sb2C30Ja3&{LrsnZZT#_2`AX z&g;XsIo!OH`+ zF&NbWv1KQg-iHlz&ux{fHHF&^-JXe5#|O;h!eU|Ioc%h`Ug}-vc$iQEbQq6*yr= zvOij?-S~)?d)COHAKOV(i=oqidtD z9|)6}EPt#pGu-MOe2d-pJ5=?8yOTYvZ>G_M`u_zUl}mBFw|YDP+4);FlM!CVNi!W@ z8=kj!PQYmo!)p(~dv<%=;p?rwzX1}w#@~2L-L~95TVis(&=mVMJ8-`2G+z!{05w`H zCoNMAy`?HzMRoU{-g|?JZhrSt@VRTtD$(>Q}0lFXe-E@y*x8bwc-C~UEQ!hYSIPg39gTSa<=tKQ|D2p%S}y;|3}Yp7RThXcvL($ zo{F2agoDERxI+9TJ@qf~x#+A@VJ}O*jfy8bqbhs@@AKFx*%u8?4o4HHq828(a2c+P zclwQo^sZ+yTynyB8riwz{AyQFP6w}Aj|1G_2j!~r z@=Hz}sNZmhKac$ubBY=8v}g1~{p0=kTu03`PV1*a6T{p%U{{A-G2XG(u+wUCh`9@| zxFJ4qb8MEjn2jC73~Z%qlWx>>kHTrXg(GNT=isMrNIKBHwF^%tt#C-1@e+D4yu+NB z3nL`6f%)=X=E=941=cn{t{xT+D~EN=j=P0rxcFTYzHc}D%5Nlh%85G8S!kE~k^9qU zd<$1QGk9k3W$LNmV5&p#d+N5}q~76V>fhwg)UM=b-c-9&&r@di;HUinN9bz2N6Jv# z7RJ-a=iHae^}SbOs}!-`%BV%F;C9xx4qJ%Dk3yRUi!o#6hRHCZnOtS&(`>vZ($D9u zFblJ6s;cODHRcHH_P(M=7g%8{9K43=O6SnvG>?NH4#I!#>FVlv7vJmV&mK2_9A@@3 zUR-%CenKre(%%~|D!uCL-^J7xZ>vz=tRqE)xjUqKYVNjeF{nZ(lqWXeaLQz=r-}sYx$jc3_>9c=vv<719$#s%Pp5JlDl4|*L|0oZD6Uow?d&6QMLcn; z6g-~AP~aJHCpA+Ky>&nM)?i%2;rQxL>#;|1q8Q~Ay{!Ipm+oDFC)MSRYuw)yW%X~- zV@@T03fEu~73G3tR`jlObw72k?vGAG)$2u<20TB4MNxGpgf$Mnin>#vk2d$21-n>7 zf&7JJbq6H4E2Q!T^#HYd@{=o1c+|T*n)HT6 z&T@U`y5Q^UQ&^qO-x-nmo=3!2&dJ#3B(il$PWVIV^lVtw_@qI4c=BNSF{e{?aBgg4 z=ab)_+<|jd8Rx2ax&T*~Y+}Y4XDb}xYqRM!Q0V0I=3KL=o!;Uc`VrmJ z_p->}oE0v;$Z)J<0#**J5`yx^sT#8pkMezcbGqYB+qYf zzuyVY%fcriZNFTsxSkeG`s?nxQ)qSwI=H_z<)wmV87Fj6+toJnWXU10h^BEhcv=-R z&&rffm4bayWgeMTxF}RN`>ah*SkI|<54c(fFGr7X;&~!?!@AxaP4%;jxi-BA)io3N1n;sRbQb%9RfEKjP_w0A#(}Fzs!&on<)k$#%(G#2wM#wj(oK`uVaH@x*h|kb5PN=vssHHY2%Knx%XtBE z$`F-#A6-Ki)l*Bc=3ZH)7Ob}dKbn%*%mw9w+@jAVB2I?cR5mz9PLEf5R$N573EEND zT)G*JRd;pl2ouQ3o-KwpzlRa=sT{QzO8k@F_o!LKpZK1~)ntE|VIG57|AHUw{G?>J z*!~%eb&G4A=WqFMu|J;;lbULV_k1$XJY$C6Ite=UoUHeh4A7sR>rs>OHX`G_IHlEP zt|Ld29KmvD|Z~by`IqURu3< z1*|6VuK!e}ekTfT7q!;v)R*ea=OlYg)c%A16^B#Y?!vI(Mlg{QdlEju%RKjA(KF7Z zmYb8D#Oyg0E^zte;8#DpcKUjqKb!66hWR^fsO&0-!{OF_lSA<8^`6zjv|JZ#Q$+DHdm!(728yj~vLxbD|*wk&RXSi(fF1b$y}+>@gC-i7S9EA=e7 za04@N7ylDiPFTgi=w^3|A6t^~;{8Zf(i1ACZo0I#IMa>cQg@lj-s;}mq&vG#UsS^U z_G%7P1t2p#q+*#U&TY<{)9m*Wv)>F@LxQn<0cLfI``I6AsYA*87zUeZezs#X?~J$N z>})U{T1zFeDoNtEOyc#=R%#t{nKoAI;A8=1;XIn8IX?HR ztRZ{S*es(q=a%bIhw-t#i%*AN;BRl{{P7Mq`vowmmsQ7Oou)K|lA@Pcd8hb5&|0%zzE-7-xq(`ys7Z3J@F-@`-*jUCnil*E&pN4oI81T# z4a9wu?sDNp4NhD7u?O%o?u1C(3Ln1N&bt9FR!UAPrgtoC_Fn*#rx(k*dRiQ- zaTtut;@`|v&vIl4!>f}VdXe0;fq9_LIWdOPs>1V7hCjTv12B@$pt0-WRZB4xXX>2C zdmp24Er!rq^wpL3{*CRq!ER)xq#ydZD_UwQr#lDjig!+o+lP4-fYj zeZ@8$-Pbt`KFbZOTXG59^8}{!9y6}@C@f#KK8EX>I$0a_tc~*Fi`2Hya;kh5yJx&t zKP^58Mfg@nwoApg!<6YG6Q~Vvjdyfxi_My++v#KN^nPZ|?cCvd;!Op0Ye9%gf))FF z(g9A@-lVLxNZwo=Z;~wKxAD4b8J5k;i+QF4)+=+-#R+oGTu_zl*@ZR+PHl-CdI?)wF$^&HjX7lX#? zq~2zEFRHEI^2npdr~0B#^eL~Pj||*a?7B;>T298urw0Bv3FME$s@sb2?)oO^t-byp zn4d#%rp9{hll;w@Ui$*R#Y?de-n9Exx>mc^n0v3rMtR$=TxR9Z!=j&}TN-5s{uuP6 z1@yQU9zijjf>`Hw05-MGexJtOufKQMRJ6NAhjA5FV&YV(-zZraXo+YA;{KX+?1(snUyllS7O88GV$fGg^K1}P1SV++==P#$os)c`0XZK z#4p5x{YiuHx1H@s|Q!F9?a8=Pc*H4CBJ&*73G>zyWwL~vo z5FL}w;e*Lz;T_4KuwpXYJbOf#+v9&cXO6+3_SpMdxqYmpl$eJ<^^(2+ln$@YMK#?# zNbHjk)8;Cin{cl$#lilWvU0wa#heJwNrai}Y~8ox zfAr^KJkJpj(0+KUkHRrKnIyD< zY_+rknu|Bh%om%(C|g3j+rY-MdeBbRa2I&lqd37lORR+ltdBck2{rA`>d7$7laUxG zV_>k)({4->b!M3NzGf|~um;w`7q;qsc3#W{m3Wmlr3D-y^1tj=thso)4zyj(a~bVI zX^tW_5ab@9U+5VArZU<`J^N8G3jX>WWyJV!G*9(j)PSv2Pj}J>-HaDi3?DC-E7Oeg zPuG5PPICtHhzaV+aTn{(fpClV^ul-G)cjAEbBXGWvn%J9lbk-kR^h%A^oS=@%=8T^ zt8|OtUuKs*c}_T~}P=bkK@iX@3aKCMki2>cK-JkQ~ zHOTW}zBk%$deP5KhfXbok*(6MBNbh-YF$ z4TUPS@>tC-FK9AxHhx^z7)~uafwKNJ-fizu*?%QY{0Y&{K~u*yNd9;Q7WulBu>tn* zjd}QSr#k66&6KalnQGt_b`fX!-phC|$!v4s3Cl0?^dh)Vep9P*5Vkv9tz7g$6!Giz z9qUYlvnFtBRM)F@#;cQKuG1!6arj=6Ewupybu;DC#~ycJqVD49v&ZD_dmauyCi|g# zJ50acbS9_(p{KP4*t?%42Q_sP7GLQy0I^w9G{P$!qV;o%j{@fG!r@Q(`((OGpR$LSt~B% zy4UGny&zxXagUZ?5{;$q~h4>hS4-cL*O5SB+DbF!!GzZcIcESpEz&lXEwHI;5tjt`>^DBHW zn>~GAy!l&{_*0d5%#8o2D)%>$?N1%{-)hHmv^@z;I2 z_xcBSr#kB=8U+<_Z;Pj{3-YF}P=#eW+vSMeuoGWyt?WCO)?uvO)tgqRi5*c*-BA>` zFQh^`>;&h}sccvA5||bLMH{>yli^cLp7+!jOM-gwbPT!YRK>#~aQ!@X_jPCAZ|~3B z__;RLP#d|Ytxu)BPopD*@nO|+SGDUSGHVw-N9SO${)3kywEjNPypAYYNwroQI#7T{ zBU#z{ms+$zHFxWtbSlPW_dsN_EGh1d-zxr*hm&KU&}qd*;Kc*`&`(xH%kkcGRr-u zdOiVpJ*G}NWODZtAJm^YQXN(${lRSjua^hG8)D%K5p%h}zdWuCgSZn0(G+Ui6%H{d zSrb1GhnS=0eusu^n;7{$4bgGhqQt+O&m5zqSXo&oaHm?NshHDQK6yeO87)3e(MiB) z#hb#q%$j=7cFxlnMg=y5v+Ziv=brz>vxOs^DrBQ7WEltkmv!F5U0poCk3Q!n+L-)M zmI^QFP430J!eyM^SV&q3MM;6piGxS7tSX2?5qy*>67NUuc)2h zb?tGtPGd-10kyA)7u6Wg7FDbuY zg{hp=rx@dip28eD0#DzsKiq|zy$wpUk#=*9ShvzHc+;+UQ-8TqCVp4_toFuhsBMST zz!9y%@vw&Ou2xV+yr_w9(ZqUqRNQz9j{dUFcZK-16|VlB?)rrJNeU0Mfa%utIGDF` zbhrnLqXj-)7grx0(UVjzV`ZO7Cc$$|c9&Z{>r95X>yY-E<{XCP|6`XW!CvU!X8j27 zv~Y?@`lO89iRanfke&*z0?Exz)G9ANTo>-rdu$XV-Y`{~3zwQoGyVc*(KGSKm<%hi z3}?EY!DxM)>Mbi4++~_~6VHJn!Rv7jsOuTK;4sGA9<}7h5Z`xIjY~}aUlo1Fi^QWK zor56^kEzDGVI_CeTenqjwS*Nm6Vsc*ESp-zEqKDV_Db9H#C#azue)gWn5fp@J%!^z zR^JRqQF%S1hLEGf7hDseDU(g}rt$;Cu!q*pfXI%A){ekL>jR<9I@PU_uj}~!P2r5B zgij=&^^k(ro^~$|i(Y%}mQNtI>tK~DAhz@L3)ArPU(ltG!6+FHs~RW=^uq=2t@<*X zH|1GmhfK!-eo>b;#u^wA-%jsW6_2>APp*hF47tF{k*`DYHu!}0sLuY0zfe2l#;TWF ziD@I$ri*+&UwS;_>!Lc+yR3?ys^OR1M_%Eom_HzPXH=evSAU7VDKE8b0S?)Pd7TxZ zxGq8+SjY~^Z^zI?nvmQB)vbXiR93H1F#Lq?-709;Y#gUCVnYu&!~?qjim;3PX2fT` zqwlO}r=W=Qt>lU3m^#faI{Dn;Yv)TtPdY*7x?!93fxHibt~?!FVOJD}zLgY5D#VLa z0dMF}*2o(laDdt7llc<5_pP0A&@MVAZ~QH9{3mZ@*nyW>msh%Dg-t83v1_kY6PM;N z@jvKu864mLg|z~+H5K*)5Q1hZ?3SiHt*Pf*sjXY$yEF^ZVIwuyJ+un9!z8Qg8O!V7 zN~*rEf+XaiHq26&o>ayCth(C`o7pM`u6Ac(!#cpOI>$z4zSXUO;_#eHpp$>eDtpAZ zjq=E1nPsBN;YpZ%H!7j#Du}zxVQ!IS%BYG9Q3_pVB9p|qc(mn%-azQ(jRPvGy)+5V z$L507lK!|W^rkBM8RI=>sS_T+^Ss66p{TF2dCw=~N4=XK95x=)GY){b z4c9-8Z@>u)Q4eClU*P|fau+;8`8KccwZY<9IF zDFu(b(`vWlFoQNiBe!GH?a}uggHZ+a7RAK4x{$<2V6hY7vhVX!K4Na2FFa}bmkv9G zSB6hxM$HY&g=@o#;g0YYE}vDxAK?o>hBt-Zg=NE?y7kRrvG9%Xs_+$AWu*SSoBsU) zS*Ma2VPTJn{{E=mekad{bynEIppURLd}4aLiSyl!BWFtFy7sI7GKE%kp{ z-Z_ozwgz%fZHl{_;bo=FQL@g-%Aor`qb@w^+UNaz?(qYUD_sj+)5QAcTu-Ww9#bE6 zvQk^9lI|15@4&yRg|k-;3%Dw_Lp3=e%N-Q@Cg$7qE_*b~0QUn!Hn z>CUWxye_pe7UQ!lR9DTn59Z*<&9o+FsCr&j1x?0UnSi~*i6I_yan9YzDsM}L(<<)h zYNr3ZTmM;8Us}QXD2WAg71nWX9`hMy;aSzz8P(Vcem%eY@31=UC;lx5{Ect8c1@w4tY2v3@>L~+$HCl+#H%nshu*gQ`| zRVGj*O&4|Mgk88nKPFcUbd7M0(N8{aiZ?;sFbOI%RsNU`m6?%@6rF~KvnYsXo1e|W zM4ye%G#i&`w!AXO#BVOf$vjM<*W|24YKbLW-j+db-V)W`F>`oVHeN3)Z^l&IhWq!q zX!JEM@OSdwFRHRX%w|sG*(LC*%dGao)@E+nN+#iYE_3&`ViW&> zw{u3FomY-8quaerN8QZTO~o@rxBXoBv3I&l#ylWn@`;4zZj=)?Td`YB^0tONEua@0 z=qA?aKbLbRm>oW-FKrSG6;B^F*SH5Wt9*D1?Otgd{(NeT|KJcmL&&$P^%sapBma-3 zy8xG}`q}_K=MsXX0VvWEq9EO+ARPiCN=Pdpf~1H@2nLEEC{ohh-Q6A1U4n!lA|Mhn z^Zm~K{twS);!d2|d+oK}^{&|PU?c2lQ5;PwS$%)dWMOZiaaj4DVpR>vN15nPv1}m= z9UT#S@OVtj*0O zeKD2;GR?z|7EocR%P?#4B%GDfNNtCu_BfRtmpoFKw_m_6&1=qI=Evl;Qu?s7%{#A1sCMgbF^MaB4V>j^9-=>Y+bbJ+B`ZX57h=(7o1@T5|4(brrb%5Kx@+e4S&_O@1_r10u++I7fHS&gcs&tRMBKnhwj6RJKNlddmlC|>} z)OQKncT&c}uNcJbA~ox*(Q^8JJ_cz9D>q3TV~h;9k+E51Vgf5TobDJB{T}NyKyJ*} zMnAfwmu@YeI&Y(Me73;+XbaVQ4V+!~Ce2U@#w;b%w18-MF5i*Ge5aR#kRFoCz@lY~ z>FpcaZ01k$25+)sabk{X*u`9tU3~YOuy-nZEI(9T3#x7_+JMR8vG;?izf`gDrFqe7 z!m}fJW#8F-pPJFIw`NtZ&lj(A^&uDAK5J}&AZ8ms@rL`rw5t4Y&>ySC=%zmAbGZLY z(X2s`*3a#m2$;xx?!m6{mPv8 zzyBZx zc0i!3AhX%zWGwXcL*$Yf+o_UwuGtyv3On#(YGLd1&|%40tv_SsdHrSV$r55EMX+oI zFm8FxP;N1??2t}oF|g-lgg-44FsbV3h`jB)y!R{g$}#e@9dcd@9Zi;}JrvsNL$2DB zgNE4XYO-QVz-BMWqezF1PAvQ69!a^VM*E=d1M5lDToN~ib{ZJnA>Z<%?gKG=)(bMU z!c48Yn6Y;FtNz%r(XzPa>s`4KmvsP|Jts1MU7gikeAXj%b&=phk(!U9iFDOT6m*Ec zjq_L!+UR3!+anSD`*KqL!Mfdnpl^xp-^8Ze@LAV<*Of>OwG%b)V>P1ZJ$FVt@`Ud= z?Dc!ScMHyJm0Z7hJg^Da-68yk(C=)|&Nbq-SAh+S^1@$)2cO1i#>wrvBTjlrZst)s zZ>L!5I`PTH@;hh2yOU&mjbSfF#9m<8%J7z&@POnauo1t=0+}cuauzmrnXYYHoJw&R z6MI=tfCsU(aVeb`on75u$;gXwwQ*MOi*u{#j{Dy2SdZsMvc|27WQf}uNr%fy8FxfI z`EfPkCnDE^BYJA?!*6bOy3f+c$Y8QelEJ!}e4>L?Lx`yi`OFA)J;K(=Mm|pt*PV!Gcts>?48yvo%(2@wXV6uDFk;&~#NhurOvOOMJ#^dGUlwv_w^NSJUj(=7+s4%ch}dLDSe4 z=ywacg)EO|bz6|vSw6N2bG19F+Zli4QXb5xGeW!C5)*O^e(|I-j@R-Iz=!J}m&?bMNchr^jl2d?+% z7;X5c4lMy~@)UcKB|d656kZ-?RfjuSc%&Te(W?K)i0F-@btZVtM6CEEc_Wi}W|LF~ zPGW&3`P@nD)Fj_9+4oI`fTm(Xrpq~=2{p~;qs<``^J&%vqPYur=nHAr1(<_{|6>jo zxSfxKoG0&LE~af3&uBW2ei9Ds7jgQZFfHHljQY~j-FQP`?~kT*(c7{htI2J6U1qVf zDe#p^Fe!3puvU5bkNKf6nT@>4qPmKdqR(FU+bj9qHE6iH*xh$kQZ!LP(MlF=TiUO^ z(SeupF;1p~U)LVr^`UH-*7&_vFh*0@ys>Q7cU-|+EI8{!8Y|<3vfzjQ6UjM+V_qB0 z%VW+ZS2`OWA(Oc53v|Nsbi;Gm4$DJlvi?v70rKn zdZe5@^3pI=F;`!Z*OJd1y@K(6DITG=QMuSpEz4-x*^AUVZ8uIDH;jjH!V?f@>L3eT zkyT_jD@&7=g~~#9!upr2k^A0%S?`B~BtUI8zoj^qOIIZ`9iA3hggwb1wG`(u#yd# z$5u_0E$|cj^_9_$4Qq+Tug%_+mRFUNT}q`_`+Xky1s%C}(h7@o+Zn6k>01c2I~&@X z-`@c5UV}ego_v(VLKGJ9%MUT-g>Lf7S9#T*DroN&mpAtsE=hL=d-0Zi`9MS@kU#$f zwl$dytCVa>Dti5?KutK`xi@^qaI#VdS@xA&MW zy~0Kvh^&>@x!KBYmjki`qraVA*+#Eyk%A^T+H_4FSg)omD}B3QyF_-z&3G>+&Zl0thY#Xb zS71)YS-CIqH0@ZddXR8A8av=~^ih%B|Dri{stHFf!>M^XXFgQn z_HXnR)hc;JFY>@!c}c~q`fk6<^Ueoz=i_?|Eg@uyqK;$_^&*o|6v7f zUf=$zN|1c6s-VjFqP&(8yn-?iU3plgiu}(vz80yCoyal_HhZ7K-c2L_*`YC*KH$S8q?ewshr?)6 zN}S%)qP^*4ab<+UU&Q3*CbM#A;LdC@$e7N5kxait0~cfm%2}&gc5XAdSY3JH1F$1w z@uG|9gstqyaoHl*Aou(H->}9q4V0S=lanv9)Vi%?6V_O-4OVRnJ->sM*o(J1#7drm zh%dqIH}O0Vjl{v{*r8sqd7pSJUMlzoyY#*I@J~7&j)sdS;B=?+z2~x|i(&VbwB|bU zzlqnj&HuajC41<>{q*7i@^L`j#sM6{uY8-m;t4yU>n-%sI&p?&q#qlrwyQGkrvm?@ zf?uSL+phgWETR_#-osjSlSR?lukJv5w#5;)Al=SfGCO~oslc~9>-Xkl8w;Cpu^@AI zEfbOgi zscsd@+bJcjZ6P~EzO&Vtq~GjptF;IRzm!g0DnD2@_D)cQ(UeeQb~Pmi<0h^kJNcp$MhxDGR$5f%)>gqI|BXfUJ~aWTu?RT{V97 zJEC_Vzy)3Xu6|}=h^riHOlJ@Eub>CY_}wM2W`$wxyhcuXArqUF20xI@-y`PaE|312 zZ#;udJ0MSH3yyyUk9ZypI|afV%~SpnZv4)xzQkPg=H2(eFmxl=oq6^h&2$HA(ZSy7 zz^;6ZC+bKdJK=b{KuF#6ALz~k_K08eJ@PRQvEaus1ZVB53$)-RzQOO-^>-HWB8zyQ z77MG5kJ>4}y22fm_tpFzIMRJ#p zR3w9I0@DA0{<}jL-ee!HTcxW$`KmmYYizK-9-dDc+?E+~FP1_4I%^Q{9+U7tpEA%9$O@&h_=HTJQ@h zk<8pi3bK4hZq4cFODbOSiux6l6;O;-E~W2jS(R+$B3j=4@+#b3gT6|O1{Q(9@?a;k zil;myVw6D4!cY{haKc?#`3l$QQNy+#-QM_8@Vf?PJ`gEZ53Os52LTOYI3 z7(#2wp9s&#>j9mABS$gpVl|C+T~5w)sEZsUv%k}Ex5?onzae2e@nNuNMI?|ATQfFnV_3A5&0F0IJsiMp9+Q`@cYJV(h5l1K?;g)JaBfu!S$5CM zsLK@@gQXmY?HC`_a=(FWyOx;Dwqn8`JLkQ#$k_j%6j(TD@7#;lDx{mz%(^$gm%~X# zCkVefw4c+}1+Ma}+_JTzBV)~n%oDuZaqF@Vr?3^num;btR3;pRBO@&Z{w_5xFP$~Y zVn4q^$CdETDtNy7a3X(1L}j5ox|Qa8gTL(Xt^+aIPbv~FLgLri@;mJ1L;ib&9ZP5* zCB-|YG*i#;OPKJPWp(jTeW=#+L(GK_RmvzEjsC&8IMVI~u?f(h-p!0bl(Wshj5`?S(sb^Euh z?4L4?|Iq8;nzJ>psY(pv)qi^aPJC7TY46+PdRMv98IkNhqX5+R8uqOg)c2mf`w<@U zGrrlk#X1ZQrpmcpglk$ap0Qh<#!+W`v3bEY6-|Ff7Ro4D z9Q>zWf6Ryki;V^91Lp=0^;y5`^*1BG$V~YuI34*;Cg&G&wz>za^)s9+#;!`o_f!me zVl~>Rf_e)!(;qR&A@-aitcD@ur$u)*^ABc`dyz3KI~)>|0V8hm25eb`f0YJ)IqHfWpo09yn!*Si)%fxetBP5`L-7LcHu=DzVdlq94e* z_#i&Eye!&E&Snei*u*+F#-KMc8t8x5!1_0|wvFU6H4%wzL1sP>OKWFsJK>%_Ay+-D zZ%_3CJ$VT|cne*vb$hohUAj$HVxzS4<71~vwCzw#axXL*ZVg5 zVyutMs~*v^;(Rs4ff|Ytw-LknL>*H<=YbCLts`;x6WN#<`1^T&(;^bHgjIw`=*mAx zSW+{XlU1p}tAEdK`@|0Wo(`G_-z=em#vUpE(KTJvj@nlZ%{kw33Uk z+@*2OL1McgvlXs-I>!H(cus4AEYo3-+Bfl8$6v}?|C*0Iklr3jZ;!wxOpuo_la5(T z$EUwfPE`QSC(Sq%F<}%{9ld@ zD2>}HPOlZ>HRqu-v(wlap!#R+w&d~F=QnxU$6(RDqFNh8*08d2c`m66IuDDSlW}(z zHaSfS&)D~8MDfEuA7^n0XYrb+MTbwSN!1-EdQhZpH}-xD)^Rn@QvL~Db=%565c1I*knUNE-S&8GbJTT=dBAxJ~C?$BbWMQO~hsr@iYWIXf}+?KLs0PVtMjlpGxw~tLjPZv^!DScd!sOv0mjN(xTXjmv!FA#AbzY z(nPA_9zol;@FG`W-?KP~V>pO|aPK}9`Mc#`?Zz$aC8_(3L;4n+lACo2*KkwR_#yrz zF@F(yWZjOytQ)M`Y~MayM5Qkq)y@^v_kW08^71pEW4jWviML~)`F)>ZvOeXBbmLEl z9iH2XDTNhRjrj?0@gMvOd{uM5zj3^$lU43jI-YVWc~42i6asY;kLa0y>Gr?l^JdFJ zh$YEDQEOZf8o~`gI(x+5wu-o{!vZXWe&(q&ni^lxnu)&8#H-0{9kV)r=|!>G91yN^ zd~gx9ytm^2c!NSdD-RDWCr>P^&(0`H`U1}^J+$=#W+9XB&*oZkW9IW?r{tTus!lL+ zA9`aj=3|`Ccak?WHH0@cj+D-J)vH+89Z#nnl_COma&GS{Syl3qbLX%HHk{R^5eTm&EB#u#>ytCnCec=B*v`=_wAM((ecx_$psYu?6k-OaF zFFmxA#O}TaRbHf1_u1ns;kL=fAUmYHzqYVTYTH>d%B{nn*6N~tjJKl!lF7SG!edRs zdrwL~r(n^awzlb5_e^||T=r%@`?Q!nTaGVL<$u{j_0$u-%SJWE3pKHF?^(0PUe(Y# zzH4pki`&(s1?q}^)RpP+mP+1wvCS;a3K5ZovZdy@l_f6g_HD?fu1wpSA|zE=stP)8 zlya4Y_)V|iy|SrEe~t{MU^n7IJr*s;>uSSm`$2BV5*e*Wbbq_cGfU+(oE*VK)mCGp zRgL<(!!>tm#)rYoXh&7L-JHbKL$>ti!I@|u^-f=42m8q(>8EPLG9edU7u zqNZWFN}WUM8~)PyHYrKSj;$$&+txjqq_9b{F-PIFXUX4P1<7ug!SO3D=s5jxhP<7p zRW87o=VAJD*87w-JSu;BKi#&2E!c>AScz9%%-5P_g~Uks)|GhwPEo?BIe!a-QYG-s*A=^2AtaQ7aeq18%u>de6Dje&^dWa7vKmzZF#kuc)Pox z&VzK?N$9NJG+If+9*_?yAxx_2I0RJem%E-={p8 zzJAXDsCAGC{ZJg#Fjx8$?rEqo*!^!k*U#U2@C!fo+V>%rhCH8Iq^*L_Do!)y_1Rf{ zM_T@63g4W-3O{rWx6R@;miGevbb|jH_W#;N4mSI(YcP*X{f7Ch)(lrNnH3wW8f%2x zpM2Y(cwV-%nePDowf61JX|INU^IMp(>P7{ll*ffxxmO{>oZ?zpy)T2$cut?DG%&(5 zzUNur`K<3xM+%;Y*)x&7Y~(HUjq}(G1^yTJD-AzY5dEy`|2MHk@30S{_xV1{+g|=% zS2Eudo7CU!cQRytvR{6&2PX4V=Xm{6`(Pc^upQRgZ+{&n-KSx#-|V?7a*qBWR z5}tffNbWk8AzD!$dv$TDH^r%H%RhLNPOT;Ht|~^Xf+~TMss-Sa=!+`U)6%t1c>aNC z^EH*r$3+Xa@g0}S-k8j{_)#|L7qVwN(%H>1uJuHjs;WXS2N@LOH_AnZ1%~5TrojWy zoZXWR(#YaUGsAzG?Ba|pRoHPZJNFt@sH!Z-;+sE)Ttdjv#gA57waTtEnNV8e8x7 zY*0&fM7>z*xJI#Taqq|S$8~g;@aM7D#4E`ARIKJQ=-_8Y`l2v%SB-uG?UT?V~r(7}tz{JdW6BPl_n$PRlYRv-c9) zaS_(y0jc|&2DyfTI*)5P9M;_IIy@6|U-9YS97p?EFT2 zrdIBK#JYWA735jVdmF*3&0yh{$J_JyVm!;N)+(0X*|F^WEw_K;75`>E|Dpx{WD&2k zhF4j_%QVIBKI<|c;tE~y2OV*f?{J6D_7E-&;Qpk1fTw~ze1OG#h0vSrD`xqDo-#G~ z3k7vSf{TMVy+Ln83#iJ@?F^2rdPt;!V3I&I59Pu9ftNlZKW4k?gQfECC#lsKtP0^X z`7y0kxz!b6E@S8B)b${x`k#Be`qNf;BX4XPeD*DE&>p_73%}`-XRQZW`$>Gc6{3Rs zg?5gWE z`nr0VB6LQ0qHTIuExC-ZD4yXj{^NN&{ve-nGu^a|rkE+lIS$qjdk+ny#rm_Xy-47v zP(U}{WP=g?L;6xB@JKG z@ITS;ldSYYtGtN^cgRX#a{DhSNE%$>Km0EC_nQovGx((APSHD{iV5Nq=lfaAZ;E*L zV$8$_l}>xb3y3s zZu=;Eah^Q259BIlQ{Qbrw2b@?g{B z{+Yb;-rmtano@pgdaPK^*u=)70OiHzh@Csx~B^mNY43|jY?48|vd)M{7L%f!y5W~rFo zO4Vi6HFpAh&sdST;kwf;);;K8?5nstvEgygL?*`-iOh};%0v16^|K-87* z@H%dyAkH;AJn|eFNhu=ugxsk(I5fhqjM!uPj``K+c!3+(;z>L(u>#SlYW%v!y!wv( zvM>4iqu7u+e8}}Yy#u(O-;7)C|A)6s%u=TiiGM~WUOHKL&*2@?%IbO=#!L>mC1OKV zD~O8B#yu^Er8oJE-7?h<+WkkdLV6+@qj{-=Jr4E3r~cla)He5u`uM~exW)3cMk!jO zFs+f_tDL4|bawwk&$V#f4Y5~mv3@n&s|XR5qiKr!MFm~|E3W@VzcK>^@(fI$TvRL( z*^jVekLj)dqD9pRmRFfyQ!o5?{iYV;(CtK#yYkF>^T-DHEkB99j$^IEGeVcJZtEbw z9oV8@X_R9ahBGka`FNc%i#NF5r8eHOhe8YHMQU>eD^f~^!t$>*?Km4;aNg&3xtnui>EeEj<%;W0;u^FkZQ(cE!MqtC}JaQ^sQXA#edo(kscmAg1PONBNa!YkhItjII^R{iM| zS4x`XpT|*elmj%u%6$QUG=rYX@dPrl{86)i4szQFCrsh>e9Q0ZXk8ofcq&5Xg}pKd ztQvK6lDFY96QMcVrOv&dL+W#T&}&+X{Jq37Wes zX6FquRE>{ON1ofeB%&E9Xbbapv4*|K;cc zXI*Vtx9Q1PR?p}5_kw;y8CbCjiF=zoH5ch<&)@8Ux%`Gk&`pho?9J2r%qzS5q;7o9 zt}sy-Sg5N=ayMG_Q&y)ZkFF1YDLfJ4TfgZCarO}q+(h{=b7Zfr)Y&51b0d>Weyp?HH;?WRQB1Zz0~V>4yHQ zFZ=1%+m~+mLRDse*yL;c)b}E^KS9dl;LllBZ8;kfW(b^w+5Y4`1Ta+^UQKQuRvCU^ zZAh)T-`NH8-JeAsilvwUYtOakSMp%DMqZcyQ`o1zq$6TRal16Kfm6sRd%{TI8v~d; z&fgNs4@x3#m%?6pMovq5a+8gAdW8pAm@Jgx5mcgQYFS0R5r%M-mG2G7wDPZph=~k6 z7Oa)s+|tPg&FTIo@NgrQdG*Vx(;2RiOz$z!x-9GEY`nbZ>d#OzM`LUCHAqU{LQ}d^~O76 ze7%bM^D6HX1jTSu`X~gIbOx$t=e3S533@wqZKMv?%c4))v3Z=BQz0&sZr2rJE#lh7 zK8hO>>lnA(II9a?!br2Ymm{^~YCBE7tL*HNPVsVzR&bK$zb^u&w{7rp&?+eBUmd6K zHVf{FYaMik^*l8{1I5u=>upxi$PwuiBqBrCct3kpJuPuw$2h$azm3&rFRH1^Egih4 z+s0dRIjX~~Z&Ga)a-8 z)#_hhqmJXa_TmuMlgouTv&piCeiC=-Z+CU3w_5VC-zH&|NtCnPNY0E$(ZiU~_59V@F!Lygc>vAR z9T(k(kJ*6bs!k5e@ILeV_1Uoz&q0}Fg&e=nhV+mb@*Tc%w7k$6K5+>dUoU5L2QTh` zy>NntJx{k?k=gvGoS(bcod@hm6nYI*c*ThtBnWP~pGXbH^AKiUUdQXK$6IX6`|L+| zp31j;kkO(Pb1-o0c{2w^=zr%CJ@AecIOi*&esBzfdRX`G!_oY~QQe47=`rz}y!;!{{eICBJ9m`o z_g?0=Ia#PE`z*IgmlQn3zwOCmwCEake3I4tmi6ukbvCe`71;iK^k)XRAi1b$48P#q zJ^n#?9&L3iQrBo;HJV$E)>h*qbJyAGiGyH{a+ADLZfjzUnqic>@`?wrHDlttQ&h$_ zg%wk|=%#0RiDBl#P2Y5a-r9_pn&&!4!^7XW=B}(wb5^IeEVI{D*cOr}oQuxOD7Kr1 z21>0`Ck0v6Qdl7tKV zlH0yBj$iRCwkZe3s3`BWO1!^OglAC%OIw8IFDj#{SbY7}GOyX>wY$9MAV2lE6+LCm zPxD$&@>Ow+Qyp&Yb`m~C zW_iy=RFYRy6Z0P3*->SAU#Cz1AoqEU+ZlS(FOIZ~>fI<8x}z?MJxE`F6(c{$ksfQz zaes~H_D6T%vDT8zh2&~7yD`-M>J6)XXqDfBU`kt|Fshi8C-fKOcSJmC6{(yUk78~^ zjt^nnh~F1SpQ-hQWYZXl@lg-)EPn$>*P`-xqW@sMZW-5|GIWJSxG3Iz-mi4Z6*Qa4 zd=wBxs|0T}rbD{P${GxB%<`RES*;87NfdTX4<{7k$<`CI>*N{-Vf$y=MO$dnvzXxf zbjs5~W|lb<3!R0f&I!#Ihz$&0Q_osQ)n{8){|m@)yv~#BVvhrn$ha6^L@s_@`N;Ee zE!=*oKi`x{;3%`S})=gHM9SdUA9K@`{2#!GfHZb+t-Qvth=^ z?$?rck%#R_36tJoBTmZH+9Jn#fja*2vE;!JT1)&E0@4qFKJG|H+S`%sSnPImc00)M zBO3f;8lV%FsVnXO3BCUrq}Gdv(MLW)e-Wau)Wv)oo2*jsyXX&mz#r9a{j6)zNPUQY zaYDm*=WtAl6^%~kH_p-3Xko0TYV=0YH7b%f#aE(_i0%_jI>?G2Qx|sJYMg@hPVw|k zU?PuL-F>Qiwn6>j$zBUsnTfQ+k5-`{S?&bIHN`Kz3B{EqHF2nR|Ga zKV;yakB*b~Hxjop6tnS-F8Dp&+KA$9i!RtfOKfKgw#bNcE|EN!ocM>*!7Q=Nad?TL zkxzmzWZHFN6UU^fckq4Kln`HcL!S`;!$8s$B%>2D3A@6si2>!wy4dsa=wSp}b4LVZR)tDBAf zc$UUb#D}@Z>R+bOLN0v^NnOk%n8Z)`(aQCsJG%0ETk~QY(HC`eJgUk+sv!PR7ROW? zGA`{mmZkq+7d@#gc3ay{Zz%rrff4pb{f1V8Dj?@NV!U-&tvbAey13G}*oyl2%#e|4 zf{AJspA#V45jWLSzl5-d?NAj+VMgq1>${3Sxs#1JiHW+7m3)kKOeGg8JXfZ$QQ7?l z!EOD?uG`DM@g0uKlG!CAW;Gsa7L70xr|?zeDp|N4G}807lJj6?`(d?{#w9iG>2G@} zmH@NyIM}SC+mhHHWa4ZvT#xK;b%#`c8FbcHyNzxxO=2Gf@92+N3sX`>|GNr0e3g~; zUy=vz7S!}H~Prrek5+eD|JhncVn?b6=$I2|4qw;)> z9(H@;d%@O=-k=BMOh%pA8GRnllq-^eEqU-*#n!rJCynD;{P zB^-pYcuG{{d3;26w*@_};9d3oy}dd3+UqBI-+IrU!(l{7QWm2;E7j7ild##kH$)A} zv(~T6vMh(;d5v~1k4e_4J$O_9yaxKcwt@D0h;t9o_f?;FUg8+YaFT8_(}TP^1k{ry z*UNCCon9F;_!N_aEWv0tVuUl6o^|N%NRG$xzrO_E1kdKjph8!QSj(z za7P297;Bb>A9@=S-piAo!57tS6XqX=yB#iqHHv32hK3ju|L;@(*E0$lPZ@XFisR8S zFveJZ*;pBC<6{HlBlV0$F1Q1En}+|S)V!Z{&bAec>c@Jc(H*HP9GY*vnWbS7Ns65{|3gOuy^FZ zEu>>@k{XYF(w(RZT$bx{bS4Zj$q?Hn-PfaE_*++&P#0;^pN8yYXCI4_foi;urey6C zd*KJ%+6) zZ=CPeo5$HDvMu-^az1FN9=N)mccs)3zZ}UQ_gtiGTw?NY7aMvZ)+p}RSo64bu~u<& zV(-T}TSqjlDYo?8xDL*DY96Z)_in6k+?#qJI!`&SqH{hfusLtwD=Nnp@IQa%r}WU( zqcI+{T+mV6U>2sr>x)^*jWEZ4aimjnbFYXOgc)ISv{?8(B8AI%B@=1r zK`e4lenDHv)jadPp5;qC!y-M!i%G>xOo6XT#wSU_f0e>VA!-$BsMa4Xw zVG_MZKK@oobses~BzAHdH*gq7u*(i#4|goVGV2t=qLmY0sX;~>kdY7L1BTm{UFx6FHrP&Gt@xMOZ=SoOwLo^K(VwW_jIopEM;NV>j`hj^yo29@Z$IzJUDg zpeZhR<{``UG(_VZLptIstW8+=6Ye6t7*u@DFb}Illy_ovPQV&h@Rt9`po$9~&>JaK zW@L|Kl);ouMpO2nlWfQD-JkBcP5yQo8+A{$Mk=|sxpWICi(hyv*cfROEW|O4ms9(F zWR#k}sknv(ILzh28zP}0rpUq0q>HQzo{X&GlP=|l%nkm~zvP0BEXN_*U-eDiCF^E0 zu6iAV z#$p&J@rkCBf|+pfOuXZCC}Ro@KTc-c2s?f-R=Ph9UbkCZcAV>e9IYjK_8LyBklP#{ zKj-=6{vMtW@=r7iEs~!+R-peH%B1Q_hlM?;7U|e>L@nq;8CF@vx_%; zCk{47vm37k8=~(T?E@VGF~b9c9lDP0i;l$vOb9MUCk1jlgGBo1ykOM|>!VZK%5}gR z48|rbz$PAHXCCqrGV7mIQAS)l*>FQ3kR_1kQB3ze6`#+-BKd+#kt*V1O@p;6IA-Xd z`6GX$7fjNIUa3cRoGT=nzBtI}ehSaUViSVftk%`o*LcouWTrW;v!;AY=j8EWcc?dT z77&R(Bo}Ih+i^yJqYYGD({O5y==27wwNAFruOSEpI&sc?OmAvA>~OyVC; zf?y|zoQEFY7%&5?^8?rrV?Ts@RrdRIveQm&z>Kv0(cT^F_j&e<~>vC%!ycmG2%Es<9G+M(0Zug zWoDV>H6rt&2EOQ5-Iwv6LOx7398}nIr@ z@3%<}_*PutPMqLAa&TB~-3dP21>W^lmhmqf&A+&x@Z|TfZYJyn^fZR#d3kA>WGlSr zFS%rs<`yY-4z+QDE|bNjH(hnfS*WXgZ@tBoQ24lRSKULmHoQUUbzVAg6aU`DK{9cySI2E6A8DwFMAaw)%r4Om< z%F}I4HX4z*S|sju_luCDmq}x0vXTa(O^zW+$TyCvGQKa5>W*0VU#bzW({|VVqTlJ$ zi}EMWWAD#k1V!hp%R1{a)hrCOCS9$^dlC_mm>aS>D4B)5ap%Yu% zPK4MGQjeb7s8 z@}A754w#2t)?}~@r?Fy{vt$b|lP9@R#>@_5pK;Ku4tf5Nzsq?M+w6{i`rK~sLvMVE zuNWXwG1#XL!(#k`v6_Urnq|)~w%?u4YP|w`{7(_m6Y}G?>GQBO#{a;Cjt~|39!k~s zAJ>pD_)KJ^2MJWm#CAT!qK46vn_|?LA>Fg~$YFbBr+S=qH1Y!TF`4B4>@&Y&7e3MJ z?E|b@eHy-^%-|wyLr!v$#&;(sBM))gy zta-m_HE&=wuR-}&?5IoD@dDg-R-6)NiV3c2H@{(rmGfz(>@c|_WNHnau!0A$RK$8Q z9kGzFw}391FJe8PKAMj$SU@K&g!Y`G7+pj+slDf~MvcIG5*sP}EuE3g>+*ZgYjjv0 zGTVym^q@P$qsjC{wq>lHJsJ`oPCpKVWQUOd!F;BHV!H#`fPrFq-}9x0$a46ZZv91; z!(>eQJo{%A9&`sJa2$_!g=hHy8~G$pQAb-$lijkDR1*%#G#JH5F}csY32Uqm%KO$NfG3 zy_&wCy~f#lIEp!#f%&+Jg}8|&#tNL*8qaNrPE|cIL2lkJbjfg8o;MORIKJk(sITwzaP#rbe|`1c9`yz@9;I-1oAMkkNtQ719Xh}UdNb0dP)scnfZ0F zQp9?jO3i~9%yU-yhN_zfq#|)pl0T6r$fgHuK3(m~1aa7@e|+LKoy1Rjb(nj%O{{y3 zxc8z+f4O)4gI|2hP(0|@&UNV#=^T8dXIWF9TK9imOL1OH9`!$2Sf(_*mSnt^Ks@z< zYQ(=~Zd}7V{6_vyLdb{gk6n;+*b`>CoWrnZ<9HtUPiEsQ_^rF$-iEZiORHDqhn8gT zU!fN=dX$O|NPs7}A5F)aJV}zG(chwXqUWMFqGzI)qG#Q^5Ir5e96cGmE+g)C^tdyA zPDEu%$ap)&13VMW2AAZ;Boz~xdd;WE_Kw!28yb;;mNFPV#3XdVMD?U+`up~QbkNUO zgt4w@8d;d{6)WN~QBPOe--><@4g5rh{X(}*q9ta+vLNr%v$lg-Wwb%uMOv-8&bHtU-}7l^0LVCEpT6H%TutLY;p?ZnqG0&lSS6@$WL zptN0H(epL^y}s8q_4+nsq?7ItpV1Kg=v7N)vr6ph90$!d*r%o z`k(JGmo)2V#x7o!Z*YsZ^AElFh<=RWB;!;IB!rn0iPa{8J`&I*5qS9#%oWxh+%#jq z!(L~MBOdDx0X4nIKIatSf0=E{BN~~Pp3R4u%Ex}?l~MLGESEz(Dyz6z*nR35F_L65 zY4na~m+G-JwaHZt`>m?oR|#GT`Nr~gXn7j_bu#8WDqMmS4f%R+SnG&`1_1BJc=uU3kjV>^!XAoscEjef&It3Hmk z{1yW1!uxM1*83Wyo`aX4%&OjkypP(0Ypm!Lt1t*g=x(iA8Ff7l^A?L*l{{7@yPUVo zA|e^BNJguX#Y$wiqhH276rg2F;{Phxw>8-Lx9IZ*Fi8`8xS7%1{idF4Y-Q@>m1=uk zRj+^Dij}|@7KB?~@i{rIfoN$VauXQy{=BNLlQ@_Kk_ zO+3De<3~SjN9Hh!8`V5+V#jpmdweB>V-#&WhwfRc#&;hSa@JgjRW|p@NgO1QjOKot zT}c;IQ>)jItr_6aUirZSc(cQAq7nS+5^5h(1%ObOodN7hUuZ z{q)dn)Z<7{idB)7um5-?cr|#8`Fub>-NA?6Pz(6GlVHxs%Q*ts>?KKC~7C|*+7`VowE10FrYAK%X}*$9&^7K@(7f{vsU2GSY5 z%}6KvzZLf29oS7)vna$)>$2J!&4U&vvn3;Gd?#mF*M~fld(ocIKzE~?`&~TO8G7t2 z!?g>qqYJdyl}vTRHFhThJs{GaWT=3arPAMG7!Jq8wG1haMHDBnu)iMOprW7eProY@-6`^fJ7n9k_TtLduB;}bjg6MOv=uj;1qt1A|$i%;pOLZv+> z<3r!@fp2Po4{7Y1o!#hjpOA->$m%6hRh$^J^#m{JNqkWX*ZY*;mWHNJ4-wD+@Xuy; zay6!I5uG-h98a~Hla7x!s}Er18j>pEX@?b&I}%;M!QxW06Jy=PooYCKp*dstUqV=GuTgA*!CQdOKz+4D*vS*n_di#D-E%fBY_oURaB%!DwDdZ z*0?GyUe)?mvBF_Z`5UB9*9Ue!13ojGx{;UJ`a*1dS$3(4ztr=p7NnvRWYCvv59LWt z;#n>x6I*%NM|qxC$izLCKS3}>_r#Ghu?9L7qn8Z54n`AMShcX9Wkft)fn3sKvy+O? zJ&bOVaW_|P!Z7uDJ!G6T`Co0G^Kzr%`BwLE59jy=yVT^Z!2i#L2}eVdKj_8qwWwlW zKH}%%qo1+yJ=ynOe8ey8_-`PCA6SGDEXG7uUtE+Q+RYyB#bSr&i4U@~hZ(=PKgn}5 zNZ~wac8Pl{*!VSc{RW@2#XEM;D0@UiezMsMOm?!j(Ry7mb?MoN5!I$sw1JF7U=aGK8ejTX6z2ho=p#vDfDOmr)o)T^&S4VUPha~RDN zUUiUnyW2T|n|Thad7g`mneLCLtA56A4&(uUp(dcaT+WZ64rjCYrn&sk(In~{SJB1Q zya!iSqH|v5p{9d>^i_kjGy82XuoY>|*;6oNQt}_z7x(Gto96sC_Wmd=umdJo1^dlm z@AZDN+9mAdFtegCJWY$m=um!4HKXkK%A&*?5@= zJBUvAm*pZ2JM8vjJe(_5{w@iK&>+dwO{byHoa!2{$6lew^0R1R1yTtsTgH{kTB7$3 ziEf;-()d7Duo?T%7WV5*|M#*B2iSo>S*dY$-wdm@*bJ;;Ik)oA_t=Sttlmk8|17P2 zQT^W)EW~vj^q=g(U-s`UyYiNmyGj3F$3tDV*5}RcaqF-j9^7ihRE*DsG79kMdiGSJ~dYxSqUrXIl7q3nWg@wmB7 zQ;d0XdY98Z>qzf5zhDn!|Et)^VedcUbB@yXN1&O5zGJ`ZR>8o3c$RcIgIHDSOcFYt z%?NwJe#cMkya871hOygZ65o(<1`N$M3*$nZ8F;tD4Bq^sM9 z&D{t!E_FM@<6p$z26;^%nz6HA(~<_Q&yrTQ_e#(XdH7kG?ayau<|LS%XjFFv+|f-u z^wnr{9QK=fL^+2dT0mYyPP~Hr)#&rMg|wbeE2jJ$e!*EBxQ6ShBmRsQbUI9F9Cj64 z!`mt)T0os0Rhah1iG2@={(`%l!PYG1MXcpPY-MkDlc@7N!evz%rr^-R66vRxHH5XNA|GMO= zf%%b%2}z&Bf@AH>T?^Qu?QC>=Ue><2?8&ePfNCRuEebCdf(Hx8qQ;0? ztKszSc=u;|ZjryO^qNgxyMwgt_s%0U^(naiH|us)#Op8G=AJeCkH$$DbXO1BN8aT( zk+h=VFW@q>29t40^VEAS$8v0PPQz}uM?4lo3v>gLSCK*HRXSeAQ@o8Qc^!$2D4ctr z?YQILU%u%&jC)0P=Ow=81@`AGpZ7Fudy*wNhLt$tIuFv6`^ozrQnQm>Z^fZ+fb&+1 z2`*E^F<%UMM&vC!|vG`Pg|c zf5^r?7Aq|xRxY|ERv|h?1*elCqXSfMelEMJv#hEQW%0Ih8bdQIXk+N45ov137ilD8 zA!Hbu(>?FgYHi_ycKpT;v`r`4qBGqVqROr!Q?k&F@YLVuja;rgjB>pO+16lV-i0Ju z@r*jMHNE7z41_A2%&9Z{H+HHxA7psc?PcSh`-uZ*gvc7o<~E;E(s;vT9l)GP`h#9# z!{o{7MFyKSQ8^_u!PqP7=)5z4?y8(fih<6eLQmI-;7$JG2mHrgR_;gEVLEHEibdGX z2R&t-uCgX~*qi^LfjHh};$T01d#AC9T&$6wxs04Ff*ab6zQV5g6Je2w z5Wqw}$^>ls1kyF0rWi{SN7IQT=*OWD>vyzHKUU;Z`0^vjry1=Oc8sV1`4(YwbMhe5 z@;4HTh}>iQukh!N8rzL!9#3U!hVkI~`9I7jdXJx1oi|t9YUJSWJ;jkTXZVi<~=)f zG2DqE-y>qWk5<6}MGx^wkI)#$a1O^=t&=o_UZA2TO~gIh!!e&@2EL1rS6pQsud_Zk z>9AY$#a;U2K9=F3cSgM{0@KIAdI>N|2}JJ`(D9+?8RLWg=l9?D$~zw2jDJN^oKs3x zIP4UX9y^zrHqTCX$aV_X}X|@OWDbVVlf7t&Hs;_ha^$qq_&oF{Wy<``$3IR| z*0bf)cn5?1-1pIT*C+>k-w!88TK4~Srve>iEjP@NjeAj)iNC-Kn z;w`498ME<5Uba{AVeSjLE#h$z`=<~OCBK}xSNP%C?X663Od4|&_AHV)$+li3Teo=Q zkFgy|NkzzJz9>GB&uvMME12n;WTBqfZ(!Fpfk&F_Orie|*6|giq)`q2Z{)cSn4+)) z+)og|bkD3JA$uW$^LUNF>4CVQF3V7d&Z&dPs;yIHZJh>c>rhyWeyXio;hQ30wRI*7 zqZw~`{Ejp18j6)P!D+RM6qS>eTVIm&K~MVXOY$+86&dL}CgC{e&@W3#-C9?nY7@$~ zFUal@I7j?W#^BGSsI&VbnzG&w%alJLV{UKcfa;s2PH7vhQm3EVu#cj%os2Un{`}#h z(SecN(LRw((aw>yx&u7Lwxo(ycgAEH=SvohJQvOBe8~)vyfW-cMN>qoMW2W?mUa1Y zG@{Zc!X`x`3!(w9H=+89Ff%iS?|oW*PC6An8RflXQ$3S|m3WB-fU*pypYRQHS*h$Y zcl2tuav?+WxA%m7cN6p1Q`4g_*p<0pgF^OjIs8+N_*J?EvH!taycDlrf3VJD?Ejfi z-!csTCVu>0pU!g-HJHK1Ow!qQg6Q*j6^G+xFN+O({tguRH_UsJ?|%cZyDL^E2U ztv%S8U+|fG{n+*}()1&rVw8I8@odR7v98&W=K?5lxeA@t5T_b)w&4io;xKz~Bzjb2 z>y(J*1&A}OtGWqg{)54IOur6<{`esA^Th{C@CpU-U`KAkZml+X0o`>wXQUl z7-1jF8J?TuZz12w zCstKPT}4r`*Mhma&rJ53Q8Mp-AVXh~mCr@Vx{~FObggPd-kYjjZ79Z9A955+HuGQ0 zGU`H>nmUJ|no-zp%q06PiOi!r{MEChU?(q9*Ch746p1KKe;1?Ai|}rXkeuRVr36VT zgKr4qR8_nqjDNh#V{PWqhrER@yoJyCpI<|cKX_)OXC{)h*`8VInGJNl6JuDbd06Ka zJcW($z-|^kjJutqy{}>({>C^wkRubJSHpb7WM)mLHa594Hn)rU>`dQvv@1U{d#$l8 zE$HV)X7g>)om!ZfD)AmeDV|$t_sY;3Wq1jtWf7FXKNR7s=J(E*WUgg3(z>6_b200E z+gkr_cGNXl$2a0RuIaqBk-U@da8$kU1f9uO8y2}azq7GuY6E#o4as36vf9+Fzi)IP zo87UB{rRLrY`Bk75XM`stes!c4{jVz=gj0KuVjUG zc;#U_?i@??yLVp0QrsZL*Gc>}Lx&1~J519bZ$aS&>9v9h#y zu`I0AD+G-Z$2N zpilhKii{u?V@cR#YcbQ_oNp|~5G*%Vxxa=4uJ*T8Ubo!)oDYHzEDQ&f<_lC}n`^@b z4aGHEi7S1CMQ}neUaPBb(p}g!4Djp#>mATY76?Yy=*{qVN;R)#Z6_^g+G z<_kVM8UN!y>v|(TX1S0CoX={`!FJ5BVv|VdXqss#KkJ+L4AS4WYrXTX_!{^IP9O3L_!6hHh966^6CHWCgqMmmIH0kXvGQKQ)<7PYPZn1+QRi z3R>S1&{sc1DSkdO+#zXFZ@22@fBzg30*UY+i!g=4Hkf4t3O zd6x|k-=cB%(H*;R;bD&ZCcO9h_?%u{JTa(Yci^>h9XHS`Td_pp*;hv({c~>r@Z5cW zOXOOgfjG0#b@~0KvaYWNjq$Gk-}n6;U11Nu{|hqs4LKj^DhHXDL1cI!jromte-W<( z68W_;SmBq|@|5u*FY5wF@sQw?m zVuXE2W;drZKQEe{JZ7t?eOJajyusd973HlVv!EulTobokgGXN#LsJo6e@*U6DRIjp zJpTOrxZE&X4kN4QGQx1_#WkPDPp8M`JkQ@t5A$V$#j}bUD{Sx+iafQ0vdb?1iLJLoN)h)=Pvr& zAMns!x$#lEJ1LzJolvKR09_F5#i9KDjyw`5{-lCwRJ?6N<2&&RP7+hCuiFwYctC&W5m zvJu_L_6OoT?~tadWV@8xydGzU?Ve&U6R;isL@TKXF0C4?pmQTt7Dee%wqiP;VNCP^ zPU0pW;&*-OPscvRFLc2-bYv4g#z%C-O>~Q{Q4zRG<=+agT^@gm!osL5757g>u?w(i z63k^5dZGjk7RFK9vld^oBja!tVaKZdw8v#K@(72Y((lV=M-?*r7062+bJPsR|Hy53 zqrbf|$nPKFH50_YXV}pTFcT{vx%K8}tFgy8;JIT~@3hamfHk;m_g}XH zFJYk9WROw>;wfjHq~!-bk2%c5r+ZP?xm@xZ@~G6y@3tszptR@9;{~eV1#0O+T@NqN z5HHY_{I$d_v?h0L$=!$Kt_{rCS{$mCEL>-s(Nh~pzp-#Pt#p3KH#5`{*5myL!~J1| z)$m)O!-WvsIQn5=JkK!0?w+n+hV2U&J#h+NF$M~oxt^0FO zW==b+ApKF6o~&kA0k$ET*-lDlCMF-@Nh%S$H;T`D#LM^>nhVcyzs2kL)9%z)SbSuR z9UE4$jH1Cu*l)v(p~m+f53twz+lRgF#~#+BD>>>&&f1Zmwm!GDYkA*yy)T~jzVB{r zAGhKEwu90-xb5t5H`n!BlmBP2Rq<#y_~WpUCe>pEJgoNRFo(Gu@wK%r_SL z`=a>4Pxtz{-Z#tdn(A}Lli87e>k!{Nz<2d#-MX?HZRykZ%urqOR0$3#3H#(RG8<2M zoPfT$2T_EzoX7e4>MGf^Cso!Y#@YljBp$OE4=}`cVToJTChT;3MHKS_t8mKRJ)(YY zAKS2<&$&UQW2LCZB3Zg~WYSK4ovyLO z=^AU}WoatzavpuMLIud?=w+XE6R)kaFKqfB-ID_U6IPKHim$H90JCRsov>>O=j1bW@^0>bBtK)jBTgwXMwxsJW2va(z z$=Xe`dh^KI3cqqA8Q#eT9AFQQ@s3a9Q_u2<&p{?M{wFz9)6Y^M+@rA^;Md-r5)nO&sZnA#D2$|)-$oxaLl2lpMB;+L_#1QezACjhf zJjuUZeR%5jAAZSIR?!*qMq~P-B^}Tf4*S@=eL@d>&R+Mk9^ab7A@=4-pEbc6&G5Mk z$k0mPvC-b#$v^y+?5NZvQ|ZZ926C1Op2=clC41S(VAlAU`%%vwa7BB)ZilPeY~9ye zpVdBl1^uwh_b#zQi|MV!e$^t($RZYhpwWo0AIU$GgDq;68^(E+d=~VJ zivAy0e*tb))x81V=cp(m-Jx`Ybc52Zba#VviKHmqT~g95Eg&d}h$1P{AR$VLfJhj? z;rq?~{J;0Q-j{O?_TFo+wf35Gj&YBBj9JFL6xa4)pTW{F-ZyBtgvV_ANH9uay0B8T3P*vJ@2b%%P<H$e+53yo_7cu^8KPaLp;wgV3LbaGJub?q zT%dzz>EKCEeGKOhR=AH9?!wx(ibTSCfXn!!1?+g1RV1fc%WtB5(KuXjjMp);9b@U` z1iW#www}&P=eTa6>zB!L14bW4kb**cGTfv4U@ ztGkz3K^$5a-%}@|d5I%e9COtVKJQbSFO_y18{DY<*0^pNz4(-le8QK973e1FB^!g` z4Wm;-=vIH@ocf3yyW=07agp}MLA0h_&56Am%G~}+ zB;fxtGM|pkpg9wKKhn8FT{l1k*e8PbZ85`6_(E5|*OMpc%M|VNzZgRRGe56`PAf#A2|n7<4Rx2Nj~5dFg3Rl8HX1iL|y=XtjQfw){(*-q5yI z(38)NM|+a9vkWvU^yCzRI;Ci3MZWnR8d;lNHq-`9<>8vkA1k5P4}9(iRr|_g^^s%l z#iDw!-R?B8i=0uw@s6~wy*Qw){8DRNqb1~NCfCqRj=RfXNP)m=k~(VYKpLpqSoK6i7xC!7d9knbB?@`3XQlEGxC&mTKJ+&QD* z`B=Cv|4*{w1UdB(y@?0rCx!61qGHiv@>#`jhT@T2(oFAXIX*{RJQqIAlOLP!`~_NY zftH*f8MmKV{wtkpQoCPLxdS@S{|w-ddh%%YkF(oBnV){qs)TkZfqF^!-b89SA-hZn`4V}8BqYqcPeamc|17@C z4POeuno=;OBJ8QfrklD~NB8Utmp-P6lOWSvI=LJd*vKE*tDKLz;fZc|q8l{lhBDmL zlXwe4+!hnv(bD&LtOvCCA8j7v?V&) zSLw?ubSReJcv1c{3i3SDPXCE_AG3-F5b96f><+O8C_ht@oxTl)%V^IEY_&2wtqzTA z%0kqIYz_I0CKyjknd>m>y#v4To;L0R{km!2uKYk3y(1l2XvmN^)w5RztE(&zDI-NM zhUxhmsinq2(#dLWrs!;bB#zt&lNZ41v1u@Cg0643Y0a-KTWnm1z4v%O9kS&Tg%joJJz$j>;I3Jl=<$dkYxB zRLB^FBIack*WdV-e#f%rXH_r`zp8uGkk6_spVdhJWOMgzZ5(EMV_ZAyx9p}bs;5zw zeQ?Mygb$E_i zqL-Rb#$GxRKT;iqyBBdhzar+_3F1Wo}8~$(3RSwKDoAKjW zVSQHP;Irat*~JLC^oYv*i`BC8IB9UR`0n>2&HXo~PSm}a#`-5(TM4X_e&}xUPkr=N zd_V?~!9I&vyk3eK=$sF%1kl?mGF|nLx3_j=GkL2zR+*_}W%1$$jO29R)K)+?i-3hE z(7s7<*OV|ntvq!WE&rxGb{N6=HXjl853Wm#TF|EVpx65}>thnu7MknW3g2zvVfOMY zKVrF;WGU_%(;34y;u$ZOR=y=K-dq~%sex0qz{9)B8V?j}jnZQ|72eL1AzsQx*YKyC zXw`PL_$3DNHC#3ZT`YH3p6b4M?qOuK<2lUa694=wUv!hN{)6`Y1w;PkZ=TYzsOT*6 zRk_WcEMT6Dm2v4z{OEFK`5IFnjj={o^SPSi)#S9Q(x*yBN|l$ND&yKx@?AxYPsnEs zbxyg|jP7sLjr(8XH%@8ogIasHXW9g7mcp0mtYJ8g*qgU+jWgB|6_$c2IeGn*;)PfE zkH_lv8lHB_h}Cb|;#U44?9@F&elLu=?akA+h9f2AcR@7%P@7TSplJR$CttnZ`N>kW+Og^j3<%&v-XJmmhMX=t={7oaz z(TR^TnzZZCsm!U%( zd@?>yofu9eW4S5Wn2~m{Vg#At{Zja`k#+5YDf{uUBd$5&+B1B>Iez=Rwm%1T&d`Gs ze8Ew#2gqK>LLcL1--r1;OTC}(*sMt2__6ig@5@`=66am@{sP_mNq?ewMA~erm~O4u z;d9Z^ZZY5Y;({Obh@Vs9E5`d>SNgD4@_kX&BbnlV^p^goxAckH`A?tpzxtpbiN@}U z%5I9zF6);%LB8{Tn`28wX_KATpO6* z();#~b>Yi;($)7p{Qzwob{ZbaAAH284Wln3_^45Q+8FmAN2exNDVJi@)H-RR3qW@My;6={rw zP07dL|8#jh-?*5}^nMJzALMvs1HXf#d@m<4tkNV?6ssqgN8b(Kp~~4)|J_&6XBb+b@j9A6DY9qR=Th zn{%E%>jNZI#YA z+06PHtlf?09;8pl=-CN6cZTkr7du`cKYRbPW5zL&e2(SvU1k`SmQJN0@yW~bTF;Dm zcxaUV@5VIz!k?XazE7XUm;A+M7O+a07T}*#@z3FMP4CMlcHle1ZoQRBQSW6rA@Qdy zbUEu>#|pQ?*xkIx0so)iT`sG`Kkzs?H{7xpdu{{6>)?*%*?K{Gk_A4bfDNx=wa@s4 zzhc_r7440~Hc!OsPd*>vGY;|>2V$C#2Hw|p>|NvR>?^`oT;eOP#}vjb^5PUZU{5A` z^oIMUfFFrrM_l==*bwg}X!l&3KZOrhp@+b2hxq-2=liG zI6g?*4q-_{oI8;8b6rmn&3mL3v8$QuUxo~^weYK~E{@iFjiu`E^8Q7~Lk#pETnr<5 z|M5IeJnM7y9ZPR`d>OQq^f9yeFuyh`4e4q?L4ERcup^$+jGonzU#zANt}MS)LYo)n zr}7yA_9mZ|)40CuUbE?w%Rwh{@)vpZYvtG9QB=Q689u8jy{+$lZRMnUv&Lb5Yr5RS za)=v79UbI9epY{Xc#kkQGY$`y(vxI%WLB0H8E$ts zdcBv3rw>i*i?Q{^*!o~>Ju$YhURNj4a2wXvgvQopaaBafZ;7G`(BiD}9I04dJeC(N z;(Dy@?r6ndJ=f2)@wA#d?m3Uqj3YGU2=qASisQV_DVh^TNM81O&F98B zVdUm3u6xb%B=VYqq;-$1e%T5myiPXv&lJJ+n(oE}i%#Of`&Z~_H2q^2u&6VS_a2Vh zQS92*IL#LDfj5%*xu}*e(x;11{}QfnMHcC*y17p0!YK2*wDA!Od4j=P0gQ#5@y$`S z`<)v73Sw;4_G|fz#o`ik=dhXjG_$3??yydJZ?VT9BkG1@F7_$LlFC4#QnKR3Wyp)k zq8B$Nu_Vnbjm20O)#$n-M&2AXXZ9#Qc$ohDfEj%&oBAbRv{mH121Al|o=*X>G^&AP_5=J0g$<$D(!SGpX&u2ISj;*Bky zQvbW?Y%xEw*pq&0eZBc=ZjL9OPP-@4?hv1RL_Y8=J!tuR_+ltYQ+nG_ZP)djVJ25i zZB$bW)YL|`m9DOKYQXn2)}GBtOVU<}gR~>(_axwD?GypI%|q@{SX5uQ;Cx2Wv? zrJ-99eII$T;%vqnrlU{x8kcDsrPV`8meYdIm1aM^I7QE|Db+*T{$iwu>MqZAS6%#x zN#6H_57gr$?fx%~eyTtFxxQ-qm9pB+tampnKEN0M1W_*FUstiKo4DB>zWz@<^TkE}g}vtUIlJTOeQyaJgs3IVBb+WZ~0``PC}?djmOxwxpXF zqaS@3f=3P$H;lpy$I_Sa_@?#RNK-Z6g|{2%yK!)3&hrno;{ev%hY#t>y4tg%W^mwt z+16Y1;JzCBPY&b-=ozp+h4#v*KJ&oFFq6fI8~L|jCl_Jmg~&Lc5B0u|#_=Y@#aX_a z=bTSvqL$)V%W=CEymlC0xGKWu(t4l6`+{Cei47`izuLwMG?AZdOFz2kpYDyL_opXA z@V$?<^9Z@hQFJEANE|+_%JPvs=3wprftaF~`*g+^+lp74%Ud_lCtVZgs74nmxN{ki zMJbU>aV)V2Z&8Q`DZqo}ll{s|)83?6IY>5=iMFNlmqxjhVVv=Kk5_n-DE#IhICl?v z-%t;iL`J7z%wafTY!FY0jF}U)DQ3A`#OA1FF<;3?9Ee&NbKGtT=j9o$8XI*ZYD3H&xrRTj`1?Tq z;sK4lPfPDvVZhFnMjWLy+BX~DQUnvKz>m~^zEa=$C;BPTQtnT!w62ZAy|(ZjoA{CSe*ZJSwSm9c z$U3&d`!AGgH}AQR_x?fK9#Ps8`X$e3*Yns}*dgr#-*;Y(oQ9UiF}y>1kG?fOahG1B z&Ajp|y+sSKys3KmM&Y1?pVP?bz7iQ_CcI^F_K6cvaA%FFryp;@m zOw*`o^lq;EER8Wn1?ugLDIhkE0=OcZie~K(`M`rNO zfV`{ux!vRhu@|O& zyXdYBk8bTeS;4>fl$e+h*(KC76|#{x)#?d7SbKPlH8f$CK9AvW#2Tdfldr>!XU5AW z6|q>M1g>XMqv=_kb))$FL2?iyw9hymZ4$qsq|iG*mSqho@#zSoxj&Ri`+!#V5Sw(M z%~t<}a1-P;We=b$MU&TQ7oer9#~d%Q<3dgVVm{>gQs_3ZJ4zYOG_ov;%P+~ zxwU#&8>6z=v7Xj!1@F7^ApP|s4Ch5Ah%aZ*q50y@rP^_o*R|r#_0HHR$GJi5xgPgl z>)Mr~lqK#mpBBub1yg9jSe{}S?=XlT=*J)Q^sDdj5$&**mV9Gl5=O37=PfGGlu~?3 z@GZf+WZ-R5h=;>?zi7|=H&6RJo4?F++d&e>41p~p;L7+&u3;J9`6-m0%YSN3**SZ= z;J;Jw-zi#V8tj-M?wl<$oX>MFrgy7x(~bP{7wYd@G2BsBcV0|<9ea47?w@OmxU@5s zv$MKOLD*eJrt@86(i-cd?7*+|mWvo7mo$b>OqYLJfLE-POW25C?ufb-^EDRp9X4~A zHvY)hobYOmtC*8^X8Xzb+oN*f2WaSC*X+Pe*UNh@k!PAta?5wU|I0DiER1IX zzBoHFE-DI-d;#Nl0sDx?E~4pC6x4Vo-uO>R9<%NHFyIezom}=g`$Sn^ zM%s0v{Pi5Tu#Df|r2W3)BM#`ZJjt3a$_QQO5AMiY|AnbNmQVOci~NJRJ(gd1AOgIn zC;2A%g%>{O^D%q9$dg(Zt0w+WB3aqlt`BJ1nHTn4w<;&vQeEqOiR@3x8L8H}u+C9{4@% zvKNaVfIkk^_9M0ZIEXS)j$kr$u^XM9@cKqX)G~6R8V*_kl7w+Cd^gY3Py_Z$&!jQMJmNlY=PnCQ+1RSXr!s_vzptU?WpB*whB_V2Q$XU*r z75Mr}@VzoyszN`j`ju)htvY`Ycyvv8SDV+VCxg~VPNA7Tpw@U+JMtds%IEfoWXvY8 zzv+DKJhF^z@Oc-!{K5C9;qEmy`p`9D9CVm@olcH246s^M&re|fVj0X;@=P0`z*bMYL%DZCnO!miyIA}VanKwvuV%j9749kDX%Dt{K`=% z5q1s_>(@TkhyJpsPU-4gFuAncOk0QOdpJ~HIT49Fv8Uqan%Q^H^3mx=6HBlqA zX-pMy=-ce3C|k+LR>I!M*}Tu9r82YmEPP@%v1e|%>3sM>QE^3SwOBRdq+wyV_x{?12u+#dd6Cw=@JGHq0QA%D5TuP&mypWuYk)Zzp>JxX?O zD18{99s6K3-PCF)-nSjE+L~UpU<*y@MpK!ArfNG#nA_Tn9)#EC5edh_cc02ZE!2ND zS3Y!(r=3A2k+Jff!|~D~&g-W?r3ZVmMvaW#NBG4gXtzMg*Fn25QuMei&2KW<|B5W) z;qO+9ggpO6d_r6PyB{Ao7ADWv?$*_lHT^~G_N&^RNK{D5ullDk+3Ll*EXlX;kry!PgG+Ittq zOTL3omf=|nUW$aJ`D$T-BIE75-QZ<(sQtnnJ(c9CA3(mqG!5f5Pc`{ayvV@kV> zUfzK{ZPPovRgd2mxuwmbz|BT9ZH}x5AKU9oB#QiR&G=1bDTKA-!{<$oZ!%kMqukVb zqfb6Ft8R_eWmi~-eyI_0VYRr~#+OXh+dNMH;4m075Hj^)S6y)V_MW5_&1p`9n?Rk$ z>boiL+l)505^uDp7hSN|KH78;Y#br#o}`xNh{TtOp4aKk-_FwZVmXII=w|-&0>@b) zPN_wnxaK9iJYK|?441Fz&C2XEEbc1-r7J+_ci3Znabk04w--Zplbh;?3w(%+jdpi? zBGbTSG;E%Bo(ZX^@LUtc^5bPX#%b&E+Io_CG7vdU2?Bl zGw8nk^rFqPN)VZAl{q@BwNlcY`pU*c)zkmn${3c;V$i#B8VVyQBG(LGZArCvWSi8}M>f*;xtw zM7d=a(()S#*jW@i!%6vymb^j>mfMmxw9<;L_=&bgl(sj*tdmyjV$@Ji-}V(d_7^1% zF>dH%8OM<_&SUA(L^(-gVfmffG_4V7MLPT3*YTmgpWwVXBF+`=XjcjN+e@U^MTVj+ z|Iq|rs|#(bv$^t^OiAp#Aj~jl6C%|TZ#Kd2+KCIg$!nScqnEAce5w+PgutlNAI$L7!seiOOH1flGCfo1GdD7!al5F z4Um;^;%oSDo`pWZCE~z_4AH5qsglY1D{tCiv#8w&N=NmKu4FCycdag8*RE&{y>A-T zJ-S6yTX~B5(XFj{-r73nta#3M?ZP?|z7Ee<}8tZ^clqxX)v^;uspuDkFDLKWyqey z%n(bM^%gVRug{YGn5BPXhFD>`nA0jnF|y1t6UlgD9Y_5gzr>6dZ;bNPBb+;&3?oCy zhs3J%F#`y+kLe>m>4_zGH3rX$dg74Sm}D}3EQ@%!sG6&;W?J(j12EC)JZxB1?jU;$ z=m|bgw^PM@Yx69zHqUBn`Fv*1=0-ZT#WyHV)7&W6 z^_g)oD~)?tBnLiQj(h^-7)noM1YqKHm^cn54ug;b=t?h4{5?5`)|h$|I#OGnrD~*9 zL$yR#*?`7S#7gj5CcS>{R7M>o(IW9hcCqEDUJA=u@exn>i^pF7;w%2(OK$KL=2h@a z$sqshqOnX6AQy&O5b~5{Q57P(u@T0shBZsz$0tT6O@$w0AjnX9(vRkJV}b42U~~Mo z0TxmVc2sApmH3eIcxO2brVN`ds|00XV0nmMQKVZLuc#*StpO{Ic*F@-;tH!UqZJVz z(GquPgLkxNt?$8$u6TD(IMEMx9jq3HY1eTgtLgad0{tti=;#*gc@JzjEN5k%JWul# z-ndB}tdK=pAa0(iz9xym#;~6ek=Uazc0Urgo+%y)Y=0|$y^sGmj>TTWuW!SR$8uOv z(dMbhPbZCjYAv3)e8wB}B9AtHD=HuUkUzQ!F-L;m$QeD<8s%fn)17WC(>!CImf{v` ztj)ARf7w=j9$}~7FCrdgxYw}zP79;8-=P~N>4se=Fw=6IR1q1a0w5l{OFT3n;k zSLkw>EpZl~3u6RMK)d4+_8IbZKap?Q_jbqDz{G_($8>Sw7z}l&_WnS-hkb3j(St6~ zv@-ML{6w&(b693DFv zpPvp3&mrC@Ao44LXOZMA+t`oRE0klhqwl*S=?J%5yVvH$JWvR$=n;7AHm~vW#{40dHqqpi2KkzSq@B}}6B0Kpv zlUX95z$wgV5kDlAyNVmuy-?EUF`(0pG7j){k__OSGIDc_+p9HpC@k})(;%-b(mb$03M;c7^EF6Xd+it%XRik$1Ix3 zS=E-a3TuuOqj@=bvD9LuI5g%7YrKVBo*`lM$`-QJ(@!NID@iZXMycv4T@|G)MG9)e zTqGO24=b`|@IE8S;=3Hq%jcTnt}o|aHQc|6T4=8pdJ}t2K;+Ca3RxqW>NQ?JCws^N z64nnn=liS9`@{K!@*~mCJVsg)&5&q{@cW*`f?qb{v7w2_)VDffv$9g8p-A?k$ zQ)%!lk&c;4>iKJ2?t5cQj^LcfmHo8b^|=V&e4?+>>`Ym;b6V>RKJt_D9#P)?^yABj zCuon4bd|B{LkIecG6%we!Q#y!T6!>a8Gys|lg;RbMVgc0iO#^*?{V`T@No_7U4Ru# zQ^RBU#-Tjm2Sx(*f{R^wfleaO_BelA<5t?hwKiCF8@Ypa@UXooqLZkgt8t^f`1^h$ zjX`>`KjtsSI6B4qPkgf!T7D*q-0quiL=#7$=6UzIu4gmstrlavb*RIn{6sn}mO})X zABKe$_jycK5mw@`Y-f6UJis*2%yv77QDPz_AC_d+7tuzEO_17xBwN4k&VO!c6c2KSeoim6rS8I?|$yS&JQ@ z{|^TW`#=5eIe*i>mqmOR`26#D|5-8O8FAwoobQa@XKT5T*Kxw6kzMd2#-9dSLl$DP={&S5&CjWp7Xdm zI!SZR(wYn2T~c#bamMSkh6;UG9i6{?J~xd7`j-<4rYpT}~>r{}r}=k+q%E z+iyQmcKih!|B`3kEr+`Y>ICbvL#f`HdGfl`SY%*!LyZdP%g1!VG20la)s0?0hRM*v3A#VFjN%dy9Ph2C{}M^Lf7SrqhP8JjD?A=*h>l#Tx31Co1{f zqAV{59ZSu!%#eepVSHeiS9@Ll`7EaNgLc{}>RTr_y+BTC5-a|Q_VEIxD&9DI9fH9t6yLv(NN_@5{tY_5|UJohaJ%KI=7^2tGZ_+_wVRe z-^b8~(}<}eiLh(wTDr6ylMVX`g%zAm={36KNw4Fqcj@wDe=+jpv6LeoWJxZf4kL`S zv8a6fM+qei>(Xg3k**)-KGuwqtGWXnE_ppln|9HNz%6FeiV<|Dr{`^k zgI1w`h1F3;95aD>3!{7P%K2P`Vn^uU`KOUQ!C?5`SC*=)9=#57 z2d%`3O|8^YAFHlSFKWP$YQ{EHA(gzZP1(V|+!n!jU!D@nY|1WA1iCg8wb$o0_xRCfHB|xKLMoQWJBoChu8E z99mWmv?M7^@?gDTZlRTaML?bK+wOcuShHpjzdD?Lj)!M6Al^bxyjnlc7O`C#)Uj^=JqiY+jD2jtiTO}_JsKSG-GBHU|u`F-pq%(aRW@eljuB|=Pb4NrK; z7rYSN$XvTN=FD^zhgzjDda&4YnB3JU`(KSE$FM{KQ|7_=83|Gv@;z~cw$Q5%924HdaGVyhB(?C(PK- z8<8hopZNTgK0NjPQ|CQ({!C8bq`mxhdsxw4`?hnutvq*YncbFR-R9c5 z2@7lJiR;0q+N>^&RIl!d?04hdQSdqJSCyWv=JQ&XZ>a;hTf>0fqQH-M_bJ+GAy2;+ zu780e*dJB9j}%{owZP?Hwf6&&`K5_3!e&b`mWi4jE^*-Ox(bF^$>(&sxmEpq*(bAk^yL32ba)=uj z+Qwpup$GhLvBY1H{w{BEi>J88&VQACxndS(*hBqi{a)vZIo@#cZ75ofRKZDVdTq%2 zw{-kH=k#&@5Un*v`ver2uccOK%Z=FM7j*9%4DujdJfX+p0@VHulMFM+A42n|kl`gY z_nJsNvDZ|5b$T_NjfTI89TiZTq9U@AzA4R1m!;_y`2C9F{EB=@McPy$lGFM`K6y0F ze;*pQmFueO*UIC6h3H5&sGdg5mCzH#W&zQj@JY;lEcqt(e3_gwt~0Dlw-+LAhnVZ3 z;W8|0zI6;{$iPnIEl1P%VZ7u}CI3+5^&ubgkx~yAWsGt5q(}_1M?7)_dj3q;f9G@l z#h6}!7|Cf*R@zfIqCM%fR$e;uHfCtI4LoHGo;e+QEWl7!(4zJ9<#QMia?xLrJ>Kup zWAT;mc8WE(Id`M%!x~&_sajir>CD1yrsx$Phe?f8zaRPCu%FEbETp&irw4TI3=7(` zh*sEkQ+bAZvc5It8LH?JDl5+rWt>=tMdCk1`YpmpkNKT(A zb}^W>jc0XpA@FLow4FYN)s{}ctzTF}82$eQ*1QZi63J4f(YBez5V_@`3h@CYX-8=~ zP(htk=2xnDt>JS`JhHZyu1zQE(23d+1nTT~EA|w|+`Pk<%K9tp9%0->Sbh05^(bG? z1{2FzzXC;Lc>f1<{&%{2g}$Gm&&TQcL3!zK0R*xhX1{J4mGg$wLevi))n2W2Cy zBnKx(U;*PJ@vJpZ-E*m&<63NBi{IMC62B39huL(;py(-Ck@NH~>>>0kCU6T2xWoJ3 zmqmPJ^z6S{^qH28!qcres%AdL%a`C@OFjQ`t-svs3bM?xWw36k@57pF3*pIJ*UTaz z2RzQ}aK{G8TJ>^HN9TuqifXdMvT`2<#rj#)cyhQCW@J2}rFUuRRs7{F3<>)ghOs(( z$WF4&=dG+Gta`AG?+G&^zrqjpv6LT_^f+rd$2a}LVs6Q_+$Uk2L|En4$~S&BmH*!m z8D;dl*|c%k=dcjnF9owJYTp`>wyO&r8jCkuh$Gv`F?P__o#kD+s*&z$qo-2$!~}Xk zfbOJ=%tc2&rHz_s7RhSP*7}opsgZ1HpwU>}jhbogx9h64iV-AwKnHJlzVqVFqmcU> z5#4qffpuDYiKx)3jSzjj4AcYyPD6!ww}t6OxDy>D$SPD32Ey3A}D9HAiYkVXG^ zD%=qBqv5r&k~+L*eV(w1xTuw~yeGEk%Nu;C_9lqX=8BuU7cI>o+iE5QT;D1qxdi8$ zCVCoX+*|06YNK>@`S*(QLPgnlPS}u+|4+*1UlWbK#Mi~d{J?X5$D8i8U)h&lxB0w@ zCtYjx=oLKaV!Pok)B`k6{5hA-%*8wB%QGxgTT5w?9HF>!o;vyruIzy+$MK9`>BeJ- z8rze;uC#gSYLVa-LcreYXYF}*v*Fss5wjuiO6 z(M8$3W)PL9ax9^!?G+5{nb|K7j24#3U<_DSsyWt`IdBhR0Au#e!`GGJR zBCxU}JZV@F@DK8ogc;dko%HN9vj~hT$EVj++Qv%TR>r6c<{5U>9jGOTvgHx7R%4+5 zcy%+8^-qNV6P-DMPaOw2Mw8)e;zRa7!1ulNhID~5VMJzAH5As|u1^bo#=)0wbJf2wNz=^o?jbg@@)!s9}gD9qUA9$?dU*bI}z0( z)p^NE;>_~$PGzmaQ`QPR<*};DaN=EAh`QpDX6&N9t9!7TfwW^J6q?H0ET9dmY2G$I z^&7tQ815F?X0joEtw;T{6H;u_d=kvi-xj{HvpD%2r z?wTlfGo@`#TK>PJ7|bo^c(KSi4ZYSQVF$bFq#~q#n>Q`$?s>h-2BkvHC0CaT!~#|X zV3n0UR~2m;R`3c^g9<`|QUq6wl@|6_z@73zGJE*a=C-7}_x&CF z*t3sw-Za1Yi4ra3*_Xn*Ra$SIHV*qQZ>9}fVf}VqZwGYzf-l$!6?aDd_W69!@nd-7 z3Fn=Hm8an5NnYbeIC_Xj`NqA!aP@k+w1}=uqhrG&S<9wsq=wR$_6+&xUtda zk`=s`$D}IIp~`fmihTDw^4@jmR|D}!*juxmI5Uj1>I*YIgc+k{!KOgh`LtsNHnIh3 zd?k~95bp|eX+snl)~kF3-Ja9ru%~nau~9PqBn>@D$HFqvkt~oSs}{(rJu+hj8MR+p z8uS03%^sKSyu@Oju=PLrh>#7!cUZ?5_B&oyW&(SjAnqIoxkrmZ)EnmARF1q3#$5&5 zE(^WirVAy-DJ5B0X_!!vFL}qaH4>Aw7kR%g8XL(w%%S1yl;vxF@w6D?Hjf!4FO@99 zIyT`@>-mHAe8757v;pJYr2pb`y=*)6&VDTeZ{!swvPZnN-_suP)IZV2utvwvYTy_4 za!tu^(!ATs{s+6bL!*P-_Wq_;{*C=yQcq{q){nIMd+q&|TvS+Vc9mY9uq(~eL$EvehaU-LZH`0+b zB$*!I1dhMz`xl(|pWR0OvP;qLqO)J*ug}q&pX@krm>>Ecc32gK&NWd_oylOnW0GTw zGv*b zOho}Xue|go4}bF}e-c z1~kMUEfraf!xE=LiCHXuo>=o!dFiDx4$Jj>}jZlr%Z12&wMmFHv+Z$gB;BtI$O zb3s^<-}hmizdURp7Y~!&e96p^FFhU=R#6WtWy>n4@Axbv0o;!doj2L}VZZe;WVCw)8H*=OAk#ek9I}Y4 zCR@lJKJXAZE7A%xJsr=`g$Vh3M{)8K1GEd*Dyj){pLqTUlFF9CDwpy6&8F z&JUT&o$j}q=FR3~hhrN(_)mM}@t(oQ74v*~wL=!Q^oBmsB&_gNH4=?y{KIqH!z8Zb z0++CXGurkz+&^SI_IG+R_Okh}^kD4dDR%G}+r4h{c{`8s1vaq@E8inO^{wdpAfElB zdz_V%yuvS85skl^Lm!smx*OE!E^KT+X7;0q?>uYzjkSeU&mY3BFn1=(GruH55q7J8 zjaP^l@$#j$d2v#JiwRv8gvY_0A z=!4x3l7HwXZ`Fd-^1i$$DD3+jZ2b)^EQz@CHTQdo9z@gP=P`d{S9hS{ub9?3`@PET zshJEc;!U+s3L~pd|C{m0-DL1T#LlLO<`$DJ%KfclC(Y;hO}|Ho`J-r2e087N2%xO$ zB0p3tDLxNtioPQ{tS5rCN*G`B64o7GOQwJ#VeW1=yyQ*MQ~^j*7~d=c9STDf>z~oN zX*6%DcAm^Gj6fA>Jus&4S5ek+{^DzR@R`j0T#?pje&c<((1!1*Ekjd|r!2&WWR($l zou5nWHLlOEIR2vVqh)EMo&TciUWO^Jxlcm*gXG42rGw_#{YHKsxRhK%WnQ{2Y3056 zRc)Iqz|LAFFmlU4ze6gNexW4bc8{_g|(MrTcu=4O0d6TG7-h_m?9C+W|ws% z1oEn(+#;?VykAxpl2I>B*r71Bwoaj@63bD>m#>WDHMU&kOZvg0)nZsho&}SJKDT$g zpYKH}dI^%S|J34MJ4A}m?(m|{X1|qr1iOAhtIB+)(cb>=>)CM}`u?#Jj-l@pwEh&j zJY=olqL|+- zpvH3XHd)kSI`)&&|A`}59`C+Zqtw|K-@fz6mSdzQUWthg?auM=D;- zsxeUP85DcPQzgY5(`)ZMo~|TsS(OenqzmmlT`$?T!Ok2l(>oRG)Sso#{+dy|SBwp@ z77RbQO5SrBd-#;!`h<>6)(WFo;Xquvt7x+Y8>s;&--ZxxvX$3mC0;Xv>p!01mYDD? z6hFX2?11QN(3&RHb6#a0rXbwFgkfHr@)&ut~R$;N+WmlxUiNp*fmN%pewt#on~8~+4z8t<8gXqO&zwv|Y) z4kW82XH~*;=fU=}C}~=8X-f8ylq4a^981n-(%^*|ou8BS6q3a&t%lyg*cvNaCpGth z-yfwsvpwHRIXJ5c(bAf5qJif%2Us*0);3Gdp0hxPf~>qO-%vxW-b@tUi3Pk50fv!D zWF7=u2?sVq!QCw62iKjHr@aIXZa{)RUHuP+_MHES6`eXV>m^=vl+`#NJx>_@`|||R zm(7|w^*na;A*-|OGi&Zkqu)Oli`a`(jaYxi%t|YBd*Uy&%4fLZB3^Ym);&rs^%tFW zHd>&$@f0<9!Ls^63o2<=`OH*a*-|<-^*T&Wg?}Zd{Yhj3%@p;-KhlG}5eEGt-3y}x zpNRj$nE8itNq^#kf7p%grZ)af9^s0ZK~IMgJ&!rb8~!Lyd6-zoH^wa8n0=1pj^wrBDJ4X&GV(&EQ4Ct>*ooqnCqERYK`R1ObXGGK%vAiD&*x8@Q(5Epm;Ps z@WS}?KLIw7koQkS^OMTcr+{^-SwK3N80Pcl^qLoIDhww}shO}&KviC~7TXBEwyBt* zrN}j);s5sS&cjkNI-1;fad^!rcJ^4`omJprU_PEI>?)re{=I@V{Db}8gl1=%Xzr=3U}YgzwG2&!YI_k{K`p3fA&n)`G!p2L7n3e z>u0EQ9qQcYRl;7`VK1(@YCNghPD2|qXqzn9NOtv;L;Yk|X6p+$Uf-G4ImS*xFIE)1 zc?^+$CzrJK2|0}e#OJlZ0x zKOo+E=)4t1e98ZQCr@<@5}(DiuClzl_(#|;Ar=pxnC6G^?784|F;7rI3>EetXacv* zqTtPs8=-St#_Tvw{S!_edT39|37nECJ1ui|2Gc!fq-T)x0#{|R?0dlKXnS@ zwE`2V;eC0lYn9?BikKr@Kz>X1#O#wDWRq;&Y9(Ln_$OjdqnOq5TX3bU_@k2E+3ISw zCjF|XHXGBE7HX`WQoqMb_7J1?(~C5iryfCD5@B;52 zR!E6M_Y=UXO8> z+Pnd&s$JjG_612!l2Kcy`9EDq>U|RJA0)XNNJBD^9Ham#MXLI)0cl5X`Z#}>Yp1&J zV!yN5?|n^Q0w20eXYP2Wr=IOq&zOv4AaBx$us3p9C|S+BI`~0T_0b9*w1NykHEQo?LJOD^xi2#`XUK{2iVmhzU>ZWSS2RW1Ln)~wAw zLguoO(l+H^S}JWDT&q1T=;VDDHPGGvJ#n!}L`7JQp{f3Eih* zNRvex6ZwIOa!iwO(8+ozrt>Sa$UN^CIku8#Snr&zVva9)p>O%1!_GX3XNNs?uDS0W z-#(Pldg9w?T=x}L5)aOWu^GwnWSM%L^d_ABLo3{4X`z4o5&6gGr{uZsqSa}vNG+#= z5Xt%BL@*|<+J1!(j>4e+)%WxK=t5-Hbb ztJT?TIrUN!+b--W3-Dk0Fz0;wW(#=o!m>yu)mb^LtD0!3E>6^fuj`B#_QSr0V~JCg z)~bFYt21izB+q=@|HpiDI1*p|Ct{67n-a(arcwtPVP;MmY!(h%In7?r)6n2wuF}@) zF#Hx2x&zPsruwL+KC05TAXQjG*rlYh_mv#C#v8d0@9&V`-NU{_v@~GBTUxyY%`L*8 zguRsW_}yImNOsztnT=+GTp95H47_0`7L=7G=u=W*$3b2A3MZG zkBUBiqJO98<{27v-fv!{&t{ow-8`(PC_F4f=PQ$UNgdi#pEUSCspqvejd;gv(5$yf zA(GRvv?MXhdzsdRv8Q+Z{USm-McczVXig&7#YcR_9qc94Sy=J;yr({+E>Eza$RjxMFL^3r zkJ4fZ@u$%m7S)btYoNNjy=~2x|y4Bf|c_p+`3^u~)MvPZ{}p-T!H!URY%| zlcQNlHfLmmirLj(cIC?sEwe+-Y%DG-q{s?Mvap4qH<>Wa4Dc}>|M`a2NTXF!vihWK zBO$K*nheOxS~ZG|JP`#vlx?~z%XC97^)l=`D`Gq@w;EI3^ocJPJ1h}*FEhSs1%I-J9c=J!t1Q$m@%BE}5$4;RR%4gw+)cImmnV4w z#a~o{*Z9yR7*A?kC%u->qSbTC$c2^R3u?)tTC@atiMhD*U?p=Bi z=0AmfpK8H~T58RjB95gX34H&ub7P?EBldn<`~LzxPh(Gq+5KL&ww(@qrrwsRuled~ zX2c3!mNkk}SO1EX!swAZZ2krh8b%7*Us(=eHhWscidQS;RyH2`yu&(4=h=SP#qBYR ze+d>R7S+&rcJu|GupM{b%Az;Z%#CblJ4~R32UI#>WDq{U{k@ay88Sc>EOH8 zq^a{7M)>(UzGIzxtz(yKY4aK-S%nd-;8&N@=0$#YJ{_E+#b?mu$x1R#Nk-D&k7#Vz zsk)yM^!Ci%J#A;6yd%77uLavGQ(MT;R_nG?#*SLLvsUk}ZTs+r{b@vq7Du>lJS>~8 zHs-l!*mrLo&HCJL?xyz#wDV7%=^}p^_I-N*Kb|Xlh=!B%2AMFTykh54SWsm#cwITC zmhiu`IJ~dO;Y0R6TC6>p%q9!GTOwDviicPS7dAR0S7%A#~VaE#6)_lHy(|I{Oc~)qZ5mE(4 zlv@1;zLm&*<3avcl<+0L90eJkK&h~1lrdr~{XYDZQ# zOIN?v)9;5~n*Pvd2v$9uevV}eQB@Cc=%-bJI`jzlWcPaZ&*t%FwX_qLNIa zu;lo9Y#Q>E-;{4tYt_|a4gR1ujjhjO8|d$BEM{oJQk${V<`LQIb(_yS9pB^o{mwhg z6P{3lkUh9cvv0fC1NRJLre1_qaUffmNt+rZw(b&`?fW$ZYe@&&PJNBo(BHvI6I7$u0qQD9QM!%@m4|S=5bvv_sQZH zGAR2Selex`Nx~u$sD)P@c~MP-Q3a2At2+_T^$a`tTaNHP7V|s4dkyM`J!{V5I49(z zjzEp?BCX%fw@sjYZBas1jHeu=C}qT(-B66AooVFLR6N^`Qt%-^+$n@D6@#8{d9C2H zwdnPVPZDiRr71IM$R|)~As?~~lB_W@Y9ofSoqmOxgWu8BqjdK)k9A1~=eLNCCuC14 z*i~Ak$Q1F2v6L>hoWpDUKs>Jr)IlP39>zW-r7g*{LrR~5=d~__$m6N2AJE8K5sS>s z8nd!CdkV3#fY_T@+-f{w37N|RXLy}N#zNZ>zW>O1LtHb^6AXaz10dc&9C9#C{|Ew) zRKMe(@HEyR#z`#oIeuM63=G}AA_WY!%_f|nvbCuXp zu&gpTLMir8lwA}MlfTJMvfz|yvB;2@jSC@OU{(LX^1D#z7hd+15d#M>+g-fwdN{vG zUU~*kJVq@I78&)@Yv0l6wHBlSsU>GojZ_k6RPec+@52h;6?yO~Q0-kFyqDsBgmh2>~dt1*vYO)^#dyOK7PpKJF2~wds;O1D z&uXo+R(aRyIrvOmy1`h!P4s#T&%KSl@8H>Y$rA3CQQRw|xL*eGfK1{+a@hN0k@YhU zvAl28_7|AoXJjFnLWYv=jyL09t2(DJ8wn%t;`6yrAyMe5K1<6(pY#spUgJ~Phjkj9 z93x8jm=+Jj75nkGePC&CSM^a#ebrdln{lvO{8%Z+sMX0l_Z(KWSWSmq)m9}A{h;5& zoa0I!c2N&KjrTCTe^_IfzxOH)Pt2pG;RiCp{jiTx0SFP+8h%^ETn?L(Bc}O%weJUH zFlH4-S`YKxD7G-hx#M|)39g;M6HIikiO_KZBpE9z8A&TXrUgU%W`7#gi-vV!8|@%T zbKbZCG^y#ig5NC5UP@vgg&;~EHI^OsvG#;A?NPpM%DE0&gxNH6wDKhNFhYF{qzSzt zM`vuY9WD}P@ih}mHC2DjFq7ucpf$vAuU@-h(|yPw=rWQ_B(r^9OxF2+8y36QxrbeI zMjM2Fy})ja!}DvuLe*=Y;<}o?iQ)gQcEbun_vAqCYumrzi9HnQcu5joi}_r{@dETc z%m&ZFqi4dY-f*wLu|lRWmX>+uIUZ>H;FB(4P$$R%@|DkHnqn4cEosE;<1frK-goc3N0uj0u6bue6%44 zyGhU9tc(jke&7Xz$NNUE@P8}Fh8)BL_#k_v&DN{SwX|Th5{8{1R;z{8O1;kO2FJGI zle_quZ&=nLI1=XE>Rr~RDLh;7|9Sb&Fx#?Hq{k_5g!{GQ1-kMKz1d4Ym_HDU8l(mW z$<7YK5I+!W^da56@8r+EpRB44UiH6dvp9e8rkcwbi6^V^7hxQ{@vSo7zq89LH0UIa zImiZf)0)jnzLMsI^>U_?k+O1wNKeuMvNk96Nlj9fRCK(Y*RU31SyG-7O+F5&eGRFPiUq83%|9slxt$srkl?&@1%y7Qp99(C6}w zyi5BV$^^8={<@M6$Z#@6)?uFSRyt=3{ojoV9E5-;>CGja;)c84b;L05MGKzilg*mO&e+@cXN*jdL^-|H4 zFp~CP93*K7c6=!*+|xtFr#R_?>0DRvvaq(W~X}lTAl6ZgLpIl zhg!SHPOhl$-_%yv<>59z9M+xs3sybmv;UKGiILfOK`CEWL$AU8uu4Z#ydow2ex2=v zeNDseM>)I?GyC%K4+Yg=5ol3dJ(t8q?CT9pqOq#K>Hi(pe%0$axyPe4!1}%X^Sc zKDQ!`d|!(Xsj5Wf)Z$yNEe4GX)55|^R+yF-!B$FQ4rTpbRli$Ttv6?jooVj}(0&9> zo<{o@d)D=yJ6OSYGAJixB`%6YZ;+5}5983oJeXHWLK4vPb)GZ>-y7IwR(O&fugpoy zLjQI)68imu)}`}Z7+03UbymBDq-AJPDH>Fi_7tQ!dDLAF+LBrQrT3e!tHTuRHsl@? z(T?~mD=tfoBMyC44o^;EEBu3j4JE=@$a8yRTLLWU_YU#QAzs3?$((kO=u@DTgg1UI2 z3hz;yKWNH3w9!A&0e9+RWJou&55h`nU1X>`>CbK>AEj54cUeL+7RfGz5p;9u#Y}oI z1&U12?xWP~$7*Ycn(D8X`tTJ!@V%}WZ6`L;K~GB?wbP0nw7?IW!ucjf7B}WM%%tUE zJCSzYw{iUcN7Y||U0r4G<39~j0#X8kpnwW0f`}p^3SujQ-HxN9CpSBS^1bp6%t`?LG6 zCzYGD2>#)Dck<5nqsP&6ey69trx@;2*Jg+JA3Q647c(H#LK0rZ9uoQN5qp+}4(XMy zLwgT`d(GfnD-mE@cozSDGEXDBoI8i;G5Ok4igp38Yq{Q&>@V`Vmd(qe#2593Jn!kx zvb1Md-qWn`DHiYq>wKJ?ALYd|@_s)wxR-X{$u2e{xB#p8sEmU{)w=4l4niE!^xgIQNP4k*gHZ@H3+(VPAht$*ZH$23(@v2_OX*h zcksd4Q*Il7y9GLIW(S*C_eS;DKzEbeQfRchV0GSj&H~=tyx`|wHn8;f>vnzo5q+ar8F5L%-=ESi|a`^jc|JG^~}mOcwL-&DTde9#wY5E_Xlpv<Rnt0)?%Br2!`?~6u**71L9;r(hTw}KXCTze5~n9Fa?6z3)9?L>ST!%{}z(NI0a zgYfHH^cCOwIVI^lUaaiUp&aKJivp@0_)^+n9rs+?!kW4>1X0+H7uQP)zvRoVS|J*(PG zcXrUVVz^e6$!8e}t|z%|Fr*keDX;D|c#s1MQNp>nlJ&Zm(*G;*rKm6W9N)V;cE0%Z zLVURh2QPBPMQrRsmUceQ^k66FK!&q;<>c=>k+O#7;gHdG~b z*^jQ&rVsV$K-R1tLW7z^gBE0eG%U>i8^@D<2i9-`OE}RJJCpy(qRNwL>dAaf7apq% zT{wv)WL|TJf>vj)cK?Fbw}KXFc^$5#%?f=-*|jQ>=WFEus;Dsim#;vWSNNP)(d(kc zw|u_iv!~;|M5skO*A?}|4D_7r7&4L`jZ?ntCpJS_=CY7Q^lUi~s_%mQuZ7We5TuJc z%$L}O6MN}tF=JJ-NTE0Ueiwb)L)Wy}x_EV0B-5)|V{sE2xn*s$bBr{@(W~ zMEQsf^n?O$L4lV|GWGzq2~LjVb&4Wgd6q5&IhuFCM4X94zxf=qhrwVj&*=5Cpw@0tgbv`a`T=+ zU(RyxIXvXK5TQHjp{>ya2Yc|)J#go*x6MS+L^RY zB()Bt*N&zfi&L%j61F5=GnsLzCx7#ymR7I3hS(H*igi=m~ zQieU0f;1)BTG}SXp-6F&X(E+S2~?`!8}5V(_qgW)ym**@_%C`6z3O+?N%r=v&phW_ z2=W8}JBEL;gDGFW4(_akLrd^9-hB>FKZ6I#9!!%(@DtHEG{)~A9skk!F|Hfqu5s>4 z@7Lr)zvL8c!UwEe8nJ-7B|~BL~1+ zZ9aJ0UMuY+cbw`8XBV_3>sz)!whh|gX{%&E?}hAiE}H54RQ_VJa}z|u<6Jw2R{cnm zexyY|!kDo%XFOb)L}M~TWQOQuHrukhfoD}w(+ZxQ)%hj(Ew5D1QvPQQrxF{eN{@?j zJ5*NA{ggWES?jBBV z=l{{UekW`9nSAJJdaO={w4Hd4_N>S{Oty9mUwf!`)%8X@ktp8*O1wW=)l&QY$kIO9 zWcaC?^d!@aO+8QEPpZ|Ug*c})JubsrmJywoE$H+^G>{Maj!h=t*)TL5l8^E^290%mg7ZH?*q`+sOm*)J&-ew` zXL;@%WuEJO3zTsYPX9`8m(l(eFv#w`EGKiCCer9p&J2M@16bl{(ljqpHuoxYQ5z_gywgEu{mL`~XN|4gngiMB|~x&-}^^ z{$-w0{;IvalAdf3k!?}qU67|Z8I&cNN+ei~pRc9Y@c=PT+7eB~JI(aU8$YB2|K$^( z7bCnz8{Q$i57EcyGrzwg#Q|jb9c>xx`XS^w#QlRkXOQO&^xSV~*O#p1GrIQ)t?Nw# z-=~vrDa$KR@>!AQ@^^ZCt+HRjbDu-MJFCyJB+(3F)@S8aNvM?iZr9hk z2Kp~D$}tm9Cqa?SW*f#U4CXh!Mc+V`um7OzK{wE`!Td?C8CB@58mNsjh=hjf2_FHC zidrPAVAXn_B>m@GwA{8ivkUs|5^e27<=d@IkL$PRQ`QQ1pEw0$=%B}9$ z1}(EDVTb4M@U)#!bC>sJ|H|Y|$m+My9^XTI3pBOxS&S5m)4SrhP?8R27H3%!E>F@G zaG)~1twMjR(S{myx|Y7y+WNHW(5Slfy&g@H(G9&-pn5*DS7suL{hhB(!)m&=hI{s- zVcAopYN5Y2nZ@iRMboN!b~RXAgSI6@Mok)5i^kPvEBo{K`zy=-Y(<;W@d?iVlb!7m(4CBtz|TRrMj=hla26wQ{mf6+*yh%i_t=qnQZe4+}h@Ii{G0Z-++r*3A9p_ zyA;o}CUc(W%qsA*7<(+`o#miqMSR*9Y1b;tPwF{^#!O=oGofQfX&1nNUs;7UY*4o` zUyxPu2hqfa;)4cSxd;5=4{FoYtP9eVsm0*4Tmqn#$b8(+R(qtsq<;^=tWIGSE zorJcL)Hb-gO|7?*?pAf%;{WgB^Ua0YZ}5M;nq?=w4QltBy8R9@!jIkRTZ}I##dgcH zg?-sV4Yk}~jp}Q49mF3T!Ux12j>P?z?4cD&9L) z-1)9K&+~imyw;IJtp%=~E9RT+{+XUN&9mb>CTS;6;Dg4}x6vdzf}ITGzrJVtgXzY1 z^k5*J{}w`gs}Eoxq#6jP28oP@h=#w{%aEMmqqGfwG?F{ssH52tFymIZaub`l5!T!Q zbBg+*miWEY@#K|Sq0efizNl61S*5>nwVsL9X3VTH`nSe2*Xq4kub*;*NPANu0_#T~ zzoG?$=;LrYJ%&C`R^nubokJHFvz`@t0M_xOn|R1=Vw2q>-{Rz0M!!^feUX*(=Zk!Z*Av+ISy*fO`4e$MN1YskcbvLsER-J@2r;H^eG0)3oPE^+{3r zBP`NhF52T|#2cl&qa?pojOG-hCGy6R`f2R(M07m+OFwZlbTCV=rwvzwhAgN`SwU$l z`>TSgq3WoHV>O+r>58;LYPh4i5?6y7RSLam>sjL}D6*7JEcDblo<36vr+V9DZ%xLP zao+!<5{*{ok))b_wc#*yIKBCS0lyM)ALj26(Qs2OktQsqF%4-vcDY)-=t7sV!;ZO+fg9mTg~hFN+$r?c@H?w!eIW_iPG9Lox`%&CdLUW}W+vX{)( zTt<6V__or~)i@rmryqC&P5aHU&02X|$Y(p*Bn~h}=_y;uVJA;l47W?MoPBuN^z&9> z^ZWU$;jb23sRcV~kwPt8sR1LZqiU$iAGEK}%4lEbtDvfAzrwYdO;^WXT?kVLU+dst zU7W0oqxF0q;4|4m8#vyu@GU*dwede|srJ)C$;_t8yjn$mt|G}-;OmU+`5yh~=$+}2 zKiRdXxaUk|?53nWyy+ruxzt;(K-Zw_{Jz1tzp2sRU4N^4|Do3ZBC&tr!9VfhpOETS z$aD)y{SCrikK=zPg)8`vjE9`d^PkRdoXEQ$Ck|_&r=_WOOj-n1Gm}tu;r#?IeS-h} z3bs8@i(1U)OXlz;^Lfi;pkAbx_g7wZiMBw-s#h8*T5UvX4en%)hxPR64!Zq6y4~-8 zp>XhK*WBRR?5%nQRK3Xk-Es8{l#$d9s0}*Gvzz%$RM^1x{ZaPtse+PWsw^sriYZ6t zb?!vl)iTlj7T>eJ;&(OPjMtmhI-?hxX?Vs$ey8QXyD~EavO`$(eVZq4hx0pG=T4Tj z+Z%VYv^^|t56j!bI`^=~-J&~r;azhyTsp=*ZQR?2&9tE-ZD7_huq&BH<(G5aYPztJ zC8st^Sn*=d%6g0WYBd*Lq@6k&8q9(h$!C=;B#HiJsO1cLGlSoe2cbYSlyQddS!eQ# zbHBJIJK!Z6or5!T;N~39nS)ca3zQx5{`cG}?n~cG1ys(xrJO6~+TC<2?V-&$w;s1v z6%b=Jgjfg7GfQYA+?HVnjYC<{J>2o{aWnI3zNJrJaM=pR3Fjq-t_lF zdfd~U8Do8qcX*eMzvJ$=`K7mMF$z3ulqj=$%8Vyj+!IokmdRP&{cM#NZ*d%t(v~MV7HT9nW*ae7YoDzgv)UE@bmtw;;UBUFF;W; zf4Q-?etqr!TKWO1=oi~ZzgltqzdMacZ$|61FjheP#VjVPXlJpk>3qo)vEF31lXZgQ zA$lUTF>KDfRk7JT7Lyok1&^{0#dfzip1HPrTvJSGO7LEphgFIvE3JeQ%kl1E+5cdRqJVIx1Wng7^|wxeCfEA}{6j8`tkLl-yO&Tby) zGIT+K)5S@lXfASV(oLjzFs-cX-s-d}eE_9AYcGu34(T`HzRv_k^gf`X0d1=)r6;AwF>SqgW;q4We)F6iJde8$Xze%7O#4REp{Z+eiHS!2Be zSsTz)`?9Gxp{X`z6Rmz(Pn2}HYkqL;5AGR8&xd)&5A@&%rB4Q+5%hS3T8toxQQkSq zyX{|tilOXow~Hhr(QRn!{}1K&b`sx#cA>qfxc8URVk}$ekDoxIljz&eY+_oW{I%34 z>%!}hR@NcK>n5jPZ4#Bt68voAp5u0^7s`D&KE z7N@KA&phZPvFvy>295T6I8J2E@%Jo!klGJa^8w!Sjc0x34PPqt7i#u7-OWfufAZ>2 z4*mI;{@P#B{_Ja%S#Dp_|F3w3Z}^vQ`HZY1{NC>$93Q3bH0u_G8o6YDqw9)Ym&aZRbTDrW3 zzOSbHX-lu9>FM8E;mKKJxkAlXsQF5omstjD$Ry*`_No`z9HKRrIXy>^d<%AVlyaq2 z){yOIlO3*kf#)qeBUpSKzt;|rI=b>i96QO=yO3EITG&OYyFi!D?DQm>cOo0?MBh8o zxc2O*9lL5v>ynYeNp!?0qmzyf#}+7t+#Yv~;bSZ$>*&F*V89Qza$N z_)={pJ3t8=sd*DzNPdUI$-9LzwxkEGv@NX?N1aGNYq*jF>Lm3@zgTBA?96v`F4RHR z0cdpsD{2o}lk2Pv3`yCJ;4coi7M{~$Wxo)@~rlDp$eQy@S5WP!-`IW(14@3Bq?^)4MmNTqi zH{)2&&+hrfy$hhsQWmt@vo?ypwy>q0&hDjQC2+Y6^vmk!%o|P~y=rW~1`gJS_jOVF zr5d3oz8~iJ5qv>Q*R^qdJNI?ucRJ(zDRANpQaGp3sv4&kZ;aUnKWb}?<~K$gcN?SK zFoypaXZFEFV{|_olbeC&@FxokKIPg%`(l&t+a2HIx>EX~_Te-36}#;xZmX>|SeK2~ zXRnP&M|MS8bre0z&b1jSJOI^1MeY1Xv^eejM&#d+EjFYJ>6>oo4gaG?z8{1dqo(Lk zIMNJd-sVwakfZ5P8rWfu(n*Rj2b0-C#j_r7nY0SBn|1idkxJ4`DpU!5T!R_b{T6#y}^8)h+&v(3s9*J&$jN6{7$D*@2K^?V4+B$QzCmrdj z$^GA0>*PR)RF5{Nhoy!RRpU*n(A&y%u@Y=aUuH$VJ#)>Syix{be5;h-8S5&+GK;~ajPLBljoo;&izL(X-VS-T;>s49@;m-)a^^QzZuEqW z-k8{6eIbgd15xX<(MGHw83>a9uo9BGqw+lx4)@$Z@Dj7AbWa7Z`x-MLH3xvaWamO z9mwkOBU!awTkUIlXAPb(ZL6#&4Z74Kx&2{JUDB*CCTK{O$z_n)T+It@%(OKxrau?+ zCl}J89;9psPCC_*79^i+M;Z`KYwzxMo_L&irD)Fh@5;6cXXoQc;?Xg@XL)(6+ zMzKq>sy$}1Z8tY^lhKRy)(x#TBX+q_j3wqrF7mm+@405a&EYdx?!*HM}&SqZe zfw)_r{v;zqMmx<>#*OhLH4aC9gwk1YJDhb7L&?oP7!CSAs_J*B8s8;vbD>?6Tv|2U zTf=i|vg(?io%U08@2{p@RY^MS_=->>d*LMeN8;b@aOF3;vkJB>hCX(L=0DO3+6$|T zk!vwlSTrl?ZD{qXJD-O?$zJk=Cp`-79)^6`8}dPDn9qm&&aA6Poqyc5Px2Mdc=q$q z>?I!SRd0KP{=Ur$-s2N`ihe#64fUbtAA9R3bpA6fjxV%4zD5J}Pz~09HB3Y`$~9w@ zZjyFKR{CYdO?H}I;<<_9e^ajQYMWU$rTLCXy((+2Mbg+P+8Wjc#r2 z*>Tn*wKt!g;tINT}5?oKl%ce-quXA~&kU-y((pvsG`dzKFU7d|{pr~czD zx6`v*M6B2I7gs@}i(t|@G%TxEIw(tPT9vkRb_Yu)t*W%OoJgiP+DQtV$zr{4nQ@WU z!!i;m>WwW9?@F-NSaxZ;SDFr%ri*3hWNEsYl|^ZhTNj0F04X{vMxXbRMS7QllG~x? z7Fze4_s4qHvCq})bOqaA3Vj#T*sPD5%kF2<;^}OB3R}<2h`~)Q@)5o6+z9Pt2%XL-VQ{FR5dhQ-|PJEcTN9luJ$!a3EMPxaj zT<73z@-3xTWSS>UCI2ZRpPy-C+Iv40KJ&c=I>Pbht~m@9jRGeB>ulIOk990Wzxuum z741bGYl+WkPET5}prg^TsGZ*@;96%~I|av1FL3HtHC>>!HP`1XoSUxJQ`Pz>*GytD z6Wx>D5hkelMCD5RIGJRAD%Ac!*B;=Wj7HW{r|O@FyI&5eNQ>19!px2VC(Ogm?-HJkKY*3@KiP z3a{&vc%wik@+CRe(VUQW=As{GZMQX$dNl6wynlK2-=M{x`HM?o&bfTWsr*DocpaN; z4x<~<^!-V%3P~roW5yP@7y1p-e=wG>NOYJs)p(KPM75oa(knMrtus@6hI;-2F=naj z9DK}Nzj^FxK3>mfNegJy0@|~{dlo><%pjXjE9cYBxh!Z7ji1F{XNdcz7wq>mT6-4X z&<%2*2kS2s^IZa2FN6PAlKIvA!PR2JYaseHBL6>& z!&uBnmNJ%=Ok!Qr=+qqbU(6C$DOXXxvaC7Y&2Ma?=PPK|0yaOL)=f~t;cV+WsP%=C zedLMnd-ChzwimtaX>Wf_DIP-ip?j3>E+xHFiSJbUyIg;d`|e|>52@wjYWoZueVJCj zg$uplMfS89pftm1^*C|dRGgZNmioR9Wqw^U;FRP6G8U6uaT!~$sWp*ZQ?o-*9db|p zpPF>2nz*5o&$50Oqix#@7;~65 z5$>BrPk!-?U+G9zs%_O?DGn9mzpIl)R!F7KDVY&7F40~|PogKM;>20x)g3~e51B54 z0hiL3%hlovvb++vuKGWk=2(u;bp99DWgpjhBJ1QZSmgfrn9MR;PQS9UU^TD5#yhgh z?RxLqz&)jg8eWv0~Pobz$MjdUy#8xR(9j%I5E874CXnl_>$)Q$ie!y z?EK1KU5UrtY5K{uF0ov5ygZPk_Tw|s1GU@O&2L&)E3~RI=93Z6>@)tO9)}+wLH6)S zrs8izm|x@VH+nP&;OlqVV?+4g%%vIa=y?1MV$AfcdG1=w!)6}LI{e=3s_keGDxn=v z7F8^0(f2fEh}bjZQr{}qHxTYiGX7kw&`;F%3Atxw&_^t>51-SUhG#F3(8nk%EBiV2 z1s(Ys;tzEF_wF0P0>*gOBxsot?bLS>%}qbZI-32v^6XaclI%L8u9?q~vF}FWuZ)^y zW^wW*BooIeN^_Ps_3+k<*wba~>nf4RpY=;!3wN_dyolcO`vX3uH$U<*6zj)kzhbZ7 zvFRV!_88B~TESn~Yfv({lDKgvpOSfZ70`aDj^3(WL)iq`m??V4_=2gEy{EJGKd z^H2|^?&kBXLR^rwhofQ`aWRa@e5SOJ*OPtvOt->@o%Awe zecAIUdQgUTlw%$HxFdV(Rp7lUdP+qYT#0T}Vlmkvq%y7Am!4F`!~IYV-)lQo*SY$x z$?B6P^yx6qIKuN10UzVd@d??Ds)Kl-Ba6)5JDnh3Cl-1FMC=F|JHW_x=y=o?9S1d! zbv$$Kj&gkHSP_> zy&=%_2Rbtn1yPfE`zOaTCYn`pv*}dkJ}tuUrS4z(2fx!}{k!L8<-i{NE>2TRqwMpN z88}sOrn;KeR@2PjX@Fw~X;(DCy^InzgSUsP^$|Sjk)G6oT+?RFob3O%-_KHBaM2%n zkQP&V;eS?>33OnL8jOPCKPXq)4QUlbX9ugp5cv1Kex+glkD$+^q1#v}H-RUa4DqJm z!E`j!_gQE*e$0W3bMYkTH~){8$O3mRWI6w@pLVXd{mQ;p^CDRbvJ0O|DSu`4sjYsE z)c0`pZ^L(F_un(s{z5gshPS+lN4bM7KS=&h(VJIbzB=t9)Kn_&wh7AGIt-xORwp1{nAFj9=;Hd2i9f?1TQe`19_9uWG<& z)pTV=Z^`KCc8HZW$TDS}r_9p}){xaX#h~VP7_b>P)K4Jf5+V&xV*>xCAAgG@Cz0A`0PYk&2y`x+Ndt72jlBI-vB}%2=g2A z8x8r5hN7B=v^p8v8bDz)?BPt-k!3yEyIRlhcusQfzK7pEJ--(XOeTYV;)X8^@Bf{| z6CWf8&{CSbfEK4mahj4R%Vp-D4=1_7YWj`4K6m%0w5qo!zVGSph|RO+?p1d55)62O zRzAmbC8O%IQ0iH>|17(E4%)pSc6*5(zRGsr6v@7eKJ@)#)ZcOYPpV}XNSBph>3z%U z>rU_~y)m8Gd&aUmvy3iqp)0&NO|)^Q_~RVCOWj2Q=ab;YS{j#$v#y{$SCi^B=+Ed{ zzpoQ5T<4l=`HXA$oU7e?MWNMMnbwu{jI49tsyyqwYl-q_#OP<1H%9%2;qU+mn;kFv zkl6b);7zvvDxH222cIYD=h(utIQTpczQlH3V-0V!y${*mr#P9JB*XkoU@^a-*y1L? zvkJE|O4f?)j5(+7J`e>?i0vm853!8`+O-KCD;_p_Z}Ftc}Plj^9}F7G>N{ zD|SNz`yz^uGY*?vKVQ+L{)PCu9Ql>QkE|&!SHR8>@#aIJ;TR_=NB3 z#fQC1m*4P=S8(hFNc=2ac#6e6$U=N6?Y+kYOr}oQszFzLu?R@h$UZk|n>gQr1-3WP@#j1I=lD=3i#s+s*Z7;oKSS&0Y>&aG;atwfEfM+0iuoNbgBb!GpcCKCaXj z5hNE)d77V|>0Qc~8Ea_+FM;>-=ymclXTH;DHTa(Q_?C`;4k@y)nf*BR*+$=!1!)}J zA16QJIQ$rk#`yjtTN!P{e5BZGIQ|S{7vDq5!I1Gg{$!wE-xj=2W+2XVY$2|#baa#J zcRN#>q^i*0{Yfq}o-+&j7ljIA081aKB+D7 z0`9yDE8gZedb0msdbB=<5ud<@env0;N1yussbe2G_aUr!-?eY>BQLx6S{Z!Se^cjIw-7*~^P)@;wC^)p$(&#WQyB}dVk;b z1v)`Amt!4Jaz`Ae-zw|LTe7?(U_!+{i4!@{W}%lrJ;?GFN+| z7~&_|I-O<|<$Ie(hbQuIW9fNXnvvEZG1CBc@D-_ju4bR9*{6k=s+1V4G`uLJG6I_@%A^!yjG;XTz{D@W~IndIlm;ZZ~{ zu%l;0?@vJAhj8s4_Wv(-^*51qGOb=B&g@2iyF$|T1w;)Rm!{?Wh*>Ma;PiRa5S`Y> z=Zvx+;AsuKHLKGbkT^`UchQ*k#AaGS*4p< zr-SHYk-Qu5eOZ&ezgVXRpHQWc!{agadXRPhN88|THMoPV+^$Bqv7dkQ2Dg*{o#cNn z9K9c2K1#k%k@t(F{w6H^5GM8$K@32{#hlqma;9U8om=agZS*2oQc;O&;ao<8n(^EH(<$|{N-Cbz&oV>KHhw&9r6*|=!Z97;Z0`a4(B_@@f%a{Vy>fE zGm=<*yU!A0%N?CnX)!=!(LR;W| z@x#N~9#3c|JgqhG9Im_s|6j$aH{iirW?Q{$6 z1=-Y45tXWVSZ=8oo*o3qKrG_3(m z%brfvaWZX$iY%x+PL_j?Wg$$NLIl=}Eqs8hJ=ObtT9*jDr}}3%kzStBhlck3qp#_6 z8lBYw-+IqrxRIHwBheUC)VI)z^p1if*%#ta{7N4~0~nHVnEgn&5-I~bis55+rr9Kp z$xPs7Fl_;^{fl2elSaHob|4u-Dgz4sptuqzE-a^n74<1)G_0EJNHy6@@_6iz>iS;K z*hM`tcHKfdqo;Gd)uu0-`b-JFf*AvGBl!_BpM4CPsP!fuBn{}7Sg*`G(Ox$68-8fsdH1H*ks`!t7T$yU+`B3Lnyu7ibMFGh-&~8e6+Jak`d-Mht$@T`q0foA-J#rdN{fLb@=6v?s#Gk-mS2J{Ko^BhuD ze9;3Q^kDzzs=?XhoveKsC2B|Jt;7qN+n-VOtnMmJ{#h}z)|)a5X1XI|$tg(o4LkkR zn?Ix_Zxw96j{XAsfQu6z!-t2_1MK!ce&6GG_Q<@;wRhspUGBe&tnc~5uRg$kJnSux z^90YZl^4|ZH7N8BZ`n(%)KAjmGsp{AbE?*2avj6CHbmb=6^ba_G8%?^KCfrLi?kDf;8}x{gZ9Da7Cp@8l=>`8^ zNAH?5^`Ul1AN27b^pVeAs3&^M`Bz>4tot7GjC(!npPq6(q`6#wSa(nDYM#~cJWYBu z4&u9O)3Qpmumo+~ri>X`TcW&k3lV8iR-=*RGDI6PS;(??X7(5F1NkE3cYWF~1q#dl zv?H>@D65J#;(W4&E+CT`^mn2*~ZIxrGKxu_8s^1c5gpE z=^M}ao=uJN%n5~l+zsBm(OWmspe>@1qU_bb)7kBOOVPT?I*=nhm(3u@(WnFTNTk#a zT>@wR%+_xrk=xLJ_>f1@)95AihGXxd4_(v8wSC>w&;9)&V}HIUI}7#q?0%4`FO=#H zFM4|KTOziXNcS1O>ko?DvNuCCBDKYULosO zv~*t6v;Mk%rMJXT?`zc2tKH9HXX)_^3N};X`bwYm9;Nt_?aI6c<}D=q|F>)4HrTck&h3GL>4z(>Z?Oa! zmoVS0gt>Ah;6^d=_8thAESuXQ;8x>2zsrKMi5J|!`>j>GHSS-n)~oc0Cc9?#mrg$0 z)$}0Q4zg2K(cV?bu~vlyl8qsK^UY~NE7Cun1W(}cyNCdaR&Ew$kjT2MGxPwQu7BWE zXHOPGW^bg9?6|G?u(jB+1>0^Wj%Y$p4rJ-s`Jg)MuM8>5A({(4ABCh3k@tOMeK*U# z4QFn_rN4^K{)}_U9Crb3rnm4^+&vM3wPk(D40IUZ*a+?KdllR-!yW$bdR#A{zEF1?Q2Z}jF{3NgbtrT$6A>FH>m?~B9`%jwG+@x=x$YCF!049=$Ioz*yN zjSs`)x-1|$m@=nxBbhE%l4-Q!NB$%G7G)jE2mHtzO7{X^@FY!rSR8Oax)|7jqp8em zjbX;Y@S>4yIt}$w9mtzB5Qo;+(l~(ssOQ=B_@}ybv97VUx}H$imG#6s_33Z}T+V*D z2lL@gwHGs@mwX{bd%{iR6UHk4DCHl{uMFkyhww9lA|#IZ}AyPURN zLEEn2ORsRBQ5bE`an4WF?y|QdUFarKKZC8Gj5i(W#BsE{CA~OIWYiF^YD2i>@F`74 zcCo|XSYtA#&-ZOQ%F3}3KELz%h4UZ7xSr@ua(x9okDfwLpvO@9T#9Ht*>7-s13cI$ zR!ATI2C>2hl3q{Z>tM?o8n8fV67vY_^)kBvWpER^L*tLNZUmjcJax%B&FKhu)@Yi2x^ zaYY$-mGP9K)#pE}+gLOL4MGDP|AKb)gDrhoa33*3Z+@j0iS~vMedxo-tUM!o$$at+ z`43j};drkmos}dGonr?U|n_t zEv{#v3|~=RnMNO@htUxTwwu@E3}@iND{3@^8@4 zqTR6e>Vw%P9^1&S?0;B@5a#j_)A`>?c=03255tRrc##p}H?`uST9J1v zrD&zjt(2@)VXos#u6fx#8SirRcLTINLE}aw~JIalK%>I zUZBo1)OjNLk0k%W>YSeIPu007PkUCTB&?6)@v6Jq>A}UI4?p^A2a>!l}M>rUxBheH*QkqYATYA7&Fz;?Q$= z@-kog8eMsv#=XwVzDf&UqA$rZZdlt1}0UuBWcdBT6`{3GaobT7Kwu{+USg?45=?aI2&)e%$d zZ?#WtQBSRc7Qdqps!!pW)eFxa#bzd>M6JuvM(^A$-YSdZ)u2&5k$w~QeWa)^`BFN< zs4fM&FQ%V$H@RmYz3pVb4LWb7_d))xesAR`w>!TJ*6rmhONbN7kaK3xR3rJ?^uB@T zHW3FL&i@|`7uxCRK1rX$sos7zJ2;QVTm&C3_mr!>;To27ExBKdbJvplHSqB&NOigL zXAWle5luvXGTi6@rH{eGtY~RO61CuFMbUibN@iY6#x@tC5=tiDo^YZMYwXKr zvU2iE$TdL!?I1Rj**(MYWwbtsaRtoCxW~C_kUhZ8q#vikj?U1qJ={(1!pxs(jMs@P zlMA5?3GGy$`dN-Iq>0E=&;6L_QT4|pfbu;8P(z!lC!O;xGSR!nKj#S$Nk1q?P^$J6}9)32kM$*%Y(Ug;|Q$U46Z=|$#Moxzs6h}hb}yH zkx{TPGgRW~2Nh!R?3B5g2VkccG_Qx8#^hs3~)mLMwHBfEe>zK)2Pg|tEh(EdX8^F-)(b#}=>XX6& z@Ut!r-CyZyLh0%-G#N=Mszq6~$x5iPP-Ro_4!i(F{ZFq4T+qeVWjqXE_pr;*w+4;;b`^ddtc}DgkOfUXS zCCYl~4NA02iOPy$s;}Am{mF z9xaS)pRNC?tI=_@tYK9jZH>OP?Ni>SAGGiYy$%Aa@cBS~6 zQeUY=nF)Wg(jANQ*>$Y0@9CE>=GYULn%Ifc2i^?$h}E zE;O|hP3nj`{6Qx;b`t8UMVLKXy1VXDpVz3_P3R6<_mI!$9DCcjzG$F(lRarVzGR(a z<_hkCgcbOU+I+{sINk!*AJ2n#apWu(dp?hH8QlL9x&DO}|5faBvsU0O#xrjfFWySl zx47$Omi{+$6K){)Yqcb=*3L`|^Fq;OcgS)UeCP@rI_W_?9wxM=4M*y`I+RWvtpAFZ zd)qzUe4kR>Pn#Zq6Zi8c_wio$@sCo#le*q7Yo%B6ziq{~@3pknGIv;19kFw#1A?`yg z@*%eJusGmRp6Lm3!PET1b8zz&dhiCVdr$kc7yr;#8?!%;_I06sdI3zhfX_(#;{r%_ z9&7B*>d)cr&eUQ$l}%;8j}GjrjZ(HiO_e(RQPpW}d9_J&kz9Z)#e4H1*Hm8YN2MJ? zr@vI9FQRABQ+{X6|8Y9>I8FaA>l zqM^Td)(z-p&;1AOztcPJqgC03`g!z*H-4xL{Y4Oi@L)82nnFkBvz%2*vPDEyO#LgW zdrfu7jQ+#bDY-0?q3Bd~Jr`Yy{^a)!j{n^?|90(N?#mv-57O<2>GdO|_6X~J1foC0 zvLE1;?iCr_sh8;AB!3GnxRFL&OGB=tD;Lv~9{Re^f-+r=Z+3zSZTXO+^>sJ5Lc9q- z!Z*{Ecj(HybmcwT_?{T!ZQkikc=4Ku=S5iYj5j|9EACf{qMZS?oP+<-2J~Dc+}ov(;RkxnT{}?GW|M{HSBZhiN~Y$g^kt_8B6( zbI`edpXd1b&YxGng2a#$wZndb9odO-hWlngueo$At46cCNJbTx(aDvpH*KHHhRPh- z_3Uaa|BNnk?n2jeckMauIm^9gdd8V_>o-wv6pR?^_;}}Mx^6LzSWkD7votH@tKd*wXxvzz^%3HV zHY~Iw#6Fn>&g56l)iSt%R4&$jz6`=&F0Q;>8~QS+eNs7UfAMQB)YiOARC^^|xJD_m zKg(Z~^m_8XLEHNVvDx)J-CwlV5-DG!rdNs2FXs_2K}CHM;XpYNcscwkgHstH${hEs zh)hQPWW6j#0(+G)S;hB|TUK}PCiUH9ncd6xkZWc;CWFyVEvg-4orq-%+U)xV`nE>Q zl2NoJyu?BpF&9GoQb4Dbo{-$CJH$1aMOA^_XV2q$J`eW0IUPS5H{0TN_L1t${=2H@ zX{2*HYdoD*o=$I0;n`1C)|2Q=M~)hFQ`tMKk+>t@YV#>s8J%68lL@q> zEB8X%Cml8kiSP?AaZgl|acOOalGrR~Q@Wd2-V zHRxMtW2MhOyQ*Y{&nE57HKN94c(PCgIfsW#pIq`{WbN-{{Kzc!Ni1)&JCa*$nmD3p zc1U$~t_yVr7WUIErdbAA9!`BX%axzq2l+BZq-Rqc?M zl=6Ay%s0)C%?dv#dxxY zRwd(P=IHE$^NC^VqoydcBr_&)l5=PHj8$F=fv-V-MYr(ax1qZW$h?GKu;VFSJ&B%S z&Ck-6=h(&bYWWQj|Xw!UOc-Uul|ll*W<}mH02`k$2qX2tJpNVGq%Rn z=5XX7N9u||sySPpS1BpN&CW@im1rHST#go^*=U+$lk`oEEktoyHGK|#o-V5IqG#X) z`jfqWvO+F%Ng8Ph)+w~-iuR5w4()fauit1x_O4tCIT!e!(Z1<8JO$S$lfp#!F#&#z zC$VuPH~|_>q$`ONrofUJg}CsL0xuW(z0~nl&L^9|Cf9G{D|WHHV(OCI=cW0pWJ0Xq zj;yWPS1)Js+GO9As)as;H+(*a9&`LY=WqA+TY2`sx#xQKUPp8POh>L&y6fo34RqvY zrTr%zxr>w^6kld+^JUup9v%4&zzDJ(MnaiKl1y)dVdi&uFi>>#9sN%qME0p31Tlv=I#kc~ z4TdMJ8*%(5`fv-${nOiSC*OPF zY1$zFCHLo*=5-kIK8@=u7XL~KGy8Y6@5yYDoadRxx12U6_hr^+@51|H>R$?SWCW-@ z?EK%(GnE}J+Ic1!Y@$V3H?~y?Hq(I(>Y05FRx4H3mn}m}(Gohb_z%kW&{DJlt>#nK z(U+_S%v!+RC^=R$8*5+XsR{3s1*0+DZH`)@tn5C?vD2MD*R>Zb$yG4%I{N%KWxiE; z|6Pa=Ytn(tnbdy@4et~g+)ktZDQ>u#jbBf*uBI;+v-opq!D;OMM1R>G>?m|79Xilw z`YfwDUf%gquG`H|Y=y$fZ@7jp$&9jO>Y692n}xGQ8DI}$l?_;E9o()#ACfaF>$Zz> z$_!M`Z`s8FHZTB24#1&-eC2m+eX#f;JyOGILs8$BHyl_}rae zz=yBt!~jnl#NvkHVe$)(A&p?g6iA*d>lyP|jHk;{qSWkZ{X4zM?2%pgo!!}r7f@jr zS#EJ;gF9C`n~cb53;m47q94#ePt5+aAGt0*;x$rx-dmp}$45x=0j2p5d%ufx?;zzn zNc&EH;BL}SK7)c;G=D4GDMq`>!}gjq zt$`9AR?vyxNh9q_eH&tz^VH%}y6`8~dxOaD@37-veBxbD{6FyHA^7naz4$NbJ`LfY zWA!hJ6<&n+FVT;e`GA+{$V?D*OOV5HXX{&ZniZ-IK7Wyy_K@A~K9kieC6;UFzV(!|ZEt;9n>&2m~#HLI6jfF60 zu1IGVO`lPSN!pO)QDkr!I!F{)*YD(%$%>7VuG>X>HsjS=T+3cn3ux9XN2bwzDH{!jG&2HJlsx(hw*EzhC19RJ8Q->{7l-af^>^Zj3=eA|_+6s}dpy{uCRg47 zRsO7%d4;QzvGQD+bQ%wHB5i7mTIzQ?3>mF~75Br6`@HQxH1HmlbvIkOgKYmzw*Q2M zH!I~|$@V%jyP9k-CByT{@*HwKm0Xjz^b}Cp(x*74G zj0dAgV-RU%C!LR2Ng}YA(YY;6XBfL_F{~gnH6j zo`d}_(X2Q4fs97=LfJ3%OUJV!BJ&RuC61>jKYK=c%;vJmqE*G&b#9|KXQ!fN{CQ?x z&r^b5V8Jw>Klwe;@$pKQHOfD->d_Evl)8+fE2GuqM>QIwW|{Rff$mKr-=EcSns2|j zd#<}P;=hCrWR_4?J7=%Y?I^kNO48wdP!+nK%zgF5M~&!ma&jLovTv!(#}+*If9q1x=zFqwt20f_8q)Uk;aE{_YY5!}H?t#7 z)~#orT=GKIMj5}Wj52??h~snFd)9Mjhq~->ll^WM@re@uSh%w;c%Dprc@7t^Awy_ivGHwS6T^M{!$wU<{*7OCv` zwOaq=5_UdM59hbNQGa{LA@x zkaZ~+^DXf!mqPAK`IO5Fcv9VGZFGQRjZhQ%au|I%oDLmDmyU)ei7t<)6&+|oCvoga zwB}^ma|-=QcGEMQ%`O~gi(=EW(ajUi)!UJ*h&^z(hc{%8=f2B5E@ctx{?WA@cQTnTnb?j*8TBvDrxOAwRW_PlzYpq9q z`>SP5kyJH)s*3;nvV@|s;sd??AQI1*an{@%;)>+sYocTayY4`GQO_N z2JM9~+tF|2zq-&{Rkfhg(@7NaF zc$ob^L?<59>+m2sKSUoM5eq#|i=LtpFTlXpXw$nqQXiDr=im7q?=i{wUyK3&%44mi zFI(tJ);5+C)x-~Ggf-c0o6wEs5W1yE{}`NVi=!QIx0AT@f2+Mai!YMRyo;Kiq{f}J zNjgG+c3R)Zk$r1&Z&CQw34glq|EFn7W)@AdPxipy^I6q}sMHQ{E z9zbe?_<-aE7)hGrNH$(-2L5G7g(a+gHBHKXcSYIj>cWm7MXdsAR4-^q_DiVYjnYS&2PRo=axk8f7KO@&`m3E_&Z{~lt z@ebK5HT?^v*mpT)tH=jbrEN8FG*Nlhls3Yl#x(CxI?!C5j)X|9P-}E7I^MDNIN4EM zPoN`3tFeFdnST514LjBM?65emfaV>QrX%cZN2158$#FFQ81Y9dxOpV0r#Gr8E@c0& z1Ju1%!D1TW%t1Jm`X8+B2Z?MO(YOZev_79ux4_w-)jRo45~YoV+QZc>EA)r*?%$JE zB= z=tg$+8;OhAS7j{yn25(&IhEPYvkRW*f3wlIve&KjYn$?H7d>uQ#vRHV=_gnGP8`mf zrk%K&Ju!FV+%7u23vTTa^X`VSyXpIG`oCLW$}XQfQDWom^nV-u-$M7(>e~p@)_ca9 z!l=@b^dLL3G=OV0Njn+&OXJxd+PzghHsHca{$L3dS>XR1zp_KbbmdQMob29Hl{mA9 z(i1jQiL;N#TrIizO1xN!mwMYuGF~GhPppug)qm$3w(04KUr1?F>OE$v>@^Rnm{>8h zS`yu-EmE96D9O^(dz4+50WZva*5y1q92O>bXSx>nR~FHf(^DM1+}%vyo;uq0FYUOd=C zrn|{E8HRV_QP#8WfDJovW(V)PgT%9A+D_8nDZ0wuD!b`XXpisNS!XX@%B-@zG@}>} z7H4h6$)O|^D1~cT>r{?4RKTt5;hGG<)s?3xqfBY~pDbP3+dMfGO2N(IYF$iZy_@AF zPfs!gY{dQKdt1fYmK7{5`J6lQA;%Xy_;eQgGyF@;oXpl^>BEoEXcTnHYVqW99j<)I zfHfRKj8OZLYCl@-$Ey8Ay74n@nTZ2)>2}8dR~6!?>^L&doeQ97vYIZTWh)A8jG$B6 zpDkEyYuegY%{uU$oz$c=dq0^xyOKrbQ=d#$CzDVYHh&U{oxs-H^9pU%`&h9;E7EP@ ze{){r5J$5AQUkuDu9%@FuTsq&*$Z+Xlzc_WTeJ%zZN-D+y9?s1gf>gq!~(v3HqD)G zgeC383B1{lFm!}c55r*K?iWR%FXC!OkY8eRFXQrSp8O^pc^7@a z20s$*#Rq>w5<_50R=Q;0we)pn@8#@Xy%z5?b1uC?CDnHylzq5r_}m}W$Mx(@aWMLS z`%ok^#TN9th<`Im&(UVq{5xK3fkWF|8y}N6HLLDQLE>_}Qze#@Js-1^SJo0YcC;B; z9z~|fGT)wkpXjY!z4Z*5nbmSd7?62!TOrjp7_bcnWG>`R<2A`tzE|H=F|Ga*FrhTc zYX0&tA?u>5!h`A}fm$MhI`E)AJZMN?8q=RcNIaSDk0`_v$Iy*dbmK@RX-+?y(vO4D zfvBD0v(tjvdgNs31+oS5MxXbosIX-@w6-Z7L8M)vCya}d+J*6+KiIxB{SIfK~)M7 zM^WppAursR-5(;NFUqr!oTJILFbAbgHJ?{s=-gtm&p2(eQ7tRP61DK78oS9p5oN>} z#qnr2ecZ-tY@&Ev5St&9$|slEU0{I#Vo z$I+uU^riJ5sB@I^9YLd-(fp=#@t}fF$?m2V=vEo{ll{h%iF+$4Wardmd(K?7<@od~ zOI=v-vP)JL9XQ~}z0N>(@hu-VK&`$}tFP7S>jF9@BjaMn=fT2RG&K=YGU`p{MOL#;%xS(SWP zAwJ#3kL=(Dx8Y;@i8r&GO=5-L*vuyOlAY!@li5~s%-H4*d`ZNRNHsZRN@%5&)}AP1 zlrdSFlC>*yvC9|c*1hN0yY%dB=ie4Vyyg1joPL{hvhMGF68g|{`*?n4G=1h>$tyb0 z`@hHYk*qdzk}~^Y21>5ns*>yWJE)xgsrN6Q8|Nn7|U!BYD3+drn z{J>FEVPLxt!%hrRj4|&ddE#=4l*+vNLM(e;(Jg&AX0ZMc(P3j} z8@W2WtkosA{mHK;U8x2aBGdFmC4+UwFw+xS4yFBB+Go~&mxeE;T$7&C66Bw3PQ^VZ z`3H)5cF}H|hZKBf_V3(DlXuYQ#QKT(vtLDWu!aY}k^e?fzy`X%o}I7bQ8GttE!nJP zIoV5fEsI)5ZtFxG>&S4uHu-vSMG^HY99vh25592i*LeIb8szDjA2}RlHRL$QCiyJd zYkL)0uOgvUB(uuVqMg*U|KWDD2NlQZQc$}boUef6m7sSc89n>pK$_mk0BdYjhvFn0!72GZ&5@0StvuRS@L zA-=)6Z+YZFa9}7)8V(DR#cdM3NQV5`^dh54t3+|h47P(_BpQw-R)Gz*P$I)7sJY`= zf76D3v?rxb5WNfiIEA&IPCw3c#aXoE9GITI$!>I{+yBScn?F}|W%-^OK>{HmBq7j% zMkFzadB)6mwlke+JF~Ltz3i7&l|#Rbj`uoVclBS=ucLqIj*jk#dX<^Zp)&2V(*t0@ zfEkUL#2`RILITb6gw&tUS=_=|9nmNj_ug~QJ!hZ2)?Rz>HGCJk;d`uj-a$^ji!{BH ztUj!WQOsv)8C3YMuixSQ+t6E(e(=|!S0Sw)hoSu(e~FguLdLctW7cz7i;P+Cv<7)H zvZRzg)QXr-4=zGqm=%#p|Le_p#)z4LV^fT&amJRBb)!hnFdBCVj=IwvLXxy(4xv%R zvy2=&cW|y2gT^?n#+l^2DSk7P%pjZ@hBNYhgtfp3 z?F=gBcro7>(avRX+-i^c{63FSo6GgG8Ob8F&vBn=?l;DL2Wf-RZ{{-VnJ^~K9M((F zIo_XoKI?tTc^`AWXq8qH`e&r}pZV1v=!-uh`+wvKKjdkQ!2OKYpWtq%=(%&W;4-Zd z<=JY=)|(U6NL0E}XqsJ58q%=<-JeI#TZP=pmhl@iCl_&@Y_6%V*sKF?jlBpku=5hXfdP59ryBts(G~vgP&I=-xV>k+=*NJ z%3l`nw}k8Dp>Y>;-Np3yV(ycdXd0!@`@5brBBukMK7fz+m@96h6K*nBxS}=FsRdnd zl^yR@IA)BLSZdeUliYwKdJ6CGxsBuPaHfm%o^XCY^UDY;;R$wwv+RJ<&;yyYcp*~Z z+Ri*6E&3%q?F!_foUtl8h7tdD@Yv{)2Cmk~RgE@WOUu@BC+jAyrFCn#d(`T+Xz&1vXM zjvQxI^#$}9^eOb`gs$*ugb1+ZVPim3v+|sBk=^RMRXncu|C%gB<&_rrBBLs zC{1^fB)uQ^k%ot~S)1DdTWKm@1g^Yn|+V>KV)bBBj%02VVD2!>G2=avt~E^geUk3vTa`8PiU>#mp`VJ z|Be=lCMSB(`^e?@Xv=qy0`obo=dG1`AG5_S^!GMew~-lS9n=8Tvd&lqiBwj?_l1zv zw9OvN;fWTo9rRq1NtTNgEpkI z%-G>Ms;0N=;Y=fPy_ON$Opm+2)KX~0rmgh+R&?4{G|Lv`W;0yagzN+&azA6GpV_{j zabg5RKjWmIYxmPP{fQ=6gd~WNosO)Di#>(@8)KF;KFXb`$R9n-n%0!-64h;h$ol^7A$zRE>P^Py$T2FrOZ;p`UVU<T!6qXdY}_AlJ^&)oNO z=A08qf|>hgSY3Qgzh8o`LO0-oyG^TQyZY~e`k+zxGRa(R%xosTo5S<(2fu zD#n7o4XXl+6KmDrZS=ZW;(O?Mt-br{{ez6igN&_%=;Q;8%ze;HaAOZ-)x_=4W=87< z`rmr(jf~nlxKz!^^V3Sks`g>IvJ5_IL$;dU5-b3DtlAc{id)Q>U(DDSK`aklumn3o zK0Ckys4&?H8h8Cy-1&X(_I>2wyKwwX?(iyiIKqAR^9*~CgYC3sGk0p{S(^A%kMvX} z(&H{=Csz|ke+P209i6a^e%M0axHi~;B(6s$M0jmL<5~4keQOQw62{O{bg&go7C;eM zAlmCgw8H~5!vo$ufb;hm%OZqW_x&F8!X0Fw6@K1iPk5dEVGDM~Z}1*o#^!Vpo7H(D zNX`+ldIr1IY3LMj5htN9Id+2Yj$@}hj{WjD-sj`&F^}OdJchg+Lq3in8^<}zn5-|5 zq%XM37qr0W?k|wpW3*2n%yHI($61l+N&FIhcM`cj#q*tJEPTZ?pW*pMMg5w!lBgBN zd3?hxeJ!zJi|M?FIcO*SuszWsR)HH#?3UUq>yRUBFs`Iui|OBdxSg9Espp$@$(R-s ze2lfh2&*`w6^Z3X`@F~K`w?WWx^u$M&%Qu0?w%X|F2!=Ca@ zY!a_>hiA`bM5QBHX*`4Y?dry1#%3Rq;Vj>Q1Xy>;I#gPlTr-?QPn>4# zA4ea2jt%+~?2sQ~$NnQW$v+@7zs1Jz0U2<==JnTn{uRf6#rMBLrhbLZ;n)2AhO>Ty ztbM?&@muuI@3{8wk*Gg#^?ycR{E@r;32FS0yMIJ0K04bleNtd|*P5G_2)U8cC>9Q)S{WGshXUIGt_ z&?}yy?`{>8Fu|>QJZx6F}cnQhYyRwln&_oN3 zEwR$AsH*B&_vq=IGm8eENLCVQ$az?C)F8$u-QxIlzQ4j*7wG-3k(ICL!IS8C>(Drg zwJm%?j~I>lA-xlro$kquZtH@}-Ml{GwU^fcUWa%ch2#3kr}&K#ozJ+&Jl9IYr|iDX zZ0j^ezxXR@tk>p|!g)AkR^>A!b2i!2h*PNtt}~h2tu#5q9cJLK^#P5Ho#mZR7`BK^ zMr$@Cr*atu)@*Qhu>`J(d+TmvN%DU_?+Q4wly9u%u#7W{_=O%gqcWDm@6u$Z(yEj4 zsLY~mv`;=`CZA)4GmqvrBr}&Ds(nbvJ|yHNW~4o=36+lRjP@=3 zZQz|%7#sPvp1G|GzE-kUE`!I*nVCuwyTV&s?JdUm+gw?k%eT18TeS8~p79NyHP+eI zy`N%bJkCyM6iqjTd_;9bt!P?JHn6^M-QIu>4LiT9p@K^_he)$Qn;*}kPPV)IIug0rg zx>T}o$TA^_s*|m!77;>&Z!}X&*;0$ZT|1DN3qGx==9WF5|pHF6~ z9jtiwF=HKpUWdNJ=Xa6eAM*X*aNdtO|0lHa@7Qzvlso;58T@DT{LkUW&zOULhAjOQ zdHFl!?k8|Isu@&ZX|6>>G&0JYnKib;-Can*OK6dUNX8N5>J?<{b@Yk$tGCcLZ*#WU zq2FPp<8I|UXd}^8-a?MGZHguFDpIb;^)NDX0GWA-J6mr@Kl0W@dR>>!qCfQeS;u~Y zy~!x+Gi^Kr^jR-`){W+~maBbskMY*Z$QH}#D*U=c51vOeoP~#{k%})l<2YRW0`7gz z+VC^Z{1m+(q)($IA)_kvX&mRgFFF4-5^)A;vMS&OBrbIJljI)P&?v4lZ!q3(asS(l zcK4@um;>&jncb(#*SoAS%=o>Nkg?g;HqYU80o)W{M9cs+-VAp)@^6IJ^mC6M=n=D` zzHX}!24<3Z<}EzgHRSy|>l{}%uB(;lyXdt0&_n1EpSzg7dU)bqo@(IvI(d}ePjEFO z2cFR{VQqUtORU~HN$cj2vn=$_Vt7^v?@HleCAz;3jUg^hGw-%QR+87RxgR4f! zuCJpr-sBge56Szt(PMAJ_qVvB6$eBrGOO6gto`T)>p5BL$68C)k84C1xX!GEzbld* z(j(>$?Re^g$MlzVeV_0Iz0A*6GaY~zgY=oZ605b2@YJJ>T`g2&$iZkrdH{@cbuKkK zIBI?y{hFdd&KrhCAb&dvStV6#wDXi!P%|fTS+nIaZVKoxZBRu#jhL{@k!1aED;P(v z{Pa@mpI(XNub>x*U`)CSjnzqMX%@e z1$FTL5oC>X@zlDL-V>Q}Wemz=&eQ%YjzK&00G_wO0apiMQJiPo%`pa^F&1VLX}$ut zE+g5O;oN07Xzbi&WWq{ySK#3lc=`>a;wp1@3!~&3vVWZsbAyp{lM!SFtUgfnzmX;P zAW;R}zqIqIgHKi=>f)~(>FYsitP8APs-N+z9bk~B9;OFI&{cX3MX$Bi{W$VHj)tB< zLr-B01v-@>{702hCZ4*6@I zI$AkH%!S!lt_CvE_zTeXu0^zAL>2jFWVneo#*Ser?F=hyA+LEH7Z=_frZhf1qi?4f zdHOI%61g#3I|F%fePGVNaS~H#>v4E!9J~5?0Dkwu`=^QSJ`eBC!o|~^eF80MzGrH+ zU>zz~2-=%|ha`Le{f4WEr6889^xru4PkjGRocAka;@8~mH(bXERbz3){``ph{h51z zPCHH@8^&Ipqn&2=h#qq-ne$8N=`woUSnFCi;NDua!e)AL6Jug4J*g#lC%w6wXWj$P z_ri0d6s`2Qml3ub+L`d){rVjIpJxQi<#}W;joDXUj*;N@pX<+g+F`!%JpE)nKmB9r zjAOIFGP!#eBX0q%ac0ed7D0I&6JKa4=PcvgVt!+um$lc+xw{sVYV=MWd~HAJ#n zNZls1)K=QCgV|s=GPyUIDOR&`XvB8e#Eh{fp+meLgv{&ZJe_aO}3Wb)bm?y zvHC^Se2dYV+1$ffYBJHp`Uul#rK|EWxT2lxyk2S9B*ZQZRH$eEpI0)3GG>~8&5-LxWhR}EW?XP zg!QkkA{8P$+(IghVQiyC?dS>To+n7BF|T7t$5gUU2)-<2lq^Y}&D>wduxOe_aOXYm zStC*l*k>28+FD8v1#RN>|DUWyu`Jo6Z-z6Qx%y^$ax-_>Lf>vl?1ZsT+)STu;_4gu zeBhJQvdtz)KN3xAr}H#EYl2F7wde5-?M;Ab_o3aWs#4wOMFI7jQ)a?UT|cSZbO ztg%9_x0KaG0XvO+?yiSxF?U+T9dfvO%wtvbMmh6_5^O$00US5?&FXhqa9%I=9D06+ z9-X9Tjr|#>r=8cWh4d8OcOwnqfa;MHa505iTY2aEebS;N&ql zYArh}*nI+h#QQ%%f8~$*3-IL9BStOPgK2xa!D2tL8H53Za$Acs1nAYsIA<=!usg>*~G= zy#>7ky$k(-QTjvZuORc(eD(ADynh#Z2kmbKkJp*2UPiwhq9?7fXsquxdfIv=>*2y0 zq|vN&y&%Dk56{cz_f{Cxv@2Of)g|JTrup}*zWPobYf=57B+&i^G+ z_e*~NOE~*WRxkgNYyN_3{~Q_mDfjt1?(h@t`D3IY@FSYoe>7o*TEqTh4J*hs%q30S z(|9{~sP0`?BSZDcs#Gk2x}>dD+^LdvQ#q2h0_iH@dE80sEzIXR^=B+Zi)GOVMn`Ga z4xgsc5JsTfXPn-JC(iyi;oLReeZ#v;@a%jdH5VEEMrP~VIE}(<2VxY1rqZ) z67v~SU|lNhPSR&cj`Rg|3_8KFlaT(8udz-TVRwaH>NPAIt%=lpinM6EJWdNmMLdo6 zI7?f_cDP8}ukZ{lJj->S$vvsqNO#fHX6fH&Rcf4d2P?Bq=rI&Xan`XY;kk>D1v7^V zkb@=2!6N3F7-7@Q4B9Y^=G0m~&wOSbP@|Fcc4YADOr%Z9N=STNS979~nWa94^tzwW zcRJ4;k^wJ_P17Q}6p1K763UUNYP8&H__hXa8YQEhdIwUtn|a_RbcGRB2l@XHe}~z1 z9%1)zgq_9_WI;O2`$HT%$fpCznfsF8?cw*kxZ)13xs~g0;$F>2qrR|)WW}6`{Au^M z>W?ucy=WM#rx|}Bj>cV{yOr^1WRjJRTar1&*tUC+@dDR5X4JqH&WTy370%p&m+l%L zB;))XV?3fGA9K8$@7gA&*Fw*dC$NgBSZT(RrZG0=7-_TU=pm*MJV&bY+c zm$;Afz$NZ0#>i#3YAjsHfjJLj$XLYY-J(^uX`RuNZH&hUPzSBkxu`l(^pp9jw&WKyw=0P2F{LMqt#-yzgRPI1{+MoY-`J{g3J+d z^`}kO-EZjAIix30DwMrB+Wszb^e%GreV*YRIQ(7Y>1|}-EuQBMxc(Y)@d|VV z8F5XrA9@Me4efxo@SU=^j&mFNO&wGPmGQS6a*dgnNU#XdD`;6MPf<#bE~jTp=;0E2 zx|klKtqt;&oO8cc@vk!4DvMg92r>uvhiIlWS&@r z?J=TnuG8L|wB1^ncX^gJp2-^Y9lY;^jI`-V)*bnbi#(n=mobu!_RnCvI1|ipr*ZD2 z&D$vK9*#cZj`um^4m#o{XFDrhrWY=7=X3PRS6t~dy>yb<#=3xC(hBJ$cM@IKT;(&+ z*U$y%GSmWTWxWlxLG856UVlo<2WaUCt)7HCvsjAekxC7E04YNl$Q5E5sfEGMdU9(fd3D80;SJro({B2CoR_k*h8zWE!t0uhiL66I%7QX zOmF4-+tA=zJ+{+&$(8F?$fuEfvJITm%(?661*06A;8r8Ov>K^4QmU5w)G&gpY0oO; zs{+oIVdpRsss!#WW5g6Na`F;6z62LsammRGaLlR!&fyoB2`(n<`8GJ+3g?W8Qrnvk zf1Xi(jw4?&>QABhPckpKdpyo6;W)ZnkHr_v9bcf&k8u_0IC}jA$yj^A78&@e}#69FjyGN`F#AtCxpG}V$>%Ew#7NJKyT}*En+bLphS0T&B zeOl?Tp4Gr=_}9p6(S)3@h1S8<^=M*o&^IxoZ)Q|&g|fC(hZ!?&U!ZU}tj4N6nvM%|>0$O6^ zig7Rtxzj@SlnYt!_EAr7`# zTIsAF)0iusVOg1Ghc$r}#t2CxAherm^9-3W0-_w5DNSUz4H>yhPv1t)ThV{|FK;3v zM!1T_p%p4nO7(Mh!vA(Sr(LlP8MucG7&m2h?;X|=clb7ZPwreFKPUZAX1Gb_0rhef zai=0`K5FuqD?AL1ARlAUI1)GsO+&MMo`Xbr%s{KEopX@z#ZZ1C>DLm8U&;GQs2VbE z$|z?e8aFZ`#M|7CE)s`#4^R6NbKE{g&3<}eKO@)bkoy?5dl~J<-&reJ1RyKTucJ53 z`Km<@D-&&PjB%_QbLshP$kjt+JqDtUnpISrK4w)haGpHZc0IJTo4)Tsl6&C86R!A_ z@!N~u=%eKWjA`xCL(m8`%CRv>jX1%1lbr7=W;&sAUROS+6}*=6o!Js4oLj>0iuipI zGOCIJ-KKbLaL- zXHGG?$I%KSka0|Xc%97aehhU&?U3H9Hb_5ZYob9;BTZj1FPw$fU&CATz{GgH%KygQ zwetQh#~*OS*ybLzPd~CUjO>U)I*X*~^~p-KvDr~!*)#8~oF1#F@&}K&SYNaq2s)AW+6?1$< zKNu0YiBDV5JKMOAe(oLI(`46?$MZOQ+ z#-e<6Ji|9yvo=kDi!bG&wO zjctkiC|lM}SkH4b@?7munct6JpDY26im|ZWAwjh<^%Af z7rvPH-o>cxfL9`OYP-A(-DXtZf^I-sAB>rf|NW$o*l%t^o+W9g__yR$zD;-dEExfz zZNq#Bt=JE^=0olgc*R!dxJj#SAtSA{(7EUil5&?;oBMGOdC~vU#uK$6E!t4jHTv(Y z*VvBSbUa@LK4z3WK~uWF7=-kwj&p1pjc-LG_vo%BT~nG7uZ_Ws^a9SXep+DNsGCPv z*$gobgVbWBjLnrC*9)g#QhjgsaW&efmMg1atn1u_l&(cO*TbC+NRgGBwTy2;g0{j( zbGNo4pIeb6?Gu|JZ4pLTujiOLrh)J4Ij@HEE8)2N$kIeJ#ahM+$BP&R3y}k##@)CX zO7rxOcC}eXm^SQb`q9XbX-1%{xheX6ir$;1S0mH^Hm|K5Gh@bFy+A*AjWG@xiD2BT z>y7YyX|=kUtP+gc?}obgy>T-}YKuaBkL%yzT`Na!@{Ray*Whald^L{ZDjLAbR4rWH z?8qCm^cJm-IQ$G|zHE4EjnqY~r1O}K^U;V)nfnXjY1DFdhc=qbqDfWOf{|MI|;rb)!o2by8#`p6);~cGd#xu`CGt6i+^u=`YH_QKXP#Q9l z4!<*@1;|Ve5~V*npMKW@Y&?~|`ju#`3izU(ss=u(fy8N81OLsf-GJ8L#1XwBMn-Mr z-L~X!2mhOqyNhGH`ED2I?L>ZdB(sPesdnao`I*Q z;O7bW`315iy3(gy&9s9{Mx=|0#U<41RnLUyecIVtxsU z9CHRbM-Q5xdztgBNG4KCE58j?`$fn?F0zo5?D)izFsft>(ylxWH=aT65Yw1791G$Y zMs%Fj3bf2mbG1pXItIT+xQp5FvG$66zqvQD1}x!D%m0F`(Cl|lAgwfgkUq;nNIfwI zO+uobJmXwFq}KY%hBrCLg8PkpxTOuCgt5IMkp=CI8#$_Nayv)$QSL=1jITb3TpWhq zN0E(}IpY;1;@f0~p9qzQIA=fS?&UYTxau~pvx(~|XARuBhI>^Y8DfGIp_B5FkzC|c z{g+NV=aBtrBtm%~MdHKjWe$dwV2oB7h1-$M+rg{3dB$QtNc6GmKJ%{CBxYq!xW@7EQ?gr#?Ei$fMLi8kS__(JLw?tdll4L}+ z(=+$s);)UT4$s=kDAqS%Hf8MiL?Rw$Ri`B}$oJpyy}Q8{`Y0mR%*&fjX6JT#wG)Zb z>fHwoL;6(Jq4SV2JJyvk`)w&LcjT-{W>aHZhUvAz#5&W=NZZV)GY)%31-zE3Z_$F;vtue`&3zC#ba z3B5*d>90Hpy#(#z*cQkfg(j$u^DChh{8qnKKI9HE3rd5YAuVD~iGDPS^!UFqmi;_+ zAN}e4>w2u0XE&mtmmcWl*M0OtA5!W{#jKwJ_@X{}vCb%m-<9xH>$q3}qUvc;U(LvE zVEi^Ric?mVCiF}rBU=lIWajN^v|v4Y$hAogJ+9@>h)3}%RwS}uo{;v>R-USbc3kAi z&eASXrHo2foBs)}{hp`%0QvYeBjulX(tqUn{*h<>N1pp1S*86GPid9f-!hhrmHrSt z@d-NO7`ovUdOUYHG1V0de^<>VS36+1TWEZyXnCltgiyKYcU!%5B+S#n*w@s zDKoE`wab|CwJa4S^KU+yRzIX`^BhRjyKE>6%1o%3*Ptw3J?{4lxJpE4tUx~-jZ)5? zD$&+eT(>5fH;UkCF)dh5OIEN7Sc#rD7RFqya&&(=yOMHRE?SUr5-XV-N|{5JGfxyl zg-`*X^Y|`@bBvHp=P9*!nw2w}*rOVe3nPar7)|CEThC+>y_$v2(!Maop3r!C@p{ac zvYOUI_K)|H7-$iL{)9&O5DoGX{Qein*cbD&jDkH5o#Z>MmSSaGg4|WyfQ*oP0Cn=| z2_(XpdEHa|Mg-anwCw_DF|E;h>Kx+SQ3h2))le-|&+*kz1Lrj*bQOKlf;PMcU59Q! z=IxogdYkX=aGt31ZT#jwzZIdO9T^l)OeERI^oDgkdY+G(UPfsjvKs4AR}!u-qiX6n zBPcwWdaGQ8xE4vH$3@Gw>fS8#hPENcqQ0v!?r21xwY7#5t+Sa|>u~OX)JA*welO?k zLrd=GcL%t`K_pfbYAq#)xPFjm4PsAaLZVblnF$@>_yNw_M?17dMzoi6$5?V7b5G4w z+{BY^;HlTq3-(Gqy;Mz)RU~tr6)(P~_rHP*U!o_>ko*K4@ge%{4{+)Obi=RUn!4ft zK@&M#kNrse)u)X8 zC+L74bU_a~r3d}cgPwQ-iTo+`LH+2EeqK{^#}MZZqeEWIA4j>qxd{6pYf5aVS7SuU z*-_+a6j@U9jv_O6>@_Q^Vv^4U>yE z1b);|QYGTQGE4ri(0^s5|8LO$4p}+k|AhW;W{3aAZ218@#(zf7e9XLJw2C-{=6@Oo z*UC)tfcYhIDXjNt?Cy0WNBi?tW;NH3=0;qh|F0kq)^53iJX}E@uD~br?ykbKtBj}? z=o%yH26U76tS3kT2Y&|{p`%<&kMj(2X2tak*6z;8`XG$2Tf*9Y zDSc_wfQVx5(u~b6<&*1i(E;>MSVwIIocl}Cx)l~K@iL9i_IDBEEuO~^|ZNHUSVh6j0U15uL6@N`92e$w=sm~ z3y5hqg#;T_I?J#8rBOi{NSRnNuE%rew?$AMvrs;gw+y|fJ$MDWxdQFvTHZCiYkTV< zm{Ge8N!*2&P`4j|7e^SYufP{;biTk)jRO)dr0K>&<0j${K$LP!B^yQcI8r^-F~GuGTwyi?B(BOAuViCPp{3=vBm`Tpkoi$tS^#JK9k zjJw60mq#nKfQoFhgqg%FNn>4?^2A|HUxYR(pa;zDR40|e`s_AERbfe?-7TjeVi3lPt@fBV#@EQ?O9q`-y|IUQY@%lBt(^q~Oy2=%9Br`%c z^HMiwb|-%#f0?DDMf@4pOGgS9GTQPOyJAo;N8gvB`>Pn=qJlRv=G~!hL>Fvf6|#+8 z`%d;7yIJ|{f%dWo*_*stz0J=aKf?&L9`QNOKaWgYWL~&}ELjCmT$EPi;2!sQz`e{t z(wY>gGx^BDVtBtWnPHu0t-+PgkwTXYeH`<< zK1jc1_$+IYxH_(9)J6l>Z{j{{83pU%o`_9b;O2IuV;54fHyQI~>=(?^tVWO2;s>us zk2Ii1n$RB+Kl>h6klyF>d&x2L`b5AtTTk48Rs6P$Ym}l#iqIhi=nwC;kUdNm66VO8 z<+&!{f(T3l$g#36w#6fK#C^uP2)?4k=qb>1AWG|HB*b`YqaiNx=@L>PZsZj-$W^4{ z8d~HgTEuAjdyt3`=4kXl{d_07gRwGV+Rh_YdQnC1GOEZLCt?K_!_Vc=N_bliRlw&} z@V<)Gsr7xtQ>|eo?0&Tdn`U)F$2dn+uVeh?7{9fy?lIPE$Kj@RQ^cZmb^BFvPwS~o z(|Y~tq6PNxls!DHqu?P=Z&gQYZYbAU(7&Pe)(pFVM4#hVuDQbRYj(I5q_UtKTDzE* z=kpZHkYZ7zS2CMbGLzLHk)rfi`==S{7a?;yQYj+*J|y!XeQ#ZA(VpHwhTeiF-$m}e zha7z$9$7i>+w^^8^Bw3r9Dj@N-#|`ZUFEs1(sS+xThNd046iYdUZ)4OLk0TR zIFcc)pKwa<3J%}&Ows0AHp0yE}1;AaQC6{op_vF?1) z$@u7Gd~_vqf^pH-2obm3b;Z|=E+Zi?p-;4H8V{|k80{pgqgm3TK@KrXj6+7Mr=uy2 zV9raP(pV?8)hxW2MvEHDFwQ(7?wLq^{mcVOyA`s#kbZglkdbsBx`(~;4x^(Lx(VHY zu5;`-JiBCR&qKJF2+KDyV|@Msm3_DrH7@m#Gb_ znBNtnx3zESYYH#C7$kb&y^r4aVy;z!_BXy^1v7%x7mdx4{t{bDnRqw!FdG@&-EfI`VRz zIVC*cTB+Ljz8#6t>lu|*jC~PjP~6$OjI?`<#e0m&HZ+!Ni>PY2oY$y|a2MIR%NV}H zQ`}}Axs{L>mTLHJd{i}0Vy?e={Z%~Cszht+Ww?w6k2*l16-B&@+zj);y{|h6Jrzc8 z2d1LP9s{&7@S{d}r?pzE^#G%*pZ@CSSH@Cmi|$9tQ?_U$k_TzE(L0fgYs`}oJPl|$ ztt)2JizBs#5n`O=4*0bjdD@H2n0>z=9v(<2n|-7dHmL*2cNy#{MHZb$x}Py8%rJu7 zO^X~j%$(rr`3d*yq8HlfwKksqHau5PeS_Y*n9Oa)|5^oM0W)matpCi@e~b)#3|Btl z>8)Y$5o?H#80#PN^dIxoe};R3Bl-k>ce9_g0+A7s=294U`IvQt^KB1YH!`jdX|c|- zILGFGPa#ppD9>{woi<0_f)NJkNU*yLvrHGG3k#5JGv3|78wb9MV>NtV&v_9kki$yI z2*fN#hX@Km<^&13l0at`n%_?ig0Wkri-IgctKOmea>-ZtspW%u|@QA7;SS4sSyt*=954L62JBZiDw&8J65E)ot$qLkG4;}wozj(JhvmlUNSqZHtcM}mo4GjF_ufta?`3Y#v$h`z)CYBlv2X-QJPLPShF`BF zvQ@-tx)>^fmUFB$**hNP^$2trI>_;qJ!&_<5$A3z*KiMORJlIUI_|ZKdzQiT<+Nid zqaY70lFKN{qRrZ_pYhaYWR0WSN9fJLWL6LK-fnuS6W%KW59nPHD6I){n{#g^M~t9* z%o$G;K1QEU(8n`;j_PX0KipteHG20pEx60fb)VL>({r7S(WoHe*mj3rORv>2?&}%< z=0>@$(;k%~YgM>&uct@Ny{P598qTlgcUAPdRsm6ft-7SIY9&u-e&%vGSIj+&phDK7 z1<(>me7#(bEo8lr&3PGc)r>DOP@LIZH;Cpv!v7*rn$7B-Df0QX21s<+4bT>7JG6^qd!hZ%A)@o{Svu3Wy~n%f;#i&CZpdSim?XbJDYo?H8PLZ zM?O_6e9~_at4C3~oIhq0>*ANpA;%e4#~7np9*@x%l89knLXkT=#L7V&wh_j=I3at~0^R>*~x}WrW0rxISc+*ACDF;39@hN=mg~xr&p#AjV0BigKM$=$2w>!U! z1?a2P%y(iauH*c*{I-eS67NCG+FGt(O^;S`zjAtfMY3{oXBXH~1*|cR$BAh2tH^+{ z`%w)rgE=+q+7D>^Bl<$5(E*;tDl9XM7~|l~=GAhUs_tYKpU7+*_--4t3n`UP`Y4Uo zwpROV(ChH*b@cJ;@a=WR>8tSW6}V&#wL|EF{fuL4xtq&x6z&%0oQ-f(#Mw2Bd*kSg zgRMiu*Fe>f7RyzT)=Tw&1?N<7t_aQUM~n#8TVKg_SFtlc_n%c@zY(Jf+&%%XoqOqxf(1Qv~0Pu8$7H%mMxE0>AybQ_s98^AcHR$5{d zlDT&7)^EWH`RnV=gv4AnN@*#4U7GM)oDlQ%F45u(wAjpnv)t1fPN!(cNk+NeJFzUk zpv_iNuquy;sGrk%cTujE%_Pv4c9y5m8)qf^7D&BlUD$^_Q5PhB>ku>!&2rSpD{*QU zA)y6Gs_}MOK`ZE2wV)Nx0;ASggmGla{lGLkUiSvikgGX#iQ_(vHHMUqoMf=)5a}zE z^-9*ilGd3Ql$p#uQALqp`XgpZ>5Z4 zd%J*9WX59-y_&@+N@qV}M&2wm%?vO>Z@!YnIt{Q$E^s{X`NdgTd6l>>2at;M5f zq=&0UgrQMdHMF3SUU7VHrdNyvFbY6it3!;kqx8rt^vG-UhPZfd(i3md6G550W^zo- zy!2$$y~24B-!hGjX%d@)e(E8v+sBhXp_jEYwWCY!^W@eZ)f#t$cC^5mZ_p#|KQA#V zF47AZk*|yN#3f|Vcuh0etT1iv&kZmk3M{BwoZ%Y6`eOGu_dZvN{D@8@q8lj@`+9(@4|5lD zV$GH}(pX#?cV*%8FGq7#p*ic&Vn%(5aMH}{CP>S=d2!o#-OhR2+1YJJ9=7pYGa22j zTcv0NG9(hTHl#-6A!>gPFpIl}c#^CiV(nlSirHvdT5i!BQCCMxtk^@@@IS~Xp{uml zc_ea6v-q9y`C9XhLe55xvynezWAwx=puO&swJC{hCYG)KWD&*l(D#8%(~RzpIQkOi z0zFXLliW2_@Z^CWx0+wo(jQgK4i$;zsR8XDnYjh*HQZ~*%CHg5&_G{^n5*5ahW;>S zM9h0J%1a>E3;B>yxxFrJ?~wy81+h7ZC#wCZaDvx>*y^r;?Vq#aqg*aFtTP45wy3h6aQ3- zY&UTTjER=4?Ziql;p;Y&S|7e~ zOA8q}DMUtn7G_|JE;mUJj6;#Be}$26W`w&!b1Yh+yU+tjtG7L3kt*c7o*r>Uyn&XB&+cx1H%}+F@&QJUh)RbUIY(JP9c2W(%-E5RLPwxO z96QLB_OU)Ps&FU0WCcns1I=77F#i{FW+rF3axzoi>_#&ijjY#-Xitdq-bGJDhIZ^X z+;f<-6O@Hkb$1!qe9Br6ToxJ8=wb2ihv_j9a3_$0$QYaC+sR}sWG9kq^wcQh#)v9w zctrJ<5?=KwmO|#$X${eq88vC$F%2bDg~V4UG{CF=Qg>wfPQ$-i%{UK>iz{_8^}D#9 z$Ty-IxCb&$!yVNFp3F#d@h{!ATYXfd7kPOTY1e;w0~x>07;53DxmH*Cv%0x4EJ0Sa zw{nHG(#~_nd47A5v&9_yCi&%ku41;YK0@)(dy$YqB*duoN&3saQbQPjXsm;nz=h98 z#!95S5^1l7r*+7}YGlFevNgzq+B8TMl(5e4*|cUMEpzr;jHE9?I;}orjV$XJ6tgzew_xU88C1bBu{vsyWOJ^pWYCBuX$>>O zIzDMv+Q3;G`Nd{_Z9d=@uDq4IY~ha1om-J}ts2G@ZbYiwk2NDLYm@cb6Xwuvq()m@ zClaEaGNi-(c1|K0=B>5CA3aX@=!M8}dz$d^F8tKn>l)hCK^sT!Ll5A4WN_bQwQX#~ zJbk)-D!E5PS9`T^q{u5K{)Ro!rErhlMi{<&XX$#mq!Y(N3abSZkpgO|}~CW~|Z%WI|+uowP>m&;9g_(ZnKbx8sqG-MBiw!n1}Swh{&Xu z+z8vi*fm4Yye+c_$B~n9^!hlmqUBwy+BhTG%2itAtVAA_tQt9AAIBO-jCFt4LB`u6+b(|J$*Q}9Ya3~9T|g~0?lG-Tb(eYU4%~A0 zuQjHX9*G=H5lF3E%XT<#wx#}b;yBhicQEt7& z^U%z$x!tE^C47%4QELFXW{Em*?g>PGv*zj)(knW-XqJ8O{|Qgt!l=e`7}*Dp)ricGRDqn z5t1tk$&$nlwTmaS4!2b)b|CHB8SPsc>zf(#dMegKYtRD?=md3xS(*N6jnWEb#&*=X zFQ-4s=#Q23hjq5h6fB_y)`) zFSR^;X(dvwKdzGA)b4DJFiBL5T0Wb@r9Imfl{RU$ZyDFpcjt~;yJrD%XRWP8 zNV@i|EOtS$GX4e`Fc#_}Pi$R_v$XXLt@?^qimxl~y762_5uZVl&m!@9=`SMfdS+Zp zU57-gxX0@QNOWfNS{(_lt+mUzE(@>yJ@oH=TJis#JG4#dNn6F!i~ZpaxdCo8!JV~m zX+0XO8BHeQ(MGsw^y4P9oQO0rm$)v;W5sMPxO>@rMnpccvV>6)5k>V#guD0}e&@yXeJ7aLKkCH|$>HF5D9<%jo1*dNp)M?AY$W z>3eYeK7G)RJShcXb#TsBpIU>@jK?0XAhMFV*GB#H!gsB7#Dsdh!qR z^sh34-r$M8!(8z_p7K3(!4J?0f5n{eBS;j_6#ay6LJh2(RnnFfw99%@BB!{|FlXHTgnCURyJt|~H;cRX0Iokw$QqO5 zNR!dn&aloe+T*P9JcY!Gy{!e;4Ek9{$sD83nIx)zx(;&2oiSe8U5 zPv=jBa`9)(OxEJz4qR-*DI|X)nTPUuk^*GQ5oR88I&ZJjSxYmieuJ>G%`wD zk}7^v#hjowqLQnWqXAYjc2aYK$p3{%O97H%g-Ca|uGO^yWgz#lH#R$2eAy?+aKuns zH?JS+<*9sXeY(z@t{I&#MSg1K_)X|K*Km#< zOlA%dg+;0lFJZ(xh&%m^(Sm>=`DP1c(ofku;R1RqA_AB4DbQNQk{8EC3?3sw%zVs+ zoFA=mB~GFdXvRp)GBT!+=&-a{J2a2cA|`;AS*>qY*cL-VED5s;Mw2y9U~7tgDOP_M zWbF(674E{VH`&J1xzoDGC(&x!I7^h?2R!pbq`-I|b2c0cqQaZMD=NRcCwEPbV^=eB zD{vsR(x!4d&Ffswlb7;zfi{%K`)}v6U&49DrY=VE zjI0!UH1-r)Qv)MYoK^8ut;p?;&o-;kMIqJatuJPV=XCEBQ3S3Eof9L9AR-AOD#5wZ z7)eo{q7tt)o!cSnTR3L=AxF$4^o-*o`Pw_mthK?-?-mJ5OkCHJVll0Qzs`_bc>?zc z5ou!GyA{kM<>-FVHmjHiYS>>Gd2XGs7vwR&dxg<-l=F;IKfv#-l_yruHu}>&d^0_^ zhTb)|t(qPSJh;fvwHneAxFZ@u;31f&sjfCdUCpkBPp9?jJvD!j_Se`2`<>+e&GW^{ zHPS~tk`+hQ$ttql+QOIMMQCl#kfFqQ{HF-2g`$S$8)gEh%c?{c4WY@trt2Rm>n(Ml@4pIkU$y zzRl;GMO=RY_i?rDN?X(ct9y^Yje(>mvspc4vMP1ekd|nYHrgE?*?II{io!dqZ7=K% z8LYcpZRo#Rz%>Go(yGQrWowW0J|fx9!9C;ZtbY^rgtagi(Z;CHqD|VUCB4;EkZ8lT zd|StFl-+tHRirRiibn2M^E6fTW+gIJhLo=0PFg!f5GX)y#LTg-jkrj*GJJ7bOSGEY zgj2>HX=%2O#W##&bLEVhz6cL5FupIK|IQ;b=jjKLr_aOd^Zfb(e3vdlmmsq+Ty2Z{ zBnrN@PeP|^wbGWQrOKU~73a*uQELn{rzor1j;wB`JxTA1BgpD=YBJH879lNpJg@nu z5mA{#j~LToUaDkoDoqQZEGY7T#Q?O<>@u#oEE#8F0S&8gaV{GG^9FW3^D&k&KI-f~MKYSVzfBb+ZoVp)~kwwVe!jte4V?%6eIH;ju9* zd1#41ybBbtLfYYaAjX0y_?eKI7{)ur`cXTJ7B6kQIn2x2ezmul9Wo2QL@3d2>zY~r zvnYM8i=)Hp;{_j=1R`9&#&`RE~;F;Zf6mx}T94+98 zYj4-#%C-488N3raOCPc^%agQzod5OK3^8U0_zUT9G;5XXq6Z!!CGGTr^?1XgFZ!Fv zv$vsE`bE0+7i4rq*eb(vX%7EGda;9RbaFLqRPNB8^1l|YkPJOUff+eT-x^t+s)XrI zTm2=@MixDls!kv-p0Vxb8@cLO!Wl*Zi`Hf(F%kbwp4#dmj#TZ{OVAhY2CW6>uE-dAEBMs%EcNg(A|1pO zD?@`tOvGBwSP#!7vB09PL3s7d=ru2=5g8B-!dmnqKh)As)$~~vvS4hSnCE5mUhGQU z(}?n~cCl`b`Ke->%+oVQN|^iL8c&Mog2=fRp-tQSNTPjm=;<7^xaj7&^mvdvxo?xl z`ykJYx_E(%VYGtRpNAZqdK}FuE1;$BxWv{sny-cyu7;X8Q!md(ezgU;-cHNRsSr8q zC3r8Q$^lv@_R~RHc!<$=h+aR)T@P^g{j_o)E!j&;chSNf%tc#~k&TSl^~_UE%=C3o zH8N8HX|)OaW@KWE-=4{D+^-u$DYCtuP)C;dj3elVA-)~rEIo38EM=UsnSIM>jn??( zw9Y7gYaN&!u!<+B;%T&B*UuX?;XMx%x3`KI+=I&I^gQ%CMe3NA*3% zb5y(u+k;W(B7ENFSGUpn=JwvB7ux6zS6*7?!hUb%oEgqG?$}(V@Lt-oK>iD4lt4vs z)>A&sR}1{b9Qwd~w?J$XG2F+1TA z*A73o_PT!L!d-+bmx*L#nX{?AV~iI0{37RQm(k0p?li+@jwhLC>=^YWUaOw1bo5{b zJk3CVier%lW%Jpp1gZ6xy(ErfE*eH`FUL{jwZ<6A<;q%hV~u6M7!_>Ppk8UwD8vi7 z##l1OT(tl5aPTagJPSw9z*!Ny&(QueaQ!ULWBinHQmK00E%ZQS9qI42YGg<97apB1 z`osLA@Q7-O(P|yOPvfPG025i+D$xrV5n;Dn`g{$g^hOoXmAtwmm&8-H&Xv*h?kcoe znWtrxRVwBqkE3D`Qp{&(Hv47r}8n|NyDo_6xod+3XO^uq!A;4oJ>!nirg)vPY_3RgGA z)|lLvx$99{a2Q>B5NX_xKHbZh*~Q4*#+ccRUbP;hHP`APvuE@&Sv$)J^Cij5sD;sW zx-*2CWDygiKU;6<5Ih}VeE0LbeasbME%FL0IaUD~(9Op}o!{)%;Qot8FT3I*p-yUCk)l+Fji5 z>!Z^@Cw}a0q{v8lQKHPa{RTO?0w=A&ei8m&;H(SqJ7t5?4i$9k`Kxv*^X#m)Ze7m1 z{66r;TaXPSibOm&tIzDdK!(x#<8HT~zkeHNs*TsECpG#Mt=48})PlKW&Lhw0yE*!6 zj&U~62u|avUr-ftwF>@KK-wK$m6|`VthrJZV?z6jyA>_=FKULl;x}5s8jTqVm$gkj zL~_NLvOto{qjsG>~HL7r0L0c=cdgcWNv0e;q zoBDpNWOkc=5{p>NZQygIA!}jX?c~WHL$M~G=h|vitzC0mWsWO|zN7~$Z1~#7L{|@= zpEdzeEW`&1&z~`n`HW$G3xN}27Mgk8fvU5B(VvATFf(4{#aZTqDfSrS%n!rN69bS~ zggww>=8i{vZYN{v0q3-_2f54s;|}|k+r(wHviE2ucK;Td`xe@|743c->AQn8-erbs zLqZ=g8yH(*rCrgYT)*p+F^0tX*r(Jtw2&5P6*30O{e^g4W*|23c`dYo@3(NC^=iZz zwvy)o=F~%sg~L3>5qM@^SBehvbWx3Cj&sfT6jyXM@!B`@TVp2bxb7FW_~d=|an-q(Gw zm?!Q&_4DcDv%ZkF*od-Kzgq_`>@+pJ)D~T=TyL`^c zV`MGjH(CO%UlMyrq8zM9XqkbPCod3w?HjaawI==)s$7q?AR zcSmMaIdJ_k!Ly0D95GR9^sqNa%vi)gxhuVobhSasRZ!})QMa9Z*9DmkZA_W|-=|21 zF_$9FxSLU{is9!zZi2G{E5>L=_oia)xGpo2QA=y+ReeC}Rz3N0NgeBcBuFHN$TkUk zivBUz>Bd*+Jx}MI9z~_jQR^xolRJq9XI_T2?Dc(TFuO6SebXh`&~o@?Zj5Oors^U;FWF0R=ELNzod_scl1 zfb;XPacb)nQzJXc=`x1c`W)u}Jfg4Mrx_om70Z2q$Wxj0mbO3FOYXgl+A#lK&%bu6 zI;erZ(JJSD!+poM$tr?P$vNVj&+r?g1}3?N*l^BO!;B?uWadL@BQ-PmF?@7fI!0se z=wUpI&G85ceu$>O4;kg5)mf`_keU2uNOyKeRz)a zA-SS9i!n8ShON^Y0FEf*B-}$972*0%3uG!f$i0uzE$)MisS0HE@R6Iz<(v>C60SCj zRPL5&GGUw0rx6Hbfo5ZcA9tmEY(EAK`3mF{0{? z(q^*?QdO&`xI^H#Y6tYrwjg{??sUwLT8w1tS=3`|W=PDB1&rYWMzX#a=eqo4&a=uu z)Q?)iI!Rm;*HT8@7eHnu6+%T&2~^54vDI8xtz!KZ2svxf7ENfE)m+J{QdS)jQ>Qff zgF%ztM)KzJv6&B6T;Bxa;VZYe;Yl z($<1bX+gqU&?#1`yG|dN0o%%Wvu15uB3I(B8d0hxJaUE1|1x6JNL$xXqWgmUIA*L`b86wU6%4L}f1o|8m2+?No4fqB zjXj3-+dJ5GL@rJn8qo-dF76spa_STD*VH<$GTcjOx3o@^HLcA6FlxnhO*)$1EE%H^ zjP}twpM!kl!p}u;)g7iXQotH|DcoHK6+z+KXl5NDnv(g^&0Ikrpi;1gD>QM12Ch)Y zyBdzV0?-5bzi#h*Agu1PVH>Qjv}*HeRby@qblB~BCVq$m!m8+L_}Q2D%c1iEhbUz$vH&pvsK-~AE-~z zh(7-}GEz&673uXD8dDTi=_49Dn{li4I1ue4T2fDw_UG{OcQO9eU3#S+(Ec{sdyjS- z^>zo@72QEER*+G$)~)O1oLo&!+Yiu5rb5E}-R->t7?CjBPWMq!=w^R*Bgq%F=S4+`43?^g=1UVa0$_ z`XLpYYJ95oO7stEpD~&wVuti*Ek@#udePRehF$WU=MmKwF(%OxTV!>usJCYAy~WHi zxsY)u5x*?Hr+b*tpGH!u|6OONF$zLzoTZc^dHn4)wup5v{Cw%jn!224v)Zs$M^`*i zaX1sH%|OnLqj8NTg>5_L%xA1ct*0~1ys1CcysuH#nZ{lW(s#zh^;oEgqv`BX|pFBl!t*~ZRgs~YB zVeQw6tn6dh4iCGjPjxQi$& zdSPn0t2y4bpp;e{g_cjt%_kMXLMyCTVQK*V)gq^LGTN=TY?MpX$-Bi|qRr_hc4>VT zH`!y~WWRljSE-fx!fHr&m^1FN-)>_K6}2#LpdlikJFE~7kr3C{W+#}_8+B8)gbvXL zSFEl|v`dAKbR>ym95|Hbm{{}f8FQ$}wf2>ywl@A&e9DNZR8|6`HxC~BPRWTVRZ%&N ze^YA&PQIwH69B zbn1ywS{z>e$Tg0AcSI!WPgFDbDG)B41I$Btfo$OlKO(H7CZo^21PN7VNK1Kk@rffs z?nUh-qx({|m-LEDW+E7`pCYqP^!XYEXtZhAud^9V=7BixXse0!b$CCmX=~OgRE|5iy~(?e7N0QE#f=XlygY;9P^<2i!|(2+7VnAT6s>}Ra9)yPac%bv24D# z8ith|0wbc6S8ZOA(P_ktIx_5H?jN*8`L_UW81nO93qZ0jkIA`AX zW@sD7MD^YU?Lq2|4&BGK_8~L-;qQK~9%Q!Lv&53Wm-%BidPjuT9gKje2qP+h7>J>7 zn&|VW0@2Ti&@bDcj0SC~W$;3L-M|>L=BzdjvjVggMqGPPb;XL4+N)eMn19(1nT;!kLl=KWm>cP0l(|+YBV{9!+V!JR9%3s5(yV#!;oWwm zMpUGjwMPf(t-z@ayxK122IJ@2IsOn5 zP4XVSsZYz=9JlGw+i;~7UhC0rO?aHkt9wkn-e$W7=2-aq=Gg6-`)N&{8FY`R|I_rW zcmn3PYKMsU6XQ^{>KK!vchvf)VGmMghu#xSAT*+uV`DM2cxv}F4kiAcaX*oMqjrn$ z1C`17AZ9UR>pgDVs&UOTP+Ub!8~?&4J;goDrc2$^SECoaxA8RAB{sGubzfJ_^4#2` zm=hz%bcSBi7OO6Ce_$T5c2uo^|vol@@ZjM_O$4^ zI(3!miq#oV$x@ols4^njszz2f3LADFJ+v5Y>rTx)643-*xvT%hn)){0lsQqxp9GmN z?z$p8pXR)~zOX{8tEhCi6d9C8(*@?%Gsd86JIAilZ1%BMOxNL76nE$58q4)oR5{XD zk_Lw)JB6-+8R9^gb9T;*N0%>nv@WdL^W#eD98O8FPxhaWmeGCo*qb2@!|Q zSVQYqRB&Zy99L}4M%Bz#HMB=dw_YSOs2pYDAY0K$EMzlm)K*oDhE;Gw&8CK1$p{h2 z)tZauS;n4n1ldW|gx6bZ?U7Nq6i6{fkm@Ip?2oWfr0f)dfa@9}MIu!hSrPh8{j8o= zUprr<^tU=akZDq{&Mz;>`Nn;MR#E45NpHQ_vF7NAE^pLpH{TfT88No{Wz5Pp`)UA5 z5N%Wvol-KgTI=Ts--j)}gX?#4ub2zNuN&E#p$Ek*poTB&B}01y9m-!=jzh`V^^j1CuPOYf!`5h zYow2~mtFyTO z(jj*mqCy(Gq<2XTrR`R~ui5G`FQg&45l3k}rP&DDpqe1_y!3mngVuB9z@eJuRZJ?$ zsxroD8jE8cZxJ`F>LA9t2+<;q^&t78jaeVVDz%T0_70@IgEq9&mUiZf4*I-<_I1Fc zs1IlKo$(zDxQ2SknDK}x(CQTUlBs;=OxotNX2GKuxi#iXhsREhAbO|%HZ_B5KT#O9 z8%Wj}kiv$b1u|BhM(IaXsl8RoU4qP_Gr}&&RfU!vsrWgCg;OhMc$oDvzaS+|GFwKg zXyjK%j`bLy_0Gol|Mshx;01gqiAA*pSumEyJ&ZNVwa#mMlH1DTLVCl!k8&DT<`+Gp zg*5O?m-1?zy;N_M@LpR(*j`0?$YQ=US3x_bakk3M9AwRnh_-i~_H7L6BKRNp)7mAS z?Yd}{`KImg&YksrMy?T+chMYo81-7G&9^WI&&tZyG%))psx9mP5T*7y=b5`}RkbT< zi_4s0?Bylq5^Hs*<`OggtXp7Q?+vbe3*F*f%q%w_Bz$~q%~e!o<3m7eY^3IN?6P}q8mhxzTd_!Sg)oT)kbAnfodFiP`|sA789nA9&|@$ zbgOkTte5ea)$3zMi`30r5$|i0_tw>ls(ID?)}5U@J!`&;6lu-gsG$)aeX9wCOuFiw z4RRp^GKkM##x@Zg!7DmN|Bz# z6vb{nJdE1$w9<=9V*lwuRi`?Gr|eUa`k^ zK8?(|OEb^IS310RsO@- z34buZ0j#|9=HoeUKA!W2_tnR9-ZV$jB-3xr*UGYJoN1k#)MHkVDK>Pg_A~pMUM9{K z-V+~&^k^^y!4riV^|=X}b^Qtav78g>d~eKWLa425sH==Y&rzlMyV(Wv$I)j>G5Epk ziu}uN&Nb7yF*V$EbNx((<+#3dN6a#gAq^o%rMIeZ@~^ zz2O`G^lIld(YHF)WD&i?I1i9p%#rW~cMME5C96svn{ja)dgD5&yEmp_z1;=j_7m4^ zwocn;=l#~5eRke^D2%*BUl8Ze32E1)3YW8)9_XFp`h5TFo}pwEU9e^MAI3(mV6>^Y zk4-l=;aF!Y-=vS`H>41TUx#JK0D^Vbv4~UQ_YSM_d&@2PSm&aJR}05?W$o}!OxR)d zGGz+ioTvklHibUrgnoH_^P^Y&>g>u`K%Fg4ZEQvFE_6a)P0!(z`Ki`So)Asa<-l!L z$Im8yI{ARv3;Z($5A+lk56>eU%6tZU#sl-Uu9Yp9^x~dd6-gk^v*Fg3JIfiF{W$tcX z3b6^&aK@XI6WK>#EYu{g%(J~RKb`&+FVMcjQ-fz`mENyso#3nZb7i`3)>3^>>1k-1X7xA=12KlkMyZmqnod~>Di_{*MU(ofD_f~QF( znyb;LbN^O-yDq#pIfd)jnk^^abzabAb}yUTL_q|9esC)PhbB4?X?b6pv+lK7EwF^| zOm_X9+1TjAo z$03xH2Upk^<~Mj@ek0619h+DJAI)!QDvYdLe}keSMIQA!ZLBraj;go+;aoX&Hs0(< zb7kq~Z@Rmw4#Ii0M2Cf|G%UNBb-Ne2flfZ7WOae1*tL=C( zK{hVY4G0JPYB8+1H@U7`b9MVTvwA(hnb-2!{M=7(p%8q)^e{e{?4QzCx8mkh13c&6 zp3{)OfxxS!v?$EH@bTt+9A)ou5Vc%JXljjv1-h5~>8{d}A=v*~)){O!FCjzG>s z+?VI)DmP*9!MXb7T9Bp}XT`rbcft#^z9@Nc^GpLUA&(;nr1I&d)bHk#^pDuQ{ zY=;*U#54*aeRjHHv*Q#{03vEs@Yc*o}7 z|L)a3XfLEHxo`Hy&cF~)eoGh?Y1@7=D;V#z;G)E5K;Jl={++qj_%c-icEox!degci z@#*6E>j>XJ!EXWrB)`GUh8XkO%x{zDrmEjGFi0TA5*+I{^YxvwdMNrAc*@?Tty<%a z@k)RXI9Yhy#rmdm#IFu-#I;s8zHk*zs9cA!!CNDgggObZ5G*Y{Gagn=vxtEu_tlxz zwXM4bQ@DTDG~KEmURnsx6n;RPX!oMp|NXVte>dmKo*U+n#y1=yts-m!PYhlG!GY$$ zF`zq89{z2RpXy8s@DMvXIqMTf1~IGGCC@ud2z6rTnjJ7-DF`GsR0?otwEE8pTHJT5 zMueZMx}3-<>}C1u6~cGT?D)9X9K}NfA%HpBH|+#dHzpNVzOs@qE_n&(hB$)lCfGB2 z59K`vZ*Z+Sb9^$_k0m@6E{KQEDj$B@7~ z(Bm$wTIVy&NrZMhH_!Xryb^BhI=Rp}$Q<2`kR4uThO)x$p?qK5i>hMhQ70hww=>NI zEV_!fW<}6eQ5b|^o3(2Wr``vzkegTWlX<*7{^jcP@6Felpn!FWJsX|(u;AI)C>;k;Yi;69jl(F}KL{rBgZ!>X*E4`%IzJ;9$|nRks^(j3^-*;U!?A5cOL z(K#Db>DfE(pS$#O|FCnYqOs3mOQzcJxaveRC#SQ{1RNb0!OujG@K42l-Sw$CJ?GZ# z@GAS34_p#VmAoS2Ey7WS{j?cHvR7Fw7F(PiSbSQ>`{q5!&D8nR8T92`TkbjhA!I4E zX)wpm2R#(%l*~GuDz3SymMIXpOT0JF0B^F-IHlg4pZLc7^j}PvQ8!?5%&B^#jd+YvSDrp4XfotSzS)+!})g_XYm-@~;$9Lx$Fv0Q9&F1>vJk$5)nZ7sch8{d0sb}Z&bbh=% zIghj2f@*1&E+XH1r z&gUjw+1GQ<$B)*Na<=P{V;W+U@?m^#?y%=4xS6~A3fvtI^>nW9&E5Ild~F(oC+G3; z3C;yvBOyyo#=^I2&&b&cd!%X(GAg%+_gvzV6bWIH9Oq$AoZ8`ya0Hr($A$xSW8(4A zP&|1n%y4}AD)6Jkk0#80{FjNJ&Tsd#`P$d#dwwy$u}+W4VQ{bT$^3cjZ272AyTG4h zPyW6BU=yvix`PvW*6_CDN34(oJ=qHcZ>V?0g!byf+tm?lcl>Y4M*|5!!dyf)RkzR8>O z`@|A>*;AdDfc5-X#xp{9) zNN~0~Z|gMo-dK}Ob*NUyJA->UNBp1j0bZJurqeRSJO;sLJmy3`GJ)&x*aUXE;L&fD z@(9ky&fZ`e*~jxazNclm?uxQpm}!|VRHSS-#*VuQ^TnN2?NP_`R37d;l}1$swxuf+ z7hTt3efOyPiqu&+9R(K*QLp5y^GHQ+b;R(mrviVHVd(d9^N|yMK39XMv%m6 z5B6liQ(45M4DrmtbLH1Io9#fIW6=l|6p7=0BU?)0J?gaL#@=?*Rs&>GNePY8KV=H)aJ?W6++^ zDLghSK%P>jlAbg6(t~rR=$KMbn>Pm)| zHDNY%1-R#XZhk)Z6Yo2ynXFoNZFTc;!5YAipQs`l0S>91V{7RoYFFyhUY zfl<;V{C2*_I{DrF^tAunCsh@?%Bp;Onq7^~9}*79+dPs3q^jnFd5!PScQsS*>vPZZ zsw@{UrBu8-@4UEqD=6eB=Xi)W!^M0b&Vs67{1-d-&imq+Mm<2q&0oRp3So)0mxe3< zsa#X6*Q)*yZFv-g+iDZ(WLkk%p^lsra6^~Qs(j~GsQdK%Fv+b@%yO%AvpTFys!S)B zZe`p|rxy=tIUxUe+AVVf>s~;}WO)4)?)MnDZ(T*4P%f^VN8X<2Gz;gQS^qqWZ_YE) z?xeghl{NN_-iP{uk`cpQ?g-_;>w*yTeSY+6S8g|!r9E}E>f|7>Q1~P_z@8>e31DuO<91CA@|5f&e$-5mMU6R5xUPg>XKjrW)x&v1cc9dX`%qzj{ z)rEt3EeiJZ+y(yDY|Z?o_3ZF~(@pT0d~gEC0Xhbsi1AT%QZLFLTDSbmS-rSG_%pN` zD($$I-HA{zIzOjTJkmv{>R-p8z0N0XUL>5g8Dd|~mHKk7t5tU}r}PIr(XWTue3yIY z_H3FW6|$;2RDVTbOL zdsC2x+EyFULws%aHoc2>Xb!6H&;7k>+V|!iE|ZlnP}Y8ZK5xF0c^bUYV549%kTN z;xFgx|7yO^RAM}+AI?usVP;zKALq{$XJ&QbPvw<8G`rf1+3U5xQSeVTO|7fcFkFS_fX21Sup7a0A ziumVw_J5k|_m8tK%!&Vpxt?Y({lmPLf0(PvvF9J>_5IVtKTmL6`SCpd%LL5f_4&Lh z1x_(c7do(yuhv+vU0tVZlbdk;OXz4ydiVRyt9+q6fx_{fSsSKgzB}tf+?;ERah}_g z3>LQe{H#VCS`!lMXTY!ZIUvr5-SOhAffwiQ(n;Y`&&NhxufBh2?#GwsOgJp_WtfKw zKn7m9!Uz26TUWswQM@?u!mW6I9!1lXzIFAiJmyT;%dr%S@cgVgT=z~fS!Aato|Gv> zxMlE=roO;?t8;Rpi}z`z#OU^ZLIt6NP(o)*{oFDY8Uo$1!u_xI-@YzZl^Q!233aJw zix<@-Rvt3;1$DJdPqaVSBQC?g+dJgmYH|6vD$&Vg3XIGLw)Odh$*=T-xwc!av_m>| zoW>!oO>N;B_^hn-UOZZ%M-NT-=4kxJM-Ot zKKsaD%vJpUTu(DJemK|nujlIi%{-f_qyKHzz~9bV`0ukOiYMpssjK+gSrt4qObPny z33E0$ubHp$y~$kklV6>HP`xm(0!N+a%o7vbttg;mJ31v^r5vj%5YNs&sq#?)sYCUK zo|u5qZSBpe7Fq@KN5N%CYq$l+TcNbUIOlMpcV?AvedFWwZ?ke-N4w1bnb-2q^IiWqui)?Jwf)_!vcH>W`X5*E z&^$hT6@NF6|8suI-%tFniGP~--}7(qOOp+LHY?P*NqfNqnd%{?@$%$a7xI>)Uar^3 zF*}}pJ~i>Laj<27O=j9Td-de%VV?ddjA%PKD!IG$MEf_N+prc-e>8Nl6?(d?i&>vX zJ)#X*c6hd~lFANyIwyk=tFpv2hwcd7f_jx0NbdWx5ZvC!r>*5tGUmYs~!gw5@aq zx^0}*^JBu4pEa5wV)R%7#AzdYJiw!P`g69RtN$imVljMVm4=3Wl@ZZAmfPBel@C275 zsec?_FebUKTQ!lLf$mXUdIHoJ<$-{+lrGWlx-7WklLE`D3y;YWztGGT=S{s3t~ZoO zbg-|?N)hI#nOUOxi4t6+!^@udOXx~%c<)uspj@Nb=~r?Frim8@gmQQqn@F~*|1LwyLSJlz0r?&^j7we|4Vu;)NHNJXP0aO-;J%;r%uW{RTqr4k%z}{c@U)dwfv&sM)kH%k4fOnGam}dYd zf_wv(PW9G|s5o}m54pqWL_6^~d8aquz36hXRmdz2YQ0ifS(#aUmDP;G zWW_<6^mVz1b80GoYVuRFpqxEb|1IvdB{vIb_3gHTI#^a4+xInFz=dudop4kbs zZasP`(2Fl;$FLXNH~T)-RJ_HD1*KpMZNh(VS*wxA`LF1RL|17o1Z}EO!d}N#AR|r09()p6GC%>T2j^H2LP4`K&O(TdYfgsm$vA&?Jb`H|Lo)@W(e^n$H!q z>on}#uLVv^amkkAeDKtSKBs+$#wn&$=M!Bi#jQ#S&vW4O;m>_BNRVk9rc30Y@#A?6{eb{+)_?+; zu<(cZ+MG3hI*&h}?|FT`*Bn&S!BUvlRihU&mk4j1ik<6KK4v)Y`R&%{wqncCLEVL&ch2<*C|Eu7Lwk##^@^I^AADyR4)2 z{6x(02PX5+Ns2Sht8<+w6uv!=9ListXB4Im3sXBeBbeOy%3SN0=W2(GnyZ9u*W4xU z7^Z()e?F{5-19s#S}pPR)D_nAr7o0fM(#~b1kV>rz7#jtX_;ynSDg9@X9~kZ&4hKA z2hy_d?EOW`_`1>gsdGq3Ie-2R$Ji)SPh)U&1u|LwpY4Py$)-YePKCZ#_u(V#o+x)- zmhgx39o3q#h3RPo*6`h;&*yFM{(OzVJZ%o2K4DDH5Hh|{`7QeGOb`2f_Mi5gTy&Zw z*4#)r?z$ONw^gI@!Zhvq_p=-3yz|)PylJF#C$>XjU)zvviBx!L80i@G{h>l4M`;SZ z4KCBdj3{C1K-yE@nyccsztT48=gT54dz43mP1P>TDhH8)r6}t+X-Z}uLaVCBtl@Ev zZKFm=--8XkTw=Upk>B+`VoOtL^TNi-!@|HBfp4ZmRLkVTf%#dlM)jlqm+6$rsPV!f z@~hyOdZK-!y`x(2cs7M0$fI)hUwoxgU3E+2Bpa2N$rSedt#>RJAo>T6FsoY``m7{R#7F&tu*t)K*saIs1@)mxKaeV!B`f|@7r)t$) zs`lJ8;$M%Rs)el8HrhIPH7KJ+F zD-&i`(I@dKlz%L@W1Y8TJ+d51(bVT$E-9gMUZ;kF6Xn281unM6-ux*|2#g!E(TDuk ziYe!957IHWFIiuH$1vD>g=XRiQ&b8QRYclB*E~~q6m|+fjSIZ) z$o{5s;=59{9mCeC=(~#$P|T>1?oe)6A!X-AwLt!3YJwD>Du7soadlVynVX0TvXe<& zx9^wVVe3FUQ5x6g+vxJvL!n%5-`u^E$ME_L^BRU9g&~=Fo$oMRtrcx$hehv6Wv>*Q zzw1HNhe!(*;;0J4!bl@^9_vqpLVk5#b0=%;74JqI_w$bJyXEx7@Zb|*ul6&;wZh}o z3yWV_=dFGB0_^nttE@_PMUnT}RQsZA@9lXkaL)@EL-FxsggF&M4N`FHZS$lyEv(+5 ze9ceNF>-q9FG8|04|Hm1D~1S_L=$3 z(~{&{ZD))l)Lt7`czMFBJA%^Wd+AT>3qaM{%m;W<&aZaxb4J6aZV&fT=Q{jKMebgO zTksxJ6CStRdIfNV)l|sGS)u}&b2sh#WYHUD;Ar)igX|K%5P`P#u~##Qj@)Acj5mO~QMVKI+6v*ck0b<`J% z;j~>0om_5cCJyD-p=Qi6jdL0vH8-_4WmnVp%mNq;h1Sx!_}i zD~KbAAIJeP46CUDP_qwbHS@^jnQH>c^NfPCcEbRPrHE%{=mM=w#|>!h7uVb-{MV zH*b1X`$MyS+KHR|v62-|+DIfn6Rw<(|%Rla%p(D`trocC?17lgf z7h@TQ9JI#VS&WRPNm!rG@37h_^H=y@(xO*e@hGA24x7eyrN92=+-YVNU_8Bl^}*ZT z1Opl;r-;+#&H3tZh2LChp*Rb2LgB*t`dk$ZgP+eko|cNUvpwOViAN?Lop@~aiyIS* zIZ?9|Z_Fc2hHlbZ^EGeJ*S<5~qib|?>XtO1&LX)f_Xe2LG7H?*IrE(zuQRsmy4m1n zy82BjkWXjDiYgLS3GXDzlXDgbcq8T8*C%dVtq)EtKbxO{ zUGlT5^LOtO0k@%0HFi?~ys@!bg?1+pa{FfI5S1)}br9YjTL6>%V>yrC3VJIi~# z+uxjbmutX!Uziz7d$-;fugt1>W%8SsXDx*)rptVOt~l3B&ZaCXZ_Ax`|r-R|L**>KcAm!a^s)R)ykc=X$hQZO^eRyi978_qi*4sU8od%liEg~?lM~|>r!{CY@jEb)j)uu!Jp1G z_kKbcB79TQKjjbp1GCPyJFynHuFteIh-A=*8=+7fs?y#Q2W)|^sDtW8CV1?uG*=(64NNR!L8o% zVDEfc{>#x+rKh5-OR;1=kL$2m^)hUqnxB#zRK41KD6ncVYWcROpxk%fslGMUVojG5 z1e&4S&mrv5&@(P`B{c22BIMe$0 z=MK28fpt7`QG0ssv%+Lf@#w@O6Q){lo;$8#VNbBAEbmb5toT$id|)W%3qpm_VQdiYWP&~ zl<^euznXvNp76=7tQTV)Yx(1O9$L(g=lZKhbs3w<*Hj`aI;Sm2!0P+;M)g(e+oy=5 zlanW@g5klWY ztL$rfXV6}L7CT4K8m1xQFv3m0_lEM#3?q6o{#ec1Gz*ZQ5=SGxXq0p2kelWLMdT8Y zvU>Fq_0S-;32fr#!rhp?A=xAdfWaHktl{nL=pl5D&`?>HUm29Kl=0;q4Et4>Ah z2BwwQMO={bC;HLOjaGatJ+IqN7d8;0YL_eEwN?Glwoo!zOk6M+Z~B_m6~RFD*22ee zal>O`XsV(01mu5P*@VsKG!kR`u(YvnPsC|5(JrJE`!d!xRpoJ-;OJ%gN)rheuZDQ% z_8-fsu4yRIBKyrm%RkTAajD#0%nkAz`Wvq`zPr!X391_uYNxW(*+0GQIzi>f{LyeZ z?R|Utu38+r8^YUUn%q0<&2V>#8~EPb6LA2mGu#W#$|l}4#ksjRpMIVu zex2R1PJ{`=#eMVGD_O5u-3a=?!+U*n?g;fp!BtZ<0SsHQNWn+N*Tf^!j(A+DMRWhD z7s@T-U{PL&4oMjs*OxN380$Ij=wo3PQ8p|Jut)&avzi>Tl#{}8WA*0L^W_nAo^(0X zo{&&_DqrENQ$U3)@B^ZbPkV`pm^yY9HE=FFv~Z zEp+0b$*RN73;Y?iWz0M}#3mh<)tPVCy&_MNt=iE|U~>QSg)4Z(wI}%ZBF;4S{&8+o zrydLXxO^d@nn7g52q7a%G*;QOE3>|GtCJ2U9=#6PFXtS9R#5(@$)wHV1d#p^ z`=8ERwDKOE_sS&U`ohj1ex&fJLY<@T*>&8 zstNwlWtP||+sA}kLEcX!-`OVJLo&&Q(~;;mDjL6GMDShFLQaP3drn z$q|r}JdJ3kpeH%`)$4N~6ml}TS$(}~tL2E>3t~#090~^|m1Ib94u@m*x>LuC(V`XCUFV}%9uskQH0FH!QHeR0HB)wg8Rc}8x z{%ZD%AIyFsemGbB2Xp0rFe^a()vSi^--^GORb>{;cP3t)z=pz{dwxO|B17Shf6kFK z4Ui9~o3jKea+pfZ>|KAbFkD^zOUBW1Fjp^d`niWIwsm*@m$A5)!CL=kEAkxNq5kzi zd}pj0&(@3-892B4V;!&`!8F^&hH@gr2Xl`W9K;&J% zq1z_blb#px+Y{KFRnAVRiZ4XwxX;CDi|1ySIaOfZqA*E2QXZq!Fa>9oBBrw)%$%Id?yUFHuCCIG z0pRs`ZF3ps|A(1Vw~ZTV`+x5db*SLuB)ChZeqU}`^Ks0`X>zpQ#WGy-6z}R%m&E{d zf8~zdD&HR0@qem*gyIN%m-zoNN-nWUD6ecn#a@^bqX$+mWZmoaxJ8~O#}$A>Sowu| zM=w8IAm@zO@_G`f*4iK9T{uI`jr zl)(7Q?q&F)fN8R+|Lz`D{_e=;uhuK*&gATHu&C5c_LTgN-K)+tRR=bcyw`V>tyYt# zKdk0hEV5~2USZ)Cd*#<%p{p4`r+DWDT!M@5POhqYg4A-{5{RVZVg zfb9aV_-ZHOO?N7qZgnPfHK_}$FfU;zD8{~bxp(@yP9nVi?2&n|yO&oVv3uk4#&(DE z@2eniN6Yf8v(Dwt6R%hG!Wqu4yzGot2F-Lj(`w4~si&KxpSxaPR9pOVcBbOSd`(ex zoAU^SF&1XIR^6)nQRM7;ob@@o-*T;5=iHE`H@eL8}6*LADoKcRVsKrtlz_WK7^7S-(vW*Ge!5sxe;^Nq=l|Tjy!fgT)f!q zcjF+&;!BSOi^5*HZ|)k{5m&RK9+5uL21M1~d!jxbjZD)tbnbSVKzLUZ+>UN9Mj>Y5 zWn5<&p6@wTDIR_i_e%dT#*No}UiWHe_c3l%9Ee4In~%}ER$d{KX})-Br_B**3LyW@ z>x((MdMa5Q{Hf`EDN9qA#zKZk$-1GpP+YO9ZjX8CwE}ai&9SyA0L#9}J){}mF)@BQ zujeviXXFGJFGg1)W$Dg_P}^9uDMznoyx5AJfR|ZO`}=+KAny%B*z^ASV?8OzLQV;( zQ~bXvk(iuTTZqqtqGwaVgD5Bj6UdN5w9mk*;oRZOgR%%_c2JX@n@_4cPd)!NaT?qCPw zscb4lGfzI9`$DAdXinncx|E;Ces%`L1lGmlF&xjBVhR6=68$n0gpb#$&aEL1X9z^@ zzWO<>59_3Jy~lPN^Luv3u~Sg&SZB>PJF^XRIqGf1I(F(@W^uQpeFvXCnV34ME*zBk zcuVU6UdNtz6*Mz#J*L-ZdRK#E$YTsToomt2(6;CUyT@rG)7=XPMb9A#7NObzz-uG32p2vgItaCx9 zkG!-A<2ulVlW0$&-G6pPrTD|rrm#zCw_I?Xl;weQ*)RN?;B{%57jF(-w)il4S=H}u zHQ8;pH(5qp=i_Ma;uPb>7c#oL)me7dMLSiPDTH&%p?f|Wq^dmKvt@F3^2} z+ghi}JmNe!o}cfCO`F2b`Qhw%=_+`I6|c+%sg@NDw z&op#*M&1^;KhI8@TAvCTMVwF`!EnY{I*0jRPout7Dxln^(y7s_-K2 zU4`adf(wNx@MBWHIZcjV=3!H=o50CpW9OIAZl>tbEV0W7`L=uIF?L*2&Kc6Q;ar9= zdG{l>idWcc-TWY&uqh!VP)p7uA1iKr?KbV*_if>#>aFGBw&<0iVjd;Zp69 zf(fphZoGZ~9LXwi^0?Oa_pMCs?w_o4mX9un=gZ)Jyc1k?KVZ%|n%C=&kzHjn zbWKR-s|oQv6x8-1w z5;Qb^JDfV(Rq7%4ap1!jmsNyWbZ|y^V>|Hm$JoisOzv?z2jv#!7cpzXlidAVuc%Mj zhf-H_ayS|+4Axz>P1K4L*mTNN;5y@}3D+YM7s@M-VO5vydfrHF{NSviSn;MF)_<~( zJFPGCMk@{bp?Rf8J)9~kM~1Q$SqpT~)$3~36VHF1vuFE6Spj@Yj@a(QS;J1Q0#KF4 zrd7StY+5O2YOxa> znt9rMhA@@uzIThLpYGRcvFpgjZcTZpe{8q522>5{?#la|3^6eGs^DmbCtWg@F1f;I z3UZ1FstBIr9O{k(V``}75_2wF$1-azr?To6Yo>LxS!G_cl(@tiv2xWSh92K}K(9sq=c?zD<8!61wf8$d%&B{cp zOUx-m36aT>CxGr4wM*KOI&-Lo1Dm3)H${CBo-O$}H2hO`= z&x|TI=lFJq-C>ukx)9EsXm!bQH*fx-ihm*4vcFr#5vc;;X=*Fta8 z4Cd`;Qu)X+?fi5M?o@XvygIDfl?b~zu7#IQ9je$fcOd_pym!wG0;(xW{2YSHV0q}2 zy<&lHr>tpM@XNen!bFejpt(m+%&MxU$P<7sz((Fs>knlzaUM}ybF3%SW;U(EMKbM_ zttHv4|Kz2xx_ZnqPR8V&O?$2+ zdw$q~W3V*6Omz`AR=@z0{448c#`*>I}F=P5Z&jW*qA)`|xbHdMxXMq3gRJB)>F~o5w$19W5 zZ_1r8bdb}|_8stR3ofR2k9F%gjy;#>M$SF-PTNsYf!0^8f7N*wYg5(XmEyjKaJZ&; zO!))ihvuzM)yHR_7VS(QqbAG!+61!a=bgcBY}&D@m-O{q8#p#RHg$vZL-74`3h-#) z)zF8(aR(Hqzlb%vfkm5VXtjUnRjU6S0Nndk9aSE&r1s2kZWR1YuE+i@cRfldp zn%~3vKKQ)Zj3(2W+NI7t{!pDK&}?XjtR{cBl%&o33ZL#Lu0shPBwzZ6=A3wVB6ZNe z&z*tA{nk}*61Xv6*9=8-k*=Q~abf6LV4P|EAD;D7b{B%wo~k}r)tq~;dm4(tp=s~` zOGmitbq5}9T~*a`YROa-eoC%Q+flVTF!W(mkR_8%7N3GkaiS5;?aJYj<49qm{bE<^ zK?$uoCr3Rq&tC?UJ5-eqq-whx@e@_7Ap>%}JwM#dGDBAn8$Z9I^_BQ+t}<_lU(FTY z_+E)JqjZx=)F@Ze~fWAec! zA*oI0tZyvCiUXN*TJuV}hoA!6pMX@EW0qfBP8^shIl1TPzG|+VyF$sK<+77%P!738 zE-Z&9UTvJUe74rN7Kx2UqWx~yLpDqDB& zvg$afq^v(=k}}49>Tc~Lf7(yyn#Y33@$<8}o@q8N9d7G)hgNQ9%1Y|2(A%aAJN%k@ zAmzKNUnhk=Y+RRRb%)pKIUVJ3?|{>GPxjTxKQp_e{)CBq-?)me&RLMlPcvUkx>Ltj zbHJBqCc3v|WtX$^sz`8Kc2*vhoqlBsyXId~*m3q-M`FspWn0zHI{mBVKiG5Z>*X%7 zFRCE6TLG!?FLK>t5z|hCcCAH5JyyLo(2eekYE+(-(b}N=2dCBZH@wjeI{jZ$To9ej;Ko!3n(-l~p zdUbmC40)uzBKPVh-R6n*#dOPxbq4gv@mi#k+1jJd+nN4tFIeaFW$0KZFmpS)k0#w7 zpY-KHEH@&ihRg*X6()tPv1}a-9n74%uBsgORNXHaX?i5gSN-45=lwM&B@{F7i0y!? zGOGij+w}F+zWg|TJGnnUFZF>sL2TVn4RVQ1;XA}>ptHw?*jXbf!kW>w+=j5q_@O$p zDr86~1>oUCUGH7s(Bs_4y$>~n7Vf)!SzFeRygqXs(rsd=?q@0}YABP`UYvFC(pB(- z;m(soPcA*bo6noN)@+yO=MKzmg@0ZX7Pv%KpQb2QlWuTiD0F6Tk5w5T%xkf)>*rS< z4V_=*G}fEU2s>DZKQ}|QMH+RPJM_n$w|y(KHJ0Vn9sldOYpWy<62(JhgU8#(<^uA8 z>p?%hVP>Dml_ySKT)l7zOgl)`GQM#?(~del_^j0yg;Uw)-O7{e zzNsH_S;()eNt9}gb%(R-cXl*qH9wAei%-s-rT(vfm>TcWsl$2kz=WB}XSV8b?C6!4 z0ISQMD;O7^q8aS=n~hGsToSj|j8B0nto~iq;NjezvnJLgwnTk(cx-+GvA(ye=&ZmU z$dL;orHW8Nm^KellJTe1G_B&dbMK2U=3eIK7v|7?&12_RS7C03oKe0gPc-T7!-Z}WoK^MYH4 z`O0}*sarpK=bzL2!OU1HJFMpJ{m4Ss-py*OPuPEF0T` zs`B>0zUbeYv3b^p%xBr)Sl_oh3$*LQIGqCK5?6UF%kNCiXy(z^=S=Vc+2X>#d^05Kn z#hcfHgIz9mohL9Z>n=Y@>(QCyY^p-FZmTg4;A#=kz+C-Xb}Y9iJK2e3aq&pzrOj+^F@BciO55Wqj9pQHL9~vrBr+4R@zzcc)@QLIm~E?be~pG2Ha7mmMwZ+?~38 zL`Dj)SUkvzllgDWxOd~$wqYTjRQS z>JzW52?Z+9(5bCCF86@;V?9K@W_`u>fSihXSmuq)Ew(%Yb|7DHI($4|@1ty?bK>;B z@;m-;)|wim-bS8fx~bKl%fH8-(icxPBG0MkRh@BEWUJGw8*}(Eu!mynk7|(hnq0rh zOCEkYmcysCPp%F&Mr|`ESIaz?cHYy&?)KSKxYhaIX_!-te!I}_M7t)IR}%wg)jl{( zF(&fA*~u$rufUMv>xECn3D0Lh_pA#17?KtK} z=UtDVz1*wQC53jU%P9BEJ0^zBogU+>+DRRJclAzeq|0j?uR8XPdaxOfWwQ0JcLJ9V zYhBjuWiLOD<>oG`YLjFZ@7GHpV&O|@Ntj2%jy58f^KNnS8X%r^$&ScQh48XVn}n*i(GVyHBFg zZQK9STH!w*rxu)s*er6tG&M0yQ&f2;RFmC#`xplW>tQ4Bgmibi|J(KB%&dZ8-?x25 zan$Apnfs%UG?^(f<09gIHEpN)8LN57i%h03nyHbZT-Pm)>*AJrQQ?+hmPbjn=#va+ znYeK#;$$y}cUkr!<PmOfYlc}?yy*UpWBJM?ss1M@JSeDT^c)dck!7YfZ4iy zGBsAQn)hbu<|L5XV!4NVoqLRxxLS;Lp7AW(52|Bo7hRw4@aSb|#L`&yHv}vu=klwX zfU~`iGpS-(o#hoVJKgJ6wiR6ODtxL5M%H_7vEfbhplMh;1FCEnb>G>k+qH*^K+Du# zoIgLks?E%u!xJ#IfY(x!)jybZlaAV2S${GvV|BOW{A$*GSKvp@^bDQW^Vpegm$OD& zC)Ns2vtm(fY){^QheI3t@=pHKa-g`6*yUG4;ePg!4l{=Pa^jX32aP-^ul_*&;U=kt zZshc+H=SyBryg}3Wv`1N9b5^%bh~rilk;z)X`g4}=T82-^YQodwb!wydL^bQ6?j+6 zz1qIHbIj|HtG6h1tW)dxXz6399U5&nyNYrmZ_5#Sm@Y@-3=HR3z54-nnXJJvy6JTlZ7dBO^K3Q(ggShMTE| zA%DClu;juoe|eQBf`k#)zX1lnprQ_imi!?_ee5>tP zl}XA^*J(^XTTfZlNV^*jwWmccgx9+macQ@Ef$7kcXOo_xk;Sfx^SEl2>GSQ&k}&*ei)os_TahU3}US+cq2(50scy>7*XIGt6qr~?#YtAc$z$V9zqRYDcQV&q{*@X}@G>v5`r zs+RJhNWES!{b)zG@3FOP>tVZ?A@bGEcE)v?THfvIHF8qpwx`b=6QW*1wPj3)&_(&f zqLP=KmfQclDCHuPSQ2zyaLCiY%C$z9Ew4ir+F?KBg?131+v|(1xX$BYhwc6lqL9Dn za(>_uR$a$+b8a<@?VBiuL38}>{N`W?Y7l%%OxazA0N!a^e(58U+p}uoX7LTv4dSQFIz2p zj>vAfLmdn%&P1x+9Aol2Rv#>9YI#%3wA3Fxu}F1~d5*;Lhl*p$>9K}!q!|E1?{c+QduX`WnP^QMN&R#8NKl{9bRe-t| zx*zz>9rUI8b8FDv`#e~4bZe(vFS^y}FGm80u640!q<#N(SD0N59%Uj{L$&N8l~#d0 z_aKw2&0GxOV2+q%n6Mg711tg~g$mxIJEXZ)a&xqnxErk?2yJTHL+;^4+-ca+|aEbEeyweLi|95j1w&pmj>&2kbS{}~pa$nD5 zSXE#5r?huRcb7KHJ><+fQzce65jGh*Tc28WSB@F8td3sXr&d(@noC9PdL-Pb(Oi#& zW9Y=ip3syDBCZ&o`yf<`&9>tTBUra%B>j`t9gY-!JG8>mfJ*oeK_gy(lKP4 zaE~v0sbivLl7-DNoV+;-`3@&vy*%M zQf1dke)fgicllV+AzLc1>oITU?738$h0H3C==wu;rZQkG)OB%%0yhN)zxl9dT_ub~ z)&H{T52HS$pb0965f>ZkgQ+u2^DW+=@5hshUmxBO58oPTeb9Q*3g=diNvp@cZZrA6 zoHd~$N)vxP8gl&ej<0K1p1-n$vT#`gPLNoCup9HI8}lzG&$zD4U(byC1gY~oS1$D| z2bD@6iMj8}ZQfc|*8le;-%Mz0q&nm0nHDGMas6VhV_lsTFj2W+xXC5Q;!f;jD_Hn^ zj0bsG3_3Z_zUFiC0gFKqG4Sl9gWa@Eg40N<}k>vUV`DykCVEXe3@&*rN?>w~VNX=w9hf1|9W3UxbW zhr*jo2kyNC|JmPxdw1=B_A7Mn_1$|1?!5!|-hq4Xz`b|i-aByb9k}-n+T6 literal 0 HcmV?d00001 diff --git a/kolf/sounds/blackholeeject.wav b/kolf/sounds/blackholeeject.wav new file mode 100644 index 0000000000000000000000000000000000000000..1c397dc2353501e7073ad22985b1a48015ab6adb GIT binary patch literal 25708 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 literal 0 HcmV?d00001 diff --git a/kolf/sounds/holed.wav b/kolf/sounds/holed.wav new file mode 100644 index 0000000000000000000000000000000000000000..d8ccd4af8848c9cec73e246d9028605fc86c619d GIT binary patch literal 10124 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 literal 0 HcmV?d00001 diff --git a/kolf/sounds/holeinone.wav b/kolf/sounds/holeinone.wav new file mode 100644 index 0000000000000000000000000000000000000000..8f5c0703a62cb7ae0fb98468e0f8b46e272c45d6 GIT binary patch literal 40200 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 I_RsbI0%t_!p8x;= literal 0 HcmV?d00001 diff --git a/kolf/sounds/puddle.wav b/kolf/sounds/puddle.wav new file mode 100644 index 0000000000000000000000000000000000000000..d9c9d4ac36970d1873ac3f9aa7f301134c4c1738 GIT binary patch literal 248296 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 literal 0 HcmV?d00001 diff --git a/kolf/sounds/wall.wav b/kolf/sounds/wall.wav new file mode 100644 index 0000000000000000000000000000000000000000..65591bb4f94bfad2fdd1bd1b050ae106fc259b5c GIT binary patch literal 471 zcmXAj&2G~`5XYSxN8SJ~^f^F?3M5Vyf>RNZl9ncIjP0bh*PhSawd=YesI)n7Aw3|F zdO+g9g%{ujcpo+}ADY?U{IxTC&vtkJ45H}OqnA$(PUiRTMo}~f{_tM(F9*?`Xf&J; zZ-eG(_w&K@X8-rMo$0T~_G7)-d|saB?$##X>rbow_x-hpcJ`ynuIbBSy>Z>q<=30@ zF~zUl60h1EdUF8l52F=5ze>k4ir4Nf%tyFNQhCvv?DAwH$7yNKIX-@q9%VXN5_V&x zVa_?Pv`7**IhssO)dE_Q`mnLKKTOKHKXY95(DB!e9v%3UUFi0E*`XC34w9ZtJQtm}^r*1%vk$TcxlOyZb4QX|@^ zc4zDw=R2pZcV4Y+ycPE9yi;Yd%uXDsw~km*1X!4nLLnt2LC^pQMTrO`D7LSVA!w_W au#~s9r4krRxcfgL2{WQV!!Z07dgQ+Z;M*4f literal 0 HcmV?d00001 diff --git a/kolf/statedb.h b/kolf/statedb.h new file mode 100644 index 00000000..522d147f --- /dev/null +++ b/kolf/statedb.h @@ -0,0 +1,23 @@ +#ifndef KOLF_STATEDB_H +#define KOLF_STATEDB_H + +#include +#include +#include + +// items can save their per-game states here +// most don't have to do anything +class StateDB +{ +public: + void setPoint(const QPoint &point) { points[curName] = point; } + QPoint point() { return points[curName]; } + void setName(const QString &name) { curName = name; } + void clear() { points.clear(); } + +private: + QMap points; + QString curName; +}; + +#endif diff --git a/kolf/tutorial.kolf b/kolf/tutorial.kolf new file mode 100644 index 00000000..cfc5f5af --- /dev/null +++ b/kolf/tutorial.kolf @@ -0,0 +1,1245 @@ +[0-course@-50,-50] +Name=Tutorial Course +Name[af]=Tutoriaal Natuurlik +Name[bg]=Обучение +Name[bn]=টিউটোরিয়াল কোরà§à¦¸ +Name[bs]=Teren za obuku +Name[ca]=Tutorial de camp +Name[cs]=Výukový kurz +Name[da]=Øvebane +Name[de]=Einstieg +Name[el]=Πίστα εκμάθησης +Name[es]=Campo de tutorial +Name[et]=Õppeväljak +Name[fi]=Harjoituskenttä +Name[fr]=Parcours didacticiel +Name[gl]=Campo de adestramento +Name[he]=מסלול לימוד +Name[hi]=शिकà¥à¤·à¤£ कोरà¥à¤¸ +Name[hu]=Gyakorlópálya +Name[is]=Kennslubraut +Name[it]=Percorso scuola +Name[ja]=ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚³ãƒ¼ã‚¹ +Name[lv]=ApmÄcÄ«bas kurss +Name[mk]=Терен за учење +Name[nb]=Øvingsbane +Name[nl]=Lesparcours +Name[nn]=Øvingsbane +Name[nso]=Course ya Tutorial +Name[pl]=Tor ćwiczebny +Name[pt]=Percurso de Aprendizagem +Name[pt_BR]=Curso Tutorial +Name[ru]=Учебный ÐºÑƒÑ€Ñ +Name[sk]=VýuÄba +Name[sl]=ZaÄetniÅ¡ko igriÅ¡Äe +Name[sr]=Терен за упознавање +Name[sr@Latn]=Teren za upoznavanje +Name[sv]=Övningsbana +Name[ta]=போதக மாரà¯à®•à¯à®•à®®à¯ +Name[tg]=КурÑи Омӯзиш +Name[tr]=Öğretici Pist +Name[uk]=ÐšÑƒÑ€Ñ Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ +Name[ven]=Thero dza Ngudo +Name[xh]=Imbuyekezo yokufundisiweyo +Name[xx]=xxTutorial Coursexx +Name[zh_CN]=教学路线 +Name[zh_TW]=教學的路線 +Name[zu]=Isifundo sokubuyekeza +author=Jason Katz-Brown + +[1-ball@34,267] +dummykey=true + +[1-cup@52,159|6] +dummykey=true + +[1-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[1-sign@2,2|3] +Comment=

    Welcome

    to the tutorial course for Kolf! +Comment[bg]=

    Добре дошли

    на обучаващите нива по голф! +Comment[bn]=কলà§â€Œà¦« à¦à¦° টিউটোরিয়াল কোরà§à¦¸à§‡

    সà§à¦¬à¦¾à¦—তম

    ! +Comment[bs]=

    Dobrodošli

    u Kolf teren za obuku! +Comment[ca]=

    Benvingut

    al tutorial de camp Kolf! +Comment[da]=

    Velkommen

    til Kolfs øvebane! +Comment[de]=

    Willkommen

    zur Einstiegsbahn in Kolf +Comment[el]=

    Καλώς ήÏθατε

    στην πίστα εκμάθησης για το Kolf! +Comment[es]=¡

    Bienvenido

    al curso de Kolf! +Comment[et]=

    Tere tulemast

    Kolfi õppeväljakule! +Comment[fi]=

    Tervetuloa

    Kolfin harjoituskierrokselle! +Comment[fr]=

    Bienvenue

    dans le didacticiel de Kolf ! +Comment[gl]=

    Benvido

    ao campo de adestramento de Kolf! +Comment[he]=

    ×‘×¨×•×›×™× ×”×‘××™×

    למסלול הלימוד של Kolf! +Comment[hi]=

    सà¥à¤µà¤¾à¤—त

    कोलà¥à¤« के शिकà¥à¤·à¤£ कोरà¥à¤¸ में! +Comment[hu]=

    Üdvözöljük

    a Kolf gyakorlópályáján! +Comment[is]=

    Velkomin(n)

    á kennslubraut Kolf! +Comment[it]=

    Benvenuto

    nel percorso-scuola per Kolf! +Comment[ja]=

    よã†ã“ã

    Kolf ãƒãƒ¥ãƒ¼ãƒˆãƒªã‚¢ãƒ«ã‚³ãƒ¼ã‚¹ã¸! +Comment[lv]=

    Laipni lūgti

    ieks Kolf apmÄcÄ«bas kursa! +Comment[mk]=

    Добредојдовте

    во теренот за вежбање на Kolf! +Comment[nb]=

    Velkommen

    til øvingsbanen i Kolf! +Comment[nl]=

    Welkom

    bij het lesparcours voor Kolf! +Comment[nn]=

    Velkomen

    til øvingsbanen i Kolf! +Comment[pl]=

    Witamy

    na torze ćwiczebnym Kolfa! +Comment[pt]=

    Bem-vindo

    ao percurso de aprendizagem do Kolf! +Comment[pt_BR]=

    Bem-vindo(a)

    ao curso tutorial do Kolf! +Comment[ru]=

    Добро пожаловать

    на учебный ÐºÑƒÑ€Ñ Ð³Ð¾Ð»ÑŒÑ„Ð°! +Comment[sk]=

    Víta vás

    výuÄba v hre Kolf! +Comment[sl]=

    Dobrodošli

    na zaÄetniÅ¡ko stezo za Kolf! +Comment[sr]=

    Добродошли

    у терен за упознавање Kolf-а! +Comment[sr@Latn]=

    Dobrodošli

    u teren za upoznavanje Kolf-a! +Comment[sv]=

    Välkommen

    till Kolfs övningsbana +Comment[ta]=கோலà¯à®ƒà®ªà¯ போதக மாரà¯à®•à¯à®•à®¤à¯à®¤à®¿à®±à¯à®•à¯

    வரவேறà¯à®ªà¯

    ! +Comment[tg]=

    Марҳамат намоед

    ба курÑи омӯзиши Колф! +Comment[tr]=

    HoÅŸgeldiniz

    Kolf'ün deneme pistine hoşgeldiniz! +Comment[uk]=

    ЛаÑкаво проÑимо

    до курÑу Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ Kolf! +Comment[ven]=

    Vhotanganedzwa

    kha thero dza ngudo ya Kolf! +Comment[xh]=

    Wamkelekile

    kwimbuyiselo yokufundiweyo ye Kolf! +Comment[xx]=xx

    Welcome

    to the tutorial course for Kolf!xx +Comment[zh_CN]=

    欢迎

    加入 Kolf æ•™å­¦è·¯çº¿ï¼ +Comment[zh_TW]=

    æ­¡è¿Ž

    來到 Kolf çš„æ•™å­¸è·¯ç·šï¼ +Comment[zu]=

    Wamukelekile

    esifundweni sokubuyekeza se-Kolf! +botWallVisible=true +height=74 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=319 + +[1-sign@73,81|4] +Comment=To hit the ball, press and hold the Down Arrow or left mouse button. How long you hold down the mouse button or key determines strength of the shot. +Comment[af]=Na getref die bal, druk en hou die Ondertoe Pyl van links muis knoppie. Hoe lang jy hou Ondertoe die muis knoppie van sleutel bepaal sterkte van die geskiet. +Comment[bg]=ÐатиÑнете и задръжте клавиша "долна Ñтрелка" или Ð»ÐµÐ²Ð¸Ñ Ð±ÑƒÑ‚Ð¾Ð½ на мишката, за да ударите топката. Силата на удара завиÑи от продължителноÑтта на задържане на бутона на мишката или клавиатурата. +Comment[bn]=বলে আঘাত করার জনà§à¦¯ ডাউন অà§à¦¯à¦¾à¦°à§‹ অথবা মাউসের বাম বাটন চেপে ধরে রাখà§à¦¨à¥¤ আঘাতের ফলে বল কী পরিমাণ দূরতà§à¦¬ অতিকà§à¦°à¦® করবে তা নিরà§à¦­à¦° করে আপনি কতকà§à¦·à¦£ যাবতà§â€ কীবোরà§à¦¡ বা মাউসের বাটন চেপে ধরে রাখছেন তার ওপর। +Comment[bs]=Da biste udarili lopticu, pritisnite i držite dugme Strelica dole ili lijevo dugme miÅ¡a. Snagu udarca odreÄ‘ujete dužinom pritiska na tipku odnosno dugme miÅ¡a. +Comment[ca]=Per a picar la bola, prémer i mantindre la tecla de la fletxa cap avall o el botó esquerra del ratolí. El temps que el mantingueu premut determinarà la força del tir. +Comment[da]=For at slÃ¥ til bolden, tryk og hold ned-pilen eller venstre museknap nede. Slagets kraft afhænger af hvor lang tid du holder knappen i bund. +Comment[de]=Halten Sie die linke Maustaste oder die Pfeiltaste abwärts gedrückt, um den Ball zu schlagen. Die Dauer des Tastendrucks entscheidet über die Wucht. +Comment[el]=Για να χτυπήσετε τη μπάλα κÏατήστε πατημένο το πλήκτÏο του κάτω βέλους ή το αÏιστεÏÏŒ πλήκτÏο του ποντικιοÏ. Το πόσο κÏατάτε πατημένο το πλήκτÏο καθοÏίζει τη δÏναμη της βολής. +Comment[es]=Para golpear la bola, pulsar y mantener la tecla de dirección abajo o el botón izquierdo del ratón. El tiempo que mantiene pulsado el botón del ratón o la tecla determinan la intensidad del disparo. +Comment[et]=Löömiseks vajuta ja hoia all klahvi "Nool alla" või vasakut hiirenuppu. Mida kauem nuppu all hoiad, seda tugevam löök. +Comment[fi]=Lyödäksesi palloa, paina ja pidä alhaalla nuolta alas tai hiiren vasenta nappia. Painalluksen pituus määrittelee lyönnin voiman. +Comment[fr]=Pour frapper la balle, actionnez la flèche Bas ou le bouton gauche de la souris. Le temps pendant lequel vous maintenez le bouton ou la touche enfoncée détermine la force de la frappe. +Comment[gl]=Para golpear a bola, manteña premida a tecla 'Abaixo' ou o botón esquerdo do rato . O tempo que a manteña premida determinará o forza do golpe. +Comment[he]=כדי לחבוט בכדור, לחץ והחזק ×ת מקש ×”×—×¥ למטה ×ו ×ת הלחצן השמ×לי של העכבר. משך הלחיצה על לחצן העכבר ×ו המקש קובע ×ת עוצמת החבטה. +Comment[hi]=गेंद पर पà¥à¤°à¤¹à¤¾à¤° करने के लिठनीचे तीर कà¥à¤‚जी या बायाठमाउस बटन दबाकर रखें. कितनी देर आप बटन या कà¥à¤‚जी दबाकर रखे रहते हैं, यह आपके पà¥à¤°à¤¹à¤¾à¤° की शकà¥à¤¤à¤¿ निरà¥à¤§à¤¾à¤°à¤¿à¤¤ करता है. +Comment[hu]=A labda elütéséhez tartsa benyomva a lefelé nyilat vagy a bal egérgombot. Annál erÅ‘sebb lesz az ütés, minél tovább tartja lenyomva a billentyűt vagy az egérgombot. +Comment[is]=Tila að slá boltann, ýttu og haltu niðri 'ör-niður' eða vinstri músahnappi. Hversu lengi þú heldur hnappnum niðri ákvarðar höggkraftinn. +Comment[it]=Per colpire la pallina, premi a lungo la "freccia destra" o il bottone sinistro del mouse. Quanto più a lungo li tieni premuti, tanto più forte sarà il colpo. +Comment[ja]=ボールを打ã¤ã«ã¯ã€ä¸‹ã‚­ãƒ¼ã‹å·¦ã®ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ã‚’押ã—続ã‘ã¾ã™ã€‚押ã™æ™‚é–“ãŒé•·ã„ã¨ã€ã‚·ãƒ§ãƒƒãƒˆãŒå¼·ããªã‚Šã¾ã™ã€‚ +Comment[lv]=Lai sistu pa bumbiņu, nospiediet un turiet nospiestu bulti'na uz leju taustiņu vai kreiso peles taustiņu. Taustiņa nospieÅ¡anas ilgums noteiks sitiena spÄ“ku. +Comment[mk]=За да ја удрите топката притиÑнете ја и држете ја долната Ñтрелка, или левото копче од глушецот. Јачината на ударот завиÑи од тоа колку долго го држите копчето на глушецот или Ñтрелката. +Comment[nb]=Trykk og hold nedoverpila eller venstre museknapp for Ã¥ slÃ¥ ballen. Styrken pÃ¥ slaget bestemmes av hvor lenge du holder knappen inne. +Comment[nl]=Om een bal te raken, houdt u de pijltoets naar beneden of de linkermuisknop ingedrukt. De tijd dat u de muisknop of toets ingedrukt houdt bepaalt de kracht van de slag. +Comment[nn]=Trykk og hald nede pil ned eller den venstre museknappen for Ã¥ slÃ¥. Styrken pÃ¥ slaget vert avgjort av kor lenge du held nede knappen. +Comment[pl]=Aby uderzyć piÅ‚eczkÄ™ naciÅ›nij i przytrzymaj klawisz StrzaÅ‚ki w dół lub lewy przycisk myszy. DÅ‚ugość czasu przytrzymania przycisku myszy lub klawisza okreÅ›la siÅ‚Ä™ strzaÅ‚u. +Comment[pt]=Para acertar na bola, carregue e mantenha a tecla Baixo ou no botão esquerdo do rato. O tempo que mantiver carregado o botão do rato ou a tecla determina a força da tacada. +Comment[pt_BR]=Para arremessar a bola, pressione e mantenha pressionada a seta para baixo ou o botão esquerdo do mouse. A quantidade de tempo que você segura este botão determina a força do arremesso. +Comment[ru]=Чтобы ударить по мÑчу, нажмите Ñтрелку вниз или левую кнопку мыши. Сила удара будет завиÑеть от того, как долго вы удерживаете нажатие. +Comment[sk]=Úder do loptiÄky sa ovláda stlaÄením a podržaním Å¡ipky dolu alebo ľavého tlaÄidla myÅ¡i. Podľa toho, ako dlho ho držíte, urÄíte silu úderu. +Comment[sl]=Da udarite žogico, pritisnite in držite spodnjo puÅ¡Äico ali levi miÅ¡kin gumb. MoÄ udarca je doloÄena z dolžino trajanja pritiska miÅ¡kinega gumba ali tipke. +Comment[sr]=Да биÑте ударили лоптицу притиÑните и држите Ñтрелицу доле или лево дугме миша. Јачину ударца одређује колико дуго држите дугме миша или таÑтер притиÑнутим. +Comment[sr@Latn]=Da biste udarili lopticu pritisnite i držite strelicu dole ili levo dugme miÅ¡a. JaÄinu udarca odreÄ‘uje koliko dugo držite dugme miÅ¡a ili taster pritisnutim. +Comment[sv]=För att slÃ¥ bollen, hÃ¥ll nere och släpp nerÃ¥tpilen eller vänster musknapp. Hur länge du hÃ¥ller nere musknappen eller tangenten bestämmer hur hÃ¥rt slaget blir. +Comment[ta]=பநà¯à®¤à¯ˆ அடிகà¯à®•, கீழà¯à®¨à¯‹à®•à¯à®• à®…à®®à¯à®ªà¯ˆà®¯à¯‹ அலà¯à®²à®¤à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à®¿à®©à¯ இடத௠பொதà¯à®¤à®¾à®©à¯ˆ à®…à®´à¯à®¤à¯à®¤à®¿ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®µà¯à®®à¯.எவà¯à®µà®³à®µà¯ நேரம௠அழà¯à®¤à¯à®¤à®¿ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®¿à®±à¯€à®°à¯à®•à®³à¯‹ அவà¯à®µà®³à®µà¯ பலம௠உஙà¯à®•à®³à¯ அடிகà¯à®•à¯ கிடைகà¯à®•à¯à®®à¯. +Comment[tg]=Барои задан ба тӯб, тирчаи поёнро Ñ‘ тугмаи чапи мушро пахш кунед, Қувваи Задан аз он вобаÑта мешавад, ки шумо чӣ қадар тугмаи муш Ñ‘ калидро паахш кунед. +Comment[tr]=Topa vurmak için aÅŸağı ok ya da sol fare tuÅŸuna basılı tutun. Basılı tutma süresi, topa vuruÅŸ hızınızı belirler. +Comment[uk]=Щоб вдарити м'Ñчик, натиÑніть Ñ– тримайте Ñтрілку вниз або ліву кнопку мишки. Сила удару буде залежати від того, наÑкільки довго ви будете тримати клавішу Ñтрілки або кнопку мишки. +Comment[xh]= Ukubetha ibhola cofa ubambe umkhonto ojonge phantsi okanye icala lasekhohlo lemouse. Ixesha olithathayo xa ucinezela iqhosha lemouse okanye iqhosa lekeyboard lithetha amandla obethe ngawo. +Comment[xx]=xxTo hit the ball, press and hold the Down Arrow or left mouse button. How long you hold down the mouse button or key determines strength of the shot.xx +Comment[zh_CN]=è¦å‡»çƒï¼Œè¯·æŒ‰ä½ä¸‹æ–¹å‘键或鼠标左键。您按ä½å¤šä¹…决定了击çƒçš„力é‡ã€‚ +Comment[zh_TW]=è¦æ“Šçƒï¼ŒæŒçºŒæŒ‰è‘— 下 æ–¹å‘éµæˆ–滑鼠左éµã€‚ 您按著滑鼠或éµç›¤æŒ‰éµçš„時間長短決定擊出的強度。 +Comment[zu]=Ukushaya ibhola, bamba umcibisholo obheke phansi noma inkinobho ye-mouse engakwesokunxele. Ubude besikhathi osithathayo ucindezele inkinobho ye-mouse noma ukhiye isho amandla okushaya ibhola. +botWallVisible=true +height=163 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=319 + +[1-sign@74,251|7] +Comment=To aim the putter, press the Left (rotate counterclockwise) and Right (rotate clockwise) Arrow keys or use the mouse. +Comment[bg]=За да наÑочите Ñтика, натиÑнете клавиш "лÑва Ñтрелка" (за завъртане обратно на чаÑовниковата Ñтрелка) и "дÑÑна Ñтрелка" (по чаÑовниковата Ñтрелка) или използвайте мишката. +Comment[bn]=পà§à¦Ÿà¦¾à¦°à§‡à¦° দিকে লকà§à¦·à§à¦¯à¦¸à§à¦¥à¦¿à¦° করার জনà§à¦¯ কীবোরà§à¦¡à§‡à¦° বাম ( ঘড়ির কাটার বিপরীতে) à¦à¦¬à¦‚ ডান ( ঘড়ির কাটার দিকে ) অà§à¦¯à¦¾à¦°à§‹ কী (Key) অথবা মাউস বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨à¥¤ +Comment[bs]=Da naniÅ¡anite palicom, pritisnite strelicu lijevo (rotacija suprotno kazaljci sata) ili desno (rotacija u smijeru kazaljke sata) ili koristite miÅ¡. +Comment[ca]=Per a dirigir el disparador, premeu les tecles de la fletxa esquerra (gira en el sentit contrari al rellotge) i dreta (gira en el sentit horari) o useu el ratolí. +Comment[da]=For at sigte mod hullet brug venstrepil (rotér mod uret) og højrepil (rotér med uret), eller brug musen. +Comment[de]=Um den Putter auszurichten, verwendet man entweder die linke bzw. rechte Pfeiltaste oder die Maus. +Comment[es]=Para dirigir el empujador, pulse las teclas de dirección izquierda (rotar en sentido antihorario) y derecha (sentido horario) o use el ratón. +Comment[et]=Putteri suunamiseks kasuta klahve "Nool vasakule" (pöörab vastupäeva) või "Nool paremale" (päripäeva) või hiirt. +Comment[fi]=Suunnataksesi putteria paina vasenta (kierrä myötäpäivään) ja oikeaa (kierrä vastapäivään) kursorinäppäintä tai käytä hiirtä. +Comment[fr]=Pour viser le trou, actionnez la touche Gauche (rotation anti-horaire) ou droite (rotation horaire) ou utilisez la souris. +Comment[gl]=Para dirixir o 'empuxador' prema as teclas das frechas esquerda (sentido antihorario) e dereita (sentido horario) ou ben use o rato. +Comment[he]=כדי לכוון ×ת המחבט, הקש על מקש ×”×—×¥ הימני (סיבוב ×¢× ×›×™×•×•×Ÿ השעון) והשמ×לי (סיבוב נגד כיוון השעון) ×ו השתמש בעכבר. +Comment[hi]=पà¥à¤Ÿà¤° पर निशाना साधने के लिठबायाठऔर दायाठतीर कà¥à¤‚जी का पà¥à¤°à¤¯à¥‹à¤— करें या माउस को घड़ी की दिशा में या उलट दिशा में घà¥à¤®à¤¾à¤à¤ +Comment[hu]=Célzáshoz a balra (balra forgatás) és jobbra (jobbra forgatás) nyilak ill. az egér használható. +Comment[is]=Til að breyta stefnu púttersins, ýttu á vinstri (Vinstri snúningur) og Hægri (Hægri snúningur) örvunum eða notaðu músina. +Comment[it]=Per dirigere il putter, premi i tasti delle frecce sinistra (rotazione antioraria) e destra (rotazione oraria), oppure usa il mouse. +Comment[ja]=å·¦ã®ã‚­ãƒ¼ã‚’押ã™ã¨ã€ãƒ‘ターãŒå·¦(å時計回り)ã«å›žã‚Šã€å³ã®ã‚­ãƒ¼ã‚’押ã™ã¨ パターãŒå³(時計回り)ã«å›žã‚Šã¾ã™ã€‚マウスも使ãˆã¾ã™ã€‚ +Comment[mk]=За да ја намеÑтите палката, притиÑнете ги Ñтрелките за лево (вртење обратно од Ñтрелката на чаÑовникот) и деÑно (вртење во правец на Ñтрелката на чаÑовникот) или употребете го глушецот. +Comment[nb]=Trykk venstrepil (med klokka), høyrepil (mot klokka) eller bruk musa for Ã¥ treffe hullet. +Comment[nl]=Om op de putter te richten drukt u op de linker pijltjestoets (tegen de klok draaien), of op de rechter pijltjestoets (met de klok mee draaien), of gebruikt u de muis. +Comment[nn]=Bruk musa eller venstre og høgre piltast for Ã¥ sikta. +Comment[pl]=Aby wycelować kij, wciÅ›nij klawisz z lewÄ… lub prawÄ… strzaÅ‚kÄ… (aby przekrÄ™cić odpowiednio przeciwnie lub zgodnie z ruchem wskazówek zegara) lub użyj myszy. +Comment[pt]=Para apontar o taco, carregue nas teclas Esquerda (rodar no sentido contrário aos ponteiros do relógio) e Direita (rodar nos ponteiros do relógio) ou use o rato. +Comment[pt_BR]=Para direcionar a mira, pressione a seta para Esquerda (rotação no sentido anti-horário) e Direita (sentido horário) ou use o mouse. +Comment[ru]=Чтобы прицелитьÑÑ, нажмите Ñтрелку влево (поворот против чаÑовой Ñтрелки) или вправо (по чаÑовой) или иÑпользуйте мышь. +Comment[sk]=Smer urÄíte pomocou Å¡ipky vľavo (otoÄenie proti smeru hodinových ruÄiÄiek) a vpravo (otoÄenie po smere) alebo použite myÅ¡. +Comment[sl]=Da namerite palico, pritisnite puÅ¡Äici levo (zasuk v nasprotni smeri urinega kazalca) ali desno (zasuk v smeri urinega kazalca) ali uporabite miÅ¡ko. +Comment[sr]=Да биÑте нациљали путер притиÑните леву Ñтрелицу (окреће Ñупротно Ñмеру казаљке на чаÑовнику) или деÑну (у Ñмеру казаљке на чаÑовнику), или кориÑтите миша. +Comment[sr@Latn]=Da biste naciljali puter pritisnite levu strelicu (okreće suprotno smeru kazaljke na Äasovniku) ili desnu (u smeru kazaljke na Äasovniku), ili koristite miÅ¡a. +Comment[sv]=För att sikta med puttern, tryck pÃ¥ vänster (rotera moturs) eller höger (rotera medurs) piltangent eller använd musen. +Comment[ta]=எறிபவரைக௠கà¯à®±à®¿ வைகà¯à®•, இடதà¯(கடிகாரசà¯à®´à®±à¯à®šà®¿à®•à¯à®•à¯ எதிராக சà¯à®±à¯à®±à¯)மறà¯à®±à¯à®®à¯ வலதà¯(கடிகாரசà¯à®´à®±à¯à®šà®¿à®¯à®¾à®• சà¯à®±à¯à®±à¯) à®…à®®à¯à®ªà¯ விசைகள௠அலà¯à®²à®¤à¯ சà¯à®Ÿà¯à®Ÿà®¿à®¯à¯ˆ பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®µà¯à®®à¯. +Comment[tg]=Барои нишон гирифтан тирчаҳои Чап (гардиш ба муқобили ақрабаки Ñоат) ва РоÑÑ‚ (гардиш ба Ñамти ақрабаки Ñоат) Ñ‘ мушро иÑтифода баред. +Comment[tr]=Mekanizmayı ayarlamak için Sol ve SaÄŸ klavye yön tuÅŸlarını veya fareyi kullanın +Comment[uk]=щоб прицілитиÑÑŒ, натиÑніть клавішу лівої Ñтрілки (повернути проти годинникової Ñтрілки) або правої Ñтрілки (повернути за годинниковою Ñтрілкою), або ÑкориÑтайтеÑÑŒ мишкою. +Comment[xh]=Jolisa induku ye golf, ncinizela Ekohlo (ukujikelezisa ngokwe kloko)ockwise) Nasekunene (jikelezisa ikloko) Itolo lezitshixo zebhodi okanye usebenzise i mouse. +Comment[xx]=xxTo aim the putter, press the Left (rotate counterclockwise) and Right (rotate clockwise) Arrow keys or use the mouse.xx +Comment[zh_CN]=è¦çž„准çƒæŽ¨ï¼Œè¯·æŒ‰ä½å·¦æ–¹å‘é”®(逆时针)或å³æ–¹å‘é”®(顺时针)或使用鼠标。 +Comment[zh_TW]=想è¦æŽ¨çƒå…¥æ´žï¼ŒæŒ‰å·¦ï¼ˆé€†æ™‚é‡æ—‹è½‰ï¼‰èˆ‡ å³ï¼ˆé †æ™‚é‡æ—‹è½‰ï¼‰æ–¹å‘éµæˆ–使用滑鼠。 +Comment[zu]=Ukuqondisa induku yokushaya, cindezela umcibisholo wangakwesokunxele (jikelezisa ngokuphambene newashi) kanye nowangakwesokudla (jikelezisa ngokwewashi) noma usebenzise i-mouse. +botWallVisible=true +height=142 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=320 + +[10-ball@27,177] +dummykey=true + +[10-bridge@280,206|20] +botWallVisible=true +height=109 +leftWallVisible=true +rightWallVisible=true +topWallVisible=false +width=84 + +[10-bridge@30,193|3] +botWallVisible=false +height=58 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=62 + +[10-bridge@40,227|10] +botWallVisible=false +height=55 +leftWallVisible=false +rightWallVisible=false +topWallVisible=false +width=58 + +[10-cup@331,253|6] +dummykey=true + +[10-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[10-puddle@-6,246|18] +changeEnabled=false +changeEvery=50 +height=56 +width=242 + +[10-puddle@324,386|47] +changeEnabled=false +changeEvery=50 +height=296 +width=206 + +[10-puddle@95,137|12] +changeEnabled=false +changeEvery=50 +height=238 +width=112 + +[10-sign@7,9|4] +Comment=

    Bridges

    Bridges can have walls on the top, bottom, left, or right. +Comment[bg]=

    МоÑтове

    МоÑтовете могат да имат Ñтени отгоре, отдолу, отлÑво или отдÑÑно. +Comment[bn]=

    বà§à¦°à§€à¦œ

    বà§à¦°à§€à¦œà§‡à¦° উপরে, নীচে, ডানে, অথবা বামে দেয়াল থাকতে পারে। +Comment[bs]=

    Mostovi

    Mostovi mogu imati zidove iznad, ispod, lijevo ili desno. +Comment[ca]=

    Ponts

    Els ponts poden tindre baranes a dalt, a baix, a l'esquerra o a la dreta. +Comment[da]=

    Broer

    Broer kan have vægge øverst, nederst, til venstre og til højre. +Comment[de]=

    Brücken

    Brücken können oben, links, rechts oder unten Wände haben. +Comment[el]=

    ΓέφυÏες

    Οι γέφυÏες μποÏεί να έχουν τοίχους πάνω, κάτω, δεξιά ή αÏιστεÏά. +Comment[es]=

    Puentes

    Los puentes pueden tener paredes, en la parte superior, fondo, izquierdo o derecho. +Comment[et]=

    Sillad

    Sildadel võivad olla seinad üleval, all, vasakul või paremal. +Comment[fi]=

    Sillat

    Silloissa voi olla seinät ylhäällä, alhaalla, vasemmalla tai oikealla. +Comment[fr]=

    Ponts

    Les ponts peuvent avoir des murs en haut, en bas, à droite ou à gauche. +Comment[gl]=

    Pontes

    As pontes poden ter valados na parte superior, inferior,esquerda ou dereita. +Comment[he]=

    גשרי×

    ×œ×’×©×¨×™× ×™×›×•×œ×™× ×œ×”×™×•×ª קירות מלמעלה, מלמטה, משמ×ל ×ו מימין. +Comment[hi]=

    पà¥à¤²

    पà¥à¤²à¥‹à¤‚ के ऊपर, नीचे, बाà¤à¤ और दाà¤à¤ दीवार हो सकते हैं. +Comment[hu]=

    Hidak

    A hidakhoz fal tartozhat felül, alul, jobbról vagy balról. +Comment[is]=

    Brýr

    Brýr geta haft veggi að ofan,að neðan, til vinstri eða hægri, +Comment[it]=

    Ponti

    . I ponti possono avere muri in alto, in basso. a destra o a sinistra. +Comment[ja]=

    æ©‹

    å£ã¯æ©‹ã®ä¸Šã¨ä¸‹ã¨å·¦ã¨å³ç«¯ã«å­˜åœ¨ã™ã‚‹ã“ã¨ãŒå‡ºæ¥ã¾ã™ã€‚ +Comment[mk]=

    МоÑтови

    МоÑтовите може да имаат ѕидови одгоре, оддолу, одлево или оддеÑно. +Comment[nb]=

    Broer

    Broer kan ha vegger på toppen, bunne, venstre, eller høyre. +Comment[nl]=

    Bruggen

    Bruggen kunnen boven, links, rechts of onder muren hebben. +Comment[nn]=

    Bruer

    Bruer kan ha rekkverk oppe, nede, til venstre eller til høgre. +Comment[pl]=

    Mosty

    Mosty mogą mieć ściany na górze, na dole, po lewej lub po prawej. +Comment[pt]=

    Pontes

    As pontes podem ter paredes no topo, no fundo, na esquerda ou na direita. +Comment[pt_BR]=

    Pontes

    Pontes podem ter muros no topo, base, esquerda ou direita. +Comment[ru]=

    МоÑÑ‚Ñ‹

    МоÑÑ‚Ñ‹ могут иметь Ñтены Ñверху, Ñнизу, Ñлева или Ñправа. +Comment[sk]=

    Mosty

    Mosty môžu mať hore, dole, vľavo a vpravo múry. +Comment[sl]=

    Mostovi

    Mostovi imajo lahko zidove zgoraj, spodaj, levo ali desno. +Comment[sr]=

    МоÑтови

    МоÑтови могу да имају зидове на врху, дну, лево или деÑно. +Comment[sr@Latn]=

    Mostovi

    Mostovi mogu da imaju zidove na vrhu, dnu, levo ili desno. +Comment[sv]=

    Broar

    Broar kan ha väggar över, under, till vänster eller till höger. +Comment[ta]=

    பாலஙà¯à®•à®³à¯

    பாலஙà¯à®•à®³à¯ அதன௠மேலà¯,கீழà¯,இடபà¯à®±à®®à¯ அலà¯à®²à®¤à¯ வலபà¯à®±à®¤à¯à®¤à®¿à®²à¯ சà¯à®µà®°à¯à®•à®³à¯ˆ கொணà¯à®Ÿà®¿à®°à¯à®•à¯à®•à®²à®¾à®®à¯. +Comment[tg]=

    Кӯпрукҳо

    Кӯпрукҳо деворҳоро дар боло, поён, чап Ñ‘ роÑÑ‚ дошта метавонад. +Comment[tr]=

    Köprüler

    Köprülerin üst, alt, sol veya sağ taraflarında duvarlar olabilir. +Comment[uk]=

    МоÑти

    МоÑти можуть мати Ñтіни зверху, знизу, з правого Ñ– лівого боків. +Comment[ven]=

    Maburoho

    Maburoho a ngavha na dzi mbondo nga ntha kana nga fhasi kana nga matungo. +Comment[xh]=

    Iibhulorho

    Iibhulorho zingaba neendonga ngaphezulu, ngezantsi, ekhohlo okanye ekunene. +Comment[xx]=xx

    Bridges

    Bridges can have walls on the top, bottom, left, or right.xx +Comment[zh_CN]=

    æ¡¥

    æ¡¥å¯ä»¥åœ¨ä¸Šã€ä¸‹ã€å·¦ã€å³å¸ƒç½®çŸ­å¢™ã€‚ +Comment[zh_TW]=

    æ©‹

    æ©‹å¯ä»¥åœ¨é ‚端ã€åº•éƒ¨ã€å·¦æ–¹ã€å³æ–¹æœ‰ç‰†ã€‚ +Comment[zu]=

    Amabhuloho

    Amabhuloho angaba nezindonga phezulu, phansi, kwesokunxele, noma kwesokudla. +botWallVisible=true +height=115 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=387 + +[11-ball@41,51] +dummykey=true + +[11-blackhole@55,332|11] +exit=137,371 +exitDeg=10 +maxspeed=3 +minspeed=2 + +[11-bridge@287,158|16] +botWallVisible=false +height=34 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=37 + +[11-cup@305,173|17] +dummykey=true + +[11-floater@42,211|9] +botWallVisible=false +endPoint=42,211 +height=74 +leftWallVisible=true +rightWallVisible=true +speed=5 +startPoint=38,69 +topWallVisible=false +width=67 + +[11-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=5 + +[11-puddle@162,25|21] +changeEnabled=false +changeEvery=50 +height=280 +width=116 + +[11-puddle@189,186|19] +changeEnabled=false +changeEvery=50 +height=154 +width=72 + +[11-sand@155,72|24] +changeEnabled=false +changeEvery=50 +height=214 +width=108 + +[11-sand@294,384|35] +changeEnabled=false +changeEvery=50 +height=120 +width=112 + +[11-sand@295,399|34] +changeEnabled=false +changeEvery=50 +height=90 +width=172 + +[11-sign@145,4|3] +Comment=

    Everything

    Here's a hole that has it all. Have fun with Kolf!
    -- Jason Katz-Brown
    +Comment[bg]=

    Ð’Ñичко накуп

    Това е дупка, коÑто притежава вÑичко. ПриÑтна игра!
    -- Jason Katz-Brown
    +Comment[bn]=

    সবকিছà§

    à¦à¦‡ গরà§à¦¤à§‡ সবকিছà§à¦‡ আছে। কে-ওলফ খেলে মজা করà§à¦¨!
    -- জেসন কাটà§â€Œà¦œ-বà§à¦°à¦¾à¦‰à¦¨
    +Comment[bs]=

    Svega pomalo

    Ovo je rupa koja ima sve. Zabavite se s Kolfom!
    -- Jason Katz-Brown
    +Comment[ca]=

    Tot

    Aquest és un forat que ho té tot. Que el gaudiu amb kolf!
    -- Jason Katz-Brown
    +Comment[da]=

    Det hele

    Her er en bane der har det hele. God fornøjelse med Kolf!
    -- Jason Katz-Brown
    +Comment[de]=

    Alles

    Hier ist ein Loch, das hat alles in sich. Viel Vergnügen mit Kolf.
    -- Jason Katz-Brown
    +Comment[el]=

    Τα πάντα

    Εδώ είναι μια Ï„ÏÏπα που τα έχει όλαl. Îα πεÏάσετε καλά με το Kolf!
    -- Jason Katz-Brown
    +Comment[es]=

    Todo

    Aquí hay un agujero que lo tiene todo. Disfrute con Kolf
    -- Jason Katz-Brown
    +Comment[et]=

    Täisvärk

    See on kõikvõimalike takistustega rada. Naudi Kolfi!
    -- Jason Katz-Brown
    +Comment[fi]=

    Kaikki

    Tässä reiässä on kaikki mahdollinen. Pidä hauskaa Kolf pelin kanssa!
    -- Jason Katz-Brown
    +Comment[fr]=

    Tout

    Voici un trou qui a tout. Amusez-vous avec Kolf !
    -- Jason Katz-Brown
    +Comment[gl]=

    Todo

    Aquí hai un burato que o ten todo. Paseo ben con Kolf!
    -- Jason Katz-Brown
    +Comment[he]=

    הכל

    ×”× ×” גומה שיש בה הכל. שיהיה לך ×›×™×£ ×¢× Kolf!
    -- ×’'ייסון ק×טס־בר×ון
    +Comment[hi]=

    सबकà¥à¤›

    यहाठà¤à¤• होल है जिसमें सब है. कोलà¥à¤« के साथ मजा करें!
    -- जेसन कातà¥à¤œ-बà¥à¤°à¤¾à¤‰à¤¨
    +Comment[hu]=

    Minden

    Itt egy mindennel ellátott lyuk. Kellemes szórakozást kívánunk a játékhoz!
    -- Jason Katz-Brown
    +Comment[is]=

    Allt

    Hér er hola sem hefur það allt. Skemmtu þér með Kolf!
    -- Jason Katz-Brown
    +Comment[it]=

    Tutto

    Ecco una buca che ha tutto. Divertiti con Kolf!
    -- Jason Katz-Brown
    +Comment[ja]=

    全部

    ã“ã¡ã‚‰ã¯å…¨ã¦ã®ã‚ªãƒ–ジェクトãŒã‚るホールã§ã™ã€‚Kolf を楽ã—ã‚“ã§ä¸‹ã•ã„ï¼
    -- Jason Katz-Brown
    +Comment[mk]=

    СÑ

    Еве дупка која што има СЀ. Забавувајте Ñе Ñо Колф!
    -- Jason Katz-Brown
    +Comment[nb]=

    Alt

    Her er et hull som har alt. Kos deg med Kolf!
    -- Jason Katz-Brown
    +Comment[nl]=

    Alles

    Hier is een hole dat alles in zich heeft. Veel plezier met Kolf!
    -- Jason Katz-Brown
    +Comment[nn]=

    Alt saman

    Dette holet har alt saman. Ha det kjekt med Kolf!
    -- Jason Katz-Brown
    +Comment[pl]=

    Wszystko

    Ten dołek ma wszystkie atrakcje. Wesołej zabawy z Kolf!
    -- Jason Katz-Brown
    +Comment[pt]=

    Tudo

    Existe um buraco que tem tudo. Divirta-se com o Kolf!
    -- Jason Katz-Brown
    +Comment[pt_BR]=

    Tudo

    Aqui está um buracoque tem de tudo. Divirta-se com o Kolf!
    -- Jason Katz-Brown
    +Comment[ru]=

    Ð’Ñе

    Вот лунка, где вÑе Ñто еÑÑ‚ÑŒ. Играйте в гольф!
    -- Jason Katz-Brown
    +Comment[sk]=

    VÅ¡etko

    Tu je jamka, kde je všetko. Užite si Kolf!
    -- Jason Katz-Brown
    +Comment[sl]=

    Vse

    Tukaj je luknja, ki ima vse. Uživajte v Kolfu!
    --Jason Katz-Brown
    +Comment[sr]=

    Све

    Ево рупе која има Ñвега. Забављајте Ñе Kolf-ом!
    — ÐејÑон Кец-Браун (Jason Katz-Brown)
    +Comment[sr@Latn]=

    Sve

    Evo rupe koja ima svega. Zabavljajte se Kolf-om!
    — Džejson Kec-Braun (Jason Katz-Brown)
    +Comment[sv]=

    Alltihop

    Här är ett hål som har alltihop. Hoppas du får roligt med Kolf!
    -- Jason Katz-Brown
    +Comment[ta]=

    எலà¯à®²à®¾à®µà®±à¯à®±à®¿à®²à¯à®®à¯

    இஙà¯à®•à¯ ஓடà¯à®Ÿà¯ˆ உளà¯à®³à®¤à¯. கோலà¯à®ƒà®ªà¯à®¯à¯ˆ வைதà¯à®¤à¯ வேடிகà¯à®•à¯ˆ செயà¯!
    -- ஜேஸன௠கேடà¯à®¸à¯ பிரவà¯à®©à¯
    +Comment[tg]=

    Ҳама чиз

    Ин Ñӯрохие, ки ҳамаи чизҳоро дар он ҷо дороÑÑ‚. Бо бозии Голф дилхушӣ кунед!
    -- Jason Katz-Brown
    +Comment[tr]=

    HerÅŸey

    Burada tümünü barındıran bir delik var. Kolf ile eğlenin
    -- Jason Katz-Brown
    +Comment[uk]=

    Ð’Ñе

    Це лунка, Ñка вÑе це має. Удачі вам з Kolf!
    -- Jason Katz-Brown
    +Comment[ven]=

    Zwithu zwothe

    Henefha buli li re nazwo zwothe.Diphineni nga Kolf!
    -- Jason Katz-Brown
    +Comment[xh]=

    Yonke into

    Nanku umngxunya oqulathe konke. Yiba nolonwabo ne Kolf!
    -- Jason Katz-Brown
    +Comment[xx]=xx

    Everything

    Here's a hole that has it all. Have fun with Kolf!
    -- Jason Katz-Brown
    xx +Comment[zh_CN]=

    å„ç§éšœç¢

    这是一个有å„ç§éšœç¢çš„çƒæ´žã€‚ç¥æ‚¨çŽ©å¾—愉快ï¼
    -- Jason Katz-Brown
    +Comment[zh_TW]=

    æ¯æ¨£æ±è¥¿

    這是個什麼都有的洞。希望您在 Kolf 玩得開心ï¼
    -- Jason Katz-Brown
    +Comment[zu]=

    Yonke into

    Nansi imbobo enako konke. Zijabulise nge-Kolf!
    -- Jason Katz-Brown
    +botWallVisible=true +height=132 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=253 + +[11-slope@13,101|10] +grade=7 +gradient=Horizontal +height=157 +reversed=true +stuckOnGround=true +width=125 + +[11-slope@231,141|15] +grade=3 +gradient=Vertical +height=122 +reversed=false +stuckOnGround=false +width=156 + +[11-slope@309,264|23] +grade=3 +gradient=Opposite Diagonal +height=124 +reversed=true +stuckOnGround=false +width=77 + +[11-wall@0,0|12] +endPoint=231,255 +startPoint=93,391 + +[11-wall@0,0|13] +endPoint=229,118 +startPoint=231,255 + +[11-wall@0,0|22] +endPoint=390,389 +startPoint=311,295 + +[11-windmill@231,255|14] +botWallVisible=false +bottom=true +height=40 +leftWallVisible=true +rightWallVisible=true +speed=2 +topWallVisible=false +width=80 + +[2-ball@38,373] +dummykey=true + +[2-cup@129,209|6] +dummykey=true + +[2-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[2-sign@1,5|3] +Comment=

    Slopes

    Slopes are slanted areas of ground that push the ball in the direction that they slope. This direction is shown when you choose Hole->Show Info. +Comment[bg]=

    Склонове

    Това Ñа наклонени земни учаÑтъци, които изтлаÑкват топката по поÑока на Ñ‚ÐµÑ…Ð½Ð¸Ñ Ð½Ð°ÐºÐ»Ð¾Ð½. За да видите поÑоката изберете от менюто Дупка/ИнформациÑ. +Comment[bn]=

    ঢাল

    ঢাল হল মাটির মধà§à¦¯à§‡ নিচৠসà§à¦¥à¦¾à¦¨ যা বলকে কোন নিরà§à¦¦à¦¿à¦·à§à¦Ÿ দিকে নিয়ে যায়। বলের à¦à¦‡ গতিপথ দেখার জনà§à¦¯ গরà§à¦¤->তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ +Comment[bs]=

    Nagibi

    Nagibi su nakošene oblasti tla koje guraju lopticu u smijeru u kojem padaju. Ovaj smjer se može vidjeti kada izaberete Rupa->Prikaži informacije. +Comment[ca]=

    Baixada

    Les baixades són àrees ratllades en el terra que empenyen la bola en la direcció de la baixada. Aquesta direcció se selecciona quan escolliu Forat->Mostra informació. +Comment[da]=

    Skråninger

    Skråninger på banen skubber bolden i den retning de hælder. Denne retning vises når du vælger Hul -> Vis info. +Comment[de]=

    Hügel

    Hügel sind kurvige Bodenstücke, die den Ball in die eine oder andere Richtung ablenken. Diese Richtung ist sichtbar, wenn man Loch->Informationen anzeigen auswählt. +Comment[es]=

    Pendientes

    Las pendientes son áreas rayadas de suelo que empujan la bola en la dirección de la pendiente. Esta dirección se muestra cuando se selecciona Hoyo->Mostrar información. +Comment[et]=

    Kallakud

    Kallakud on sellised maapinna osad, mis lasevad pallil veereda oma kalde suunas. Seda suunda võid näha, kui valid Rada->Näita infot. +Comment[fi]=

    Kallistukset

    Kallistukset ovat alueita, jotka painavat palloa mäen suuntaan. Tämä suunta näytetään, kun valitset Reikä->Näytä tiedot. +Comment[fr]=

    Pentes

    Les pentes sont des zones inclinées du sol qui poussent la balle dans la direction où elles penchent. Cette direction est montrée quand vous choisissez Trou / Afficher les informations. +Comment[gl]=

    Pendentes

    As pendentes son zonas raiadas do chan que empuxan a bola no sentido da pendente. Este sentido amósase cando se selecciona Buraco->Amosar Información. +Comment[he]=

    מדרוני×

    ×ž×“×¨×•× ×™× ×”× ×ž×©×˜×—×™× ×ž×©×•×¤×¢×™× ×©×“×•×—×¤×™× ×ת הכדור לפי כיוון השיפוע שלה×. כיוון ×–×” מוצג ×›×שר ×תה בוחר בגומה->הצג מידע. +Comment[hi]=

    ढलान

    ढलान भूमि के à¤à¥à¤•à¥‡ हà¥à¤ कà¥à¤·à¥‡à¤¤à¥à¤° होते हैं जो गेंद को ढलान की ओर ढकेलते हैं. यह दिशा दिखाई जाती है जब आप चà¥à¤¨à¤¤à¥‡ हैंहोल->जानकारी दिखाà¤à¤. +Comment[hu]=

    Lejtők

    A lejtős területeken a golyók a lejtő irányában gurulnak tovább. Az irány megjelenítéséhez válassza a Lyuk->Információk menüpontot. +Comment[is]=

    Hallar

    Hallar eru hallandi svæði í brautinni sem ýta boltanum í þá átt sem hún hallar. Þessi stefna er sýnd þegar þú velur Hola->Sýna upplýsingar +Comment[it]=

    Pendii

    I pendii sono zone di prato in pendenza che fanno scendere la pallina lungo la discesa. La direzione di discesa viene mostrata scegliendo Buca->Mostra informazioni. +Comment[ja]=

    æ–œé¢

    æ–œé¢ã¯ãƒœãƒ¼ãƒ«ã‚’å‹•ã‹ã™å‚¾ã„ã¦ã„る地é¢ã§ã™ã€‚ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€æ–œé¢ã®å‹¾é…ã®æ–¹å‘ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ +Comment[mk]=

    Ðаклони

    Ðаклоните Ñе закривени површини на земјата кои што ја туркаат топката во наÑоката во која Ñе Ñпуштаат. Оваа наÑока Ñе прикажува кога ќе изберете Дупка->Прикажи информации +Comment[nb]=

    Skråninger

    Skråninger er områder som får ballen til å rulle i den retningen de skråner. Du kan se retningen ved å velge Hull->Vis info. +Comment[nl]=

    Heuvels

    Heuvels zijn hellende stukken grond die de bal in een bepaalde richting afbuigen. De richting ervan wordt getoond als u kiest voor Hole->Info tonen. +Comment[nn]=

    Bakkar

    I ein bakke vil ballen rulla i ein viss retning. Du kan sjå retningen med Hol->Vis info. +Comment[pl]=

    Zbocza

    Zbocza to nachylone obszary spychające piłkę w kierunku w którym opadają. Kierunek opadania zostanie pokazany, gdy wybierzesz Dołek->Pokaż informacje. +Comment[pt]=

    Rampas

    As rampas são zonas inclinadas de terra que empurram a bola na direcção da inclinação. Esta direcção é mostrada quando escolhe o Buraco->Mostrar a Informação. +Comment[pt_BR]=

    Ladeiras

    Ladeiras são as áreas inclinadas de terra que empurram a bola na direção que inclinam. Esta direção é mostrada quando você escolhe Buraco-> Mostrar Informação. +Comment[ru]=

    Горки

    Ðа горке мÑч будет катитьÑÑ Ð¿Ð¾Ð´ уклон. Ðаправление будет показано, еÑли вы выберите Лунка->СведениÑ. +Comment[sk]=

    Svahy

    Svahy sú naklonené miesta, ktoré usmerňujú loptiÄku v smere, v ktorom sú sklonené. Smer sa dá zobraziÅ¥ výberom Jamka->ZobraziÅ¥ informácie. +Comment[sl]=

    Strmine

    Strmine so nagnjene površine, ki porinejo žogico v smer strmine. Ta smer je prikazana, ko izberete Luknja -> Pokaži informacije. +Comment[sr]=

    Ðагиби

    Ðагиби Ñу закошене облаÑти тла које гурају лоптицу у Ñмеру у коме Ñу нагнуте. Тај Ñмер Ñе приказује када изаберете Рупа->Прикажи информације. +Comment[sr@Latn]=

    Nagibi

    Nagibi su zakošene oblasti tla koje guraju lopticu u smeru u kome su nagnute. Taj smer se prikazuje kada izaberete Rupa->Prikaži informacije. +Comment[sv]=

    Sluttningar

    Sluttningar är lutande markområden som rullar bollen i riktningen de lutar. Riktningen visas när du väljer Hål->Visa information. +Comment[ta]=

    சரிவà¯à®•à®³à¯

    சரிவà¯à®•à®³à¯ ஒர௠நிலதà¯à®¤à®¿à®©à¯ சாயà¯à®¨à¯à®¤ பரபà¯à®ªà®³à®µà¯. சரிவின௠திசையில௠அத௠பநà¯à®¤à¯ˆà®¤à¯ தளà¯à®³à¯à®®à¯.நீஙà¯à®•à®³à¯ தகவலை காடà¯à®Ÿà¯ எனà¯à®ªà®¤à¯ˆ தேரà¯à®¨à¯à®¤à¯†à®Ÿà¯à®•à¯à®•à¯à®®à¯ போத௠இதன௠திசை Hole-> காடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®®à¯. . +Comment[tg]=

    Теппаҳо

    Дар теппачаҳо тӯб ба хамӣ меғелад. Ин Ñамт нишон дода мешавад, агар шумо -Сӯрохӣ->Ðишондиҳии Маълумот-ро интихоб кунед. +Comment[tr]=

    EÄŸimler

    Eğimler yerin değişik eğimlerde bulunmasını sağlar böylece top eğimlerin yönünde hareket eder. Bu yön Delik->Bilgi Göster seçtiğinizde gösterilir. +Comment[uk]=

    Схили

    Схили - це похилі ділÑнки землі6 Ñкі підштовхують м'Ñч у напрÑмку нахилу. Цей напрÑмок видно, коли ви вибираєтеЛунка->Показати інформацію. +Comment[ven]=

    Zwivhanga

    Zwivhanga ndi vhethu ho tshingamaho hune ha sukumedza bola uri i yele kha siya le ha tshingamela hone.Siya heli li sumbedzwa musi ni tshi nanga Buli->Sumbedza zwidodombedzwa. +Comment[xh]=

    Amathambeka

    Amathambeka ngamacala lalele kwiindawo zebala alityhala ibhola kwicala athambekele kulo. Le ndlela iboniswe when you choose Hole->Show Info. +Comment[xx]=xx

    Slopes

    Slopes are slanted areas of ground that push the ball in the direction that they slope. This direction is shown when you choose Hole->Show Info.xx +Comment[zh_CN]=

    æ–œå¡

    æ–œå¡å°±æ˜¯åœºåœ°ä¸Šçš„倾斜区域,这会使çƒæœå®ƒä»¬å€¾æ–œçš„æ–¹å‘è¿åŠ¨ã€‚该方å‘会在您选择çƒæ´ž->显示信æ¯æ—¶æ˜¾ç¤ºå‡ºæ¥ã€‚ +Comment[zh_TW]=

    æ–œå¡

    æ–œå¡æ˜¯å ´åœ°ä¸­å‚¾æ–œçš„å€åŸŸï¼Œä¸¦ä¸”會將çƒæŽ¨å‘它們傾斜的方å‘。這個方å‘會在您é¸æ“‡æ´ž->顯示資訊時顯示。 +Comment[zu]=

    Okusantaba

    Okusantaba izindawo ezitshekile zomhlaba ezidudula ibhola liye endleleni lapho zitshekele ngakhona. Lendlela iyakhombiswa uma ukhetha Imbobo->Bonisa ulwazi. +botWallVisible=true +height=187 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=392 + +[2-sign@197,198|5] +Comment=Hit the ball onto the slope and let it roll into cup. +Comment[bg]=Ðко Ñте на Ñклон, ударете топката леко и Ñ Ð¾Ñтавете да Ñе Ð¸Ð·Ñ‚ÑŠÑ€ÐºÐ°Ð»Ñ Ð´Ð¾ дупката. +Comment[bn]=বলে আঘাত করে ঢালের মধà§à¦¯à§‡ ঠেলে দিন à¦à¦¬à¦‚ তা নিজে থেকেই গড়িয়ে গড়িয়ে কাপের দিকে চলে যাবে। +Comment[bs]=Å utnite loptu prema nagibu i pustite da se spusti u rupu. +Comment[ca]=Pica la pilota en la baixada i deixa'l·la arribar fins a la copa. +Comment[da]=SlÃ¥ bolden op pÃ¥ skrÃ¥ningen og lad den rulle ned i fordybningen. +Comment[de]=Spielen Sie den Ball aufs Grün und lassen Sie ihn ins Loch rollen +Comment[es]=Golpear la bola en la pendiente y dejarla rodar hasta la copa. +Comment[et]=Löö pall kallakule ja lase tal auku veereda. +Comment[fi]=Lyä pallo rinteeseen ja anna sen vieriä kuppiin. +Comment[fr]=Frappez la balle dans la pente et laissez-la rouler dans le trou. +Comment[gl]=Golpee a bola na pendente e déixea rodar ata a copa. +Comment[he]=חבוט בכדור כך שיטפס על המדרון, ותן לו להתגלגל לתוך הגומה. +Comment[hi]=गेंद पर पà¥à¤°à¤¹à¤¾à¤° करें ताकि ढलान पर लà¥à¤¢à¤¼à¤•à¤¤à¤¾ हूआ कप में चला जाà¤. +Comment[hu]=Ãœsse a golyót a lejtÅ‘ irányában és hagyja lefelé gurulni. +Comment[is]=Sláðu á boltann í brekkuna og láttu renna í bollann. +Comment[it]=Batti la pallina verso il pendio e lasciala rotolare in buca. +Comment[ja]=カップã«å…¥ã‚‹ã‚ˆã†ã«æ–œé¢ã«ãƒœãƒ¼ãƒ«ã‚’打ã£ã¦ä¸‹ã•ã„。 +Comment[lv]=Uzsitiet bumbiņu uz nogÄzes un ļaujiet tai ierpot caurumÄ. +Comment[mk]=Удрете ја топката во наклонот и оÑтавете ја да Ñе дотркала до дупката. +Comment[nb]=SlÃ¥ ballen ned skrÃ¥ningen og la den rulle ned i koppen. +Comment[nl]=Speel de bal op de heuvel en laat deze in het gat rollen. +Comment[nn]=SlÃ¥ ballen opp bakken og lat han trilla ned i holet. +Comment[pl]=Uderz piÅ‚kÄ™ na zbocze i pozwól jej stoczyć siÄ™ do doÅ‚ka. +Comment[pt]=Acerta na bola para a rampa e deixe-a rolar para o buraco. +Comment[pt_BR]=Bata na bola em direção à ladeira e deixe-a rolar para dentro do buraco. +Comment[ru]=ЗаброÑьте мÑч на Ñклон и дайте ему закатитьÑÑ Ð² лунку. +Comment[sk]=Trafte svah a nechajte loptiÄku dokotúľaÅ¥ do jamky. +Comment[sl]=Udarite žogico na strmino in jo spravite v luknjico. +Comment[sr]=Пошаљите лоптицу на нагиб и пуÑтите је да Ñе откотрља близу рупе. +Comment[sr@Latn]=PoÅ¡aljite lopticu na nagib i pustite je da se otkotrlja blizu rupe. +Comment[sv]=SlÃ¥ bollen över sluttningen och lÃ¥t den rulla i hÃ¥let. +Comment[ta]=சரிவில௠பநà¯à®¤à¯ˆ உதைதà¯à®¤à®¾à®²à¯ அத௠கோபà¯à®ªà¯ˆà®•à¯à®•à¯à®³à¯ உரà¯à®³à¯à®®à¯. +Comment[tg]=Тӯбро ба теппача партоед ва ба он имкониÑÑ‚ диҳед, ки ба Ñӯрохи дароÑд. +Comment[tr]=EÄŸimdeki topa vurun ve fincana düşmesini saÄŸlayın. +Comment[uk]=Вдарити м'Ñчик на Ñхил Ñ– дати йому закотитиÑÑŒ в лунку. +Comment[ven]=Rwisani bola na mutsiso ni i tendele i kunguwele nga ngomu bigirini. +Comment[xh]=Betha ibhola edongeni uphinde uyeke iqengqelekele ekhaphini. +Comment[xx]=xxHit the ball onto the slope and let it roll into cup.xx +Comment[zh_CN]=把çƒæ‰“到斜å¡ä¸Šï¼Œè®©å®ƒæ»šåˆ°æ¯å­é‡Œã€‚ +Comment[zh_TW]=å°‡çƒæ“Šåˆ°æ–œå¡ä¸Šä¸¦è®“它滾進çƒæ´žã€‚ +Comment[zu]=Shaya ibhola liye kokusantaba ebese uliyeka ligingqikele enkomishini. +botWallVisible=true +height=195 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=197 + +[2-slope@11,237|4] +grade=4 +gradient=Vertical +height=113 +reversed=false +stuckOnGround=false +width=181 + +[3-ball@86,353] +dummykey=true + +[3-cup@229,307|3] +dummykey=true + +[3-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[3-sign@4,4|4] +Comment=Try out these different types of slopes. +Comment[bg]=Тук може, да изпробвате различните видове наклони. +Comment[bn]=বিভিনà§à¦¨ ধরনের à¦à¦‡ ঢালগà§à¦²à§‹ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে দেখà§à¦¨à¥¤ +Comment[bs]=Isprobajte ove razne vrste nagiba. +Comment[ca]=Intenta aquests diferents tipus de baixades. +Comment[da]=Afprøv disse forskellige typer skrÃ¥ninger. +Comment[de]=Probieren Sie diese verschiedenen Typen von Grüns aus +Comment[es]=Intente estos diferentes tipos de pendiente. +Comment[et]=Proovi erinevat tüüpi kallakuid. +Comment[fi]=Kokeile näitä eri kaltevuuksia +Comment[fr]=Essayez ces différents types de pente. +Comment[gl]=Probe estes distintos tipos de pendentes. +Comment[he]=נסה ×ת סוגי ×”×ž×“×¨×•× ×™× ×”×©×•× ×™×. +Comment[hi]=ये भिनà¥à¤¨ पà¥à¤°à¤•à¤¾à¤° के ढलान आजमा देखें. +Comment[hu]=Válogatni lehet a különféle lejtÅ‘k között. +Comment[is]=Reyndu þig við þessar mismunandi brekkur. +Comment[it]= Prova questi diversi tipi di pendio. +Comment[ja]=ã“ã®ã‚¿ã‚¤ãƒ—ã®é•ã†æ–œé¢ã«ãƒœãƒ¼ãƒ«ã‚’打ã£ã¦ã¿ã¦ä¸‹ã•ã„。 +Comment[lv]=IzmÄ“Ä£iniet Å¡Ä«s dažÄdÄs nogÄzes. +Comment[mk]=ИÑпробајте ги различните типови на наклони. +Comment[nb]=Prøv disse forskjellige skrÃ¥ningene. +Comment[nl]=Probeer de verschillende typen heuvels uit. +Comment[nn]=Prøv desse ulike bakketypane. +Comment[pl]=Wypróbuj różne typy zboczy. +Comment[pt]=Experimente estes diferentes tipos de inclinação. +Comment[pt_BR]=Experimente estes diferentes tipos de ladeiras. +Comment[ru]=Попробуйте разные типы Ñклонов. +Comment[sk]=Skúste tieto rôzne sklony. +Comment[sl]=Poskusite razliÄne vrste strmin. +Comment[sr]=ИÑпробајте ове различите типове нагиба. +Comment[sr@Latn]=Isprobajte ove razliÄite tipove nagiba. +Comment[sv]=Prova med de här olika typerna av sluttning. +Comment[ta]=இநà¯à®¤ பலவகைச௠சரிவà¯à®•à®³à¯ˆ à®®à¯à®¯à®±à¯à®šà®¿à®•à¯à®•à®µà¯à®®à¯. +Comment[tg]=Ðавъҳои гуногуни теппачаҳоро иÑтифода баред. +Comment[tr]=Farklı tür eÄŸimleri de denemelisiniz. +Comment[uk]=Спробуйте ці різні типи Ñхилів. +Comment[ven]=Lingedzani dzinwe tshaka dza mitsitso. +Comment[xh]=Zama ezintlobo zahlukeneyo zezlopi. +Comment[xx]=xxTry out these different types of slopes.xx +Comment[zh_CN]=试é这些å„ç§ä¸åŒç±»åž‹çš„æ–œå¡ã€‚ +Comment[zh_TW]=試驗這些ä¸åŒé¡žåž‹çš„æ–œå¡ã€‚ +Comment[zu]=Zama lezizinhlobo ezahlukene zokusantaba +botWallVisible=true +height=87 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=343 + +[3-slope@148,292|5] +grade=4 +gradient=Vertical +height=79 +reversed=true +stuckOnGround=false +width=66 + +[3-slope@15,264|9] +grade=4 +gradient=Diagonal +height=76 +reversed=true +stuckOnGround=false +width=93 + +[3-slope@190,147|6] +grade=4 +gradient=Horizontal +height=80 +reversed=false +stuckOnGround=false +width=71 + +[3-slope@209,239|11] +grade=4 +gradient=Opposite Diagonal +height=42 +reversed=false +stuckOnGround=false +width=46 + +[3-slope@231,315|10] +grade=4 +gradient=Opposite Diagonal +height=67 +reversed=true +stuckOnGround=false +width=92 + +[3-slope@266,240|7] +grade=4 +gradient=Horizontal +height=63 +reversed=true +stuckOnGround=false +width=74 + +[3-slope@285,144|13] +grade=4 +gradient=Elliptic +height=87 +reversed=true +stuckOnGround=false +width=87 + +[3-slope@32,129|8] +grade=4 +gradient=Diagonal +height=72 +reversed=false +stuckOnGround=false +width=90 + +[3-slope@99,204|12] +grade=4 +gradient=Elliptic +height=84 +reversed=false +stuckOnGround=false +width=84 + +[4-ball@34,271] +dummykey=true + +[4-cup@367,353|6] +dummykey=true + +[4-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=3 + +[4-sign@6,3|3] +Comment=The steepness of a slope is shown when you choose Hole->Show Info. Steepness goes from 8 (steepest) to 1 (shallowest). +Comment[bg]=За да видите Ñтръмнината на Ñклона, изберете от менюто Дупка/ИнформациÑ. Ðаклонът Ñе Ð¸Ð·Ð¼ÐµÐ½Ñ Ð¾Ñ‚ 8 (най-Ñтръмен) до 1 (най-полегат). +Comment[bn]=ঢালের বকà§à¦°à¦¤à¦¾ দেখার জনà§à¦¯ গরà§à¦¤->তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ বকà§à¦°à¦¤à¦¾à¦° বà§à¦¯à¦¾à¦ªà§à¦¤à¦¿ ১ ( অগভীরতম ) থেকে ৮ ( বকà§à¦°à¦¤à¦® ) পরà§à¦¯à¦¨à§à¦¤à¥¤ +Comment[bs]=Stupanj nagiba možete dobiti ako izaberete Rupa->Prikaži informacije. Nagnutost se kreće od 8 (najveća) do 1 (najmanja). +Comment[ca]=La intensitat de la baixada es mostra quan escolliu Forat->Mostra informació. La força va des de 8 (màxim) a 1 (mínim). +Comment[da]=Du kan se hvor stejl en skrÃ¥ning er ved at vælge Hul -> Vis info. Stejlhed gÃ¥r fra 8 (stejlest) til 1 (fladest). +Comment[de]=Die Steigung eines Grüns bekommen Sie angezeigt, wenn Sie Loch->Informationen anzeigen auswählen. Steigungen gehen von 8 (sehr steil) bis 1 (sehr flach). +Comment[es]=La intensidad de la pendiente se muestra cuando se selecciona Hoyo->Mostrar información. La intensidad va de 8 (máximo) a 1 (mínimo). +Comment[et]=Kallaku kallet näeb, kui valid Rada->Näita infot. Kalle jääb 8 (kõige järsem) ja 1 (laugeim) vahele. +Comment[fi]=Mäen kaltevuus näytetään kun valitset Reikä->Näytä tiedot. Kaltevuus on välillä 8 (jyrkin) - 1 (loivin) +Comment[fr]=L'inclinaison d'une pente est montrée quand vous choisissez Trou / Afficher les informations. L'inclinaison va de 8 (le plus pentu) à 1 (le moins pentu). +Comment[gl]=A inclinación dunha pendente amósase cando se elixe Burato->Amosar Información. A inclinación vai dende 8 (máximo) ata 1 (mínimo). +Comment[he]=השיפוע של המדרון מוצג ×›×שר ×תה בוחר בגומה->הצג מידע. השיפוע × ×¢ בין 8 (התלול ביותר) ל־1 (המתון ביותר). +Comment[hi]=ढलान की ढाल दिखाई देगी जब आप चà¥à¤¨à¥‡à¤‚गेहोल->जानकारी दिखाà¤à¤. ढाल 8 (सबसे तेज ढाल) से 1 (सबसे कम ढाल) तक है. +Comment[hu]=A lejtÅ‘ meredeksége megtudható a Lyuk->Információ menüponttal. A meredekség értéke 8-tól (legmeredekebb) 1-ig (leglaposabb) terjedhet. +Comment[is]=Halli brekkunnar er sýndur þegar þú velur Hola->Sýna upplýsingar. Hallinn fer frá 8 (brattast) til 1 (flatast). +Comment[it]= Il grado di inclinazione di un pendio viene mostrato quando scegli Buca->Mostra Infornazioni nel menu. La ripidezza va da 8 (il più scosceso) a 1 (il più dolce). +Comment[ja]=ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€ æ–œé¢ã®å‹¾é…ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚勾é…㯠8 (急) ã‹ã‚‰ 1 (ç·©ã‚„ã‹) ã¾ã§ã®ã‚¹ã‚±ãƒ¼ãƒ«ãŒã‚ã‚Šã¾ã™ã€‚ +Comment[lv]=NogÄzes slÄ«pumu var apskatÄ«ties, ja izvÄ“las Caurums->RÄdÄ«t informÄciju. SlÄ«pums ir no 8 (slÄ«pÄkais) lÄ«dz 1 (lÄ“zenÄkais). +Comment[mk]=СтрмноÑта на наклонот е прикажана кога избирате Дупка->Прикажи информација. СтрмноÑта Ñе движи од 8 (најÑтрмна) до 1 (најблага). +Comment[nb]=Brattheten pÃ¥ en skrÃ¥ning vises hvis du velger Hull->Vis info. Brattheten gÃ¥r fra 8 (brattest) til 1 (slakest). +Comment[nl]=De stijging van een een heuvel wordt getoond als u kiest voor Hole->info tonen. De stijging gaat van 8 (steilste) tot 1 (vlakste). +Comment[nn]=Du kan sjÃ¥ kor bratt bakken er med Hol->Vis info. Den brattaste bakken har verdien 8. +Comment[pl]=Gdy wybierzesz DoÅ‚ek->Pokaż informacje pokazany zostanie kÄ…t nachylenia zbocza. KÄ…t nachylenia może przybierać wartoÅ›ci od 8 (najbardziej stromy) do 1 (najbardziej pÅ‚aski). +Comment[pt]=A inclinação de uma rampa é mostrada quando escolhe o Buraco->Mostrar a Informação. A inclinação vai de 8 (a pique) até 1 (liso). +Comment[pt_BR]=A inclinação de uma ladeira é mostrada quando você escolhe Buraco-> Mostrar Informação. O valor da inclinação da ladeira vai de 8 (mais íngreme) para 1 (mais baixo/plano). +Comment[ru]=Крутизна Ñклона показываетÑÑ Ð¿Ñ€Ð¸ нажатии Лунка->СведениÑ. Она менÑетÑÑ Ð¾Ñ‚ 8 (крутые) до 1 (пологие). +Comment[sk]=Sklon svahu sa zobrazí po výbere Jamka->ZobraziÅ¥ informácie. Sklon môže byÅ¥ od 8 (najstrmÅ¡ie) až po 1 (najrovnejÅ¡ie). +Comment[sl]=Nagib strmine je prikazan, ko izberete Luknja -> Pokaži informacije. Nagib gre od 8 (najbolj strmo) do 1 (najmanj strmo). +Comment[sr]=Стрмина нагиба Ñе приказује када изаберете Рупа->Прикажи информације. Стрмина иде од 8 (најÑтрмије) до 1 (најплиће). +Comment[sr@Latn]=Strmina nagiba se prikazuje kada izaberete Rupa->Prikaži informacije. Strmina ide od 8 (najstrmije) do 1 (najpliće). +Comment[sv]=En sluttnings lutning visas när du väljer HÃ¥l->Visa information. Lutningen sträcker sig frÃ¥n 8 (brantast) till 1 (minst brant). +Comment[ta]=தகவலை காடà¯à®Ÿà¯ எனà¯à®ªà®¤à¯ˆ நீஙà¯à®•à®³à¯ தேரà¯à®µà¯ செயà¯à®¯à¯à®®à¯ பொழà¯à®¤à¯ சரிவின௠செஙà¯à®•à¯à®¤à¯à®¤à¯ காடà¯à®Ÿà®ªà¯à®ªà®Ÿà¯à®®à¯.. ஆழம௠8(அதிக அளவ௠செஙà¯à®•à¯à®¤à¯à®¤à¯)லிரà¯à®¨à¯à®¤à¯ to 1(கà¯à®±à¯ˆà®¨à¯à®¤ ஆழமà¯)வரை செலà¯à®•à®¿à®±à®¤à¯. +Comment[tg]=Мушкилоти теппачаҳо дар натиҷаи интихоби Сӯрохӣ->Ðишондиҳии Маълумот, нишон дода мешаванд. Он аз 8 (шитоб) то 1 (ҳамвор) иваз мешавад. +Comment[uk]=Ðахил Ñхилу видно, коли ви вибираєте Лунка->Показати інформацію. Ðахил йде від 8 (найкрутіший) до 1 (пологий). +Comment[xh]=Ukwenyukela kwethambeka kuboniswa xa ukhetha Umngxuma->Bonisa ulwazi. Ukwenyukela buqala kwisibhozo (eyona enyukeleyo) ukuya kwenye (Eyona enganzulwanga). +Comment[xx]=xxThe steepness of a slope is shown when you choose Hole->Show Info. Steepness goes from 8 (steepest) to 1 (shallowest).xx +Comment[zh_CN]=æ–œå¡çš„å¡åº¦ä¼šåœ¨æ‚¨é€‰æ‹©çƒæ´ž->显示信æ¯æ—¶æ˜¾ç¤ºå‡ºæ¥ã€‚å¡åº¦å€¼ä¸º 8 (陡峭) 到 1 (平缓)。 +Comment[zh_TW]=æ–œå¡çš„傾斜度會在您é¸æ“‡æ´ž->顯示資訊時顯示。傾斜度å¯ä»¥å¾ž 8 (最陡)到 1 (最緩)。 +Comment[zu]=Izinga lokutsheka kokusantaba kuyakhonjiswa uma ukhetha Imbobo-> Khombisa ulwazi. Izinga lokutsheka lisuka ku-8 (okutsheke kakhulu) kuya ku 1 (okutsheke kancane). +botWallVisible=true +height=158 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=384 + +[4-slope@214,271|5] +grade=6 +gradient=Vertical +height=106 +reversed=false +stuckOnGround=false +width=103 + +[4-slope@61,255|4] +grade=2 +gradient=Vertical +height=122 +reversed=false +stuckOnGround=false +width=111 + +[5-ball@200,200] +dummykey=true + +[5-cup@204,356|7] +dummykey=true + +[5-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[5-sign@3,5|3] +Comment=

    Walls

    Bounce the ball off of the red walls. +Comment[bg]=

    Стени

    Стените изтлаÑкват топката, когато Ñе удари в Ñ‚ÑÑ…. +Comment[bn]=

    দেয়াল

    লাল দেয়ালে আঘাত করে বলকে ফিরিয়ে আনà§à¦¨à¥¤ +Comment[bs]=

    Zidovi

    Lopticu možete odbijati od crvene zidove. +Comment[ca]=

    Baranes

    Rebota la bola fora de les baranes vermelles. +Comment[da]=

    Vægge

    Få bolden til at støde ind i de røde vægge og hoppe tilbage. +Comment[de]=

    Wände

    Schlagen Sie den Ball von den roten Wänden weg +Comment[es]=

    Paredes

    Rebota la pelota fuera de las paredes rojas. +Comment[et]=

    Seinad

    Pall põrkab punastelt seintelt tagasi. +Comment[fi]=

    Seinät

    Lyö kimmoke punaisista seinistä. +Comment[fr]=

    Murs

    La balle rebondit sur les murs rouges. +Comment[gl]=

    Balados

    .Rebote a bola fora dos valados vermellos. +Comment[he]=

    קירות

    ×’×¨×•× ×œ×›×“×•×¨ לקפוץ חזרה מהקירות ×”×דומי×. +Comment[hi]=

    दीवारें

    गेंद को लाल दीवार पर से उछालें +Comment[hu]=

    Falak

    Pattintsa vissza a golyót a piros falakról. +Comment[is]=

    Veggir

    Boltarnir skoppa af rauðu veggjunum. +Comment[it]=

    Muri

    Fai rimbalzare la pallina contro i muri di mattoni. +Comment[ja]=

    å£

    赤ã„å£ã«ãƒœãƒ¼ãƒ«ã‚’ãƒã‚¦ãƒ³ã‚¹ã•ã›ã¦ä¸‹ã•ã„。 +Comment[lv]=

    Sienas

    Bumbiņa atlec no sarkanajÄm sienÄm. +Comment[mk]=

    Ѕидови

    Одбијте ја топката од црвените ѕидови. +Comment[nb]=

    Vegger

    Sprett ballen vekk fra de røde veggene. +Comment[nl]=

    Muren

    Leid de bal weg van de rode muren. +Comment[nn]=

    Veggar

    Ballen kan spretta mot dei raude veggane. +Comment[pl]=

    Åšciany

    Odbijają piłkę od czerwonych ścian. +Comment[pt]=

    Paredes

    Faça tabelas com a bola nas paredes vermelhas. +Comment[pt_BR]=

    Paredes

    Arremesse a bola fora das paredes vermelhas. +Comment[ru]=

    Стены

    Шарик отÑкакивает от краÑных Ñтен. +Comment[sk]=

    Steny

    Použite odraz loptiÄky od Äervených stien. +Comment[sl]=

    Zidovi

    Žogica se odbije od rdeÄih zidov. +Comment[sr]=

    Зидови

    Одбијте лоптицу од црвених зидова. +Comment[sr@Latn]=

    Zidovi

    Odbijte lopticu od crvenih zidova. +Comment[sv]=

    Väggar

    Studsa bollen från de röda väggarna. +Comment[ta]=

    சà¯à®µà®°à¯à®•à®³à¯

    சிவபà¯à®ªà¯ சà¯à®µà®°à®¿à®²à¯ பநà¯à®¤à¯ˆ தடà¯à®Ÿà®µà¯à®®à¯. +Comment[tg]=

    Деворҳо

    Тӯбча ба деворҳои Ñурх зада паÑмегардад. +Comment[tr]=

    Duvarlar

    Topları kırmızı duvardan sektirin. +Comment[uk]=

    Стіни

    Ðœ'Ñчик відÑкакує від червоних Ñтін. +Comment[ven]=

    Mbondo

    Bammbisani bola kha luvhondo lutswuku. +Comment[xh]=

    Amadonga

    Betha phantsi ibhola ngaphandle kwamadonga abomvu. +Comment[xx]=xx

    Walls

    Bounce the ball off of the red walls.xx +Comment[zh_CN]=

    短墙

    通过红墙将çƒå弹。 +Comment[zh_TW]=

    牆

    çƒç¢°åˆ°ç´…色的牆彈回。 +Comment[zu]=

    Izindonga

    Bampisa ibhola ezindongeni ezibomvu. +botWallVisible=true +height=117 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=371 + +[5-wall@0,0|4] +endPoint=104,201 +startPoint=55,329 + +[5-wall@0,0|5] +endPoint=293,388 +startPoint=345,252 + +[5-wall@0,0|6] +endPoint=278,266 +startPoint=139,283 + +[6-ball@65,361] +dummykey=true + +[6-cup@362,248|31] +dummykey=true + +[6-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=4 + +[6-puddle@122,326|3] +changeEnabled=false +changeEvery=50 +height=44 +width=138 + +[6-puddle@319,408|14] +changeEnabled=false +changeEvery=50 +height=120 +width=208 + +[6-puddle@369,394|12] +changeEnabled=false +changeEvery=50 +height=240 +width=198 + +[6-sand@202,221|33] +changeEnabled=false +changeEvery=50 +height=134 +width=74 + +[6-sand@213,260|11] +changeEnabled=false +changeEvery=50 +height=60 +width=78 + +[6-sign@3,152|37] +Comment=

    Sand

    Sand is yellow, and slows your ball down. +Comment[bg]=

    ПÑÑъци

    ПÑÑъците Ñа жълти и забавÑÑ‚ топката. +Comment[bn]=

    বালি

    বালির রং হলà§à¦¦, à¦à¦¬à¦‚ à¦à¦Ÿà¦¿ বলের গতি হà§à¦°à¦¾à¦¸ করে। +Comment[bs]=

    Pijesak

    Pijesak je žute boje i usporava vašu lopticu. +Comment[ca]=

    Sorra

    La sorra és grogra i frena a la pilota. +Comment[da]=

    Sand

    Sand er gult og tager farten af din bold. +Comment[de]=

    Sand

    Sand ist gelb, und verlangsamt Ihren Ball. +Comment[el]=

    Άμμος

    Η άμμος είναι κίτÏινη και επιβÏαδÏνει την μπάλα σας. +Comment[es]=

    Arena

    La arena es amarilla, y ralentiza su pelota. +Comment[et]=

    Liiv

    Liiv on kollane ja aeglustab palli liikumist. +Comment[fi]=

    Hiekka

    Hiekka on keltaista, ja hidastaa palloasi. +Comment[fr]=

    Sable

    Le sable est jaune et ralentit votre balle. +Comment[gl]=

    Area

    A area é amarela, e fai que a bola vaia máis a modo. +Comment[he]=

    חול

    חול, המוצג בצבע צהוב, מ×ט ×ת הכדור שלך. +Comment[hi]=

    रेत

    रेत पीली है, तथा आपके गेंद को धीमा करती है. +Comment[hu]=

    Homok

    A sárga színű homok lelassítja a golyó mozgását. +Comment[is]=

    Sandur

    Sandur er gulur og hægir á boltanum. +Comment[it]=

    Bunker

    il bunker di sabbia è giallo e frena la pallina. +Comment[ja]=

    ç ‚

    ç ‚ã¯é»„色ã§ã€ãƒœãƒ¼ãƒ«ã¨æŽ¥ã™ã‚‹ã¨é€Ÿåº¦ã‚’è½ã—ã¾ã™ã€‚ +Comment[lv]=

    Smiltis

    Smiltis ir dzeltenas un palēnina Jūsu bumbas ripojumu. +Comment[mk]=

    ПеÑок

    ПеÑокот е жолт и ја уÑпорува топката. +Comment[nb]=

    Sand

    Sand er gul, og sakker farten på ballen. +Comment[nl]=

    Zand

    Zand is geel, en vertraagt uw bal. +Comment[nn]=

    Sand

    Dei gule felta er sand som seinkar ballen. +Comment[pl]=

    Piasek

    Piasek jest żółty i spowalnia twoją piłkę. +Comment[pt]=

    Areia

    Areia é amarela e reduz a velocidades das bolas. +Comment[pt_BR]=

    Areia

    A areia é amarela, e faz sua bola andar mais devagar. +Comment[ru]=

    ПеÑок

    ПеÑок жёлтый, и шарик на нем тормозит. +Comment[sk]=

    Piesok

    Piesok je žltý a spomaľuje loptiÄku. +Comment[sl]=

    Pesek

    Pesek je rumen in upoÄasni vaÅ¡o žogico. +Comment[sr]=

    ПеÑак

    ПеÑак је жут и уÑпорава вашу лоптицу. +Comment[sr@Latn]=

    Pesak

    Pesak je žut i usporava vašu lopticu. +Comment[sv]=

    Bunkrar

    Bunkrar är gula, och saktar ner bollen. +Comment[ta]=

    மணலà¯

    மஞà¯à®šà®³à®¾à®© மணல௠உஙà¯à®•à®³à¯ பநà¯à®¤à®¿à®©à¯ வேகதà¯à®¤à¯ˆ கà¯à®±à¯ˆà®•à¯à®•à¯à®®à¯. +Comment[tg]=

    Қум

    Қум зард аÑÑ‚ ва тӯби шуморо ÑуÑÑ‚ мегардонад. +Comment[tr]=

    Kum

    Sarı renkli kumluk bölgeler topunuzu yavaşlatır. +Comment[uk]=

    ПіÑок

    ПіÑок жовтий; він ÑповільнÑÑ” рух м'Ñчика. +Comment[ven]=

    Mutavha

    Mutavha ndi yellow, fhungudzani luvhilo lwa bola yanu. +Comment[xh]=

    Isanti

    Isanti etyheli, ucothise ibhola phantsi. +Comment[xx]=xx

    Sand

    Sand is yellow, and slows your ball down.xx +Comment[zh_CN]=

    沙地

    沙地是黄色的,å¯ä»¥å‡æ…¢çƒé€Ÿã€‚ +Comment[zh_TW]=

    æ²™

    沙是黃色的,而且會減慢您çƒçš„速度。 +Comment[zu]=

    Isihlabathi

    Isihlabathi siqanda, kanye sihambisa ibhola kancane. +botWallVisible=true +height=147 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=138 + +[6-sign@3,5|4] +Comment=

    Puddles (Water)

    Hitting into a puddle (blue) adds a penalty stroke to your score, and your ball is placed outside the puddle. +Comment[bg]=

    Локви

    Падането на топката в локва, Ð¿Ñ€Ð¸Ð±Ð°Ð²Ñ Ð½Ð°ÐºÐ°Ð·Ð°Ñ‚ÐµÐ»ÐµÐ½ удар към резултата и топката Ñе изважда от локвата за ÑÐ»ÐµÐ´Ð²Ð°Ñ‰Ð¸Ñ ÑƒÐ´Ð°Ñ€. +Comment[bn]=

    ডোবা

    ডোবার (নীল রঙের) মধà§à¦¯à§‡ বল ফেললে আপনার সà§à¦•à§‹à¦° কমে যাবে, তবে বলটি ডোবা থেকে তà§à¦²à§‡ দেওয়া হবে। +Comment[bs]=

    Bare (Voda)

    Pogodak u baru (plava boja) dodaje kaznene bodove na vaš rezultat, a loptica se premješta izvan bare. +Comment[ca]=

    Basses

    Passar per una bassa (blau) afegirà una penalització a la vostra puntuació i la pilotarà serà emplaçada fora de la bassa. +Comment[da]=

    Vandhuller

    Rammer du et vandhul (blåt) får du strafpoint, og din bold placeres uden for vandhullet. +Comment[de]=

    Pfützen (Wasser)

    Wenn Sie in eine Pfütze hineinspielen (blau dargestellt), bekommen Sie einen zusätzlichen Strafpunkt, und Ihr Ball wird außerhalb der Pfütze auf dem Spielfeld platziert. +Comment[es]=

    Charcos (agua)

    Pisar un charco (azul) añade una penalización a su puntuación, y su pelota es situada fuera del charco. +Comment[et]=

    Lombid (vesi)

    Kui sul õnnestub pall vette (sinine) lüüa, karistatakse sind ühe löögi lisamisega ja pall paigutatakse uueks löögiks lombi äärde. +Comment[fi]=

    Lammet (Vesi)

    Lampeen pyöminen (sininen) lisää rangaistuslyönnin tulokseesi ja pallosi asetetaan lammen ulkopuolelle. +Comment[fr]=

    Rivières

    Tomber dans une mare (bleue) ajoute une pénalité à votre score, et votre balle est placée hors de la rivière. +Comment[gl]=

    Pozas (Auga)

    Caer nunha poza (azul) engade un golpe de penalización á súa puntuación, e a bola sitúase fora da poza. +Comment[he]=

    שלוליות (מי×)

    כניסה לשלולית (בכחול) מוסיפה חבטת עונשין לניקוד שלך. הכדור שלך יונח מחוץ לשלולית. +Comment[hi]=

    डबरी (पानी)

    डबरी(नीली) में पà¥à¤°à¤¹à¤¾à¤° करने पर आपके अंक में पेनालà¥à¤Ÿà¥€ सà¥à¤Ÿà¥à¤°à¥‹à¤• जोड़ता है तथा आपकी गेंद डबरी के बाहर रख दी जाती है. +Comment[hu]=

    Pocsolyák

    Ha a golyó pocsolyába esik (a víz kék színű), akkor pontlevonást kap, és a golyót a pocsolya mellé helyezik. +Comment[is]=

    Pollar (vatn)

    Ef þú hittir í pollinn (blár) þá færð þú refsistig, og boltinn verðu settur við hlið pollsins. +Comment[it]=

    Laghetti (acqua)

    Se mandi la pallina in un laghetto, ti viene aggiunto un colpo di penalità al punteggio e la tua pallina viene collocata fuori dal laghetto. +Comment[ja]=

    æ±  (æ°´)

    é’ã„æ± ã«å…¥ã‚‹ã¨ã€1ショットを罰ã—ã¦ã€ æ± ã®è¿‘ãã«ãƒœãƒ¼ãƒ«ãŒç½®ã‹ã‚Œã¾ã™ã€‚ +Comment[mk]=

    Бари (Вода)

    Удирањето во барата (Ñино) додава казнен удар на вашите поени, а вашата топка е поÑтавена надвор од барата. +Comment[nb]=

    Pytter (Vann)

    Hvis du treffer en pytt (blå) gir deg et straffeslag, og ballen din plasseres utenfor pytten. +Comment[nl]=

    Poelen (water)

    Als u de bal in een poel (blauw gebied) slaat, dan krijgt u er een strafpunt bij. Uw bal wordt buiten de poel op het speelveld geplaatst. +Comment[nn]=

    Pyttar (vatn)

    Dersom du treff ein pytt (blå), får du eit straffeslag og ballen vert plassert utanfor pytten. +Comment[pl]=

    Oczka wodne

    Trafienie w oczko wodne (niebieski kolor) dolicza karne uderzenie do twojego wyniku i powoduje umieszczenie piłki na brzegu oczka. +Comment[pt]=

    Poças (Ãgua)

    Se acertar numa poça (azul) adiciona uma tacada de penalização à sua pontuação, e a sua bola é posta fora da poça. +Comment[pt_BR]=

    Poças (Ãgua)

    Acertar a bola numa poça (azul) adiciona uma tacada de penalidade à sua pontuação, e sua bola é colocada fora da poça. +Comment[ru]=

    Лужи (вода)

    ЕÑли мÑч попадает в лужу (Ñинюю), вам начиÑлÑетÑÑ ÑˆÑ‚Ñ€Ð°Ñ„Ð½Ð¾Ð¹ удар, а мÑч помещаетÑÑ Ð² Ñтороне от лужи. +Comment[sk]=

    Puddles (Voda)

    Ak zasiahnete puddle (modrá), pripoÄíta sa vám penalizaÄný úder a vaÅ¡a loptiÄka sa objaví vedľa puddle. +Comment[sl]=

    Ribniki (Voda)

    ÄŒe zadenete ribnik (modro), dobite kazenski udarec k toÄkam, žogica pa je postavljena zunaj ribnika. +Comment[sr]=

    Баре (вода)

    Убацивањем лоптице у бару добијате казнени поен у резултату, а ваша лоптица Ñе поÑтавља изван баре. +Comment[sr@Latn]=

    Bare (voda)

    Ubacivanjem loptice u baru dobijate kazneni poen u rezultatu, a vaša loptica se postavlja izvan bare. +Comment[sv]=

    Vattenhinder

    Att slå i ett vattenhinder (blå) lägger till ett extra slag till poängen, och bollen placeras utanför hindret. +Comment[ta]=

    சேறà¯à®±à¯à®®à®Ÿà¯(தணà¯à®£à¯€à®°à¯)

    சேறà¯à®±à¯à®®à®Ÿà¯à®µà®¿à®²à¯ உதைபà¯à®ªà®¤à®¾à®²à¯ (நீலமà¯) உஙà¯à®•à®³à¯ மதிபà¯à®ªà¯†à®£à¯à®£à®¿à®²à¯ அபராத கà¯à®±à®¿ சேரà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. உஙà¯à®•à®³à¯ பநà¯à®¤à¯ சேறà¯à®±à¯à®®à®Ÿà¯à®µà®¿à®©à¯ வெளிபà¯à®ªà¯à®±à®¤à¯à®¤à®¿à®²à¯ வைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

    Кӯлмак (Об)

    Ðгар тӯбча ба кӯлак афтад (кабуд) шумо Ñоҳиби задани ҷаримавӣ мегардед ва тӯбча дар канори кӯлмак ҷойгир карда мешавад. +Comment[tr]=

    Birikinti (Su)

    Bir birikintiye (mavi) çarpmak skorunuza bir penaltı puanı eklenmesine yol açar, ve topunuz birikinti dışına çıkartılır. +Comment[uk]=

    Калюжі (Вода)

    Якщо м'Ñчик попадає в калюжу (ÑинÑ), вам нараховуєтьÑÑ ÑˆÑ‚Ñ€Ð°Ñ„Ð½Ð¸Ð¹ удар Ñ– ваш м'Ñчик поміщаєтьÑÑ Ð¿Ð¾Ð·Ð° калюжею. +Comment[xh]=

    Ichityana (Amanzi)

    Bethela kwi chityana (bhlowu) yongeza umvumbo wesohlwayo kwinqaku lakho, nebhola yakho ibekwe ngaphandle kwechityana. +Comment[xx]=xx

    Puddles (Water)

    Hitting into a puddle (blue) adds a penalty stroke to your score, and your ball is placed outside the puddle.xx +Comment[zh_CN]=

    æ°´å‘(æ°´)

    打到水å‘里(è“色)会在您的æˆç»©ä¸­è®°ä¸€æ¬¡æƒ©ç½šï¼Œç„¶åŽæ‚¨çš„çƒä¼šè¢«æ”¾åœ¨æ°´å‘的外边。 +Comment[zh_TW]=

    æ°´å‘(水)

    å°‡çƒæ“Šå…¥æ°´å‘(è—色)會在您的分數中增加懲罰性的桿數,而您的çƒæœƒè¢«æ”¾åœ¨æ°´å‘外é¢ã€‚ +Comment[zu]=

    Amachibi (amanzi)

    Ukushayela echibini (liluhlaza) kufaka isigwebo emaphuzwini akho, ebese ibhola libekwa ngaphandle kwechibi. +botWallVisible=true +height=145 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=394 + +[6-wall@0,0|38] +endPoint=264,354 +startPoint=167,390 + +[7-ball@48,355] +dummykey=true + +[7-cup@104,54|5] +dummykey=true + +[7-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[7-sign@130,2|4] +Comment=

    Windmills

    Windmills (brown base with moving arm) have brown walls (or half walls). The windmill arm's speed may vary by hole. +Comment[bg]=

    Ð’Ñтърни мелници

    Ð’Ñтърните мелници имат кафÑви Ñтени Ñ Ð¿Ð¾Ð´Ð²Ð¸Ð¶Ð½Ð¸ чаÑти, между които Ñ‚Ñ€Ñбва да мине топката. СкороÑтта им на движение може да е различна за различните дупки. +Comment[bn]=

    বায়à§à¦•à¦²

    বায়à§à¦•à¦²à§‡à¦° ( বাদামী ভিতà§à¦¤à¦¿à¦° ওপর চলনà§à¦¤ পাখা ) দেয়ালের ( বা অরà§à¦§à§‡à¦• দেয়ালের ) রং বাদামী। গরà§à¦¤ ভেদে বায়à§à¦•à¦²à§‡à¦° পাখার গতি ভিনà§à¦¨ হতে পারে। +Comment[bs]=

    VjetrenjaÄe

    VjetrenjaÄa (smeÄ‘i stub sa pokretnom rukom) ima smeÄ‘e zidove (ili polu-zidove). Ruka vjetrenjaÄe može biti razliÄita za svaku rupu. +Comment[ca]=

    Molins de vent

    Els molins de vent (base marró amb braç mòbil) tenen parets marrons (o mitja paret). La velocitat de rotació del braç no és constant. +Comment[da]=

    Vindmøller

    Vindmøller (brun base med bevægende vinge) har brune vægge (eller halve vægge). Vindmøllevingens fart kan variere fra bane til bane. +Comment[de]=

    Windmühlen

    Windmühlen (braune Gebäude mit sich bewegenden "Armen") haben braune Wände (oder Halbwände). Die Geschwindigkeit der "Arme" kann sich von Loch zu Loch ändern. +Comment[es]=

    Molinos de viento

    Los molinos de viento (base marrón con brazo móvil) tienen paredes marrones (o medias paredes). La velocidad del brazo del molino puede variar completamente. +Comment[et]=

    Tuuleveskid

    Tuuleveskid (pruun korpus, keerlevad tiivikud) on pruunide seinte (või poolseintega). Tiiviku liikumise kiirus võib olla rajati erinev. +Comment[fi]=

    Tuulimyllyt

    Tuulimyllyissä (ruskea jalusta, pyörivät siivet) on ruskeat seinät (tai puoliseinät). Tuulimyllyn siiven vauhti voi vaihdella reikien välillä. +Comment[fr]=

    Moulins à vent

    Les moulins à vent (base marron avec des pales qui remuent) ont des murs marrons (ou des demi-murs). La vitesse des bras des moulins peut varier à chaque tour. +Comment[gl]=

    Muíños de vento

    Os muiños de vento (marróns con aspas en movemento) teñen valados marróns (ou medios valados). A velocidade das aspas pode variar dependendo do burato. +Comment[he]=

    טחנות רוח

    לטחנות רוח (בסיס ×—×•× ×¢× ×–×¨×•×¢ × ×¢×”) יש קירות (×ו חצ××™ קירות) בצבע חו×. המהירות של זרוע הטחנה עשויה להיות שונה מגומה לגומה. +Comment[hi]=

    पवनचकà¥à¤•à¤¿à¤¯à¤¾à¤

    पवनचकà¥à¤•à¤¿à¤¯à¤¾à¤ (भूरे आधार यà¥à¤•à¥à¤¤ घूमती भà¥à¤œà¤¾ लिठहà¥à¤) भूरे रंग की दीवार (या आधी दीवार) यà¥à¤•à¥à¤¤ हैं. होल के अनà¥à¤¸à¤¾à¤° पवनचकà¥à¤•à¤¿à¤¯à¥‹à¤‚ के भà¥à¤œà¤¾à¤“ं की गतियों में अनà¥à¤¤à¤° होगा. +Comment[hu]=

    Szélmalmok

    A szélmalmok (barna alap, mozgó karok) falai barna színűek (vagy felezve vannak). A szélmalmok lapátjainak sebessége lyukanként változhat. +Comment[is]=

    Vindmyllur

    Vindmyllur (Brúnnn flötur með breyfiarmi) hafa brúna veggi (eða hálf-veggi). Hraði arma vindmyllunnar getur verið breytilegur. +Comment[it]=

    Mulini a vento

    I mulini a vento (una base marrone con delle pale mobili) hanno murature marroni (o mezzi muri). La velocità dellepale del mulino può cambiare da una buca all'altra. +Comment[ja]=

    風車

    風車(茶色ã§ã€å‹•ã„ã¦ã„るアーム) ã¯ã€ 茶色ã®å£ãŒãƒœãƒ¼ãƒ€ãƒ¼ã«ã‚ã‚Šã¾ã™ã€‚ホールã«ã‚ˆã£ã¦ã‚¢ãƒ¼ãƒ ã¯é•ã†é€Ÿåº¦ã§ å‹•ãã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 +Comment[mk]=

    Ветерници

    Ветерниците (кафеава подлога Ñо подвижна преграда) имаат кафеави ѕидови (или половина ѕидови). Брзината на преградата на ветерницата завиÑи од дупка до дупка. +Comment[nb]=

    Vindmøller

    Vindmøller (brun base med bevegelig arm) har brune vegger (eller halv-vegger). Vindmøllas fart kan variere fra hull til hull. +Comment[nl]=

    Windmolens

    Windmolens (bruine basis met bewegende wieken) hebben bruine muren (of halve muren). De bewegingssnelheid van de wieken varieert van hole tot hole. +Comment[nn]=

    Vindmøller

    Vindmøller (brun, med arm som rører seg) har brune veggar (eller halvveggar). Farten på armen kan vera ulik frå hol til hol. +Comment[pl]=

    Wiatraki

    Wiatraki (brązowa podstawa z ruchomym ramieniem) mają brązowe ściany (lub pół-ściany). Szybkość ramienia wiatraka może być różna dla różnych dołków. +Comment[pt]=

    Moinhos

    Os moinhos (base castanha com um braço móvel) têm paredes (ou meias-paredes) castanhas. A velocidade do moinho pode variar de acordo com o buraco. +Comment[pt_BR]=

    Moinhos

    Os moinhos (base marrom com braço móvel) possuem paredes marrons (ou meias-paredes). A velocidade do braço do moinho pode variar de um buraco a outro. +Comment[ru]=

    Мельницы

    Мельницы (коричневое оÑнование и движущиеÑÑ Ð»Ð¾Ð¿Ð°Ñти) имеют коричневые Ñтены (или ворота). СкороÑÑ‚ÑŒ лопаÑтей может менÑÑ‚ÑŒÑÑ Ð¾Ñ‚ лунки к лунке. +Comment[sk]=

    Veterné mlyny

    Veterné mlyny (hnedá základňa s pohybujúcim sa ramenom) má hnedé steny (alebo polo-steny). Rýchlosť ramena môže byť rôzna. +Comment[sl]=

    Mlini na veter

    Mlini na veter (rjava osnova z gibajoÄo roko) imajo rjave zidove (ali polzidove). Hitrost roke mlina na veter se lahko spreminja. +Comment[sr]=

    Ветрењаче

    Ветрењаче (Ñмеђа база Ñа покретним краком) имају Ñмеђе зидове (или полузидове). Брзина крака ветрењаче може варирати од рупе до рупе. +Comment[sr@Latn]=

    VetrenjaÄe

    VetrenjaÄe (smeÄ‘a baza sa pokretnim krakom) imaju smeÄ‘e zidove (ili poluzidove). Brzina kraka vetrenjaÄe može varirati od rupe do rupe. +Comment[sv]=

    Väderkvarnar

    Väderkvarnar (brun bas med vinge som flyttas) har bruna väggar (eller halva väggar). Hastigheten på väderkvarnens vinge kan variera för olika hål. +Comment[ta]=

    வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯

    வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯(பிரவà¯à®©à¯ அடிதà¯à®¤à®³à®¤à¯à®¤à¯‹à®Ÿà¯ சà¯à®±à¯à®±à¯à®®à¯ கரஙà¯à®•à®³à¯)பிரவà¯à®©à¯ சà¯à®µà®°à¯à®•à®³à¯ உளà¯à®³à®©(அலà¯à®²à®¤à¯ பாதி சà¯à®µà®°à¯à®•à®³à¯) இநà¯à®¤ வினà¯à®Ÿà¯à®®à®¿à®²à¯à®¸à¯ கரஙà¯à®•à®³à®¿à®©à¯ வேகம௠ஒர௠ஓடà¯à®Ÿà¯ˆà®¯à®³à®µà¯ வேறà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

    ОÑиёҳо

    ОÑиёҳо (аÑоÑи ҷигарранг ва даÑтаҳои ҳаракаткунанда) деворҳои ҷигарранг доранд (Ñ‘ ниÑфи деворҳо). Ðз Ñӯрох то ба Ñӯрох Ñуръати даÑтаҳо тағир ёфта метавонанд. +Comment[tr]=

    Yel DeÄŸirmenleri

    Yel değirmenleri (hareketli kolu olan kahverengi cisimler) kahverengi duvarlara (veya yarım duvarlara) sahiptir. Yel değirmeninin kolu topun hızını artırır. +Comment[uk]=

    Млини

    Млини (коричневі оÑнови з вітрÑками) мають коричневі Ñтіни (або напів-Ñтіни). ШвидкіÑÑ‚ÑŒ вітрÑка млина може бути різною Ð´Ð»Ñ Ñ€Ñ–Ð·Ð½Ð¸Ñ… лунок. +Comment[xh]=

    Amaphiko omoya

    Amaphiko omoya (Isiseko esimdaka nebala kunye nengalo ehambayo) Yiba namadonga amdaka ngebala (Okanye isiqingatha samadonga). amendu engalo yephikomoya angahluka ngomngxunya. +Comment[xx]=xx

    Windmills

    Windmills (brown base with moving arm) have brown walls (or half walls). The windmill arm's speed may vary by hole.xx +Comment[zh_CN]=

    风车

    风车(棕色的底座和è¿åŠ¨çš„转臂)有棕色的墙(或åŠå¢™)。ä¸åŒçƒæ´žé£Žè½¦è½¬è‡‚的速度å¯ä»¥ä¸åŒã€‚ +Comment[zh_TW]=

    風車

    風車(è¤è‰²åŸºç¤Žä¸¦æœ‰ç§»å‹•çš„風扇)有è¤è‰²çš„牆(或一åŠçš„牆)。風車的風扇速度å¯èƒ½éš¨è‘—洞而改變。 +Comment[zu]=

    Iphiko lomoya

    Iphiko lomoya (Isiqu esinsundu sinengalo enyakazayo) inezindonga ezinsundu (noma izindonga ezinguhhafu). Ijubane lengalo yephiko lomoya lingashiyana ngezimbobo. +botWallVisible=true +height=206 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=267 + +[7-windmill@15,145|3] +botWallVisible=false +bottom=true +height=71 +leftWallVisible=true +rightWallVisible=true +speed=5 +topWallVisible=false +width=107 + +[8-ball@39,234] +dummykey=true + +[8-blackhole@60,343|4] +exit=195,287 +exitDeg=0 +maxspeed=3 +minspeed=1 + +[8-cup@333,287|5] +dummykey=true + +[8-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=false +maxstrokes=10 +par=2 + +[8-sign@5,5|3] +Comment=

    Black Holes

    Black Holes transport the ball to their exit, and eject it at a speed directly relational to the speed your ball was going. Choose Hole->Show Info to see which Black Hole goes to which exit and the direction the ball will come out at. +Comment[af]=

    Swart Gate

    Swart Gate vervoerder die bal na hulle beïendig, en uitskiet dit na 'n spoed direk verwante na die spoed jou bal was gaan. Kies Gat->Show Inligting na sien wat Swart Gat gaan na wat beïendig en die rigting die bal sal kom uit na. +Comment[bg]=

    Черни дупки

    Черните дупки пренаÑÑÑ‚ топката до даден изхода и Ñ Ð¸Ð·Ñ…Ð²ÑŠÑ€Ð»ÑÑ‚ ÑÑŠÑ ÑкороÑÑ‚, пропорционална на ÑкороÑтта на движение на топката. Изберете от менюто Дупка/ИнформациÑ, за да видите ÐºÐ¾Ñ Ñ‡ÐµÑ€Ð½Ð° дупка до кой изход води и поÑоката, от коÑто ще Ñе поÑви топката. +Comment[bn]=

    কালো গরà§à¦¤

    কালো গরà§à¦¤ দিয়ে বল তার বহিরà§à¦—মনের দিকে চলে যায়; কী গতিতে বহিরà§à¦—মন দিয়ে বল বের হবে তা বলের গতির সাথে সরাসরি সমà§à¦ªà¦°à§à¦•à¦¿à¦¤à¥¤ কোন কালো গরà§à¦¤ কোন বহিরà§à¦—মনের সাথে যà§à¦•à§à¦¤ à¦à¦¬à¦‚ কোন দিক দিয়ে বল বের হয়ে আসবে তা জানার জনà§à¦¯ গরà§à¦¤-তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ চাপà§à¦¨à¥¤ +Comment[bs]=

    Crne rupe

    Crne rupe transportuju lopticu do svog izlaza, te je izbacuju brzinom proporcionalnom brzini kojom je loptica ranije iÅ¡la. Izaberite Rupa->Pokaži informacije da vidite koja Crna rupa odgovara kojem izlazu kao i smjer kojim će loptica biti izbaÄena. +Comment[ca]=

    Forats negres

    Els forats negres transporten la pilota cap a la sortida i la disparen a una velocitat directament proporcional a la que ja tenien. Seleccioneu Forat->Mostra informació per a veure quin forat negre porta a cada sortida i al direcció en la sortirà la bola. +Comment[da]=

    Sorte huller

    Sorte huller transporterer bolden til deres udgang og affyrer bolden med en fart i direkte relation til boldens oprindelige fart. VælgHul -> Vis info for at se hvilket sort hul der fører til hvilken udgang og den retning bolden vil komme ud i. +Comment[de]=

    Schwarze Löcher

    Schwarze Löcher teleportieren den Ball zu ihrem Ausgang, und werfen den Ball mit einer Geschwindigkeit, die zu seiner ursprünglichen Geschwindigkeit direkt im Verhältnis steht, wieder auf das Spielfeld. Wählen Sie Loch->Info anzeigen um zu sehen, welches Schwarze Loch zu welchem Ausgang gehört, und in welcher Richtung der Ball herauskommt. +Comment[es]=

    Agujeros negros

    Los agujeros negros transportan la pelota a la salida y la disparan a una velocidad directamente proporcional a la que llevaban. Seleccione Hoyo->Mostrar información para ver qué agujero negro lleva a cada salida y la dirección en la que saldrá la bola. +Comment[et]=

    Mustad augud

    Mustad augud toimetavad palli väljumisavani ning paiskavad selle sealt välja sama kiirusega, millega pall musta auku löödi. Vali Rada->Näita infot, mis näitab, kus asub musta augu väljumisava ja millises suunas pall sealt väljub. +Comment[fi]=

    Mustat aukot

    Mustat aukot kuljettavat pallon ulostuloonsa, ja ampuvat pallon nopeudella, joka on verranollinen pallon entiseen nopeuteen. Valitse Reikä->Näytä tiedot nähdäksesi mikä musta aukko on yhteydessä mihinkin ulostuloon ja mihin suuntaan pallo lähtee. +Comment[fr]=

    Trous noirs

    Les trous noirs transportent la balle vers leur sortie et l'éjectent à une vitesse directement en relation avec la vitesse à laquelle votre balle est entrée. Choisissez Trou / Afficher les informations pour voir quel trou noir va à quelle sortie et la direction dans laquelle la balle sortira. +Comment[gl]=

    Buracos negros

    Os buracos negros levan a bola ata a súa saída, e expúlsana a unha velocidade directamente proporcional á que tiña a bola. Elixe Burato->Mostrar Información para ver cara onde vai saír un burato negro e a dirección na que sairá a bola. +Comment[he]=

    ×—×•×¨×™× ×©×—×•×¨×™×

    ×—×•×¨×™× ×©×—×•×¨×™× ×ž×¢×‘×™×¨×™× ×ת הכדור ליצי××” שלה×, ×•×¤×•×œ×˜×™× ××•×ª× ×‘×ž×”×™×¨×•×ª העומדת ביחס ישר למהירות שבה הכדור שלך × ×¢ ×›×שר ×”×•× × ×›× ×¡ לחור השחור. בחר בגומה->הצג מידע כדי לר×ות ל×יזו יצי××” מוביל כל חור שחור וב××™×–×” כיוון ×™×™×¦× ×”×›×“×•×¨. +Comment[hi]=

    बà¥à¤²à¥‡à¤• होलà¥à¤¸

    बà¥à¤²à¥‡à¤• होलà¥à¤¸ गेंद को उनके निरà¥à¤—म पर ले जाती हैं, तथा आपकी गेंद की गति के सीधे अनà¥à¤ªà¤¾à¤¤ की गति से निकाल बाहर फेंकती है. चà¥à¤¨à¥‡à¤‚ होल->जानकारी दिखाà¤à¤ यह देखने के लिठकि कौन सा बà¥à¤²à¥‡à¤• होल किस निरà¥à¤—म पर जाता है तथा गेंद किस दिशा से बाहर आà¤à¤—ा. +Comment[hu]=

    Fekete lyukak

    A fekete lyukba esett golyó a lyuk kijáratához kerül, a kilépési sebesség arányos lesz a belépésivel. Válassza a Lyuk->Információ menüpontot, ha tudni szeretné, melyik fekete lyuk hová vezet és milyen a kilépési irány. +Comment[is]=

    Svarthol

    Svartar holur flytja boltann um ormagöng að úttaki, og spýta boltanum út í réttu hlutfalli við hraðann sem þú sendir á inn í svartholið. Veldu Hola->Sýna upplýsingar til að sjá ormagöngin og þá hvar boltinn mun koma út. +Comment[it]=

    Buchi neri

    I buchi neri trasportano la pallina alla loro uscita e la espellono con una velocità proporzionale a quella con cui è entrata. Scegli Buca->Mostra informazioni per vedere la corrispondenza tra buchi neri e uscite e la direzione con cui vengono espulse le palline. +Comment[ja]=

    ブラックホール

    ブラックホールã¯ãƒœãƒ¼ãƒ«ã‚’ å…¥ã£ãŸæ™‚ã®é€Ÿåº¦ã¨åŒã˜é€Ÿåº¦ã§å‡ºå£ã«é‹ã‚“ã§å‡ºã—ã¾ã™ã€‚ ホール->情報を表示 ã¨ã™ã‚‹ã¨ã€ãƒ–ラックホールã¨å‡ºå£ã®é€£ä¿‚㨠出å£ã®æ–¹å‘ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ +Comment[mk]=

    Црни дупки

    Црните дупки ги пренеÑуваат топките до нивниот излез и ги иÑфрлаат Ñо брзина која е директно во релација Ñо брзината Ñо која вашата топка предходно Ñе движела. Изберете Дупка->Прикажи информација за да видите која црна дупка оди Ñо кој излез и наÑоката во која топката ќе излезе. +Comment[nb]=

    Svarte hull

    Svarte hull sender ballen til utgangen, og kaster den ut i samme fart som den kom inn. Velg Hull->Vis info for å se hvilket svart hull som går til hvilken utgang og retningen ballen vil komme ut av hullet. +Comment[nl]=

    Zwarte gaten

    Zwarte gaten teleporteren de bal naar hun uitgang, en werpen de bal met een snelheid die overeenkomt met de oorspronkelijke snelheid terug op het speelveld. Kies Hole->Info tonen om te zien welk zwart gat naar welke uitgang leidt, en in welke richting de bal de uitgang zal verlaten. +Comment[nn]=

    Svarte hol

    Svarte hol flyttar ballen til utgangen, og kastar han ut med ein fart som svarar til farten han hadde. Vel Hol->Vis info for å sjå kva for svarte hol som går til kva for utgang, og kva for retning ballen vil få. +Comment[pl]=

    Czarne dziury

    Czarne dziury przenoszą piłkę do swojego wyjścia i wyrzucają ją z prędkością proporcjonalną do prędkości z jaką leciała. Wybierz Dołek->Pokaż informacje, aby zobaczyć które wyjście odpowiada której Czarnej Dziurze i w jakim kierunku piłka zostanie wyrzucona. +Comment[pt]=

    Buracos Negros

    Os buracos negros transportam a bola para a sua saída e expulsam-na a uma velocidade proporcional à velocidade com que a sua bola estava a ir. Escolha o Buraco->Mostrar a Informação para ver para onde vai um determinado buraco negro e a direcção com que a bola irá sair. +Comment[pt_BR]=

    Buracos negros

    Os buracos negros transportam a bola para sua saída, e ejetam-na com uma velocidade diretamente relacionada com a velocidade em que sua bola estava entrou. Escolha Buraco-> Mostrar Informação para ver qual Buraco Negro dá em qual saída e qual a direção da bola ao sair dele. +Comment[ru]=

    Чёрные дыры

    Чёрные дыры ведут мÑч к выходу и выбраÑывают его Ñо ÑкороÑтью, прÑмо завиÑÑщей от его ÑобÑтвенной ÑкороÑти. См. Лунка->СведениÑ, чтобы узнать, к какому выходу ведёт Ñ‡Ñ‘Ñ€Ð½Ð°Ñ Ð´Ñ‹Ñ€Ð° и в каком направлении вылетит мÑч. +Comment[sk]=

    ÄŒierne diery

    ÄŒierne diery presúvajú loptiÄku na svoj druhý koniec a tam ju vystrelia rovnakou rýchlosÅ¥ou, ktorou do diery vletela. Výberom Jamka-ZobraziÅ¥ informácie môžete zistiÅ¥, kde je druhý koniec a ktorým smerom loptiÄka vyletí. +Comment[sl]=

    ÄŒrne luknje

    ÄŒrne luknje prestavijo žogico do svojega izhoda in jo izvržejo s hitrostjo, ki je odvisna od prvotne hitrosti žogice. Izberite Luknja -> Pokaži informacije, da vidite, katera Ärna luknja vodi do katerega izhoda in smer izvržene žogice. +Comment[sr]=

    Црне рупе

    Црне рупе транÑпортују лоптицу до Ñвог излаза и избацују је брзином директно пропорционалном брзини којом је лоптица претходно ишла. Изаберите Рупа->Прикажи информације да биÑте видели која црна рупа има који излаз и Ñмер који ће лоптица имати када изађе. +Comment[sr@Latn]=

    Crne rupe

    Crne rupe transportuju lopticu do svog izlaza i izbacuju je brzinom direktno proporcionalnom brzini kojom je loptica prethodno išla. Izaberite Rupa->Prikaži informacije da biste videli koja crna rupa ima koji izlaz i smer koji će loptica imati kada izađe. +Comment[sv]=

    Svarta hål

    Svarta hål transporterar bollen till sina utgångar, och skickar ut den med en hastighet som är direkt proportionell mot hastigheten som bollen hade. Välj Hål->Visa information för att se vilket svart hål som går till vilken utgång, och i vilken riktning bollen kommer ut. +Comment[ta]=

    கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆà®•à®³à¯

    கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆà®•à®³à¯ பநà¯à®¤à¯ˆ வெளியேறà¯à®±à®¤à¯à®¤à®¿à®²à¯ நிறà¯à®¤à¯à®¤à®¿ பநà¯à®¤à¯ எநà¯à®¤ வேகதà¯à®¤à®¿à®²à¯ வரà¯à®•à®¿à®±à®¤à¯‹ அதே வேகதà¯à®¤à®¿à®²à¯ வெளியேறà¯à®±à¯à®®à¯. தேரà¯à®µà¯ ஓடà¯à®Ÿà¯ˆ-> எநà¯à®¤ கரà¯à®ªà¯à®ªà¯ ஓடà¯à®Ÿà¯ˆ எநà¯à®¤ வழியாக வெளியேறà¯à®•à®¿à®±à®¤à¯ எனà¯à®ªà®¤à¯ˆà®¯à¯à®®à¯ பநà¯à®¤à¯ வெளியேறà¯à®®à¯ திசையையà¯à®®à¯ பாரà¯à®ªà¯à®ªà®¤à®±à¯à®•à¯ தகவலை காடà¯à®Ÿà®µà¯à®®à¯. +Comment[tr]=

    Karadelikler

    Kara delikler topu çıkışlarına taşırlar ve topuzun hızı ile orantılı bir hız ile dışarı atarlar. Delik->Bilgi Göster seçerek hangi karadeliğin hangi çıkışa sahip olduğunu ve topun çıkış yönünü öğrenebilirsiniz. +Comment[uk]=

    Чорні діри

    Чорні діри переноÑÑÑ‚ÑŒ м'Ñчик до Ñ—Ñ… виходу Ñ– викидають його із швидкіÑÑ‚ÑŒ прÑмо залежною від його влаÑної. Виберіть Лунка->Показати інформацію, щоб подивитиÑÑŒ Ñка чорна діра веде до Ñкого виходу Ñ– в Ñкому напрÑмку вилетить м'Ñчик. +Comment[xh]=

    Imingxuma Emnyama

    Imingxuma Emnyama ihambisa ibhola iyokutsho kwindawo yazo yokuphuma, ize iyikhuphe ngqo ngesantya esihlobene kwisantya ibihamba ngaso ibhola yakho. Khetha Umngxuma->Bonisa Ulwazi ze bone ukuba ngowuphi Umngxuma Omnyama ohamba ayokutsho kweliphi icala lendlela nakweyiphi indawo yokuphuma eza kuza kulo ibhola yakho. +Comment[xx]=xx

    Black Holes

    Black Holes transport the ball to their exit, and eject it at a speed directly relational to the speed your ball was going. Choose Hole->Show Info to see which Black Hole goes to which exit and the direction the ball will come out at.xx +Comment[zh_CN]=

    黑洞

    黑洞将çƒè¿é€åˆ°å‡ºå£å¤„,并根æ®æ‚¨çš„çƒé€ŸæŒ‰æˆæ¯”例的速度将其弹出。选择çƒæ´ž->显示信æ¯å¯æŸ¥çœ‹å“ªä¸ªé»‘洞对应哪个出å£ï¼Œä»¥åŠçƒä»Žå‡ºå£å¼¹å‡ºçš„æ–¹å‘。 +Comment[zh_TW]=

    黑洞

    黑洞會將çƒå‚³é€åˆ°å®ƒå€‘的出å£ï¼Œè€Œå°‡å®ƒå€‘射出的速度與您的çƒé€²å…¥çš„速度有直接關係。é¸æ“‡æ´ž->顯示資訊來看看從哪個黑洞進入會從從哪裡出來以åŠçƒå‡ºç¾çš„æ–¹å‘。 +Comment[zu]=

    Izimbobo ezimnyama

    Izimbobo ezimnyama zithatha ibhola ziliyise endaweni yazo yokuphuma, zililahle ngejubane elihambisana nejubane lebhola lakho. KhethaImbobo->Khombisa ulwaziukubona ukuthi yiphi imobo emnyama iya kuyiphi indawo yokuphuma nendlela ibhola elizophuma kuyo. +botWallVisible=true +height=206 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=390 + +[8-wall@0,0|6] +endPoint=145,132 +startPoint=147,317 + +[8-wall@0,0|7] +endPoint=147,317 +startPoint=69,390 + +[9-ball@57,307] +dummykey=true + +[9-cup@360,255|6] +dummykey=true + +[9-floater@236,298|4] +botWallVisible=false +endPoint=236,298 +height=40 +leftWallVisible=false +rightWallVisible=false +speed=3 +startPoint=105,248 +topWallVisible=false +width=80 + +[9-hole@-50,-50|0] +borderWalls=true +hasFinalLoad=true +maxstrokes=10 +par=2 + +[9-sign@5,6|3] +Comment=

    Floaters

    Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. +Comment[bg]=

    Хвърчащи килимчета

    Хвърчащите килимчета Ñа подвижни платформи, които ноÑÑÑ‚ падналите върху Ñ‚ÑÑ… топки. СкороÑтта им може да варира. +Comment[bn]=

    ফà§à¦²à§‹à¦Ÿà¦¾à¦°

    ফà§à¦²à§‹à¦Ÿà¦¾à¦° হল à¦à¦•à¦Ÿà¦¿ ভাসমান পà§à¦²à¦¾à¦Ÿà¦«à¦°à§à¦® যার ওপর বল পড়লে সে তা নিয়ে ভেসে বেড়ায়। বিভিনà§à¦¨ ফà§à¦²à§‹à¦Ÿà¦¾à¦°à§‡à¦° গতি বিভিনà§à¦¨ রকম। +Comment[bs]=

    PlutaÄi

    PlutaÄi su pokretne platforme koje prenose lopticu koja padne na njih. Brzina plutaÄa je promjenljiva. +Comment[ca]=

    Flotadors

    Els flotadors són plataformes mòbils que transporten a una pilota que aterra a sobre seu. La velocitat dels flotadors no és constant. +Comment[da]=

    Svævere

    Svævere er bevægende platforme som flytter en bold der lander på dem. Svæveres fart varierer. +Comment[de]=

    Floß

    Flöße sind bewegliche Plattformen, die einen Ball weitertragen, wenn er auf ihnen landet. Es gibt Flöße mit unterschiedlichen Geschwindigkeiten. +Comment[es]=

    Flotadores

    Los flotadores son plataformas móviles que transportan una pelota que aterrice en ellas. La velocidad de los flotadores es variable. +Comment[et]=

    Hõljukid

    Hõljukid on liikuvad platvormid, mis suudavad neil peatunud palli liigutada. Hõljukite kiirused erinevad. +Comment[fi]=

    Kellujat

    Kellujat ovat tasoja, jotka siirtävät palloa joka on tasolla. Tasojen vauhti vaihtelee. +Comment[fr]=

    Flotteurs

    Les flotteurs sont des plates-formes qui portent une balle qui atterrit dessus. La vitesse des flotteurs varie. +Comment[gl]=

    Flotadores

    Os Flotadores son plataformas móbiles que transportan á pelota que aterrou nelas. A velocidade dos flotadores é variable. +Comment[he]=

    ×ž×©×˜×—×™× ×¦×¤×™×

    ×לה ×ž×©×˜×—×™× × ×¢×™× ×©× ×•×©××™× ××™×ª× ×›×“×•×¨ שנוחת עליה×. ×ž×”×™×¨×•×ª× ×¢×©×•×™×” להשתנות ממשטח למשטח. +Comment[hi]=

    तैराक

    तैराक चलते हà¥à¤ चबूतरे होते हैं जो अपने ऊपर अवतरित गेंद को लिठहà¥à¤ होते हैं. तैराक की गति में अनà¥à¤¤à¤° होते रहता है. +Comment[hu]=

    Lebegők

    Ezek a vízen úszó platformok elbírják a rájuk eső golyókat. Sebességük különböző lehet. +Comment[is]=

    Prammar

    Prammar geta flutt bolta sem lenda á honum. hraði er breytilegur. +Comment[it]=

    Floater

    I floaters sono piattaforme mobili che trasportano la pallina che atterra su di essi. La velocità dei floater è variabile. +Comment[ja]=

    フローター

    フローターã¯ãƒœãƒ¼ãƒ«ã‚’é‹ã¶ã€ å‹•ãプラットフォームã§ã™ã€‚ホールã«ã‚ˆã£ã¦é•ã†é€Ÿåº¦ã§å‹•ãã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。 +Comment[lv]=

    SlÄ«doÅ¡Äs platformas

    SlÄ«doÅ¡Äs platformas pÄrvietojas un pÄrvieto bumbiņu, kas atordas uz tÄs. Platformu Ätrums variÄ“. +Comment[mk]=

    Лебдачи

    Лебдачите Ñе подвижни платформи кои ја ноÑат топката која ќе Ñлета на нив. Брзините на лебдачите Ñе различни. +Comment[nb]=

    Fløterne

    Fløterne er bevegelige plattformer som kan bære en ball som lander på den. Farten kan variere. +Comment[nl]=

    Drijvers

    Drijvers zijn bewegende platforms die een bal meenemen als deze er op landt. De snelheid van drijvers kan vari�en. +Comment[nn]=

    Plattformar

    Plattformar flyttar seg og tek med seg ballar som landar på dei. Farten til plattformane kan variera frå hol til hol. +Comment[pl]=

    Promy

    Promy to poruszające się platformy, które przewożą piłkę, która na nich wyląduje. Prędkość promów może być różna. +Comment[pt]=

    Bóias

    As bóias são plataformas móveis que carregam uma bola que pare em cima delas. As velocidades das bóias variam. +Comment[pt_BR]=

    Bóias

    As bóias são plataformas em movimento que têm uma bola sobre ela. A velocidade das bóias pode variar. +Comment[ru]=

    Ползуны

    Ползуны движутÑÑ Ð¸ увлекают мÑч, который приземлилÑÑ Ð½Ð° них. СкороÑÑ‚ÑŒ ползунов может быть разной. +Comment[sk]=

    Floaters

    Floaters sú presúvajúce sa plochy, ktoré odnášajú loptiÄky. Ich rýchlosÅ¥ sa mení. +Comment[sl]=

    Plovci

    Plovci so premikajoÄe se ploÅ¡Äadi, ki bodo nosili žogico, ki pristane na njej. Hitrost plovcev se lahko spreminja. +Comment[sr]=

    Плутачи

    Плутачи Ñу покретне платформе које ноÑе лоптицу која падне на њих. Брзина плутача може варирати. +Comment[sr@Latn]=

    PlutaÄi

    PlutaÄi su pokretne platforme koje nose lopticu koja padne na njih. Brzina plutaÄa može varirati. +Comment[sv]=

    Flottar

    Flottar är flyttbara plattformar som kan bära en boll som landar på dem. Flottarnas hastighet varierar. +Comment[ta]=

    மிதபà¯à®ªà®µà®°à¯à®•à®³à¯

    மிதபà¯à®ªà®µà®°à¯à®•à®³à¯ எலà¯à®²à¯‹à®°à¯à®®à¯ நகரà¯à®®à¯ தளமேடை. அத௠தன௠மேலிரà¯à®•à¯à®•à¯à®®à¯ பநà¯à®¤à¯à®•à®³à¯ˆ எடà¯à®¤à¯à®¤à¯à®šà¯ செலà¯à®²à¯à®®à¯. மிதபà¯à®ªà®µà®°à¯à®•à®³à®¿à®©à¯ வேகம௠மாறà¯à®ªà®Ÿà¯à®®à¯. +Comment[tg]=

    Хазандаҳо

    Хазандаҳо тӯберо, ки ба онҳо афтидааÑÑ‚, гирифта мебаранд. Суръати Хазандаҳо гуногун буда метавонад. +Comment[tr]=

    Yüzücüler

    Yüzücüler üstüne konan topları taşıyan platformlardır. Yüzücülerin değişik hızları vardır. +Comment[uk]=

    Плавуни

    Плавуни - це рухомі платформи, Ñкі неÑуть м'Ñчик, котрий на них попадає. ШвидкіÑÑ‚ÑŒ плавунів може бути різною. +Comment[xh]=

    Izinto ezidadayo

    Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary. +Comment[xx]=xx

    Floaters

    Floaters are moving platforms that carry a ball that lands on it. Floaters' speeds vary.xx +Comment[zh_CN]=

    æµ®å°

    æµ®å°æ˜¯å¯ä»¥è½½ç€çƒç§»åŠ¨çš„å¹³å°ã€‚æµ®å°çš„速度是å¯å˜çš„。 +Comment[zh_TW]=

    浮標

    浮標是會移動的平å°ï¼Œå¯ä»¥æ‰¿è¼‰è½åœ¨å®ƒä¸Šé¢çš„çƒã€‚浮標的速度是多變的。 +Comment[zu]=

    Okuntantayo

    Okuntantayo amabala anyakazayo aphatha ibhola elihlala kuwo.Okuntantayo kunamajubane ashiyanayo. +botWallVisible=true +height=127 +leftWallVisible=true +rightWallVisible=true +topWallVisible=true +width=391 + +[9-wall@0,0|5] +endPoint=177,126 +startPoint=171,391 diff --git a/kolf/tutorial.kolfgame b/kolf/tutorial.kolfgame new file mode 100644 index 00000000..30736700 --- /dev/null +++ b/kolf/tutorial.kolfgame @@ -0,0 +1,10 @@ +[0 Saved Game] +Competition=false +Course=/opt/kde3/share/apps/kolf/tutorial.kolf +Current Hole=1 +Players=1 + +[1] +Color=#bbffd4 +Name=Player 1 +Scores=0 diff --git a/kolf/vector.cpp b/kolf/vector.cpp new file mode 100644 index 00000000..f4c05262 --- /dev/null +++ b/kolf/vector.cpp @@ -0,0 +1,106 @@ +#include + +#include "vector.h" + +// this and vector.h by Ryan Cummings + +// Creates a vector with between two points +Vector::Vector(const QPoint &source, const QPoint &dest) { + _magnitude = sqrt(pow(source.x() - dest.x(), 2) + pow(source.y() - dest.y(), 2)); + _direction = atan2(source.y() - dest.y(), source.x() - dest.x()); +} + +// Creates a vector with between two points +Vector::Vector(const Point &source, const Point &dest) { + _magnitude = sqrt(pow(source.x - dest.x, 2) + pow(source.y - dest.y, 2)); + _direction = atan2(source.y - dest.y, source.x - dest.x); +} + +// Creates an empty Vector +Vector::Vector() { + _magnitude = 0.0; + _direction = 0.0; +} + +// Copy another Vector object +Vector::Vector(const Vector& v) { + _magnitude = v._magnitude; + _direction = v._direction; +} + +// Set the X component +void Vector::setComponentX(double x) { + setComponents(x, componentY()); +} + +// Set the Y component +void Vector::setComponentY(double y) { + setComponents(componentX(), y); +} + +// Operations with another Vector performs vector math +Vector Vector::operator+(const Vector& v) { + double x = componentX() + v.componentX(); + double y = componentY() + v.componentY(); + + return Vector(sqrt((x * x) + (y * y)), atan2(y, x)); +} + +Vector Vector::operator-(const Vector& v) { + double x = componentX() - v.componentX(); + double y = componentY() - v.componentY(); + + return Vector(sqrt((x * x) + (y * y)), atan2(y, x)); +} + +Vector& Vector::operator+=(const Vector& v) { + setComponents(componentX() + v.componentX(), componentY() + v.componentY()); + return *this; +} + +Vector& Vector::operator-=(const Vector& v) { + setComponents(componentX() - v.componentX(), componentY() - v.componentY()); + return *this; +} + +double Vector::operator*(const Vector& v) { + return ((componentX() * v.componentX()) + (componentY() * v.componentY())); +} + +// Operations with a single double value affects the magnitude +Vector& Vector::operator+= (double m) { + _magnitude += m; + return *this; +} + +Vector& Vector::operator-= (double m) { + _magnitude -= m; + return *this; +} + +Vector& Vector::operator*= (double m) { + _magnitude *= m; + return *this; +} + +Vector& Vector::operator/= (double m) { + _magnitude /= m; + return *this; +} + +// Sets both components at once (the only way to do it efficiently) +void Vector::setComponents(double x, double y) { + _direction = atan2(y, x); + _magnitude = sqrt((x * x) + (y * y)); +} + +void debugPoint(const QString &text, const Point &p) +{ + kdDebug(12007) << text << " (" << p.x << ", " << p.y << ")" << endl; +} + +void debugVector(const QString &text, const Vector &p) +{ + // debug degrees + kdDebug(12007) << text << " (magnitude: " << p.magnitude() << ", direction: " << p.direction() << ", direction (deg): " << (360L / (2L * M_PI)) * p.direction() << ")" << endl; +} diff --git a/kolf/vector.h b/kolf/vector.h new file mode 100644 index 00000000..1da7328b --- /dev/null +++ b/kolf/vector.h @@ -0,0 +1,92 @@ +#ifndef KOLF_VECTOR_H +#define KOLF_VECTOR_H + +#include + +#include + +class Point +{ +public: + Point(double _x, double _y) + { + x = _x; + y = _y; + } + + Point() + { + x = 0; + y = 0; + } + + double x; + double y; +}; + +void debugPoint(const QString &, const Point &); + +// This and vector.cpp by Ryan Cummings + +// Implements a vector in 2D +class Vector { + public: + // Normal constructors + Vector(double magnitude, double direction) { _magnitude = magnitude; _direction = direction; } + Vector(const QPoint& source, const QPoint& dest); + Vector(const Point& source, const Point& dest); + Vector(); + + // Copy constructor + Vector(const Vector&); + + // Accessors, sorta + double componentX() const { return (_magnitude * cos(_direction)); }; + double componentY() const { return (_magnitude * sin(_direction)); }; + + // Sets individual components + // Wrappers around setComponents(double, double) - below + void setComponentX(double x); + void setComponentY(double y); + + // Sets both components at once + void setComponents(double x, double y); + + // Accessors + double magnitude() const { return _magnitude; } + double direction() const { return _direction; } + void setMagnitude(double m) { _magnitude = m; } + void setDirection(double d) { _direction = d; } + + // Vector math + Vector operator+(const Vector&); + Vector operator-(const Vector&); + + Vector& operator+=(const Vector&); + Vector& operator-=(const Vector&); + + // Dot product + double operator*(const Vector&); + + // Magnitude math + Vector operator+(double m) { return Vector(_magnitude + m, _direction); } + Vector operator-(double m) { return Vector(_magnitude - m, _direction); } + Vector operator*(double m) { return Vector(_magnitude * m, _direction); } + Vector operator/(double m) { return Vector(_magnitude / m, _direction); } + + Vector& operator+=(double m); + Vector& operator-=(double m); + Vector& operator*=(double m); + Vector& operator/=(double m); + + // Return the vector's equalivent on the unit circle + Vector unit() const { return Vector(1.0, _direction); } + + protected: + double _magnitude; + double _direction; +}; + +void debugVector(const QString &, const Vector &); + +#endif diff --git a/kolf/x-kolf.desktop b/kolf/x-kolf.desktop new file mode 100644 index 00000000..e2c622e1 --- /dev/null +++ b/kolf/x-kolf.desktop @@ -0,0 +1,60 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kolf +Type=MimeType +Comment=Kolf Saved Game +Comment[ar]=لعبة Kolf محÙوظة +Comment[be]=Ð—Ð°Ñ…Ð°Ð²Ð°Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž гольф +Comment[bg]=ЗапиÑана игра на Kolf +Comment[bn]=কলà§â€Œà¦« খেলা সংরকà§à¦·à¦£ করেছে +Comment[br]=C'hoari enrollet Kolf +Comment[bs]=Kolf snimljena igra +Comment[ca]=Partida de Kolf desada +Comment[cs]=Uložená hra Kolfu +Comment[cy]=Gêm Kolf wedi'i Gadw +Comment[da]=Gemt Kolf-spil +Comment[de]=Gespeichertes Kolf-Spiel +Comment[el]=Αποθηκευμένο παιχνίδι Kolf +Comment[eo]=Kolf konservita ludo +Comment[es]=Juego de Kolf guardado +Comment[et]=Kolfi salvestatud mäng +Comment[eu]=Kolf-en gordetako jokoa +Comment[fa]=بازی ذخیره‌شدۀ Kolf +Comment[fi]=Kolfi-pelitallennus +Comment[fr]=Partie enregistrée de Kolf +Comment[gl]=Xogo de Kolf gravado +Comment[he]=משחק שמור של Kolf +Comment[hi]=सहेजा गया कोलà¥à¤« खेल +Comment[hr]=Spremljena Kolf igra +Comment[hu]=Kolf elmentett játék +Comment[is]=Vistaður Kolf leikur +Comment[it]=Partita di Kolf salvata +Comment[ja]=Kolf ã®ä¿å­˜ã•ã‚ŒãŸã‚²ãƒ¼ãƒ  +Comment[km]=ល្បែង Kolf ដែល​បាន​រក្សាទុក +Comment[lt]=Kolf iÅ¡saugotas žaidimas +Comment[lv]=Kolf saglabÄta spÄ“le +Comment[mk]=Зачувана игра од Kolf +Comment[nb]=Kolf lagret spill +Comment[nds]=Sekert Kolf-Speel +Comment[ne]=कोलà¥à¤«à¤²à¥‡ खेल बचत गरà¥à¤¯à¥‹ +Comment[nl]=Opgeslagen Kolf-spel +Comment[nn]=Kolf lagra spel +Comment[pa]=ਗੋਲਫ਼ ਸੰਭਾਲੀ ਖੇਡ +Comment[pl]=Zapisana gra w Kolfa +Comment[pt]=Jogo Gravado do Kolf +Comment[pt_BR]=Jogo Kolf Salvo +Comment[ru]=Ð¡Ð¾Ñ…Ñ€Ð°Ð½Ñ‘Ð½Ð½Ð°Ñ Ð¸Ð³Ñ€Ð° в гольф +Comment[se]=Kolf vurkii spealu +Comment[sk]=Uložená hra Kolf +Comment[sl]=Shranjena igra Kolf +Comment[sr]=Снимљена игра Kolf-а +Comment[sr@Latn]=Snimljena igra Kolf-a +Comment[sv]=Sparat Kolfspel +Comment[ta]=கோலà¯à®ƒà®ªà¯ சேமிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии Захиракардаи Голф +Comment[tr]=Kayıtlı Kolf Oyunu +Comment[uk]=Збережена гра в гольф +Comment[zh_CN]=Kolf ä¿å­˜çš„æ¸¸æˆ +Comment[zh_TW]=Kolf 儲存的éŠæˆ² +Icon=kolf +Patterns=*.kolfgame diff --git a/kolf/x-kourse.desktop b/kolf/x-kourse.desktop new file mode 100644 index 00000000..19951038 --- /dev/null +++ b/kolf/x-kourse.desktop @@ -0,0 +1,53 @@ +# KDE Config File +[Desktop Entry] +MimeType=application/x-kourse +Comment=Kolf Course +Comment[be]=ПлÑцоўка Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[bg]=ÐšÑƒÑ€Ñ Ð½Ð° Kolf +Comment[bn]=কলà§â€Œà¦« কোরà§à¦¸ +Comment[bs]=Kolf teren +Comment[ca]=Camp de Kolf +Comment[cs]=Kurs Kolfu +Comment[cy]=Cwrs Kolf +Comment[da]=Kolf-bane +Comment[de]=Kolf-Platz +Comment[el]=Πίστα kolf +Comment[eo]=Golftereno +Comment[es]=Partida de Kolf +Comment[et]=Kolfi väljak +Comment[eu]=Kolf-en zelaia +Comment[fa]=مسابقۀ Kolf +Comment[fi]=Golf-rata +Comment[fr]=Parcours de Kolf +Comment[he]=מסלול Kolf +Comment[hr]=Kolf teren +Comment[hu]=Kolf-pálya +Comment[is]=Kolf Braut +Comment[it]=Percorso di Kolf +Comment[ja]=Kolf コース +Comment[km]=វគ្គ Kolf +Comment[lt]=Kolf trasa +Comment[lv]=Kolf kurss +Comment[mk]=Терен на Kolf +Comment[nb]=Kolf-bane +Comment[nds]=Kolf-Platz +Comment[ne]=कोलà¥à¤« सà¥à¤°à¥‹à¤¤ +Comment[nl]=Kolf-baan +Comment[nn]=Kolf-bane +Comment[pa]=ਗà©à¨²à¨«à¨¼ ਕੋਰਸ +Comment[pl]=Kurs Kolfa +Comment[pt]=Percurso do Kolf +Comment[pt_BR]=Curso do Kolf +Comment[ru]=Площадка Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[se]=Kolf-bána +Comment[sk]=Kolf kurz +Comment[sl]=IgriÅ¡Äe za Kolf +Comment[sr]=Kolf-ов терен +Comment[sr@Latn]=Kolf-ov teren +Comment[sv]=Kolfbana +Comment[ta]=காலà¯à®ƒà®ªà¯ கோரà¯à®¸à¯ +Comment[uk]=Майданчик Ð´Ð»Ñ Ð³Ð¾Ð»ÑŒÑ„Ð° +Comment[zh_TW]=Kolf 路線 +Icon=kolf +Type=MimeType +Patterns=*.kolf;*.course;*.kourse; diff --git a/konquest/AUTHORS b/konquest/AUTHORS new file mode 100644 index 00000000..6d46c5fd --- /dev/null +++ b/konquest/AUTHORS @@ -0,0 +1,12 @@ +Galactic Konquest +----------------- + +Contributers are: + +- Russell Steffen + + +- Computer/AI Player +Stephan Zehetner + + diff --git a/konquest/ChangeLog b/konquest/ChangeLog new file mode 100644 index 00000000..0601252e --- /dev/null +++ b/konquest/ChangeLog @@ -0,0 +1,26 @@ +2005-02-18 Inge Wallin + + * version.h (KONQUEST_VERSION): Bumped version number to 1.1 + because of the upcoming release of KDE 3.4. + +2004-08-22 Inge Wallin + + * gameboard.cc (nextTurn): Fixed a grammatical error. + +0.99.4 -- + Brought the KDE CVS tree in sync with my own. Changes from previous version + include: + -- removed dependence on libg++ random integer classes + -- fixed a couple of problems with the game state machine. + +0.0.4 -- + Fixed (hopefully) a compiler error. gamecore.cc would compile fine with + debug (-g), but not with the optimizer (-O2). Added a destructor + and things seem okay. + + Added the distance measuring function of the original game. It appears as a + in the toolbar. + +0.0.3 -- + wasn't keeping a changelog prior to 0.0.4 + diff --git a/konquest/Konquest.cc b/konquest/Konquest.cc new file mode 100644 index 00000000..90a6b6c6 --- /dev/null +++ b/konquest/Konquest.cc @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#include "version.h" +#include "mainwin.h" +#include "map_widget.h" + +static const char description[] = I18N_NOOP("Galactic Strategy KDE Game"); + +int +main(int argc, char **argv) +{ + KAboutData aboutData( "konquest", I18N_NOOP("Konquest"), + KONQUEST_VERSION, description, KAboutData::License_GPL, + I18N_NOOP("Copyright (c) 1999-2001, Developers")); + aboutData.addAuthor("Russ Steffen",0, "rsteffen@bayarea.net"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + QApplication::setGlobalMouseTracking( true ); + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(MainWindow) + else { + MainWindow *w = new MainWindow; + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} + diff --git a/konquest/Makefile.am b/konquest/Makefile.am new file mode 100644 index 00000000..e6b50150 --- /dev/null +++ b/konquest/Makefile.am @@ -0,0 +1,23 @@ +SUBDIRS = pics + +INCLUDES= -I$(top_srcdir)/libkdegames $(all_includes) +KDE_ICON = konquest + +bin_PROGRAMS = konquest +konquest_SOURCES = Konquest.cc gameboard.cc gamecore.cc int_validator.cc \ + mainwin.cc map_widget.cc minimap.cc newgamedlg.cc planet_info.cc \ + gameenddlg.cc scoredlg.cc fleetdlg.cc newGameDlg_ui.ui +konquest_LDFLAGS = $(all_libraries) $(KDE_RPATH) +konquest_LDADD = $(LIB_KDEGAMES) +konquest_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + + +METASOURCES = AUTO + +xdg_apps_DATA = konquest.desktop + +rcdir = $(kde_datadir)/konquest +rc_DATA = konquestui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cc -o $(podir)/konquest.pot diff --git a/konquest/README b/konquest/README new file mode 100644 index 00000000..b8950f43 --- /dev/null +++ b/konquest/README @@ -0,0 +1,10 @@ +Konquest +---------------------------------------- + +Konquest is a multi-player strategy game. The goal of +the game is to expand your interstellar empire across the + galaxy and, of course, crush your rivals in the process. + +Please see the konquest help page for more information. + + diff --git a/konquest/TODO b/konquest/TODO new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/konquest/TODO @@ -0,0 +1 @@ + diff --git a/konquest/fleetdlg.cc b/konquest/fleetdlg.cc new file mode 100644 index 00000000..0117f74d --- /dev/null +++ b/konquest/fleetdlg.cc @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "fleetdlg.h" + +FleetDlgListViewItem::FleetDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5) : QListViewItem(parent, s1, s2, s3, s4, s5) +{ +} + +int FleetDlgListViewItem::compare(QListViewItem *i, int col, bool) const +{ + if (col == 1) + { + if (text(col) > i -> text(col)) return 1; + else if (text(col) < i -> text(col)) return -1; + else return compare(i, 0, true); + } + else if (col == 3) + { + if (text(col).toDouble() > i -> text(col).toDouble()) return 1; + else if (text(col).toDouble() < i -> text(col).toDouble()) return -1; + else return compare(i, 0, true); + } + else + { + if (text(col).toInt() > i -> text(col).toInt()) return 1; + else if (text(col).toInt() < i -> text(col).toInt()) return -1; + else return compare(i, 0, true); + } +} + + +FleetDlg::FleetDlg( QWidget *parent, AttackFleetList *fleets ) + : QDialog(parent, "FleetDlg", true ), fleetList(fleets) +{ + setCaption( kapp->makeStdCaption(i18n("Fleet Overview")) ); + + fleetTable = new KListView( this, 0 ); + fleetTable->addColumn(i18n("Fleet No.")); + fleetTable->addColumn(i18n("Destination")); + fleetTable->addColumn(i18n("Ships")); + fleetTable->addColumn(i18n("Kill Percentage")); + fleetTable->addColumn(i18n("Arrival Turn")); + fleetTable->setMinimumSize( fleetTable->sizeHint() ); + + KPushButton *okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setMinimumSize( okButton->sizeHint() ); + okButton->setDefault(true); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + QHBoxLayout *layout2 = new QHBoxLayout; + + layout1->addWidget( fleetTable, 1 ); + layout1->addLayout( layout2 ); + + layout2->addStretch( 2 ); + layout2->addWidget( okButton ); + layout2->addStretch( 2 ); + + connect( okButton, SIGNAL(clicked()), this, SLOT(accept()) ); + + init(); + + resize( 580, 140 ); +} + +void +FleetDlg::init() +{ + AttackFleet *curFleet; + AttackFleetListIterator nextFleet( *fleetList ); + int fleetNumber = 0; + + while( (curFleet = nextFleet())) { + fleetNumber++; + new FleetDlgListViewItem(fleetTable, + QString("%1").arg(fleetNumber), + curFleet->destination->getName(), + QString("%1").arg(curFleet->getShipCount()), + QString("%1").arg(KGlobal::locale()->formatNumber(curFleet->killPercentage, 3)), + QString("%1").arg((int)ceil(curFleet->arrivalTurn))); + } +} diff --git a/konquest/fleetdlg.h b/konquest/fleetdlg.h new file mode 100644 index 00000000..abab1957 --- /dev/null +++ b/konquest/fleetdlg.h @@ -0,0 +1,29 @@ +#ifndef FLEETDLG_H +#define FLEETDLG_H + +#include + +#include + +#include "gamecore.h" + +class FleetDlgListViewItem : public QListViewItem +{ + public: + FleetDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5); + int compare(QListViewItem *i, int col, bool) const; +}; + +class FleetDlg : public QDialog { + +public: + FleetDlg( QWidget *parent, AttackFleetList *fleets ); + +private: + void init(); + + AttackFleetList *fleetList; + QListView *fleetTable; +}; + +#endif diff --git a/konquest/gameboard.cc b/konquest/gameboard.cc new file mode 100644 index 00000000..75e440d7 --- /dev/null +++ b/konquest/gameboard.cc @@ -0,0 +1,991 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "gamecore.h" + +#include "int_validator.h" +#include "newgamedlg.h" +#include "gameenddlg.h" +#include "scoredlg.h" +#include "fleetdlg.h" +#include "gameboard.h" +#include "gameboard.moc" + + +/********************************************************************* + Game Board +*********************************************************************/ +GameBoard::GameBoard( QWidget *parent ) + : QWidget( parent ), gameInProgress( false ), gameState( NONE ) +{ + QColorGroup cg( white, black, green.light(), green.dark(), green, green.dark(75), green.dark() ); + QPalette palette( cg, cg, cg ); + + neutralPlayer = Player::createNeutralPlayer(); + map = new Map; + + planets.setAutoDelete(true); + players.setAutoDelete(true); + + //******************************************************************** + // Create the widgets in the main window + //******************************************************************** + mapWidget = new ConquestMap( map, this ); + msgWidget = new QTextEdit( this ); + msgWidget->setTextFormat(LogText); + msgWidget->setMinimumHeight(100); + msgWidget->setHScrollBarMode(QScrollView::AlwaysOff); + msgWidget->setPaper(QBrush(Qt::black)); + planetInfo = new PlanetInfo( this, palette ); + gameMessage = new QLabel( this ); + gameMessage->setPalette( palette ); + turnCounter = new QLabel( this ); + turnCounter->setPalette( palette ); + turnCounter->setText( "Turn" ); + turnCounter->setMaximumHeight( turnCounter->sizeHint().height() ); + + endTurn = new QPushButton( i18n("End Turn"), this ); + endTurn->setFixedSize( endTurn->sizeHint() ); + endTurn->setPalette( palette ); + + shipCountEdit = new QLineEdit( this ); + IntValidator *v = new IntValidator( 1, 32767, this ); + shipCountEdit->setValidator( v ); + shipCountEdit->setMinimumSize( 40, 0 ); + shipCountEdit->setMaximumSize( 32767, 40 ); + shipCountEdit->setEnabled(false); + shipCountEdit->setPalette( palette ); + shipCountEdit->setEchoMode( QLineEdit::Password ); + + splashScreen = new QLabel( this ); + splashScreen->setPixmap(QPixmap(IMAGE_SPLASH)); + splashScreen->setGeometry( 0, 0, 600, 550 ); + + setMinimumSize( 600, 600 ); + + setMouseTracking( true ); + setFocusPolicy( StrongFocus ); + setFocus(); + + //******************************************************************** + // Layout the main window + //******************************************************************** + QHBoxLayout *layout1 = new QHBoxLayout( this ); + QVBoxLayout *layout2 = new QVBoxLayout; + QHBoxLayout *layout3 = new QHBoxLayout; + QVBoxLayout *layout4 = new QVBoxLayout; + + layout1->addLayout( layout2 ); + layout2->addLayout( layout3 ); + + layout3->addSpacing( 5 ); + layout3->addWidget( gameMessage, 10 ); + layout3->addWidget( shipCountEdit, 1 ); + layout3->addWidget( endTurn, 1 ); + + layout2->addSpacing( 5 ); + layout2->addWidget( mapWidget, 0, AlignTop ); + layout2->addWidget( msgWidget ); + layout2->addStretch( 1 ); + + layout1->addSpacing( 5 ); + layout1->addLayout( layout4, 10 ); + + layout4->addWidget( planetInfo, 1 ); + layout4->addSpacing( 10 ); + layout4->addWidget( turnCounter, 1 ); + layout4->addStretch( 1 ); + + layout1->addStretch( 1 ); + + //********************************************************************** + // Set up signal/slot connections + //********************************************************************** + connect( mapWidget, SIGNAL( planetSelected(Planet *) ), this, SLOT(planetSelected(Planet *)) ); + connect( shipCountEdit, SIGNAL(returnPressed()), this, SLOT(newShipCount()) ); + connect( endTurn, SIGNAL( clicked() ), this, SLOT( nextPlayer() ) ); + connect( mapWidget, SIGNAL( planetHighlighted(Planet *)), planetInfo, SLOT(showPlanet(Planet *)) ); + + changeGameBoard( false ); +} + + +//********************************************************************** +// Destructor +//********************************************************************** +GameBoard::~GameBoard() +{ + // Nothing much to do yet +} + +#if 0 +QSize GameBoard::sizeHint() const +{ + return QSize( 600, 550 ); +} +#endif + +//************************************************************************ +// Keyboard Event handlers +//************************************************************************ +void +GameBoard::keyPressEvent( QKeyEvent *e ) +{ + // Check for the escape key + if( e->key() == Key_Escape ) { + switch( gameState ) { + case DEST_PLANET: + case SHIP_COUNT: + case RULER_SOURCE: + case RULER_DEST: + gameState = SOURCE_PLANET; + haveSourcePlanet = false; + haveDestPlanet = false; + turn(); + break; + default: + break; + } + return; + } + + if( !isgraph( e->ascii() ) ) { + e->ignore(); + return; + } + + PlanetListIterator planetSearch( planets ); + QString planetName; + + planetName += toupper( e->ascii() ); + + for(Planet *p = planetSearch.toFirst(); + p != NULL; + p = ++planetSearch ) { + + if( p->getName() == planetName ) + planetSelected( p ); + } + +} + +QString +GameBoard::playerString(Player *player) +{ + if (!player) + player = currentPlayer->current(); + return player->getColoredName(); +} + +//************************************************************************ +// Game engine/state machine +//************************************************************************ +void +GameBoard::turn() +{ + PlanetListIterator planetAi( planets ); + PlanetListIterator planetAttack( planets ); + Planet *target = 0; + + switch( gameState ) { + case NONE : + // stuff for none + gameState = SOURCE_PLANET; + haveSourcePlanet = false; + haveDestPlanet = false; + haveShipCount = false; + shipCount = 0; + mapWidget->unselectPlanet(); + + + turn(); + setFocus(); + break; + + case SOURCE_PLANET : + + if( haveSourcePlanet ) { + gameState = DEST_PLANET; + + sourcePlanet->select(); + turn(); + + } else { + shipCountEdit->hide(); + endTurn->setEnabled( true ); + mapWidget->unselectPlanet(); + + gameMessage->setText( "" + playerString() + ": " + + i18n("Select source planet...") + "" ); + setFocus(); + } + + break; + + case DEST_PLANET : + + if( haveDestPlanet ) { + mapWidget->unselectPlanet(); + gameState = SHIP_COUNT; + turn(); + + } else { + shipCountEdit->hide(); + endTurn->setEnabled( false ); + sourcePlanet->select(); + gameMessage->setText( "" + playerString() + ": " + + i18n("Select destination planet...") + "" ); + setFocus(); + } + + break; + + case SHIP_COUNT: + + if( haveShipCount ) { + // We now have a complete fleet to send, so send it + sendAttackFleet( sourcePlanet, destPlanet, shipCount); + + shipCountEdit->hide(); + endTurn->setEnabled( true ); + + gameState = NONE; + turn(); + + endTurn->setFocus(); + + } else { + gameMessage->setText( currentPlayer->current()->getName() + + i18n(": How many ships?") ); + + shipCountEdit->setText( "" ); + shipCountEdit->show(); + shipCountEdit->setEnabled(true); + shipCountEdit->setFocus(); + + endTurn->setEnabled( false ); + + mapWidget->unselectPlanet(); + } + + break; + + case RULER_SOURCE: + if( haveSourcePlanet ) { + gameState = RULER_DEST; + sourcePlanet->select(); + turn(); + } else { + shipCountEdit->hide(); + endTurn->setEnabled( true ); + mapWidget->unselectPlanet(); + + gameMessage->setText( i18n("Ruler: Select starting planet.") ); + setFocus(); + } + + break; + + case RULER_DEST: + if( haveDestPlanet ) { + mapWidget->unselectPlanet(); + + // Display the distance between the two planets + CoreLogic cl; + double dist = cl.distance( sourcePlanet, destPlanet ); + + QString msg; + msg = i18n("The distance from Planet %1 to Planet %2 is %3 light years.\n" + "A ship leaving this turn will arrive on turn %4") + .arg(sourcePlanet->getName()) + .arg(destPlanet->getName()) + .arg(KGlobal::locale()->formatNumber( dist, 2 )) + .arg(KGlobal::locale()->formatNumber( turnNumber + (int)dist, 0 )); + KMessageBox::information( this, msg, i18n("Distance")); + + gameState = NONE; + turn(); + } else { + gameMessage->setText( i18n("Ruler: Select ending planet.") ); + shipCountEdit->hide(); + endTurn->setEnabled( false ); + sourcePlanet->select(); + + setFocus(); + } + + break; + + case AI_PLAYER: + endTurn->setEnabled( false ); + gameMessage->setText( i18n("Computer Player thinking...") ); + + Planet *home; + + int ships; + planetAi.toFirst(); + + while ((home = planetAi())) { + if (home->getPlayer() == currentPlayer->current()) { + + bool hasAttack = false; + ships = (int)floor(home->getFleet().getShipCount() * 0.7 ); + + if (ships >= 20) { + + Planet *attack; + double minDistance = 100; + planetAttack.toFirst(); + while ((attack = planetAttack())) { + bool skip = false; + + CoreLogic cl; + double dist = cl.distance( home, attack ); + + if ((dist < minDistance) && (attack->getPlayer() != currentPlayer->current()) && + (attack->getFleet().getShipCount() < ships )) { + AttackFleetListIterator FleetsinFlight( currentPlayer->current()->getAttackList() ); + AttackFleet *curFleet; + + while ( (curFleet = FleetsinFlight())) { + if (curFleet->destination == attack) { + skip = true; + } + } + if (skip) continue; + + target = attack; + hasAttack = true; + minDistance = dist; + } + } + + if (hasAttack) { + sendAttackFleet( home, target, ships ); + } + else { + planetAttack.toFirst(); + minDistance = 100; + int shipsToSend = 0; + bool hasDestination = false; + + while ((attack = planetAttack())) { + bool skip = false; + CoreLogic cl; + double dist = cl.distance( home, attack ); + int homeships = (int)floor(home->getFleet().getShipCount() * 0.5 ); + + if ((dist < minDistance) && (attack->getPlayer() == currentPlayer->current()) && + (attack->getFleet().getShipCount() < homeships )) { + AttackFleetListIterator FleetsinFlight( currentPlayer->current()->getAttackList() ); + AttackFleet *curFleet; + + while ( (curFleet = FleetsinFlight())) { + if (curFleet->destination == attack) { + skip = true; + } + } + if (skip) continue; + + shipsToSend = (int)floor((home->getFleet().getShipCount() - attack->getFleet().getShipCount()) / 2) ; + + target = attack; + hasDestination = true; + minDistance = dist; + } + } + + if (hasDestination) { + sendAttackFleet( home, target, shipsToSend ); + } + } + } + } + } + + endTurn->setEnabled( true ); + nextPlayer(); + + break; + + default: + break; + } + + QString turnStr; + turnStr = i18n("Turn #: %1 of %2").arg(turnNumber).arg(lastTurn); + + turnCounter->setText( turnStr ); + + emit newGameState( gameState ); +} +//************************************************************************ +// To the end turn processing (resolve combat, etc.) +//************************************************************************ +void +GameBoard::nextTurn() +{ + resolveShipsInFlight(); + + scanForSurvivors(); + + // advance to first living player + while( currentPlayer->current() && !currentPlayer->current()->isInPlay() ) { + ++(*currentPlayer); + }; + + // advance turn counter + turnNumber++; + + // update the planets + PlanetListIterator nextPlanet( planets ); + Planet *planet; + + while( (planet = nextPlanet()) ) + { + planet->turn(); + } + + // Tell the status widget to update itself + planetInfo->rescanPlanets(); + + Player *winner = findWinner(); + if (winner) + { + mapWidget->repaint(true); + KMessageBox::information(this, + i18n("The mighty %1 has conquered the galaxy!").arg(winner->getName()), + i18n("Game Over")); + } + + if( (turnNumber == lastTurn) && !winner ) + { + mapWidget->repaint(true); + GameEndDlg *dlg = new GameEndDlg( this ); + + if( dlg->exec() == KDialogBase::Yes ) { + lastTurn += dlg->extraTurns(); + } + + delete dlg; + } + + if( winner || (turnNumber >= lastTurn) ) + { + // Game over, man! Game over. + + mapWidget->repaint(true); + + gameOver(); + }; +} + +//************************************************************************ +// determine the fate of the ships in transit +//************************************************************************ +void +GameBoard::resolveShipsInFlight() +{ + AttackFleetList arrivingShips; + PlayerListIterator nextPlayer( players ); + Player *plr; + + while( (plr = nextPlayer()) ) { + AttackFleetListIterator nextFleet( plr->getAttackList() ); + + AttackFleet *fleet; + + while( (fleet = nextFleet()) ) { + double fleetArrivalTurn = floor(fleet->arrivalTurn); + + if( turnNumber == int (fleetArrivalTurn) ) { + doFleetArrival( fleet ); + plr->getAttackList().removeRef( fleet ); + delete fleet; + } + } + } + +} + +Player * +GameBoard::findWinner() +{ + Player *winner = 0; + int activePlayers = 0; + + PlayerListIterator nextPlayer( players ); + Player *plr; + + while( (plr = nextPlayer()) ) { + if (plr->isInPlay()) + { + winner = plr; + activePlayers++; + } + else if (plr->getAttackList().count() != 0) + { + activePlayers++; + } + } + if (activePlayers == 1) + return winner; + + return 0; +} + +void +GameBoard::gameMsg(const QString &msg, Player *player, Planet *planet, Player *planetPlayer) +{ + bool isHumanInvolved = false; + + QString color = "white"; + QString colorMsg = msg; + QString plainMsg = msg; + + if (player) + { + if (!player->isAiPlayer()) + isHumanInvolved = true; + colorMsg = colorMsg.arg(playerString(player)); + plainMsg = plainMsg.arg(player->getName()); + } + + if (planet) + { + if (!planetPlayer) + planetPlayer = planet->getPlayer(); + if (!planetPlayer->isAiPlayer() && !planetPlayer->isNeutral()) + isHumanInvolved = true; + + QString color = planetPlayer->getColor().name(); + colorMsg = colorMsg.arg(QString("%2").arg(color, planet->getName())); + plainMsg = plainMsg.arg(planet->getName()); + } + msgWidget->append(("Turn %1: ").arg(turnNumber)+colorMsg+""); + msgWidget->scrollToBottom(); + + if (isHumanInvolved) + { + mapWidget->repaint(true); + KMessageBox::information(this, plainMsg); + } +} + +//************************************************************************ +// check to see any players have been eliminated +//************************************************************************ +void +GameBoard::scanForSurvivors() +{ + PlayerListIterator nextPlayer( players ); + PlayerList activePlayers; + PlayerList inactivePlayers; + + // insert all of the active players into a special + // list, the deactivate them + Player *plr; + while( (plr = nextPlayer()) ) { + if( plr->isInPlay() ) { + activePlayers.append( plr ); + plr->setInPlay( false ); + } else { + inactivePlayers.append( plr ); + } + } + + + // iterate through the list of planets and + // mark their owners in play + PlanetListIterator nextPlanet( planets ); + + Planet *planet; + while( (planet = nextPlanet()) ) { + planet->getPlayer()->setInPlay( true ); + } + + + PlayerListIterator nextActivePlayer( activePlayers ); + while( (plr = nextActivePlayer()) ) { + if( !plr->isInPlay() ) { + // Player has bitten the dust + QString msg; + msg = i18n("The once mighty empire of %1 has fallen in ruins."); + gameMsg(msg, plr); + } + } + + PlayerListIterator nextInactivePlayer( inactivePlayers ); + while( (plr = nextInactivePlayer()) ) { + if( plr->isInPlay() ) { + // Player has bitten the dust + QString msg; + msg = i18n("The fallen empire of %1 has staggered back to life."); + gameMsg(msg, plr); + } + } +} + +//************************************************************************ +// handle the arrival of a fleet at a planet +//************************************************************************ +void +GameBoard::doFleetArrival( AttackFleet *arrivingFleet ) +{ + // Check to see of (fleet owner) == (planet owner) + // if the planet and fleet owner are the same, then merge the fleets + // otherwise attack. + + if( (*arrivingFleet->owner) == (*arrivingFleet->destination->getPlayer())) { + if (!arrivingFleet->owner->isAiPlayer()) { + arrivingFleet->destination->getFleet().absorb(arrivingFleet); + + QString msg; + msg = i18n("Reinforcements (%1 ships) have arrived for planet %2.") + .arg(arrivingFleet->getShipCount()); + gameMsg(msg, 0, arrivingFleet->destination); + } + } else { + + // let's get ready to rumble... + + CoreLogic cl; + AttackFleet &attacker = *arrivingFleet; + DefenseFleet &defender = arrivingFleet->destination->getFleet(); + Planet &prizePlanet = *(arrivingFleet->destination); + + bool haveVictor = false; + bool planetHolds = true; + + while( !haveVictor ) { + double attackerRoll = cl.roll(); + double defenderRoll = cl.roll(); + + if( defenderRoll < prizePlanet.getKillPercentage() ) { + attacker.removeShips( 1 ); + } + + if( attacker.getShipCount() <= 0 ) { + haveVictor = true; + planetHolds = true; + continue; + } + + if( attackerRoll < attacker.killPercentage ) { + defender.removeShips( 1 ); + attacker.owner->statEnemyShipsDestroyed( 1 ); + } + + if( defender.getShipCount() <= 0 ) { + haveVictor = true; + planetHolds = false; + } + } + + if( planetHolds ) { + prizePlanet.getPlayer()->statEnemyFleetsDestroyed(1); + QString msg; + msg = i18n("Planet %2 has held against an attack from %1."); + gameMsg(msg, attacker.owner, &prizePlanet); + } else { + Player *defender = prizePlanet.getPlayer(); + attacker.owner->statEnemyFleetsDestroyed( 1 ); + + arrivingFleet->destination->conquer( arrivingFleet ); + + QString msg; + msg = i18n("Planet %2 has fallen to %1."); + gameMsg(msg, attacker.owner, &prizePlanet, defender); + } + } + + mapWidget->repaint(true); +} + +//************************************************************************ +// Set up the game board for a new game +//************************************************************************ +void +GameBoard::startNewGame() +{ + shutdownGame(); + + if( gameInProgress ) + return; + + NewGameDlg *newGame = new NewGameDlg( this, map, &players, neutralPlayer, &planets ); + + if( !newGame->exec() ) + { + delete newGame; + return; + } + newGame->save(); // Save settings for next time + + msgWidget->clear(); + + changeGameBoard( true ); + + planetInfo->setPlanetList(planets); + + shipCountEdit->hide(); + endTurn->setEnabled( true ); + + currentPlayer = new PlayerListIterator( players ); + currentPlayer->toFirst(); + + endTurn->show(); + gameMessage->show(); + + lastTurn = newGame->turns(); + + turnNumber = 1; + turn(); + + delete newGame; +} + +//************************************************************************ +// Shut down the current game +//************************************************************************ +void +GameBoard::shutdownGame() +{ + if( !gameInProgress ) + return; + + int choice = KMessageBox::warningContinueCancel + ( this, + i18n("Do you wish to retire this game?"), + i18n("End Game"), + KStdGuiItem::ok() ); + + if( choice == KMessageBox::Cancel ) + return; + + gameOver(); +} + +void +GameBoard::gameOver() +{ + ScoreDlg *scoreDlg = new ScoreDlg( this, i18n("Final Standings"), &players ); + scoreDlg->exec(); + + cleanupGame(); +} + +void +GameBoard::cleanupGame() +{ + map->clearMap(); + + planets.clear(); + players.clear(); + + delete currentPlayer; + currentPlayer = NULL; + + shipCountEdit->hide(); + endTurn->setEnabled( false ); + + gameMessage->hide(); + endTurn->hide(); + + changeGameBoard( false ); + gameState = NONE; + emit newGameState(gameState); +} + + +//************************************************************************ +// Player selected a planet +//************************************************************************ +void +GameBoard::planetSelected( Planet *planet ) +{ + switch( gameState ) { + case SOURCE_PLANET: + if( (*planet->getPlayer()) == (*currentPlayer->current()) ) { + // got a match + haveSourcePlanet = true; + sourcePlanet = planet; + + turn(); + } + + break; + + case RULER_SOURCE: + haveSourcePlanet = true; + sourcePlanet = planet; + turn(); + break; + + case DEST_PLANET: + case RULER_DEST: + if( planet != sourcePlanet ) { + // got a match + haveDestPlanet = true; + destPlanet = planet; + + turn(); + } + + break; + + default: + case NONE : + break; + } + +} + +//************************************************************************ +// Player hit return in the ship count edit box +//************************************************************************ +void +GameBoard::newShipCount() +{ + QString temp( shipCountEdit->text() ); + bool ok; + + switch( gameState ) { + case SHIP_COUNT: + shipCount = temp.toInt(&ok); + + if( ok ) + haveShipCount = true; + + shipCountEdit->setText( "" ); + + turn(); + break; + + default: + break; + }; + +} + +//********************************************************************** +// transition board from play to non-play +//********************************************************************** +void +GameBoard::changeGameBoard( bool inPlay ) +{ + gameInProgress = inPlay; + + if( gameInProgress ) { + mapWidget->show(); + planetInfo->show(); + gameMessage->show(); + endTurn->show(); + shipCountEdit->show(); + splashScreen->hide(); + setBackgroundColor( black ); + } else { + mapWidget->hide(); + planetInfo->hide(); + gameMessage->hide(); + endTurn->hide(); + shipCountEdit->hide(); + splashScreen->show(); + setBackgroundColor( black ); + } + +} + +//************************************************************************ +// Player clicked the 'End Turn' button +//************************************************************************ +void +GameBoard::nextPlayer() +{ + // end turn and advance to next player + Player *plr; + + while( (plr = ++(*currentPlayer)) && !(plr->isInPlay()) ) {} + + if( !plr ) { + // end of player list, new turn + currentPlayer->toFirst(); + nextTurn(); + } + + if( gameInProgress ) { + if (currentPlayer->current()->isAiPlayer()) { + gameState = AI_PLAYER; + } + else { + gameState = SOURCE_PLANET; + + } + turn(); + } +} + +//************************************************************************ +// A complete set of source, destination planets and ship count has been +// entered, so do something about it +//************************************************************************ +void +GameBoard::sendAttackFleet( Planet *source, Planet *dest, int ship ) +{ + bool ok; + + ok = currentPlayer->current()->NewAttack( source, dest, + ship, turnNumber ); + + if( !ok ) { + KMessageBox::error( this, + i18n("Not enough ships to send.") ); + } +} + +//************************************************************************ +// Toolbar items +//************************************************************************ +void +GameBoard::measureDistance() +{ + switch( gameState ) { + case SOURCE_PLANET: + gameState = RULER_SOURCE; + turn(); + default: + break; + } +} + +void +GameBoard::showScores() +{ + ScoreDlg *scoreDlg = new ScoreDlg( this, i18n("Current Standings"), &players ); + scoreDlg->show(); +} + +void +GameBoard::showFleets() +{ + FleetDlg *fleetDlg = new FleetDlg( this, &(currentPlayer->current()->getAttackList()) ); + fleetDlg->show(); +} diff --git a/konquest/gameboard.h b/konquest/gameboard.h new file mode 100644 index 00000000..5e3cddfa --- /dev/null +++ b/konquest/gameboard.h @@ -0,0 +1,119 @@ +#ifndef _GAMEBOARD_H_ +#define _GAMEBOARD_H_ + +#include + +#include "planet_info.h" +#include "map_widget.h" + +//************************************************************************ +// forward declarations +//************************************************************************ +class QSlider; +class QLabel; +class QListBox; +class QPushButton; +class QLineEdit; +class QTextEdit; + +enum GameState { NONE, SOURCE_PLANET, DEST_PLANET, SHIP_COUNT, RULER_SOURCE, RULER_DEST, AI_PLAYER }; + +//************************************************************************ +// GameBoard Widget +//************************************************************************ +class GameBoard : public QWidget +{ + Q_OBJECT + +public: + GameBoard( QWidget *parent ); + virtual ~GameBoard(); + + bool isGameInProgress(void) const { return gameInProgress; } + +// virtual QSize sizeHint() const; + +protected slots: + void startNewGame(); + void shutdownGame(); + void planetSelected( Planet * ); + void newShipCount(); + void nextPlayer(); + + //*************************************************************** + // Toolbar items + //*************************************************************** + void measureDistance(); + void showScores(); + void showFleets(); + +signals: + void newGameState( GameState newState ); + + //*************************************************************** + // Event Handlers + //*************************************************************** +protected: + virtual void keyPressEvent( QKeyEvent * ); + +private: + void turn(); + void nextTurn(); + void gameOver(); + + void resolveShipsInFlight(); + void sendAttackFleet( Planet *source, Planet *dest, int ships ); + void doFleetArrival( AttackFleet *arrivingFleet ); + void scanForSurvivors(); + + void gameMsg(const QString &msg, Player *player = 0, Planet *planet = 0, Player *planetPlayer = 0); + + void changeGameBoard( bool inPlay ); + void cleanupGame(); + Player *findWinner(); + + QString playerString(Player *player = 0); + + //*************************************************************** + // Game State information + //*************************************************************** + bool gameInProgress; + GameState gameState; + PlayerListIterator *currentPlayer; + + //*************************************************************** + // Display Widgets + //*************************************************************** + ConquestMap *mapWidget; + PlanetInfo *planetInfo; + QLabel *gameMessage; + QLabel *turnCounter; + QPushButton *endTurn; + QLineEdit *shipCountEdit; + QLabel *splashScreen; + QTextEdit *msgWidget; + + + //*************************************************************** + // Game objects + //*************************************************************** + int turnNumber; + int lastTurn; + + PlayerList players; + PlanetList planets; + Player *neutralPlayer; + Map *map; + + bool haveSourcePlanet; + Planet *sourcePlanet; + + bool haveDestPlanet; + Planet *destPlanet; + + bool haveShipCount; + int shipCount; + +}; + +#endif diff --git a/konquest/gamecore.cc b/konquest/gamecore.cc new file mode 100644 index 00000000..843c1a92 --- /dev/null +++ b/konquest/gamecore.cc @@ -0,0 +1,646 @@ +#include "gamecore.h" +#include "gamecore.moc" + +#include +#include + +#include + +//******************************************************************* +// Game Core Logic +//******************************************************************* + +bool CoreLogic::class_init = false; + +CoreLogic::CoreLogic() +{ + random.setSeed(0); +} + +void +CoreLogic::generatePlanetCoordinates( int &x, int &y ) +{ + // 0 - 15 + x = random.getLong(16); + y = random.getLong(16); +} + +double +CoreLogic::generateKillPercentage() +{ + // 0.30 - 0.90 + return 0.30 + random.getDouble()*0.60; +} + +int +CoreLogic::generatePlanetProduction() +{ + // 5 - 15 + return 5 + random.getLong(10); +} + +double +CoreLogic::generateMorale() +{ + // constant + return 0.50; +} + +double +CoreLogic::distance( Planet *p1, Planet *p2 ) +{ + int k = (p1->getSector().getRow() - p2->getSector().getRow()) / 2; + int l = (p1->getSector().getColumn() - p2->getSector().getColumn()) / 2; + + return sqrt(double((k*k) + (l*l))); +} + +double +CoreLogic::roll() +{ + // 0.00 - 1.00 + return random.getDouble(); +} + +//--------------------------------------------------------------------------- +// class Map +//--------------------------------------------------------------------------- + + +Map::Map() + : QObject( 0, 0 ), freezeUpdates( false ), + rows( BOARD_ROWS ), columns( BOARD_COLS ), + hasSelectedSector( false ) +{ + // initialize the grid of Sectors + for( int x = 0; x < rows; x++ ) + { + for( int y = 0; y < columns; y++ ) + { + grid[x][y] = Sector( this, x, y ); + connect( &grid[x][y], SIGNAL( update() ), this, SLOT( childSectorUpdate() )); + } + } +} + +Map::~Map() +{ +} + +void +Map::populateMap( PlayerList &players, Player *neutral, + int numNeutralPlanets, PlanetList &thePlanets ) +{ + Freeze(); + + int index = 0; + QString names( "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),.<>;:[]{}/?-+\\|" ); + + // Create a planet for each player + Player *plr; + for( plr = players.first(); plr != 0; plr = players.next() ) + { + QString newName( names.mid( index++, 1 ) ); + Sector § = findRandomFreeSector(); + Planet *plrPlanet = Planet::createPlayerPlanet( sect, plr, newName ); + + thePlanets.append( plrPlanet ); + } + + for( int x = 0; x < numNeutralPlanets; x++ ) + { + QString newName( names.mid( index++, 1 ) ); + Sector § = findRandomFreeSector(); + Planet *neutralPlanet = Planet::createNeutralPlanet( sect, neutral, newName ); + + thePlanets.append( neutralPlanet ); + } + + Thaw(); + + emit update(); +} + +void +Map::clearMap() +{ + Freeze(); + + int x,y; + + for( x = 0; x < rows; x++ ) + for( y = 0; y < columns; y++ ) + { + grid[x][y].removePlanet(); + } + + + Thaw(); + + emit update(); +} + +Sector & +Map::findRandomFreeSector() +{ + CoreLogic cl; + + bool found = false; + + while( !found ) + { + int x,y; + + cl.generatePlanetCoordinates( x,y ); + + if( !grid[x][y].hasPlanet() ) + { + return grid[x][y]; + } + } + + // TODO: get rid of this + return grid[0][0]; + +} + +bool +Map::selectedSector( int &x, int &y ) const +{ + if( hasSelectedSector) + { + x = sel_x; + y = sel_y; + } + + return hasSelectedSector; + +} + +void +Map::setSelectedSector( int x, int y ) +{ + hasSelectedSector = true; + sel_x = x; + sel_y = y; + + emit update(); +} + +void +Map::setSelectedSector( const Planet &planet ) +{ + hasSelectedSector = true; + sel_x = planet.getSector().getRow(); + sel_y = planet.getSector().getColumn(); + + emit update(); +} + +void +Map::setSelectedSector() +{ + hasSelectedSector = false; + + emit update(); +} + + +void Map::childSectorUpdate() +{ + if( !freezeUpdates ) + emit update(); +} + +void Map::Freeze() +{ + freezeUpdates = true; +} + +void Map::Thaw() +{ + freezeUpdates = false; +} + +Sector &Map::getSector( int x, int y ) +{ + return grid[x][y]; +} + +const int Map::getRows() const +{ + return rows; +} + +const int Map::getColumns() const +{ + return columns; +} + +//--------------------------------------------------------------------------- +// class Sector +//--------------------------------------------------------------------------- + +Sector::Sector() +: QObject(0,0), planet( NULL ), parentMap(NULL ), x(0), y(0) +{} + +Sector::Sector( Map *newParentMap, int xPos, int yPos ) +: QObject(0,0), planet(NULL), parentMap( newParentMap ), x(xPos), y(yPos) +{ +} + +Sector::Sector( const Sector & other ) +: QObject(0,0), planet(other.planet), parentMap(other.parentMap), x(other.x), y(other.y) +{ +} + +bool Sector::hasPlanet() const +{ + return (planet != NULL); +} + + +void Sector::setPlanet( Planet *newPlanet ) +{ + planet = newPlanet; + + connect( planet, SIGNAL( update() ), this, SLOT( childPlanetUpdate() ) ); + + emit update(); +} + +Planet *Sector::getPlanet() +{ + return planet; +} + +void Sector::removePlanet() +{ + planet = NULL; + + emit update(); +} + + +void Sector::childPlanetUpdate() +{ + emit update(); +} + +Sector & +Sector::operator=( const Sector &other ) +{ + x = other.x; + y = other.y; + planet = other.planet; + parentMap = other.parentMap; + + return *this; +} + +void +Sector::select() +{ + parentMap->setSelectedSector( x, y ); + emit selected(); +} + +int Sector::getRow() +{ + return x; +} + +int Sector::getColumn() +{ + return y; +} + +//--------------------------------------------------------------------------- +// class Planet +//--------------------------------------------------------------------------- + +Planet::Planet( QString planetName, Sector &newParentSector, Player *initialOwner, + int newProd, double newKillP, double newMorale ) + : QObject(0,0), name(planetName), owner(initialOwner), parentSector(newParentSector), + homeFleet( this, newProd ), killPercentage(newKillP), morale( newMorale ), productionRate(newProd) + + +{ + parentSector.setPlanet( this ); +} + +Planet::~Planet() {} + +Planet * +Planet::createPlayerPlanet( Sector &parentSector, Player *initialOwner, QString planetName ) +{ + CoreLogic clogic; + + double morale = clogic.generateMorale(); + + return new Planet( planetName, parentSector, initialOwner, + 10, 0.400, morale ); +} + +Planet * +Planet::createNeutralPlanet( Sector &parentSector, Player *initialOwner, QString planetName ) +{ + CoreLogic clogic; + double morale = clogic.generateMorale(); + + double killP = clogic.generateKillPercentage(); + + int productionRate = (int)clogic.generatePlanetProduction(); + + return new Planet( planetName, parentSector, + initialOwner, productionRate, killP, morale ); +} + +double +Planet::getKillPercentage() +{ + return killPercentage; +} + +void +Planet::setKillPercentage( double newValue ) +{ + killPercentage = newValue; + + emit update(); +} + +double +Planet::getMorale() +{ + return morale; +} + +void +Planet::setMorale( double newMorale ) +{ + morale = newMorale; +} + +int +Planet::getProduction() +{ + return productionRate; +} + +void +Planet::setProduction( int newProduction ) +{ + productionRate = newProduction; +} + +void +Planet::select() +{ + parentSector.select(); + + emit selected(); +} + +DefenseFleet &Planet::getFleet() +{ + return homeFleet; +} + +Player * +Planet::getPlayer() const +{ + return owner; +} + +const QString & +Planet::getName() const +{ + return name; +} + +Sector & +Planet::getSector() const +{ + return parentSector; +} + +void +Planet::conquer( AttackFleet *conqueringFleet ) +{ + owner = conqueringFleet->owner; + owner->statPlanetsConquered(1); + homeFleet.become( conqueringFleet ); +} + +void +Planet::coup( Player *luckyPlayer ) +{ + owner = luckyPlayer; +} + +void +Planet::turn() +{ + if( !(owner->isNeutral()) ) { + homeFleet.addShips( productionRate ); + owner->statShipsBuilt( productionRate ); + } else { + homeFleet.addShips( 1 ); + } +} + +//--------------------------------------------------------------------------- +// class Player +//--------------------------------------------------------------------------- +Player::Player( QString newName, QColor newColor, int newPlrNum, bool isAi ) : name( newName ), color( newColor ), +playerNum( newPlrNum ), inPlay( true ), aiPlayer( isAi ), shipsBuilt(0), planetsConquered(0), fleetsLaunched(0), +enemyFleetsDestroyed(0), enemyShipsDestroyed(0) +{ +} + +Player::~Player() +{ +} + +bool +Player::operator==( const Player &otherPlayer ) const +{ + if( playerNum == otherPlayer.playerNum ) + return true; + else + return false; +} + +QString & +Player::getName() +{ + return name; +} + +QString +Player::getColoredName() +{ + return QString("%2").arg(color.name(), name); +} + +Player *Player::createPlayer( QString newName, QColor color, int playerNum, bool isAi ) +{ + return new Player( newName, color, playerNum, isAi ); +} + +Player *Player::createNeutralPlayer() +{ + return new Player( QString::null, gray, NEUTRAL_PLAYER_NUMBER, false ); +} + +QColor &Player::getColor() +{ + return color; +} + +bool +Player::isNeutral() +{ + if( playerNum == NEUTRAL_PLAYER_NUMBER ) { + return true; + } else { + return false; + } +} + +bool Player::isInPlay() +{ + return inPlay; +} + +void Player::setInPlay( bool status ) +{ + inPlay = status; +} + +AttackFleetList & +Player::getAttackList() +{ + return attackList; +} + +bool +Player::NewAttack( Planet *sourcePlanet, Planet *destPlanet, int shipCount, int turn ) +{ + CoreLogic cl; + + double arrival = cl.distance( sourcePlanet, destPlanet ) + turn; + + AttackFleet *fleet = sourcePlanet->getFleet().spawnAttackFleet( destPlanet, shipCount, arrival ); + + + if( fleet ) { + attackList.append(fleet); + + statFleetsLaunched( 1 ); + + return true; + } + + return false; +} + +// Player Statistics collection +void Player::statShipsBuilt( int x ) +{ + shipsBuilt += x; +} + +void Player::statPlanetsConquered( int x ) +{ + planetsConquered += x; +} + +void Player::statFleetsLaunched( int x ) +{ + fleetsLaunched += x; +} + +void Player::statEnemyFleetsDestroyed( int x ) +{ + enemyFleetsDestroyed += x; +} + +void Player::statEnemyShipsDestroyed( int x ) +{ + enemyShipsDestroyed += x; +} + +bool Player::isAiPlayer() { + return aiPlayer; +} + +//--------------------------------------------------------------------------- +// class Fleet +// \---class AttackFleet +// \---class DefenseFleet +//--------------------------------------------------------------------------- + + +Fleet::Fleet( int initialShipCount ) +: shipCount( initialShipCount ) +{ +} + +int +Fleet::getShipCount() +{ + return shipCount; +} + +void +Fleet::removeShips( int lostShips ) +{ + shipCount -= lostShips; +} + +AttackFleet::AttackFleet( Planet *source, Planet *dest, int initialCount, double arrival ) +: Fleet( initialCount ), owner( source->getPlayer() ), destination( dest ), arrivalTurn( arrival ), +killPercentage( source->getKillPercentage() ) +{ +} + +DefenseFleet::DefenseFleet( Planet *newHome, int initialCount ) : Fleet( initialCount ), home( newHome ) +{ +} + +void +DefenseFleet::absorb( AttackFleet *fleet ) +{ + shipCount += fleet->getShipCount(); +} + +void +DefenseFleet::become( AttackFleet *fleet ) +{ + shipCount = fleet->getShipCount(); +} + + +AttackFleet * +DefenseFleet::spawnAttackFleet( Planet *dest, int count, double arrivalTurn ) +{ + if( shipCount < count ) { + return NULL; + } + + AttackFleet *newFleet = new AttackFleet( home, dest, count, arrivalTurn ); + + removeShips( count ); + + return newFleet; +} + +void +DefenseFleet::addShips( int newShips ) +{ + shipCount += newShips; +} + diff --git a/konquest/gamecore.h b/konquest/gamecore.h new file mode 100644 index 00000000..c8c7c39a --- /dev/null +++ b/konquest/gamecore.h @@ -0,0 +1,331 @@ +#ifndef _GAMECORE_H_ +#define _GAMECORE_H_ + +#include + +#include +#include +#include +#include + +// Board Size Constants +#define BOARD_ROWS 16 +#define BOARD_COLS 16 + +// Maximum Number of Players +#define MAX_PLAYERS 10 + + +//********************************************************** +// Forward declarations for classes in this file +//********************************************************** +class Player; +class Planet; +class Sector; +class Map; +class Fleet; + + +//********************************************************** +// Core Logic routines +//********************************************************** + +class CoreLogic +{ +public: + CoreLogic(); + + void generatePlanetCoordinates( int &x, int &y ); + double generateKillPercentage(); + int generatePlanetProduction(); + double generateMorale(); + + double distance( Planet *p1, Planet *p2 ); + + double roll(); + +private: + KRandomSequence random; + static bool class_init; +}; + + +//********************************************************** +// class Fleet +// \--- class AttackFleet +// \--- class DefenseFleet +//********************************************************** + +class Fleet : public QObject +{ + +public: + + Fleet( int initialShipCount ); + virtual ~Fleet() {} + + int getShipCount(); + void removeShips( int lostShips ); + +protected: + int shipCount; +}; + +class AttackFleet : public Fleet +{ + +public: + AttackFleet( Planet *source, Planet *dest, int initialCount, double arrivalTurn ); + + Player *owner; + Planet *destination; + double arrivalTurn; + double killPercentage; + +}; + + +class DefenseFleet : public Fleet +{ + +public: + DefenseFleet( Planet *newHome, int initialCount ); + + void absorb( AttackFleet *fleet ); + void become( AttackFleet *fleet ); + + void addShips( int newShips ); + + AttackFleet *spawnAttackFleet( Planet *destination, int shipCount, double arrivalTurn ); + + Planet *home; + +}; + + +//************************************************************** +// class Player +//************************************************************** + +class Player : public QObject +{ + +public: + Player( QString newName, QColor color, int number, bool isAi ); + virtual ~Player(); + + enum { NEUTRAL_PLAYER_NUMBER = -1 }; + +public: + QString &getName(); + QString getColoredName(); + QColor &getColor(); + bool isNeutral(); + QPtrList &getAttackList(); + + // factory functions + static Player *createPlayer( QString newName, QColor newColor, int playerNum, bool isAi ); + static Player *createNeutralPlayer(); + + bool NewAttack( Planet *sourcePlanet, Planet *destPlanet, int shipCount, int departureTurn ); + + bool operator==( const Player &otherPlayer ) const; + + bool isInPlay(); + void setInPlay( bool ); + +private: + QString name; + QColor color; + int playerNum; + bool inPlay; + bool aiPlayer; + + QPtrList attackList; + + // statistics counters + int shipsBuilt; + int planetsConquered; + int fleetsLaunched; + int enemyFleetsDestroyed; + int enemyShipsDestroyed; + +public: + void statShipsBuilt( int ); + void statPlanetsConquered( int ); + void statFleetsLaunched( int ); + void statEnemyFleetsDestroyed( int ); + void statEnemyShipsDestroyed( int ); + + int getShipsBuilt() { return shipsBuilt; } + int getPlanetsConquered() { return planetsConquered; } + int getFleetsLaunched() { return fleetsLaunched; } + int getEnemyFleetsDestroyed() { return enemyFleetsDestroyed; } + int getEnemyShipsDestroyed() { return enemyShipsDestroyed; } + bool isAiPlayer(); + +}; + + +//************************************************************** +// class Planet +//************************************************************** + +class Planet : public QObject +{ + Q_OBJECT + +private: + + Planet( QString planetName, Sector &newParentSector, + Player *initialOwner, int newProd, + double newKillP, double newMorale ); + +public: + virtual ~Planet(); + + static Planet *createPlayerPlanet( Sector &parentSector, + Player *initialOwner, QString planetName ); + static Planet *createNeutralPlanet( Sector &parentSector, + Player *initialOwner, QString planetName ); + + Sector &getSector() const; + Player *getPlayer() const; + const QString &getName() const; + DefenseFleet &getFleet(); + + double getKillPercentage(); + void setKillPercentage( double newValue ); + double getMorale(); + void setMorale( double ); + int getProduction(); + void setProduction( int ); + + void select(); + void conquer( AttackFleet *conqueringFleet ); + void coup( Player *luckyPlayer ); + void turn(); + +signals: + void update(); + void selected(); + +private: + QString name; + Player *owner; + Sector &parentSector; + DefenseFleet homeFleet; + + double killPercentage; + double morale; + int productionRate; +}; + +//*************************************************************** +// class Sector +//*************************************************************** + +class Sector : public QObject +{ + Q_OBJECT + +public: + + // constructors + Sector(); + Sector( Map *parentMap, int xpos, int ypos ); + Sector( const Sector & ); + + // assignment operator (makes initialization easy) + Sector &operator=( const Sector & ); + + bool hasPlanet() const; + void setPlanet( Planet *newPlanet ); + Planet *getPlanet(); + void removePlanet(); + + void select(); + + int getRow(); + int getColumn(); + +signals: + void update(); + void selected(); + +protected slots: + void childPlanetUpdate( ); + + +protected: + Planet *planet; // a sector has 0 or 1 planets + Map *parentMap; + int x, y; + +}; + +//***************************************************************** +// class Map +//***************************************************************** + +class Map : public QObject +{ + Q_OBJECT + +public: + Map(); + virtual ~Map(); + + const int getRows() const; + const int getColumns() const; + + void populateMap( QPtrList &players, Player *neutral, + int numNeutralPlanets, QPtrList &thePlanets ); + void clearMap(); + + bool selectedSector( int &x, int &y ) const; + void setSelectedSector( int x, int y ); + void setSelectedSector( const Planet & ); + void setSelectedSector(); + + Sector &getSector( int x, int y ); + +protected slots: + void childSectorUpdate(); + +signals: + void update(); + +protected: + void Freeze(); + void Thaw(); + bool freezeUpdates; + +private: + Sector &findRandomFreeSector(); + +protected: + Sector grid[BOARD_ROWS][BOARD_COLS]; // a map is a 2-D array of sectors; + const int rows; // size of grid in sectors + const int columns; + + // This is used to implement a selected sector, + // one who's boarder flashes. + bool hasSelectedSector; + int sel_x, sel_y; +}; + +//--------------------------------------------------------------------------------- +// Typedefs +//--------------------------------------------------------------------------------- +typedef QPoint Coordinate; // Gotta start using this instead of int x,y crap +typedef QPtrList AttackFleetList; +typedef QPtrListIterator AttackFleetListIterator; +typedef QPtrList PlayerList; +typedef QPtrList PlanetList; +typedef QPtrListIterator PlayerListIterator; +typedef QPtrListIterator PlanetListIterator; + +#endif // _GAMECORE_H_ + + diff --git a/konquest/gameenddlg.cc b/konquest/gameenddlg.cc new file mode 100644 index 00000000..080a8477 --- /dev/null +++ b/konquest/gameenddlg.cc @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "gameenddlg.h" +#include "gameenddlg.moc" + +GameEndDlg::GameEndDlg( QWidget *parent ) + : KDialogBase( i18n("Out of Turns"), + KDialogBase::Yes|KDialogBase::No, KDialogBase::Yes, KDialogBase::No, + parent, "end_game_dialog", true, true ) +{ + QVBox *page = makeVBoxMainWidget(); + + // Create controls + QLabel *label1 = new QLabel( i18n("This is the last turn.\nDo you wish to add extra turns?")+"\n\n", page ); + label1->setAlignment( AlignCenter ); + + turnCountLbl = new QLabel( page ); + turnCount = new QSlider( 1, 40, 1, 5, Qt::Horizontal, page ); + + KGuiItem addTurns(i18n("&Add Turns"), QString::null, QString::null, + i18n("Add the specified number of turns to the game and continue playing.")); + KGuiItem gameOver(i18n("&Game Over"), QString::null, QString::null, + i18n("Terminate the current game.")); + + setButtonGuiItem(KDialogBase::Yes, addTurns); + setButtonGuiItem(KDialogBase::No, gameOver); + + init(); + + connect( turnCount, SIGNAL(valueChanged( int )), this, SLOT(turnCountChange( int )) ); +} + +GameEndDlg::~GameEndDlg() +{ +} + +void +GameEndDlg::init() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + int turns = config->readNumEntry("ExtraTurns", 10); + turnCount->setValue(turns); + turnCountChange(turns); +} + +void +GameEndDlg::slotYes() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + config->writeEntry("ExtraTurns", extraTurns()); + config->sync(); + KDialogBase::slotYes(); +} + +int +GameEndDlg::extraTurns() +{ + return turnCount->value(); +} + +void +GameEndDlg::turnCountChange( int newTurnCount ) +{ + QString newLbl = i18n("Extra turns: %1").arg( newTurnCount ); + turnCountLbl->setText( newLbl); +} diff --git a/konquest/gameenddlg.h b/konquest/gameenddlg.h new file mode 100644 index 00000000..d1c982e3 --- /dev/null +++ b/konquest/gameenddlg.h @@ -0,0 +1,32 @@ +#ifndef _GAMEENDDLG_H_ +#define _GAMEENDDLG_H_ + +#include + +class QSlider; +class QPushButton; + +class GameEndDlg : public KDialogBase +{ + Q_OBJECT + +public: + GameEndDlg( QWidget *parent ); + virtual ~GameEndDlg(); + + int extraTurns(); + +private: + void init(); + +private slots: + void turnCountChange( int ); + void slotYes(); + +private: + QSlider *turnCount; + QLabel *turnCountLbl; +}; + +#endif // _GAMEENDDLG_H_ + diff --git a/konquest/hi128-app-konquest.png b/konquest/hi128-app-konquest.png new file mode 100644 index 0000000000000000000000000000000000000000..3d980350fd1d3da17e6cc56f08a17e5c83398cbd GIT binary patch literal 13847 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 literal 0 HcmV?d00001 diff --git a/konquest/hi16-app-konquest.png b/konquest/hi16-app-konquest.png new file mode 100644 index 0000000000000000000000000000000000000000..87f6a2d50d366d05c5f6243b011a6b55b3732ceb GIT binary patch literal 628 zcmV-)0*n2LP)fi69ult6@mPGYwfk`4HW`i`p~d5A-HJB z(?=UJD-V~IRGi;ipSZiwHtG)==H0GdAhcZ2#g%>RmBociHyl6=P<-lG&B9x!L(Gbh zm=HqTnH&?AbG1)^xNukKD-WG){?Ox@UnLpM7Sk5H{a%w$gNpdwtPsNP9}wv|*%y}` z0BHSv-LI3A*B92+T|Ju+qhxQ&H0dRMEJouRB8Vh}hF^G5JYJ*YRcF&u0ARYU?b*vL z^N#(t6E$gbvmSki3IifWc zPv0O9TOQq>56bqnDTR$QimLz+oMdGf#MYu*}G0^i~Tsc$i1GWQL3ab0e zxm8_2Y(zK#D2{|WNl#WrX0CSBRy|cvurJLkEAFA;QGX~DoAcc*M?Pq7IQ=I8zyNFj zG(Z8}&hm2Gk&@DiqQoTAI{(x{#I2k4jVl4+k^mEs0a82!JN|P2AM_Jyy6%cJ*iV%J O0000z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v&k4Z#9RCwCdl}T(Hbri-w&y1JYabjn&v$ajz#CAREY}*E-6T@MCq**A>dHdU6dMyL?I+80xz-S ziDM^rY)@>@c*e`v{&VR8AzUhz9{Ne|`g>1$-}|0|5P}CG3J*vRA6NkZJ34zEN_DMO z68R992>n?oWH;{@%yf12JKlNkQ&&SnJ-wAm!5;|lUeAKd@0~jr-SGWTE)|MewMM1U z8@9`(QZbuOuHDPJ8JeM61_!$6w$=t*zB)^m-M=iQxLC2VdB^+aR_nX%E%1*W935}z z+tV4zSITqq?!~2VE}jjzuAP=~mlhO7o2O1)@WvBma^mEGqx2KU{K zizFy+kQ^zI45^S=B9f0;H+k{Hlh<`>t>La?cXnD1dwj97$Ge&9*xua_0F$HpXU}~0 zISE7qX8UWI1&h2q~v`3D-He$06*jDFfG|urSk9GIiVKf?H zFdCuwn}KwoQPE1Mt<~6yayW`4SQ#WLg=>F@2^ zi+oN%BI!pm5krWLAn0L{j;4@LWfAj+;PZtm`sO_ok`TWk7jr^2FaVGw7f;{*{ntrV zosM~8=vkOeyCKU0Xi7kxUJWfpBeR}>f59VNyLIW1Eaz3$p0O#Bk6kY0MXnkc1XwTg z{I+n&JNQS~hql(880Z~^$pxGQjc54Nc_}@;b65Y-F5B?2c+~%O zp&$u#6}MDGSzchWSxaGa!*XkW8eZ0qI$bmDJtJr`S&`y%;_a2l7eZE8H5yG#&y2nF zvZltcyRos|9$23HO7S0;X|7k&lTG#714X5JSBX(7td>W@&+R|Nj60uyw5_d06AUKu z$z+MT7@e+m$myJz*tgI5^x$K|-85Z8rFjv8 zP{gUJ6CWw=GsM&YI`sOwp4Qe5n^vngi;^U)vcB_*|1pBqcd7(HQap4&@2>#>2u8bs T^TUyr2tCXym0im#$1N}{YcDw5^aih|flY8eQS z0GWdhU9zO@&;lK@wLpgyMTem1P_#o^7%7l6P18h8gE)z8#kORdvTT_$E%E*BcqH$5 z?|1BArEt)up$oWO`~_~f;r{mlo_n4H9{0Haa~c3}_(IY6Nb*HNTqya!{)_LGjE=I{SBUHN(O?k z+8n+}G&FPOnP<+vJTdE0$Cjqe{?Q#Qg?5Z z>!rP308mx6adYPil{mJtHa|CN_78=_mo9#?S*;c`$ETCuo133}IWQJMY%GRAXc%^* z{a*$V09+aZ+P*GSi>h@J_GD^fjP<6X7Oz^O<}3?0;N}2dD5PS>4~d zy2;Nc*PeK4^_YKRZ0rQ_uz!4N^{ErC zK1T#R--fDEV7U%>fd|WUP_H#msa8;|mXIqIP%4%|lPz#f26Ux?MycZ{l<5r9uBILa z003-yFC zL90;5K|05DiQu};=5dm>%t!xjDZUB-0HPp?t-alyvn{UWnix+Y6pX=S8iFV?;Ds6_ ziGwKdP!t&&RfSGbAu~LvVjYca4VO1R`%^k|^>>58`5z7kr)Ep}o9_V-dj$YMN2jRK zGFe#OFYd!(cOn*@g3lWVLAasQiZGiLIP4}k?G~7g28f*wnx#4_hk51m-PDg+j{YOp zskOcSnOBF0C+7>f)SvOdas71wg%t(T(e18wI&IJO%q=upRd`%pgo81-`{N)?E*NxO z=(;R;ripf)1m9@m+O3=K-z^k=4xldZOvdXSO7suJz7+~h#1AstZv&8f0RTXw&G2=% zBAw8Pymc>^MJjy*RFi_M--Ez#6jpl_D(s+}G^lJI+dHY$UoKpE6~LXN8B91lIW-<% zI0>zB(mUX{9v-CL?-d}c=w|B-o3~2hNT100>h%V;cXx2{>SrkA3UJt*7>UIZj7`HG zn1)Qa%P#j2Yc^SpR;$|@i_ff_KK;@w9*=LJ)us@Q9$O}>g>8x|?rC}ee32c%!hp%} zyvJ@iX|$Nbg2CkEyPB@eW^Wx!%w^Zlyy#ec^68k{Z9#_t@&yw0Iw|l%mta{ARI`nI zz66cdfO6^J%CFw|{@TL;06-6506qmglk z-e5ti$)HYBsFO`Jn=Qyn7c48_^`E|S{9yrKqyb<&0s;3wDDmjt2_RrHnWh(#%Zp3P zPtMNHBxAu)%%{~E4FWHtP4h_YZ0CRRvmZSF-vWBce1!1$Nhf;4S@e2z5j8K`|ADyrQG3Kj3X6G00000NkvXXu0mjf D-ar@_ literal 0 HcmV?d00001 diff --git a/konquest/hi48-app-konquest.png b/konquest/hi48-app-konquest.png new file mode 100644 index 0000000000000000000000000000000000000000..bd67b1b95ac90f1e827d39967fd6fffbf874e51e GIT binary patch literal 3185 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 XTC^3id_yY`00000NkvXXu0mjf!MsA6 literal 0 HcmV?d00001 diff --git a/konquest/hi64-app-konquest.png b/konquest/hi64-app-konquest.png new file mode 100644 index 0000000000000000000000000000000000000000..7014729ea0149d0675b37c41a06f3c2c784963f6 GIT binary patch literal 4893 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 P00000NkvXXu0mjf9a?;3 literal 0 HcmV?d00001 diff --git a/konquest/images.h b/konquest/images.h new file mode 100644 index 00000000..4c436a18 --- /dev/null +++ b/konquest/images.h @@ -0,0 +1,17 @@ +#ifndef _IMAGES_H +#include +#define _IMAGES_H + +#define IMAGE_SPLASH locate("appdata", "pics/konquest-splash.png") +#define IMAGE_PLANET_1 locate("appdata", "pics/planet1.xpm") +#define IMAGE_PLANET_2 locate("appdata", "pics/planet2.xpm") +#define IMAGE_PLANET_3 locate("appdata", "pics/planet3.xpm") +#define IMAGE_PLANET_4 locate("appdata", "pics/planet4.xpm") +#define IMAGE_PLANET_5 locate("appdata", "pics/planet5.xpm") +#define IMAGE_PLANET_6 locate("appdata", "pics/planet6.xpm") +#define IMAGE_PLANET_7 locate("appdata", "pics/planet7.xpm") +#define IMAGE_PLANET_8 locate("appdata", "pics/planet8.xpm") +#define IMAGE_PLANET_9 locate("appdata", "pics/planet9.xpm") + +#endif // _IMAGES_H + diff --git a/konquest/int_validator.cc b/konquest/int_validator.cc new file mode 100644 index 00000000..85fd1779 --- /dev/null +++ b/konquest/int_validator.cc @@ -0,0 +1,53 @@ +#include + +#include "int_validator.h" +#include "int_validator.moc" + +IntValidator::IntValidator( QWidget *parent, const char *name ) : + QValidator( parent, name ) +{ +#ifdef INT_MIN + v_bottom = INT_MIN; +#else + v_bottom = ~INT_MAX; +#endif + v_top = INT_MIN; +} + +IntValidator::IntValidator( int bottom, int top, QWidget *parent, const char *name ) : +QValidator( parent, name ) +{ + v_bottom = bottom; + v_top = top; +} + +IntValidator::~IntValidator() {} + +QValidator::State +IntValidator::validate( QString &input, int & ) const +{ + if( input.isEmpty() ) { + return QValidator::Valid; + } else { + bool ok; + + int value = input.toInt( &ok ); + + if( !ok ) + return QValidator::Invalid; + + if( value < v_bottom || value > v_top ) + return QValidator::Valid; + + return QValidator::Acceptable; + } +} + +void +IntValidator::setRange( int b, int t ) +{ + v_bottom = b; + v_top = t; +} + + diff --git a/konquest/int_validator.h b/konquest/int_validator.h new file mode 100644 index 00000000..e5973fee --- /dev/null +++ b/konquest/int_validator.h @@ -0,0 +1,31 @@ +#ifndef _INT_VALIDATOR_H_ +#define _INT_VALIDATOR_H_ + +#include + + +class IntValidator : public QValidator +{ + Q_OBJECT + +public: + IntValidator( QWidget *parent, const char *name = 0 ); + IntValidator( int bottom, int top, QWidget *parent, const char *name = 0 ); + + virtual ~IntValidator(); + + virtual QValidator::State validate( QString &, int & ) const; + + virtual void setRange( int bottom, int top ); + + int bottom() const { return v_bottom; } + int top() const { return v_top; } + +private: + int v_bottom, v_top; +}; + + + +#endif /* _INT_VALIDATOR_H_ */ + diff --git a/konquest/konquest.desktop b/konquest/konquest.desktop new file mode 100644 index 00000000..e300059d --- /dev/null +++ b/konquest/konquest.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Icon=konquest +Name=Konquest +Name[ar]=لعبة الإحتلال (Konquest) +Name[be]=Заваёва +Name[bn]=কনকোয়েসà§à¦Ÿ +Name[br]=KAloubadur +Name[da]=Erobring +Name[eo]=Konkero +Name[et]=Vallutus +Name[hi]=कॉनà¥à¤•à¥à¤µà¥‡à¤¸à¥à¤Ÿ +Name[hu]=Hódítás +Name[is]=Geimstyrjöld +Name[lt]=Užkariautojas +Name[ne]=कनकà¥à¤µà¥‡à¤·à¥à¤Ÿ +Name[pt]=Conquista +Name[ta]=கானà¯à®•à¯à®•à¯à®µà¯†à®¸à¯à®Ÿà¯ +Name[tg]=ИÑтило +Name[zh_TW]=Konquest å¾æœ +Exec=konquest %i %m -caption %c +Type=Application +GenericName=Galactic Strategy Game +GenericName[be]=СтратÑÐ³Ñ–Ñ‡Ð½Ð°Ñ Ð³Ð°Ð»Ð°ÐºÑ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=ГалактичеÑка игра +GenericName[bn]=গà§à¦¯à¦¾à¦²à¦¾à¦•à¦Ÿà¦¿à¦• কৌশলের খেলা +GenericName[bs]=GalaktiÄka strateÅ¡ka igra +GenericName[ca]=Joc d'estratègia galàctica +GenericName[cs]=Strategická galaktická hra +GenericName[cy]=Gêm Strategaeth Galaethol +GenericName[da]=Galaktisk strategispil +GenericName[de]=Galaktisches Strategiespiel +GenericName[el]=Γαλαξιακό παιχνίδι στÏατηγικής +GenericName[eo]=Galaksia Strategiludo +GenericName[es]=Juego de estrategia espacial +GenericName[et]=Galaktiline strateegiamäng +GenericName[eu]=Estrategia galaktikoaren jokoa +GenericName[fa]=بازی Galactic Strategy +GenericName[fi]=Galaktinen avaruuspeli +GenericName[fr]=Jeu de stratégie galactique +GenericName[he]=משחק ×סטרטגיה גלקטי +GenericName[hr]=GalaktiÄka igra strategije +GenericName[hu]=Stratégiai +GenericName[is]=Herkænskuleikur +GenericName[it]=Gioco galattico di strategia +GenericName[ja]=宇宙戦略ゲーム +GenericName[km]=ល្បែង​យុទ្ធសាស្ážáŸ’រ​ផ្កាយ +GenericName[ko]=우주 ì „ëžµ 게임 +GenericName[lt]=Galaktikos strateginis žaidimas +GenericName[lv]=StratÄ“Ä£iska kosmiskÄ spÄ“le +GenericName[mk]=Игра на галактичка Ñтратегија +GenericName[nb]=Strategispillet Galactic +GenericName[nds]= Galaktsch Strategiespeel +GenericName[ne]=गà¥à¤¯à¤¾à¤²à¤¾à¤•à¥à¤Ÿà¤¿à¤• रणनीति खेल +GenericName[nl]=Strategisch ruimtespel +GenericName[nn]=Strategispelet Galactic +GenericName[pl]=Gra strategiczna w kosmosie +GenericName[pt]=Jogo de Estratégia Galáctica +GenericName[pt_BR]=Jogo de Estratégia galáctico +GenericName[ru]=Завоевание +GenericName[se]=Strategiijaspeallu Galactic +GenericName[sk]=Galaktická strategická hra +GenericName[sl]=GalaktiÄna strateÅ¡ka igra +GenericName[sr]=Галактичка Ñтратешка игра +GenericName[sr@Latn]=GalaktiÄka strateÅ¡ka igra +GenericName[sv]=Galaktiskt strategispel +GenericName[ta]=கலாடà¯à®Ÿà®¿à®•à¯ தநà¯à®¤à®¿à®° விளையாடà¯à®Ÿà¯ +GenericName[uk]=Галактична гра на Ñтратегію +GenericName[wa]=Djeu di stratedjeye galactike +GenericName[zh_TW]=銀河戰略éŠæˆ² +Terminal=false +DocPath=konquest/index.html +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/konquest/konquestui.rc b/konquest/konquestui.rc new file mode 100644 index 00000000..aff9e71a --- /dev/null +++ b/konquest/konquestui.rc @@ -0,0 +1,21 @@ + + + + + &Game + + + + + + +Main Toolbar + + + + + + + + + diff --git a/konquest/mainwin.cc b/konquest/mainwin.cc new file mode 100644 index 00000000..496d3ac0 --- /dev/null +++ b/konquest/mainwin.cc @@ -0,0 +1,77 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "gamecore.h" +#include "mainwin.h" +#include "mainwin.moc" +#include "gameboard.h" + +// KonquestMainWindow + + +MainWindow::MainWindow() +{ + setCaption( i18n("Galactic Conquest") ); + + setupGameBoard(); + setupKAction(); + setupGUI(); +} + +MainWindow::~MainWindow() +{ +} + +void +MainWindow::setupKAction() +{ + KStdGameAction::gameNew( gameBoard, SLOT( startNewGame() ), actionCollection() ); + KStdGameAction::quit( this, SLOT( close() ), actionCollection() ); + endAction = KStdGameAction::end( gameBoard, SLOT( shutdownGame() ), actionCollection() ); + endAction->setEnabled(false); + + //AB: there is no icon for disabled - KToolBar::insertButton shows the + //different state - KAction not :-( + measureAction = new KAction( i18n("&Measure Distance"), "ruler", 0, gameBoard, SLOT( measureDistance() ), actionCollection(), "game_measure" ); + measureAction->setEnabled(false); + standingAction = new KAction( i18n("&Show Standings"), "help", 0, gameBoard, SLOT( showScores() ), actionCollection(), "game_scores" ); + standingAction->setEnabled(false); + fleetAction = new KAction( i18n("&Fleet Overview"), "launch", 0, gameBoard, SLOT( showFleets() ), actionCollection(), "game_fleets" ); + fleetAction->setEnabled(false); + toolBar()->setBarPos( KToolBar::Left ); + toolBar()->setMovingEnabled( false ); +} + +void +MainWindow::setupGameBoard() +{ + gameBoard = new GameBoard( this ); + setCentralWidget(gameBoard); + + connect( gameBoard, SIGNAL( newGameState( GameState )), this, SLOT( gameStateChange( GameState ) ) ); +} + + +void +MainWindow::gameStateChange( GameState newState ) +{ + endAction->setEnabled( gameBoard->isGameInProgress() ); + measureAction->setEnabled( newState==SOURCE_PLANET ); + standingAction->setEnabled( newState==SOURCE_PLANET ); + fleetAction->setEnabled( newState==SOURCE_PLANET ); +} + + + diff --git a/konquest/mainwin.h b/konquest/mainwin.h new file mode 100644 index 00000000..2954039c --- /dev/null +++ b/konquest/mainwin.h @@ -0,0 +1,33 @@ +#ifndef _MAIN_WIN_H +#define _MAIN_WIN_H + +#include + +#include "gameboard.h" + +class ConquestMap; +class PlanetStatusTable; + +class MainWindow : public KMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + ~MainWindow(); + +protected: + void setupKAction(); + void setupGameBoard(); + +private: + GameBoard *gameBoard; + KAction *endAction, *measureAction, *standingAction, *fleetAction; + +private slots: + void gameStateChange( GameState ); + +}; + +#endif + diff --git a/konquest/map_widget.cc b/konquest/map_widget.cc new file mode 100644 index 00000000..9bb1a632 --- /dev/null +++ b/konquest/map_widget.cc @@ -0,0 +1,243 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include "map_widget.moc" + +ConquestMap::ConquestMap( Map *newMap, QWidget *parent ) + : QGridView( parent ), + SECTOR_HEIGHT( 28 ), SECTOR_WIDTH( 28 ), + BOARD_HEIGHT( newMap->getRows() * SECTOR_HEIGHT ), + BOARD_WIDTH( newMap->getColumns() * SECTOR_WIDTH ), + map( newMap ), gridColor( 50, 80, 50 ), + hiLiteRow( -1 ), hiLiteCol( -1 ) +{ + labelFont = KGlobalSettings::generalFont(); + labelFont.setPointSize( 8 ); + + setFrameStyle( NoFrame ); + setPaletteBackgroundColor( black ); + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + setCellWidth( SECTOR_WIDTH ); + setCellHeight( SECTOR_HEIGHT ); + setNumRows( map->getRows() ); + setNumCols( map->getColumns() ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + connect( map, SIGNAL( update() ), this, SLOT( mapUpdate() ) ); + + QTimer *timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), this, SLOT(squareBlink()) ); + timer->start( 500, false ); + + viewport()->setMouseTracking( true ); + setMouseTracking( true ); + + show(); + + +} + +ConquestMap::~ConquestMap() +{ +} + + +void +ConquestMap::contentsMousePressEvent( QMouseEvent *e ) +{ + int row, col; + + row = rowAt( e->y() ); + col = columnAt( e->x() ); + + if( map->getSector( row, col ).hasPlanet() ) { + emit planetSelected( map->getSector( row, col ).getPlanet() ); + } + +} + +void +ConquestMap::contentsMouseMoveEvent( QMouseEvent *e ) +{ + // highlight the square under the mouse + int row, col; + + row = rowAt( e->y() ); + col = columnAt( e->x() ); + + // Check to make sure the mouse is in a valid grid location + if( (row < 0 || col < 0) || + (row >= BOARD_ROWS || col >= BOARD_COLS) ) { + return; + } + + + if( (hiLiteRow != -1) && (hiLiteCol != -1) ) { + QPainter p( viewport() ); + + p.translate( hiLiteCol * cellWidth(), hiLiteRow * cellHeight() ); + + drawSector( &p, map->getSector(hiLiteRow,hiLiteCol) ); + + hiLiteRow = -1; + hiLiteCol = -1; + + } + + if( map->getSector( row, col ).hasPlanet() ) { + QPainter p( viewport() ); + + p.translate( col * cellWidth(),row * cellHeight() ); + + drawSector( &p, map->getSector(row,col), false, true ); + emit planetHighlighted(map->getSector( row, col ).getPlanet() ); + + hiLiteRow = row; + hiLiteCol = col; + + } + +} + +void +ConquestMap::unselectPlanet() +{ + map->setSelectedSector(); +} + + +void +ConquestMap::paintCell( QPainter *p, int row, int col ) +{ + drawSector( p, map->getSector( row, col ) ); +} + +void +ConquestMap::squareBlink() +{ + static bool blinkState = true; + + int row, col; + if( map->selectedSector( row, col ) ) { + QPainter p( this, true ); + + p.translate( col * cellWidth(), row * cellHeight() ); + + if( blinkState ) { + drawSector( &p, map->getSector(row,col), true ); + } else { + drawSector( &p, map->getSector(row,col), false ); + } + } + + if( blinkState ) + blinkState = false; + else + blinkState = true; +} + + +void +ConquestMap::mapUpdate() +{ + viewport()->repaint(false); +} + + +void +ConquestMap::drawSector( QPainter *p, Sector §or, bool borderStrobe, bool highlight ) +{ + QColor labelColor( white ); + QPoint labelCorner; + + if( sector.hasPlanet() ) { + QPixmap pm; + + // simple (pathetic) way to "randomize" + // the planet graphic + // and also a really dirty hack to make the planet + // name more visible (hard coded pixel offsets) + switch( ((sector.getRow()+sector.getColumn()) % 9) + 1 ) { + case 1 : + pm = QPixmap( IMAGE_PLANET_1 ); + labelCorner = QPoint( 18, 14 ); + break; + case 2 : + pm = QPixmap( IMAGE_PLANET_2 ); + labelCorner = QPoint( 2, 14 ); + break; + case 3 : + pm = QPixmap( IMAGE_PLANET_3 ); + labelCorner = QPoint( 2, 26 ); + break; + case 4 : + pm = QPixmap( IMAGE_PLANET_4 ); + labelCorner = QPoint( 18, 26 ); + break; + case 5 : + pm = QPixmap( IMAGE_PLANET_5 ); + labelCorner = QPoint( 18, 26 ); + break; + case 6 : + pm = QPixmap( IMAGE_PLANET_6 ); + labelCorner = QPoint( 18, 26 ); + break; + case 7 : + pm = QPixmap( IMAGE_PLANET_7 ); + labelCorner = QPoint( 18, 26 ); + break; + case 8 : + pm = QPixmap( IMAGE_PLANET_8 ); + labelCorner = QPoint( 18, 26 ); + break; + case 9 : + pm = QPixmap( IMAGE_PLANET_9 ); + labelCorner = QPoint( 18, 26 ); + break; + } + + QPoint pos; + + pos.setX( ( SECTOR_HEIGHT / 2 ) - ( pm.height() / 2 ) ); + pos.setY( ( SECTOR_WIDTH / 2 ) - ( pm.width() / 2 ) ); + + p->drawPixmap( pos, pm, QRect(0, 0, pm.height(), pm.width() ) ); + + p->setFont( labelFont ); + p->setPen( labelColor ); + + p->drawText( labelCorner, sector.getPlanet()->getName() ); + + if( borderStrobe ) { + QPen gridPen( sector.getPlanet()->getPlayer()->getColor() ); + p->setPen( gridPen ); + } else if( highlight ) { + QPen gridPen( white ); + p->setPen( gridPen ); + } else { + QPen gridPen( gridColor ); + p->setPen( gridPen ); + } + + } else { + p->eraseRect( 0, 0, SECTOR_WIDTH, SECTOR_HEIGHT ); + + QPen gridPen( gridColor ); + + p->setPen( gridPen ); + } + + p->drawRect( 0, 0, SECTOR_HEIGHT, SECTOR_WIDTH ); + +} + + diff --git a/konquest/map_widget.h b/konquest/map_widget.h new file mode 100644 index 00000000..052d6e9d --- /dev/null +++ b/konquest/map_widget.h @@ -0,0 +1,59 @@ +#ifndef _MAP_WIDGET_H +#define _MAP_WIDGET_H + + +#include +#include +#include + +#include + +#include + +#include "gamecore.h" +#include "images.h" + +class ConquestMap : public QGridView +{ + Q_OBJECT + + // Constructors +public: + ConquestMap( Map *newMap, QWidget *parent = 0 ); + virtual ~ConquestMap(); + + // Interface +public: + void unselectPlanet(); + +protected: + virtual void contentsMousePressEvent( QMouseEvent *e ); + virtual void contentsMouseMoveEvent( QMouseEvent *e ); + virtual void paintCell( QPainter *p, int row, int col ); + +private slots: + void mapUpdate(); + void squareBlink(); + +signals: + void planetSelected( Planet * ); + void planetHighlighted( Planet * ); + +private: + const int SECTOR_HEIGHT; + const int SECTOR_WIDTH; + + const int BOARD_HEIGHT; + const int BOARD_WIDTH; + + void drawSector( QPainter *, Sector &, bool borderStrobe = true, bool highlight = false ); + + Map *map; + QColor gridColor; + QFont labelFont; + + int hiLiteRow, hiLiteCol; +}; + + +#endif diff --git a/konquest/minimap.cc b/konquest/minimap.cc new file mode 100644 index 00000000..eee1237b --- /dev/null +++ b/konquest/minimap.cc @@ -0,0 +1,79 @@ +#include +#include + +#include +#include + +#include "minimap.h" +#include "minimap.moc" + +MiniMap::MiniMap( QWidget *parent, const char *name ) + : QGridView( parent, name ), + SECTOR_HEIGHT( 12 ), SECTOR_WIDTH( 12 ), + BOARD_HEIGHT( 10 * SECTOR_HEIGHT ), + BOARD_WIDTH( 10 * SECTOR_WIDTH ), + map( 0 ) +{ + setFrameStyle( NoFrame ); + setPaletteBackgroundColor( black ); + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + setCellWidth( SECTOR_WIDTH ); + setCellHeight( SECTOR_HEIGHT ); + setNumRows( 10 ); + setNumCols( 10 ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); +} + +void +MiniMap::setMap(Map *newMap) +{ + map = newMap; + BOARD_HEIGHT = map->getRows() * SECTOR_HEIGHT; + BOARD_WIDTH = map->getColumns() * SECTOR_WIDTH; + setNumRows( map->getRows() ); + setNumCols( map->getColumns() ); + + setMinimumSize( BOARD_HEIGHT, BOARD_WIDTH ); + setMaximumSize( BOARD_HEIGHT, BOARD_WIDTH ); + + connect( map, SIGNAL( update() ), this, SLOT( mapUpdate() ) ); +} + +MiniMap::~MiniMap() +{ +} + + +void +MiniMap::paintCell( QPainter *p, int row, int col ) +{ + drawSector( p, map->getSector( row, col ) ); +} + +void +MiniMap::mapUpdate() +{ + updateContents(); +} + + +void +MiniMap::drawSector( QPainter *p, Sector §or ) +{ + QRect r( 0, 0, SECTOR_WIDTH, SECTOR_HEIGHT ); + + p->setPen( black ); + p->setBrush( black ); + p->drawRect( r ); + + if( sector.hasPlanet() ) { + p->setPen( sector.getPlanet()->getPlayer()->getColor() ); + p->setBrush( sector.getPlanet()->getPlayer()->getColor() ); + + p->drawPie( r, 0, (360 * 16)-1 ); + } +} + diff --git a/konquest/minimap.h b/konquest/minimap.h new file mode 100644 index 00000000..9d205abb --- /dev/null +++ b/konquest/minimap.h @@ -0,0 +1,44 @@ +#ifndef _MINIMAP_H +#define _MINIMAP_H + +#include +#include +#include +#include +#include + +#include "gamecore.h" +#include "images.h" + + +class MiniMap : public QGridView +{ + Q_OBJECT + + // Constructors +public: + MiniMap( QWidget *parent = 0, const char* name = 0 ); + virtual ~MiniMap(); + + void setMap( Map *newMap ); + +protected: + void paintCell( QPainter *p, int row, int col ); + +private slots: + void mapUpdate(); + +private: + int SECTOR_HEIGHT; + int SECTOR_WIDTH; + + int BOARD_HEIGHT; + int BOARD_WIDTH; + + void drawSector( QPainter *, Sector & ); + + Map *map; +}; + + +#endif // _MINIMAP_H_ diff --git a/konquest/newGameDlg_ui.ui b/konquest/newGameDlg_ui.ui new file mode 100644 index 00000000..2768e5f8 --- /dev/null +++ b/konquest/newGameDlg_ui.ui @@ -0,0 +1,440 @@ + +NewGameDlgUI + + + NewGameDlgUI + + + + 0 + 0 + 661 + 461 + + + + + unnamed + + + + layout9 + + + + unnamed + + + + layout4 + + + + unnamed + + + + labelPlayers + + + + + + sliderPlayers + + + + + sliderPlayers + + + 2 + + + 9 + + + Horizontal + + + + + labelPlayerList + + + Player list: + + + listPlayers + + + + + + Name + + + true + + + true + + + + + New Column + + + true + + + true + + + + listPlayers + + + + + layout3 + + + + unnamed + + + + labelNewPlayer + + + &Human player: + + + newPlayer + + + + + newPlayer + + + + 7 + 0 + 1 + 0 + + + + + + + + layout2 + + + + unnamed + + + + spacer2 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + addPlayer + + + &Add Human Player + + + + + spacer1 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + + + + + spacer5 + + + Horizontal + + + Fixed + + + + 20 + 20 + + + + + + layout8 + + + + unnamed + + + + labelPlanets + + + + + + sliderPlanets + + + + + sliderPlanets + + + 1 + + + 35 + + + 1 + + + Horizontal + + + + + labelMap + + + Preview map: + + + + + layout7 + + + + unnamed + + + + spacer7 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + map + + + + + spacer7_2 + + + Horizontal + + + Expanding + + + + 0 + 20 + + + + + + + + spacer6 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + layout5 + + + + unnamed + + + + spacer3 + + + Horizontal + + + Expanding + + + + 31 + 20 + + + + + + rejectMap + + + Reject &Map + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 51 + 20 + + + + + + + + + + + + line1 + + + HLine + + + Sunken + + + Horizontal + + + + + labelTurns + + + + + + sliderTurns + + + + + sliderTurns + + + 5 + + + 40 + + + 5 + + + Horizontal + + + + + + + MiniMap +
    minimap.h
    + + 250 + 250 + + 0 + + 0 + 0 + 0 + 0 + + image0 +
    +
    + + + 89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154789cad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a19017a725d8c60000000049454e44ae426082 + + + + + minimap.h + +
    diff --git a/konquest/newgamedlg.cc b/konquest/newgamedlg.cc new file mode 100644 index 00000000..c1beae4f --- /dev/null +++ b/konquest/newgamedlg.cc @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "newgamedlg.h" +#include "newgamedlg.moc" + +#include "newGameDlg_ui.h" + +/************************************************************************* + New Game Dialog Members + ************************************************************************/ + +NewGameDlg::NewGameDlg( QWidget *parent, Map *pmap, PlayerList *players, + Player *neutralPlayer, PlanetList *planets ) + : KDialogBase( parent, "new_game_dialog", true, i18n("Start New Game"), + KDialogBase::Ok|KDialogBase::Default|KDialogBase::Cancel, KDialogBase::NoDefault, true ), + plrList(players), plnetList(planets), neutral(neutralPlayer), + map(pmap) +{ + w = new NewGameDlgUI(this); + w->map->setMap(map); + w->listPlayers->header()->hide(); +// w->listPlayers->setMinimumSize( 100, 150 ); + w->listPlayers->setSortColumn(-1); + w->listPlayers->setHScrollBarMode(QScrollView::AlwaysOff); + w->sliderPlayers->setMinimumWidth(270); + w->sliderPlanets->setMinimumWidth(270); + + w->newPlayer->setMaxLength( 8 ); + + connect(w->sliderPlayers, SIGNAL(valueChanged(int)), this, SLOT(slotPlayerCount(int))); + connect(w->sliderPlanets, SIGNAL(valueChanged(int)), this, SLOT(slotNewMap())); + connect(w->sliderTurns, SIGNAL(valueChanged(int)), this, SLOT(slotTurns())); + connect(w->rejectMap, SIGNAL(clicked()), this, SLOT(slotNewMap())); + connect(w->newPlayer, SIGNAL(textChanged(const QString &)), this, SLOT(slotNewPlayer())); + connect(w->newPlayer, SIGNAL(returnPressed()), this, SLOT(slotAddPlayer())); + connect(w->addPlayer, SIGNAL(clicked()), this, SLOT(slotAddPlayer())); + + init(); + + setMainWidget(w); +} + +void +NewGameDlg::slotDefault() +{ + w->sliderPlayers->setValue(2); + w->sliderPlanets->setValue(3); + w->sliderTurns->setValue(15); + + w->listPlayers->clear(); + + setPlayerCount(2); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::init() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + int nrOfPlayers = config->readNumEntry("NrOfPlayers"); + if (nrOfPlayers < 2) + nrOfPlayers = 2; + if (nrOfPlayers > MAX_PLAYERS) + nrOfPlayers = MAX_PLAYERS; + + int nrOfPlanets = config->readNumEntry("NrOfPlanets", 3); + int nrOfTurns = config->readNumEntry("NrOfTurns", 15); + + w->sliderPlayers->setValue(nrOfPlayers); + w->sliderPlanets->setValue(nrOfPlanets); + w->sliderTurns->setValue(nrOfTurns); + setPlayerCount(nrOfPlayers); + slotNewPlayer(); + + // Restore player names + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling(), plrNum++ ) + { + QString key = QString("Player_%1").arg(plrNum); + + QString playerName = config->readEntry(key); + if (playerName.isEmpty()) + continue; + + item->setText(2, "H"); // Human + item->setText(1, i18n("Human Player")); + item->setText(0, playerName); + } + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::slotNewPlayer() +{ + w->addPlayer->setEnabled(!w->newPlayer->text().isEmpty()); +} + +void +NewGameDlg::slotAddPlayer() +{ + QString playerName = w->newPlayer->text(); + if (playerName.isEmpty()) + return; + + QListViewItem *item; + do + { + item = w->listPlayers->firstChild(); + while( item ) + { + if (item->text(2) == "A") + break; + + item = item->nextSibling(); + } + if (!item) + { + int nrPlayers = w->listPlayers->childCount(); + if (nrPlayers >= MAX_PLAYERS) + return; // Too bad + nrPlayers++; + w->sliderPlayers->setValue(nrPlayers); + setPlayerCount(nrPlayers); + } + } + while(!item); + + item->setText(2, "H"); // Human + item->setText(1, i18n("Human Player")); + item->setText(0, playerName); + + w->newPlayer->setText(QString::null); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::setPlayerCount(int playerCount) +{ + QColor PlayerColors[MAX_PLAYERS] = { QColor( 130, 130, 255 ), yellow, red, green, + white, cyan, magenta, QColor( 235, 153, 46 ), + QColor( 106, 157, 104 ), QColor( 131, 153, 128) }; + + int i = 0; + QListViewItem *lastItem = 0; + QListViewItem *item = 0; + QListViewItem *nextItem = w->listPlayers->firstChild(); + while( (item = nextItem) ) + { + nextItem = item->nextSibling(); + if (i >= playerCount) + { + delete item; + } + else + { + lastItem = item; + } + i++; + } + + while(w->listPlayers->childCount() < playerCount) + { + QString playerName = i18n("Generated AI player name", "Comp%1").arg(i+1); + QPixmap pm(16,16); + QColor color(PlayerColors[i]); + pm.fill(color); + QListViewItem *item = new QListViewItem(w->listPlayers, lastItem, playerName, i18n("Computer Player"), "A", color.name()); + item->setPixmap(0, pm); + lastItem = item; + i++; + } +} + +void +NewGameDlg::slotPlayerCount(int playerCount) +{ + if (w->listPlayers->childCount() == playerCount) + return; + + setPlayerCount(playerCount); + + updateMiniMap(); + updateLabels(); +} + +void +NewGameDlg::slotTurns() +{ + updateLabels(); +} + +void +NewGameDlg::slotNewMap() +{ + updateMiniMap(); + updateLabels(); +} + +int +NewGameDlg::turns() +{ + return w->sliderTurns->value(); +} + +void +NewGameDlg::updateLabels() +{ + w->labelPlayers->setText(i18n("Number of &players: %1").arg(w->sliderPlayers->value())); + w->labelPlanets->setText(i18n("Number of neutral p&lanets: %1").arg(w->sliderPlanets->value())); + w->labelTurns->setText(i18n("Number of &turns: %1").arg(w->sliderTurns->value())); +} + +void +NewGameDlg::slotOk() +{ + bool hasHumans = false; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + bool ai = (item->text(2) == "A"); + if (!ai) + hasHumans = true; + } + + if (!hasHumans) + { + KMessageBox::information(this, i18n("The game is much more fun when you add a human player!")); + w->newPlayer->setFocus(); + return; + } + KDialogBase::slotOk(); +} + +void +NewGameDlg::save() +{ + KConfig *config = kapp->config(); + config->setGroup("Game"); + + config->writeEntry("NrOfPlayers", w->sliderPlayers->value()); + config->writeEntry("NrOfPlanets", w->sliderPlanets->value()); + config->writeEntry("NrOfTurns", w->sliderTurns->value()); + + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + QString key = QString("Player_%1").arg(plrNum); + QString playerName = item->text(0); + bool ai = (item->text(2) == "A"); + if (ai) + { + if (config->hasKey(key)) + config->deleteEntry(key); + } + else + { + config->writeEntry(key, playerName); + } + plrNum++; + } + config->sync(); +} + +void +NewGameDlg::updateMiniMap() +{ + // Clear map,, player and planet lists + map->clearMap(); + + Planet *planet; + planet = plnetList->first(); + for( planet = plnetList->take(); planet != 0; planet = plnetList->take() ) { + delete planet; + } + + Player *player; + player = plrList->first(); + for( player = plrList->take(); player != 0; player = plrList->take() ) { + delete player; + } + + // Make player list + // Does the name already exist in the list + int plrNum = 0; + for( QListViewItem *item = w->listPlayers->firstChild(); + item; item = item->nextSibling() ) + { + QString playerName = item->text(0); + bool ai = (item->text(2) == "A"); + QColor color(item->text(3)); + plrList->append( Player::createPlayer( playerName, color, plrNum, ai )); + plrNum++; + } + + // make the planets + map->populateMap( *plrList, neutral, + w->sliderPlanets->value(), + *plnetList ); +} + diff --git a/konquest/newgamedlg.h b/konquest/newgamedlg.h new file mode 100644 index 00000000..ed2d48d6 --- /dev/null +++ b/konquest/newgamedlg.h @@ -0,0 +1,51 @@ +#ifndef _NEWGAMEDLG_H_ +#define _NEWGAMEDLG_H_ + +#include + +#include "gamecore.h" +#include "minimap.h" + +class NewGameDlgUI; + +/************************************************************************* + New Game Dialog + ************************************************************************/ + +class NewGameDlg : public KDialogBase +{ + Q_OBJECT + +public: + NewGameDlg( QWidget *parent, Map *map, PlayerList *playerList, + Player *neutralPlayer, PlanetList *planetList ); + + int turns( void ); + + void save(); + +protected slots: + void slotPlayerCount(int playerCount); + void slotNewMap(); + void slotTurns(); + void slotNewPlayer(); + void slotAddPlayer(); + void slotDefault(); + void slotOk(); + +private: + void init(); + void updateMiniMap(); + void updateLabels(); + void setPlayerCount(int playerCount); + +private: + PlayerList *plrList; + PlanetList *plnetList; + Player *neutral; + Map *map; + + NewGameDlgUI *w; +}; + +#endif diff --git a/konquest/pics/Makefile.am b/konquest/pics/Makefile.am new file mode 100644 index 00000000..6c702a0e --- /dev/null +++ b/konquest/pics/Makefile.am @@ -0,0 +1,7 @@ + +# add here all files +pics_DATA = konquest-splash.png planet1.xpm planet2.xpm planet3.xpm planet4.xpm \ + planet5.xpm planet6.xpm planet7.xpm planet8.xpm planet9.xpm ruler.xpm + +picsdir = $(kde_datadir)/konquest/pics + diff --git a/konquest/pics/konquest-splash.png b/konquest/pics/konquest-splash.png new file mode 100644 index 0000000000000000000000000000000000000000..6640b1a441bf795e3a7ee8f8332e0bffae1d1d35 GIT binary patch literal 207292 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%0DD0s6|@0h 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 c #425E54", +" , c #7E969C", +" < c #3A524C", +" 1 c #2E5254", +" 2 c #9A927C", +" 3 c #324E44", +" 4 c #BEDAD4", +" 5 c #324A44", +" 6 c #264A4C", +" 7 c #324644", +" 8 c #7A8E84", +" 9 c #C2D2C4", +" 0 c #1E3E44", +" q c #8A866C", +" w c #7E8274", +" e c #BACABC", +" r c #AECAC4", +" t c #AEC6C4", +" y c #BAC2BC", +" u c #16363C", +" i c #5E827C", +" p c #222E34", +" a c #62766C", +" s c #0E2A34", +" d c #6E7264", +" f c #426E74", +" g c #121E24", +" h c #121A24", +" j c #52665C", +" k c #466664", +" l c #0A161C", +" z c #0A121C", +" x c #9AAA9C", +" c c #0A0E1C", +" v c #9AA69C", +" b c #92A294", +" n c #9E9E8C", +" m c #42564C", +" M c #365654", +" N c #2A565C", +" B c #365254", +" V c #729E9C", +" C c #8A968C", +" Z c #2E524C", +" A c #DEDAC4", +" S c #D2DACC", +" D c #829284", +" F c #1A464C", +" G c #32423C", +" H c #BECECC", +" J c #868A74", +" K c #2A3E34", +" L c #C2CABC", +" P c #B6CAC4", +" I c #2A3A34", +" U c #C2C6BC", +" Y c #1E3A3C", +" T c #1E363C", +" R c #BAC2B4", +" E c #22322C", +" W c #BABEB4", +" Q c #A2BEC4", +" ! c #162E34", +" ~ c #467A84", +" ^ c #B2B6AC", +" / c #3E767C", +" ( c #627264", +" ) c #1A2224", +" _ c #0E222C", +" ` c #92BAB4", +" ' c #1A1E24", +" ] c #0E1E2C", +" [ c #5A6A5C", +" { c #12161C", +" } c #12121C", +" | c #061224", +". c #526254", +".. c #DEEEE4", +".X c #3A6264", +".o c #0A0E14", +".O c #82A6A4", +".+ c #0A0A14", +".@ c #0A0614", +".# c #325A5C", +".$ c #32565C", +".% c #9E9A84", +".& c #929A8C", +".* c #869A94", +".= c #3E4A54", +".- c #2A5254", +".; c #3A4A3C", +".: c #D2D6C4", +".> c #2E4A44", +"., c #2E4644", +".< c #2E4244", +".1 c #22424C", +".2 c #26423C", +".3 c #BECEC4", +".4 c #1A4244", +".5 c #1A3E44", +".6 c #263A3C", +".7 c #6E867C", +".8 c #56828C", +".9 c #1E2E34", +".0 c #5A727C", +".q c #162A2C", +".w c #46767C", +".e c #566664", +".r c #0E1624", +".t c #AAAE9C", +".y c #566264", +".u c #0E1224", +".i c #426664", +".p c #2E6264", +".a c #9A9E8C", +".s c #325654", +".d c #D6DED4", +".f c #869E8C", +".g c #325254", +".h c #3E4E4C", +".j c #26465C", +".k c #7A8E94", +".l c #2A4A4C", +".z c #2A464C", +".x c #669294", +".c c #728E8C", +".v c #D2D2BC", +".b c #224644", +".n c #D2CEBC", +".m c #224244", +".M c #16424C", +".N c #5E828C", +".B c #BEC6BC", +".V c #1A3A3C", +".C c #6E8274", +".Z c #1A363C", +".A c #0E3644", +".S c #1A323C", +".D c #C2C2AC", +".F c #123234", +".G c #122E34", +".H c #66766C", +".J c #122A34", +".K c #5E7664", +".L c #A2B6B4", +".P c #5E7264", +".I c #467274", +".U c #0A222C", +".Y c #6A6A5C", +".T c #0A1E2C", +".R c #161A24", +".E c #A6AEA4", +".W c #3E6A6C", +".Q c #4A6664", +".! c #0E161C", +".~ c #0E121C", +".^ c #0E0E1C", +"./ c #AAA694", +".( c #9EA69C", +".) c #0E0A1C", +"._ c #E6EADC", +".` c #7EA6A4", +".' c #96A294", +".] c #42565C", +".[ c #7EA2A4", +".{ c #060614", +".} c #3A5E54", +".| c #D2EADC", +"X c #2E5A5C", +"X. c #3A5654", +"XX c #2E565C", +"Xo c #32524C", +"XO c #C2DACC", +"X+ c #CED6C4", +"X@ c #1E424C", +"X# c #728A84", +"X$ c #668A8C", +"X% c #2E3A34", +"X& c #B2C6BC", +"X* c #1A3234", +"X= c #6E7A6C", +"X- c #6E766C", +"X; c #B6BAAC", +"X: c #AABAB4", +"X> c #9EBABC", +/* pixels */ +" ).R {.~.~.^.^.^.^.).o.+.+ c ; ;.o ;.~ +", +" '.~.^.^.+.o c.+.+ %.r c.).+.+.+.).+ ; }", +" +.~ c.^.^ ..0 *X> Q X.[.N B ..+.+.+.o }", +" +.~.^.^.e HX+ yX&.L.c.W = f ~.j c.+.).~", +" }.o.u ,.B.E x C.tX& i.XX N.I.8 f z ;.~", +" {.^.k W./ C bX: eX+ H t.` XX>.x.p.A.+ }", +" }.] U.(.t L.n._.:.B.3.d.. r.x.4.A.M.T.~", +" +X: 9X;.a n & J q.D A S.3.. 4 V / F.5.^", +".=.v AX; w ( o aX#.B P.f C R S.| ` f.p g", +".y n & : 8.& v ^.a w DX=.H D.' vXO.O.w.S", +".yX- @. 2.%.% @ I E E.q K.K.C.>.C.*X$ ", +".y [.;X%X% d m.q g.r.! l ] I o.}.2 ZXo h", +".h. G.9.q.Y < _.q # # # O.G 7.7.i 6.5.~", +" p > K.9X* w., ! O O.U.U.FX* T >.W.G ;", +" { - 5.6 T.H TX* !.S s.F u u.V 0.bX | ;", +" } p j.,.h [.ZX* T.V.S.Z.m 3.#.l.V.U.+ c", +" $.o.h.P -.H < 3 5 M MX. -.#XXX@.U.{.@ ;", +" h.^.+.< k.Q M.g.sX..gXX 1 6 Y _.{.{.@.^", +" $ ;.+.@ g.1XX.$.#.-.z.b.4 s |.@.{.{.{ ;", +" + c.+.@.@.@ c ].J.J.U %.+.{.{.{.@.+.o.^" +}; diff --git a/konquest/pics/planet2.xpm b/konquest/pics/planet2.xpm new file mode 100644 index 00000000..f77d9ddb --- /dev/null +++ b/konquest/pics/planet2.xpm @@ -0,0 +1,190 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 163 2", +/* colors */ +" c #3E161C", +" . c #CEAE9C", +" X c #7A5A64", +" o c #EAAE7C", +" O c #7A5664", +" + c #361214", +" @ c #360E14", +" # c #F6EEE4", +" $ c #72525C", +" % c #2E0E0C", +" & c #724E5C", +" * c #A292AC", +" = c #EEE6DC", +" - c #FEEEC4", +" ; c #F2EACC", +" : c #AA968C", +" > c #C2927C", +" , c #E6DED4", +" < c #FADEAC", +" 1 c #A28684", +" 2 c #DECECC", +" 3 c #A27E84", +" 4 c #F2CEA4", +" 5 c #8E7684", +" 6 c #927674", +" 7 c #927274", +" 8 c #DABEB4", +" 9 c #CEBEBC", +" 0 c #766694", +" q c #DEC6A4", +" w c #D2C2AC", +" e c #A26E5C", +" r c #D2BAAC", +" t c #DEB6A4", +" y c #8A6A6C", +" u c #7E5E74", +" i c #CAB2A4", +" p c #FAFAF4", +" a c #DAB28C", +" s c #765A6C", +" d c #B6A6A4", +" f c #C2A29C", +" g c #360E0C", +" h c #6E4E64", +" j c #F6EEDC", +" k c #664A5C", +" l c #D69A74", +" z c #66425C", +" x c #B29A8C", +" c c #6A4A4C", +" v c #5E4654", +" b c #A69294", +" n c #5E3A54", +" m c #9E828C", +" M c #56324C", +" N c #927E94", +" B c #5A3A3C", +" V c #967E84", +" C c #DECAC4", +" Z c #D2CACC", +" A c #4E2A44", +" S c #8A768C", +" D c #523234", +" F c #522A34", +" G c #4A222C", +" H c #CEBAB4", +" J c #DEBE9C", +" K c #866674", +" L c #96625C", +" P c #7E626C", +" I c #3A1A1C", +" U c #CAB29C", +" Y c #CAAE9C", +" T c #3A121C", +" R c #320E14", +" E c #CEA68C", +" W c #B6A29C", +" Q c #F2EAE4", +" ! c #AA9AA4", +" ~ c #D2967C", +" ^ c #664A54", +" / c #AE9694", +" ( c #664654", +" ) c #663E54", +" _ c #EEE2CC", +" ` c #9E8284", +" ' c #F6D6AC", +" ] c #DECEBC", +" [ c #967A7C", +" { c #D2C6C4", +" } c #C6BACC", +" | c #A67264", +". c #8E7274", +".. c #CABEBC", +".X c #8E6E74", +".o c #BEB6C4", +".O c #4A2224", +".+ c #4A1E24", +".@ c #86666C", +".# c #C2B2B4", +".$ c #FEFEFC", +".% c #86626C", +".& c #7A5E74", +".* c #C6B2A4", +".= c #7E5E64", +".- c #3A1214", +".; c #FAF6E4", +".: c #765A5C", +".> c #BEA69C", +"., c #825254", +".< c #BE9E9C", +".1 c #320E0C", +".2 c #6A4E64", +".3 c #320A0C", +".4 c #6A4A64", +".5 c #E69E74", +".6 c #6A4664", +".7 c #B69E94", +".8 c #B69A94", +".9 c #6E4A54", +".0 c #E6DEE4", +".q c #664A4C", +".w c #EAE2D4", +".e c #EEDEC4", +".r c #B28E7C", +".t c #A68A84", +".y c #A68684", +".u c #E2D2CC", +".i c #D6CED4", +".p c #9E7A7C", +".a c #EACAAC", +".s c #DECAB4", +".d c #967674", +".f c #D2C2BC", +".g c #D2BEBC", +".h c #CABAB4", +".j c #8E666C", +".k c #CEB6A4", +".l c #E6B294", +".z c #866264", +".x c #C2AAAC", +".c c #B6AAB4", +".v c #DEAE8C", +".b c #F6F2EC", +".n c #725664", +".m c #725264", +".M c #6A465C", +".N c #AA9294", +".B c #624254", +".V c #BA927C", +".C c #664244", +".Z c #AE8E84", +".A c #968694", +".S c #5A364C", +".D c #E2D6C4", +".F c #D6CECC", +".G c #927A7C", +".H c #DAC6BC", +".J c #4A263C", +".K c #FACE8C", +".L c #4E222C", +".P c #D6C2A4", +".I c #BEAEB4", +/* pixels */ +".1.3.1.1.1.1.1.1.1.1.1.1.1.1 g g g g g @", +".3.1.3.3.3.1.1.q.j.d 7.C.-.3.3.3.3.3.3.3", +".3 % % %.3.: Z ; t.l 4.a Y ( R R @ @ @ @", +" %.3.3 @.Z - <.K l > E.j.& u.= @ R.3 @.3", +" % %.1 ` ' a | L.V.v ~ (.m.2.k c R.3 @ @", +".3.3.S r f Y.* / q J X [ :.h m K.L R.3 R", +" % % N :.#.u _ w = ].7.N.m.@.t 1.9 R.3 @", +".3 I.o S 6 x.P _ _ U.7 1.B.B.@.<.d T R.1", +".3.C.c.I b V a 4 Y.z., y X.S z.@.p.O.3 @", +" % v *.# 5 H.5 o e.9.S A n h ( z.m F R R", +" % ^ 0.G s.B.N '.r O M.J A.n.G ).4 F.3 R", +" % B } N...h.@.. #.w 9 $.= X P k & G R g", +" % I.0 !.# / $ i W.$.b. .< Y. b.6 +.3 @", +" g.1.A Z.x.f d , p.e.>.X 1.F.D r ) @ R.1", +".3.3 D.w {.g #.D j.s ..8.y & 3 O.+ R.3 @", +".1 R.3 P.# 2.f = Q.w =.H Y 8.<.M.3.3 @.3", +".3.1 R @ m.;.$.$.$.$.$ p.; C.% + @.3 @ @", +".1.3.3 @ @ $.i.$.$.$.$ p.u O @.3.3 @.3.3", +".3.1.1.1.1.3 @ ^ V b V.9 R @ @.3 @ @ @", +" %.3.3.3.3 R.1.1 %.3.3 % %.3.3.3 @.3.3 g" +}; diff --git a/konquest/pics/planet3.xpm b/konquest/pics/planet3.xpm new file mode 100644 index 00000000..6375bfd1 --- /dev/null +++ b/konquest/pics/planet3.xpm @@ -0,0 +1,133 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 106 2", +/* colors */ +" c #BA6A2C", +" . c #EAB27C", +" X c #8E623C", +" o c #76624C", +" O c #825644", +" + c #120604", +" @ c #D29664", +" # c #5E3A34", +" $ c #DA8A44", +" % c #CE8A4C", +" & c #B6865C", +" * c #9E866C", +" = c #D2823C", +" - c #BA824C", +" ; c #CA7A34", +" : c #BE7A3C", +" > c #CA7634", +" , c #BE763C", +" < c #B27644", +" 1 c #3A2A24", +" 2 c #C2722C", +" 3 c #926E4C", +" 4 c #AE6A2C", +" 5 c #BA6624", +" 6 c #7E664C", +" 7 c #2A1A14", +" 8 c #B2661C", +" 9 c #965A3C", +" 0 c #8E6234", +" q c #7E564C", +" w c #82623C", +" e c #EEA664", +" r c #E69A5C", +" t c #DA9A64", +" y c #CE9A6C", +" u c #060204", +" i c #DE9254", +" p c #C68E64", +" a c #CA8A54", +" s c #763E1C", +" d c #B28A64", +" f c #4A3E2C", +" g c #4A3A2C", +" h c #C67E3C", +" j c #423224", +" k c #BA7644", +" l c #9A6E4C", +" z c #3A221C", +" x c #B66E2C", +" c c #EAA66C", +" v c #965634", +" b c #765A3C", +" n c #6A5A44", +" m c #CAA274", +" M c #E29E64", +" N c #924E1C", +" B c #C29A6C", +" V c #CE9664", +" C c #DE8E4C", +" Z c #CE823C", +" A c #B67E4C", +" S c #9E765C", +" D c #8A725C", +" F c #BE722C", +" G c #9A6E44", +" H c #F2AE6C", +" J c #32120C", +" K c #1E160C", +" L c #1E120C", +" P c #1E0E0C", +" I c #DEA26C", +" U c #725644", +" Y c #C69E7C", +" T c #160A04", +" R c #E2965C", +" E c #BE9674", +" W c #6A463C", +" Q c #020204", +" ! c #C29264", +" ~ c #663E24", +" ^ c #7E3A14", +" / c #AE8664", +" ( c #D6863C", +" ) c #CA8244", +" _ c #6A3214", +" ` c #C27E3C", +" ' c #4A321C", +" ] c #C27A3C", +" [ c #92765C", +" { c #BA7634", +" } c #C6722C", +" | c #AE723C", +". c #BE6E24", +".. c #8E6A44", +".X c #EEB274", +".o c #E6A66C", +".O c #DAA674", +".+ c #120E0C", +".@ c #C69A74", +".# c #0A0204", +".$ c #62422C", +".% c #D28244", +".& c #C6824C", +".* c #CA7E3C", +".= c #AA7644", +".- c #86725C", +/* pixels */ +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q 7 W O q # T Q Q Q Q Q", +" Q Q Q Q Q Q Q P X y @ @ @ t k s.# Q Q Q", +" Q Q Q Q Q Q L S p t $ ) ) F 8 N + Q Q", +" Q Q Q Q Q Q U Y.O ! % ` ]. . ) 5 ^ Q Q", +" Q Q Q Q Q L *.@ ! A : { , 2 > Z ; 5 J Q", +" Q Q Q Q Q 1 * d & - : ].* Z = } ; > _ Q", +" Q Q Q Q Q z [ l G A.& ` h R $ >.% R v Q", +" Q Q Q Q Q 7 6.. 0 | | 4 4 % ( = C H 9 Q", +" Q Q Q Q Q +.$ w X.= < x ,.* = $ r i ~ Q", +" Q Q Q Q Q Q z b 3 & & !.o c e M t.& + Q", +" Q Q Q Q Q Q u g D / V I ..X c I a ' Q Q", +" Q Q Q Q Q Q Q.# g.- / E Y m B d j Q Q Q", +" Q Q Q Q Q Q Q Q Q K g n o n f.+ Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q", +" Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q" +}; diff --git a/konquest/pics/planet4.xpm b/konquest/pics/planet4.xpm new file mode 100644 index 00000000..5d34f2df --- /dev/null +++ b/konquest/pics/planet4.xpm @@ -0,0 +1,122 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 95 2", +/* colors */ +" c #2A221C", +" . c #7E5E54", +" X c #221A14", +" o c #765A4C", +" O c #1A120C", +" + c #6E5644", +" @ c #6E5244", +" # c #E2AA4C", +" $ c #D6A654", +" % c #664E3C", +" & c #D6A254", +" * c #CE9E4C", +" = c #BE9264", +" - c #CE9A4C", +" ; c #B68E5C", +" : c #4A3A34", +" > c #AA8264", +" , c #42362C", +" < c #A27A5C", +" 1 c #9A7654", +" 2 c #3A2A24", +" 3 c #32261C", +" 4 c #866A54", +" 5 c #866654", +" 6 c #2A2214", +" 7 c #8A6A44", +" 8 c #8A6644", +" 9 c #7E624C", +" 0 c #F2B654", +" q c #765A44", +" w c #0E0A0C", +" e c #624E44", +" r c #060604", +" t c #C69A64", +" y c #060204", +" u c #D6A24C", +" i c #5A463C", +" p c #CA9A54", +" a c #BA8E44", +" s c #A27E54", +" d c #8E6E54", +" f c #866A4C", +" g c #261E1C", +" h c #FABE54", +" j c #7E6244", +" k c #725A4C", +" l c #16120C", +" z c #6A5644", +" x c #DEA64C", +" c c #624E3C", +" v c #D2A254", +" b c #624A3C", +" n c #BE9254", +" m c #B2865C", +" M c #B68A4C", +" N c #AA8254", +" B c #A27A4C", +" V c #362A24", +" C c #8A6E5C", +" Z c #2E261C", +" A c #261E14", +" S c #866644", +" D c #7A624C", +" F c #7A5E4C", +" G c #725644", +" H c #E6AE4C", +" J c #D69E64", +" K c #DAAA54", +" L c #DAA654", +" P c #020204", +" I c #D29E4C", +" U c #C69654", +" Y c #4E3E34", +" T c #B28654", +" R c #463A2C", +" E c #A6825C", +" W c #9E7A54", +" Q c #3E2E24", +" ! c #8A6A54", +" ~ c #8E6A44", +" ^ c #F6BA54", +" / c #1A1614", +" ( c #120E0C", +" ) c #665244", +" _ c #D6A25C", +" ` c #0A0A04", +" ' c #0A0604", +" ] c #5E4A3C", +" [ c #564234", +" { c #B68A64", +" } c #C6924C", +" | c #BA8E54", +". c #BE8E44", +".. c #927254", +".X c #322624", +/* pixels */ +" P P y P P P P P P P P P P P P P P y ' P", +" P y P y y y y y y y y y y y y y 3 c k l", +" P r r r r P r r r r r P r r / b 4 . ] r", +" P P P P P r P P P w ( ` P / . F 5 9 3 y", +" P P P y P r r l R q 9 @ 3 l Q A 9 [ r P", +" P y y r y y l ) 1 N T B j 2 y , o ( P r", +" P r P r r r e < | U } M N S F X y P y", +" P P P P P V W n _ u * } - ~ G 3 r r y r", +" P P y y y ) ; $ K L I #. j b P P P P P", +" P r P r r.. p & x u 0 u 8 < i r y r y r", +" P r P P r 1 p v # h # 7 T m : r P r P y", +" P P P P O.. | L ^ u 7 { U > 3 P P P y r", +" P y r ( + 5 p H a f | J = ! ` y y r P P", +" P r r ] Y Z M f ! | J t E.X y r r P P r", +" P y , F / w [ d { t = s , r P P P y P y", +" P X D % : k c Q D C z g r P y P y r P r", +" r [ d ! 9 , r P P y r y y P r r P r P P", +" X z D ] X y r y y r y r r r r P P P P r", +" / : 6 r P P P r P P P P P P y y y P P P", +" P P P P y y r P P r y P y P r r P r P r" +}; diff --git a/konquest/pics/planet5.xpm b/konquest/pics/planet5.xpm new file mode 100644 index 00000000..339e086e --- /dev/null +++ b/konquest/pics/planet5.xpm @@ -0,0 +1,143 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 116 2", +/* colors */ +" c #321624", +" . c #D2AE8C", +" X c #72625C", +" o c #D2AA8C", +" O c #2A121C", +" + c #2A0E1C", +" @ c #6A5A54", +" # c #CAA284", +" $ c #220A14", +" % c #6A5654", +" & c #220614", +" * c #62524C", +" = c #1A020C", +" - c #120204", +" ; c #BA9274", +" : c #FAD2AC", +" > c #523A3C", +" , c #B2826C", +" < c #927E74", +" 1 c #F2C6A4", +" 2 c #DEC6A4", +" 3 c #EABE9C", +" 4 c #42222C", +" 5 c #E2BA94", +" 6 c #E2B694", +" 7 c #8E6A5C", +" 8 c #CEB694", +" 9 c #DAB28C", +" 0 c #32161C", +" q c #725E54", +" w c #C6A68C", +" e c #725A54", +" r c #725654", +" t c #22060C", +" y c #22020C", +" u c #BE9A84", +" i c #6A4E4C", +" p c #1A0204", +" a c #B6927C", +" s c #F6DAB4", +" d c #FACEA4", +" f c #523634", +" g c #FACAA4", +" h c #462E3C", +" j c #F2C69C", +" k c #9E7A64", +" l c #F2C29C", +" z c #4A262C", +" x c #966E5C", +" c c #DEBA9C", +" v c #8E6A54", +" b c #D6B294", +" n c #82665C", +" m c #CEAA8C", +" M c #6E5E5C", +" N c #C6A684", +" B c #C69E84", +" V c #16020C", +" C c #FEE2B4", +" Z c #B69274", +" A c #FEDEB4", +" S c #564644", +" D c #B68E74", +" F c #AE866C", +" G c #EAD2B4", +" H c #AE826C", +" J c #4E363C", +" K c #4E323C", +" L c #EECAA4", +" P c #927264", +" I c #765E5C", +" U c #2E0E1C", +" Y c #765A5C", +" T c #C2A68C", +" R c #260A14", +" E c #6E5654", +" W c #260614", +" Q c #C69E7C", +" ! c #1E020C", +" ~ c #160204", +" ^ c #A69284", +" / c #5E4244", +" ( c #FED6AC", +" ) c #F2D6B4", +" _ c #AE8264", +" ` c #968274", +" ' c #462E2C", +" ] c #9A7664", +" [ c #E2C2A4", +" { c #9A7264", +" } c #DABE9C", +" | c #867264", +". c #E6BA94", +".. c #361A1C", +".X c #CAAA8C", +".o c #765E54", +".O c #26060C", +".+ c #C29E84", +".@ c #26020C", +".# c #C29A84", +".$ c #1E0204", +".% c #12020C", +".& c #B28A74", +".* c #FAD6B4", +".= c #523E44", +".- c #4A363C", +".; c #AA7E6C", +".: c #F6C69C", +".> c #422A34", +"., c #8E7664", +".< c #9A725C", +".1 c #E2BE9C", +".2 c #8E7264", +".3 c #E2BA9C", +".4 c #DAB694", +".5 c #321A24", +/* pixels */ +".@ y y y.$.$ !.$.$.$ ! p.$ = p p p ! ! !", +" y y.$ p.$ !.$ ! !.O p p = p = = = p p p", +" y.$ ! ! p.$ f v.< r z y p ~ p p = = = =", +" y !.$.$ 0 a 9 Q , n / U V = V V ~ p p.$", +" ! ! =.. 8 l 6 # , n i...$ ~ ~ = = V V =", +" y.$ y N g l 9.# , x r 4 & V = ~ ~ p p p", +" !.$.o ( d l 9.# _ n e 4 t ~ V = = = V =", +" ! y 2 :.: 3 . Q F P X z U V p ~ ~ ~ ~ ~", +" y J C d.: 3 ..+ F P * f U V.% V V V V =", +" ! Y ( : j. N u H x e K W V - - - ~ ~ ~", +" ! ` A : l 5 o.+.; x E K R p - -.% - V V", +" ! < A : 1.3 m u F 7 r.- +.% - - - V ~ ~", +" ! % A.* L. b B ; { e K R V.% - -.% V ~", +" ! 0 s A : 1 9 m.& k I.= &.% -.%.% ~ - V", +" ! ! ^ C : 1.4 w D k q h & - - - - V ~ =", +" ! =.5 G.* L c w D ] M.> & V V ~ V ~ V ~", +" y.$.$ / ) L.1.X.&.2 @.> ~ -.% - - ~ V V", +" ! = V ~ > } [.X Z., * V V - V V V ~ =", +" ! ! = p V O | T D | S & ~ V ~ p ~ V ~ V", +" ! !.$ !.$ p = $ '.- O p V ~ V V = ~ ~ =" +}; diff --git a/konquest/pics/planet6.xpm b/konquest/pics/planet6.xpm new file mode 100644 index 00000000..196b5ed7 --- /dev/null +++ b/konquest/pics/planet6.xpm @@ -0,0 +1,110 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 83 1", +/* colors */ +" c #2E0E0C", +". c #CE5A04", +"X c #CE5604", +"o c #1A0E0C", +"O c #CE5204", +"+ c #1A0A0C", +"@ c #1A060C", +"# c #06020C", +"$ c #BA4604", +"% c #BA3E04", +"& c #A64A04", +"* c #BA3A04", +"= c #A63E04", +"- c #A63A04", +"; c #A63604", +": c #923A04", +"> c #923204", +", c #7E2604", +"< c #6A2E04", +"1 c #6A2A04", +"2 c #561A04", +"3 c #4A1A0C", +"4 c #36160C", +"5 c #D65E04", +"6 c #D65A04", +"7 c #220A0C", +"8 c #C24E04", +"9 c #0E060C", +"0 c #C24A04", +"q c #0E020C", +"w c #C24604", +"e c #C24204", +"r c #060204", +"t c #AE4604", +"y c #9A3E04", +"u c #722E04", +"i c #722204", +"p c #4A1604", +"a c #3E120C", +"s c #2A0E0C", +"d c #CA5604", +"f c #CA5204", +"g c #CA4E04", +"h c #16060C", +"j c #CA4A04", +"k c #B64E04", +"l c #B64604", +"z c #B64204", +"x c #B63A04", +"c c #A24604", +"v c #A24204", +"b c #A23604", +"n c #8E3204", +"m c #7A2A04", +"M c #7A2604", +"N c #662A04", +"B c #662204", +"V c #5A220C", +"C c #521A04", +"Z c #32160C", +"A c #D25A04", +"S c #D25604", +"D c #1E0A0C", +"F c #BE4A04", +"G c #0A020C", +"H c #BE4204", +"J c #AA3E04", +"K c #AA3604", +"L c #963A04", +"P c #963204", +"I c #6E2204", +"U c #3A1A0C", +"Y c #3A120C", +"T c #260A0C", +"R c #C65604", +"E c #12060C", +"W c #C64A04", +"Q c #C64604", +"! c #B24E04", +"~ c #8A3204", +"^ c #763204", +"/ c #762604", +"( c #621E04", +/* pixels */ +"#r#r#r#r#rr#r#r#r#r#", +"r#r#r#GhD+qr#r#r#r#r", +"r#r#GUum/(aTqr#r#r##", +"#rrqNLb-b>,CsGr#r#rr", +"##rN=z%%x-P,C7Grr###", +"rrZ=zwweHxbnIY9###rr", +"#r^lWgjQ%*;>m3Drr##r", +"r9y8fffWwxKP,CT##rr#", +"r+JROOOjw*KP,2TG##r#", +"ro&XAXOfw%;P,CTrrr#r", +"r9cd66Sf0%;>Mp@##r##", +"#r^R556Sgz;~ia@rr#rr", +"rrZ!XAA.f$-~B q#r##r", +"##rNkRd8FJ:mp+rr#rr#", +"r#r9 c #362234", +" , c #CA9ECC", +" < c #925A74", +" 1 c #2E1E2C", +" 2 c #1E121C", +" 3 c #6E4A64", +" 4 c #B28AB4", +" 5 c #0E0A0C", +" 6 c #C27A9C", +" 7 c #0E060C", +" 8 c #A27AA4", +" 9 c #060604", +" 0 c #060204", +" q c #56364C", +" w c #F6AEDC", +" e c #E2AEDC", +" r c #4E2E44", +" t c #826284", +" y c #D29ECC", +" u c #7A5A7C", +" i c #D696BC", +" p c #865674", +" a c #2E1A24", +" s c #725A74", +" d c #725274", +" f c #26161C", +" g c #BA8AB4", +" h c #624A64", +" j c #160A0C", +" k c #5E3A4C", +" l c #A27A9C", +" z c #523A54", +" x c #F2B6E4", +" c c #5E364C", +" v c #AE7294", +" b c #E2AAD4", +" n c #926A8C", +" m c #462634", +" M c #CEA2D4", +" N c #322634", +" B c #322234", +" V c #825E7C", +" C c #8E5A74", +" Z c #2A222C", +" A c #2A1A2C", +" S c #CA92BC", +" D c #1A121C", +" F c #C682A4", +" G c #62465C", +" H c #0A0A0C", +" J c #0A060C", +" K c #EEBAEC", +" L c #9E7AA4", +" P c #020204", +" I c #E6B6E4", +" U c #F2B2DC", +" Y c #E6B2E4", +" T c #DEAEDC", +" R c #4A2E44", +" E c #EE9EC4", +" W c #8A5E7C", +" Q c #C696C4", +" ! c #7A526C", +" ~ c #BE92BC", +" ^ c #22121C", +" / c #7E4A5C", +" ( c #1A0E14", +" ) c #AE82AC", +" _ c #120A0C", +" ` c #56425C", +" ' c #B27A9C", +" ] c #0A0604", +" [ c #AA7694", +" { c #9E769C", +" } c #967294", +" | c #D6A2CC", +". c #3A1E2C", +".. c #DA96BC", +".X c #8A5674", +/* pixels */ +" P P P P P P P P P P P P ( a ( P P P P P", +" P P P P P P P P P P 1 o / - j P P P P P", +" P P P P P P P P 5 W v.X @ m 7 P P P P P", +" P P P P P P P _ O.. [ p k. 5 P P P P P", +" P P P P P P 9 l b X n 3 R ^ P P P P P P", +" P P P P P P % M y O n + ; D P P P P P P", +" P P P P P 9 4 : ~ 8 t G m _ P P P P P P", +" P P P P P N T Y Q L u # B 5 P P P P P P", +" P P P P P h K I y ) V $ 1 J P P P P P P", +" P P P P P K e Q { z 1 9 P P P P P P", +" P P P P P s T | g = d & f P P P P P P P", +" P P P P P ` , , X } d & A 0 P P P P P P", +" P P P P P Z M : X { # B J P P P P P P", +" P P P P P 0 4 Y | O n + > ] P P P P P P", +" P P P P P P % x U S = 3 ; _ P P P P P P", +" P P P P P P P * w i v ! r ^ P P P P P P", +" P P P P P P P H ' E F C q ^ P P P P P P", +" P P P P P P P P 0 . 6 < c f P P P P P P", +" P P P P P P P P P P 2 c @. 0 P P P P P", +" P P P P P P P P P P P P 0 ( ] P P P P P" +}; diff --git a/konquest/pics/planet8.xpm b/konquest/pics/planet8.xpm new file mode 100644 index 00000000..f1fbe368 --- /dev/null +++ b/konquest/pics/planet8.xpm @@ -0,0 +1,189 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 162 2", +/* colors */ +" c #362214", +" . c #A2623C", +" X c #8E5A3C", +" o c #8E563C", +" O c #825644", +" + c #76564C", +" @ c #1A120C", +" # c #865234", +" $ c #7A523C", +" % c #7A4E3C", +" & c #6E4E44", +" * c #6E4A44", +" = c #EA9E54", +" - c #7E522C", +" ; c #66463C", +" : c #5A4644", +" > c #5A4244", +" , c #E2964C", +" < c #523E3C", +" 1 c #523A3C", +" 2 c #DA8644", +" 3 c #CE824C", +" 4 c #3E363C", +" 5 c #D27E3C", +" 6 c #C67E44", +" 7 c #C67A44", +" 8 c #2A2A3C", +" 9 c #BE723C", +" 0 c #32221C", +" q c #321E1C", +" w c #AA663C", +" e c #9E6244", +" r c #92624C", +" t c #A26234", +" y c #965E3C", +" u c #8A5A44", +" i c #825A3C", +" p c #82563C", +" a c #82523C", +" s c #765244", +" d c #764E44", +" f c #6A4E4C", +" g c #6E4E3C", +" h c #6E4A3C", +" j c #624644", +" k c #724E2C", +" l c #D28E5C", +" z c #5A423C", +" x c #4E4244", +" c c #D68A4C", +" v c #D6864C", +" b c #CA8654", +" n c #523A34", +" m c #46363C", +" M c #CE8244", +" N c #CE7E44", +" B c #C27A4C", +" V c #322E3C", +" C c #C6763C", +" Z c #A27254", +" A c #A26E54", +" S c #3A2A1C", +" D c #BE7234", +" F c #B26E3C", +" G c #B26A3C", +" H c #A66A44", +" J c #321E14", +" K c #9E623C", +" L c #925E44", +" P c #1E1614", +" I c #8A5A3C", +" U c #8A563C", +" Y c #16120C", +" T c #825234", +" R c #764E3C", +" E c #764A3C", +" W c #5E4A4C", +" Q c #E69A54", +" ! c #6E4634", +" ~ c #62463C", +" ^ c #62423C", +" / c #564244", +" ( c #DE924C", +" ) c #DE8E4C", +" _ c #423A44", +" ` c #D68644", +" ' c #CA824C", +" ] c #52362C", +" [ c #3A323C", +" { c #C67634", +" } c #362A24", +" | c #BA723C", +". c #1E2634", +".. c #A26A4C", +".X c #966654", +".o c #3A2614", +".O c #9A6244", +".+ c #925E3C", +".@ c #865A44", +".# c #7A564C", +".$ c #1E120C", +".% c #120E14", +".& c #7E523C", +".* c #724E44", +".= c #6A4A3C", +".- c #6A463C", +".; c #5E4644", +".: c #E6964C", +".> c #020204", +"., c #DA9254", +".< c #563E3C", +".1 c #4A3E44", +".2 c #664224", +".3 c #DE8A44", +".4 c #D2864C", +".5 c #C68254", +".6 c #CA7E44", +".7 c #BE7A4C", +".8 c #2E2E3C", +".9 c #3E2E24", +".0 c #C2763C", +".q c #C2723C", +".w c #1A263C", +".e c #9E6E54", +".r c #BA6E34", +".t c #AE6E3C", +".y c #AE6A3C", +".u c #A26A44", +".i c #AE663C", +".p c #A26644", +".a c #A66634", +".s c #9A5E3C", +".d c #8E5E44", +".f c #925A34", +".g c #86563C", +".h c #7A5244", +".j c #6E524C", +".k c #6E4E4C", +".l c #7E4E34", +".z c #724A3C", +".x c #664A44", +".c c #664644", +".v c #5A464C", +".b c #E29654", +".n c #5E463C", +".m c #524244", +".M c #523E44", +".N c #6E4224", +".B c #DA8A4C", +".V c #4A3A3C", +".C c #5A3A24", +".Z c #D28244", +".A c #BA7E54", +".S c #423234", +".D c #36323C", +".F c #CA7A3C", +".G c #B2764C", +".H c #B2724C", +".J c #B66E3C", +".K c #AA6E44", +".L c #322224", +".P c #9E6A4C", +/* pixels */ +".>.>.>.>.>.>.o -.a F.a.f.N .>.>.>.>.>.>", +".>.>.>.> J.t ).3.Z G M.B.G.y K J.>.>.>.>", +".>.>.>.C.F.B ) , ).4., b f.n.# H.2.>.>.>", +".>.>.C 7 v.b Q = c.4 B.e.;.D.w.V.P k.>.>", +".> J '.4 2 (.b.b.:.Z c.A.V. .w.w & l.o.>", +".>.p c 3 M.: Q =.b.0 B b < V. 4.m.X.G.>", +" 0.7 3.F.4.B ) Q c ` ' g ^ 4 8 4 W W.5 S", +" ! 9 3 N 5.Z v ) ) c.p s / [ 8.D : + A i", +" X 6 ' 3 M.4 ) `.Z.K u z j.1.8.8 x.j...u", +" e | N C t 7 `.6 F.d ; O.M.;.V _.1 f.H.7", +" u.J.6.0.J.F 5 | ..&.< $.<.M >.;.v f.P.G", +" ] X 9 {.0 C C G e e.p.@.c ; <.x : f.k R", +".L 1 a.i {.F.q |.O p h.h.h d *.<.V / > }", +" P R ! E w.r D.r w e a p.@.* ~.= m.< /.%", +".> !.g T U.s.i.y.y.y L.d.@ z ~ < / ;.S.>", +".>.$ # #.g X.+ y . I n.- a % R n ; ; @.>", +".>.> .l T # o.f.s y I.+.+ a d R $ q.>.>", +".>.>.> q.N E h R O u o U O.& h h 0.>.>.>", +".>.>.>.> Y ] ;.- E $.z.h +.#.<.%.>.>.>.>", +".>.>.>.>.>.> @.9.n r Z.e h 0.>.>.>.>.>.>" +}; diff --git a/konquest/pics/planet9.xpm b/konquest/pics/planet9.xpm new file mode 100644 index 00000000..a8a96c17 --- /dev/null +++ b/konquest/pics/planet9.xpm @@ -0,0 +1,168 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"20 20 141 2", +/* colors */ +" c #760604", +" . c #620604", +" X c #4E0A04", +" o c #4E0604", +" O c #3A0604", +" + c #260604", +" @ c #E23204", +" # c #120604", +" $ c #E22E04", +" % c #E21E04", +" & c #CE2604", +" * c #CE1604", +" = c #CE1204", +" - c #F2AA0C", +" ; c #CE0A04", +" : c #F2A20C", +" > c #BA1204", +" , c #BA0E04", +" < c #F2960C", +" 1 c #BA0A04", +" 2 c #A60A04", +" 3 c #F2820C", +" 4 c #920E04", +" 5 c #920A04", +" 6 c #920604", +" 7 c #7E0A04", +" 8 c #6A0A04", +" 9 c #6A0604", +" 0 c #560604", +" q c #EA5204", +" w c #420604", +" e c #EA4604", +" r c #EA4204", +" t c #EA3A04", +" y c #EA3604", +" u c #D63E04", +" i c #1A0604", +" p c #060604", +" a c #D61A04", +" s c #C22604", +" d c #D60E04", +" f c #AE1E04", +" g c #C20E04", +" h c #AE1A04", +" j c #C20A04", +" k c #AE1604", +" l c #AE0E04", +" z c #AE0A04", +" x c #9A0E04", +" c c #9A0A04", +" v c #860A04", +" b c #860604", +" n c #F27204", +" m c #720A04", +" M c #720604", +" N c #5E0A04", +" B c #F25A04", +" V c #5E0604", +" C c #4A0604", +" Z c #F24604", +" A c #360604", +" S c #DE4604", +" D c #220604", +" F c #0E0604", +" G c #DE2604", +" H c #F6BA14", +" J c #DE1A04", +" K c #CA1204", +" L c #CA0E04", +" P c #B60E04", +" I c #8E2604", +" U c #B60A04", +" Y c #A20A04", +" T c #A20604", +" R c #8E0A04", +" E c #8E0604", +" W c #7A0E04", +" Q c #7A0A04", +" ! c #7A0604", +" ~ c #660A04", +" ^ c #660604", +" / c #520A04", +" ( c #520604", +" ) c #3E0604", +" _ c #2A0A04", +" ` c #2A0604", +" ' c #D24604", +" ] c #160604", +" [ c #E62A04", +" { c #F2C21C", +" } c #E62204", +" | c #020604", +". c #020204", +".. c #D21E04", +".X c #D21A04", +".o c #D21604", +".O c #BE1E04", +".+ c #D20E04", +".@ c #BE1604", +".# c #BE1204", +".$ c #BE0E04", +".% c #BE0A04", +".& c #AA0E04", +".* c #AA0A04", +".= c #AA0604", +".- c #960A04", +".; c #820A04", +".: c #6E0A04", +".> c #6E0604", +"., c #EE6604", +".< c #E2660C", +".1 c #EE6204", +".2 c #5A0A04", +".3 c #EE5A04", +".4 c #5A0604", +".5 c #EE5604", +".6 c #460A04", +".7 c #460604", +".8 c #EE3E04", +".9 c #320604", +".0 c #EE3604", +".q c #EE3204", +".w c #1E0604", +".e c #0A0604", +".r c #DA1E04", +".t c #DA1A04", +".y c #DA1204", +".u c #C61604", +".i c #F2A214", +".p c #C61204", +".a c #C60E04", +".s c #C60A04", +".d c #B21604", +".f c #B20E04", +".g c #B20A04", +".h c #9E0A04", +".j c #9E0604", +".k c #F27614", +".l c #8A0A04", +".z c #8A0604", +".x c #760E04", +/* pixels */ +" p p p | | p p p ] ` D F p p p | | | p p", +". . . . . _ m 5 c.-.; M o.w. . . . . . ", +" | | p p.2 l , 5.f l.- ! w . ( + p | | |", +". . . C v.l M R ,.p 1 v M.4 o 0 (. . . ", +" p p.9.> O ` (.d.t a , , 5 ~ E z X p |", +". # C ~ 0.4.2.p Y 7 / 9 2 l ..7 z i. ", +". (.:.9.2 9.7.9 O ) w.4 D.2 8 m.w v N |", +" i .l.4 C 0 D.7.x.; E.z V 2 x z b.; 6. ", +" O.2 m.- ( M.6 w h.l > 1.h 4 % * * =.+ D", +" X b c.l R.u.. f.d s.<.<.X a.q Z.q.a g O", +" ).> Y 2 K P.* G.1 - { : r a.8 <.k G.h O", +" `.g E.- J.r.r t 3.i - H n t 3 < H.3.@ D", +" # , 8.6 l W P.$ = = $ q ' u.5.i., n S p", +". Q.z.z.= N U L.s T ! & k } B.5 e [ I. ", +" p _.4.$.* x.g 1.s b V.& y.q.t.0 @.O i. ", +". . N g U L.% j U.* 5.$.a > J.t.#.7. p", +" p. .e.-.a L.s d.+ ; U U.= ,.o.g 0. . . ", +". |. .e Q.s d.y d.s 1.=.% j.j ). p. p", +". . . . . A 5 j ; ;.%.% U ^ #. . p. |", +" p p p p p | |.e.9 ) O +.e p. p p. p. " +}; diff --git a/konquest/pics/ruler.xpm b/konquest/pics/ruler.xpm new file mode 100644 index 00000000..6e60655c --- /dev/null +++ b/konquest/pics/ruler.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static char * ruler_xpm[] = { +"16 16 2 1", +" c None", +". c #000000000000", +" ", +"................", +"................", +" . . . ", +" . . . ", +" . . . ", +" . . ", +" . . ", +" . ... ", +" . ..... ", +" . .. .. ", +" . ... ", +" ... ", +" .. ", +" ..... ", +" ..... "}; diff --git a/konquest/planet_info.cc b/konquest/planet_info.cc new file mode 100644 index 00000000..4c1d4be1 --- /dev/null +++ b/konquest/planet_info.cc @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include + +#include "planet_info.h" +#include +#include "planet_info.moc" + +PlanetInfo::PlanetInfo( QWidget *parent, QPalette palette ) + : QFrame( parent ) +{ + setPalette( palette ); + + name = new QLabel( this ); + name->setMinimumWidth( 100 ); + owner = new QLabel( this ); + owner->setMinimumWidth( 100 ); + ships = new QLabel( this ); + ships->setMinimumWidth( 100 ); + production = new QLabel( this ); + production->setMinimumWidth( 100 ); + kill_percent = new QLabel( this ); + kill_percent->setMinimumWidth( 100 ); + + clearDisplay(); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + + layout1->addWidget( name ); + layout1->addWidget( owner ); + layout1->addWidget( ships ); + layout1->addWidget( production ); + layout1->addWidget( kill_percent ); + layout1->addStretch(1); + + setMouseTracking( true ); + + setMinimumSize( sizeHint() ); + setMaximumHeight( sizeHint().height() ); +} + +PlanetInfo::~PlanetInfo() +{ + emptyPlanetInfoList(); +} + +QSize PlanetInfo::sizeHint() const +{ + int height; + + height = name->sizeHint().height() + + owner->sizeHint().height() + + ships->sizeHint().height() + + production->sizeHint().height()+ + kill_percent->sizeHint().height(); + + return QSize( 100, height ); +} + +void PlanetInfo::setPlanetList( PlanetList &newPlanets ) +{ + emptyPlanetInfoList(); + + PlanetListIterator itr( newPlanets ); + + Planet *p; + while( (p = itr()) ) { + planet_info_buffer *stats = new planet_info_buffer; + stats->planet = p; + planet_stats.append( stats ); + } + + rescanPlanets(); +} + +void PlanetInfo::rescanPlanets() +{ + PlanetInfoListIterator itr( planet_stats ); + planet_info_buffer *p; + + while( (p = itr()) ) { + p->production = p->planet->getProduction(); + p->ships = p->planet->getFleet().getShipCount(); + p->killRate = p->planet->getKillPercentage(); + } +} + +void PlanetInfo::clearDisplay() +{ + QString temp; + + temp = "" + i18n("Planet name: "); + name->setText( temp ); + + temp = "" + i18n("Owner: "); + owner->setText( temp ); + + temp = "" + i18n("Ships: "); + ships->setText( temp ); + + temp = "" + i18n("Production: "); + production->setText( temp ); + + temp = "" + i18n("Kill percent: "); + kill_percent->setText( temp ); +} + +void PlanetInfo::emptyPlanetInfoList() +{ + planet_stats.first(); + + planet_info_buffer *p; + while( (p = planet_stats.take()) ) { + delete p; + } + +} + +void PlanetInfo::showPlanet( Planet *planet ) +{ + if( planet->getPlayer()->isNeutral() ) { + clearDisplay(); + + QString temp; + + temp = "" + i18n("Planet name: %1").arg(planet->getName()); + name->setText( temp ); + return; + } + + QString nameToShow = planet->getName(); + + PlanetInfoListIterator itr( planet_stats ); + planet_info_buffer *p; + + while( (p = itr()) ) { + if( p->planet == planet ) { + + QString temp; + + temp = "" + i18n("Planet name: %1").arg(p->planet->getName()); + name->setText( temp ); + + temp = "" + i18n("Owner: %1").arg(p->planet->getPlayer()->getColoredName()); + owner->setText( temp ); + + temp = "" + i18n("Ships: %1").arg( KGlobal::locale()->formatNumber(p->ships, 0) ); + ships->setText( temp ); + + temp = "" + i18n("Production: %1").arg( KGlobal::locale()->formatNumber(p->production, 0) ); + production->setText( temp ); + + temp = "" + i18n("Kill percent: %1").arg( KGlobal::locale()->formatNumber(p->killRate, 3) ); + kill_percent->setText( temp ); + } + } +} + diff --git a/konquest/planet_info.h b/konquest/planet_info.h new file mode 100644 index 00000000..4080f6b9 --- /dev/null +++ b/konquest/planet_info.h @@ -0,0 +1,53 @@ +#ifndef _PLANET_INFO_H_ +#define _PLANET_INFO_H_ + +#include +#include +#include +#include + +#include "gamecore.h" + +class QLabel; + +struct planet_info_buffer { + Planet *planet; + int production; + int ships; + float killRate; +}; + +typedef QPtrList PlanetInfoList; +typedef QPtrListIterator PlanetInfoListIterator; + +class PlanetInfo : public QFrame +{ + Q_OBJECT + +public: + PlanetInfo( QWidget *parent, QPalette palette ); + virtual ~PlanetInfo(); + + void setPlanetList( PlanetList &newPlanets ); + void rescanPlanets(); + QSize sizeHint() const; + +public slots: + void showPlanet( Planet * ); + +private: + void emptyPlanetInfoList(); + void clearDisplay(); + + PlanetList *planets; + PlanetInfoList planet_stats; + + QLabel *name; + QLabel *owner; + QLabel *ships; + QLabel *production; + QLabel *kill_percent; +}; + +#endif // _PLANET_INFO_H_ + diff --git a/konquest/scoredlg.cc b/konquest/scoredlg.cc new file mode 100644 index 00000000..685ba9c5 --- /dev/null +++ b/konquest/scoredlg.cc @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +#include "scoredlg.h" + +ScoreDlgListViewItem::ScoreDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5, QString s6) : QListViewItem(parent, s1, s2, s3, s4, s5, s6) +{ +} + +int ScoreDlgListViewItem::compare(QListViewItem *i, int col, bool) const +{ + if (col == 0) + { + if (text(col) > i -> text(col)) return 1; + else if (text(col) < i -> text(col)) return -1; + else return 0; + } + else + { + if (text(col).toInt() > i -> text(col).toInt()) return 1; + else if (text(col).toInt() < i -> text(col).toInt()) return -1; + else return compare(i, 0, true); + } +} + + +ScoreDlg::ScoreDlg( QWidget *parent, const QString& title, PlayerList *players ) + : QDialog(parent, "ScoreDlg", true ), plrList(players) +{ + setCaption( kapp->makeStdCaption(title) ); + + scoreTable = new KListView( this, 0 ); + scoreTable->addColumn(i18n("Player")); + scoreTable->addColumn(i18n("Ships Built")); + scoreTable->addColumn(i18n("Planets Conquered")); + scoreTable->addColumn(i18n("Fleets Launched")); + scoreTable->addColumn(i18n("Fleets Destroyed")); + scoreTable->addColumn(i18n("Ships Destroyed")); + scoreTable->setMinimumSize( scoreTable->sizeHint() ); + + KPushButton *okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setMinimumSize( okButton->sizeHint() ); + okButton->setDefault(true); + + QVBoxLayout *layout1 = new QVBoxLayout( this ); + QHBoxLayout *layout2 = new QHBoxLayout; + + layout1->addWidget( scoreTable, 1 ); + layout1->addLayout( layout2 ); + + layout2->addStretch( 2 ); + layout2->addWidget( okButton ); + layout2->addStretch( 2 ); + + connect( okButton, SIGNAL(clicked()), this, SLOT(accept()) ); + + init(); + + resize( 580, 140 ); +} + +void +ScoreDlg::init() +{ + Player *curPlayer; + PlayerListIterator itr( *plrList ); + + for( ;(curPlayer = itr()); ) + new ScoreDlgListViewItem(scoreTable, + curPlayer->getName(), + QString("%1").arg(curPlayer->getShipsBuilt()), + QString("%1").arg(curPlayer->getPlanetsConquered()), + QString("%1").arg(curPlayer->getFleetsLaunched()), + QString("%1").arg(curPlayer->getEnemyFleetsDestroyed()), + QString("%1").arg(curPlayer->getEnemyShipsDestroyed())); +} + diff --git a/konquest/scoredlg.h b/konquest/scoredlg.h new file mode 100644 index 00000000..570a3b36 --- /dev/null +++ b/konquest/scoredlg.h @@ -0,0 +1,32 @@ +#ifndef _SCOREDLG_H_ +#define _SCOREDLG_H_ + +#include + +#include + +#include "gamecore.h" + +class ScoreDlgListViewItem : public QListViewItem +{ + public: + ScoreDlgListViewItem(QListView *parent, QString s1, QString s2, QString s3, QString s4, QString s5, QString s6); + int compare(QListViewItem *i, int col, bool) const; +}; + +class ScoreDlg : public QDialog +{ + +public: + ScoreDlg( QWidget *parent, const QString& title, PlayerList *players ); + +private: + void init(); + + PlayerList *plrList; + QListView *scoreTable; + +}; + +#endif // _SCOREDLG_H_ + diff --git a/konquest/version.h b/konquest/version.h new file mode 100644 index 00000000..df59820a --- /dev/null +++ b/konquest/version.h @@ -0,0 +1,4 @@ +#ifndef KONQUEST_VERSION +#define KONQUEST_VERSION "1.1" +#endif + diff --git a/kpat/AUTHORS b/kpat/AUTHORS new file mode 100644 index 00000000..cc9f0a73 --- /dev/null +++ b/kpat/AUTHORS @@ -0,0 +1,6 @@ +Olav Tvete (original author) +Matthias Ettrich +Mario Weilguni +Rodolfo Borges +Peter H. Ruegg +Stephan Kulow (current maintainer) diff --git a/kpat/CHANGES b/kpat/CHANGES new file mode 100644 index 00000000..2480f12e --- /dev/null +++ b/kpat/CHANGES @@ -0,0 +1,150 @@ +2005-06-25 Inge Wallin + + Fix bug 131587: "cannot win this game" message just after + start of mod3 game + - Check for ace at the store in addition to empty + +2005-06-25 Inge Wallin + + Code cleaning + - Rename Card::Value --> Card::Rank + - Rename Card::Suites --> Card::Suit + +2005-02-18 Inge Wallin + + Bumped version number to 2.2.2 because of all the bug fixes + during the last months and the upcoming release of KDE 3.4. + +2005-01-07 Inge Wallin + + Fix bug 91317: Klondike (draw 3) incorrect unwinnable game message + - Fix criteria for lost game in klondike. + + ---- CVS commit here ---- + + Fix bug 96531: KPat: Mod3 incorrect unwinnable game message + - Fix criteria for lost game in mod3. + +2004-11-10 Inge Wallin + + Continue the code cleaning + - card.h, card.cpp + +2004-11-04 Inge Wallin + + Fix the fix for 92002 below: + - Only ask for confirmation if the user actively aborts the game. + + ---- CVS commit here ---- + + Start of a thorough code cleaning: + - Start with hide.h and dealer.cpp + +2004-10-31 Inge Wallin + + - fix wish 92002: Restart game button should have a confirmation dialog + +2004-10-15 Inge Wallin + + - fix bug 89276: kpat: make nag screen "you have lost this game" an option + +2004-10-14 Inge Wallin + + - fix bug 87451: crash in patience when selecting 'unknown' as game type + ---- CVS commit here ---- + + - fix bug 89755: Aces up move counter doesn't work (idiot.cpp) + - bump version to 2.2.1 + +------------------ KDE 3.3.1 released here --------------------- + +since kpat-0.7.3 (18/Nov/1999) + + - bugfix in idiot.cpp + - bugfixes in grandf.cpp + - added CTRL-R for new game + +1999-02-01 Mario Weilguni + + * fixes to avoid many warning from egcs + +Sat May 2 13:49:46 1998 Mario Weilguni + + * replaced all locale->translate with i18n + +kapt 0.7.1 + - [Robert Williams] added getHelpMenu() + - [Robert Williams] added -caption "%c" to kpat.kdelnk + +since kpat-0.7 (11/Dec/1997) + + - added "Mod3" game (Rodolfo) + - added "Freecell" game (Rodolfo) + - Deck class can now handle any multiple of 52 cards (Rodolfo) + +since kpat-0.6 + + - bugfix for "Grandfather"-hint (Rodolfo) + - new KDE FSSTND compliant + - depending on display colors sets either a + 16 color or high color icon and miniicon + - fixes for new KConfig + +since kpat-0.5 + + - fixed the bug in Napoleon (cards not completely drawn) + - added locale + - german translation + +since kpat-0.4 + + - major rewrite of pwidget.(cpp|h) + - KDE compliant menubar and dialogs + - kpat now stores preferences (type of game...) + - window title includes current game type + - misc. cleanups + - kpat uses now kfixedtopwidget (similar to ktoplevelwidget) + - toolbar added (though it does not have many buttons) + - uses KMsgBox instead of QMessageBox + - now kpat has a fixed size + - (hopefully) KDE compliant shortcut keys + - did a lot of reformatting the sourcecode to make + it more readable (had to many empty lines IMHO) + - pwidget does not decide which sizes to use for + the different type of games, now the games itself supply + a sizehint (will make it more extendible) + - HTML help + - animation on startup, since loading the cards may take + several seconds to finish on slow machines. + - a lot more not mentioned here... + +since kpat-0.3 + + - kpat is a KApplication now (Matthias) + +since kpat-0.2 + + - added "MicroSolitaire" game (Paul) + - removed "cheating" option from Klondike (Paul) + - set wholeColumn for Klondike (Paul) + - Changed Klondike accelerator to Ctrl+K (Paul) + - Added menu bar hot keys (Paul) + +since kpat-0.1 + + - changed shading colorGroup (Paul) + - fixed drawing of suit symbols (Paul) + - renamed "very easy" to "cheating" :) (Paul) + - fixed spurious core dump(Paul) + - handle mousePress outside cards (Paul) + - menu cleanup (Paul) + +since "patience" + + - renamed the stuff kpat-0.1 (Matthias) + - improved drag'n'drop (no more weird jumping of cards) (Matthias) + - improved look (shaded borders around the cards and piles) (Matthias) + - hopefully improved cards-background (Matthias) + - "very easy"-option for klondike to give only one card. (Matthias) + - unset wholeColumn for klondike, but this all belongs into OPTIONS (Matthias) + diff --git a/kpat/Makefile.am b/kpat/Makefile.am new file mode 100644 index 00000000..1243bf9f --- /dev/null +++ b/kpat/Makefile.am @@ -0,0 +1,30 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +bin_PROGRAMS = kpat + +kpat_SOURCES = main.cpp cardmaps.cpp card.cpp dealer.cpp \ + pwidget.cpp pile.cpp deck.cpp pile_algorithms.cpp kings.cpp freecell.cpp klondike.cpp simon.cpp grandf.cpp \ + mod3.cpp idiot.cpp napoleon.cpp computation.cpp gypsy.cpp fortyeight.cpp \ + yukon.cpp clock.cpp golf.cpp spider.cpp \ + gamestatsimpl.cpp \ + gamestats.ui +kpat_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kpat_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) ./freecell-solver/libfcs.la +kpat_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +SUBDIRS = icons freecell-solver + +xdg_apps_DATA = kpat.desktop + +rcdir = $(kde_datadir)/kpat +rc_DATA = kpatui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kpat.pot + +bgdir = $(kde_datadir)/kpat/backgrounds +bg_DATA = green.png + diff --git a/kpat/README b/kpat/README new file mode 100644 index 00000000..72a2cd6a --- /dev/null +++ b/kpat/README @@ -0,0 +1,41 @@ +Hello, + +this is the very first release of kpat, the KDE solitaire patience game. + +It is an almost unchanged "patience" from Paul Olav Tvete, who uploaded +this stuff some months ago on www.troll.no. + +My changes are so far: + + - renamed the stuff kpat-0.1 + - improved drag'n'drop (no more weird jumping of cards) + - improved look (shaded borders around the cards and piles) + - hopefully improved cards-background + - "very easy"-option for klondike to give only one card. + - unset wholeColumn for klondike, but this all belongs into OPTIONS + +Anway, kpat is IMO already yet the best X-based solitaire-like game, +thanks to Paul Olav Tvete and Qt :-) + + +Greets, + + Matthias + + + +----- original README (probably Paul): + +This is an implementation of patience (solitaire). It consists of a +general class library and six games implemented using it. + +I wrote this program when learning Qt. There are a number of things I +would have done differently now. One of the silliest is that all the +intelligence lies in the cards (which are individual widgets, by the way) +instead of in a Patience class. + +There are a lot of static variables. Don't try to instantiate more than one +dealer at a time -- that way lies madness. + +The general base classes could doubtlessly have been a lot more general. + diff --git a/kpat/README.tkcTrump b/kpat/README.tkcTrump new file mode 100644 index 00000000..93cbba95 --- /dev/null +++ b/kpat/README.tkcTrump @@ -0,0 +1,8 @@ +If you bought a binary of tkcTrump from theKompany and want to modify +the source code to fit your needs, check out +http://developer.kde.org/~coolo/tkcTrump-path.diff.bz2 + +Please note: I do not support the code, but if you saw something in tkcTrump +worth backporting, look at the patch. + +Stephan Kulow diff --git a/kpat/TODO b/kpat/TODO new file mode 100644 index 00000000..3f4e41c1 --- /dev/null +++ b/kpat/TODO @@ -0,0 +1,42 @@ +Cleaning: + type Dealer --> Patience + type Card::Value --> Card::Rank + + +TODO: + - more options (wholeColumn, etc.) + kpat already supports a lot, but they aren't accessible via GUI yet + - implement support for nice (configurable) backgrounds, like + color gradients, background pixmaps (tiled, wallpaper, etc. ). + - give feedback in the statusbar if there is no move except draw + possible. Open a messagebox when no more moves are possible at all. + (ie. Game Over). + - separate the dealer and related classed into a library, so + that other card games can use it. + +DONE: + - preview on cards that are hidden but faceup (RMB) + - animation when a card moves back automatically + - nice animation when you win a game + - game numbers as in MS freecell (thanks Marcus) + - undo + - ambigious place - choose one + - flip animation + - dblclick to put cards directly onto the piles. + - more colorful cards (there are some nice with xpat2) + same cards as KPoker (shamelessly stolen :-) + - better scaling (what about a 640x480 resolution?) + - more options (different backgrounds) + - kpat still uses the WidgetAt function when dropping cards. That + means it is not possible to put the hot spot onto the middle of the + dragged cards what would be better. + +Solutions: +#1 Wilson Callan +3a 32 7b 3c 37 37 b7 8b 87 48 +82 a8 4a 34 57 54 85 8d 87 c7 +d7 b8 38 23 28 32 6b 6c 78 a3 +73 7a 7c 74 c7 67 63 56 8h b8 +5b 51 b5 24 25 6h 6h 24 26 a4 +37 2a 8h 4h 1h 17 1h 1b 8h 4h +4b 4c 4d a2 42 46 3h 7h 13 diff --git a/kpat/card.cpp b/kpat/card.cpp new file mode 100644 index 00000000..983a901d --- /dev/null +++ b/kpat/card.cpp @@ -0,0 +1,380 @@ +/****************************************************** + + Card.cpp -- support classes for patience type card games + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +*******************************************************/ + +#include +#include + +#include + +#include + +#include "card.h" +#include "pile.h" +#include "cardmaps.h" + + +static const char *suit_names[] = {"Clubs", "Diamonds", "Hearts", "Spades"}; +static const char *rank_names[] = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", + "Nine", "Ten", "Jack", "Queen", "King" }; + +// Run time type id +const int Card::RTTI = 1001; + + +Card::Card( Rank r, Suit s, QCanvas* _parent ) + : QCanvasRectangle( _parent ), + m_suit( s ), m_rank( r ), + m_source(0), scaleX(1.0), scaleY(1.0), tookDown(false) +{ + // Set the name of the card + // FIXME: i18n() + m_name = QString("%1 %2").arg(suit_names[s-1]).arg(rank_names[r-1]).utf8(); + + // Default for the card is face up, standard size. + m_faceup = true; + setSize( cardMap::CARDX(), cardMap::CARDY() ); + + m_destX = 0; + m_destY = 0; + m_destZ = 0; + + m_flipping = false; + m_animSteps = 0; + m_flipSteps = 0; +} + + +Card::~Card() +{ + // If the card is in a pile, remove it from there. + if (source()) + source()->remove(this); + + hide(); +} + + +// ---------------------------------------------------------------- +// Member functions regarding graphics + + +// Return the pixmap of the card +// +QPixmap Card::pixmap() const +{ + return cardMap::self()->image( m_rank, m_suit ); +} + + +// Turn the card if necessary. If the face gets turned up, the card +// is activated at the same time. +// +void Card::turn( bool _faceup ) +{ + if (m_faceup != _faceup) { + m_faceup = _faceup; + setActive(!isActive()); // abuse + } +} + +// Draw the card on the painter 'p'. +// +void Card::draw( QPainter &p ) +{ + QPixmap side; + + // Get the image to draw (front / back) + if( isFaceUp() ) + side = cardMap::self()->image( m_rank, m_suit, isSelected()); + else + side = cardMap::self()->backSide(); + + // Rescale the image if necessary. + if (scaleX <= 0.98 || scaleY <= 0.98) { + QWMatrix s; + s.scale( scaleX, scaleY ); + side = side.xForm( s ); + int xoff = side.width() / 2; + int yoff = side.height() / 2; + p.drawPixmap( int(x() + cardMap::CARDX()/2 - xoff), + int(y() + cardMap::CARDY()/2 - yoff), side ); + } else + p.drawPixmap( int(x()), int(y()), side ); +} + + +void Card::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); +} + + +// Return the X of the cards real position. This is the destination +// of the animation if animated, and the current X otherwise. +// +int Card::realX() const +{ + if (animated()) + return m_destX; + else + return int(x()); +} + + +// Return the Y of the cards real position. This is the destination +// of the animation if animated, and the current Y otherwise. +// +int Card::realY() const +{ + if (animated()) + return m_destY; + else + return int(y()); +} + + +// Return the > of the cards real position. This is the destination +// of the animation if animated, and the current Z otherwise. +// +int Card::realZ() const +{ + if (animated()) + return m_destZ; + else + return int(z()); +} + + +// Return the "face up" status of the card. +// +// This is the destination of the animation if animated and animation +// is more than half way, the original if animated and animation is +// less than half way, and the current "face up" status otherwise. +// + +bool Card::realFace() const +{ + if (animated() && m_flipping) { + bool face = isFaceUp(); + if ( m_animSteps >= m_flipSteps / 2 - 1 ) + return !face; + else + return face; + } else + return isFaceUp(); +} + + +/// the following copyright is for the flipping code +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qt Palmtop Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +// Used to create an illusion of the card being lifted while flipped. +static const double flipLift = 1.2; + +// The current maximum Z value. This is used so that new cards always +// get placed on top of the old ones and don't get placed in the +// middle of a destination pile. +int Card::Hz = 0; + + +void Card::setZ(double z) +{ + QCanvasRectangle::setZ(z); + if (z > Hz) + Hz = int(z); +} + + +// Start a move of the card using animation. +// +// 'steps' is the number of steps the animation should take. +// +void Card::moveTo(int x2, int y2, int z2, int steps) +{ + m_destX = x2; + m_destY = y2; + m_destZ = z2; + + double x1 = x(); + double y1 = y(); + double dx = x2 - x1; + double dy = y2 - y1; + + if (!dx && !dy) { + setZ(z2); + return; + } + setZ(Hz++); + + if (steps) { + // Ensure a good speed + while ( fabs(dx/steps)+fabs(dy/steps) < 5.0 && steps > 4 ) + steps--; + + setAnimated(true); + setVelocity(dx/steps, dy/steps); + + m_animSteps = steps; + + } else { + // _really_ fast + setAnimated(true); + setAnimated(false); + emit stoped(this); + } +} + + +// Animate a move to (x2, y2), and at the same time flip the card. +// +void Card::flipTo(int x2, int y2, int steps) +{ + // Check that we are not already animating. + assert(!animated()); + + int x1 = (int)x(); + int y1 = (int)y(); + double dx = x2 - x1; + double dy = y2 - y1; + + // Mark this animation as a flip as well. + m_flipping = true; + m_flipSteps = steps; + + // Set the target of the animation + m_destX = x2; + m_destY = y2; + m_destZ = int(z()); + + // Let the card be above all others during the animation. + setZ(Hz++); + + m_animSteps = steps; + setVelocity(dx/m_animSteps, dy/m_animSteps-flipLift); + + setAnimated(TRUE); +} + + +// Advance a card animation one step. This function adds flipping of +// the card to the translation animation that QCanvasRectangle offers. +// +void Card::advance(int stage) +{ + if ( stage==1 ) { + // If the animation is finished, emit stoped. (FIXME: name) + if ( m_animSteps-- <= 0 ) { + setAnimated(false); + emit stoped(this); + } else { + // Animation is not finished. Check for flipping and add + // that animation to the simple translation. + if ( m_flipping ) { + if ( m_animSteps > m_flipSteps / 2 ) { + // animSteps = flipSteps .. flipSteps/2 (flip up) -> 1..0 + scaleX = ((double)m_animSteps/m_flipSteps-0.5)*2; + } else { + // animSteps = flipSteps/2 .. 0 (flip down) -> 0..1 + scaleX = 1-((double)m_animSteps/m_flipSteps)*2; + } + if ( m_animSteps == m_flipSteps / 2-1 ) { + setYVelocity(yVelocity()+flipLift*2); + turn( !isFaceUp() ); + } + } + } + } + + // Animate the translation of the card. + QCanvasRectangle::advance(stage); +} + + +// Set 'animated' status to a new value, and set secondary values as +// well. +// +void Card::setAnimated(bool anim) +{ + // If no more animation, reset some other values as well. + if (animated() && !anim) { + // Reset all things that might have changed during the animation. + scaleX = 1.0; + scaleY = 1.0; + m_flipping = FALSE; + setVelocity(0, 0); + + // Move the card to its destination immediately. + move(m_destX, m_destY); + setZ(m_destZ); + } + + QCanvasRectangle::setAnimated(anim); +} + + +void Card::setTakenDown(bool td) +{ + if (td) + kdDebug(11111) << "took down " << name() << endl; + tookDown = td; +} + + +bool Card::takenDown() const +{ + return tookDown; +} + + +// Get the card to the top. + +void Card::getUp(int steps) +{ + m_destZ = int(z()); + m_destX = int(x()); + m_destY = int(y()); + setZ(Hz+1); + + // Animation + m_animSteps = steps; + setVelocity(0, 0); + setAnimated(TRUE); +} + +#include "card.moc" diff --git a/kpat/card.h b/kpat/card.h new file mode 100644 index 00000000..61f07399 --- /dev/null +++ b/kpat/card.h @@ -0,0 +1,131 @@ +/*****************-*-C++-*-**************** + + + + Card.h -- movable and stackable cards + with check for legal moves + + + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + ****************************************************/ + + +#ifndef PATIENCE_CARD +#define PATIENCE_CARD + +#include + +// The following classes are defined in other headers: +class cardPos; +class Deck; +class Dealer; +class Pile; +class Card; + + +// A list of cards. Used in many places. +typedef QValueList CardList; + + +// In kpat, a Card is an object that has at least two purposes: +// - It has card properties (Suit, Rank, etc) +// - It is a graphic entity on a QCanvas that can be moved around. +// +class Card: public QObject, public QCanvasRectangle { + Q_OBJECT + +public: + enum Suit { Clubs = 1, Diamonds, Hearts, Spades }; + enum Rank { None = 0, Ace = 1, Two, Three, Four, Five, Six, Seven, + Eight, Nine, Ten, Jack, Queen, King }; + + Card( Rank r, Suit s, QCanvas *parent=0); + virtual ~Card(); + + // Properties of the card. + Suit suit() const { return m_suit; } + Rank rank() const { return m_rank; } + const QString name() const { return m_name; } + + // Some basic tests. + bool isRed() const { return m_suit==Diamonds || m_suit==Hearts; } + bool isFaceUp() const { return m_faceup; } + + QPixmap pixmap() const; + + void turn(bool faceup = true); + + static const int RTTI; + + Pile *source() const { return m_source; } + void setSource(Pile *p) { m_source = p; } + + virtual int rtti() const { return RTTI; } + + virtual void moveBy(double dx, double dy); + void moveTo(int x2, int y2, int z, int steps); + void flipTo(int x, int y, int steps); + virtual void setAnimated(bool anim); + void setZ(double z); + void getUp(int steps = 12); + + int realX() const; + int realY() const; + int realZ() const; + bool realFace() const; + + void setTakenDown(bool td); + bool takenDown() const; + +signals: + void stoped(Card *c); + +protected: + void draw( QPainter &p ); // Redraw the card. + void advance(int stage); + +private: + // The card values. + Suit m_suit; + Rank m_rank; + QString m_name; + + // Grapics properties. + bool m_faceup; // True if card lies with the face up. + Pile *m_source; + + double scaleX; + double scaleY; + + bool tookDown; + + // Used for animation + int m_destX; // Destination point. + int m_destY; + int m_destZ; + int m_animSteps; // Let the animation take this many steps. + + // Used if flipping during an animated move. + bool m_flipping; + int m_flipSteps; + + // The maximum Z ever used. + static int Hz; +}; + + +#endif diff --git a/kpat/cardmaps.cpp b/kpat/cardmaps.cpp new file mode 100644 index 00000000..5a2decae --- /dev/null +++ b/kpat/cardmaps.cpp @@ -0,0 +1,248 @@ +/***********************-*-C++-*-******** + + cardmaps.cpp defines pixmaps for playing cards + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#include +#include + +#include + +#include + +#include +#include "cardmaps.h" +#include + +#include +#include +#include +#include +#include "version.h" +#include +#include +#include +#include +#include +#include + +cardMap *cardMap::_self = 0; +static KStaticDeleter cms; + +cardMap::cardMap(const QColor &dim) : dimcolor(dim) +{ + assert(!_self); + + card_width = 0; + card_height = 0; + + kdDebug(11111) << "cardMap\n"; + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + QString bg = config->readEntry( "Back", KCardDialog::getDefaultDeck()); + setBackSide( bg, false); + + QString dir = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + setCardDir( dir ); + + cms.setObject(_self, this); +// kdDebug(11111) << "card " << CARDX << " " << CARDY << endl; +} + +bool cardMap::setCardDir( const QString &dir) +{ + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + // create an animation window while loading pixmaps (this + // may take a while (approx. 3 seconds on my AMD K6PR200) + bool animate = config->readBoolEntry( "Animation", true); + + QWidget* w = 0; + QPainter p; + QTime t1, t2; + + QString imgname = KCardDialog::getCardPath(dir, 11); + + QImage image; + image.load(imgname); + if( image.isNull()) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << dir << "\n"; + p.end(); + delete w; + return false; + } + + int old_card_width = card_width; + int old_card_height = card_height; + + card_width = image.width(); + card_height = image.height(); + + const int diff_x_between_cards = QMAX(card_width / 9, 1); + QString wait_message = i18n("please wait, loading cards..."); + QString greeting = i18n("KPatience - a Solitaire game"); + + const int greeting_width = 20 + diff_x_between_cards * 52 + card_width; + + if( animate ) { + t1 = QTime::currentTime(); + w = new QWidget( 0, "", Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool ); + QRect dg = KGlobalSettings::splashScreenDesktopGeometry(); + w->setBackgroundColor( Qt::darkGreen ); + w->setGeometry( dg.left() + ( dg.width() - greeting_width ) / 2, dg.top() + ( dg.height() - 180 ) / 2, greeting_width, 180); + w->show(); + qApp->processEvents(); + + p.begin( w ); + p.drawText(0, 150, greeting_width, 20, Qt::AlignCenter, + wait_message ); + + p.setFont(QFont("Times", 24)); + p.drawText(0, 0, greeting_width, 40, Qt::AlignCenter, + greeting); + + p.setPen(QPen(QColor(0, 0, 0), 4)); + p.setBrush(Qt::NoBrush); + p.drawRect(0, 0, greeting_width, 180); + p.flush(); + } + + setBackSide(back, true); + + for(int idx = 1; idx < 53; idx++) + { + // translate index to suit/rank + // this is necessary since kpoker uses another + // mapping in the pictures + int rank = (idx - 1) / 4; + if(rank != 0) + rank = 13 - rank; + int suit = 0; + switch((idx - 1) % 4) { + case 0: + suit = 0; + break; + case 1: + suit = 3; + break; + case 2: + suit = 2; + break; + case 3: + suit = 1; + break; + } + + imgname = KCardDialog::getCardPath(dir, idx); + image.load(imgname); + + if( image.isNull() || image.width() != card_width || image.height() != card_height ) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in (" << idx << ") " << dir << "\n"; + p.end(); + delete w; + card_width = old_card_width; + card_height = old_card_height; + + setBackSide(back, true); + return false; + } + + img[rank][suit].normal.convertFromImage(image); + KImageEffect::fade(image, 0.4, dimcolor); + img[rank][suit].inverted.convertFromImage(image); + + if( animate ) + { + if( idx > 1 ) + p.drawPixmap( 10 + ( idx - 1 ) * diff_x_between_cards, 45, back ); + p.drawPixmap( 10 + idx * diff_x_between_cards, 45, img[ rank ][ suit ].normal ); + p.flush(); + } + } + + if( animate ) + { + const int time_to_see = 900; + p.end(); + t2 = QTime::currentTime(); + if(t1.msecsTo(t2) < time_to_see) + usleep((time_to_see-t1.msecsTo(t2))*1000); + delete w; + } + + return true; +} + +bool cardMap::setBackSide( const QPixmap &pm, bool scale ) +{ + if (pm.isNull()) + return false; + + back = pm; + + if(scale && (back.width() != card_width || + back.height() != card_height)) + { + kdDebug(11111) << "scaling back!!\n"; + // scale to fit size + QWMatrix wm; + wm.scale(((float)(card_width))/back.width(), + ((float)(card_height))/back.height()); + back = back.xForm(wm); + } + + return true; +} + +int cardMap::CARDX() { + return self()->card_width; // 72; +} + +int cardMap::CARDY() { + return self()->card_height; // 96; +} + +QPixmap cardMap::backSide() const +{ + return back; +} + +QPixmap cardMap::image( Card::Rank _rank, Card::Suit _suit, bool inverted) const +{ + if( 1 <= _rank && _rank <= 13 + && 1 <= _suit && _suit <= 4 ) + { + if (inverted) + return img[ _rank - 1 ][ _suit - 1 ].inverted; + else + return img[ _rank - 1 ][ _suit - 1 ].normal; + } + else + { + kdError() << "access to invalid card " << int(_rank) << ", " << int(_suit) << endl; + } + return 0; +} + +cardMap *cardMap::self() { + assert(_self); + return _self; +} + diff --git a/kpat/cardmaps.h b/kpat/cardmaps.h new file mode 100644 index 00000000..8bc8d92c --- /dev/null +++ b/kpat/cardmaps.h @@ -0,0 +1,59 @@ +/***********************-*-C++-*-******** + + cardmaps.h defines pixmaps for playing cards + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#ifndef P_HACK_CARDMAP +#define P_HACK_CARDMAP + +#include "card.h" + +class cardMap +{ +public: + + static cardMap *self(); + cardMap(const QColor &dimcolor); + + static int CARDX(); + static int CARDY(); + + static const int NumColors = 4; + static const int CardsPerColor = 13; + + QPixmap image( Card::Rank _rank, Card::Suit _suit, bool inverted = false) const; + QPixmap backSide() const; + bool setCardDir( const QString &dir); + bool setBackSide( const QPixmap & _pix, bool scale = true); + +private: + + cardMap(); + struct + { + QPixmap normal; + QPixmap inverted; + } img[ CardsPerColor ][ NumColors ]; + QPixmap back; + QColor dimcolor; + int card_width, card_height; + + static cardMap *_self; +}; + +#endif diff --git a/kpat/clock.cpp b/kpat/clock.cpp new file mode 100644 index 00000000..7fcb94b4 --- /dev/null +++ b/kpat/clock.cpp @@ -0,0 +1,91 @@ +#include "clock.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Clock::Clock( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this); + deck->move(10, 10+dist_y*3); + deck->hide(); + + for (int i=0; i<12; i++) { + target[i] = new Pile(i+1, this); + const double ys[12] = { 0./96, 15./96, 52./96, 158./96, 264./96, 301./96, 316./96, 301./96, 264./96, 158./96, 52./96, 15./96}; + const double xs[12] = { 200./72, 280./72, 360./72, 400./72, 360./72, 280./72, 200./72, 120./72, 40./72, 0./72, 40./72, 120./72}; + target[i]->move(15 + cardMap::CARDX() * 24 / 5 + xs[i] * cardMap::CARDX(), 10 + ys[i] * cardMap::CARDY()); + target[i]->setCheckIndex(1); + target[i]->setTarget(true); + target[i]->setRemoveFlags(Pile::disallow); + } + + for (int i=0; i<8; i++) { + store[i] = new Pile(14+i, this); + store[i]->move(15+dist_x*(i%4), 10 + cardMap::CARDY() * 5 / 2 * (i/4)); + store[i]->setAddFlags(Pile::addSpread); + store[i]->setCheckIndex(0); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Clock::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +bool Clock::checkAdd( int ci, const Pile *c1, const CardList& c2) const +{ + Card *newone = c2.first(); + if (ci == 0) { + if (c1->isEmpty()) + return true; + + return (newone->rank() == c1->top()->rank() - 1); + } else { + if (c1->top()->suit() != newone->suit()) + return false; + if (c1->top()->rank() == Card::King) + return (newone->rank() == Card::Ace); + return (newone->rank() == c1->top()->rank() + 1); + } +} + +void Clock::deal() { + static const Card::Suit suits[12] = { Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, + Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, + Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, }; + static const Card::Rank ranks[12] = { Card::Nine, Card::Ten, Card::Jack, Card::Queen, + Card::King, Card::Two, Card::Three, Card::Four, + Card::Five, Card::Six, Card::Seven, Card::Eight}; + + int j = 0; + while (!deck->isEmpty()) { + Card *c = deck->nextCard(); + for (int i = 0; i < 12; i++) + if (c->rank() == ranks[i] && c->suit() == suits[i]) { + target[i]->add(c, false, true); + c = 0; + break; + } + if (c) + store[j++]->add(c, false, true); + if (j == 8) + j = 0; + } +} + +static class LocalDealerInfo11 : public DealerInfo +{ +public: + LocalDealerInfo11() : DealerInfo(I18N_NOOP("G&randfather's Clock"), 11) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Clock(parent); } +} gfi11; + +#include "clock.moc" diff --git a/kpat/clock.h b/kpat/clock.h new file mode 100644 index 00000000..42521862 --- /dev/null +++ b/kpat/clock.h @@ -0,0 +1,24 @@ +#ifndef CLOCK_H +#define CLOCK_H + +#include "dealer.h" + +class Clock : public Dealer { + Q_OBJECT + +public: + Clock( KMainWindow* parent=0, const char* name=0); + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool startAutoDrop() { return false; } + +public slots: + void deal(); + virtual void restart(); + +private: + Pile* store[8]; + Pile* target[12]; + Deck *deck; +}; + +#endif diff --git a/kpat/computation.cpp b/kpat/computation.cpp new file mode 100644 index 00000000..22f104aa --- /dev/null +++ b/kpat/computation.cpp @@ -0,0 +1,120 @@ +/***********************-*-C++-*-******** + + computation.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// This one was discussed on the newsgroup rec.games.abstract +// +****************************************/ + +#include "computation.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Computation::Computation( KMainWindow *parent, const char *name ) + :Dealer( parent, name) +{ + deck = Deck::new_deck(this); + deck->hide(); + + for (int i = 0; i < 4; i++) { + play[i] = new Pile(1 + i, this); + play[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10 + cardMap::CARDY() * 15 / 10); + play[i]->setAddFlags(Pile::addSpread); + play[i]->setCheckIndex(1); + + target[i] = new Pile(5 + i, this); + target[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10); + target[i]->setRemoveFlags(Pile::disallow); + target[i]->setCheckIndex(0); + target[i]->setTarget(true); + } + + pile = new Pile(13, this); + pile->setAddFlags(Pile::disallow); + pile->setRemoveFlags(Pile::autoTurnTop); + pile->move(10, 10); + + setActions(Dealer::Demo | Dealer::Hint); +} + +void Computation::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Computation::deal() { + while (!deck->isEmpty()) { + Card *c = deck->nextCard(); + pile->add(c, true, false); + } + // no animation + pile->top()->turn(true); +} + +inline bool matches(const CardList &cl, Card *start, int offset) +{ + Card *before = start; // maybe 0 for ignore first card + for (CardList::ConstIterator it = cl.begin(); it != cl.end(); ++it) + { + if (before && (*it)->rank() % 13 != (before->rank() + offset) % 13) + return false; + before = *it; + } + return true; +} + +bool Computation::checkStore( const Pile*, const CardList& cl) const +{ + if (cl.count() != 1) + return false; + return (cl.first()->source()->index() == 13); +} + +bool Computation::checkAdd( int index, const Pile* c1, const CardList& cl) const +{ + if (index == 1) + return checkStore(c1, cl); + + assert(c1->index() >= 5 && c1->index() <= 8); + + if ( c1->top() && c1->top()->rank() == Card::King) // finished + return false; + + if ( c1->cardsLeft() == 13 ) + return false; + + int offset = c1->index() - 4; + + if (c1->isEmpty()) { + Card::Rank start = static_cast(Card::Ace + (offset - 1)); + return cl.first()->rank() == start && matches(cl, 0, offset); + } + + return matches(cl, c1->top(), offset); +} + +static class LocalDealerInfo6 : public DealerInfo +{ +public: + LocalDealerInfo6() : DealerInfo(I18N_NOOP("&Calculation"), 6) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Computation(parent); } +} ldi6; + +#include "computation.moc" diff --git a/kpat/computation.h b/kpat/computation.h new file mode 100644 index 00000000..3b9b3fbc --- /dev/null +++ b/kpat/computation.h @@ -0,0 +1,53 @@ +/***********************-*-C++-*-******** + + computation.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// This one was discussed on the newsgroup rec.games.abstract +// + + +****************************************/ + +#ifndef P_COMPUTATION_H +#define P_COMPUTATION_H + +#include "dealer.h" + +class Computation : public Dealer { + Q_OBJECT + +public: + Computation( KMainWindow *parent = 0, const char *name=0 ); + + virtual void restart(); + +private: + Card *getCardByValue( char v ); + void deal(); + + bool checkStore(const Pile* c1, const CardList& c2) const; + virtual bool checkAdd( int index, const Pile* c1, const CardList& c2) const; + + Deck *deck; + Pile *pile; + + Pile *play[4]; + Pile *target[4]; +}; + +#endif diff --git a/kpat/copyright.h b/kpat/copyright.h new file mode 100644 index 00000000..5e1f2b5b --- /dev/null +++ b/kpat/copyright.h @@ -0,0 +1,154 @@ +/* + + + Patience -- a general class for patience card games + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + except for the card bitmaps contained in the files + + newface.bm rank.bm suit.bm + +which are + + + (c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc. + (c) Copyright 1990, David Lemke and Network Computing Devices Inc. + + + + See the statement below for the terms of the copyright. + + + */ + + + + + + +/**** Copyright statement from spider/copyright.h follows this line ****/ + + + + + +/* + * Copyright 1990 Heather Rose and Sun Microsystems, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the names of Donald Woods and Sun Microsystems not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Heather Rose and Sun Microsystems not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Heather Rose and Sun Microsystems make + * no representations about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT + * SHALL HEATHER ROSE OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + * + * Author: + * Heather Rose + * hrose@sun.com + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, CA 94043 + */ + +/* + * Copyright 1990 David Lemke and Network Computing Devices + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Network Computing + * Devices makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: + * Dave Lemke + * lemke@ncd.com + * + * Network Computing Devices, Inc + * 350 North Bernardo Ave + * Mountain View, CA 94043 + * + * @(#)copyright.h 2.2 90/04/27 + * + */ + +/* +% Copyright (c) 1989, Donald R. Woods and Sun Microsystems, Inc. +% +% Permission to use, copy, modify, distribute, and sell this software and its +% documentation for any purpose is hereby granted without fee, provided that +% the above copyright notice appear in all copies and that both that copyright +% notice and this permission notice appear in supporting documentation, and +% that the names of Donald Woods and Sun Microsystems not be used in +% advertising or publicity pertaining to distribution of the software without +% specific, written prior permission. Donald Woods and Sun Microsystems make +% no representations about the suitability of this software for any purpose. +% It is provided "as is" without express or implied warranty. +% +% THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +% INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +% SHALL DONALD WOODS OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR +% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +% DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +% TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +% OF THIS SOFTWARE. +% +% History: Spider is a solitaire card game that can be found in various books +% of same; the rules are presumed to be in the public domain. The author's +% first computer implementation was on the Stanford Artificial Intelligence Lab +% system (SAIL). It was later ported to the Xerox Development Environment. +% The card images are loosely based on scanned-in images but were largely +% redrawn by the author with help from Larry Rosenberg. +% +% This program is written entirely in NeWS and runs on OPEN WINDOWS 1.0. +% It could be made to run much faster if parts of it were written in C, using +% NeWS mainly for its display and input capabilities, but that is left as an +% exercise for the reader. Spider may also run with little or no modification +% on subsequent releases of OPEN WINDOWS, but no guarantee is made on this +% point (nor any other; see above!). To run Spider, feed this file to 'psh'. +% +% Author: Don Woods +% woods@sun.com +% +% Sun Microsystems, Inc. +% 2550 Garcia Avenue +% Mountain View, CA 94043 +*/ diff --git a/kpat/dealer.cpp b/kpat/dealer.cpp new file mode 100644 index 00000000..2100ae2e --- /dev/null +++ b/kpat/dealer.cpp @@ -0,0 +1,1455 @@ +#include "dealer.h" +#include +#include +#include "deck.h" +#include +#include "kmainwindow.h" +#include +#include +#include +#include +#include +#include "cardmaps.h" +#include "speeds.h" +#include +#include "version.h" + + +// ================================================================ +// class MoveHint + + +MoveHint::MoveHint(Card *card, Pile *to, bool d) +{ + m_card = card; + m_to = to; + m_dropiftarget = d; +} + + +// ================================================================ +// class DealerInfoList + + +DealerInfoList *DealerInfoList::_self = 0; +static KStaticDeleter dl; + + +DealerInfoList *DealerInfoList::self() +{ + if (!_self) + _self = dl.setObject(_self, new DealerInfoList()); + return _self; +} + +void DealerInfoList::add(DealerInfo *dealer) +{ + list.append(dealer); +} + + +// ================================================================ +// class Dealer + + +Dealer *Dealer::s_instance = 0; + + +Dealer::Dealer( KMainWindow* _parent , const char* _name ) + : QCanvasView( 0, _parent, _name ), + towait(0), + myActions(0), + ademo(0), + ahint(0), + aredeal(0), + takeTargets(false), + _won(false), + _waiting(0), + stop_demo_next(false), + _autodrop(true), + _gameRecorded(false) +{ + setResizePolicy(QScrollView::Manual); + setVScrollBarMode(AlwaysOff); + setHScrollBarMode(AlwaysOff); + + setGameNumber(kapp->random()); + myCanvas.setAdvancePeriod(30); + // myCanvas.setBackgroundColor( darkGreen ); + setCanvas(&myCanvas); + myCanvas.setDoubleBuffering(true); + + undoList.setAutoDelete(true); + + demotimer = new QTimer(this); + + connect(demotimer, SIGNAL(timeout()), SLOT(demo())); + + assert(!s_instance); + s_instance = this; +} + + +const Dealer *Dealer::instance() +{ + return s_instance; +} + + +void Dealer::setBackgroundPixmap(const QPixmap &background, const QColor &midcolor) +{ + _midcolor = midcolor; + canvas()->setBackgroundPixmap(background); + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) { + (*it)->resetCache(); + (*it)->initSizes(); + } +} + +void Dealer::setupActions() { + + QPtrList actionlist; + + kdDebug(11111) << "setupActions " << actions() << endl; + + if (actions() & Dealer::Hint) { + + ahint = new KAction( i18n("&Hint"), QString::fromLatin1("wizard"), Key_H, this, + SLOT(hint()), + parent()->actionCollection(), "game_hint"); + actionlist.append(ahint); + } else + ahint = 0; + + if (actions() & Dealer::Demo) { + ademo = new KToggleAction( i18n("&Demo"), QString::fromLatin1("1rightarrow"), CTRL+Key_D, this, + SLOT(toggleDemo()), + parent()->actionCollection(), "game_demo"); + actionlist.append(ademo); + } else + ademo = 0; + + if (actions() & Dealer::Redeal) { + aredeal = new KAction (i18n("&Redeal"), QString::fromLatin1("queue"), 0, this, + SLOT(redeal()), + parent()->actionCollection(), "game_redeal"); + actionlist.append(aredeal); + } else + aredeal = 0; + + parent()->guiFactory()->plugActionList( parent(), QString::fromLatin1("game_actions"), actionlist); +} + +Dealer::~Dealer() +{ + if (!_won) + countLoss(); + clearHints(); + parent()->guiFactory()->unplugActionList( parent(), QString::fromLatin1("game_actions")); + + while (!piles.isEmpty()) + delete piles.first(); // removes itself + + if (s_instance == this) + s_instance = 0; +} + +KMainWindow *Dealer::parent() const +{ + return dynamic_cast(QCanvasView::parent()); +} + + +// ---------------------------------------------------------------- + + +void Dealer::hint() +{ + unmarkAll(); + clearHints(); + getHints(); + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + mark((*it)->card()); + clearHints(); + canvas()->update(); +} + + +void Dealer::getHints() +{ + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) + { + if (!takeTargetForHints() && (*it)->target()) + continue; + + Pile *store = *it; + if (store->isEmpty()) + continue; +// kdDebug(11111) << "trying " << store->top()->name() << endl; + + CardList cards = store->cards(); + while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin()); + + CardList::Iterator iti = cards.begin(); + while (iti != cards.end()) + { + if (store->legalRemove(*iti)) { +// kdDebug(11111) << "could remove " << (*iti)->name() << endl; + for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit) + { + Pile *dest = *pit; + if (dest == store) + continue; + if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target()) + continue; + if (!dest->legalAdd(cards)) + continue; + + bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards ); + if (!takeTargetForHints() && dest->target()) + newHint(new MoveHint(*iti, dest)); + else { + store->hideCards(cards); + // if it could be here as well, then it's no use + if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards)) + newHint(new MoveHint(*iti, dest)); + else { + if (old_prefer && !checkPrefering( store->checkIndex(), + store, cards )) + { // if checkPrefers says so, we add it nonetheless + newHint(new MoveHint(*iti, dest)); + } + } + store->unhideCards(cards); + } + } + } + cards.remove(iti); + iti = cards.begin(); + } + } +} + +bool Dealer::checkPrefering( int /*checkIndex*/, const Pile *, const CardList& ) const +{ + return false; +} + +void Dealer::clearHints() +{ + for (HintList::Iterator it = hints.begin(); it != hints.end(); ++it) + delete *it; + hints.clear(); +} + +void Dealer::newHint(MoveHint *mh) +{ + hints.append(mh); +} + +bool Dealer::isMoving(Card *c) const +{ + return movingCards.find(c) != movingCards.end(); +} + +void Dealer::contentsMouseMoveEvent(QMouseEvent* e) +{ + if (movingCards.isEmpty()) + return; + + moved = true; + + for (CardList::Iterator it = movingCards.begin(); it != movingCards.end(); ++it) + { + (*it)->moveBy(e->pos().x() - moving_start.x(), + e->pos().y() - moving_start.y()); + } + + PileList sources; + QCanvasItemList list = canvas()->collisions(movingCards.first()->rect()); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->isFaceUp()) + continue; + if (c->source() == movingCards.first()->source()) + continue; + if (sources.findIndex(c->source()) != -1) + continue; + sources.append(c->source()); + } else { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = static_cast(*it); + if (p->isEmpty() && !sources.contains(p)) + sources.append(p); + } else { + kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl; + } + } + } + + // TODO some caching of the results + unmarkAll(); + + for (PileList::Iterator it = sources.begin(); it != sources.end(); ++it) + { + bool b = (*it)->legalAdd(movingCards); + if (b) { + if ((*it)->isEmpty()) { + (*it)->setSelected(true); + marked.append(*it); + } else { + mark((*it)->top()); + } + } + } + + moving_start = e->pos(); + canvas()->update(); +} + +void Dealer::mark(Card *c) +{ + c->setSelected(true); + if (!marked.contains(c)) + marked.append(c); +} + +void Dealer::unmarkAll() +{ + for (QCanvasItemList::Iterator it = marked.begin(); it != marked.end(); ++it) + { + (*it)->setSelected(false); + } + marked.clear(); +} + +void Dealer::contentsMousePressEvent(QMouseEvent* e) +{ + unmarkAll(); + stopDemo(); + if (waiting()) + return; + + QCanvasItemList list = canvas()->collisions(e->pos()); + + kdDebug(11111) << "mouse pressed " << list.count() << " " << canvas()->allItems().count() << endl; + moved = false; + + if (!list.count()) + return; + + if (e->button() == LeftButton) { + if (list.first()->rtti() == Card::RTTI) { + Card *c = dynamic_cast(list.first()); + assert(c); + CardList mycards = c->source()->cardPressed(c); + for (CardList::Iterator it = mycards.begin(); it != mycards.end(); ++it) + (*it)->setAnimated(false); + movingCards = mycards; + moving_start = e->pos(); + } + return; + } + + if (e->button() == RightButton) { + if (list.first()->rtti() == Card::RTTI) { + Card *preview = dynamic_cast(list.first()); + assert(preview); + if (!preview->animated() && !isMoving(preview)) + preview->getUp(); + } + return; + } + + // if it's nothing else, we move the cards back + contentsMouseReleaseEvent(e); + +} + +class Hit { +public: + Pile *source; + QRect intersect; + bool top; +}; +typedef QValueList HitList; + +void Dealer::contentsMouseReleaseEvent( QMouseEvent *e) +{ + if (!moved) { + if (!movingCards.isEmpty()) { + movingCards.first()->source()->moveCardsBack(movingCards); + movingCards.clear(); + } + QCanvasItemList list = canvas()->collisions(e->pos()); + if (list.isEmpty()) + return; + QCanvasItemList::Iterator it = list.begin(); + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->animated()) { + if ( cardClicked(c) ) { + countGame(); + } + takeState(); + canvas()->update(); + } + return; + } + if ((*it)->rtti() == Pile::RTTI) { + Pile *c = dynamic_cast(*it); + assert(c); + pileClicked(c); + takeState(); + canvas()->update(); + return; + } + } + + if (!movingCards.count()) + return; + Card *c = static_cast(movingCards.first()); + assert(c); + + unmarkAll(); + + QCanvasItemList list = canvas()->collisions(movingCards.first()->rect()); + HitList sources; + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + if (!c->isFaceUp()) + continue; + if (c->source() == movingCards.first()->source()) + continue; + Hit t; + t.source = c->source(); + t.intersect = c->rect().intersect(movingCards.first()->rect()); + t.top = (c == c->source()->top()); + + bool found = false; + for (HitList::Iterator hi = sources.begin(); hi != sources.end(); ++hi) + { + if ((*hi).source == c->source()) { + found = true; + if ((*hi).intersect.width() * (*hi).intersect.height() > + t.intersect.width() * t.intersect.height()) + { + (*hi).intersect = t.intersect; + (*hi).top |= t.top; + } + } + } + if (found) + continue; + + sources.append(t); + } else { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = static_cast(*it); + if (p->isEmpty()) + { + Hit t; + t.source = p; + t.intersect = p->rect().intersect(movingCards.first()->rect()); + t.top = true; + sources.append(t); + } + } else { + kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl; + } + } + } + + for (HitList::Iterator it = sources.begin(); it != sources.end(); ) + { + if (!(*it).source->legalAdd(movingCards)) + it = sources.remove(it); + else + ++it; + } + + if (sources.isEmpty()) { + c->source()->moveCardsBack(movingCards); + } else { + HitList::Iterator best = sources.begin(); + HitList::Iterator it = best; + for (++it; it != sources.end(); ++it ) + { + if ((*it).intersect.width() * (*it).intersect.height() > + (*best).intersect.width() * (*best).intersect.height() + || ((*it).top && !(*best).top)) + { + best = it; + } + } + countGame(); + c->source()->moveCards(movingCards, (*best).source); + takeState(); + } + movingCards.clear(); + canvas()->update(); +} + +void Dealer::contentsMouseDoubleClickEvent( QMouseEvent*e ) +{ + stopDemo(); + unmarkAll(); + if (waiting()) + return; + + if (!movingCards.isEmpty()) { + movingCards.first()->source()->moveCardsBack(movingCards); + movingCards.clear(); + } + QCanvasItemList list = canvas()->collisions(e->pos()); + if (list.isEmpty()) + return; + QCanvasItemList::Iterator it = list.begin(); + if ((*it)->rtti() != Card::RTTI) + return; + Card *c = dynamic_cast(*it); + assert(c); + if (!c->animated()) { + if ( cardDblClicked(c) ) { + countGame(); + } + takeState(); + } +} + +QSize Dealer::minimumCardSize() const +{ + return minsize; +} + +void Dealer::resizeEvent(QResizeEvent *e) +{ + int x = width(); + int y = height(); + int hs = horizontalScrollBar()->sizeHint().height(); + int vs = verticalScrollBar()->sizeHint().width(); + + int mx = minsize.width(); + int my = minsize.height(); + + int dx = x; + int dy = y; + bool showh = false; + bool showv = false; + + if (mx > x) { + dx = mx; + if (my + vs < y) + dy -= vs; + else { + showv = true; + } + showh = true; + } else if (my > y) { + dy = my; + if (mx + hs < x) + dx -= hs; + else + showh = true; + showv = true; + } + canvas()->resize(dx, dy); + resizeContents(dx, dy); + setVScrollBarMode(showv ? AlwaysOn : AlwaysOff); + setHScrollBarMode(showh ? AlwaysOn : AlwaysOff); + + if (!e) + updateScrollBars(); + else + QCanvasView::resizeEvent(e); +} + +bool Dealer::cardClicked(Card *c) { + return c->source()->cardClicked(c); +} + +void Dealer::pileClicked(Pile *c) { + c->cardClicked(0); +} + +bool Dealer::cardDblClicked(Card *c) +{ + if (c->source()->cardDblClicked(c)) + return true; + + if (c->animated()) + return false; + + if (c == c->source()->top() && c->realFace()) { + Pile *tgt = findTarget(c); + if (tgt) { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, tgt); + canvas()->update(); + return true; + } + } + return false; +} + +void Dealer::startNew() +{ + if (!_won) + countLoss(); + if ( ahint ) + ahint->setEnabled( true ); + if ( ademo ) + ademo->setEnabled( true ); + if ( aredeal ) + aredeal->setEnabled( true ); + toldAboutLostGame = false; + minsize = QSize(0,0); + _won = false; + _waiting = 0; + _gameRecorded=false; + kdDebug(11111) << "startNew stopDemo\n"; + stopDemo(); + kdDebug(11111) << "startNew unmarkAll\n"; + unmarkAll(); + kdDebug(11111) << "startNew setAnimated(false)\n"; + QCanvasItemList list = canvas()->allItems(); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) { + if ((*it)->rtti() == Card::RTTI) + static_cast(*it)->disconnect(); + + (*it)->setAnimated(true); + (*it)->setAnimated(false); + } + + undoList.clear(); + emit undoPossible(false); + emit updateMoves(); + kdDebug(11111) << "startNew restart\n"; + restart(); + takeState(); + Card *towait = 0; + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) { + if ((*it)->rtti() == Card::RTTI) { + towait = static_cast(*it); + if (towait->animated()) + break; + } + } + + kdDebug(11111) << "startNew takeState\n"; + if (!towait) + takeState(); + else + connect(towait, SIGNAL(stoped(Card*)), SLOT(slotTakeState(Card *))); + resizeEvent(0); +} + +void Dealer::slotTakeState(Card *c) { + if (c) + c->disconnect(); + takeState(); +} + +void Dealer::enlargeCanvas(QCanvasRectangle *c) +{ + if (!c->isVisible() || c->animated()) + return; + + bool changed = false; + + if (c->x() + c->width() + 10 > minsize.width()) { + minsize.setWidth(int(c->x()) + c->width() + 10); + changed = true; + } + if (c->y() + c->height() + 10 > minsize.height()) { + minsize.setHeight(int(c->y()) + c->height() + 10); + changed = true; + } + if (changed) + resizeEvent(0); +} + +class CardState { +public: + Card *it; + Pile *source; + double x; + double y; + double z; + bool faceup; + bool tookdown; + int source_index; + CardState() {} +public: + // as every card is only once we can sort after the card. + // < is the same as <= in that context. == is different + bool operator<(const CardState &rhs) const { return it < rhs.it; } + bool operator<=(const CardState &rhs) const { return it <= rhs.it; } + bool operator>(const CardState &rhs) const { return it > rhs.it; } + bool operator>=(const CardState &rhs) const { return it > rhs.it; } + bool operator==(const CardState &rhs) const { + return (it == rhs.it && source == rhs.source && x == rhs.x && + y == rhs.y && z == rhs.z && faceup == rhs.faceup + && source_index == rhs.source_index && tookdown == rhs.tookdown); + } + void fillNode(QDomElement &e) const { + e.setAttribute("value", it->rank()); + e.setAttribute("suit", it->suit()); + e.setAttribute("source", source->index()); + e.setAttribute("x", x); + e.setAttribute("y", y); + e.setAttribute("z", z); + e.setAttribute("faceup", faceup); + e.setAttribute("tookdown", tookdown); + e.setAttribute("source_index", source_index); + } +}; + +typedef class QValueList CardStateList; + +bool operator==( const State & st1, const State & st2) { + return st1.cards == st2.cards && st1.gameData == st2.gameData; +} + +State *Dealer::getState() +{ + QCanvasItemList list = canvas()->allItems(); + State * st = new State; + + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Card::RTTI) { + Card *c = dynamic_cast(*it); + assert(c); + CardState s; + s.it = c; + s.source = c->source(); + if (!s.source) { + kdDebug(11111) << c->name() << " has no parent\n"; + assert(false); + } + s.source_index = c->source()->indexOf(c); + s.x = c->realX(); + s.y = c->realY(); + s.z = c->realZ(); + s.faceup = c->realFace(); + s.tookdown = c->takenDown(); + st->cards.append(s); + } + } + qHeapSort(st->cards); + + // Game specific information + st->gameData = getGameState( ); + + return st; +} + +void Dealer::setState(State *st) +{ + CardStateList * n = &st->cards; + QCanvasItemList list = canvas()->allItems(); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = dynamic_cast(*it); + assert(p); + CardList cards = p->cards(); + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) + (*it)->setTakenDown(p->target()); + p->clear(); + } + } + + for (CardStateList::ConstIterator it = n->begin(); it != n->end(); ++it) + { + Card *c = (*it).it; + CardState s = *it; + bool target = c->takenDown(); // abused + s.source->add(c, s.source_index); + c->setVisible(s.source->isVisible()); + c->setAnimated(false); + c->setX(s.x); + c->setY(s.y); + c->setZ(int(s.z)); + c->setTakenDown(s.tookdown || (target && !s.source->target())); + c->turn(s.faceup); + } + + // restore game-specific information + setGameState( st->gameData ); + + delete st; + canvas()->update(); +} + +void Dealer::takeState() +{ + kdDebug(11111) << "takeState\n"; + + State *n = getState(); + + if (!undoList.count()) { + emit updateMoves(); + undoList.append(n); + } else { + State *old = undoList.last(); + + if (*old == *n) { + delete n; + n = 0; + } else { + emit updateMoves(); + undoList.append(n); + } + } + + if (n) { + if (isGameWon()) { + won(); + return; + } + else if (isGameLost() && !toldAboutLostGame) { + if ( ahint ) + ahint->setEnabled( false ); + if ( ademo ) + ademo->setEnabled( false ); + if ( aredeal ) + aredeal->setEnabled( false ); + QTimer::singleShot(400, this, SIGNAL(gameLost())); + toldAboutLostGame = true; + return; + } + } + if (!demoActive() && !waiting()) + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); + + emit undoPossible(undoList.count() > 1 && !waiting()); +} + +void Dealer::saveGame(QDomDocument &doc) { + QDomElement dealer = doc.createElement("dealer"); + doc.appendChild(dealer); + dealer.setAttribute("id", _id); + dealer.setAttribute("number", QString::number(gameNumber())); + QString data = getGameState(); + if (!data.isEmpty()) + dealer.setAttribute("data", data); + dealer.setAttribute("moves", QString::number(getMoves())); + + bool taken[1000]; + memset(taken, 0, sizeof(taken)); + + QCanvasItemList list = canvas()->allItems(); + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) { + Pile *p = dynamic_cast(*it); + assert(p); + if (taken[p->index()]) { + kdDebug(11111) << "pile index " << p->index() << " taken twice\n"; + return; + } + taken[p->index()] = true; + + QDomElement pile = doc.createElement("pile"); + pile.setAttribute("index", p->index()); + + CardList cards = p->cards(); + for (CardList::Iterator it = cards.begin(); + it != cards.end(); + ++it) + { + QDomElement card = doc.createElement("card"); + card.setAttribute("suit", (*it)->suit()); + card.setAttribute("value", (*it)->rank()); + card.setAttribute("faceup", (*it)->isFaceUp()); + card.setAttribute("x", (*it)->realX()); + card.setAttribute("y", (*it)->realY()); + card.setAttribute("z", (*it)->realZ()); + card.setAttribute("name", (*it)->name()); + pile.appendChild(card); + } + dealer.appendChild(pile); + } + } + + /* + QDomElement eList = doc.createElement("undo"); + + QPtrListIterator it(undoList); + for (; it.current(); ++it) + { + State *n = it.current(); + QDomElement state = doc.createElement("state"); + if (!n->gameData.isEmpty()) + state.setAttribute("data", n->gameData); + QDomElement cards = doc.createElement("cards"); + for (QValueList::ConstIterator it2 = n->cards.begin(); + it2 != n->cards.end(); ++it2) + { + QDomElement item = doc.createElement("item"); + (*it2).fillNode(item); + cards.appendChild(item); + } + state.appendChild(cards); + eList.appendChild(state); + } + dealer.appendChild(eList); + */ + // kdDebug(11111) << doc.toString() << endl; +} + +void Dealer::openGame(QDomDocument &doc) +{ + unmarkAll(); + QDomElement dealer = doc.documentElement(); + + setGameNumber(dealer.attribute("number").toULong()); + undoList.clear(); + + QDomNodeList piles = dealer.elementsByTagName("pile"); + + QCanvasItemList list = canvas()->allItems(); + + CardList cards; + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + if ((*it)->rtti() == Card::RTTI) + cards.append(static_cast(*it)); + + Deck::deck()->collectAndShuffle(); + + for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) + { + if ((*it)->rtti() == Pile::RTTI) + { + Pile *p = dynamic_cast(*it); + assert(p); + + for (uint i = 0; i < piles.count(); ++i) + { + QDomElement pile = piles.item(i).toElement(); + if (pile.attribute("index").toInt() == p->index()) + { + QDomNodeList pcards = pile.elementsByTagName("card"); + for (uint j = 0; j < pcards.count(); ++j) + { + QDomElement card = pcards.item(j).toElement(); + Card::Suit s = static_cast(card.attribute("suit").toInt()); + Card::Rank v = static_cast(card.attribute("value").toInt()); + + for (CardList::Iterator it2 = cards.begin(); + it2 != cards.end(); ++it2) + { + if ((*it2)->suit() == s && (*it2)->rank() == v) { + if (QString((*it2)->name()) == "Diamonds Eight") { + kdDebug(11111) << i << " " << j << endl; + } + p->add(*it2); + (*it2)->setAnimated(false); + (*it2)->turn(card.attribute("faceup").toInt()); + (*it2)->setX(card.attribute("x").toInt()); + (*it2)->setY(card.attribute("y").toInt()); + (*it2)->setZ(card.attribute("z").toInt()); + (*it2)->setVisible(p->isVisible()); + cards.remove(it2); + break; + } + } + } + } + } + } + } + setGameState( dealer.attribute("data") ); + + if (undoList.count() > 1) { + setState(undoList.take(undoList.count() - 1)); + takeState(); // copying it again + emit undoPossible(undoList.count() > 1); + } + + emit updateMoves(); + takeState(); +} + +void Dealer::undo() +{ + unmarkAll(); + stopDemo(); + if (undoList.count() > 1) { + undoList.removeLast(); // the current state + setState(undoList.take(undoList.count() - 1)); + emit updateMoves(); + takeState(); // copying it again + emit undoPossible(undoList.count() > 1); + if ( toldAboutLostGame ) { // everything's possible again + if ( ahint ) + ahint->setEnabled( true ); + if ( ademo ) + ademo->setEnabled( true ); + toldAboutLostGame = false; + } + } +} + +Pile *Dealer::findTarget(Card *c) +{ + if (!c) + return 0; + + CardList empty; + empty.append(c); + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + { + if (!(*it)->target()) + continue; + if ((*it)->legalAdd(empty)) + return *it; + } + return 0; +} + +void Dealer::setWaiting(bool w) +{ + if (w) + _waiting++; + else + _waiting--; + emit undoPossible(!waiting()); + kdDebug(11111) << "setWaiting " << w << " " << _waiting << endl; +} + +void Dealer::setAutoDropEnabled(bool a) +{ + _autodrop = a; + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); +} + +bool Dealer::startAutoDrop() +{ + if (!autoDrop()) + return false; + + QCanvasItemList list = canvas()->allItems(); + + for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it) + if ((*it)->animated()) { + QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop())); + return true; + } + + kdDebug(11111) << "startAutoDrop\n"; + + unmarkAll(); + clearHints(); + getHints(); + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) { + MoveHint *mh = *it; + if (mh->pile()->target() && mh->dropIfTarget() && !mh->card()->takenDown()) { + setWaiting(true); + Card *t = mh->card(); + CardList cards = mh->card()->source()->cards(); + while (cards.count() && cards.first() != t) cards.remove(cards.begin()); + t->setAnimated(false); + t->turn(true); + int x = int(t->x()); + int y = int(t->y()); + t->source()->moveCards(cards, mh->pile()); + t->move(x, y); + kdDebug(11111) << "autodrop " << t->name() << endl; + t->moveTo(int(t->source()->x()), int(t->source()->y()), int(t->z()), STEPS_AUTODROP); + connect(t, SIGNAL(stoped(Card*)), SLOT(waitForAutoDrop(Card*))); + return true; + } + } + clearHints(); + return false; +} + +void Dealer::waitForAutoDrop(Card * c) { + kdDebug(11111) << "waitForAutoDrop " << c->name() << endl; + setWaiting(false); + c->disconnect(); + takeState(); +} + +long Dealer::gameNumber() const +{ + return gamenumber; +} + +void Dealer::setGameNumber(long gmn) +{ + // Deal in the range of 1 to INT_MAX. + gamenumber = ((gmn < 1) ? 1 : ((gmn > 0x7FFFFFFF) ? 0x7FFFFFFF : gmn)); +} + +void Dealer::addPile(Pile *p) +{ + piles.append(p); +} + +void Dealer::removePile(Pile *p) +{ + piles.remove(p); +} + +void Dealer::stopDemo() +{ + kdDebug(11111) << "stopDemo " << waiting() << " " << stop_demo_next << endl; + if (waiting()) { + stop_demo_next = true; + return; + } else stop_demo_next = false; + + if (towait == (Card*)-1) + towait = 0; + + if (towait) { + towait->disconnect(); + towait = 0; + } + demotimer->stop(); + if (ademo) + ademo->setChecked(false); +} + +bool Dealer::demoActive() const +{ + return (towait || demotimer->isActive()); +} + +void Dealer::toggleDemo() +{ + if (demoActive()) { + stopDemo(); + } else + demo(); +} + +class CardPtr +{ + public: + Card *ptr; +}; + +bool operator <(const CardPtr &p1, const CardPtr &p2) +{ + return ( p1.ptr->z() < p2.ptr->z() ); +} + +void Dealer::won() +{ + if (_won) + return; + _won = true; + + // update score, 'win' in demo mode also counts (keep it that way?) + { // wrap in own scope to make KConfigGroupSave work + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int n = config->readUnsignedNumEntry(QString("won%1").arg(_id),0) + 1; + config->writeEntry(QString("won%1").arg(_id),n); + n = config->readUnsignedNumEntry(QString("winstreak%1").arg(_id),0) + 1; + config->writeEntry(QString("winstreak%1").arg(_id),n); + unsigned int m = config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(_id),0); + if (n>m) + config->writeEntry(QString("maxwinstreak%1").arg(_id),n); + config->writeEntry(QString("loosestreak%1").arg(_id),0); + } + + // sort cards by increasing z + QCanvasItemList list = canvas()->allItems(); + QValueList cards; + for (QCanvasItemList::ConstIterator it=list.begin(); it!=list.end(); ++it) + if ((*it)->rtti() == Card::RTTI) { + CardPtr p; + p.ptr = dynamic_cast(*it); + assert(p.ptr); + cards.push_back(p); + } + qHeapSort(cards); + + // disperse the cards everywhere + QRect can(0, 0, canvas()->width(), canvas()->height()); + QValueList::ConstIterator it = cards.begin(); + for (; it != cards.end(); ++it) { + (*it).ptr->turn(true); + QRect p(0, 0, (*it).ptr->width(), (*it).ptr->height()); + int x, y; + do { + x = 3*canvas()->width()/2 - kapp->random() % (canvas()->width() * 2); + y = 3*canvas()->height()/2 - (kapp->random() % (canvas()->height() * 2)); + p.moveTopLeft(QPoint(x, y)); + } while (can.intersects(p)); + + (*it).ptr->moveTo( x, y, 0, STEPS_WON); + } + + bool demo = demoActive(); + stopDemo(); + canvas()->update(); + emit gameWon(demo); +} + +MoveHint *Dealer::chooseHint() +{ + if (hints.isEmpty()) + return 0; + + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if ((*it)->pile()->target() && (*it)->dropIfTarget()) + return *it; + } + + return hints[randseq.getLong(hints.count())]; +} + +void Dealer::demo() { + if (waiting()) + return; + + if (stop_demo_next) { + stopDemo(); + return; + } + stop_demo_next = false; + unmarkAll(); + towait = (Card*)-1; + clearHints(); + getHints(); + demotimer->stop(); + + MoveHint *mh = chooseHint(); + if (mh) { + // assert(mh->card()->source()->legalRemove(mh->card())); + + CardList empty; + CardList cards = mh->card()->source()->cards(); + bool after = false; + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + if (*it == mh->card()) + after = true; + if (after) + empty.append(*it); + } + + assert(!empty.isEmpty()); + + int *oldcoords = new int[2*empty.count()]; + int i = 0; + + for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) { + Card *t = *it; + Q_ASSERT(!t->animated()); + t->setAnimated(false); + t->turn(true); + oldcoords[i++] = int(t->realX()); + oldcoords[i++] = int(t->realY()); + } + + assert(mh->card()->source() != mh->pile()); + // assert(mh->pile()->legalAdd(empty)); + + mh->card()->source()->moveCards(empty, mh->pile()); + + i = 0; + + for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) { + Card *t = *it; + int x1 = oldcoords[i++]; + int y1 = oldcoords[i++]; + int x2 = int(t->realX()); + int y2 = int(t->realY()); + t->move(x1, y1); + t->moveTo(x2, y2, int(t->z()), STEPS_DEMO); + } + + delete [] oldcoords; + + newDemoMove(mh->card()); + + } else { + Card *t = demoNewCards(); + if (t) { + newDemoMove(t); + } else if (isGameWon()) { + canvas()->update(); + emit gameWon(true); + return; + } else + stopDemo(); + } + + takeState(); +} + +Card *Dealer::demoNewCards() +{ + return 0; +} + +void Dealer::newDemoMove(Card *m) +{ + towait = m; + connect(m, SIGNAL(stoped(Card*)), SLOT(waitForDemo(Card*))); +} + +void Dealer::waitForDemo(Card *t) +{ + if (t == (Card*)-1) + return; + if (towait != t) + return; + t->disconnect(); + towait = 0; + demotimer->start(250, true); +} + +bool Dealer::isGameWon() const +{ + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + { + if (!(*it)->target() && !(*it)->isEmpty()) + return false; + } + return true; +} + +bool Dealer::isGameLost() const +{ + return false; +} + +bool Dealer::checkRemove( int, const Pile *, const Card *) const { + return true; +} + +bool Dealer::checkAdd( int, const Pile *, const CardList&) const { + return true; +} + +void Dealer::drawPile(KPixmap &pixmap, Pile *pile, bool selected) +{ + QPixmap bg = myCanvas.backgroundPixmap(); + QRect bounding(int(pile->x()), int(pile->y()), cardMap::CARDX(), cardMap::CARDY()); + + pixmap.resize(bounding.width(), bounding.height()); + pixmap.fill(Qt::white); + + if (!bg.isNull()) { + for (int x=bounding.x()/bg.width(); + x<(bounding.x()+bounding.width()+bg.width()-1)/bg.width(); x++) + { + for (int y=bounding.y()/bg.height(); + y<(bounding.y()+bounding.height()+bg.height()-1)/bg.height(); y++) + { + int sx = 0; + int sy = 0; + int dx = x*bg.width()-bounding.x(); + int dy = y*bg.height()-bounding.y(); + int w = bg.width(); + int h = bg.height(); + if (dx < 0) { + sx = -dx; + dx = 0; + } + if (dy < 0) { + sy = -dy; + dy = 0; + } + bitBlt(&pixmap, dx, dy, &bg, + sx, sy, w, h, Qt::CopyROP, true); + } + } + } + + + float s = -0.4; + float n = -0.3; + + int mid = QMAX( QMAX(midColor().red(), midColor().green()), midColor().blue()); + + // if it's too dark - light instead of dark + if (mid < 120) { + s *= -1; + n = 0.4; + } + + KPixmapEffect::intensity(pixmap, selected ? s : n); +} + +int Dealer::freeCells() const +{ + int n = 0; + for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it) + if ((*it)->isEmpty() && !(*it)->target()) + n++; + return n; +} + +void Dealer::setAnchorName(const QString &name) +{ + kdDebug(11111) << "setAnchorname " << name << endl; + ac = name; +} + +QString Dealer::anchorName() const { return ac; } + +void Dealer::wheelEvent( QWheelEvent *e ) +{ + QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->delta(), e->state()); + viewportWheelEvent(&ce); + if ( !ce.isAccepted() ) { + if ( e->orientation() == Horizontal && hScrollBarMode () == AlwaysOn ) + QApplication::sendEvent( horizontalScrollBar(), e); + else if (e->orientation() == Vertical && vScrollBarMode () == AlwaysOn ) + QApplication::sendEvent( verticalScrollBar(), e); + } else { + e->accept(); + } +} + +void Dealer::countGame() +{ + if ( !_gameRecorded ) { + kdDebug(11111) << "counting game as played." << endl; + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int Total = config->readUnsignedNumEntry(QString("total%1").arg(_id),0); + ++Total; + config->writeEntry(QString("total%1").arg(_id),Total); + _gameRecorded = true; + } +} + +void Dealer::countLoss() +{ + if ( _gameRecorded ) { + // update score + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int n = config->readUnsignedNumEntry(QString("loosestreak%1").arg(_id),0) + 1; + config->writeEntry(QString("loosestreak%1").arg(_id),n); + unsigned int m = config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(_id),0); + if (n>m) + config->writeEntry(QString("maxloosestreak%1").arg(_id),n); + config->writeEntry(QString("winstreak%1").arg(_id),0); + } +} + +#include "dealer.moc" diff --git a/kpat/dealer.h b/kpat/dealer.h new file mode 100644 index 00000000..c5593cbd --- /dev/null +++ b/kpat/dealer.h @@ -0,0 +1,228 @@ +#ifndef _DEALER_H_ +#define _DEALER_H_ + +#include "pile.h" +#include "hint.h" +#include + +class QDomDocument; +class KMainWindow; +class Dealer; +class DealerInfo; +class KAction; +class KSelectAction; +class KToggleAction; +class KPixmap; + +class DealerInfoList { +public: + static DealerInfoList *self(); + void add(DealerInfo *); + + const QValueList games() const { return list; } +private: + QValueList list; + static DealerInfoList *_self; +}; + +class DealerInfo { +public: + DealerInfo(const char *_name, int _index) + : name(_name), + gameindex(_index) +{ + DealerInfoList::self()->add(this); +} + const char *name; + uint gameindex; + virtual Dealer *createGame(KMainWindow *parent) = 0; +}; + +class CardState; + +typedef QValueList CardStateList; + +struct State +{ + CardStateList cards; + QString gameData; +}; + + +/*************************************************************** + + Dealer -- abstract base class of all varieties of patience + +***************************************************************/ +class Dealer: public QCanvasView +{ + Q_OBJECT + +public: + + Dealer( KMainWindow* parent = 0, const char* name = 0 ); + virtual ~Dealer(); + + static const Dealer *instance(); + + void enlargeCanvas(QCanvasRectangle *c); + void setGameNumber(long gmn); + long gameNumber() const; + + virtual bool isGameWon() const; + virtual bool isGameLost() const; + + void setViewSize(const QSize &size); + + void addPile(Pile *p); + void removePile(Pile *p); + + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + + virtual Card *demoNewCards(); + + virtual void setupActions(); + + bool demoActive() const; + + void drawPile(KPixmap &, Pile *p, bool selected); + + QColor midColor() const { return _midcolor; } + void setBackgroundPixmap(const QPixmap &background, const QColor &midcolor); + + void saveGame(QDomDocument &doc); + void openGame(QDomDocument &doc); + + void setGameId(int id) { _id = id; } + int gameId() const { return _id; } + + void setTakeTargetForHints(bool e) { takeTargets = e; } + bool takeTargetForHints() const { return takeTargets; } + + bool isMoving(Card *c) const; + + virtual QSize minimumCardSize() const; + virtual void resizeEvent(QResizeEvent *); + + int freeCells() const; + + QString anchorName() const; + void setAnchorName(const QString &name); + + void setAutoDropEnabled(bool a); + bool autoDrop() const { return _autodrop; } + + int getMoves() const { return undoList.count(); } + +public slots: + + // restart is pure virtual, so we need something else + virtual void startNew(); + void undo(); + virtual void takeState(); + virtual bool startAutoDrop(); + void hint(); + void slotTakeState(Card *c); + +signals: + void undoPossible(bool poss); + void gameWon(bool withhelp); + void gameLost(); + void saveGame(); // emergency + void gameInfo(const QString &info); + void updateMoves(); + +public slots: + virtual void demo(); + void waitForDemo(Card *); + void toggleDemo(); + virtual void stopDemo(); + void waitForAutoDrop(Card *); + +protected: + + enum { None = 0, Hint = 1, Demo = 2, Redeal = 4 } Actions; + + void setActions(int actions) { myActions = actions; } + int actions() const { return myActions; } + + virtual void restart() = 0; + + virtual void contentsMousePressEvent(QMouseEvent* e); + virtual void contentsMouseMoveEvent( QMouseEvent* ); + virtual void contentsMouseReleaseEvent( QMouseEvent* ); + virtual void contentsMouseDoubleClickEvent( QMouseEvent* ); + virtual void wheelEvent( QWheelEvent *e ); + + void unmarkAll(); + void mark(Card *c); + Pile *findTarget(Card *c); + virtual bool cardClicked(Card *); + virtual void pileClicked(Pile *); + virtual bool cardDblClicked(Card *); + void won(); + + virtual void getHints(); + void newHint(MoveHint *mh); + void clearHints(); + // it's not const because it changes the random seed + virtual MoveHint *chooseHint(); + + KMainWindow *parent() const; + + bool waiting() const { return _waiting != 0; } + void setWaiting(bool w); + +protected: + PileList piles; + + State *getState(); + void setState(State *); + + // reimplement this to add game-specific information in the state structure + virtual QString getGameState() const { return QString::null; } + // reimplement this to use the game-specific information from the state structure + virtual void setGameState( const QString & ) {} + + virtual void newDemoMove(Card *m); + + bool moved; + CardList movingCards; + QCanvasItemList marked; + QPoint moving_start; + Dealer( Dealer& ); // don't allow copies or assignments + void operator = ( Dealer& ); // don't allow copies or assignments + QCanvas myCanvas; + QSize minsize; + QSize viewsize; + QPtrList undoList; + long gamenumber; + QValueList hints; + Card *towait; + QTimer *demotimer; + int myActions; + bool toldAboutLostGame; + + KToggleAction *ademo; + KAction *ahint, *aredeal; + + KRandomSequence randseq; + QColor _midcolor; + Q_UINT32 _id; + bool takeTargets; + bool _won; + int _waiting; + bool stop_demo_next; + QString ac; + static Dealer *s_instance; + bool _autodrop; + bool _gameRecorded; + +private: + void countLoss(); + void countGame(); +}; + +#endif diff --git a/kpat/deck.cpp b/kpat/deck.cpp new file mode 100644 index 00000000..c2b6e0e5 --- /dev/null +++ b/kpat/deck.cpp @@ -0,0 +1,154 @@ +#include +#include "deck.h" +#include "dealer.h" +#include +#include + +const int NumberOfCards = 52; + + +Deck *Deck::my_deck = 0; + + +Deck::Deck( Dealer* parent, int m, int s ) + : Pile( 0, parent ), mult( m ) +{ + _deck = new Card * [mult*NumberOfCards]; + Q_CHECK_PTR (_deck); + + // only allow 1, 2, or 4 suits + if ( s == 1 || s == 2 ) + suits = s; + else + suits = 4; + + makedeck(); + addToDeck(); + shuffle(); + + setAddFlags(Pile::disallow); + setRemoveFlags(Pile::disallow); +} + + +Deck::~Deck() +{ + for (uint i=0; i < mult*NumberOfCards; i++) { + delete _deck[i]; + } + m_cards.clear(); + delete [] _deck; +} + + +// ---------------------------------------------------------------- + + +Deck *Deck::new_deck( Dealer *parent, int m, int s ) +{ + my_deck = new Deck(parent, m, s); + return my_deck; +} + + +void Deck::makedeck() +{ + int i=0; + + show(); + for ( uint m = 0; m < mult; m++) + { + for ( int r = Card::Ace; r <= Card::King; r++) + { + for ( int s = Card::Spades-1; s >= Card::Clubs-1 ; s--) + { + _deck[i] = new Card(static_cast(r), + static_cast(Card::Spades - (s % suits)), + dealer()->canvas()); + _deck[i]->move(x(), y()); + i++; + } + } + } +} + + +void Deck::collectAndShuffle() +{ + addToDeck(); + shuffle(); +} + + +Card* Deck::nextCard() +{ + CardList::Iterator c; + + c = m_cards.fromLast(); // Dealing from bottom of deck .... + if ( c != m_cards.end() ) { + return *c; + } else + return 0; +} + + +// ---------------------------------------------------------------- + + +static long pseudoRandomSeed = 0; + +static void pseudoRandom_srand(long seed) +{ + pseudoRandomSeed=seed; +} + + +// Documented as in +// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q28150 +// + +static long pseudoRandom_random() { + pseudoRandomSeed = 214013*pseudoRandomSeed+2531011; + return (pseudoRandomSeed >> 16) & 0x7fff; +} + + +// Shuffle deck, assuming all cards are in m_cards + +void Deck::shuffle() +{ + + assert(m_cards.count() == uint(mult*NumberOfCards)); + + assert(dealer()->gameNumber() >= 0); + pseudoRandom_srand(dealer()->gameNumber()); + + kdDebug(11111) << "first card " << m_cards[0]->name() << " " << dealer()->gameNumber() << endl; + + Card* t; + long z; + int left = mult*NumberOfCards; + for (uint i = 0; i < mult*NumberOfCards; i++) { + z = pseudoRandom_random() % left; + t = m_cards[z]; + m_cards[z] = m_cards[left-1]; + m_cards[left-1] = t; + left--; + } +} + + +// add cards in deck[] to Deck +// FIXME: Rename to collectCards() + +void Deck::addToDeck() +{ + clear(); + + for (uint i = 0; i < mult*NumberOfCards; i++) { + _deck[i]->setTakenDown(false); + add( _deck[i], true, false ); + } +} + + diff --git a/kpat/deck.h b/kpat/deck.h new file mode 100644 index 00000000..f5239fe0 --- /dev/null +++ b/kpat/deck.h @@ -0,0 +1,46 @@ +#ifndef _DECK_H_ +#define _DECK_H_ + +#include "pile.h" +class dealer; + +/*************************************** + + Deck (Pile with id 0) -- create and shuffle 52 cards + +**************************************/ +class Deck: public Pile +{ + +private: + Deck( Dealer* parent = 0, int m = 1, int s = 4 ); + virtual ~Deck(); + +public: + static Deck *new_deck( Dealer *parent = 0, int m = 1, int s = 4 ); + static Deck *deck() { return my_deck; } + + static const long n; + + void collectAndShuffle(); + + Card* nextCard(); + + uint decksNum() const { return mult; } + +private: // functions + + void makedeck(); + void addToDeck(); + void shuffle(); + +private: + + uint mult; + uint suits; + Card** _deck; + + static Deck *my_deck; +}; + +#endif diff --git a/kpat/fortyeight.cpp b/kpat/fortyeight.cpp new file mode 100644 index 00000000..1d867378 --- /dev/null +++ b/kpat/fortyeight.cpp @@ -0,0 +1,205 @@ +#include "fortyeight.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +HorLeftPile::HorLeftPile( int _index, Dealer* parent) + : Pile(_index, parent) +{ + // TODO: create a pile that moves the cards together when filling space + setHSpread( cardMap::CARDX() / 11 + 1 ); +} + +QSize HorLeftPile::cardOffset( bool _spread, bool, const Card *) const +{ + if (_spread) + return QSize(-hspread(), 0); + + return QSize(0, 0); +} + +void HorLeftPile::initSizes() +{ + Pile::initSizes(); + setHSpread( cardMap::CARDX() / 11 + 1 ); +} + + +Fortyeight::Fortyeight( KMainWindow* parent, const char* name) + : Dealer(parent,name) +{ + deck = Deck::new_deck(this, 2); + + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + deck->move(10 + cardMap::CARDX() * 82 / 10, 10 + cardMap::CARDX() * 56 / 10); + deck->setZ(20); + + pile = new HorLeftPile(20, this); + pile->setAddFlags(Pile::addSpread | Pile::disallow); + pile->move(10 + cardMap::CARDX() * 69 / 10, 10 + cardMap::CARDX() * 56 / 10 ); + + for (int i = 0; i < 8; i++) { + + target[i] = new Pile(9 + i, this); + target[i]->move(8+dist_x*i, 10); + target[i]->setType(Pile::KlondikeTarget); + + stack[i] = new Pile(1 + i, this); + stack[i]->move(8+dist_x*i, 10 + dist_y); + stack[i]->setAddFlags(Pile::addSpread); + stack[i]->setRemoveFlags(Pile::autoTurnTop); + stack[i]->setCheckIndex(1); + stack[i]->setSpread(stack[i]->spread() * 3 / 4); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +//-------------------------------------------------------------------------// + +void Fortyeight::restart() +{ + lastdeal = false; + deck->collectAndShuffle(); + deal(); +} + +void Fortyeight::deckClicked(Card *) +{ + if (deck->isEmpty()) { + if (lastdeal) + return; + lastdeal = true; + while (!pile->isEmpty()) { + Card *c = pile->at(pile->cardsLeft()-1); + c->setAnimated(false); + deck->add(c, true, false); + } + } + Card *c = deck->nextCard(); + pile->add(c, true, true); + int x = int(c->x()); + int y = int(c->y()); + c->move(deck->x(), deck->y()); + c->flipTo(x, y, 8); +} + +Card *Fortyeight::demoNewCards() +{ + if (deck->isEmpty() && lastdeal) + return 0; + deckClicked(0); + return pile->top(); +} + +bool Fortyeight::checkAdd(int, const Pile *c1, const CardList &c2) const +{ + if (c1->isEmpty()) + return true; + + // ok if in sequence, same suit + return (c1->top()->suit() == c2.first()->suit()) + && (c1->top()->rank() == (c2.first()->rank()+1)); +} + +void Fortyeight::deal() +{ + for (int r = 0; r < 4; r++) + { + for (int column = 0; column < 8; column++) + { + if (false) { // doesn't look + stack[column]->add(deck->nextCard(), true, true); + stack[column]->top()->turn(true); + } else { + stack[column]->add(deck->nextCard(), false, true); + } + } + } + pile->add(deck->nextCard(), false, false); +} + +QString Fortyeight::getGameState() const +{ + return QString::number(lastdeal); +} + +void Fortyeight::setGameState( const QString &s ) +{ + lastdeal = s.toInt(); +} + +bool Fortyeight::isGameLost() const +{ + kdDebug(11111) << "isGameLost ?" << endl; + if(!lastdeal) + return false; + if(!deck->isEmpty()) + return false; + + Card *c; + for(int i=0; i < 8; i++) + { + if(stack[i]->isEmpty()) + return false; + + c=stack[i]->top(); + + if(c->rank() == Card::Ace) + return false; + + if(!pile->isEmpty()) { + if(pile->top()->suit() == c->suit() && + pile->top()->rank()+1 == c->rank()) + return false; + + if ( !target[i]->isEmpty() && + pile->top()->suit() == target[i]->top()->suit() && + pile->top()->rank() == target[i]->top()->rank()+1) + return false; + } + for(int j=0; j <8;j++){ + if(target[j]->isEmpty()) + continue; + if(c->suit() == target[j]->top()->suit() && + c->rank()-1 ==target[j]->top()->rank()) + return false; + } + for(int j=1; j < 8; j++) { + int k=(i+j) % 8; + if (stack[k]->isEmpty()) + continue; + if(c->suit() == stack[k]->top()->suit() && + c->rank()+1 ==stack[k]->top()->rank()){ + int indexi=stack[i]->indexOf(c); + if(indexi==0) + return false; + Card *c2=stack[i]->at(indexi-1); + if(c2->rank()!=stack[k]->top()->rank() || + c2->suit()!=stack[k]->top()->suit()) + return false; + } + } + } + + return true; +} + +static class LocalDealerInfo8 : public DealerInfo +{ +public: + LocalDealerInfo8() : DealerInfo(I18N_NOOP("Forty && &Eight"), 8) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Fortyeight(parent); } +} ldi9; + +//-------------------------------------------------------------------------// + +#include "fortyeight.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/fortyeight.h b/kpat/fortyeight.h new file mode 100644 index 00000000..858e6e15 --- /dev/null +++ b/kpat/fortyeight.h @@ -0,0 +1,46 @@ +#ifndef _FORTY_EIGHT_H +#define _FORTY_EIGHT_H + +#include "dealer.h" + +class HorLeftPile : public Pile +{ + Q_OBJECT + +public: + HorLeftPile( int _index, Dealer* parent = 0); + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; + virtual void initSizes(); +}; + +class Fortyeight : public Dealer +{ + Q_OBJECT + +public: + Fortyeight( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void deal(); + virtual void restart(); + void deckClicked(Card *c); + +protected: + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual Card *demoNewCards(); + virtual QString getGameState() const; + virtual void setGameState( const QString & stream ); + +private: + Pile *stack[8]; + Pile *target[8]; + HorLeftPile *pile; + Deck *deck; + bool lastdeal; +}; + +#endif + + +//-------------------------------------------------------------------------// diff --git a/kpat/freecell-solver/CREDITS b/kpat/freecell-solver/CREDITS new file mode 100644 index 00000000..7ce9e216 --- /dev/null +++ b/kpat/freecell-solver/CREDITS @@ -0,0 +1,57 @@ +Shlomi Fish (me) - doing most of the work on Freecell Solver. + +Eric Warmenhoven - sending a program that generates the board of GNOME +Freecell. + +Hai Huang - noting several boards of Microsoft Freecell that could not be +solved by Freecell Solver. + +Magnus Reftel - noting the correct procedure for calculating how many cards +can be moved as a function of the free freecells and free stacks. + +Colin Plumb - writing the MD5 code. + +Ron Rivest - inventing the MD5 hashing algorithm. + +Jim Horne - supplying the shuffling algorithm for +Microsoft Freecell/Freecell Pro. + +Tom Holroyd - sending several Seahaven Towers games which Freecell Solver +was unable to solve, thus making me improve the algorithm. + +Markus F. X. J. Oberhumer - writing PySol on whose code the board generation +program for it is based. Also, contributing some patches. + +Justin-Heyes Jones - wrote a nice introduction to the A* algorithm, and +wrote the basis for the pqueue.c code. + +Stephan Kulow - integrated Freecell Solver into the kpat Solitaire suite for +KDE 2.1 and onwards; reported several bugs and memory leaks. + +Michael Keller - Contributing some useful input about some minor features +lacking and the Spades/Clubs mix-up. + +GeYong - He created Freecell Tool, whose randomized scan provided +inspiration for the random-DFS scan of Freecell Solver. + +Adrian Ettlinger - Integrating Freecell Solver into "Freecell Pro", and +contributing some input. + +The perl 5.x Hackers - I copied its hash function. + +Gergeley Kontra - wrote a Vim script to align text which I used. + +Bob Jenkins - wrote the lookup2 hash function, which I now use as the +primary hash. +(check http://burtleburtle.net/bob/hash/) + +Tzafrir Cohen - His "RPM" lecture provided help in creating the RPM +Spec. + +Yotam Rubin - Preparing an initial Debian Package. + +Risko Gergely - Maintaining the current Debian Package. + +Chris Moore - Pointing to an out-of-date comment regarding the +MAX_NUM_CARDS_IN_A_STACK which I updated. + diff --git a/kpat/freecell-solver/INSTALL b/kpat/freecell-solver/INSTALL new file mode 100644 index 00000000..9b718633 --- /dev/null +++ b/kpat/freecell-solver/INSTALL @@ -0,0 +1,70 @@ +INSTALL file for Freecell Solver +================================ + +Quick and Dirty Compilation +--------------------------- + +Usually typing "./configure" followed by "make" and "make install" will +build and install "fc-solve" which is the Freecell Solver executable for you. + +It will also build and install the board generation program, more +information about which can be found in the "board_gen" sub-directory of +this distribution. + +Changing the Maximal number of Freecells or Stacks or Cards per Stack +--------------------------------------------------------------------- + +The following parameters to the "configure" script which accept an argument +control the hard-coded parameters of the Freecell Solver executables: + +"--enable-max-num-freecells=$NUM" - The maximal number of freecells + +"--enable-max-num-stacks=$NUM" - The maximal number of stacks + +"--enable-max-num-initial-cards-per-stack=$NUM" - The maximal number of initial +cards per stack. + +Notice that it's very important to set the maximal number of initial cards +per stack, or else it's possible that a stack will eventually overflow. + +"Compact" States +--------------------- + +In Compact States, the contents of the card stacks are stored inside the +states, rather than in a central collection (where the states contain only +pointers). Despite its name, it actually consume more memory than Indirect +Stack States which is the default. + +Compact states used to be faster than Indirect Stack States, but now it +seems indirect stack states is at least slightly faster even for games +whose stacks are not very long. If you still would wish to enable it, +run ./configure with the "--enable-states-type=compact" flag. + +Installing under Win32 +---------------------- + +Freecell Solver is distributed with a makefile suitable for use with +Microsoft Visual C++. To build it using it follow the following steps: + +1. Copy "config.h.win32" to "config.h" and "prefix.h.win32" to "prefix.h"; +In the directory Presets/ copy presetrc.win32 to presetrc. + +2. Optionally, edit it to set its preferences + +3. Type "nmake /f Makefile.win32". + +If you have an IDE of some sort you can take the following steps to compile +Freecell Solver: + +1. Open a project for Freecell Solver. + +2. Add all the C files except "test_multi_parallel.c" to the project. + +3. Copy the file config.h.win32 to config.h and prefix.h.win32 to prefix.h. + +4. Build. + +If you are using gcc or some other command-line compiler, you should +write the makefile based on the files "Makefile" or "Makefile.lite", +and then compile according to it. + diff --git a/kpat/freecell-solver/Makefile.am b/kpat/freecell-solver/Makefile.am new file mode 100644 index 00000000..b0f5acff --- /dev/null +++ b/kpat/freecell-solver/Makefile.am @@ -0,0 +1,5 @@ + +noinst_LTLIBRARIES = libfcs.la +AM_CPPFLAGS = -DFCS_STATE_STORAGE=FCS_STATE_STORAGE_INTERNAL_HASH -DFCS_STACK_STORAGE=FCS_STACK_STORAGE_INTERNAL_HASH +libfcs_la_SOURCES = alloc.c app_str.c caas.c card.c cl_chop.c cmd_line.c fcs_dm.c fcs_hash.c fcs_isa.c freecell.c intrface.c lib.c lookup2.c move.c pqueue.c preset.c rand.c scans.c simpsim.c state.c + diff --git a/kpat/freecell-solver/Makefile.lite b/kpat/freecell-solver/Makefile.lite new file mode 100644 index 00000000..2a6248eb --- /dev/null +++ b/kpat/freecell-solver/Makefile.lite @@ -0,0 +1,94 @@ + +CC = gcc +OFLAGS = -Wall -O3 -Wno-long-long -Wundef -Wcast-align -Wconversion -Wchar-subscripts -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -g +OLFLAGS = -Wall +DLFLAGS = + +END_OLFLAGS = +END_DLFLAGS = + +INCLUDES = alloc.h app_str.h caas.h card.h cl_chop.h fcs_config.h fcs_cl.h fcs.h fcs_dm.h fcs_enums.h fcs_hash.h fcs_isa.h fcs_move.h fcs_user.h inline.h jhjtypes.h lookup2.h move.h ms_ca.h prefix.h pqueue.h preset.h rand.h state.h test_arr.h tests.h + +TARGETS = fc-solve + +all: $(TARGETS) + +board_gen: dummy + make -C board_gen/ + +dummy: + +#<<>>OBJECTS.END + +fc-solve: $(OBJECTS) + $(CC) $(OLFLAGS) -o $@ $(OBJECTS) -lm + +clean: + rm -f *.o $(TARGETS) $(DTARGETS) + + diff --git a/kpat/freecell-solver/README b/kpat/freecell-solver/README new file mode 100644 index 00000000..4902e3a7 --- /dev/null +++ b/kpat/freecell-solver/README @@ -0,0 +1,105 @@ +1. Introduction +--------------- + +This is Freecell Solver version 2.8.x, a program that automatically +solves most Freecell and Simple Simon games. + +Freecell Solver is distributed under the public domain. + +I hope you'll enjoy using it, and make the best of it. + + Shlomi Fish (shlomif@vipe.technion.ac.il) + +2. Building +----------- + +Read the file "INSTALL" for information on how to do that. For the impatient: +type: + +./configure +make +make install + +3. Usage +-------- + +The program is called "fc-solve". You invoke it like this: + +fc-solve board_file + +board_file is the filename with a valid Freecell startup board. The file is +built as follows: + +It has the 8 Freecell stacks. +Each stack contain its number of cards separated by a whitespace +and terminated with a newline character( it's important that the last stack +will also be terminated with a newline !). The cards in the line are ordered +from the bottom-most card in the left to the topmost card in the right. + +A card string contains the card number followed by the card deck. The card +number is one of: A,1,2,3,4,5,6,7,8,9,10,J,Q,K. The card deck is one of: +H,S,D,C (standing for Hearts, Spades, Diamonds and Clubs respectively). + +Here is an example board: (PySol board No. 24) + +4S 2S 9S 8S QC 4C 2H +5H QH 3S AS 3H 4H QD +QS 9C 6H 9H 3C KC 3D +5D 2C JS 5S JH 6D AC +2D KD 10H 10S 10D 8D +7H JC KH 10C KS 7S +AH 5C 6C AD 8H JD +7C 6S 7D 4D 8C 9D + +And another one: (PySol board No. 198246790) + +KD JH 5H 7D 9H KS 9D +3H JD 5D 8H QH 7H 2D +4D 3C QS 3S 6C QC KC +10S 9C 6D 9S QD 8C 10D +10C 8S 7C 10H 2S AC +8D AS AH 4H JS 4S +6H 7S 4C 5C 5S JC +AD KH 6S 2H 3D 2C + +You can specify the contents of the freecells by prefixing the line with +"FC:". For example: +FC: 3H QC + +will specify that the cards 3 of hearts and queen of clubs are present in +the freecells. To specify an empty freecell use a "-" as its designator. + +If there's another "FC:" line, the previous line will be overriden. + +You can specify the contents of the foundations by prefixing the line with +"Founds:" and then using a format as follows: + +Founds: H-5 C-A S-0 D-K + +Hence, the deck ID followed by a dash followed by the card number in the +foundation. A suit that is not present will be assumed to be 0. Again, if +there's more than one then the previous lines will be overriden. + + +The program will stop processing the input as soon as it read 8 lines of +standard stacks. Therefore, it is recommended that the foundations and +freecells lines will come at the beginning of the file. + +The program will process the board and try to solve it. If it succeeds it +will output the states from the initial board to its final solution to the +standard output. If it fails, it will notify it. + +For information about the various command-line switches that Freecell +Solver accepts, read the USAGE file in this directory. + +To solve Simple Simon boards append "--game simple_simon" right after +the "fc-solve" program name. + +4. The board generation programs +-------------------------------- + +Several programs which can generate the initial boards of various Freecell +implementations can be found in the "board_gen/" sub-directory. Read the +"README" file there for details on how they can be compiled and used. + +In any case, they can save you the time of inputting the board yourself. diff --git a/kpat/freecell-solver/USAGE b/kpat/freecell-solver/USAGE new file mode 100644 index 00000000..f78295b2 --- /dev/null +++ b/kpat/freecell-solver/USAGE @@ -0,0 +1,518 @@ +Freecell Solver's Command-Line Syntax and Usage +=============================================== + + +1. The programs +--------------- + +Most command-line switches have two versions: a short POSIX one which +is a dash followed by a letter; and a long GNU one which is two dashes +followed by the command string. Note, that Freecell Solver does not +support specifying more than one command letter after a dash, (e.g: +"-sip"). Furthermore, a command that accepts a parameter, will require +this parameter to be present in the next command-line argument, not in +the GNU manner of "--command=option". + +I don't use getopt because I want Freecell Solver to be a pure ANSI C +program, so I'm sorry for the inconvenience. + + +2. Getting Help +--------------- + +-h --help + +This option displays a help text on the screen. This help +text summarizes the command-line options and their meaning, as well as +the signal combinations that fc-solve accepts. + + +3. Output Options +----------------- + +-p --parseable-output + +This option will display the stacks in a format that can be more easily +manipulated by text-processing programs such as grep or perl. Namely, +The freecells will be displayed in one line, and the foundations in a +separate line. Plus, Each stack will be displayed horizontally, in its +own line, while beginning with a ":". + + +-t --display-10-as-t + +This option will display the 10 cards as a capital T instead of a 10. +Thus, the cards will be more properly aligned. + + +-c --canonized-order-output + +Freecell Solver re-arranges the stacks and freecells in a given state +according to their first card. It keeps their actual position in a +separate place, but internally it uses their canonized place. Use +this option, if you want Freecell Solver to display them in that order. +One should be warned that that way the place of a given stack in the +board will not be preserved throughout the solution. + + +-m --display-moves + +This option will display the moves instead of the intermediate states. +Each move will be displayed in a separate line, in a format that is +human-readable, but that can also be parsed and analyzed by a computer +program with some effort on the programmer's part. + + +-sn --standard-notation + +This option will display the moves in standard notation in which every +move consists of two characters and there are ten moves in a line. Naturally, +this option will only become apparent if the display moves is specified. +(it does not implicitly specify it, though). + +For more information regarding standard notation refer to the following +web-page: + +http://home.earthlink.net/~fomalhaut/freecell.html + +-snx --standard-notation-extended + +This option is similar to the previous one, only that when a sequence +move is made to an empty stack with more than one card in the sequence, +the move will be followed with "v" and the number of cards moved in +hexadecimal. + +-sam --display-states-and-moves + +This option will display both the intermediate states and the moves that +are needed to move from one to another. The standard notation +option applies to it to. + + +-pi --display-parent-iter + +This option (assuming the -s and -i options are specified) will also +display the iteration index of the state from which the current state +was derived. This is especially useful for A* or BFS scans. + +4. Game Variants Options +------------------------ + + +--freecells-num [Number of Freecells] + +This option specifies the number of freecells which are available to +the program. Freecell Solver can use any number of freecells as long as +it does not exceed its maximal number. + +This maximum is hard-coded into the program, and can be specified at +compile-time by modifying the file "config.h". See the file INSTALL for +details. + + +--stacks-num [Number of Stacks] + +This option specifies the number of stacks present in the board. Again, +this number cannot exceed the maximal number of stacks, which can be +specified in the file "config.h" during compile-time of Freecell +Solver. + + +--decks-num [Number of Decks] + +This options specifies how many decks are found in the board. This number +cannot exceed the maximal number of decks, which can be specified in the +file "config.h" during compile time of Freecell Solver. + + +--sequences-are-built-by {suit|alternate_color|rank} + +This option specifies whether a card sequence is built by suit or by +alternate colour or by rank regardless of suit. + + +--sequence-move {limited|unlimited} + +This option specifies whether the sequence move is limited by the +number of freecells or vacant stacks or not. + + +--empty-stacks-filled-by {kings|none|all} + +Specifies which cards can fill an empty stack. + + +--game [game] +--preset [game] +-g [game] + +Specifies the type of game. Each preset implies several of the +settings options above and sometimes even the tests order below. +Available presets: + + bakers_dozen - Baker's Dozen + bakers_game - Baker's Game + beleaguered_castle - Beleaguered Castle + citadel - Citadel + cruel - Cruel + der_katz - Der Katzenschwanz + die_schlange - Die Schlange + eight_off - Eight Off + fan - Fan + forecell - Forecell + freecell - Freecell + good_measure - Good Measure + ko_bakers_game - Kings' Only Baker's Game + relaxed_freecell - Relaxed Freecell + relaxed_sehaven - Relaxed Seahaven Towers + seahaven - Seahaven Towers + simple_simon - Simple Simon + streets_and_alleys - Streets and Alleys + +Note: in order to solve Der Katzenschwanz and Die Schlange I recommend you +compile Freecell Solver with the INDIRECT_STACK_STATES option, or else it will +consume much more memory. For details consult the file INSTALL. + +5. Solving Algorithm Options +---------------------------- + +-mi [Maximal number of iterations] +--max-iters [Maximal number of iterations] + +This parameter limits the maximal number of states to check. This will +give a rough estimate on the time spent to solve a given board. + + +-md [Maximal depth] +--max-depth [Maximal depth] + +Freecell Solver recurses into the solution. This parameter specifies a +maximal recursion depth. Generally speaking, it's not a good idea to +set it, because that way several important intermediate states become +inaccessible. + +-mss [Maximal States' Number] +--max-stored-states [Maximal States' Number] + +Limits the number of the states stored by the program in the computer's +memory. This differs from the maximal number of iterations in the sense, that +it is possible that a stored state was not checked yet. + + +-to [Test's Order] +--tests-order [Test's Order] + +This option specifies the order in which Freecell Solver will try the +different types of moves that it can perform. Each move is specified by +one character, and they are performed in the order in which they appear +in the parameter string. You can omit tests by not including their +corresponding characters in the string. + +The tests along with their characters are: + +Freecell Tests: + +'0' - put top stack cards in the foundations. +'1' - put freecell cards in the foundations. +'2' - put freecell cards on top of stacks. +'3' - put non-top stack cards in the foundations. +'4' - move stack cards to different stacks. +'5' - move stack cards to a parent card on the same stack. +'6' - move sequences of cards onto free stacks. +'7' - put freecell cards on empty stacks. +'8' - move cards to a different parent. +'9' - empty an entire stack into the freecells. + +Atomic Freecell Tests: + +'A' - move a stack card to an empty stack. +'B' - move a stack card to a parent on a different stack. +'C' - move a stack card to a freecell. +'D' - move a freecell card to a parent. +'E' - move a freecell card to an empty stack. + + +Simple Simon Tests: + +'a' - move a full sequence to the foundations. +'b' - move a sequence to a true parent of his. +'c' - move a whole stack sequence to a false parent (in order to clear + the stack) +'d' - move a sequence to a true parent that has some cards above it. +'e' - move a sequence with some cards above it to a true parent. +'f' - move a sequence with a junk sequence above it to a true parent that + has some cards above it. +'g' - move a whole stack sequence to a false parent which has some + cards above it. +'h' - move a sequence to a parent on the same stack. + +Manipulating the tests order can be very helpful to the quick solution +of a given board. If you found that a certain board cannot be solved in +after a long time or in a certain maximal number of iterations, you +should try different tests' orders. Usually, one can find a test order +that solves a board very quickly. + +Note that this test order usually makes sense only for the Depth-First +Search scans (see the "--method" option below). + +Also note that Freecell tests are not suitable for solving Simple Simon games +and Simple Simon tests are not suitable for solving anything except Simple +Simon. + +Tests can be grouped together into random groups using parenthesis +(e.g: "(0123)") or square brackets ("[012][3456789]"). Such grouping is +only relevant to the Random DFS scan (see below). + + +-me [Solving Method] +--method [Solving Method] + +This option specifies the solving method that will be used to solve the +board. Currently, the following methods are available: + +a-star - An A* scan +bfs - A Breadth-First Search (or BFS) scan +dfs - A Depth-First Search (or DFS) scan +random-dfs - A randomized DFS scan +soft-dfs - A "soft" DFS scan + +The difference between "dfs" and "soft-dfs" is that the soft DFS does not +use procedural recursion but rather its own internal stack. "random-dfs" is +similar to "soft-dfs" only it determines to which states to recurse into +randomly. Its behaviour will differ depending on the seed you supply to it. +(see the "-seed" option below.) + +BFS does not yield good results, and A* has a mixed behaviour, so for +the time being I recommend using either DFS or Soft-DFS. + +The Random-DFS scan processes every tests' random group, randomizes the +states that it found and recurses into them one by one. Renegade tests +that do not belong to any group, are processed in a non-random manner. + + +-asw [A* Weights] +--a-star-weight [A* Weights] + +Specify weights for the A* scan, assuming it is used. The parameter +should be a comma-separated list of numbers, each one is proportional +to the weight of its corresponding test. + +The numbers are, in order: +1. The number of cards out. +2. The maximal sequence move. +3. The number of cards under sequences. +4. The length of the sequences which are found over renegade cards. +5. The depth of the board in the solution. + +The default weights are respectively: 0.5,0,0.3,0,0.2 + + +-seed [Seed Number] + +Specifies a seed to be used by Freecell Solver's internal random number +generator. This seed may alter the behaviour and speed of the "random-dfs" +scan. + + +-opt +--optimize-solution + +This option instructs Freecell Solver to try and optimize the solution +path so it will have a smaller number of moves. + + +-opt-to [tests order] +--optimization-tests-order [tests order] + +This argument specifies the test order for the optimization scan, in case +it should be different than an order that contains all the tests that were +used in all the normal scans. + + +--reparent-states + +This option specifies that states that were encountered whose depth in the +states graph can be improved should be reparented to the new parent. This +option can possibly make solutions shorter. + + +--calc-real-depth + +This options become effective only if --reparent-states is specified. What it +does, is explicitly calculate the depth of the state by tracing its path +to the initial state. This may make depth consideration more accurate. + + +6. Running Several Scans in Parallel: +------------------------------------- + +Starting from Version 2.4.0, Freecell Solver can run several scans in +parallel on the same state collection. Each scan resides in its own +"Soft Thread". By specifying several soft threads on the command line +one can create use several parallel scans. Once one of the scans +reaches a solution, the solution will be displayed. + + +-nst +--next-soft-thread + +This option creates a new soft-thread and let the other scan-specific options +initialize it. For example: + +# fc-solve --method a-star -nst --method soft-dfs -to 0123467 myboard.txt + +will run an A* scan and a Soft-DFS scan with a tests order of 0123467 on +myboard.txt. + + +-step [Number of Iterations in the Step] +--soft-thread-step [Number of Iterations in the Step] + +This option will set the number of iterations with which to run the +soft thread before switching to the next one. By specifying a larger +step, one can give a certain scan a longer run-time and a higher priority. + + +-nht +--next-hard-thread + +This argument lets one initialize the next hard thread. If Freecell Solver was +compiled with such support, then it is possible to run each hard thread in its +own system thread. Each hard-thread contains one or more soft threads. + + +--st-name [soft thread name] + +This argument sets the name used to identify the current soft thread. This name +can later be used to construct the prelude (see below). + + +--prelude [i1@st1{,i2@st2{,i3@st3...}}] + +Sets the prelude for the hard thread. At the beginning of the search, the +hard thread plays a static sequence of iterations at each of the soft threads +specified in the prelude, for the number of iterations specified. + +For example, if you had three soft threads named "foo", "bar" and "rin", then +the following prelude: + + --prelude 500@foo,1590@bar,100@foo,200@rin + +Will run 500 iterations in "foo", then 1590 in "bar", then 100 in "foo" again, +and then 200 in "rin". After the prelude finishes, the hard thread would +run the scans one after the other in the sequence they were defined for their +step number. + + +--scans-synergy {none|dead-ends-mark} + +Specifies the synergy between the various scans, or how much they cooperate +between themselves. "none" means they do not cooperate and only share +the same memory resources. "dead-end-marks" means they try to mark states +that they have withdrawn from, and states whose all their derived states are +such, as "dead ends". This may or may not improve the speed of the solution. + + +-ni +--next-instance + +This option allows to run two or more separate solvers one after the +other. If the first one returned an unsolvable verdict, then the second +one would run and so on. One use of it is to run an atomic moves scan +after a meta-moves scan, so we will always get an accurate verdict and +still enjoy some of the speed of the meta-moves scan. + + +7. Meta-Options +--------------- + + +--reset + +This option resets the program to its initial state, losing all the +logic that was inputted to it up to that state. Afterwards, it can +be set to a different configuration, again. + + +--read-from-file [{num_skip},]filename + +This option will read the configuration options from a file. The format +of the file is similar to that used by the UNIX Bourne Shell. (i.e: +spaces denote separate arguments, double-quotes encompass arguments, +backslash escapes characters). + +The filename can be preceeded by an optional number of the arguments to +skip followed by a comma. (the default is 0) + + +-l [preset] +--load-config [preset] + +Reads the configuration specified by [preset] and configures the solver +accordingly. A preset is a set of command line arguments to be analyzed +in the place of this option. They are read from a set of presetrc files +: one installed system-wide, the other at $HOME/.freecell-solver/presetrc +and the third at the path specified by the FREECELL_SOLVER_PRESETRC +environment variable. You can add more presets at any of these places. +(refer to http://groups.yahoo.com/group/fc-solve-discuss/message/403 +for information about their format) + +Presets that are shipped with Freecell Solver: + + abra-kadabra - a meta-moves preset + cool-jives - a meta-moves preset + crooked-nose - an atomic-moves preset (guarantees an accurate verdict) + fools-gold - an atomic-moves preset + good-intentions - runs cool-jives and then fools-gold + hello-world - a meta-moves preset + john-galt-line - a meta-moves preset + rin-tin-tin - a meta-moves preset + yellow-brick-road - a meta-moves preset + +They can be abbreviated into their lowercase acronym (i.e: "ak" or "rtt"). + + +8. Run-time Display Options +--------------------------- + + +-i +--iter-output + +This option tells fc-solve to print the iteration number and the +recursion depth of every state which is checked, to the standard +output. It's a good way to keep track of how it's doing, but the output +slows it down a bit. + + +-s +--state-output + +This option implies -i. If specified, this option outputs the cards and +formation of the board itself, for every state that is checked. +"fc-solve -s" yields a nice real-time display of the progress of +Freecell Solver, but you usually cannot make what is going on because +it is so fast. + + +9. Signal Combinations +---------------------- + +If you are working on a UNIX or a similar system then you can set some +run-time options in "fc-solve" by sending it some signal +combinations. + +If you send the signal USR1, without sending any other signals before +that, then "fc-solve" will output the present number of +iterations. This method is a good way to monitor an instance that takes +a long time to solve. + +If you send it the signal USR2 and then USR1, then "fc-solve" +will print the iteration number and depth on every state that it +checks. It is the equivalent of specifying (or unspecifying) the +option -i/--iter-output. + +If you send it two USR2 signals and then USR1, then "fc-solve" +will also print the board of every state. Again, this will only be done +assuming the iteration output is turned on. + diff --git a/kpat/freecell-solver/alloc.c b/kpat/freecell-solver/alloc.c new file mode 100644 index 00000000..81abdcc5 --- /dev/null +++ b/kpat/freecell-solver/alloc.c @@ -0,0 +1,127 @@ +/* + * alloc.c - a dynamic memory allocator. It allocates blocks of relatively + * small size, in a contiguous, compact manner. The most recent block can + * be released, but otherwise the blocks are kept for prosperity. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include + +#include "fcs_config.h" + +#include "alloc.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define ALLOCED_SIZE (8*1024-10*sizeof(char *)) + +fcs_compact_allocator_t * + freecell_solver_compact_allocator_new(void) +{ + fcs_compact_allocator_t * allocator; + + + allocator = (fcs_compact_allocator_t *)malloc(sizeof(*allocator)); + allocator->max_num_packs = IA_STATE_PACKS_GROW_BY; + allocator->packs = (char * *)malloc(sizeof(allocator->packs[0]) * allocator->max_num_packs); + allocator->num_packs = 1; + allocator->max_ptr = + (allocator->ptr = + allocator->rollback_ptr = + allocator->packs[0] = + malloc(ALLOCED_SIZE)) + + ALLOCED_SIZE; + + return allocator; +} + +void freecell_solver_compact_allocator_extend( + fcs_compact_allocator_t * allocator + ) +{ + /* Allocate a new pack */ + if (allocator->num_packs == allocator->max_num_packs) + { + allocator->max_num_packs += IA_STATE_PACKS_GROW_BY; + allocator->packs = (char * *)realloc(allocator->packs, sizeof(allocator->packs[0]) * allocator->max_num_packs); + } + + allocator->max_ptr = + (allocator->ptr = + allocator->rollback_ptr = + allocator->packs[allocator->num_packs++] = + malloc(ALLOCED_SIZE)) + + ALLOCED_SIZE; +} + +#if 0 +char * + freecell_solver_compact_allocator_alloc( + fcs_compact_allocator_t * allocator, + int how_much + ) +{ + if (allocator->max_ptr - allocator->ptr < how_much) + { + freecell_solver_compact_allocator_extend(allocator); + } + allocator->rollback_ptr = allocator->ptr; + allocator->ptr += (how_much+(4-(how_much&0x3))); + return allocator->rollback_ptr; +} + +void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator) +{ + allocator->ptr = allocator->rollback_ptr; +} +#endif + +void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator) +{ + int a; + for(a=0;anum_packs;a++) + { + free(allocator->packs[a]); + } + free(allocator->packs); + free(allocator); +} + +void freecell_solver_compact_allocator_foreach( + fcs_compact_allocator_t * allocator, + int data_width, + void (*ptr_function)(void *, void *), + void * context + ) +{ + int pack; + char * ptr, * max_ptr; + for(pack=0;packnum_packs-1;pack++) + { + ptr = allocator->packs[pack]; + max_ptr = ptr + ALLOCED_SIZE - data_width; + while (ptr <= max_ptr) + { + ptr_function(ptr, context); + ptr += data_width; + } + } + /* Run the callback on the last pack */ + ptr = allocator->packs[pack]; + max_ptr = allocator->rollback_ptr; + while (ptr <= max_ptr) + { + ptr_function(ptr, context); + ptr += data_width; + } +} + + + + diff --git a/kpat/freecell-solver/alloc.h b/kpat/freecell-solver/alloc.h new file mode 100644 index 00000000..5b339f24 --- /dev/null +++ b/kpat/freecell-solver/alloc.h @@ -0,0 +1,86 @@ + +#ifndef FC_SOLVE__ALLOC_H +#define FC_SOLVE__ALLOC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +struct fcs_compact_allocator_struct +{ + char * * packs; + int max_num_packs; + int num_packs; + char * max_ptr; + char * ptr; + char * rollback_ptr; +}; + +typedef struct fcs_compact_allocator_struct fcs_compact_allocator_t; + +extern fcs_compact_allocator_t * + freecell_solver_compact_allocator_new(void); + +extern void freecell_solver_compact_allocator_extend( + fcs_compact_allocator_t * allocator + ); +#if 0 +extern char * + freecell_solver_compact_allocator_alloc( + fcs_compact_allocator_t * allocator, + int how_much + ); +#else +#define fcs_compact_alloc_into_var(result,allocator_orig,what_t) \ +{ \ + register fcs_compact_allocator_t * allocator = (allocator_orig); \ + if (allocator->max_ptr - allocator->ptr < sizeof(what_t)) \ + { \ + freecell_solver_compact_allocator_extend(allocator); \ + } \ + allocator->rollback_ptr = allocator->ptr; \ + allocator->ptr += ((sizeof(what_t))+(sizeof(char *)-((sizeof(what_t))&(sizeof(char *)-1)))); \ + result = (what_t *)allocator->rollback_ptr; \ +} + +#define fcs_compact_alloc_typed_ptr_into_var(result, type_t, allocator_orig, how_much_orig) \ +{ \ + register fcs_compact_allocator_t * allocator = (allocator_orig); \ + register int how_much = (how_much_orig); \ + if (allocator->max_ptr - allocator->ptr < how_much) \ + { \ + freecell_solver_compact_allocator_extend(allocator); \ + } \ + allocator->rollback_ptr = allocator->ptr; \ + /* Round ptr to the next pointer boundary */ \ + allocator->ptr += ((how_much)+((sizeof(char *)-((how_much)&(sizeof(char *)-1)))&(sizeof(char*)-1))); \ + result = (type_t *)allocator->rollback_ptr; \ +} + +#endif + +#if 0 +extern void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator); +#else +#define fcs_compact_alloc_release(allocator) \ +{ \ + (allocator)->ptr = (allocator)->rollback_ptr; \ +} +#endif + +extern void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator); + +extern void freecell_solver_compact_allocator_foreach( + fcs_compact_allocator_t * allocator, + int data_width, + void (*ptr_function)(void *, void *), + void * context + ); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/kpat/freecell-solver/app_str.c b/kpat/freecell-solver/app_str.c new file mode 100644 index 00000000..0a1ced21 --- /dev/null +++ b/kpat/freecell-solver/app_str.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include + +#define GROW_BY 4000 + +struct freecell_solver_append_string_struct +{ + char * buffer; + char * end_of_buffer; + size_t max_size; + size_t size_of_margin; +}; + +typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t; + +freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin) +{ + freecell_solver_append_string_t * app_str; + + if (size_margin > GROW_BY) + { + return NULL; + } + + app_str = malloc(sizeof(freecell_solver_append_string_t)); + app_str->max_size = GROW_BY; + app_str->end_of_buffer = app_str->buffer = malloc(app_str->max_size); + app_str->size_of_margin = size_margin; + + return app_str; +} + +int freecell_solver_append_string_sprintf( + freecell_solver_append_string_t * app_str, + char * format, + ... + ) +{ + int num_chars_written; + va_list my_va_list; + + va_start(my_va_list, format); + num_chars_written = vsprintf(app_str->end_of_buffer, format, my_va_list); + app_str->end_of_buffer += num_chars_written; + /* + * Check to see if we don't have enough space in which case we should + * resize + * */ + if (app_str->buffer + app_str->max_size - app_str->end_of_buffer < (int)app_str->size_of_margin ) + { + char * old_buffer = app_str->buffer; + app_str->max_size += GROW_BY; + app_str->buffer = realloc(app_str->buffer, app_str->max_size); + /* + * Adjust end_of_buffer to the new buffer start + * */ + app_str->end_of_buffer = app_str->buffer + (app_str->end_of_buffer - old_buffer); + } + + return num_chars_written; +} + +char * freecell_solver_append_string_finalize( + freecell_solver_append_string_t * app_str + ) +{ + char * ret; + ret = strdup(app_str->buffer); + free(app_str->buffer); + free(app_str); + return ret; +} diff --git a/kpat/freecell-solver/app_str.h b/kpat/freecell-solver/app_str.h new file mode 100644 index 00000000..c6b6732a --- /dev/null +++ b/kpat/freecell-solver/app_str.h @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +#ifndef FC_SOLVE__APP_STR_H +#define FC_SOLVE__APP_STR_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct freecell_solver_append_string_struct +{ + char * buffer; + char * end_of_buffer; + int max_size; + int size_of_margin; +}; + +typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t; + +extern freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin); + +extern int freecell_solver_append_string_sprintf( + freecell_solver_append_string_t * app_str, + const char * format, + ... + ); + +extern char * freecell_solver_append_string_finalize( + freecell_solver_append_string_t * app_str + ); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__APP_STR_H */ diff --git a/kpat/freecell-solver/caas.c b/kpat/freecell-solver/caas.c new file mode 100644 index 00000000..82492f34 --- /dev/null +++ b/kpat/freecell-solver/caas.c @@ -0,0 +1,629 @@ +/* + * caas.c - the various possible implementations of the function + * freecell_solver_check_and_add_state(). + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__CAAS_C +#define FC_SOLVE__CAAS_C + +#include +#include +#include + +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "lookup2.h" + + +#ifdef INDIRECT_STACK_STATES +#include "fcs_hash.h" +#endif + +#include "caas.h" +#include "ms_ca.h" + +#include "test_arr.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +/* + The objective of the fcs_caas_check_and_insert macros is: + 1. To check if new_state is already in the prev_states collection. + 2. If not, to add it and to set check to true. + 3. If so, to set check to false. + */ + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) +#ifdef FCS_WITH_MHASH +#define fcs_caas_check_and_insert() \ + /* \ + Calculate the has function of the state. \ + */ \ + { \ + char * temp_ptr; \ + instance->mhash_context = mhash_init(instance->mhash_type); \ + mhash(instance->mhash_context, (void *)new_state, sizeof(fcs_state_t)); \ + temp_ptr = mhash_end(instance->mhash_context); \ + /* Retrieve the first 32 bits and make them the hash value */ \ + hash_value_int = *(SFO_hash_value_t*)temp_ptr; \ + free(temp_ptr); \ + } \ + \ + if (hash_value_int < 0) \ + { \ + /* \ + * This is a bit mask that nullifies the sign bit of the \ + * number so it will always be positive \ + * */ \ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ + } \ + check = ((*existing_state = freecell_solver_hash_insert( \ + instance->hash, \ + new_state, \ + hash_value_int, \ + 1 \ + )) == NULL); + + + +#else +#define fcs_caas_check_and_insert() \ + { \ + const char * s_ptr = (char*)new_state; \ + const char * s_end = s_ptr+sizeof(fcs_state_t); \ + hash_value_int = 0; \ + while (s_ptr < s_end) \ + { \ + hash_value_int += (hash_value_int << 5) + *(s_ptr++); \ + } \ + hash_value_int += (hash_value_int>>5); \ + } \ + if (hash_value_int < 0) \ + { \ + /* \ + * This is a bit mask that nullifies the sign bit of the \ + * number so it will always be positive \ + * */ \ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \ + } \ + check = ((*existing_state = freecell_solver_hash_insert( \ + instance->hash, \ + new_state, \ + freecell_solver_lookup2_hash_function( \ + (ub1 *)new_state, \ + sizeof(fcs_state_t), \ + 24 \ + ), \ + hash_value_int, \ + 1 \ + )) == NULL); + +#endif +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) +#define fcs_caas_check_and_insert() \ + /* Try to see if the state is found in indirect_prev_states */ \ + if ((pos_ptr = (fcs_state_with_locations_t * *)bsearch(&new_state, \ + instance->indirect_prev_states, \ + instance->num_indirect_prev_states, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect)) == NULL) \ + { \ + /* It isn't in prev_states, but maybe it's in the sort margin */ \ + pos_ptr = (fcs_state_with_locations_t * *)freecell_solver_bsearch( \ + &new_state, \ + instance->indirect_prev_states_margin, \ + instance->num_prev_states_margin, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect_with_context, \ + NULL, \ + &found); \ + \ + if (found) \ + { \ + check = 0; \ + *existing_state = *pos_ptr; \ + } \ + else \ + { \ + /* Insert the state into its corresponding place in the sort \ + * margin */ \ + memmove((void*)(pos_ptr+1), \ + (void*)pos_ptr, \ + sizeof(fcs_state_with_locations_t *) * \ + (instance->num_prev_states_margin- \ + (pos_ptr-instance->indirect_prev_states_margin) \ + )); \ + *pos_ptr = new_state; \ + \ + instance->num_prev_states_margin++; \ + \ + if (instance->num_prev_states_margin >= PREV_STATES_SORT_MARGIN) \ + { \ + /* The sort margin is full, let's combine it with the main array */ \ + if (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ + { \ + while (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \ + { \ + instance->max_num_indirect_prev_states += PREV_STATES_GROW_BY; \ + } \ + instance->indirect_prev_states = realloc(instance->indirect_prev_states, sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); \ + } \ + \ + freecell_solver_merge_large_and_small_sorted_arrays( \ + instance->indirect_prev_states, \ + instance->num_indirect_prev_states, \ + instance->indirect_prev_states_margin, \ + instance->num_prev_states_margin, \ + sizeof(fcs_state_with_locations_t *), \ + freecell_solver_state_compare_indirect_with_context, \ + NULL \ + ); \ + \ + instance->num_indirect_prev_states += instance->num_prev_states_margin; \ + \ + instance->num_prev_states_margin=0; \ + } \ + check = 1; \ + } \ + \ + } \ + else \ + { \ + *existing_state = *pos_ptr; \ + check = 0; \ + } + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + +#define fcs_caas_check_and_insert() \ + *existing_state = (fcs_state_with_locations_t *)rbsearch(new_state, instance->tree); \ + check = ((*existing_state) == new_state); + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) +#define fcs_libavl_states_tree_insert(a,b) avl_insert((a),(b)) +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) +#define fcs_libavl_states_tree_insert(a,b) rb_insert((a),(b)) +#endif + +#define fcs_caas_check_and_insert() \ + *existing_state = fcs_libavl_states_tree_insert(instance->tree, new_state); \ + check = (*existing_state == NULL); + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) +#define fcs_caas_check_and_insert() \ + *existing_state = g_tree_lookup(instance->tree, (gpointer)new_state); \ + if (*existing_state == NULL) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + g_tree_insert( \ + instance->tree, \ + (gpointer)new_state, \ + (gpointer)new_state \ + ); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + } + + + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +#define fcs_caas_check_and_insert() \ + *existing_state = g_hash_table_lookup(instance->hash, (gpointer)new_state); \ + if (*existing_state == NULL) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + g_hash_table_insert( \ + instance->hash, \ + (gpointer)new_state, \ + (gpointer)new_state \ + \ + ); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + } + +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) +#define fcs_caas_check_and_insert() \ + { \ + DBT key, value; \ + key.data = new_state; \ + key.size = sizeof(*new_state); \ + if (instance->db->get( \ + instance->db, \ + NULL, \ + &key, \ + &value, \ + 0 \ + ) == 0) \ + { \ + /* The new state was not found. Let's insert it. \ + * The value must be the same as the key, so g_tree_lookup() \ + * will return it. */ \ + \ + value.data = key.data; \ + value.size = key.size; \ + instance->db->put( \ + instance->db, \ + NULL, \ + &key, \ + &value, \ + 0); \ + check = 1; \ + } \ + else \ + { \ + check = 0; \ + *existing_state = (fcs_state_with_locations_t *)(value.data); \ + } \ + } + +#else +#error no define +#endif + +#ifdef INDIRECT_STACK_STATES +static GCC_INLINE void freecell_solver_cache_stacks( + freecell_solver_hard_thread_t * hard_thread, + fcs_state_with_locations_t * new_state + ) +{ + int a; +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) + SFO_hash_value_t hash_value_int; +#endif + void * cached_stack; + fcs_card_t * new_ptr; + freecell_solver_instance_t * instance = hard_thread->instance; + int stacks_num = instance->stacks_num; + + + for(a=0 ; astacks_copy_on_write_flags & (1 << a))) + { + continue; + } + /* new_state->s.stacks[a] = realloc(new_state->s.stacks[a], fcs_stack_len(new_state->s, a)+1); */ + fcs_compact_alloc_typed_ptr_into_var(new_ptr, char, hard_thread->stacks_allocator, (fcs_stack_len(new_state->s, a)+1)); + memcpy(new_ptr, new_state->s.stacks[a], (fcs_stack_len(new_state->s, a)+1)); + new_state->s.stacks[a] = new_ptr; + +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH + /* Calculate the hash value for the stack */ + /* This hash function was ripped from the Perl source code. + * (It is not derived work however). */ + { + const char * s_ptr = (char*)(new_state->s.stacks[a]); + const char * s_end = s_ptr+fcs_stack_len(new_state->s, a)+1; + hash_value_int = 0; + while (s_ptr < s_end) + { + hash_value_int += (hash_value_int << 5) + *(s_ptr++); + } + hash_value_int += (hash_value_int >> 5); + } + + if (hash_value_int < 0) + { + /* + * This is a bit mask that nullifies the sign bit of the + * number so it will always be positive + * */ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); + } + + cached_stack = (void *)freecell_solver_hash_insert( + instance->stacks_hash, + new_state->s.stacks[a], + (SFO_hash_value_t)freecell_solver_lookup2_hash_function( + (ub1 *)new_state->s.stacks[a], + (fcs_stack_len(new_state->s, a)+1), + 24 + ), + hash_value_int, + 1 + ); + +#define replace_with_cached(condition_expr) \ + if (cached_stack != NULL) \ + { \ + fcs_compact_alloc_release(hard_thread->stacks_allocator); \ + new_state->s.stacks[a] = cached_stack; \ + } + + replace_with_cached(cached_stack != NULL); + +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + cached_stack = +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + avl_insert( +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + rb_insert( +#endif + instance->stacks_tree, + new_state->s.stacks[a] + ); +#if 0 + ) /* In order to settle gvim and other editors that + are keen on parenthesis matching */ +#endif + + replace_with_cached(cached_stack != NULL); + +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + cached_stack = (void *)rbsearch( + new_state->s.stacks[a], + instance->stacks_tree + ); + + replace_with_cached(cached_stack != new_state->s.stacks[a]); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + cached_stack = g_tree_lookup( + instance->stacks_tree, + (gpointer)new_state->s.stacks[a] + ); + + /* replace_with_cached contains an if statement */ + replace_with_cached(cached_stack != NULL) + else + { + g_tree_insert( + instance->stacks_tree, + (gpointer)new_state->s.stacks[a], + (gpointer)new_state->s.stacks[a] + ); + } +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + cached_stack = g_hash_table_lookup( + instance->stacks_hash, + (gconstpointer)new_state->s.stacks[a] + ); + replace_with_cached(cached_stack != NULL) + else + { + g_hash_table_insert( + instance->stacks_hash, + (gpointer)new_state->s.stacks[a], + (gpointer)new_state->s.stacks[a] + ); + } +#endif + } +} +#else +#define freecell_solver_cache_stacks(instance, new_state) +#endif + + +#ifdef FCS_WITH_TALONS +void freecell_solver_cache_talon( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * new_state + ) +{ + void * cached_talon; + SFO_hash_value_t hash_value_int; + + new_state->s.talon = realloc(new_state->s.talon, fcs_klondike_talon_len(new_state->s)+1); +#error Add Hash Code + hash_value_int = *(SFO_hash_value_t*)instance->hash_value; + if (hash_value_int < 0) + { + /* + * This is a bit mask that nullifies the sign bit of the + * number so it will always be positive + * */ + hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); + } + + cached_talon = (void *)freecell_solver_hash_insert( + instance->talons_hash, + new_state->s.talon, + hash_value_int, + 1 + ); + + if (cached_talon != NULL) + { + free(new_state->s.talon); + new_state->s.talon = cached_talon; + } +} +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +guint freecell_solver_hash_function(gconstpointer key) +{ + guint hash_value; + const char * s_ptr = (char*)key; + const char * s_end = s_ptr+sizeof(fcs_state_t); + hash_value = 0; + while (s_ptr < s_end) + { + hash_value += (hash_value << 5) + *(s_ptr++); + } + hash_value += (hash_value >> 5); + + return hash_value; +} +#endif + + +/* + * check_and_add_state() does the following things: + * + * 1. Check if the number of iterations exceeded its maximum, and if so + * return FCS_STATE_EXCEEDS_MAX_NUM_TIMES in order to terminate the + * solving process. + * 2. Check if the maximal depth was reached and if so return + * FCS_STATE_EXCEEDS_MAX_DEPTH + * 3. Canonize the state. + * 4. Check if the state is already found in the collection of the states + * that were already checked. + * If it is: + * + * 5a. Return FCS_STATE_ALREADY_EXISTS + * + * If it isn't: + * + * 5b. Call solve_for_state() on the board. + * + * */ + +GCC_INLINE int freecell_solver_check_and_add_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * new_state, + fcs_state_with_locations_t * * existing_state + ) +{ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + SFO_hash_value_t hash_value_int; +#endif +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + fcs_state_with_locations_t * * pos_ptr; + int found; +#endif + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int check; + + if (check_if_limits_exceeded()) + { + return FCS_STATE_BEGIN_SUSPEND_PROCESS; + } + + freecell_solver_cache_stacks(hard_thread, new_state); + + fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); + + fcs_caas_check_and_insert(); + if (check) + { + /* The new state was not found in the cache, and it was already inserted */ + if (new_state->parent) + { + new_state->parent->num_active_children++; + } + instance->num_states_in_collection++; + + if (new_state->moves_to_parent != NULL) + { + new_state->moves_to_parent = + freecell_solver_move_stack_compact_allocate( + hard_thread, + new_state->moves_to_parent + ); + } + + return FCS_STATE_DOES_NOT_EXIST; + } + else + { + return FCS_STATE_ALREADY_EXISTS; + } +} + + + +/* + * This implementation crashes for some reason, so don't use it. + * + * */ + + +#if 0 + +static char meaningless_data[16] = "Hello World!"; + +int freecell_solver_check_and_add_state(freecell_solver_instance_t * instance, fcs_state_with_locations_t * new_state, int depth) +{ + DBT key, value; + + if ((instance->max_num_times >= 0) && + (instance->max_num_times <= instance->num_times)) + { + return FCS_STATE_EXCEEDS_MAX_NUM_TIMES; + } + + if ((instance->max_depth >= 0) && + (instance->max_depth <= depth)) + { + return FCS_STATE_EXCEEDS_MAX_DEPTH; + } + + fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num); + + freecell_solver_cache_stacks(instance, new_state); + + key.data = new_state; + key.size = sizeof(*new_state); + + if (instance->db->get( + instance->db, + NULL, + &key, + &value, + 0 + ) == 0) + { + /* The new state was not found. Let's insert it. + * The value should be non-NULL or else g_hash_table_lookup() will + * return NULL even if it exists. */ + + value.data = meaningless_data; + value.size = 8; + instance->db->put( + instance->db, + NULL, + &key, + &value, + 0); + if (freecell_solver_solve_for_state(instance, new_state, depth+1,0) == FCS_STATE_WAS_SOLVED) + { + return FCS_STATE_WAS_SOLVED; + } + else + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + } + else + { + /* free (value.data) ; */ + return FCS_STATE_ALREADY_EXISTS; + } +} + + +#endif + +#endif /* #ifndef FC_SOLVE__CAAS_C */ diff --git a/kpat/freecell-solver/caas.h b/kpat/freecell-solver/caas.h new file mode 100644 index 00000000..e1969488 --- /dev/null +++ b/kpat/freecell-solver/caas.h @@ -0,0 +1,28 @@ + +#ifndef FC_SOLVE__CAAS_H +#define FC_SOLVE__CAAS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #define FCS_USE_INLINE */ + +/* + * check_and_add_state is defined in caas.c. + * + * DFS stands for Depth First Search which is the type of scan Freecell + * Solver uses to solve a given board. + * */ + +extern int freecell_solver_check_and_add_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * new_state, + fcs_state_with_locations_t * * existing_state + ); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__CAAS_H */ diff --git a/kpat/freecell-solver/card.c b/kpat/freecell-solver/card.c new file mode 100644 index 00000000..d4df80f7 --- /dev/null +++ b/kpat/freecell-solver/card.c @@ -0,0 +1,286 @@ +/* + * card.c - functions to convert cards and card components to and from + * its user representation. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include + +#include "card.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#define uc(c) ( (((c)>='a') && ((c)<='z')) ? ((c)+'A'-'a') : (c)) + +/* + * This function converts a card number from its user representation + * (e.g: "A", "K", "9") to its card number that can be used by + * the program. + * */ +int freecell_solver_u2p_card_number(const char * string) +{ + char rest; + + while (1) + { + rest = uc(*string); + + if ((rest == '\0') || (rest == ' ') || (rest == '\t')) + { + return 0; + } + if (rest == 'A') + { + return 1; + } + else if (rest =='J') + { + return 11; + } + else if (rest == 'Q') + { + return 12; + } + else if (rest == 'K') + { + return 13; + } + else if (rest == '1') + { + return (*(string+1) == '0')?10:1; + } + else if ((rest == '0') || (rest == 'T')) + { + return 10; + } + else if ((rest >= '2') && (rest <= '9')) + { + return (rest-'0'); + } + else + { + string++; + } + } +} + + +/* + * This function converts a string containing a suit letter (that is + * one of H,S,D,C) into its suit ID. + * + * The suit letter may come somewhat after the beginning of the string. + * + * */ +int freecell_solver_u2p_suit(const char * suit) +{ + char c; + + c = uc(*suit); + while ( + (c != 'H') && + (c != 'S') && + (c != 'D') && + (c != 'C') && + (c != ' ') && + (c != '\0')) + { + suit++; + c = uc(*suit); + } + + if (c == 'H') + return 0; + else if (c == 'C') + return 1; + else if (c == 'D') + return 2; + else if (c == 'S') + return 3; + else + return 0; +} + +static int fcs_u2p_flipped_status(const char * str) +{ + while (*str != '\0') + { + if ((*str != ' ') && (*str != '\t')) + { + return (*str == '<'); + } + str++; + } + return 0; +} +/* + * This function converts an entire card from its string representations + * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type. + * */ +fcs_card_t freecell_solver_card_user2perl(const char * str) +{ + fcs_card_t card; +#if defined(COMPACT_STATES)||defined(INDIRECT_STACK_STATES) + card = 0; +#endif + fcs_card_set_flipped(card, fcs_u2p_flipped_status(str)); + fcs_card_set_num(card, fcs_u2p_card_number(str)); + fcs_card_set_suit(card, fcs_u2p_suit(str)); + + return card; +} + + +/* + * Those strings contain the string representations of the different cards. + * If CARD_DEBUG_PRES is defined then an asterisk is printed as an empty card. + * + * Notice that there are two of them: one prints 10 and one prints T for the + * 10 card. + * + * */ +#ifdef CARD_DEBUG_PRES +static char card_map_3_10[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; + +static char card_map_3_T[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" }; + +#else +static char card_map_3_10[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" }; + +static char card_map_3_T[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" }; + +#endif + +/* + * Converts a card_number from its internal representation to a string. + * + * num - the card number + * str - the string to output to. + * card_num_is_null - a pointer to a bool that indicates whether + * the card number is out of range or equal to zero + * t - whether 10 should be printed as T or not. + * flipped - whether the card is face down + * */ +char * freecell_solver_p2u_card_number( + int num, + char * str, + int * card_num_is_null, + int t, + int flipped) +{ + char (*card_map_3) [4] = card_map_3_10; + if (t) + { + card_map_3 = card_map_3_T; + } +#ifdef CARD_DEBUG_PRES + if (0) + { + } +#else + if (flipped) + { + strncpy(str, "*", 2); + *card_num_is_null = 0; + } +#endif + else + { + if ((num >= 0) && (num <= 13)) + { + strncpy(str, card_map_3[num], strlen(card_map_3[num])+1); + *card_num_is_null = (num == 0); + } + else + { + strncpy(str, card_map_3[0], strlen(card_map_3[0])+1); + *card_num_is_null = 1; + } + } + return str; +} + +/* + * Converts a suit to its user representation. + * + * */ +char * freecell_solver_p2u_suit(int suit, char * str, int card_num_is_null, int flipped) +{ +#ifndef CARD_DEBUG_PRES + if (flipped) + { + strncpy(str, "*", 2); + } + else +#endif + if (suit == 0) + { + if (card_num_is_null) +#ifdef CARD_DEBUG_PRES + strncpy(str, "*", 2); +#else + strncpy(str, " ", 2); +#endif + else + strncpy(str, "H", 2); + } + else if (suit == 1) + strncpy(str, "C", 2); + else if (suit == 2) + strncpy(str, "D", 2); + else if (suit == 3) + strncpy(str, "S", 2); + else + strncpy(str, " ", 2); + return str; +} + +/* + * Convert an entire card to its user representation. + * + * */ +char * freecell_solver_card_perl2user(fcs_card_t card, char * str, int t) +{ + int card_num_is_null; +#ifdef CARD_DEBUG_PRES + if (fcs_card_get_flipped(card)) + { + *str = '<'; + str++; + } +#endif + + fcs_p2u_card_number( + fcs_card_card_num(card), + str, + &card_num_is_null, + t, + fcs_card_get_flipped(card) + ); + /* + * Notice that if card_num_is_null is found to be true + * it will affect the output of the suit too. + * + * */ + fcs_p2u_suit( + fcs_card_suit(card), + str+strlen(str), + card_num_is_null, + fcs_card_get_flipped(card) + ); + +#ifdef CARD_DEBUG_PRES + if (fcs_card_get_flipped(card)) + { + strcat(str, ">"); + } +#endif + + return str; +} diff --git a/kpat/freecell-solver/card.h b/kpat/freecell-solver/card.h new file mode 100644 index 00000000..d67ea645 --- /dev/null +++ b/kpat/freecell-solver/card.h @@ -0,0 +1,100 @@ +/* + * card.h - header file for card functions for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + + +#ifndef FC_SOLVE__CARD_H +#define FC_SOLVE__CARD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FC_SOLVE__STATE_H +#include "state.h" +#endif + +/* + * This function converts an entire card from its string representations + * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type. + * */ +extern fcs_card_t freecell_solver_card_user2perl(const char * str); +#define fcs_card_user2perl(str) (freecell_solver_card_user2perl(str)) + + + +/* + * Convert an entire card to its user representation. + * + * */ +extern char * freecell_solver_card_perl2user( + fcs_card_t card, + char * str, + int t + ); + +#define fcs_card_perl2user(card,str,t) (freecell_solver_card_perl2user((card),(str),(t))) + + + +/* + * Converts a card_number from its internal representation to a string. + * + * num - the card number + * str - the string to output to. + * card_num_is_null - a pointer to a bool that indicates whether + * the card number is out of range or equal to zero + * t - whether 10 should be printed as T or not. + * */ +extern char * freecell_solver_p2u_card_number( + int num, + char * str, + int * card_num_is_null, + int t, + int flipped + ); + +#define fcs_p2u_card_number(num,str,card_num_is_null,t,flipped) \ + (freecell_solver_p2u_card_number((num),(str),(card_num_is_null),(t),(flipped))) + +/* + * Converts a suit to its user representation. + * + * */ +char * freecell_solver_p2u_suit( + int suit, + char * str, + int card_num_is_null, + int flipped + ); + +#define fcs_p2u_suit(suit,str,card_num_is_null,flipped) \ + (freecell_solver_p2u_suit((suit),(str),(card_num_is_null),(flipped))) + +/* + * This function converts a card number from its user representation + * (e.g: "A", "K", "9") to its card number that can be used by + * the program. + * */ +extern int freecell_solver_u2p_card_number(const char * string); +#define fcs_u2p_card_number(string) (freecell_solver_u2p_card_number(string)) + +/* + * This function converts a string containing a suit letter (that is + * one of H,S,D,C) into its suit ID. + * + * The suit letter may come somewhat after the beginning of the string. + * + * */ +extern int freecell_solver_u2p_suit(const char * deck); +#define fcs_u2p_suit(deck) (freecell_solver_u2p_suit(deck)) + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__CARD_H */ diff --git a/kpat/freecell-solver/cl_chop.c b/kpat/freecell-solver/cl_chop.c new file mode 100644 index 00000000..4bb82aab --- /dev/null +++ b/kpat/freecell-solver/cl_chop.c @@ -0,0 +1,245 @@ +#include +#include +#include + +#include "cl_chop.h" + +#ifdef DMALLOC +#include +#endif + +#define ARGS_MAN_GROW_BY 30 + +args_man_t * freecell_solver_args_man_alloc(void) +{ + args_man_t * ret; + ret = malloc(sizeof(args_man_t)); + ret->argc = 0; + ret->max_num_argv = ARGS_MAN_GROW_BY; + ret->argv = malloc(sizeof(ret->argv[0]) * ret->max_num_argv); + return ret; +} + +void freecell_solver_args_man_free(args_man_t * manager) +{ + int a; + for(a=0;aargc;a++) + { + free(manager->argv[a]); + } + free(manager->argv); + free(manager); +} + +#define skip_ws() { while((*s == ' ') || (*s == '\t')) { s++; } } +#define skip_non_ws() { while((*s != ' ') && (*s != '\t') && (*s)) { s++; }} + +#define add_to_last_arg(c) \ + { \ + *(last_arg_ptr++) = (c); \ + if (last_arg_ptr == last_arg_end) \ + { \ + new_last_arg = realloc(last_arg, (size_t)(last_arg_end-last_arg+1024)); \ + last_arg_ptr += new_last_arg - last_arg; \ + last_arg_end += new_last_arg - last_arg + 1024; \ + last_arg = new_last_arg; \ + } \ + } + +#define push_args_last_arg() { \ + new_arg = malloc((size_t)(last_arg_ptr-last_arg+1)); \ + strncpy(new_arg, last_arg, (size_t)(last_arg_ptr-last_arg)); \ + new_arg[last_arg_ptr-last_arg] = '\0'; \ + manager->argv[manager->argc] = new_arg; \ + manager->argc++; \ + if (manager->argc == manager->max_num_argv) \ + { \ + manager->max_num_argv += ARGS_MAN_GROW_BY; \ + manager->argv = realloc(manager->argv, sizeof(manager->argv[0]) * manager->max_num_argv); \ + } \ + \ + /* Reset last_arg_ptr so we will have an entirely new argument */ \ + last_arg_ptr = last_arg; \ + } + +#define is_whitespace(c) \ + (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) + +int freecell_solver_args_man_chop(args_man_t * manager, char * string) +{ + char * s = string; + char * new_arg; + char * last_arg, * last_arg_ptr, * last_arg_end, * new_last_arg; + char next_char; + int in_arg; + + last_arg_ptr = last_arg = malloc(1024); + last_arg_end = last_arg + 1023; + + while (*s != '\0') + { +LOOP_START: + in_arg = 0; + while (is_whitespace(*s)) + { + s++; + } + if (*s == '\0') + { + break; + } + if (*s == '#') + { + in_arg = 0; + /* Skip to the next line */ + while((*s != '\0') && (*s != '\n')) + { + s++; + } + continue; + } +AFTER_WS: + while ((*s != ' ') && (*s != '\t') && (*s != '\n') && + (*s != '\r') && + (*s != '\\') && (*s != '\"') && (*s != '\0') && + (*s != '#')) + { + in_arg = 1; + add_to_last_arg(*s); + s++; + } + + + if ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s == '\0') || (*s == '\r')) + { +NEXT_ARG: + push_args_last_arg(); + in_arg = 0; + + if (*s == '\0') + { + break; + } + } + else if (*s == '\\') + { + char next_char = *(++s); + s++; + if (next_char == '\0') + { + s--; + goto NEXT_ARG; + } + else if ((next_char == '\n') || (next_char == '\r')) + { + if (in_arg) + { + goto AFTER_WS; + } + else + { + goto LOOP_START; + } + } + else + { + add_to_last_arg(next_char); + } + } + else if (*s == '\"') + { + s++; + in_arg = 1; + while ((*s != '\"') && (*s != '\0')) + { + if (*s == '\\') + { + next_char = *(++s); + if (next_char == '\0') + { + push_args_last_arg(); + + goto END_OF_LOOP; + } + else if ((next_char == '\n') || (next_char == '\r')) + { + /* Do nothing */ + } + else if ((next_char == '\\') || (next_char == '\"')) + { + add_to_last_arg(next_char); + } + else + { + add_to_last_arg('\\'); + add_to_last_arg(next_char); + } + } + else + { + add_to_last_arg(*s); + } + s++; + } + s++; + goto AFTER_WS; + } + else if (*s == '#') + { + in_arg = 0; + /* Skip to the next line */ + while((*s != '\0') && (*s != '\n')) + { + s++; + } + goto NEXT_ARG; + } + } +END_OF_LOOP: + + free(last_arg); + + return 0; +} + +#ifdef CMD_LINE_CHOP_WITH_MAIN +int main(int argc, char * * argv) +{ + args_man_t * args_man; + char * string; + +#if 0 + string = argv[1]; +#else + { + FILE * f; + + f = fopen(argv[1],"rb"); + string = calloc(4096,1); + fread(string, 4095, 1, f); + fclose(f); + } + +#endif + + /* Initialize an arg man */ + args_man = args_man_alloc(); + /* Call it on string */ + args_man_chop(args_man, string); + + /* Now use args_man->argc and args_man->argv */ + { + int a; + for(a=0;aargc;a++) + { + printf("argv[%i] = \"%s\"\n", a, args_man->argv[a]); + } + } + /* Free the allocated memory */ + args_man_free(args_man); + + free(string); + + return 0; +} +#endif diff --git a/kpat/freecell-solver/cl_chop.h b/kpat/freecell-solver/cl_chop.h new file mode 100644 index 00000000..3f6a873e --- /dev/null +++ b/kpat/freecell-solver/cl_chop.h @@ -0,0 +1,19 @@ + +#ifndef FC_SOLVE__CMD_LINE_CHOP_H +#define FC_SOLVE__CMD_LINE_CHOP_H + +struct args_man_struct +{ + int argc; + char * * argv; + int max_num_argv; +}; + +typedef struct args_man_struct args_man_t; + +extern args_man_t * freecell_solver_args_man_alloc(void); +extern void freecell_solver_args_man_free(args_man_t * manager); +extern int freecell_solver_args_man_chop(args_man_t * manager, char * string); + +#endif /* #ifndef FC_SOLVE__CMD_LINE_CHOP_H */ + diff --git a/kpat/freecell-solver/cmd_line.c b/kpat/freecell-solver/cmd_line.c new file mode 100644 index 00000000..63fbf6c9 --- /dev/null +++ b/kpat/freecell-solver/cmd_line.c @@ -0,0 +1,964 @@ +#include +#include +#include +#include + +#include "fcs_user.h" +#include "fcs_cl.h" +#include "cl_chop.h" + +#include "prefix.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static int read_preset(char * preset_name, args_man_t * * args, char * * opened_files_dir_to_assign, char * user_preset_dir) +{ + int ret_code = 1; + char * home_dir_presetrc = NULL, * global_presetrc = NULL, * env_var_presetrc = NULL; + char * path; + char * * presetrc_pathes[5] = {&env_var_presetrc, &home_dir_presetrc, &global_presetrc, &user_preset_dir, NULL}; + int path_idx; + char line[8192]; + FILE * f = NULL; + char * fgets_ret; + char * opened_files_dir = NULL; + int read_next_preset = 0; + + { + char * home_dir; + home_dir = getenv("HOME"); + if (home_dir) + { + home_dir_presetrc = malloc(strlen(home_dir) + 50); + sprintf(home_dir_presetrc, + "%s/.freecell-solver/presetrc", home_dir + ); + } + } + env_var_presetrc = getenv("FREECELL_SOLVER_PRESETRC"); + + global_presetrc = (FREECELL_SOLVER_PKG_DATA_DIR "/presetrc"); + + for(path_idx=0;(presetrc_pathes[path_idx] != NULL) ; path_idx++) + { + path = (*presetrc_pathes[path_idx]); + if (path == NULL) + { + continue; + } + f = fopen(path, "rt"); + if (f == NULL) + { + continue; + } + while(1) + { + fgets_ret = fgets(line, sizeof(line), f); + if (fgets_ret == NULL) + { + break; + } + if (!strncmp(line, "dir=", 4)) + { +#define nullify_newline() \ + { \ + char * s; \ + \ + s = strchr(line, '\n'); \ + if (s != NULL) \ + { \ + *s = '\0'; \ + } \ + } + nullify_newline(); + + if (opened_files_dir != NULL) + { + free(opened_files_dir); + } + opened_files_dir = strdup(line+4); + } + else if (!strncmp(line, "name=", 5)) + { + nullify_newline(); + if (!strcmp(line+5, preset_name)) + { + read_next_preset = 1; + } + } + else if (!strncmp(line, "command=", 8)) + { + if (read_next_preset) + { + *args = freecell_solver_args_man_alloc(); + freecell_solver_args_man_chop(*args, line+8); + ret_code = 0; + goto HAVE_PRESET; + } + } + } + fclose(f); + f = NULL; +#undef nullify_newline + } +HAVE_PRESET: + + if (f) + { + fclose(f); + } + + if (home_dir_presetrc) + { + free(home_dir_presetrc); + } + + if (ret_code == 0) + { + *opened_files_dir_to_assign = opened_files_dir; + } + else + { + if (opened_files_dir) + { + free(opened_files_dir); + } + } + + return ret_code; +} + + +int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg, + int file_nesting_count, + char * opened_files_dir + ) +{ + int arg; + char * * known_param; + int num_to_skip; + int callback_ret; + int ret; + + *error_string = NULL; + + for(arg=start_arg;arg '9') && (*start_num < '0') && (*start_num != '\0')) + { + start_num++; + } + if (*start_num == '\0') + { + break; + } + end_num = start_num+1; + while ((((*end_num >= '0') && (*end_num <= '9')) || (*end_num == '.')) && (*end_num != '\0')) + { + end_num++; + } + num_copy = malloc(end_num-start_num+1); + memcpy(num_copy, start_num, end_num-start_num); + num_copy[end_num-start_num] = '\0'; + freecell_solver_user_set_a_star_weight( + instance, + a, + atof(num_copy) + ); + free(num_copy); + start_num=end_num+1; + } + } + } + else if ((!strcmp(argv[arg], "-opt")) || (!strcmp(argv[arg], "--optimize-solution"))) + { + freecell_solver_user_set_solution_optimization(instance, 1); + } + else if ((!strcmp(argv[arg], "-seed"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_set_random_seed(instance, atoi(argv[arg])); + } + else if ((!strcmp(argv[arg], "-mss")) || (!strcmp(argv[arg], "--max-stored-states"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_limit_num_states_in_collection( + instance, + atoi(argv[arg]) + ); + } + else if ( + (!strcmp(argv[arg], "-nst")) || + (!strcmp(argv[arg], "--next-soft-thread")) || + (!strcmp(argv[arg], "-nht")) || + (!strcmp(argv[arg], "--next-hard-thread")) + ) + { + int ret; + int is_st = ((!strcmp(argv[arg], "-nst")) || (!strcmp(argv[arg], "--next-soft-thread"))); + + ret = + is_st ? + freecell_solver_user_next_soft_thread(instance) : + freecell_solver_user_next_hard_thread(instance) + ; + + if (ret) + { + char * errstr; + + errstr = strdup("The maximal number of soft threads has been exceeded\n"); + + *error_string = errstr; + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + } + else if ((!strcmp(argv[arg], "-step")) || (!strcmp(argv[arg], "--soft-thread-step"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + freecell_solver_user_set_soft_thread_step( + instance, + atoi(argv[arg]) + ); + } + else if ((!strcmp(argv[arg], "--reparent-states"))) + { + freecell_solver_user_set_reparent_states( + instance, + 1 + ); + } + else if ((!strcmp(argv[arg], "--calc-real-depth"))) + { + freecell_solver_user_set_calc_real_depth( + instance, + 1); + } + else if ((!strcmp(argv[arg], "--st-name"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + freecell_solver_user_set_soft_thread_name(instance, argv[arg]); + } + else if ((!strcmp(argv[arg], "--prelude"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + freecell_solver_user_set_hard_thread_prelude(instance, argv[arg]); + } + else if ((!strcmp(argv[arg], "-opt-to")) || (!strcmp(argv[arg], "--optimization-tests-order"))) + { + char * fcs_user_errstr; + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + ret = freecell_solver_user_set_optimization_scan_tests_order( + instance, + argv[arg], + &fcs_user_errstr + ); + + if (ret != 0) + { + char * errstr = malloc(strlen(fcs_user_errstr)+500); + sprintf( + errstr, + "Error in the optimization scan's tests' order!\n%s\n", + fcs_user_errstr + ); + free(fcs_user_errstr); + + *error_string = errstr; + + *last_arg = arg; + return FCS_CMD_LINE_ERROR_IN_ARG; + } + } + else if ((!strcmp(argv[arg], "--scans-synergy"))) + { + int value; + + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + + if (!strcmp(argv[arg], "none")) + { + value = 0; + } + else if (!strcmp(argv[arg], "dead-end-marks")) + { + value = 1; + } + else + { + char * errstr; + + errstr = malloc(strlen(argv[arg])+500); + + sprintf(errstr, "Unknown scans' synergy type \"%s\"!\n", argv[arg]); + *last_arg = arg; + *error_string = errstr; + return FCS_CMD_LINE_ERROR_IN_ARG; + } + + freecell_solver_user_set_scans_synergy( + instance, + value + ); + } + else if ((!strcmp(argv[arg], "-ni")) || + (!strcmp(argv[arg], "--next-instance"))) + { + freecell_solver_user_next_instance(instance); + } + else if (!strcmp(argv[arg], "--reset")) + { + freecell_solver_user_reset(instance); + } + else if (!strcmp(argv[arg], "--read-from-file")) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + if (file_nesting_count == 0) + { + /* do nothing */ + } + else + { + int num_to_skip = 0; + char * s, * buffer; + FILE * f; + long file_len; + int ret; + size_t num_read; + args_man_t * args_man; + + s = argv[arg]; + while(isdigit(*s)) + { + s++; + } + if (*s == ',') + { + num_to_skip = atoi(argv[arg]); + s++; + } + + if (opened_files_dir) + { + char * complete_path; + + complete_path = malloc(strlen(opened_files_dir)+strlen(s)+1); + sprintf(complete_path, "%s%s", opened_files_dir, s); + f = fopen(complete_path, "rt"); + free(complete_path); + } + else + { + /* + * Initialize f to NULL so it will be initialized + * */ + f = NULL; + } + + /* Try to open from the local path */ + if (f == NULL) + { + f = fopen(s, "rt"); + } + + /* If we still could not open it return an error */ + if (f == NULL) + { + char * err_str; + + err_str = malloc(strlen(s)+100); + sprintf(err_str, + "Could not open file \"%s\"!\nQuitting.\n", + s); + + *error_string = err_str; + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + fseek(f, 0, SEEK_END); + file_len = ftell(f); + buffer=malloc(file_len+1); + if (buffer == NULL) + { + *error_string = strdup("Could not allocate enough memory to parse the file. Quitting.\n"); + fclose(f); + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + fseek(f,0,SEEK_SET); + num_read = fread(buffer, 1, file_len, f); + fclose(f); + buffer[num_read] = '\0'; + + args_man = freecell_solver_args_man_alloc(); + ret = freecell_solver_args_man_chop(args_man, buffer); + free(buffer); + if (ret != 0) + { + *error_string = + strdup("Could not parse the file. Quitting\n"); + freecell_solver_args_man_free(args_man); + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + + if (num_to_skip >= args_man->argc) + { + /* Do nothing */ + } + else + { + ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + args_man->argc - num_to_skip, + args_man->argv + num_to_skip, + 0, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)), + opened_files_dir + ); + + if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION) + { + /* Do nothing - continue */ + } + else if (ret != FCS_CMD_LINE_OK) + { + freecell_solver_args_man_free(args_man); + return ret; + } + } + freecell_solver_args_man_free(args_man); + } + } + else if ((!strcmp(argv[arg], "-l")) || (!strcmp(argv[arg], "--load-config"))) + { + arg++; + if (arg == argc) + { + *last_arg = arg-1; + return FCS_CMD_LINE_PARAM_WITH_NO_ARG; + } + { + int status; + args_man_t * preset_args = 0; + char * dir = NULL; + + status = read_preset(argv[arg], &preset_args, &dir, NULL); + if (status != 0) + { + char * err_str; + err_str = malloc(strlen(argv[arg]) + 100); + sprintf(err_str, "Unable to load the \"%s\" configuration!\n", argv[arg]); + *error_string = err_str; + + *last_arg = arg; + + return FCS_CMD_LINE_ERROR_IN_ARG; + } + else + { + ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + preset_args->argc, + preset_args->argv, + 0, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)), + dir ? dir : opened_files_dir + ); + + if (dir) + { + free(dir); + } + freecell_solver_args_man_free(preset_args); + + if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION) + { + /* Do nothing - continue */ + } + else if (ret != FCS_CMD_LINE_OK) + { + return ret; + } + } + } + } + else + { + *last_arg = arg; + return FCS_CMD_LINE_UNRECOGNIZED_OPTION; + } + } + + *last_arg = arg; + return FCS_CMD_LINE_OK; +} + +int freecell_solver_user_cmd_line_parse_args( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg + ) +{ + return freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + instance, + argc, + argv, + start_arg, + known_parameters, + callback, + callback_context, + error_string, + last_arg, + -1, + NULL + ); +} + diff --git a/kpat/freecell-solver/fcs.h b/kpat/freecell-solver/fcs.h new file mode 100644 index 00000000..43300310 --- /dev/null +++ b/kpat/freecell-solver/fcs.h @@ -0,0 +1,797 @@ +/* + * fcs.h - header file of freecell_solver_instance and of user-level + * functions for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_H +#define FC_SOLVE__FCS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fcs_config.h" +#include "state.h" +#include "move.h" +#include "fcs_enums.h" + +#include "rand.h" + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)) + +#include + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)) + +#include + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)) + +#include + +/* #define TREE_IMP_PREFIX(func_name) rb_##func_name */ + +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) || (defined(INDIRECT_STACK_STATES) && ((FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH))) + +#include + +#endif + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + +#include "fcs_hash.h" + +#endif + +#ifdef INDIRECT_STACK_STATES +#include "fcs_hash.h" + +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) +#include +#include +#include +#endif + +#include "pqueue.h" + +#include "alloc.h" + +/* + * This is a linked list item that is used to implement a queue for the BFS + * scan. + * */ +struct fcs_states_linked_list_item_struct +{ + fcs_state_with_locations_t * s; + struct fcs_states_linked_list_item_struct * next; +}; + +typedef struct fcs_states_linked_list_item_struct fcs_states_linked_list_item_t; + +/* + * Conventions for use of the tests' order flags: + * A test that should be scanned sequentially should have both flags cleared. + * The first test in its random group should have both flags set. All the + * other tests in the group should contain the FLAG_RANDOM flag. + * + * For instance: 123(45)(67)8 translates into: + * 1 , 2, 3, 4|RANDOM|START_RANDOM_GROUP, 5|RANDOM, + * 6|RANDOM_START_RANDOM_GROUP, 7|RANDOM, 8 + * + * */ +enum FCS_TESTS_ORDER_FLAGS +{ + FCS_TEST_ORDER_NO_FLAGS_MASK = 0xFFFFFF, + FCS_TEST_ORDER_FLAG_RANDOM = 0x1000000, + FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP = 0x2000000 +}; + +#ifdef FCS_WITH_TALONS +#define FCS_TESTS_NUM 27 +#else +#define FCS_TESTS_NUM 25 +#endif + +/* + * Declare these structures because they will be used within + * freecell_solver_instance, and they will contain a pointer to it. + * */ +struct freecell_solver_hard_thread_struct; +struct freecell_solver_soft_thread_struct; + +typedef struct freecell_solver_hard_thread_struct freecell_solver_hard_thread_t; + +struct fcs_tests_order_struct +{ + int num; + int * tests; + int max_num; +}; + +typedef struct fcs_tests_order_struct fcs_tests_order_t; + +typedef struct freecell_solver_instance +{ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + /* The sort-margin */ + fcs_state_with_locations_t * indirect_prev_states_margin[PREV_STATES_SORT_MARGIN]; + + /* The number of states in the sort margin */ + int num_prev_states_margin; + + /* The sorted cached states, their number and their maximal size. + * max_num_indirect_prev_states may increase as the + * indirect_prev_states is realloced. + * */ + fcs_state_with_locations_t * * indirect_prev_states; + int num_indirect_prev_states; + int max_num_indirect_prev_states; +#endif + + /* The number of states that were checked by the solving algorithm. + * Badly named, should be renamed to num_iters or num_checked_states */ + int num_times; + + /* + * A move stack that contains the moves leading to the solution. + * + * It is created only after the solution was found by swallowing + * all the stacks of each depth. + * */ + fcs_move_stack_t * solution_moves; + + /* + * Limits for the maximal depth and for the maximal number of checked + * states. max_num_times is useful because it enables the process to + * stop before it consumes too much memory. + * + * max_depth is quite dangerous because it blocks some intermediate moves + * and doesn't allow a program to fully reach its solution. + * + * */ + int max_depth; + int max_num_times; + + /* + * The debug_iter_output variables provide a programmer programmable way + * to debug the algorithm while it is running. This works well for DFS + * and Soft-DFS scans but at present support for A* and BFS is not + * too good, as its hard to tell which state came from which parent state. + * + * debug_iter_output is a flag that indicates whether to use this feature + * at all. + * + * debug_iter_output_func is a pointer to the function that performs the + * debugging. + * + * debug_iter_output_context is a user-specified context for it, that + * may include data that is not included in the instance structure. + * + * This feature is used by the "-s" and "-i" flags of fc-solve-debug. + * */ + int debug_iter_output; + void (*debug_iter_output_func)( + void * context, + int iter_num, + int depth, + void * instance, + fcs_state_with_locations_t * state, + int parent_iter_num + ); + void * debug_iter_output_context; + + /* + * tree is the balanced binary tree that is used to store and index + * the checked states. + * + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + struct rbtree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + avl_tree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + rb_tree * tree; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + GTree * tree; +#endif + + /* + * hash is the hash table that is used to store the previous + * states of the scan. + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + GHashTable * hash; +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + SFO_hash_t * hash; +#endif + +#if defined(INDIRECT_STACK_STATES) + /* + * The storage mechanism for the stacks assuming INDIRECT_STACK_STATES is + * used. + * */ +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) + SFO_hash_t * stacks_hash; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + avl_tree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + rb_tree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + struct rbtree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + GTree * stacks_tree; +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + GHashTable * stacks_hash; +#endif +#endif + + /* + * Storing using Berkeley DB is not operational for some reason so + * pay no attention to it for the while + * */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + DB * db; +#endif + + /* + * The number of Freecells, Stacks and Foundations present in the game. + * + * freecells_num and stacks_num are variable and may be specified at + * the beginning of the execution of the algorithm. However, there + * is a maximal limit to them which is set in config.h. + * + * decks_num can be 4 or 8 + * */ + int freecells_num; + int stacks_num; + int decks_num; + + /* What two adjacent cards in the same sequence can be: */ + int sequences_are_built_by; + /* Whether an entire sequence can be moved from one place to the + * other regardless of the number of unoccupied Freecells there are. */ + int unlimited_sequence_move; + /* + * With what cards can empty stacks be filled with. + * */ + int empty_stacks_fill; + +#ifdef FCS_WITH_TALONS + /* + * The talon for Gypsy-like games. Since only the position changes from + * state to state. + * We can keep it here. + * + * */ + fcs_card_t * gypsy_talon; + + /* + * The length of the Gypsy talon + * */ + int gypsy_talon_len; + + int talon_type; + + /* The Klondike Talons' Cache */ + SFO_hash_t * talons_hash; + +#endif + + /* A flag that indicates whether to optimize the solution path + at the end of the scan */ + int optimize_solution_path; + + /* This is a place-holder for the initial state */ + fcs_state_with_locations_t * state_copy_ptr; + + /* This is the final state that the scan recommends to the + * interface + * */ + fcs_state_with_locations_t * final_state; + + /* + * This is the number of states in the state collection. + * + * It gives a rough estimate of the memory occupied by the instance. + * */ + int num_states_in_collection; + + /* + * A limit to the above. + * */ + int max_num_states_in_collection; + + int num_hard_threads; + struct freecell_solver_hard_thread_struct * * hard_threads; + + /* + * The next ID to allocate for a soft-thread. + * */ + int next_soft_thread_id; + + /* + * A persistent counters that os used to iterate over the + * threads one by one + * */ + int ht_idx; + + /* + * This is the master tests order. It is used to initialize all + * the new Soft-Threads. + * */ + fcs_tests_order_t instance_tests_order; + + /* + * This is the hard-thread used for the optimization scan. + * */ + struct freecell_solver_hard_thread_struct * optimization_thread; + + /* + * A counter that determines how many of the hard threads that belong + * to this hard thread have already finished. If it becomes num_hard_threads + * the instance terminates. + * */ + int num_hard_threads_finished; + + /* + * A flag that indicates whether or not to explicitly calculate + * the depth of a state that was reached. + * */ + int calc_real_depth; + + /* + * The tests order for the optimization scan as specified by the user. + * */ + int opt_tests_order_set; + + fcs_tests_order_t opt_tests_order; + + /* + * This flag indicates whether scans should or should not reparent the + * states their encounter to a lower depth in the depth tree + * */ + int to_reparent_states; + + /* + * This variable determines how the scans cooperate with each other. + * + * A value of 0 indicates that they don't and only share the same + * states collection. + * + * A value of 1 indicates that they mark states as dead-end, + * which may help or hinder other scans. + * */ + int scans_synergy; + +} freecell_solver_instance_t; + + + + +/***************************************************/ + + +struct fcs_prelude_item_struct +{ + int scan_idx; + int quota; +}; + +typedef struct fcs_prelude_item_struct fcs_prelude_item_t; + + +struct freecell_solver_hard_thread_struct +{ + freecell_solver_instance_t * instance; + + int num_soft_threads; + struct freecell_solver_soft_thread_struct * * soft_threads; + + /* + * The State Packs variables are used by all the state cache + * management routines. A pack stores as many states as can fit + * in a 64KB segment, and those variables manage an array of + * such packs. + * + * Such allocation is possible, because at the worst situation + * the last state is released. + * */ + fcs_state_with_locations_t * * state_packs; + int max_num_state_packs; + int num_state_packs; + int num_states_in_last_pack; + int state_pack_len; + + /* + * The hard thread count of how many states he checked himself. The + * instance num_times can be confusing because other threads modify it too. + * + * Thus, the soft thread switching should be done based on this variable + * */ + int num_times; + + /* + * The maximal limit for this variable. + * */ + int max_num_times; + + /* + * The Hard-Thread's global limit for the number of iterations + * to process + * */ + int ht_max_num_times; + + int num_times_step; + + /* + * This is the number of iterations that still have to be done for + * soft_threads[st_idx]. It is reset to (st_idx+1)->num_times_step + * when st_idx is incremented. + * */ + int num_times_left_for_soft_thread; + + /* + * These variables are used to compute the MD5 checksum of a state + * that is about to be checked. I decided to make them globals so + * they won't have to be re-allocated and freed all the time. + * + * Notice that it is only used with my internal hash implementation + * as GLib requires a dedicated hash function, which cannot + * access the instance. + * + * */ + + /* + * The index for the soft-thread that is currently processed + * */ + int st_idx; + + /* + * A counter that determines how many of the soft threads that belong + * to this hard thread have already finished. If it becomes num_soft_threads + * this thread is skipped. + * */ + int num_soft_threads_finished; + +#ifdef INDIRECT_STACK_STATES + /* + * This is the mechanism used to allocate memory for the stacks. + * */ + fcs_compact_allocator_t * stacks_allocator; +#endif + + /* + * This is a compact memory allocator for the move stacks associated + * with the states in the states collection. + * */ + fcs_compact_allocator_t * move_stacks_allocator; + + /* + * This is a move stack that is used and re-used by the + * tests functions of this hard thread + * */ + fcs_move_stack_t * reusable_move_stack; + +#ifdef INDIRECT_STACK_STATES + /* + * This is a buffer used to temporarily store the stacks of the duplicated + * state. + * */ + fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7]; +#else + fcs_card_t indirect_stacks_buffer[1]; +#endif + + char * prelude_as_string; + + int prelude_num_items; + int prelude_idx; + fcs_prelude_item_t * prelude; + +}; + + + + + +/********************************************/ + + + + + + + + +struct fcs_soft_dfs_stack_item_struct +{ + fcs_state_with_locations_t * state; + fcs_derived_states_list_t derived_states_list; + int current_state_index; + int test_index; + int num_freestacks; + int num_freecells; + int derived_states_random_indexes_max_size; + int * derived_states_random_indexes; +}; + +typedef struct fcs_soft_dfs_stack_item_struct fcs_soft_dfs_stack_item_t; + +struct freecell_solver_soft_thread_struct +{ + freecell_solver_hard_thread_t * hard_thread; + + /* + * The ID of the soft thread inside the instance. + * Used for the state-specific flags. + * */ + int id; + + /* + * The tests' order indicates which tests (i.e: kinds of multi-moves) to + * do at what order. This is most relevant to DFS and Soft-DFS. + * + * tests_order_num is the number of tests in the test's order. Notice + * that it can be lower than FCS_TESTS_NUM, thus enabling several tests + * to be removed completely. + * */ + fcs_tests_order_t tests_order; + + + /* + * The (temporary) max depth of the Soft-DFS scans) + * */ + int dfs_max_depth; + /* + * The method (i.e: DFS, Soft-DFS, BFS or A*) that is used by this + * instance. + * + * */ + int method; + + /* + * A place-holder for the original method of the scan in case + * it is replaced by FCS_METHOD_OPTIMIZE + * + * */ + int orig_method; + + /* + * A linked list that serves as the queue for the BFS scan. + * */ + fcs_states_linked_list_item_t * bfs_queue; + /* + * The last item in the linked list, so new items can be added at it, + * thus making it a queue. + * */ + fcs_states_linked_list_item_t * bfs_queue_last_item; + + /* + * The priority queue of the A* scan */ + PQUEUE * a_star_pqueue; + double a_star_initial_cards_under_sequences; + + /* + * The A* weights of the different A* tests. Those weights determine the + * commulative weight of the state. + * + * */ + double a_star_weights[5]; + + /* + * The first state to be checked by the scan. It is a kind of bootstrap + * for the algorithm. + * */ + fcs_state_with_locations_t * first_state_to_check; + + /* + * These are stacks used by the Soft-DFS for various uses. + * + * states_to_check[i] - an array of states to be checked next. Not all + * of them will be checked because it is possible that future states + * already visited them. + * + * states_to_check_move_stacks[i] - an array of move stacks that lead + * to those states. + * + * num_states_to_check[i] - the size of states_to_check[i] + * + * max_num_states_to_check[i] - the limit of pointers that can be + * placed in states_to_check[i] without resizing. + * + * current_state_indexes[i] - the index of the last checked state + * in depth i. + * + * test_indexes[i] - the index of the test that was last performed. + * FCS performs each test separately, so states_to_check[i] and + * friends will not be overpopulated. + * + * num_freestacks[i] - the number of unoccpied stacks that correspond + * to solution_states[i]. + * + * num_freecells[i] - ditto for the freecells. + * + * */ + + fcs_soft_dfs_stack_item_t * soft_dfs_info; + + /* The depth of the DFS stacks */ + int num_solution_states; + + /* + * A pseudo-random number generator for use in the random-DFS scan + * */ + fcs_rand_t * rand_gen; + + /* + * The initial seed of this random number generator + * */ + unsigned int rand_seed; + + + /* + * A flag that indicates if this soft thread have already been + * initialized. + * */ + int initialized; + + /* + * The number of iterations with which to process this scan + * */ + int num_times_step; + + /* + * A flag that indicates if this scan contains all the tests that + * are accessible to all the other scans + * */ + int is_a_complete_scan; + + /* + * A flag that indicates if this scan has completed a scan. Used by + * solve_instance() to skip to the next scan. + * */ + int is_finished; + + /* + * A malloced string that serves as an identification for the user. + * */ + char * name; +}; + +typedef struct freecell_solver_soft_thread_struct freecell_solver_soft_thread_t; + + +#define FCS_SOFT_DFS_STATES_TO_CHECK_GROW_BY 32 + +/* + * An enum that specifies the meaning of each A* weight. + * */ +#define FCS_A_STAR_WEIGHT_CARDS_OUT 0 +#define FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE 1 +#define FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES 2 +#define FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS 3 +#define FCS_A_STAR_WEIGHT_DEPTH 4 + +freecell_solver_instance_t * freecell_solver_alloc_instance(void); + +extern void freecell_solver_init_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_free_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_finish_instance( + freecell_solver_instance_t * instance + ); + +extern int freecell_solver_solve_instance( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * init_state + ); + +extern int freecell_solver_resume_instance( + freecell_solver_instance_t * instance + ); + +extern void freecell_solver_unresume_instance( + freecell_solver_instance_t * instance + ); + +extern freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread( + freecell_solver_instance_t * instance, + int ht_idx, + int st_idx + ); + +extern freecell_solver_soft_thread_t * freecell_solver_new_soft_thread( + freecell_solver_soft_thread_t * soft_thread + ); + +extern freecell_solver_soft_thread_t * freecell_solver_new_hard_thread( + freecell_solver_instance_t * instance + ); + +extern int freecell_solver_hard_dfs_solve_for_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int depth, + int ignore_osins + ); + +extern int freecell_solver_soft_dfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + +extern int freecell_solver_random_dfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + + +extern void freecell_solver_a_star_initialize_rater( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ); + +extern int freecell_solver_a_star_or_bfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume + ); + +extern int freecell_solver_hard_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread, + int depth + ); + +extern int freecell_solver_soft_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + +extern int freecell_solver_random_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + + +extern int freecell_solver_a_star_or_bfs_solve( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig + ); + +extern int freecell_solver_a_star_or_bfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread + ); + +extern int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume, + int to_randomize + ); + +extern void freecell_solver_recycle_instance( + freecell_solver_instance_t * instance + ); + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_H */ diff --git a/kpat/freecell-solver/fcs_cl.h b/kpat/freecell-solver/fcs_cl.h new file mode 100644 index 00000000..e739c98e --- /dev/null +++ b/kpat/freecell-solver/fcs_cl.h @@ -0,0 +1,65 @@ + +#ifndef FC_SOLVE__FCS_CL_H +#define FC_SOLVE__FCS_CL_H + +#include "fcs_user.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*freecell_solver_user_cmd_line_known_commands_callback_t) + ( + void * instance, + int argc, + char * argv[], + int arg_index, + int * num_to_skip, + int * ret, + void * context + ); + +enum FCS_CMD_LINE_CALLBACK_RET_VALUES +{ + FCS_CMD_LINE_OK, + FCS_CMD_LINE_SKIP, + FCS_CMD_LINE_STOP, + FCS_CMD_LINE_UNRECOGNIZED_OPTION, + FCS_CMD_LINE_PARAM_WITH_NO_ARG, + FCS_CMD_LINE_ERROR_IN_ARG, + + FCS_CMD_LINE_USER = 0x10000 +}; + +extern int freecell_solver_user_cmd_line_parse_args( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg + ); + +extern int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count( + void * instance, + int argc, + const char * argv[], + int start_arg, + char * * known_parameters, + freecell_solver_user_cmd_line_known_commands_callback_t callback, + void * callback_context, + char * * error_string, + int * last_arg, + int file_nesting_count, + char * opened_files_dir + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef FC_SOLVE__FCS_CL_H */ diff --git a/kpat/freecell-solver/fcs_config.h b/kpat/freecell-solver/fcs_config.h new file mode 100644 index 00000000..8a25205d --- /dev/null +++ b/kpat/freecell-solver/fcs_config.h @@ -0,0 +1,95 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ +/* + config.h - Configuration file for Freecell Solver + + Written by Shlomi Fish, 2000 + + This file is distributed under the public domain. + (It is not copyrighted). +*/ + +#ifndef FC_SOLVE__CONFIG_H +#define FC_SOLVE__CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* #undef DEBUG_STATES */ +/* #undef COMPACT_STATES */ +#define INDIRECT_STACK_STATES 1 + +/* #undef CARD_DEBUG_PRES */ + +/* + * Define this macro if the C compiler supports the keyword inline or + * a similar keyword that was found by Autoconf (and defined as inline). + * */ +#define HAVE_C_INLINE 1 + + +/* + The sort margin size for the previous states array. +*/ +#define PREV_STATES_SORT_MARGIN 32 +/* + The amount prev_states grow by each time it each resized. + Should be greater than 0 and in order for the program to be + efficient, should be much bigger than + PREV_STATES_SORT_MARGIN. +*/ +#define PREV_STATES_GROW_BY 128 + +/* + The amount the pack pointers array grows by. Shouldn't be too high + because it doesn't happen too often. +*/ +#define IA_STATE_PACKS_GROW_BY 32 + +/* + * The maximal number of Freecells. For efficiency's sake it should be a + * multiple of 4. + * */ + +#define MAX_NUM_FREECELLS 4 + +/* + * The maximal number of Stacks. For efficiency's sake it should be a + * multiple of 4. + * */ + +#define MAX_NUM_STACKS 10 +/* + * The maximal number of initial cards that can be found in a stack. + * */ +#define MAX_NUM_INITIAL_CARDS_IN_A_STACK 8 + +#define MAX_NUM_DECKS 2 + + +#define FCS_STATE_STORAGE_INDIRECT 0 +#define FCS_STATE_STORAGE_INTERNAL_HASH 1 +#define FCS_STATE_STORAGE_LIBAVL_AVL_TREE 2 +#define FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE 3 +#define FCS_STATE_STORAGE_LIBREDBLACK_TREE 4 +#define FCS_STATE_STORAGE_GLIB_TREE 5 +#define FCS_STATE_STORAGE_GLIB_HASH 6 +#define FCS_STATE_STORAGE_DB_FILE 7 + +#define FCS_STACK_STORAGE_INTERNAL_HASH 0 +#define FCS_STACK_STORAGE_LIBAVL_AVL_TREE 1 +#define FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE 2 +#define FCS_STACK_STORAGE_LIBREDBLACK_TREE 3 +#define FCS_STACK_STORAGE_GLIB_TREE 4 +#define FCS_STACK_STORAGE_GLIB_HASH 5 + +#define FCS_STATE_STORAGE FCS_STATE_STORAGE_INTERNAL_HASH +#define FCS_STACK_STORAGE FCS_STACK_STORAGE_INTERNAL_HASH + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/kpat/freecell-solver/fcs_dm.c b/kpat/freecell-solver/fcs_dm.c new file mode 100644 index 00000000..9fd8c9a8 --- /dev/null +++ b/kpat/freecell-solver/fcs_dm.c @@ -0,0 +1,146 @@ +/* + fcs_dm.c - Freecell Solver's data management routines. + + Written by Shlomi Fish, 2000 + + This file is distributed under the public domain. + (It's not copyrighted) +*/ + +#include +#include + +#include "fcs_dm.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* + freecell_solver_bsearch - an improved binary search function. Highlights: + + * The comparison function accepts a common context argument that + is passed to SFO_bsearch. + * If the item was not found the function returns the place in which + it should be placed, while setting *found to 0. If it was found + (*found) is set to 1. +*/ +void * freecell_solver_bsearch +( + void * key, + void * void_array, + size_t len, + size_t width, + int (* compare)(const void *, const void *, void *), + void * context, + int * found +) +{ + int low = 0; + int high = len-1; + int mid; + int result; + + char * array = void_array; + + while (low <= high) + { + mid = ((low+high)>>1); + + result = compare(key, (void*)(array+mid*width), context); + + if (result < 0) + { + high = mid-1; + } + else if (result > 0) + { + low = mid+1; + } + else + { + *found = 1; + return (void*)(array+mid*width); + } + } + + *found = 0; + return ((void*)(array+(high+1)*width)); +} + + + +/* + freecell_solver_merge_large_and_small_sorted_array - merges a large sorted + array with a small sorted array. The arrays could be of any length + whatsoever, but it works faster if the first is significantly bigger + than the second. + + This function assumes that big_array is allocated with enough + space to hold the extra elements. + + The array should be distinct or else there would be unexpected + results. +*/ +int freecell_solver_merge_large_and_small_sorted_arrays +( + void * void_big_array, + size_t size_big_array, + void * void_small_array, + size_t size_small_array, + size_t width, + int (*compare) (const void *, const void *, void *), + void * context +) +{ + int item_to_move, num_big_items_moved, pos; + char * pos_ptr; + char * big_array; + char * small_array; + int found; + int start_offset, end_offset; + + big_array = (char*)void_big_array; + small_array = (char*)void_small_array; + + num_big_items_moved = 0; + + for(item_to_move = size_small_array-1 ; item_to_move>=0; item_to_move--) + { + pos_ptr = freecell_solver_bsearch ( + small_array+item_to_move*width, + big_array, + size_big_array-num_big_items_moved, + width, + compare, + context, + &found + ); + + pos = (pos_ptr-big_array)/width; + + end_offset = size_big_array + size_small_array - + num_big_items_moved - + (size_small_array-item_to_move-1); + + start_offset = end_offset + pos - + (size_big_array - num_big_items_moved); + + memmove( + big_array+start_offset*width, + big_array+pos*width, + (end_offset-start_offset)*width + ); + + memcpy( + big_array+(start_offset-1)*width, + small_array+item_to_move*width, + width + ); + + num_big_items_moved += (end_offset - start_offset); + } + + return 1; +} + diff --git a/kpat/freecell-solver/fcs_dm.h b/kpat/freecell-solver/fcs_dm.h new file mode 100644 index 00000000..2cb6dc82 --- /dev/null +++ b/kpat/freecell-solver/fcs_dm.h @@ -0,0 +1,49 @@ +/* + fcs_dm.h - Header file for Freecell Solver's Data Management + routines. + + For more information consult fcs_dm.c. + + Written by Shlomi Fish, 2000 + This file is distributed under the public domain. + (It is not copyrighted) +*/ + +#ifndef FC_SOLVE__FCS_DATA_H +#define FC_SOLVE__FCS_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +void * freecell_solver_bsearch +( + void * key, + void * void_array, + size_t len, + size_t width, + int (* compare)(const void *, const void *, void *), + void * context, + int * found +); + +int freecell_solver_merge_large_and_small_sorted_arrays +( + void * void_big_array, + size_t size_big_array, + void * void_small_array, + size_t size_small_array, + size_t width, + int (*compare) (const void *, const void *, void *), + void * context +); + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_DATA_H */ + diff --git a/kpat/freecell-solver/fcs_enums.h b/kpat/freecell-solver/fcs_enums.h new file mode 100644 index 00000000..071383c9 --- /dev/null +++ b/kpat/freecell-solver/fcs_enums.h @@ -0,0 +1,77 @@ +/* + * fcs_enums.h - header file for various Freecell Solver Enumertaions. Common + * to the main program headers and to the library headers. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_ENUMS_H +#define FC_SOLVE__FCS_ENUMS_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum FCS_EMPTY_STACKS_FILL_T +{ + FCS_ES_FILLED_BY_ANY_CARD, + FCS_ES_FILLED_BY_KINGS_ONLY, + FCS_ES_FILLED_BY_NONE +}; + +enum FCS_SEQUENCES_ARE_BUILT_BY_T +{ + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + FCS_SEQ_BUILT_BY_SUIT, + FCS_SEQ_BUILT_BY_RANK +}; + +enum FCS_TALON_T +{ + FCS_TALON_NONE, + FCS_TALON_GYPSY, + FCS_TALON_KLONDIKE +}; + +enum freecell_solver_state_solving_return_codes +{ + FCS_STATE_WAS_SOLVED, + FCS_STATE_IS_NOT_SOLVEABLE, + FCS_STATE_ALREADY_EXISTS, + FCS_STATE_EXCEEDS_MAX_NUM_TIMES, + FCS_STATE_BEGIN_SUSPEND_PROCESS, + FCS_STATE_SUSPEND_PROCESS, + FCS_STATE_EXCEEDS_MAX_DEPTH, + FCS_STATE_ORIGINAL_STATE_IS_NOT_SOLVEABLE, + FCS_STATE_INVALID_STATE, + FCS_STATE_NOT_BEGAN_YET, + FCS_STATE_DOES_NOT_EXIST, + FCS_STATE_OPTIMIZED +}; + +enum fcs_presets_return_codes +{ + FCS_PRESET_CODE_OK, + FCS_PRESET_CODE_NOT_FOUND, + FCS_PRESET_CODE_FREECELLS_EXCEED_MAX, + FCS_PRESET_CODE_STACKS_EXCEED_MAX, + FCS_PRESET_CODE_DECKS_EXCEED_MAX +}; + + +#define FCS_METHOD_NONE -1 +#define FCS_METHOD_HARD_DFS 0 +#define FCS_METHOD_SOFT_DFS 1 +#define FCS_METHOD_BFS 2 +#define FCS_METHOD_A_STAR 3 +#define FCS_METHOD_OPTIMIZE 4 +#define FCS_METHOD_RANDOM_DFS 5 + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_ENUMS_H */ diff --git a/kpat/freecell-solver/fcs_hash.c b/kpat/freecell-solver/fcs_hash.c new file mode 100644 index 00000000..fde7a03f --- /dev/null +++ b/kpat/freecell-solver/fcs_hash.c @@ -0,0 +1,291 @@ +/* + * fcs_hash.c - an implementation of a simplistic (keys only) hash. This + * hash uses chaining and re-hashing and was found to be very fast. Not all + * of the functions of the hash ADT are implemented, but it is useful enough + * for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include "fcs_config.h" + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)) + +#include +#include + +#define DEBUG + +#ifdef DEBUG +#include +#endif + +#include "fcs_hash.h" + +#include "alloc.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static void SFO_hash_rehash(SFO_hash_t * hash); + + + +SFO_hash_t * freecell_solver_hash_init( + SFO_hash_value_t wanted_size, + int (*compare_function)(const void * key1, const void * key2, void * context), + void * context + ) +{ + int size; + SFO_hash_t * hash; + + /* Find a prime number that is greater than the initial wanted size */ + size = 256; + while (size < wanted_size) + { + size <<= 1; + } + + hash = (SFO_hash_t *)malloc(sizeof(SFO_hash_t)); + + hash->size = size; + hash->size_bitmask = size-1; + + hash->num_elems = 0; + + /* Allocate a table of size entries */ + hash->entries = (SFO_hash_symlink_t *)malloc( + sizeof(SFO_hash_symlink_t) * size + ); + + hash->compare_function = compare_function; + hash->context = context; + + /* Initialize all the cells of the hash table to NULL, which indicate + that the cork of the linked list is right at the start */ + memset(hash->entries, 0, sizeof(SFO_hash_symlink_t)*size); + + hash->allocator = freecell_solver_compact_allocator_new(); + + return hash; +} + +void * freecell_solver_hash_insert( + SFO_hash_t * hash, + void * key, + SFO_hash_value_t hash_value, + SFO_hash_value_t secondary_hash_value, + int optimize_for_caching + ) +{ + int place; + SFO_hash_symlink_t * list; + SFO_hash_symlink_item_t * item, * last_item; + + /* Get the index of the appropriate chain in the hash table */ + place = hash_value & (hash->size_bitmask); + + list = &(hash->entries[place]); + /* If first_item is non-existent */ + if (list->first_item == NULL) + { + /* Allocate a first item with that key */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + list->first_item = item; + item->next = NULL; + item->key = key; + item->hash_value = hash_value; + item->secondary_hash_value = secondary_hash_value; + + goto rehash_check; + } + + /* Initialize item to the chain's first_item */ + item = list->first_item; + last_item = NULL; + + while (item != NULL) + { + /* + We first compare the hash values, because it is faster than + comparing the entire data structure. + + */ + if ( + (item->hash_value == hash_value) && + (item->secondary_hash_value == secondary_hash_value) && + (!(hash->compare_function(item->key, key, hash->context))) + ) + { + if (optimize_for_caching) + { + /* + * Place the item in the beginning of the chain. + * If last_item == NULL it is already the first item so leave + * it alone + * */ + if (last_item != NULL) + { + last_item->next = item->next; + item->next = list->first_item; + list->first_item = item; + } + } + return item->key; + } + /* Cache the item before the current in last_item */ + last_item = item; + /* Move to the next item */ + item = item->next; + } + + if (optimize_for_caching) + { + /* Put the new element at the beginning of the list */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + item->next = list->first_item; + item->key = key; + item->hash_value = hash_value; + list->first_item = item; + item->secondary_hash_value = secondary_hash_value; + } + else + { + /* Put the new element at the end of the list */ + fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t); + last_item->next = item; + item->next = NULL; + item->key = key; + item->hash_value = hash_value; + item->secondary_hash_value = secondary_hash_value; + } + +rehash_check: + + hash->num_elems++; + + if (hash->num_elems > ((hash->size*3)>>2)) + { + SFO_hash_rehash(hash); + } + + return NULL; +} + +void freecell_solver_hash_free_with_callback( + SFO_hash_t * hash, + void (*function_ptr)(void * key, void * context) + ) +{ + int i; + SFO_hash_symlink_item_t * item, * next_item; + + for(i=0;isize;i++) + { + item = hash->entries[i].first_item; + while (item != NULL) + { + function_ptr(item->key, hash->context); + next_item = item->next; + + item = next_item; + } + } + + freecell_solver_hash_free(hash); +} + +void freecell_solver_hash_free( + SFO_hash_t * hash + ) +{ + freecell_solver_compact_allocator_finish(hash->allocator); + + free(hash->entries); + + free(hash); +} + + +/* + This function "rehashes" a hash. I.e: it increases the size of its + hash table, allowing for smaller chains, and faster lookup. + + */ +static void SFO_hash_rehash( + SFO_hash_t * hash + ) +{ + int old_size, new_size, new_size_bitmask; + int i; +#if 0 + SFO_hash_t * new_hash; +#endif + SFO_hash_symlink_item_t * item, * next_item; + int place; + SFO_hash_symlink_t * new_entries; + + old_size = hash->size; + +#if 0 + /* Allocate a new hash with hash_init() */ + new_hash = freecell_solver_hash_init_proto( + old_size * 2, + hash->compare_function, + hash->context + ); +#endif + + old_size = hash->size; + new_size = old_size << 1; + new_size_bitmask = new_size - 1; + + new_entries = calloc(new_size, sizeof(SFO_hash_symlink_t)); + + /* Copy the items to the new hash while not allocating them again */ + for(i=0;ientries[i].first_item; + /* traverse the chain item by item */ + while(item != NULL) + { + /* The place in the new hash table */ + place = item->hash_value & new_size_bitmask; + + /* Store the next item in the linked list in a safe place, + so we can retrieve it after the assignment */ + next_item = item->next; + /* It is placed in front of the first element in the chain, + so it should link to it */ + item->next = new_entries[place].first_item; + + /* Make it the first item in its chain */ + new_entries[place].first_item = item; + + /* Move to the next item this one. */ + item = next_item; + } + }; + + /* Free the entries of the old hash */ + free(hash->entries); + + /* Copy the new hash to the old one */ +#if 0 + *hash = *new_hash; +#endif + hash->entries = new_entries; + hash->size = new_size; + hash->size_bitmask = new_size_bitmask; +} + +#else + +/* ANSI C doesn't allow empty compilation */ +static void freecell_solver_hash_c_dummy(); + +#endif /* (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || defined(INDIRECT_STACK_STATES) */ diff --git a/kpat/freecell-solver/fcs_hash.h b/kpat/freecell-solver/fcs_hash.h new file mode 100644 index 00000000..fbe6c78c --- /dev/null +++ b/kpat/freecell-solver/fcs_hash.h @@ -0,0 +1,102 @@ +/* + * fcs_hash.h - header file of Freecell Solver's internal hash implementation. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_HASH_H +#define FC_SOLVE__FCS_HASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "alloc.h" +#include "lookup2.h" + +typedef int SFO_hash_value_t; + +struct SFO_hash_symlink_item_struct +{ + /* A pointer to the data structure that is to be collected */ + void * key; + /* We also store the hash value corresponding to this key for faster + comparisons */ + SFO_hash_value_t hash_value; + /* + * We also store a secondary hash value, which is not used for indexing, + * but is used to speed up comparison. + * */ + SFO_hash_value_t secondary_hash_value; + /* The next item in the list */ + struct SFO_hash_symlink_item_struct * next; +}; + +typedef struct SFO_hash_symlink_item_struct SFO_hash_symlink_item_t; + +struct SFO_hash_symlink_struct +{ + SFO_hash_symlink_item_t * first_item; +}; + +typedef struct SFO_hash_symlink_struct SFO_hash_symlink_t; + +struct SFO_hash_struct +{ + /* The vector of the hash table itself */ + SFO_hash_symlink_t * entries; + /* A comparison function that can be used for comparing two keys + in the collection */ + int (*compare_function)(const void * key1, const void * key2, void * context); + /* The size of the hash table */ + int size; + + /* A bit mask that extract the lowest bits out of the hash value */ + int size_bitmask; + /* The number of elements stored inside the hash */ + int num_elems; + /* A context to pass to the comparison function */ + void * context; + + fcs_compact_allocator_t * allocator; +}; + +typedef struct SFO_hash_struct SFO_hash_t; + + +SFO_hash_t * freecell_solver_hash_init( + SFO_hash_value_t wanted_size, + int (*compare_function)(const void * key1, const void * key2, void * context), + void * context + ); + +void * freecell_solver_hash_insert( + SFO_hash_t * hash, + void * key, + SFO_hash_value_t hash_value, + SFO_hash_value_t secondary_hash_value, + int optimize_for_caching + ); + + +void freecell_solver_hash_free( + SFO_hash_t * hash + ); + +void freecell_solver_hash_free_with_callback( + SFO_hash_t * hash, + void (*function_ptr)(void * key, void * context) + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_HASH_H */ + + + + diff --git a/kpat/freecell-solver/fcs_isa.c b/kpat/freecell-solver/fcs_isa.c new file mode 100644 index 00000000..0a6ffe51 --- /dev/null +++ b/kpat/freecell-solver/fcs_isa.c @@ -0,0 +1,88 @@ +/* fcs_isa.c - Freecell Solver Indirect State Allocation Routines + + Written by Shlomi Fish, 2000 + This file is distributed under the public domain. +*/ + +#include +#include + +#include "fcs_config.h" + + +#include "state.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread) +{ + hard_thread->max_num_state_packs = IA_STATE_PACKS_GROW_BY; + hard_thread->state_packs = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs); + hard_thread->num_state_packs = 1; + /* + * All the states should fit in one 64KB segment. Now, we allocate as + * many states as possible, minus one, so we would be certain that there + * would be place for the overhead required by the malloc algorithm. + * */ + hard_thread->state_pack_len = (0x010000 / sizeof(fcs_state_with_locations_t)) - 1; + hard_thread->state_packs[0] = malloc(hard_thread->state_pack_len*sizeof(fcs_state_with_locations_t)); + + hard_thread->num_states_in_last_pack = 0; +} + +#if 0 +fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_hard_thread_t * hard_thread) +{ + if (hard_thread->num_states_in_last_pack == hard_thread->state_pack_len) + { + if (hard_thread->num_state_packs == hard_thread->max_num_state_packs) + { + hard_thread->max_num_state_packs += IA_STATE_PACKS_GROW_BY; + hard_thread->state_packs = (fcs_state_with_locations_t * *)realloc(hard_thread->state_packs, sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs); + } + hard_thread->state_packs[hard_thread->num_state_packs] = malloc(hard_thread->state_pack_len * sizeof(fcs_state_with_locations_t)); + hard_thread->num_state_packs++; + hard_thread->num_states_in_last_pack = 0; + } + return &(hard_thread->state_packs[hard_thread->num_state_packs-1][hard_thread->num_states_in_last_pack++]); +} +#endif + +#if 0 +void fcs_state_ia_release(freecell_solver_hard_thread_t * hard_thread) +{ + hard_thread->num_states_in_last_pack--; +} +#endif + +void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread) +{ + int a; + for(a=0;anum_state_packs;a++) + { + free(hard_thread->state_packs[a]); + } + free(hard_thread->state_packs); + hard_thread->state_packs = NULL; +} + +void freecell_solver_state_ia_foreach(freecell_solver_hard_thread_t * hard_thread, void (*ptr_function)(fcs_state_with_locations_t *, void *), void * context) +{ + int p,s; + for(p=0;pnum_state_packs-1;p++) + { + for(s=0 ; s < hard_thread->state_pack_len ; s++) + { + ptr_function(&(hard_thread->state_packs[p][s]), context); + } + } + for(s=0; s < hard_thread->num_states_in_last_pack ; s++) + { + ptr_function(&(hard_thread->state_packs[p][s]), context); + } +} diff --git a/kpat/freecell-solver/fcs_isa.h b/kpat/freecell-solver/fcs_isa.h new file mode 100644 index 00000000..30a9a982 --- /dev/null +++ b/kpat/freecell-solver/fcs_isa.h @@ -0,0 +1,56 @@ +#ifndef FC_SOLVE__FCS_ISA_H +#define FC_SOLVE__FCS_ISA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "state.h" +#include "fcs.h" + +extern void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread); +#if 0 +extern fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_instance_t * instance); +#else + +#define fcs_state_ia_alloc_into_var(ret, instance) \ +{ \ + if ((instance)->num_states_in_last_pack == (instance)->state_pack_len) \ + { \ + if (instance->num_state_packs == instance->max_num_state_packs) \ + { \ + instance->max_num_state_packs += IA_STATE_PACKS_GROW_BY; \ + instance->state_packs = (fcs_state_with_locations_t * *)realloc(instance->state_packs, sizeof(fcs_state_with_locations_t *) * instance->max_num_state_packs); \ + } \ + instance->state_packs[instance->num_state_packs] = malloc(instance->state_pack_len * sizeof(fcs_state_with_locations_t)); \ + instance->num_state_packs++; \ + instance->num_states_in_last_pack = 0; \ + } \ + ret = &(instance->state_packs[instance->num_state_packs-1][instance->num_states_in_last_pack++]); \ +} + +#endif + + +#if 0 +extern void fcs_state_ia_release(freecell_solver_instance_t * instance); +#else +#define fcs_state_ia_release(instance) \ +{ \ + (instance)->num_states_in_last_pack--; \ +} + + +#endif +extern void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread); + +extern void freecell_solver_state_ia_foreach( + freecell_solver_hard_thread_t * hard_thread, + void (*ptr_function)(fcs_state_with_locations_t *, void *), + void * context + ); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/fcs_move.h b/kpat/freecell-solver/fcs_move.h new file mode 100644 index 00000000..ecb5166f --- /dev/null +++ b/kpat/freecell-solver/fcs_move.h @@ -0,0 +1,122 @@ +/* + * fcs_move.h - header file for the move structure and enums of + * Freecell Solver. This file is common to the main code and to the + * library headers. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__FCS_MOVE_H +#define FC_SOLVE__FCS_MOVE_H + +/* #define FCS_DEBUG_MOVES */ +#define FCS_COMPACT_MOVES + +#ifdef __cplusplus +extern "C" { +#endif + +enum fcs_move_types +{ + FCS_MOVE_TYPE_STACK_TO_STACK, + FCS_MOVE_TYPE_STACK_TO_FREECELL, + FCS_MOVE_TYPE_FREECELL_TO_STACK, + FCS_MOVE_TYPE_FREECELL_TO_FREECELL, + FCS_MOVE_TYPE_STACK_TO_FOUNDATION, + FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION, + FCS_MOVE_TYPE_FLIP_CARD, + FCS_MOVE_TYPE_DEAL_GYPSY_TALON, + FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK, + FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON, + FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON, + FCS_MOVE_TYPE_SEQ_TO_FOUNDATION, + FCS_MOVE_TYPE_CANONIZE, + FCS_MOVE_TYPE_SEPARATOR, + FCS_MOVE_TYPE_NULL +}; + +#ifdef FCS_DEBUG_MOVES +struct fcs_move_struct +{ + /* The index of the foundation, in case there are more than one decks */ + int foundation; + /* Used in the case of a stack to stack move */ + int num_cards_in_sequence; + /* There are two freecells, one for the source and the other + * for the destination */ + int src_freecell; + int dest_freecell; + /* Ditto for the stacks */ + int src_stack; + int dest_stack; + /* The type of the move see the enum fcs_move_types */ + int type; +}; + +#define fcs_move_set_src_stack(move,value) (move).src_stack = (value); +#define fcs_move_set_src_freecell(move,value) (move).src_freecell = (value); +#define fcs_move_set_dest_stack(move,value) (move).dest_stack = (value); +#define fcs_move_set_dest_freecell(move,value) (move).dest_freecell = (value); +#define fcs_move_set_foundation(move,value) (move).foundation = (value); +#define fcs_move_set_type(move,value) (move).type = (value); +#define fcs_move_set_num_cards_in_seq(move,value) (move).num_cards_in_sequence = (value); + +#define fcs_move_get_src_stack(move) ((move).src_stack) +#define fcs_move_get_src_freecell(move) ((move).src_freecell) +#define fcs_move_get_dest_stack(move) ((move).dest_stack) +#define fcs_move_get_dest_freecell(move) ((move).dest_freecell) +#define fcs_move_get_foundation(move) ((move).foundation) +#define fcs_move_get_type(move) ((move).type) +#define fcs_move_get_num_cards_in_seq(move) ((move).num_cards_in_sequence) + +#elif defined(FCS_COMPACT_MOVES) +struct fcs_move_struct +{ + unsigned char c[4]; +}; + +#define FCS_MOVE_TYPE 0 +#define FCS_MOVE_SRC 1 +#define FCS_MOVE_DEST 2 +#define FCS_MOVE_NUM_CARDS_IN_SEQ 3 +#define FCS_MOVE_NUM_CARDS_FLIPPED 3 + +#define fcs_move_set_src_stack(move,value) (move).c[FCS_MOVE_SRC] = (value); +#define fcs_move_set_src_freecell(move,value) (move).c[FCS_MOVE_SRC] = (value); +#define fcs_move_set_dest_stack(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_dest_freecell(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_foundation(move,value) (move).c[FCS_MOVE_DEST] = (value); +#define fcs_move_set_type(move,value) (move).c[FCS_MOVE_TYPE] = (value); +#define fcs_move_set_num_cards_in_seq(move,value) (move).c[FCS_MOVE_NUM_CARDS_IN_SEQ] = (value); +#define fcs_move_set_num_cards_flipped(move,value) (move).c[FCS_MOVE_NUM_CARDS_FLIPPED] = (value); + +#define fcs_move_get_src_stack(move) ((move).c[FCS_MOVE_SRC]) +#define fcs_move_get_src_freecell(move) ((move).c[FCS_MOVE_SRC]) +#define fcs_move_get_dest_stack(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_dest_freecell(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_foundation(move) ((move).c[FCS_MOVE_DEST]) +#define fcs_move_get_type(move) ((move).c[FCS_MOVE_TYPE]) +#define fcs_move_get_num_cards_in_seq(move) ((move).c[FCS_MOVE_NUM_CARDS_IN_SEQ]) +#define fcs_move_get_num_cards_flipped(move,value) ((move).c[FCS_MOVE_NUM_CARDS_FLIPPED]) +#define fcs_move_init(move) (memset((move).c, 0, 4)) +#endif + +typedef struct fcs_move_struct fcs_move_t; + +struct fcs_move_stack_struct +{ + fcs_move_t * moves; + int max_num_moves; + int num_moves; +}; + +typedef struct fcs_move_stack_struct fcs_move_stack_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_MOVE_H */ diff --git a/kpat/freecell-solver/fcs_user.h b/kpat/freecell-solver/fcs_user.h new file mode 100644 index 00000000..8ddbf6f2 --- /dev/null +++ b/kpat/freecell-solver/fcs_user.h @@ -0,0 +1,275 @@ +/* + * move.h - main header file for the Freecell Solver library. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#ifndef FC_SOLVE__FCS_USER_H +#define FC_SOLVE__FCS_USER_H + +#include "fcs_enums.h" +#include "fcs_move.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void * freecell_solver_user_alloc(void); + +extern int freecell_solver_user_apply_preset( + void * instance, + const char * preset_name + ); + +extern void freecell_solver_user_limit_iterations( + void * user_instance, + int max_iters + ); + +extern int freecell_solver_user_set_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ); + +extern int freecell_solver_user_solve_board( + void * user_instance, + const char * state_as_string + ); + +extern int freecell_solver_user_resume_solution( + void * user_instance + ); + +extern int freecell_solver_user_get_next_move( + void * user_instance, + fcs_move_t * move + ); + +extern char * freecell_solver_user_current_state_as_string( + void * user_instance, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +extern void freecell_solver_user_free( + void * user_instance + ); + +extern int freecell_solver_user_get_current_depth( + void * user_instance + ); + +extern void freecell_solver_user_set_solving_method( + void * user_instance, + int method + ); + +extern int freecell_solver_user_get_num_times( + void * user_instance + ); + +extern int freecell_solver_user_get_limit_iterations( + void * user_instance + ); + +extern int freecell_solver_user_get_moves_left( + void * user_instance + ); + +extern int freecell_solver_user_set_game( + void * user_instance, + int freecells_num, + int stacks_num, + int decks_num, + int sequences_are_built_by, + int unlimited_sequence_move, + int empty_stacks_fill + ); + +extern void freecell_solver_user_set_solution_optimization( + void * user_instance, + int optimize +); + +extern char * freecell_solver_user_move_to_string( + fcs_move_t move, + int standard_notation + ); + +extern char * freecell_solver_user_move_to_string_w_state( + void * user_instance, + fcs_move_t move, + int standard_notation + ); + +extern void freecell_solver_user_limit_depth( + void * user_instance, + int max_depth + ); + +extern int freecell_solver_user_set_num_freecells( + void * user_instance, + int freecells_num + ); + +extern int freecell_solver_user_get_max_num_freecells(void); + +extern int freecell_solver_user_set_num_stacks( + void * user_instance, + int stacks_num + ); + +extern int freecell_solver_user_get_max_num_stacks(void); + +extern int freecell_solver_user_set_num_decks( + void * user_instance, + int decks_num + ); + +extern int freecell_solver_user_get_max_num_decks(void); + + +extern char * freecell_solver_user_get_invalid_state_error_string( + void * user_instance, + int print_ts + ); + +extern int freecell_solver_user_set_sequences_are_built_by_type( + void * user_instance, + int sbb + ); + +extern int freecell_solver_user_set_empty_stacks_filled_by( + void * user_instance, + int es_fill + ); + +extern int freecell_solver_user_set_sequence_move( + void * user_instance, + int unlimited + ); + +extern int freecell_solver_user_set_a_star_weight( + void * user_instance, + int index, + double weight + ); + +typedef void (*freecell_solver_user_iter_handler_t) + ( + void * user_instance, + int iter_num, + int depth, + void * ptr_state, + int parent_iter_num, + void * context + ); + +extern void freecell_solver_user_set_iter_handler( + void * user_instance, + freecell_solver_user_iter_handler_t iter_handler, + void * iter_handler_context + ); + + +extern char * freecell_solver_user_iter_state_as_string( + void * user_instance, + void * ptr_state, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +extern void freecell_solver_user_set_random_seed( + void * user_instance, + int seed + ); + +extern int freecell_solver_user_get_num_states_in_collection( + void * user_instance + ); + +extern void freecell_solver_user_limit_num_states_in_collection( + void * user_instance, + int max_num_states + ); + +extern int freecell_solver_user_next_soft_thread( + void * user_instance + ); + +extern void freecell_solver_user_set_soft_thread_step( + void * user_instance, + int num_times_step + ); + +extern int freecell_solver_user_next_hard_thread( + void * user_instance + ); + +extern int freecell_solver_user_get_num_soft_threads_in_instance( + void * user_instance + ); + +extern void freecell_solver_user_set_calc_real_depth( + void * user_instance, + int calc_real_depth + ); + +extern void freecell_solver_user_set_soft_thread_name( + void * user_instance, + char * name + ); + +extern int freecell_solver_user_set_hard_thread_prelude( + void * user_instance, + char * prelude + ); + +extern void freecell_solver_user_recycle( + void * user_instance + ); + +extern int freecell_solver_user_set_optimization_scan_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ); + +extern void freecell_solver_user_set_reparent_states( + void * user_instance, + int to_reparent_states + ); + +extern void freecell_solver_user_set_scans_synergy( + void * user_instance, + int synergy + ); + +extern void freecell_solver_user_limit_current_instance_iterations( + void * user_instance, + int max_iters + ); + +extern int freecell_solver_user_next_instance( + void * user_instance + ); + +/* + * This function resets the user_instance, making it lose + * all the previous command line arguments it encountered + * */ +extern int freecell_solver_user_reset( + void * user_instance + ); + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__FCS_USER_H */ diff --git a/kpat/freecell-solver/freecell.c b/kpat/freecell-solver/freecell.c new file mode 100644 index 00000000..159772ff --- /dev/null +++ b/kpat/freecell-solver/freecell.c @@ -0,0 +1,2433 @@ +/* + * freecell.c - The various movement tests performed by Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include +#include +#include +#include + + +#include "fcs_config.h" + +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" +#include "tests.h" +#include "ms_ca.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +/* + * Throughout this code the following local variables are used to quickly + * access the instance's members: + * + * state_stacks_num - the number of stacks in the state + * state_freecells_num - the number of freecells in the state + * sequences_are_built_by - the type of sequences of this board. + * */ + +/* + * This function tries to move stack cards that are present at the + * top of stacks to the foundations. + * */ +int freecell_solver_sfs_move_top_stack_cards_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int stack; + int cards_num; + int deck; + fcs_card_t card; + fcs_card_t temp_card; + int check; + int state_stacks_num; + + fcs_move_t temp_move; + + tests_define_accessors(); + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + state_stacks_num = instance->stacks_num; + + for(stack=0;stackdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1) + { + /* We can put it there */ + + sfs_check_state_begin(); + + + my_copy_stack(stack); + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card)); + + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION); + fcs_move_set_src_stack(temp_move,stack); + fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card)); + + fcs_move_stack_push(moves, temp_move); + + fcs_flip_top_card(stack); + + /* The last move needs to be FCS_MOVE_TYPE_CANONIZE + * because it indicates that the internal order of the + * stacks + * and freecells may have changed. */ + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + break; + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +/* + * This test moves single cards that are present in the freecells to + * the foundations. + * */ +int freecell_solver_sfs_move_freecell_cards_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int fc; + int deck; + fcs_card_t card; + int check; + fcs_move_t temp_move; + int state_freecells_num; + + tests_define_accessors(); + + state_freecells_num = instance->freecells_num; + + /* Now check the same for the free cells */ + for(fc=0;fcdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1) + { + /* We can put it there */ + sfs_check_state_begin() + + fcs_empty_freecell(new_state, fc); + + fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card)); + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION); + fcs_move_set_src_freecell(temp_move,fc); + fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card)); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end(); + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_move_freecell_cards_on_top_of_stacks( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int dest_cards_num; + int ds, fc, dc; + fcs_card_t dest_card, src_card, temp_card, dest_below_card; + int check; + + fcs_move_t temp_move; + int is_seq_in_dest; + int num_cards_to_relocate; + int freecells_to_fill, freestacks_to_fill; + int a,b; + int state_freecells_num, state_stacks_num, sequences_are_built_by; + + tests_define_accessors(); + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* Let's try to put cards in the freecells on top of stacks */ + + /* ds stands for destination stack */ + for(ds=0;ds 0) + { + /* + * Let's search for a suitable card in the stack + * */ + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + + /* Scan the freecells */ + for(fc=0;fc dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card, dest_card)) + { + is_seq_in_dest = 1; + } + } + + + if (! is_seq_in_dest) + { + num_cards_to_relocate = dest_cards_num - dc - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if (num_cards_to_relocate == 0) + { + /* We can move it */ + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(ds); + + for(a=0 ; afreecells_num; + state_stacks_num = instance->stacks_num; + + + + /* Now let's check if a card that is under some other cards can be placed + * in the foundations. */ + + for(stack=0;stack= 0 ; c--) + { + card = fcs_stack_card(state, stack, c); + for(deck=0;deckdecks_num;deck++) + { + if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card)-1) + { + /* The card is foundation-able. Now let's check if we + * can move the cards above it to the freecells and + * stacks */ + + if ((num_freecells + + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ? + num_freestacks : + 0 + )) + >= cards_num-(c+1)) + { + /* We can move it */ + + sfs_check_state_begin() + + my_copy_stack(stack); + + + /* Fill the freecells with the top cards */ + for(a=0 ; afreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* + * Now let's try to move a stack card to a parent card which is found + * on the same stack. + * */ + for (stack=0;stackempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if (num_cards_to_relocate == 0) + { + /* We can move it */ + + sfs_check_state_begin() + + + { + int i_card_pos; + fcs_card_t moved_card; + int source_type, source_index; + + i_card_pos = fcs_stack_len(new_state,stack)-1; + a = 0; + + my_copy_stack(ds); + while(i_card_pos>c) + { + if (a < freecells_to_fill) + { + for(b=0;bdc) + { + if (a < freecells_to_fill) + { + for(b=0;bfreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + /* Now let's try to move a card from one stack to the other * + * Note that it does not involve moving cards lower than king * + * to empty stacks */ + + for (stack=0;stack dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card, dest_card)) + { + is_seq_in_dest = 1; + } + } + + if (! is_seq_in_dest) + { + num_cards_to_relocate = dest_cards_num - dc - 1 + cards_num - seq_end - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && + (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >= + seq_end - c + 1)) + { + /* We can move it */ + int from_which_stack; + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(stack); + my_copy_stack(ds); + + for(a=0 ; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + max_sequence_len = calc_max_sequence_move(num_freecells, num_freestacks-1); + + /* Now try to move sequences to empty stacks */ + + if (num_freestacks > 0) + { + for(stack=0;stackempty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY)) + { + continue; + } + + if (seq_end == cards_num -1) + { + /* One stack is the destination stack, so we have one * + * less stack in that case */ + while ((max_sequence_len < cards_num -c) && (c > 0)) + { + c--; + } + + if ( + (c > 0) && + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(fcs_stack_card(state, stack, c)) == 13) : + 1 + ) + ) + { + sfs_check_state_begin(); + + + for(ds=0;dsempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && (num_freestacks-freestacks_to_fill > 0)) + { + /* We can move it */ + int seq_start = c; + while ( + (calc_max_sequence_move( + num_freecells-freecells_to_fill, + num_freestacks-freestacks_to_fill-1) < seq_end-seq_start+1) + && + (seq_start <= seq_end) + ) + { + seq_start++; + } + if ((seq_start <= seq_end) && + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(fcs_stack_card(state, stack, seq_start)) == 13) : + 1 + ) + ) + { + sfs_check_state_begin(); + + + /* Fill the freecells with the top cards */ + + my_copy_stack(stack); + + for(a=0; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_freecells_num = instance->freecells_num; + state_stacks_num = instance->stacks_num; + + for(fc=0;fcempty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(card) == 13) : + (fcs_card_card_num(card) != 0) + ) + { + for(stack=0;stackfreecells_num; + state_stacks_num = instance->stacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + fcs_move_init(temp_move); + + /* This time try to move cards that are already on top of a parent to a different parent */ + + for (stack=0;stack dc) + { + dest_below_card = fcs_stack_card(state, ds, dc+1); + if (fcs_is_parent_card(dest_below_card,dest_card)) + { + is_seq_in_dest = 1; + } + } + + if (! is_seq_in_dest) + { + if (is_seq_in_src) + { + num_cards_to_relocate = dest_cards_num - dc - 1; + + freecells_to_fill = min(num_cards_to_relocate, num_freecells); + + num_cards_to_relocate -= freecells_to_fill; + + if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + freestacks_to_fill = min(num_cards_to_relocate, num_freestacks); + + num_cards_to_relocate -= freestacks_to_fill; + } + else + { + freestacks_to_fill = 0; + } + + if ((num_cards_to_relocate == 0) && + (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >= + cards_num - c)) + { + /* We can move it */ + + sfs_check_state_begin() + + + /* Fill the freecells with the top cards */ + + my_copy_stack(ds); + for(a=0 ; aempty_stacks_fill == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + + /* Now, let's try to empty an entire stack into the freecells, so other cards can + * inhabit it */ + + if (num_freestacks == 0) + { + for(stack=0;stackstacks_num; + sequences_are_built_by = instance->sequences_are_built_by; + + for( ds=0 ; ds < state_stacks_num ; ds++ ) + { + dest_cards_num = fcs_stack_len(state, ds); + if (dest_cards_num > 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + for( stack=0 ; stack < state_stacks_num ; stack++) + { + if (stack == ds) + { + continue; + } + cards_num = fcs_stack_len(state, stack); + for( c=cards_num-1 ; c >= 0 ; c--) + { + card = fcs_stack_card(state, stack, c); + if (fcs_card_get_flipped(card)) + { + break; + } + if (fcs_is_parent_card(card, dest_card)) + { + /* We can move it there - now let's check to see + * if it is already above a suitable parent. */ + if ((c == 0) || + (! fcs_is_parent_card(card, fcs_stack_card(state, stack, c-1)))) + { + /* Let's move it */ + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + fcs_move_sequence(ds, stack, c, cards_num-1, a); + + fcs_flip_top_card(stack); + + sfs_check_state_end(); + } + + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_yukon_move_kings_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int check; + + int stack, cards_num, c, a, ds; + fcs_card_t card, temp_card; + + int state_stacks_num; + + fcs_move_t temp_move; + + tests_define_accessors(); + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + + for( stack=0 ; stack < state_stacks_num ; stack++) + { + cards_num = fcs_stack_len(state, stack); + for( c=cards_num-1 ; c >= 1 ; c--) + { + card = fcs_stack_card(state, stack, c); + if (fcs_card_get_flipped(card)) + { + break; + } + if (fcs_card_card_num(card) == 13) + { + /* It's a King - so let's move it */ + sfs_check_state_begin(); + + + for( ds=0 ; ds < state_stacks_num ; ds++) + { + if (fcs_stack_len(state, ds) == 0) + { + break; + } + } + my_copy_stack(stack); + my_copy_stack(ds); + fcs_move_sequence(ds, stack, c, cards_num-1, a); + + + fcs_flip_top_card(stack); + + sfs_check_state_end(); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + + +#ifdef FCS_WITH_TALONS +/* + Let's try to deal the Gypsy-type Talon. + + */ +int freecell_solver_sfs_deal_gypsy_talon( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + int check; + + fcs_card_t temp_card; + int a; + + fcs_move_t temp_move; + + tests_define_accessors(); + + if (instance->talon_type != FCS_TALON_GYPSY) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + if (fcs_talon_pos(state) < fcs_talon_len(state)) + { + sfs_check_state_begin() + for(a=0;atalon_type != FCS_TALON_KLONDIKE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + /* Duplicate the talon and its parameters into talon_temp */ + talon_temp = malloc(sizeof(fcs_state_with_locations_t)); + talon_temp->s.talon = malloc(fcs_klondike_talon_len(state)+1); + memcpy( + talon_temp->s.talon, + ptr_state_with_locations->s.talon, + fcs_klondike_talon_len(state)+1 + ); + memcpy( + talon_temp->s.talon_params, + ptr_state_with_locations->s.talon_params, + sizeof(ptr_state_with_locations->s.talon_params) + ); + + /* Make sure we redeal the talon only once */ + num_redeals_left = fcs_klondike_talon_num_redeals_left(state); + if ((num_redeals_left > 0) || (num_redeals_left < 0)) + { + num_redeals_left = 1; + } + num_redeals_done = 0; + num_cards_moved[0] = 0; + num_cards_moved[1] = 0; + + first_iter = 1; + while (num_redeals_left >= 0) + { + if ((fcs_klondike_talon_stack_pos(talon_temp->s) == -1) && + (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s))) + { + break; + } + if ((!first_iter) || (fcs_klondike_talon_stack_pos(talon_temp->s) == -1)) + { + if (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s)) + { + if (num_redeals_left > 0) + { + fcs_klondike_talon_len(talon_temp->s) = fcs_klondike_talon_stack_pos(talon_temp->s); + fcs_klondike_talon_redeal_bare(talon_temp->s); + + num_redeals_left--; + num_redeals_done++; + } + else + { + break; + } + } + fcs_klondike_talon_queue_to_stack(talon_temp->s); + num_cards_moved[num_redeals_done]++; + } + first_iter = 0; + + card_to_check = fcs_klondike_talon_get_top_card(talon_temp->s); + for(s=0 ; ss)+1); + memcpy( + new_state.talon, + talon_temp->s.talon, + fcs_klondike_talon_len(talon_temp->s)+1 + ); + + memcpy( + ptr_new_state_with_locations->s.talon_params, + talon_temp->s.talon_params, + sizeof(ptr_state_with_locations->s.talon_params) + ); + + for(a=0;a<=num_redeals_done;a++) + { + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON); + fcs_move_set_num_cards_flipped(temp_move, num_cards_moved[a]); + fcs_move_stack_push(moves, temp_move); + if (a != num_redeals_done) + { + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON); + fcs_move_stack_push(moves,temp_move); + } + } + fcs_push_card_into_stack(new_state, s, fcs_klondike_talon_get_top_card(new_state)); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK); + fcs_move_set_dest_stack(temp_move, s); + fcs_klondike_talon_decrement_stack(new_state); + + sfs_check_state_end() + } + } + } + + + +#if 0 + cleanup: +#endif + free(talon_temp->s.talon); + free(talon_temp); + + return FCS_STATE_IS_NOT_SOLVEABLE; + +} + +#endif + +int freecell_solver_sfs_atomic_move_card_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int empty_stacks_filled_by, state_stacks_num; + int stack, cards_num; + fcs_card_t card, temp_card; + fcs_move_t temp_move; + int check; + int empty_stack_idx; + + tests_define_accessors(); + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + state_stacks_num = instance->stacks_num; + + for(empty_stack_idx=0;empty_stack_idxempty_stacks_fill; + + if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + + + for(stack=0;stack 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) && + (fcs_card_card_num(card) != 13)) + { + continue; + } + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + + fcs_pop_stack_card(new_state, stack, temp_card); + + + my_copy_stack(empty_stack_idx); + + fcs_push_card_into_stack(new_state, empty_stack_idx, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_stack(temp_move, empty_stack_idx); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_card_to_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num; + int stack, cards_num, ds, ds_cards_num; + fcs_card_t card, dest_card, temp_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + + for(stack=0;stack 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, ds_cards_num-1); + if (fcs_is_parent_card(card, dest_card)) + { + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_push_card_into_stack(new_state, ds, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_stack(temp_move, ds); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_card_to_freecell( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num; + int state_freecells_num; + int stack, cards_num, ds; + fcs_card_t card, temp_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + if (num_freecells == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + for(ds=0;ds 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(stack); + + fcs_pop_stack_card(new_state, stack, temp_card); + + fcs_put_card_in_freecell(new_state, ds, card); + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_FREECELL); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_dest_freecell(temp_move, ds); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_freecell_card_to_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num, state_freecells_num; + int fc, ds, ds_cards_num; + fcs_card_t card, dest_card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by; + + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + + + + for(fc=0;fc 0) + { + dest_card = fcs_stack_card(state, ds, ds_cards_num-1); + if (fcs_is_parent_card(card, dest_card)) + { + /* Let's move it */ + { + sfs_check_state_begin(); + + my_copy_stack(ds); + + fcs_empty_freecell(new_state, fc); + + fcs_push_card_into_stack(new_state, ds, card); + + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK); + fcs_move_set_src_freecell(temp_move, fc); + fcs_move_set_dest_stack(temp_move, ds); + fcs_move_set_num_cards_in_seq(temp_move, 1); + + fcs_move_stack_push(moves, temp_move); + + fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); + fcs_move_stack_push(moves, temp_move); + + sfs_check_state_end() + } + } + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + int state_stacks_num, state_freecells_num; + int fc, ds; + fcs_card_t card; + fcs_move_t temp_move; + int check; + int sequences_are_built_by, empty_stacks_filled_by; + + tests_define_accessors(); + + moves = hard_thread->reusable_move_stack; + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; + + state_stacks_num = instance->stacks_num; + state_freecells_num = instance->freecells_num; + + sequences_are_built_by = instance->sequences_are_built_by; + + if (num_freestacks == 0) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + empty_stacks_filled_by = instance->empty_stacks_fill; + + if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + for(ds=0;ds +#include +#include +#include +#include +#include + +#include +#include +#include + +#define NUM_TIMES_STEP 50 + +#include "fcs_config.h" + +/* So the FCS_STATE_STORAGE macros would be defined */ +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "caas.h" + +#include "preset.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* + General use of this interface: + 1. freecell_solver_alloc_instance() + 2. Set the parameters of the game + 3. If you wish to revert, go to step #11. + 4. freecell_solver_init_instance() + 5. Call freecell_solver_solve_instance() with the initial board. + 6. If it returns FCS_STATE_SUSPEND_PROCESS and you wish to proceed, + then increase the iteration limit and call + freecell_solver_resume_instance(). + 7. Repeat Step #6 zero or more times. + 8. If the last call to solve_instance() or resume_instance() returned + FCS_STATE_SUSPEND_PROCESS then call + freecell_solver_unresume_instance(). + 9. If the solving was successful you can use the move stacks or the + intermediate stacks. (Just don't destory them in any way). + 10. Call freecell_solver_finish_instance(). + 11. Call freecell_solver_free_instance(). + + The library functions inside lib.c (a.k.a fcs_user()) give an + easier approach for embedding Freecell Solver into your library. The + intent of this comment is to document the code, rather than to be + a guideline for the user. +*/ + +#if 0 +static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.5,0,0}; +#else +static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.3,0,0.2}; +#endif + + + + + + + +static void freecell_solver_initialize_bfs_queue(freecell_solver_soft_thread_t * soft_thread) +{ + /* Initialize the BFS queue. We have one dummy element at the beginning + in order to make operations simpler. */ + soft_thread->bfs_queue = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); + soft_thread->bfs_queue->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); + soft_thread->bfs_queue_last_item = soft_thread->bfs_queue->next; + soft_thread->bfs_queue_last_item->next = NULL; +} + +static void foreach_soft_thread( + freecell_solver_instance_t * instance, + void (*soft_thread_callback)( + freecell_solver_soft_thread_t * soft_thread, + void * context + ), + void * context + ) + +{ + int ht_idx, st_idx; + freecell_solver_hard_thread_t * hard_thread; + int num_soft_threads; + freecell_solver_soft_thread_t * * ht_soft_threads; + for(ht_idx = 0 ; ht_idxnum_hard_threads; ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + num_soft_threads = hard_thread->num_soft_threads; + ht_soft_threads = hard_thread->soft_threads; + for(st_idx = 0 ; st_idx < num_soft_threads; st_idx++) + { + soft_thread_callback(ht_soft_threads[st_idx], context); + } + } + + if (instance->optimization_thread) + { + soft_thread_callback(instance->optimization_thread->soft_threads[0], context); + } +} + + + +static void soft_thread_clean_soft_dfs( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int num_solution_states; + int dfs_max_depth; + fcs_soft_dfs_stack_item_t * soft_dfs_info, * info_ptr; + /* Check if a Soft-DFS-type scan was called in the first place */ + if (soft_thread->soft_dfs_info == NULL) + { + /* If not - do nothing */ + return; + } + + (void)context; + soft_dfs_info = soft_thread->soft_dfs_info; + num_solution_states = soft_thread->num_solution_states; + dfs_max_depth = soft_thread->dfs_max_depth; + /* De-allocate the Soft-DFS specific stacks */ + { + int depth; + info_ptr = soft_dfs_info; + for(depth=0;depthderived_states_list.states); + free(info_ptr->derived_states_random_indexes); + info_ptr++; + } + for(;depthderived_states_list.max_num_states) + { + free(info_ptr->derived_states_list.states); + free(info_ptr->derived_states_random_indexes); + } + info_ptr++; + } + + free(soft_dfs_info); + + soft_thread->soft_dfs_info = NULL; + + soft_thread->dfs_max_depth = 0; + + } +} + +static void clean_soft_dfs( + freecell_solver_instance_t * instance + ) +{ + foreach_soft_thread(instance, soft_thread_clean_soft_dfs, NULL); +} + +static freecell_solver_soft_thread_t * alloc_soft_thread( + freecell_solver_hard_thread_t * hard_thread + ) +{ + freecell_solver_soft_thread_t * soft_thread; + unsigned int a; + + /* Make sure we are not exceeding the maximal number of soft threads + * for an instance. */ + if (hard_thread->instance->next_soft_thread_id == MAX_NUM_SCANS) + { + return NULL; + } + + soft_thread = malloc(sizeof(freecell_solver_soft_thread_t)); + + soft_thread->hard_thread = hard_thread; + + soft_thread->id = (hard_thread->instance->next_soft_thread_id)++; + + soft_thread->dfs_max_depth = 0; + + soft_thread->tests_order.num = 0; + soft_thread->tests_order.tests = NULL; + soft_thread->tests_order.max_num = 0; + + + /* Initialize all the Soft-DFS stacks to NULL */ + soft_thread->soft_dfs_info = NULL; + + /* The default solving method */ + soft_thread->method = FCS_METHOD_SOFT_DFS; + + soft_thread->orig_method = FCS_METHOD_NONE; + + freecell_solver_initialize_bfs_queue(soft_thread); + + /* Initialize the priotity queue of the A* scan */ + soft_thread->a_star_pqueue = malloc(sizeof(PQUEUE)); + freecell_solver_PQueueInitialise( + soft_thread->a_star_pqueue, + 1024 + ); + + /* Set the default A* weigths */ + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a]; + } + + soft_thread->rand_gen = freecell_solver_rand_alloc(soft_thread->rand_seed = 24); + + soft_thread->initialized = 0; + + soft_thread->num_times_step = NUM_TIMES_STEP; + +#if 0 + { + char * no_use; + freecell_solver_apply_tests_order(soft_thread, "[01][23456789]", &no_use); + } +#else + soft_thread->tests_order.num = soft_thread->hard_thread->instance->instance_tests_order.num; + soft_thread->tests_order.tests = + malloc(sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num); + memcpy(soft_thread->tests_order.tests, + soft_thread->hard_thread->instance->instance_tests_order.tests, + sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num + ); + soft_thread->tests_order.max_num = soft_thread->tests_order.num; +#endif + + soft_thread->is_finished = 0; + + soft_thread->name = NULL; + + return soft_thread; +} + +static freecell_solver_hard_thread_t * alloc_hard_thread( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * hard_thread; + + /* Make sure we are not exceeding the maximal number of soft threads + * for an instance. */ + if (instance->next_soft_thread_id == MAX_NUM_SCANS) + { + return NULL; + } + + hard_thread = malloc(sizeof(freecell_solver_hard_thread_t)); + + hard_thread->instance = instance; + + hard_thread->num_times = 0; + + hard_thread->num_soft_threads = 1; + + hard_thread->soft_threads = + malloc(sizeof(hard_thread->soft_threads[0]) * + hard_thread->num_soft_threads + ); + + hard_thread->soft_threads[0] = alloc_soft_thread(hard_thread); + + /* Set a limit on the Hard-Thread's scan. */ + hard_thread->num_times_step = NUM_TIMES_STEP; + + hard_thread->ht_max_num_times = hard_thread->num_times_step; + + hard_thread->max_num_times = -1; + + hard_thread->num_soft_threads_finished = 0; + +#ifdef INDIRECT_STACK_STATES + hard_thread->stacks_allocator = + freecell_solver_compact_allocator_new(); +#endif + hard_thread->move_stacks_allocator = + freecell_solver_compact_allocator_new(); + + fcs_move_stack_alloc_into_var(hard_thread->reusable_move_stack); + + hard_thread->prelude_as_string = NULL; + hard_thread->prelude = NULL; + hard_thread->prelude_num_items = 0; + hard_thread->prelude_idx = 0; + + return hard_thread; +} + + +/* + This function allocates a Freecell Solver instance struct and set the + default values in it. After the call to this function, the program can + set parameters in it which are different from the default. + + Afterwards freecell_solver_init_instance() should be called in order + to really prepare it for solving. + */ +freecell_solver_instance_t * freecell_solver_alloc_instance(void) +{ + freecell_solver_instance_t * instance; + + instance = malloc(sizeof(freecell_solver_instance_t)); + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + instance->num_indirect_prev_states = 0; + instance->max_num_indirect_prev_states = 0; +#endif + + instance->num_times = 0; + + instance->num_states_in_collection = 0; + + instance->max_num_times = -1; + instance->max_depth = -1; + instance->max_num_states_in_collection = -1; + + instance->instance_tests_order.num = 0; + instance->instance_tests_order.tests = NULL; + instance->instance_tests_order.max_num = 0; + + instance->opt_tests_order_set = 0; + + instance->opt_tests_order.num = 0; + instance->opt_tests_order.tests = NULL; + instance->opt_tests_order.max_num = 0; + + + +#ifdef FCS_WITH_TALONS + instance->talon_type = FCS_TALON_NONE; +#endif + + instance->num_hard_threads = 0; + + freecell_solver_apply_preset_by_name(instance, "freecell"); + + /****************************************/ + + instance->debug_iter_output = 0; + + instance->next_soft_thread_id = 0; + + instance->num_hard_threads = 1; + + instance->hard_threads = malloc(sizeof(instance->hard_threads[0]) * instance->num_hard_threads); + + instance->hard_threads[0] = alloc_hard_thread(instance); + + instance->solution_moves = NULL; + + instance->optimize_solution_path = 0; + +#ifdef FCS_WITH_MHASH + instance->mhash_type = MHASH_MD5; +#endif + + instance->optimization_thread = NULL; + + instance->num_hard_threads_finished = 0; + + instance->calc_real_depth = 0; + + instance->to_reparent_states = 0; + + /* Make the 1 the default, because otherwise scans will not cooperate + * with one another. */ + instance->scans_synergy = 1; + + return instance; +} + + + + + +static void free_bfs_queue(freecell_solver_soft_thread_t * soft_thread) +{ + /* Free the BFS linked list */ + fcs_states_linked_list_item_t * item, * next_item; + item = soft_thread->bfs_queue; + while (item != NULL) + { + next_item = item->next; + free(item); + item = next_item; + } +} + +static void free_instance_soft_thread_callback(freecell_solver_soft_thread_t * soft_thread, void * context) +{ + (void)context; + free_bfs_queue(soft_thread); + freecell_solver_rand_free(soft_thread->rand_gen); + + freecell_solver_PQueueFree(soft_thread->a_star_pqueue); + free(soft_thread->a_star_pqueue); + + free(soft_thread->tests_order.tests); + + if (soft_thread->name != NULL) + { + free(soft_thread->name); + } + /* The data-structure itself was allocated */ + free(soft_thread); +} + +static void free_instance_hard_thread_callback(freecell_solver_hard_thread_t * hard_thread) +{ + if (hard_thread->prelude_as_string) + { + free (hard_thread->prelude_as_string); + } + if (hard_thread->prelude) + { + free (hard_thread->prelude); + } + fcs_move_stack_destroy(hard_thread->reusable_move_stack); + + free(hard_thread->soft_threads); + + if (hard_thread->move_stacks_allocator) + { + freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator); + } +#ifdef INDIRECT_STACK_STATES + if (hard_thread->stacks_allocator) + { + freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator); + } +#endif + free(hard_thread); +} + +/* + This function is the last function that should be called in the + sequence of operations on instance, and it is meant for de-allocating + whatever memory was allocated by alloc_instance(). + */ +void freecell_solver_free_instance(freecell_solver_instance_t * instance) +{ + int ht_idx; + + foreach_soft_thread(instance, free_instance_soft_thread_callback, NULL); + + for(ht_idx=0; ht_idx < instance->num_hard_threads; ht_idx++) + { + free_instance_hard_thread_callback(instance->hard_threads[ht_idx]); + } + free(instance->hard_threads); + if (instance->optimization_thread) + { + free_instance_hard_thread_callback(instance->optimization_thread); + } + + free(instance->instance_tests_order.tests); + + if (instance->opt_tests_order_set) + { + free(instance->opt_tests_order.tests); + } + + free(instance); +} + + +static void normalize_a_star_weights( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + /* Normalize the A* Weights, so the sum of all of them would be 1. */ + double sum; + unsigned int a; + sum = 0; + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + if (soft_thread->a_star_weights[a] < 0) + { + soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a]; + } + sum += soft_thread->a_star_weights[a]; + } + if (sum == 0) + { + sum = 1; + } + for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++) + { + soft_thread->a_star_weights[a] /= sum; + } + (void)context; +} + +static void accumulate_tests_order( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int * tests_order = (int *)context; + int a; + for(a=0;atests_order.num;a++) + { + *tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK)); + } +} + +static void determine_scan_completeness( + freecell_solver_soft_thread_t * soft_thread, + void * context + ) +{ + int global_tests_order = *(int *)context; + int tests_order = 0; + int a; + for(a=0;atests_order.num;a++) + { + tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK)); + } + soft_thread->is_a_complete_scan = (tests_order == global_tests_order); +} + +enum FCS_COMPILE_PRELUDE_ERRORS_T +{ + FCS_COMPILE_PRELUDE_OK, + FCS_COMPILE_PRELUDE_NO_AT_SIGN, + FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID +}; + +static int compile_prelude( + freecell_solver_hard_thread_t * hard_thread + ) +{ + char * p_quota, * p_scan, * p; + char * string; + int last_one = 0; + int num_items = 0; + int max_num_items = 16; + fcs_prelude_item_t * prelude; + int st_idx; + + prelude = malloc(sizeof(prelude[0]) * max_num_items); + string = hard_thread->prelude_as_string; + + p = string; + + while (! last_one) + { + p_quota = p; + while((*p) && isdigit(*p)) + { + p++; + } + if (*p != '@') + { + free(prelude); + return FCS_COMPILE_PRELUDE_NO_AT_SIGN; + } + *p = '\0'; + p++; + p_scan = p; + while((*p) && ((*p) != ',')) + { + p++; + } + if ((*p) == '\0') + { + last_one = 1; + } + *p = '\0'; + p++; + + for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++) + { + if (!strcmp(hard_thread->soft_threads[st_idx]->name, p_scan)) + { + break; + } + } + if (st_idx == hard_thread->num_soft_threads) + { + free(prelude); + return FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID; + } + prelude[num_items].scan_idx = st_idx; + prelude[num_items].quota = atoi(p_quota); + num_items++; + if (num_items == max_num_items) + { + max_num_items += 16; + prelude = realloc(prelude, sizeof(prelude[0]) * max_num_items); + } + } + + hard_thread->prelude = prelude; + hard_thread->prelude_num_items = num_items; + hard_thread->prelude_idx = 0; + + return FCS_COMPILE_PRELUDE_OK; +} + + +void freecell_solver_init_instance(freecell_solver_instance_t * instance) +{ + int ht_idx; + freecell_solver_hard_thread_t * hard_thread; +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + instance->num_prev_states_margin = 0; + + instance->max_num_indirect_prev_states = PREV_STATES_GROW_BY; + + instance->indirect_prev_states = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); +#endif + + /* Initialize the state packs */ + for(ht_idx=0;ht_idxnum_hard_threads;ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + if (hard_thread->prelude_as_string) + { + compile_prelude(hard_thread); + } + hard_thread->num_times_left_for_soft_thread = + hard_thread->soft_threads[0]->num_times_step; + freecell_solver_state_ia_init(hard_thread); + } + + /* Normalize the A* Weights, so the sum of all of them would be 1. */ + foreach_soft_thread(instance, normalize_a_star_weights, NULL); + + { + int total_tests = 0; + foreach_soft_thread(instance, accumulate_tests_order, &total_tests); + foreach_soft_thread(instance, determine_scan_completeness, &total_tests); + if (instance->opt_tests_order_set == 0) + { + /* + * + * What this code does is convert the bit map of total_tests + * to a valid tests order. + * + * */ + int bit_idx, num_tests = 0; + int * tests = malloc(sizeof(total_tests)*8*sizeof(tests[0])); + + for(bit_idx=0; total_tests != 0; bit_idx++, total_tests >>= 1) + { + if ((total_tests & 0x1) != 0) + { + tests[num_tests++] = bit_idx; + } + } + tests = realloc(tests, num_tests*sizeof(tests[0])); + instance->opt_tests_order.tests = tests; + instance->opt_tests_order.num = + instance->opt_tests_order.max_num = + num_tests; + instance->opt_tests_order_set = 1; + } + } + + +} + + + + +/* These are all stack comparison functions to be used for the stacks + cache when using INDIRECT_STACK_STATES +*/ +#if defined(INDIRECT_STACK_STATES) + +extern int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2); + +#if ((FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_TREE) && (FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_HASH)) +static int fcs_stack_compare_for_comparison_with_context( + const void * v_s1, + const void * v_s2, +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + const +#endif + void * context + + ) +{ + (void)context; + return freecell_solver_stack_compare_for_comparison(v_s1, v_s2); +} +#endif + + + + + +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) +/* A hash calculation function for use in glib's hash */ +static guint freecell_solver_glib_hash_stack_hash_function ( + gconstpointer key + ) +{ + guint hash_value_int; + /* Calculate the hash value for the stack */ + /* This hash function was ripped from the Perl source code. + * (It is not derived work however). */ + const char * s_ptr = (char*)key; + const char * s_end = s_ptr+fcs_standalone_stack_len((fcs_card_t *)key)+1; + hash_value_int = 0; + while (s_ptr < s_end) + { + hash_value_int += (hash_value_int << 5) + *(s_ptr++); + } + hash_value_int += (hash_value_int >> 5); + +} + + + + + +static gint freecell_solver_glib_hash_stack_compare ( + gconstpointer a, + gconstpointer b +) +{ + return !(fcs_stack_compare_for_comparison(a,b)); +} +#endif /* (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) */ + + + + + +#endif /* defined(INDIRECT_STACK_STATES) */ + + + + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) +/* + * This hash function is defined in caas.c + * + * */ +extern guint freecell_solver_hash_function(gconstpointer key); +#endif + +/* + * This function traces the solution from the final state down + * to the initial state + * */ +static void trace_solution( + freecell_solver_instance_t * instance + ) +{ + /* + Trace the solution. + */ + fcs_state_with_locations_t * s1; + fcs_move_stack_t * solution_moves; + int move_idx; + fcs_move_stack_t * stack; + fcs_move_t * moves; + + if (instance->solution_moves != NULL) + { + fcs_move_stack_destroy(instance->solution_moves); + instance->solution_moves = NULL; + } + + fcs_move_stack_alloc_into_var(solution_moves); + instance->solution_moves = solution_moves; + + s1 = instance->final_state; + + /* Retrace the step from the current state to its parents */ + while (s1->parent != NULL) + { + /* Mark the state as part of the non-optimized solution */ + s1->visited |= FCS_VISITED_IN_SOLUTION_PATH; + /* Duplicate the move stack */ + { + stack = s1->moves_to_parent; + moves = stack->moves; + for(move_idx=stack->num_moves-1;move_idx>=0;move_idx--) + { + fcs_move_stack_push(solution_moves, moves[move_idx]); + } + } + /* Duplicate the state to a freshly malloced memory */ + + /* Move to the parent state */ + s1 = s1->parent; + } + /* There's one more state than there are move stacks */ + s1->visited |= FCS_VISITED_IN_SOLUTION_PATH; +} + + +static fcs_tests_order_t tests_order_dup(fcs_tests_order_t * orig) +{ + fcs_tests_order_t ret; + + ret.max_num = ret.num = orig->num; + ret.tests = malloc(sizeof(ret.tests[0]) * ret.num); + memcpy(ret.tests, orig->tests, sizeof(ret.tests[0]) * ret.num); + + return ret; +} + +/* + This function optimizes the solution path using a BFS scan on the + states in the solution path. +*/ +static int freecell_solver_optimize_solution( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * optimization_thread; + freecell_solver_soft_thread_t * soft_thread; + + optimization_thread = alloc_hard_thread(instance); + instance->optimization_thread = optimization_thread; + + soft_thread = optimization_thread->soft_threads[0]; + + if (instance->opt_tests_order_set) + { + if (soft_thread->tests_order.tests != NULL) + { + free(soft_thread->tests_order.tests); + } + + soft_thread->tests_order = + tests_order_dup(&(instance->opt_tests_order)); + } + + soft_thread->method = FCS_METHOD_OPTIMIZE; + + soft_thread->is_a_complete_scan = 1; + + /* Initialize the optimization hard-thread and soft-thread */ + optimization_thread->num_times_left_for_soft_thread = 1000000; + freecell_solver_state_ia_init(optimization_thread); + + /* Instruct the optimization hard thread to run indefinitely AFA it + * is concerned */ + optimization_thread->max_num_times = -1; + optimization_thread->ht_max_num_times = -1; + + return + freecell_solver_a_star_or_bfs_do_solve_or_resume( + optimization_thread->soft_threads[0], + instance->state_copy_ptr, + 0 + ); + +} + + +extern void freecell_solver_cache_talon( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * new_state + ); + +/* + This function starts the solution process _for the first time_. If one + wishes to proceed after the iterations limit was reached, one should + use freecell_solver_resume_instance. + + */ +int freecell_solver_solve_instance( + freecell_solver_instance_t * instance, + fcs_state_with_locations_t * init_state + ) +{ + fcs_state_with_locations_t * state_copy_ptr; + + /* Allocate the first state and initialize it to init_state */ + fcs_state_ia_alloc_into_var(state_copy_ptr, instance->hard_threads[0]); + + fcs_duplicate_state(*state_copy_ptr, *init_state); + + { + int a; + for(a=0;astacks_num;a++) + { + fcs_copy_stack(*state_copy_ptr, a, instance->hard_threads[0]->indirect_stacks_buffer); + } + } + + /* Initialize the state to be a base state for the game tree */ + state_copy_ptr->depth = 0; + state_copy_ptr->moves_to_parent = NULL; + state_copy_ptr->visited = 0; + state_copy_ptr->parent = NULL; + memset(&(state_copy_ptr->scan_visited), '\0', sizeof(state_copy_ptr->scan_visited)); + + instance->state_copy_ptr = state_copy_ptr; + + /* Initialize the data structure that will manage the state collection */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + instance->tree = rbinit(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + instance->tree = avl_create(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + instance->tree = rb_create(freecell_solver_state_compare_with_context, NULL); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + instance->tree = g_tree_new(freecell_solver_state_compare); +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + instance->hash = g_hash_table_new( + freecell_solver_hash_function, + freecell_solver_state_compare_equal + ); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + instance->hash = freecell_solver_hash_init( + 2048, + freecell_solver_state_compare_with_context, + NULL + ); +#endif + + /****************************************************/ + +#ifdef INDIRECT_STACK_STATES + /* Initialize the data structure that will manage the stack + collection */ +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH + instance->stacks_hash = freecell_solver_hash_init( + 2048, + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) + instance->stacks_tree = avl_create( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) + instance->stacks_tree = rb_create( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) + instance->stacks_tree = rbinit( + fcs_stack_compare_for_comparison_with_context, + NULL + ); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) + instance->stacks_tree = g_tree_new(fcs_stack_compare_for_comparison); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) + instance->stacks_hash = g_hash_table_new( + freecell_solver_glib_hash_stack_hash_function, + freecell_solver_glib_hash_stack_compare + ); +#endif +#endif + + /***********************************************/ + +#ifdef FCS_WITH_TALONS + /* Initialize the Talon's Cache */ + if (instance->talon_type == FCS_TALON_KLONDIKE) + { + instance->talons_hash = freecell_solver_hash_init( + 512, + fcs_talon_compare_with_context, + NULL + ); + + freecell_solver_cache_talon(instance, instance->state_copy_ptr); + } +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + /* Not working - ignore */ + db_open( + NULL, + DB_BTREE, + O_CREAT|O_RDWR, + 0777, + NULL, + NULL, + &(instance->db) + ); +#endif + + { + fcs_state_with_locations_t * no_use; + + freecell_solver_check_and_add_state( + instance->hard_threads[0]->soft_threads[0], + state_copy_ptr, + &no_use + ); + + } + + instance->ht_idx = 0; + { + int ht_idx; + for(ht_idx=0; ht_idx < instance->num_hard_threads ; ht_idx++) + { + freecell_solver_hard_thread_t * hard_thread; + hard_thread = instance->hard_threads[ht_idx]; + + if (hard_thread->prelude != NULL) + { + hard_thread->prelude_idx = 0; + hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx; + hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota; + hard_thread->prelude_idx++; + } + else + { + hard_thread->st_idx = 0; + } + } + } + + return freecell_solver_resume_instance(instance); +} + + +static int run_hard_thread(freecell_solver_hard_thread_t * hard_thread) +{ + freecell_solver_soft_thread_t * soft_thread; + int num_times_started_at; + int ret; + freecell_solver_instance_t * instance = hard_thread->instance; + /* + * Again, making sure that not all of the soft_threads in this + * hard thread are finished. + * */ + + ret = FCS_STATE_SUSPEND_PROCESS; + while(hard_thread->num_soft_threads_finished < hard_thread->num_soft_threads) + { + soft_thread = hard_thread->soft_threads[hard_thread->st_idx]; + /* + * Move to the next thread if it's already finished + * */ + if (soft_thread->is_finished) + { + /* + * Hmmpf - duplicate code. That's ANSI C for you. + * A macro, anyone? + * */ + +#define switch_to_next_soft_thread() \ + /* \ + * Switch to the next soft thread in the hard thread, \ + * since we are going to call continue and this is \ + * a while loop \ + * */ \ + if ((hard_thread->prelude != NULL) && \ + (hard_thread->prelude_idx < hard_thread->prelude_num_items)) \ + { \ + hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx; \ + hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota; \ + hard_thread->prelude_idx++; \ + } \ + else \ + { \ + hard_thread->st_idx++; \ + if (hard_thread->st_idx == hard_thread->num_soft_threads) \ + { \ + hard_thread->st_idx = 0; \ + } \ + hard_thread->num_times_left_for_soft_thread = hard_thread->soft_threads[hard_thread->st_idx]->num_times_step; \ + } + + + + switch_to_next_soft_thread(); + + continue; + } + + /* + * Keep record of the number of iterations since this + * thread started. + * */ + num_times_started_at = hard_thread->num_times; + /* + * Calculate a soft thread-wise limit for this hard + * thread to run. + * */ + hard_thread->max_num_times = hard_thread->num_times + hard_thread->num_times_left_for_soft_thread; + + + + /* + * Call the resume or solving function that is specific + * to each scan + * + * This switch-like construct calls for declaring a class + * that will abstract a scan. But it's not critical since + * I don't support user-defined scans. + * */ + switch(soft_thread->method) + { + case FCS_METHOD_HARD_DFS: + + if (! soft_thread->initialized) + { + ret = freecell_solver_hard_dfs_solve_for_state( + soft_thread, + instance->state_copy_ptr, + 0, + 0); + + soft_thread->initialized = 1; + } + else + { + ret = freecell_solver_hard_dfs_resume_solution(soft_thread, 0); + } + break; + + case FCS_METHOD_SOFT_DFS: + + if (! soft_thread->initialized) + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0, + 0 + ); + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + NULL, + 1, + 0 + ); + } + break; + + case FCS_METHOD_RANDOM_DFS: + + if (! soft_thread->initialized) + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0, + 1 + ); + + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + soft_thread, + NULL, + 1, + 1 + ); + } + break; + + case FCS_METHOD_BFS: + case FCS_METHOD_A_STAR: + case FCS_METHOD_OPTIMIZE: + if (! soft_thread->initialized) + { + if (soft_thread->method == FCS_METHOD_A_STAR) + { + freecell_solver_a_star_initialize_rater( + soft_thread, + instance->state_copy_ptr + ); + } + + ret = freecell_solver_a_star_or_bfs_do_solve_or_resume( + soft_thread, + instance->state_copy_ptr, + 0 + ); + + soft_thread->initialized = 1; + } + else + { + ret = + freecell_solver_a_star_or_bfs_do_solve_or_resume( + soft_thread, + soft_thread->first_state_to_check, + 1 + ); + } + break; + + default: + ret = FCS_STATE_IS_NOT_SOLVEABLE; + break; + } + /* + * Determine how much iterations we still have left + * */ + hard_thread->num_times_left_for_soft_thread -= (hard_thread->num_times - num_times_started_at); + + /* + * I use <= instead of == because it is possible that + * there will be a few more iterations than what this + * thread was allocated, due to the fact that + * check_and_add_state is only called by the test + * functions. + * + * It's a kludge, but it works. + * */ + if (hard_thread->num_times_left_for_soft_thread <= 0) + { + switch_to_next_soft_thread(); + /* + * Reset num_times_left_for_soft_thread + * */ + + } + + /* + * It this thread indicated that the scan was finished, + * disable the thread or even stop searching altogether. + * */ + if (ret == FCS_STATE_IS_NOT_SOLVEABLE) + { + soft_thread->is_finished = 1; + hard_thread->num_soft_threads_finished++; + if (hard_thread->num_soft_threads_finished == hard_thread->num_soft_threads) + { + instance->num_hard_threads_finished++; + } + /* + * Check if this thread is a complete scan and if so, + * terminate the search + * */ + if (soft_thread->is_a_complete_scan) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + else + { + /* + * Else, make sure ret is something more sensible + * */ + ret = FCS_STATE_SUSPEND_PROCESS; + } + } + + if ((ret == FCS_STATE_WAS_SOLVED) || + ( + (ret == FCS_STATE_SUSPEND_PROCESS) && + /* There's a limit to the scan only + * if max_num_times is greater than 0 */ + ( + ( + (instance->max_num_times > 0) && + (instance->num_times >= instance->max_num_times) + ) || + ( + (instance->max_num_states_in_collection > 0) && + (instance->num_states_in_collection >= instance->max_num_states_in_collection) + + ) + ) + ) + ) + { + return ret; + } + else if ((ret == FCS_STATE_SUSPEND_PROCESS) && + (hard_thread->num_times >= hard_thread->ht_max_num_times)) + { + hard_thread->ht_max_num_times += hard_thread->num_times_step; + break; + } + } + + return ret; +} + + +/* Resume a solution process that was stopped in the middle */ +int freecell_solver_resume_instance( + freecell_solver_instance_t * instance + ) +{ + int ret = FCS_STATE_SUSPEND_PROCESS; + freecell_solver_hard_thread_t * hard_thread; + + /* + * If the optimization thread is defined, it means we are in the + * optimization phase of the total scan. In that case, just call + * its scanning function. + * + * Else, proceed with the normal total scan. + * */ + if (instance->optimization_thread) + { + ret = + freecell_solver_a_star_or_bfs_do_solve_or_resume( + instance->optimization_thread->soft_threads[0], + instance->optimization_thread->soft_threads[0]->first_state_to_check, + 1 + ); + } + else + { + /* + * instance->num_hard_threads_finished signals to us that + * all the incomplete soft threads terminated. It is necessary + * in case the scan only contains incomplete threads. + * + * I.e: 01235 and 01246, where no thread contains all tests. + * */ + while(instance->num_hard_threads_finished < instance->num_hard_threads) + { + /* + * A loop on the hard threads. + * Note that we do not initialize instance->ht_idx because: + * 1. It is initialized before the first call to this function. + * 2. It is reset to zero below. + * */ + for(; + instance->ht_idx < instance->num_hard_threads ; + instance->ht_idx++) + { + hard_thread = instance->hard_threads[instance->ht_idx]; + + ret = run_hard_thread(hard_thread); + if ((ret == FCS_STATE_IS_NOT_SOLVEABLE) || + (ret == FCS_STATE_WAS_SOLVED) || + ( + (ret == FCS_STATE_SUSPEND_PROCESS) && + /* There's a limit to the scan only + * if max_num_times is greater than 0 */ + ( + ( + (instance->max_num_times > 0) && + (instance->num_times >= instance->max_num_times) + ) || + ( + (instance->max_num_states_in_collection > 0) && + (instance->num_states_in_collection >= instance->max_num_states_in_collection) + + ) + ) + ) + + ) + { + goto end_of_hard_threads_loop; + } + } + /* + * Avoid over-flow + * */ + if (instance->ht_idx == instance->num_hard_threads) + { + instance->ht_idx = 0; + } + } + + end_of_hard_threads_loop: + + /* + * If all the incomplete scans finished, then terminate. + * */ + if (instance->num_hard_threads_finished == instance->num_hard_threads) + { + ret = FCS_STATE_IS_NOT_SOLVEABLE; + } + + if (ret == FCS_STATE_WAS_SOLVED) + { + /* Create solution_moves in the first place */ + trace_solution(instance); + } + } + + + if (ret == FCS_STATE_WAS_SOLVED) + { + if (instance->optimize_solution_path) + { + /* Call optimize_solution only once. Make sure that if + * it has already run - we retain the old ret. */ + if (! instance->optimization_thread) + { + ret = freecell_solver_optimize_solution(instance); + } + if (ret == FCS_STATE_WAS_SOLVED) + { + /* Create the solution_moves in the first place */ + trace_solution(instance); + } + } + } + + return ret; +} + + + +/* + Clean up a solving process that was terminated in the middle. + This function does not substitute for later calling + finish_instance() and free_instance(). + */ +void freecell_solver_unresume_instance( + freecell_solver_instance_t * instance + ) +{ + /* + * Do nothing - since finish_instance() can take care of solution_states + * and proto_solution_moves as they were created by these scans, then + * I don't need to do it here, too + * + * */ + (void)instance; +} + + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + +static void freecell_solver_tree_do_nothing(void * data, void * context) +{ +} + +#endif + + +/* A function for freeing a stack for the cleanup of the + stacks collection +*/ +#ifdef INDIRECT_STACK_STATES +#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) +#if 0 +static void freecell_solver_stack_free(void * key, void * context) +{ + free(key); +} +#endif + +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE +static void freecell_solver_libredblack_walk_destroy_stack_action +( + const void * nodep, + const VISIT which, + const int depth, + void * arg + ) +{ + if ((which == leaf) || (which == preorder)) + { + free((void*)nodep); + } +} +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE +static gint freecell_solver_glib_tree_walk_destroy_stack_action +( + gpointer key, + gpointer value, + gpointer data +) +{ + free(key); + + return 0; +} + +#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH +static void freecell_solver_glib_hash_foreach_destroy_stack_action +( + gpointer key, + gpointer value, + gpointer data +) +{ + free(key); +} +#endif + +#endif + +/***********************************************************/ + + + + +void freecell_solver_destroy_move_stack_of_state( + fcs_state_with_locations_t * ptr_state_with_locations, + void * context + ) +{ + (void)context; + if (ptr_state_with_locations->moves_to_parent != NULL) + { + fcs_move_stack_destroy(ptr_state_with_locations->moves_to_parent); + } +} + +/* + This function should be called after the user has retrieved the + results generated by the scan as it will destroy them. + */ +void freecell_solver_finish_instance( + freecell_solver_instance_t * instance + ) +{ + int ht_idx; + freecell_solver_hard_thread_t * hard_thread; + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT) + free(instance->indirect_prev_states); +#endif + + /* De-allocate the state packs */ + for(ht_idx=0;ht_idxnum_hard_threads;ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + freecell_solver_state_ia_finish(hard_thread); + +#ifdef INDIRECT_STACK_STATES + freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator); + hard_thread->stacks_allocator = NULL; +#endif + freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator); + hard_thread->move_stacks_allocator = NULL; + + } + + if (instance->optimization_thread) + { + freecell_solver_state_ia_finish(instance->optimization_thread); + } + + + /* De-allocate the state collection */ +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) + rbdestroy(instance->tree); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) + avl_destroy(instance->tree, freecell_solver_tree_do_nothing); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) + rb_destroy(instance->tree, freecell_solver_tree_do_nothing); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) + g_tree_destroy(instance->tree); +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) + g_hash_table_destroy(instance->hash); +#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) + freecell_solver_hash_free(instance->hash); +#endif + + + + /* De-allocate the stack collection while free()'ing the stacks + in the process */ +#ifdef INDIRECT_STACK_STATES +#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH +#if 0 + freecell_solver_hash_free_with_callback(instance->stacks_hash, freecell_solver_stack_free); +#else + freecell_solver_hash_free(instance->stacks_hash); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) +#if 0 + avl_destroy(instance->stacks_tree, freecell_solver_stack_free); +#else + avl_destroy(instance->stacks_tree, NULL); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE) +#if 0 + rb_destroy(instance->stacks_tree, freecell_solver_stack_free); +#else + rb_destroy(instance->stacks_tree, NULL); +#endif +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE) +#if 0 + rbwalk(instance->stacks_tree, + freecell_solver_libredblack_walk_destroy_stack_action, + NULL + ); +#endif + rbdestroy(instance->stacks_tree); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) +#if 0 + g_tree_traverse( + instance->stacks_tree, + freecell_solver_glib_tree_walk_destroy_stack_action, + G_IN_ORDER, + NULL + ); +#endif + g_tree_destroy(instance->stacks_tree); +#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) +#if 0 + g_hash_table_foreach( + instance->stacks_hash, + freecell_solver_glib_hash_foreach_destroy_stack_action, + NULL + ); +#endif + g_hash_table_destroy(instance->stacks_hash); +#endif +#endif + +#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE) + instance->db->close(instance->db,0); +#endif + + + clean_soft_dfs(instance); +} + +freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread( + freecell_solver_instance_t * instance, + int ht_idx, + int st_idx + ) +{ + if (ht_idx >= instance->num_hard_threads) + { + return NULL; + } + else + { + freecell_solver_hard_thread_t * hard_thread; + hard_thread = instance->hard_threads[ht_idx]; + if (st_idx >= hard_thread->num_soft_threads) + { + return NULL; + } + else + { + return hard_thread->soft_threads[st_idx]; + } + } +} + +freecell_solver_soft_thread_t * freecell_solver_new_soft_thread( + freecell_solver_soft_thread_t * soft_thread + ) +{ + freecell_solver_soft_thread_t * ret; + freecell_solver_hard_thread_t * hard_thread; + + hard_thread = soft_thread->hard_thread; + ret = alloc_soft_thread(hard_thread); + + /* Exceeded the maximal number of Soft-Threads in an instance */ + if (ret == NULL) + { + return NULL; + } + + hard_thread->soft_threads = realloc(hard_thread->soft_threads, sizeof(hard_thread->soft_threads[0])*(hard_thread->num_soft_threads+1)); + hard_thread->soft_threads[hard_thread->num_soft_threads] = ret; + hard_thread->num_soft_threads++; + + return ret; +} + +freecell_solver_soft_thread_t * freecell_solver_new_hard_thread( + freecell_solver_instance_t * instance + ) +{ + freecell_solver_hard_thread_t * ret; + + /* Exceeded the maximal number of Soft-Threads in an instance */ + ret = alloc_hard_thread(instance); + + if (ret == NULL) + { + return NULL; + } + + instance->hard_threads = + realloc( + instance->hard_threads, + (sizeof(instance->hard_threads[0]) * (instance->num_hard_threads+1)) + ); + + instance->hard_threads[instance->num_hard_threads] = ret; + + instance->num_hard_threads++; + + return ret->soft_threads[0]; +} + +void freecell_solver_recycle_instance( + freecell_solver_instance_t * instance + ) +{ + int ht_idx, st_idx; + freecell_solver_hard_thread_t * hard_thread; + freecell_solver_soft_thread_t * soft_thread; + + freecell_solver_finish_instance(instance); + + instance->num_times = 0; + + instance->num_hard_threads_finished = 0; + + for(ht_idx = 0; ht_idx < instance->num_hard_threads; ht_idx++) + { + hard_thread = instance->hard_threads[ht_idx]; + hard_thread->num_times = 0; + hard_thread->ht_max_num_times = hard_thread->num_times_step; + hard_thread->max_num_times = -1; + hard_thread->num_soft_threads_finished = 0; + hard_thread->move_stacks_allocator = + freecell_solver_compact_allocator_new(); +#ifdef INDIRECT_STACK_STATES + hard_thread->stacks_allocator = + freecell_solver_compact_allocator_new(); +#endif + for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++) + { + soft_thread = hard_thread->soft_threads[st_idx]; + soft_thread->is_finished = 0; + soft_thread->initialized = 0; + + freecell_solver_rand_srand(soft_thread->rand_gen, soft_thread->rand_seed); + /* Reset the priority queue */ + soft_thread->a_star_pqueue->CurrentSize = 0; + } + } +} diff --git a/kpat/freecell-solver/jhjtypes.h b/kpat/freecell-solver/jhjtypes.h new file mode 100644 index 00000000..5a98f4c2 --- /dev/null +++ b/kpat/freecell-solver/jhjtypes.h @@ -0,0 +1,25 @@ +/* + jhjtypes.h - header file for Justin-Heyes Jones' defined types + + Written by Justin-Heyes Jones + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html +*/ + +#ifndef FC_SOLVE__JHJTYPES_H +#define FC_SOLVE__JHJTYPES_H + +/* Data types used in JHeyes-Jones sample code */ + +typedef int int32; +typedef unsigned int uint32; +typedef short int16; +typedef unsigned short uint16; +typedef signed char int8; +typedef unsigned char uint8; + +#endif /* #ifdef FC_SOLVE__JHJTYPES_H */ diff --git a/kpat/freecell-solver/lib.c b/kpat/freecell-solver/lib.c new file mode 100644 index 00000000..1839614b --- /dev/null +++ b/kpat/freecell-solver/lib.c @@ -0,0 +1,1244 @@ +/* + * lib.c - library interface functions of Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#include +#include +#include + +#include "card.h" +#include "fcs.h" +#include "preset.h" +#include "fcs_user.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +struct fcs_instance_item_struct +{ + freecell_solver_instance_t * instance; + int ret; + int limit; +}; + +typedef struct fcs_instance_item_struct fcs_instance_item_t; + +struct fcs_user_struct +{ + /* + * This is a list of several consecutive instances that are run + * one after the other in case the previous ones could not solve + * the board + * */ + fcs_instance_item_t * instances_list; + int num_instances; + int max_num_instances; + + int current_instance_idx; + /* + * The global (sequence-wide) limit of the iterations. Used + * by limit_iterations() and friends + * */ + int current_iterations_limit; + /* + * The number of iterations this board started at. + * */ + int iterations_board_started_at; + /* + * The number of iterations that the current instance started solving from. + * */ + int init_num_times; + /* + * A pointer to the currently active instance out of the sequence + * */ + freecell_solver_instance_t * instance; + fcs_state_with_locations_t state; + fcs_state_with_locations_t running_state; + int ret; + int state_validity_ret; + fcs_card_t state_validity_card; + freecell_solver_user_iter_handler_t iter_handler; + void * iter_handler_context; + + freecell_solver_soft_thread_t * soft_thread; + +#ifdef INDIRECT_STACK_STATES + fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7]; +#endif + char * state_string_copy; + + fcs_preset_t common_preset; +}; + +typedef struct fcs_user_struct fcs_user_t; + +static void user_initialize( + fcs_user_t * ret + ) +{ + const fcs_preset_t * freecell_preset; + + freecell_solver_get_preset_by_name( + "freecell", + &freecell_preset + ); + + fcs_duplicate_preset(ret->common_preset, *freecell_preset); + + ret->max_num_instances = 10; + ret->instances_list = malloc(sizeof(ret->instances_list[0]) * ret->max_num_instances); + ret->num_instances = 1; + ret->current_instance_idx = 0; + ret->instance = freecell_solver_alloc_instance(); + freecell_solver_apply_preset_by_ptr(ret->instance, &(ret->common_preset)); + ret->instances_list[ret->current_instance_idx].instance = ret->instance; + ret->instances_list[ret->current_instance_idx].ret = ret->ret = FCS_STATE_NOT_BEGAN_YET; + ret->instances_list[ret->current_instance_idx].limit = -1; + ret->current_iterations_limit = -1; + + ret->soft_thread = + freecell_solver_instance_get_soft_thread( + ret->instance, 0,0 + ); + + ret->state_string_copy = NULL; + ret->iterations_board_started_at = 0; +} + +void * freecell_solver_user_alloc(void) +{ + fcs_user_t * ret; + + ret = (fcs_user_t *)malloc(sizeof(fcs_user_t)); + + user_initialize(ret); + + return (void*)ret; +} + +int freecell_solver_user_apply_preset( + void * user_instance, + const char * preset_name) +{ + const fcs_preset_t * new_preset_ptr; + fcs_user_t * user; + int status; + int i; + + user = (fcs_user_t*)user_instance; + + status = + freecell_solver_get_preset_by_name( + preset_name, + &new_preset_ptr + ); + + if (status != FCS_PRESET_CODE_OK) + { + return status; + } + + for(i = 0 ; i < user->num_instances ; i++) + { + status = freecell_solver_apply_preset_by_ptr( + user->instances_list[i].instance, + new_preset_ptr + ); + + if (status != FCS_PRESET_CODE_OK) + { + return status; + } + } + + fcs_duplicate_preset(user->common_preset, *new_preset_ptr); + + return FCS_PRESET_CODE_OK; +} + +void freecell_solver_user_limit_iterations( + void * user_instance, + int max_iters + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->current_iterations_limit = max_iters; +} + +void freecell_solver_user_limit_current_instance_iterations( + void * user_instance, + int max_iters + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->instances_list[user->current_instance_idx].limit = max_iters; +} + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +int freecell_solver_user_set_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + return + freecell_solver_apply_tests_order( + &(user->soft_thread->tests_order), + tests_order, + error_string + ); +} + +int freecell_solver_user_solve_board( + void * user_instance, + const char * state_as_string + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->state_string_copy = strdup(state_as_string); + + user->current_instance_idx = 0; + + return freecell_solver_user_resume_solution(user_instance); +} + +static void recycle_instance( + fcs_user_t * user, + int i + ) +{ + if (user->instances_list[i].ret == FCS_STATE_WAS_SOLVED) + { + fcs_move_stack_destroy(user->instance->solution_moves); + user->instance->solution_moves = NULL; + } + else if (user->instances_list[i].ret == FCS_STATE_SUSPEND_PROCESS) + { + freecell_solver_unresume_instance(user->instances_list[i].instance); + } + + if (user->instances_list[i].ret != FCS_STATE_NOT_BEGAN_YET) + { + freecell_solver_recycle_instance(user->instances_list[i].instance); + /* + * We have to initialize init_num_times to 0 here, because it may not + * get initialized again, and now the num_times of the instance + * is equal to 0. + * */ + user->init_num_times = 0; + } + + user->instances_list[i].ret = FCS_STATE_NOT_BEGAN_YET; +} + +int freecell_solver_user_resume_solution( + void * user_instance + ) +{ + int init_num_times; + int run_for_first_iteration = 1; + int ret; + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + /* + * I expect user->current_instance_idx to be initialized at some value. + * */ + for( ; + run_for_first_iteration || ((user->current_instance_idx < user->num_instances) && (ret == FCS_STATE_IS_NOT_SOLVEABLE)) ; + recycle_instance(user, user->current_instance_idx), user->current_instance_idx++ + ) + { + run_for_first_iteration = 0; + + user->instance = user->instances_list[user->current_instance_idx].instance; + + if (user->instances_list[user->current_instance_idx].ret == FCS_STATE_NOT_BEGAN_YET) + { + int status; + status = freecell_solver_initial_user_state_to_c( + user->state_string_copy, + &(user->state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num +#ifdef FCS_WITH_TALONS + ,user->instance->talon_type +#endif +#ifdef INDIRECT_STACK_STATES + ,user->indirect_stacks_buffer +#endif + ); + + if (status != FCS_USER_STATE_TO_C__SUCCESS) + { + user->ret = FCS_STATE_INVALID_STATE; + user->state_validity_ret = FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT; + return user->ret; + } + + user->state_validity_ret = freecell_solver_check_state_validity( + &user->state, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, +#ifdef FCS_WITH_TALONS + FCS_TALON_NONE, +#endif + &(user->state_validity_card)); + + if (user->state_validity_ret != 0) + { + user->ret = FCS_STATE_INVALID_STATE; + return user->ret; + } + + + /* running_state is a normalized state. So I'm duplicating + * state to it before state is canonized + * */ + fcs_duplicate_state(user->running_state, user->state); + + fcs_canonize_state( + &user->state, + user->instance->freecells_num, + user->instance->stacks_num + ); + + freecell_solver_init_instance(user->instance); + +#define global_limit() \ + (user->instance->num_times + user->current_iterations_limit - user->iterations_board_started_at) +#define local_limit() \ + (user->instances_list[user->current_instance_idx].limit) +#define min(a,b) (((a)<(b))?(a):(b)) +#define calc_max_iters() \ + { \ + if (user->instances_list[user->current_instance_idx].limit < 0) \ + {\ + if (user->current_iterations_limit < 0)\ + {\ + user->instance->max_num_times = -1;\ + }\ + else\ + {\ + user->instance->max_num_times = global_limit();\ + }\ + }\ + else\ + {\ + if (user->current_iterations_limit < 0)\ + {\ + user->instance->max_num_times = local_limit();\ + }\ + else\ + {\ + int a, b;\ + \ + a = global_limit();\ + b = local_limit();\ + \ + user->instance->max_num_times = min(a,b);\ + }\ + }\ + } + + + calc_max_iters(); + + user->init_num_times = init_num_times = user->instance->num_times; + + ret = user->ret = + user->instances_list[user->current_instance_idx].ret = + freecell_solver_solve_instance(user->instance, &user->state); + } + else + { + + calc_max_iters(); + + user->init_num_times = init_num_times = user->instance->num_times; + + ret = user->ret = + user->instances_list[user->current_instance_idx].ret = + freecell_solver_resume_instance(user->instance); + } + + user->iterations_board_started_at += user->instance->num_times - init_num_times; + user->init_num_times = user->instance->num_times; + + if (user->ret == FCS_STATE_WAS_SOLVED) + { + freecell_solver_move_stack_normalize( + user->instance->solution_moves, + &(user->state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num + ); + + break; + } + else if (user->ret == FCS_STATE_SUSPEND_PROCESS) + { + /* + * First - check if we exceeded our limit. If so - we must terminate + * and return now. + * */ + if ((user->current_iterations_limit >= 0) && + (user->iterations_board_started_at >= user->current_iterations_limit)) + { + break; + } + + /* + * Determine if we exceeded the instance-specific quota and if + * so, designate it as unsolvable. + * */ + if ((local_limit() >= 0) && + (user->instance->num_times >= local_limit()) + ) + { + ret = FCS_STATE_IS_NOT_SOLVEABLE; + } + } + } + + return ret; +} + +int freecell_solver_user_get_next_move( + void * user_instance, + fcs_move_t * move + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + if (user->ret == FCS_STATE_WAS_SOLVED) + { + int ret; + + ret = fcs_move_stack_pop( + user->instance->solution_moves, + move + ); + + if (ret == 0) + { + freecell_solver_apply_move( + &(user->running_state), + *move, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num + ); + } + return ret; + } + else + { + return 1; + } +} + +char * freecell_solver_user_current_state_as_string( + void * user_instance, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return + freecell_solver_state_as_string( + &(user->running_state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + parseable_output, + canonized_order_output, + display_10_as_t + ); +} + +static void user_free_resources( + fcs_user_t * user + ) +{ + int i; + + for(i=0;inum_instances;i++) + { + int ret_code = user->instances_list[i].ret; + + if (ret_code == FCS_STATE_WAS_SOLVED) + { + fcs_move_stack_destroy(user->instance->solution_moves); + user->instance->solution_moves = NULL; + } + else if (ret_code == FCS_STATE_SUSPEND_PROCESS) + { + freecell_solver_unresume_instance(user->instances_list[i].instance); + } + + if (ret_code != FCS_STATE_NOT_BEGAN_YET) + { + if (ret_code != FCS_STATE_INVALID_STATE) + { + freecell_solver_finish_instance(user->instances_list[i].instance); + } + } + + freecell_solver_free_instance(user->instances_list[i].instance); + } + + free(user->instances_list); + + if (user->state_string_copy != NULL) + { + free(user->state_string_copy); + user->state_string_copy = NULL; + } +} + +void freecell_solver_user_free( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user_free_resources(user); + + free(user); +} + +int freecell_solver_user_get_current_depth( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return (user->soft_thread->num_solution_states - 1); +} + +void freecell_solver_user_set_solving_method( + void * user_instance, + int method + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->soft_thread->method = method; +} + +#define set_for_all_instances(what) \ + { \ + for(i = 0 ; i < user->num_instances ; i++) \ + { \ + user->instances_list[i].instance->what = what; \ + } \ + user->common_preset.what = what; \ + } + +int freecell_solver_user_set_num_freecells( + void * user_instance, + int freecells_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((freecells_num < 0) || (freecells_num > MAX_NUM_FREECELLS)) + { + return 1; + } + + set_for_all_instances(freecells_num); + + return 0; +} + +int freecell_solver_user_set_num_stacks( + void * user_instance, + int stacks_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((stacks_num < 0) || (stacks_num > MAX_NUM_STACKS)) + { + return 1; + } + set_for_all_instances(stacks_num); + + return 0; +} + +int freecell_solver_user_set_num_decks( + void * user_instance, + int decks_num + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((decks_num < 0) || (decks_num > MAX_NUM_DECKS)) + { + return 1; + } + set_for_all_instances(decks_num); + + return 0; +} + + +int freecell_solver_user_set_game( + void * user_instance, + int freecells_num, + int stacks_num, + int decks_num, + int sequences_are_built_by, + int unlimited_sequence_move, + int empty_stacks_fill + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if (freecell_solver_user_set_num_freecells(user_instance, freecells_num)) + { + return 1; + } + if (freecell_solver_user_set_num_stacks(user_instance, stacks_num)) + { + return 2; + } + if (freecell_solver_user_set_num_decks(user_instance, decks_num)) + { + return 3; + } + if (freecell_solver_user_set_sequences_are_built_by_type(user_instance, sequences_are_built_by)) + { + return 4; + } + if (freecell_solver_user_set_sequence_move(user_instance, unlimited_sequence_move)) + { + return 5; + } + if (freecell_solver_user_set_empty_stacks_filled_by(user_instance, empty_stacks_fill)) + { + return 6; + } + + return 0; +} + +int freecell_solver_user_get_num_times(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->iterations_board_started_at + user->instance->num_times - user->init_num_times; +} + +int freecell_solver_user_get_limit_iterations(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->instance->max_num_times; +} + +int freecell_solver_user_get_moves_left(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + if (user->ret == FCS_STATE_WAS_SOLVED) + return user->instance->solution_moves->num_moves; + else + return 0; +} + +void freecell_solver_user_set_solution_optimization( + void * user_instance, + int optimize +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->optimize_solution_path = optimize; +} + +char * freecell_solver_user_move_to_string( + fcs_move_t move, + int standard_notation + ) +{ + return freecell_solver_move_to_string(move, standard_notation); +} + +char * freecell_solver_user_move_to_string_w_state( + void * user_instance, + fcs_move_t move, + int standard_notation + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return + freecell_solver_move_to_string_w_state( + &(user->running_state), + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + move, + standard_notation + ); +} + +void freecell_solver_user_limit_depth( + void * user_instance, + int max_depth +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->max_depth = max_depth; +} + +int freecell_solver_user_get_max_num_freecells(void) +{ + return MAX_NUM_FREECELLS; +} + +int freecell_solver_user_get_max_num_stacks(void) +{ + return MAX_NUM_STACKS; +} + +int freecell_solver_user_get_max_num_decks(void) +{ + return MAX_NUM_DECKS; +} + + +char * freecell_solver_user_get_invalid_state_error_string( + void * user_instance, + int print_ts + ) +{ + fcs_user_t * user; + char string[80], card_str[10]; + + user = (fcs_user_t *)user_instance; + + if (user->state_validity_ret == FCS_STATE_VALIDITY__OK) + { + return strdup(""); + } + fcs_card_perl2user(user->state_validity_card, card_str, print_ts); + + if (user->state_validity_ret == FCS_STATE_VALIDITY__EMPTY_SLOT) + { + sprintf(string, "%s", + "There's an empty slot in one of the stacks." + ); + } + else if ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD) || + (user->state_validity_ret == FCS_STATE_VALIDITY__MISSING_CARD) + ) + { + sprintf(string, "%s%s.", + ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD)? "There's an extra card: " : "There's a missing card: "), + card_str + ); + } + else if (user->state_validity_ret == FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT) + { + sprintf(string, "%s.", "Not enough input"); + } + return strdup(string); +} + +int freecell_solver_user_set_sequences_are_built_by_type( + void * user_instance, + int sequences_are_built_by + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((sequences_are_built_by < 0) || (sequences_are_built_by > 2)) + { + return 1; + } + set_for_all_instances(sequences_are_built_by) + + return 0; +} + +int freecell_solver_user_set_sequence_move( + void * user_instance, + int unlimited_sequence_move + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + set_for_all_instances(unlimited_sequence_move); + + return 0; +} + +int freecell_solver_user_set_empty_stacks_filled_by( + void * user_instance, + int empty_stacks_fill + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + if ((empty_stacks_fill < 0) || (empty_stacks_fill > 2)) + { + return 1; + } + set_for_all_instances(empty_stacks_fill); + + return 0; +} + +int freecell_solver_user_set_a_star_weight( + void * user_instance, + int index, + double weight + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if ((index < 0) || (index >= (int)(sizeof(user->soft_thread->a_star_weights)/sizeof(user->soft_thread->a_star_weights[0])))) + { + return 1; + } + if (weight < 0) + { + return 2; + } + + user->soft_thread->a_star_weights[index] = weight; + + return 0; + +} + +static void freecell_solver_user_iter_handler_wrapper( + void * user_instance, + int iter_num, + int depth, + void * lp_instance, + fcs_state_with_locations_t * ptr_state_with_locations, + int parent_iter_num + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->iter_handler( + user_instance, + iter_num, + depth, + (void *)ptr_state_with_locations, + parent_iter_num, + user->iter_handler_context + ); + + (void)lp_instance; + return; +} + +void freecell_solver_user_set_iter_handler( +void * user_instance, +freecell_solver_user_iter_handler_t iter_handler, +void * iter_handler_context +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +if (iter_handler == NULL) +{ + user->instance->debug_iter_output = 0; +} +else +{ + /* Disable it temporarily while we change the settings */ + user->instance->debug_iter_output = 0; + user->iter_handler = iter_handler; + user->iter_handler_context = iter_handler_context; + user->instance->debug_iter_output_context = user; + user->instance->debug_iter_output_func = freecell_solver_user_iter_handler_wrapper; + user->instance->debug_iter_output = 1; +} +} + +char * freecell_solver_user_iter_state_as_string( +void * user_instance, +void * ptr_state, +int parseable_output, +int canonized_order_output, +int display_10_as_t +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +return + freecell_solver_state_as_string( + ptr_state, + user->instance->freecells_num, + user->instance->stacks_num, + user->instance->decks_num, + parseable_output, + canonized_order_output, + display_10_as_t + ); +} + +void freecell_solver_user_set_random_seed( +void * user_instance, +int seed +) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +freecell_solver_rand_srand(user->soft_thread->rand_gen, (user->soft_thread->rand_seed = seed)); +} + +int freecell_solver_user_get_num_states_in_collection(void * user_instance) +{ +fcs_user_t * user; + +user = (fcs_user_t *)user_instance; + +return user->instance->num_states_in_collection; +} + +void freecell_solver_user_limit_num_states_in_collection( +void * user_instance, +int max_num_states + ) +{ + fcs_user_t * user; + + user = (fcs_user_t*)user_instance; + + user->instance->max_num_states_in_collection = max_num_states; +} + +int freecell_solver_user_next_soft_thread( + void * user_instance + ) +{ + fcs_user_t * user; + freecell_solver_soft_thread_t * soft_thread; + + user = (fcs_user_t *)user_instance; + + soft_thread = freecell_solver_new_soft_thread(user->soft_thread); + + if (soft_thread == NULL) + { + return 1; + } + + user->soft_thread = soft_thread; + + return 0; +} + +extern void freecell_solver_user_set_soft_thread_step( + void * user_instance, + int num_times_step + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->soft_thread->num_times_step = num_times_step; +} + +int freecell_solver_user_next_hard_thread( + void * user_instance + ) +{ + fcs_user_t * user; + freecell_solver_soft_thread_t * soft_thread; + + user = (fcs_user_t *)user_instance; + + soft_thread = freecell_solver_new_hard_thread(user->instance); + + if (soft_thread == NULL) + { + return 1; + } + + user->soft_thread = soft_thread; + + return 0; +} + +int freecell_solver_user_get_num_soft_threads_in_instance( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + return user->instance->next_soft_thread_id; +} + +void freecell_solver_user_set_calc_real_depth( + void * user_instance, + int calc_real_depth +) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->calc_real_depth = calc_real_depth; +} + +void freecell_solver_user_set_soft_thread_name( + void * user_instance, + char * name + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + if (user->soft_thread->name != NULL) + { + free(user->soft_thread->name); + } + user->soft_thread->name = strdup(name); +} + +int freecell_solver_user_set_hard_thread_prelude( + void * user_instance, + char * prelude + ) +{ + fcs_user_t * user; + freecell_solver_hard_thread_t * hard_thread; + + user = (fcs_user_t *)user_instance; + + hard_thread = user->soft_thread->hard_thread; + + if (hard_thread->prelude_as_string != NULL) + { + free(hard_thread->prelude_as_string); + hard_thread->prelude_as_string = NULL; + } + hard_thread->prelude_as_string = strdup(prelude); + + return 0; +} + +void freecell_solver_user_recycle( + void * user_instance + ) +{ + fcs_user_t * user; + int i; + + user = (fcs_user_t *)user_instance; + + for(i=0;inum_instances;i++) + { + recycle_instance(user, i); + } + user->current_iterations_limit = -1; + user->iterations_board_started_at = 0; + if (user->state_string_copy != NULL) + { + free(user->state_string_copy); + user->state_string_copy = NULL; + } +} + +int freecell_solver_user_set_optimization_scan_tests_order( + void * user_instance, + const char * tests_order, + char * * error_string + ) +{ + fcs_user_t * user; + int ret; + + user = (fcs_user_t*)user_instance; + + if (user->instance->opt_tests_order.tests) + { + free(user->instance->opt_tests_order.tests); + user->instance->opt_tests_order.tests = NULL; + } + + user->instance->opt_tests_order_set = 0; + + ret = + freecell_solver_apply_tests_order( + &(user->instance->opt_tests_order), + tests_order, + error_string + ); + + if (!ret) + { + user->instance->opt_tests_order_set = 1; + } + + return ret; +} + +void freecell_solver_user_set_reparent_states( + void * user_instance, + int to_reparent_states + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->to_reparent_states = to_reparent_states; +} + +void freecell_solver_user_set_scans_synergy( + void * user_instance, + int synergy + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->instance->scans_synergy = synergy; +} + +int freecell_solver_user_next_instance( + void * user_instance + ) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user->num_instances++; + if (user->num_instances == user->max_num_instances) + { + user->max_num_instances += 10; + user->instances_list = + realloc( + user->instances_list, + sizeof(user->instances_list[0])*user->max_num_instances + ); + } + user->current_instance_idx = user->num_instances-1; + user->instance = freecell_solver_alloc_instance(); + + freecell_solver_apply_preset_by_ptr(user->instance, &(user->common_preset)); + + /* + * Switch the soft_thread variable so it won't refer to the old + * instance + * */ + user->soft_thread = + freecell_solver_instance_get_soft_thread( + user->instance, 0, 0 + ); + + user->instances_list[user->current_instance_idx].instance = user->instance; + user->instances_list[user->current_instance_idx].ret = user->ret = FCS_STATE_NOT_BEGAN_YET; + user->instances_list[user->current_instance_idx].limit = -1; + + return 0; +} + +int freecell_solver_user_reset(void * user_instance) +{ + fcs_user_t * user; + + user = (fcs_user_t *)user_instance; + + user_free_resources(user); + + user_initialize(user); + + return 0; +} + diff --git a/kpat/freecell-solver/lookup2.c b/kpat/freecell-solver/lookup2.c new file mode 100644 index 00000000..6ab9ae7e --- /dev/null +++ b/kpat/freecell-solver/lookup2.c @@ -0,0 +1,119 @@ +/* +-------------------------------------------------------------------- +lookup2.c, by Bob Jenkins, December 1996, Public Domain. +hash(), hash2(), hash3, and mix() are externally useful functions. +Routines to test the hash are included if SELF_TEST is defined. +You can use this free for any purpose. It has no warranty. +-------------------------------------------------------------------- + +Note: + This code was ripped and modified by Shlomi Fish. The original can + be found at http://burtleburtle.net/bob/c/lookup2.c. +*/ + +#include +#include +#include + +#include "lookup2.h" + + +#define hashsize(n) ((ub4)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. hash2() is identical to hash() on + little-endian machines, except that the length has to be measured + in ub4s instead of bytes. It is much faster than hash(). It + requires + -- that the key be an array of ub4's, and + -- that all your machines have the same endianness, and + -- that the length be the number of ub4's in the key +-------------------------------------------------------------------- +*/ + +ub4 freecell_solver_lookup2_hash_function( + register ub1 *k, /* the key */ + register ub4 length, /* the length of the key */ + register ub4 initval /* the previous hash, or an arbitrary value */ + ) +{ + register ub4 a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24)); + b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24)); + c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((ub4)k[10]<<24); + case 10: c+=((ub4)k[9]<<16); + case 9 : c+=((ub4)k[8]<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((ub4)k[7]<<24); + case 7 : b+=((ub4)k[6]<<16); + case 6 : b+=((ub4)k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=((ub4)k[3]<<24); + case 3 : a+=((ub4)k[2]<<16); + case 2 : a+=((ub4)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} diff --git a/kpat/freecell-solver/lookup2.h b/kpat/freecell-solver/lookup2.h new file mode 100644 index 00000000..002502ed --- /dev/null +++ b/kpat/freecell-solver/lookup2.h @@ -0,0 +1,13 @@ +#ifndef FC_SOLVE__LOOKUP2_H +#define FC_SOLVE__LOOKUP2_H + +typedef unsigned long int ub4; /* unsigned 4-byte quantities */ +typedef unsigned char ub1; + +ub4 freecell_solver_lookup2_hash_function( + register ub1 *k, /* the key */ + register ub4 length, /* the length of the key */ + register ub4 initval /* the previous hash, or an arbitrary value */ + ); + +#endif /* FC_SOLVE__LOOKUP2_H */ diff --git a/kpat/freecell-solver/main.c b/kpat/freecell-solver/main.c new file mode 100644 index 00000000..d16468c4 --- /dev/null +++ b/kpat/freecell-solver/main.c @@ -0,0 +1,859 @@ +#include +#include +#include +#include + +#include "fcs_cl.h" + +struct freecell_solver_display_information_context_struct +{ + int debug_iter_state_output; + int freecells_num; + int stacks_num; + int decks_num; + int parseable_output; + int canonized_order_output; + int display_10_as_t; + int display_parent_iter_num; + int debug_iter_output_on; + int display_moves; + int display_states; + int standard_notation; +}; + +typedef struct freecell_solver_display_information_context_struct freecell_solver_display_information_context_t; + +static void init_debug_context( + freecell_solver_display_information_context_t * dc + ) +{ + dc->parseable_output = 0; + dc->canonized_order_output = 0; + dc->display_10_as_t = 0; + dc->display_parent_iter_num = 0; + dc->display_moves = 0; + dc->display_states = 1; + dc->standard_notation = 0; +} + + + +static void my_iter_handler( + void * user_instance, + int iter_num, + int depth, + void * ptr_state, + int parent_iter_num, + void * lp_context + ) +{ + freecell_solver_display_information_context_t * context; + context = (freecell_solver_display_information_context_t*)lp_context; + + fprintf(stdout, "Iteration: %i\n", iter_num); + fprintf(stdout, "Depth: %i\n", depth); + fprintf(stdout, "Stored-States: %i\n", + freecell_solver_user_get_num_states_in_collection(user_instance) + ); + if (context->display_parent_iter_num) + { + fprintf(stdout, "Parent Iteration: %i\n", parent_iter_num); + } + fprintf(stdout, "\n"); + + + if (context->debug_iter_state_output) + { + char * state_string = + freecell_solver_user_iter_state_as_string( + user_instance, + ptr_state, + context->parseable_output, + context->canonized_order_output, + context->display_10_as_t + ); + printf("%s\n---------------\n\n\n", state_string); + free((void*)state_string); + } + +#ifdef MYDEBUG + { + fcs_card_t card; + int ret; + char card_str[10]; + + ret = fcs_check_state_validity( + ptr_state_with_locations, + context->freecells_num, + context->stacks_num, + context->decks_num, + &card + ); + + if (ret != 0) + { + + fcs_card_perl2user(card, card_str, context->display_10_as_t); + if (ret == 3) + { + fprintf(stdout, "%s\n", + "There's an empty slot in one of the stacks." + ); + } + else + { + fprintf(stdout, "%s%s.\n", + ((ret == 2)? "There's an extra card: " : "There's a missing card: "), + card_str + ); + } + exit(-1); + } + } +#endif +} + +struct help_screen_struct +{ + char * key; + char * screen; +}; + +typedef struct help_screen_struct help_screen_t; + +help_screen_t help_screens[] = { +{ + "configs", +"These configurations are usually faster than the unmodified run:\n" +"\n" +" fc-solve -l cool-jives\n" +" fc-solve -l john-galt-line\n" +"\n" +"Or if you want an accurate verdict:\n" +"\n" +" fc-solve -l fools-gold\n" +"\n" +"If you want to try constructing your own configurations refer to the\n" +"USAGE file in the Freecell Solver distribution\n" +}, +{ + "options", +"fc-solve [options] board_file\n" +"\n" +"If board_file is - or unspecified reads standard input\n" +"\n" +"Available Options:\n" +"-h --help\n" +" display the default help screen\n" +"--help-summary\n" +" display the summary help screen\n" +"-i --iter-output\n" +" display the iteration number and depth in every state that is checked\n" +"-s --state-output\n" +" also output the state in every state that is checked\n" +"-p --parseable-output\n" +" Output the states in a format that is friendly to perl, grep and\n" +" friends.\n" +"-c --canonized-order-output\n" +" Output the stacks and freecells according to their canonic order.\n" +" (That means that stacks and freecells won't retain their place.)\n" +"-t --display-10-as-t\n" +" Display the card 10 as a capital T instead of \"10\".\n" +"-m --display-moves\n" +" Display the moves instead of the intermediate states.\n" +"-sam --display-states-and-moves \n" +" Display both intermediate states and moves.\n" +"-sn --standard-notation\n" +" Display the moves in standard (non-verbose) notation.\n" +" (Applicable only if -m was specified)\n" +"-snx --standard-notation-extended\n" +" Display the moves in extended standard notation while specifying the\n" +" number of cards moved if applicable\n" +"-pi --display-parent-iter \n" +" Display the index of the parent iteration of each state in the\n" +" run-time dump.\n" +"\n" +"--freecells-num [Freecells\' Number]\n" +" The number of freecells present in the board.\n" +"--stacks-num [Stacks\' Number]\n" +" The number of stacks present in the board.\n" +"--decks-num [Decks\' Number]\n" +" The number of decks in the board.\n" +"\n" +"--sequences-are-built-by {suit|alternate_color|rank}\n" +" Specifies the type of sequence\n" +"--sequence-move {limited|unlimited}\n" +" Specifies whether the sequence move is limited by the number of\n" +" freecells or vacant stacks or not.\n" +"--empty-stacks-filled-by {kings|none|all}\n" +" Specifies which cards can fill empty stacks.\n" +"\n" +"--game [game] --preset [game] -g [game]\n" +" Specifies the type of game. (Implies several of the game settings\n" +" options above.). Available presets:\n" +" bakers_dozen - Baker\'s Dozen\n" +" bakers_game - Baker\'s Game\n" +" beleaguered_castle - Beleaguered Castle\n" +" citadel - Citadel\n" +" cruel - Cruel\n" +" der_katz - Der Katzenschwanz\n" +" die_schlange - Die Schlange\n" +" eight_off - Eight Off\n" +" fan - Fan\n" +" forecell - Forecell\n" +" freecell - Freecell\n" +" good_measure - Good Measure\n" +" ko_bakers_game - Kings\' Only Baker\'s Game\n" +" relaxed_freecell - Relaxed Freecell\n" +" relaxed_seahaven - Relaxed Seahaven Towers\n" +" seahaven - Seahaven Towers\n" +" simple_simon - Simple Simon\n" +" streets_and_alleys - Streets and Alleys\n" +"\n" +"-md [depth] --max-depth [depth] \n" +" Specify a maximal search depth for the solution process.\n" +"-mi [iter_num] --max-iters [iter_num] \n" +" Specify a maximal number of iterations number.\n" +"-mss [states_num] --max-stored-states [states_num] \n" +" Specify the maximal number of states stored in memory.\n" +"\n" +"-to [tests_order] --tests-order [tests_order] \n" +" Specify a test order string. Each test is represented by one character.\n" +" Valid tests:\n" +" Freecell Tests:\n" +"\n" +" '0' - put top stack cards in the foundations.\n" +" '1' - put freecell cards in the foundations.\n" +" '2' - put freecell cards on top of stacks.\n" +" '3' - put non-top stack cards in the foundations.\n" +" '4' - move stack cards to different stacks.\n" +" '5' - move stack cards to a parent card on the same stack.\n" +" '6' - move sequences of cards onto free stacks.\n" +" '7' - put freecell cards on empty stacks.\n" +" '8' - move cards to a different parent.\n" +" '9' - empty an entire stack into the freecells.\n" +"\n" +" Atomic Freecell Tests:\n" +"\n" +" 'A' - move a stack card to an empty stack.\n" +" 'B' - move a stack card to a parent on a different stack.\n" +" 'C' - move a stack card to a freecell.\n" +" 'D' - move a freecel card to a parent.\n" +" 'E' - move a freecel card to an empty stack.\n" +"\n" +" Simple Simon Tests:\n" +"\n" +" 'a' - move a full sequence to the foundations.\n" +" 'b' - move a sequence to a true parent of his.\n" +" 'c' - move a whole stack sequence to a false parent (in order to\n" +" clear the stack)\n" +" 'd' - move a sequence to a true parent that has some cards above it.\n" +" 'e' - move a sequence with some cards above it to a true parent.\n" +" 'f' - move a sequence with a junk sequence above it to a true parent\n" +" that has some cards above it.\n" +" 'g' - move a whole stack sequence to a false parent which has some\n" +" cards above it.\n" +" 'h' - move a sequence to a parent on the same stack.\n" +"\n" +" Tests are grouped with parenthesis or square brackets. Each group\n" +" will be randomized as a whole by the random-dfs scan.\n" +"\n" +"\n" +"-me [solving_method] --method [solving_method]\n" +" Specify a solving method. Available methods are:\n" +" \"a-star\" - A*\n" +" \"bfs\" - Breadth-First Search\n" +" \"dfs\" - Depth-First Search (default)\n" +" \"random-dfs\" - A randomized DFS\n" +" \"soft-dfs\" - \"Soft\" DFS\n" +"\n" +"-asw [A* Weights] --a-star-weight [A* Weights]\n" +" Specify weights for the A* scan, assuming it is used. The parameter\n" +" should be a comma-separated list of numbers, each one is proportional\n" +" to the weight of its corresponding test.\n" +"\n" +" The numbers are, in order:\n" +" 1. The number of cards out.\n" +" 2. The maximal sequence move.\n" +" 3. The number of cards under sequences.\n" +" 4. The length of the sequences which are found over renegade cards.\n" +" 5. The depth of the board in the solution.\n" +"\n" +"-seed [seed_number]\n" +" Set the seed for the random number generator used by the\n" +" \"random-dfs\" scan.\n" +"\n" +"-nst --next-soft-thread\n" +" Move to the next Soft-Thread. I.e: input another scan to run in\n" +" parallel.\n" +"-step [step iterations] --soft-thread-step [step iterations]\n" +" Set the number of iterations in the step of the current soft-thread.\n" +"-nht --next-hard-thread\n" +" Move to the next Hard-Thread. This is a new group of scans to run\n" +" in their own system thread (assuming the executable was compiled with\n" +" support for them.)\n" +"--st-name\n" +" Set the name of the soft-thread.\n" +"\n" +"--prelude [prelude_string]\n" +" Set the prelude string of the hard thread. A prelude is a static\n" +" sequence of iterations quotas that are executed at the beginning of\n" +" the search. The format is a list of [Limit]@[Soft-Thread Name]\n" +" delimited by commas.\n" +"\n" +"-ni --next-instance\n" +" Move to the next distinct solver instance. This is a separate scan\n" +" which would run only if the previous ones returned an unsolvable\n" +" verdict.\n" +"\n" +"-opt --optimize-solution\n" +" Try and optimize the solution for a small number of moves.\n" +"-opt-to --optimization-tests-order\n" +" The test order of the optimization scan.\n" +"\n" +"\n" +"--reparent-states\n" +" Reparent states that have a larger depth than that of the state\n" +" from which they were reached a posteriori.\n" +"--calc-real-depth\n" +" If --reparent-states is enabled, then explictly calculate the real\n" +" depth of a state by tracing its path to the initial state\n" +"--scans-synergy {none|dead-end-marks}\n" +" Specifies the cooperation between the scans.\n" +"\n" +"\n" +"--reset\n" +" Reset the program to its initial, unconfigured state.\n" +"--read-from-file [{num_skip},]filename\n" +" Reads configuration parameter with the file while skipping num_skip\n" +" arguments from the beginning.\n" +"-l [configuration] --load-config [configuration]\n" +" Reads the configuration [configruration] and configures the solver\n" +" accordingly.\n" +"\n" +"\n" +"Signals:\n" +"SIGUSR1 - Prints the number of states that were checked so far to stderr.\n" +"SIGUSR2 SIGUSR1 - Turns iteration output on/off.\n" +"SIGUSR2 SIGUSR2 SIGUSR1 - Turns iteration's state output on/off.\n" +"\n" +"\n" +"Freecell Solver was written by Shlomi Fish.\n" +"Homepage: http://vipe.technion.ac.il/~shlomif/freecell-solver/\n" +"Send comments and suggestions to shlomif@vipe.technion.ac.il\n" +}, +{ + "real-help", +"The environment variable FREECELL_SOLVER_DEFAULT_HELP sets the default help\n" +"screen. The name of the help screen is the same name as its \"--help-\" flag\n" +"but without the preceding \"--help-\". Type:\n" +"\n" +" fc-solve --help-summary\n" +"\n" +"for the available help screens.\n" +"\n" +"Refer to your system's documentation for information on how to set environment\n" +"variables.\n" +}, +{ + "problems", +"To be discussed.\n" +}, +{ + "short-sol", +"The following configurations may produce shorter solutions:\n" +"\n" +" fc-solve -opt\n" +" fc-solve --method a-star -opt\n" +" fc-solve --reparent-states -opt\n" +" fc-solve --method a-star --reparent-states -opt\n" +"\n" +"If \"--method a-star\" is specified you can set the weights with\n" +"-asw {comma separated list of 5 numeric weights}, which may improve\n" +"the length of the solution. (refer to the USAGE file for more information)\n" +}, +{ + "summary", +"fc-solve [flags] [board_file|-]\n" +"\n" +"Reads board from standard input by default or if a \"-\" is specified.\n" +"\n" +"- If it takes too long to finish, type \"fc-solve --help-configs\"\n" +"- If it erroneously reports a board as unsolvable, try adding the\n" +" \"-to 01ABCDE\" flag\n" +"- If the solution is too long type \"fc-solve --help-short-sol\"\n" +"- To present the moves only try adding \"-m\" or \"-m -snx\"\n" +"- For a description of all options type \"fc-solve --help-options\"\n" +"- To deal with other problems type \"fc-solve --help-problems\"\n" +"- To turn --help into something more useful, type\n" +" \"fc-solve --help-real-help\"\n" +"\n" +"Contact Shlomi Fish, shlomif@vipe.technion.ac.il for more information.\n" +}, +{ + NULL, + NULL +} +} +; + +enum MY_FCS_CMD_LINE_RET_VALUES +{ + EXIT_AND_RETURN_0 = FCS_CMD_LINE_USER, + +}; + +static void print_help_string(char * key) +{ + int i; + for(i=0;help_screens[i].key != NULL ; i++) + { + if (!strcmp(key, help_screens[i].key)) + { + printf("%s", help_screens[i].screen); + } + } +} + +static int cmd_line_callback( + void * instance, + int argc, + char * argv[], + int arg, + int * num_to_skip, + int * ret, + void * context + ) +{ + freecell_solver_display_information_context_t * dc; + *num_to_skip = 0; + + dc = (freecell_solver_display_information_context_t * )context; + + if ((!strcmp(argv[arg], "-h")) || (!strcmp(argv[arg], "--help"))) + { + char * help_key; + + help_key = getenv("FREECELL_SOLVER_DEFAULT_HELP"); + if (help_key == NULL) + { + help_key = "summary"; + } + print_help_string(help_key); + *ret = EXIT_AND_RETURN_0; + return FCS_CMD_LINE_STOP; + } + else if (!strncmp(argv[arg], "--help-", 7)) + { + print_help_string(argv[arg]+7); + *ret = EXIT_AND_RETURN_0; + return FCS_CMD_LINE_STOP; + } + else if ((!strcmp(argv[arg], "-i")) || (!strcmp(argv[arg], "--iter-output"))) + { +#define set_iter_handler() \ + freecell_solver_user_set_iter_handler( \ + instance, \ + my_iter_handler, \ + dc \ + ); \ + dc->debug_iter_output_on = 1; + + set_iter_handler(); + } + else if ((!strcmp(argv[arg], "-s")) || (!strcmp(argv[arg], "--state-output"))) + { + set_iter_handler(); + dc->debug_iter_state_output = 1; +#undef set_iter_handler + } + else if ((!strcmp(argv[arg], "-p")) || (!strcmp(argv[arg], "--parseable-output"))) + { + dc->parseable_output = 1; + } + else if ((!strcmp(argv[arg], "-c")) || (!strcmp(argv[arg], "--canonized-order-output"))) + { + dc->canonized_order_output = 1; + } + else if ((!strcmp(argv[arg], "-t")) || (!strcmp(argv[arg], "--display-10-as-t"))) + { + dc->display_10_as_t = 1; + } + else if ((!strcmp(argv[arg], "-m")) || (!strcmp(argv[arg], "--display-moves"))) + { + dc->display_moves = 1; + dc->display_states = 0; + } + else if ((!strcmp(argv[arg], "-sn")) || (!strcmp(argv[arg], "--standard-notation"))) + { + dc->standard_notation = 1; + + } + else if ((!strcmp(argv[arg], "-snx")) || (!strcmp(argv[arg], "--standard-notation-extended"))) + { + dc->standard_notation = 2; + } + else if ((!strcmp(argv[arg], "-sam")) || (!strcmp(argv[arg], "--display-states-and-moves"))) + { + dc->display_moves = 1; + dc->display_states = 1; + } + else if ((!strcmp(argv[arg], "-pi")) || (!strcmp(argv[arg], "--display-parent-iter"))) + { + dc->display_parent_iter_num = 1; + } + else if ((!strcmp(argv[arg], "--reset"))) + { + init_debug_context(dc); + freecell_solver_user_set_iter_handler( + instance, + NULL, + NULL + ); + *num_to_skip = 0; + return FCS_CMD_LINE_OK; + } + else + { + printf("Unimplemented option - \"%s\"!", argv[arg]); + exit(-1); + } + *num_to_skip = 1; + return FCS_CMD_LINE_SKIP; +} + + +static int command_num = 0; +static int debug_iter_output_on = 0; + +static void select_signal_handler(int signal_num) +{ + command_num = (command_num+1)%3; +} + +static void * current_instance; +static freecell_solver_display_information_context_t * dc; + + +static void command_signal_handler(int signal_num) +{ + if (command_num == 0) + { + fprintf( + stderr, + "The number of iterations is %i\n", + freecell_solver_user_get_num_times(current_instance) + ); + } + else if (command_num == 1) + { + if (debug_iter_output_on) + { + freecell_solver_user_set_iter_handler( + current_instance, + NULL, + NULL + ); + debug_iter_output_on = 0; + } + else + { + freecell_solver_user_set_iter_handler( + current_instance, + my_iter_handler, + dc + ); + debug_iter_output_on = 1; + } + } + else if (command_num == 2) + { + dc->debug_iter_state_output = ! dc->debug_iter_state_output; + } + + command_num = 0; +} + + +static char * known_parameters[] = { + "-h", "--help", + "--help-configs", "--help-options", "--help-problems", + "--help-real-help", "--help-short-sol", "--help-summary", + "-i", "--iter-output", + "-s", "--state-output", + "-p", "--parseable-output", + "-c", "--canonized-order-output", + "-t", "--display-10-as-t", + "-m", "--display-moves", + "-sn", "--standard-notation", + "-snx", "--standard-notation-extended", + "-sam", "--display-states-and-moves", + "-pi", "--display-parent-iter", + "--reset", + NULL + }; + +#define USER_STATE_SIZE 1024 + +int main(int argc, char * argv[]) +{ + int parser_ret; + void * instance; + char * error_string; + int arg; + FILE * file; + char user_state[USER_STATE_SIZE]; + int ret; + + freecell_solver_display_information_context_t debug_context; + + init_debug_context(&debug_context); + + dc = &debug_context; + + instance = freecell_solver_user_alloc(); + + current_instance = instance; + + + parser_ret = + freecell_solver_user_cmd_line_parse_args( + instance, + argc, + argv, + 1, + known_parameters, + cmd_line_callback, + &debug_context, + &error_string, + &arg + ); + + if (parser_ret == EXIT_AND_RETURN_0) + { + freecell_solver_user_free(instance); + return 0; + } + else if ( + (parser_ret == FCS_CMD_LINE_PARAM_WITH_NO_ARG) + ) + { + fprintf(stderr, "The command line parameter \"%s\" requires an argument" + " and was not supplied with one.\n", argv[arg]); + return (-1); + } + else if ( + (parser_ret == FCS_CMD_LINE_ERROR_IN_ARG) + ) + { + if (error_string != NULL) + { + fprintf(stderr, "%s", error_string); + free(error_string); + } + freecell_solver_user_free(instance); + return -1; + } + + if ((arg == argc) || (!strcmp(argv[arg], "-"))) + { + file = stdin; + if (!getenv("FREECELL_SOLVER_QUIET")) + { + fprintf(stderr, "%s", + "Reading the board from the standard input.\n" + "Type \"fc-solve --help\" for more usage information.\n" + "To cancel this message set the FREECELL_SOLVER_QUIET environment variable.\n" + ); + } + } + else if (argv[arg][0] == '-') + { + fprintf(stderr, + "Unknown option \"%s\". " + "Type \"%s --help\" for usage information.\n", + argv[arg], + argv[0] + ); + freecell_solver_user_free(instance); + + return -1; + } + else + { + file = fopen(argv[arg], "r"); + if (file == NULL) + { + fprintf(stderr, + "Could not open file \"%s\" for input. Exiting.\n", + argv[arg] + ); + freecell_solver_user_free(instance); + + return -1; + } + } + memset(user_state, '\0', sizeof(user_state)); + fread(user_state, sizeof(user_state[0]), USER_STATE_SIZE-1, file); + fclose(file); + + /* Win32 Does not have those signals */ +#ifndef WIN32 + signal(SIGUSR1, command_signal_handler); + signal(SIGUSR2, select_signal_handler); +#endif + +#if 0 + { + int limit = 500; + freecell_solver_user_limit_iterations(instance, limit); + ret = freecell_solver_user_solve_board(instance, user_state); + while (ret == FCS_STATE_SUSPEND_PROCESS) + { + limit += 500; + freecell_solver_user_limit_iterations(instance, limit); + ret = freecell_solver_user_resume_solution(instance); + } + } +#else + ret = freecell_solver_user_solve_board(instance, user_state); +#endif + + if (ret == FCS_STATE_INVALID_STATE) + { + char * error_string; + error_string = + freecell_solver_user_get_invalid_state_error_string( + instance, + debug_context.display_10_as_t + ); + printf("%s\n", error_string); + free(error_string); + } + else + { + if (ret == FCS_STATE_WAS_SOLVED) + { + printf("-=-=-=-=-=-=-=-=-=-=-=-\n\n"); + { + fcs_move_t move; + FILE * move_dump; + char * as_string; + int move_num = 0; + + move_dump = stdout; + + + if (debug_context.display_states) + { + as_string = + freecell_solver_user_current_state_as_string( + instance, + debug_context.parseable_output, + debug_context.canonized_order_output, + debug_context.display_10_as_t + ); + + fprintf(move_dump, "%s\n", as_string); + + free(as_string); + + fprintf(move_dump, "%s", "\n====================\n\n"); + } + + while ( + freecell_solver_user_get_next_move( + instance, + &move + ) == 0 + ) + { + if (debug_context.display_moves) + { + as_string = + freecell_solver_user_move_to_string_w_state( + instance, + move, + debug_context.standard_notation + ); + + if (debug_context.display_states && debug_context.standard_notation) + { + fprintf(move_dump, "Move: "); + } + + fprintf( + move_dump, + (debug_context.standard_notation ? + "%s " : + "%s\n" + ), + as_string + ); + move_num++; + if (debug_context.standard_notation) + { + if ((move_num % 10 == 0) || debug_context.display_states) + { + fprintf(move_dump, "\n"); + } + } + if (debug_context.display_states) + { + fprintf(move_dump, "\n"); + } + fflush(move_dump); + free(as_string); + } + + if (debug_context.display_states) + { + as_string = + freecell_solver_user_current_state_as_string( + instance, + debug_context.parseable_output, + debug_context.canonized_order_output, + debug_context.display_10_as_t + ); + + fprintf(move_dump, "%s\n", as_string); + + free(as_string); + } + + if (debug_context.display_states || (!debug_context.standard_notation)) + { + fprintf(move_dump, "%s", "\n====================\n\n"); + } + } + + if (debug_context.standard_notation && (!debug_context.display_states)) + { + fprintf(move_dump, "\n\n"); + } + } + + printf("This game is solveable.\n"); + } + else + { + printf ("I could not solve this game.\n"); + } + + printf( + "Total number of states checked is %i.\n", + freecell_solver_user_get_num_times(instance) + ); +#if 1 + printf( + "This scan generated %i states.\n", + freecell_solver_user_get_num_states_in_collection(instance) + ); +#endif + } + + freecell_solver_user_free(instance); + + return 0; +} + diff --git a/kpat/freecell-solver/move.c b/kpat/freecell-solver/move.c new file mode 100644 index 00000000..aa8ed560 --- /dev/null +++ b/kpat/freecell-solver/move.c @@ -0,0 +1,531 @@ +/* + * move.c - move and move stacks routines for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ +#include +#include +#include + +#include "move.h" +#include "state.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#include "inline.h" + +#if 0 +/* This variable was used for debugging. */ +int msc_counter=0; +#endif + +#if 0 +/* This function allocates an empty move stack */ +fcs_move_stack_t * fcs_move_stack_create(void) +{ + fcs_move_stack_t * ret; + + /* Allocate the data structure itself */ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); + + ret->max_num_moves = FCS_MOVE_STACK_GROW_BY; + ret->num_moves = 0; + /* Allocate some space for the moves */ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves); + + return ret; +} +#endif + +#if 0 +int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move) +{ + /* If all the moves inside the stack are taken then + resize the move vector */ + + if (stack->num_moves == stack->max_num_moves) + { + int a, b; + a = (stack->max_num_moves >> 3); + b = FCS_MOVE_STACK_GROW_BY; + stack->max_num_moves += max(a,b); + stack->moves = realloc( + stack->moves, + stack->max_num_moves * sizeof(fcs_move_t) + ); + } + stack->moves[stack->num_moves++] = move; + + return 0; +} +#endif + +int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move) +{ + if (stack->num_moves > 0) + { + *move = stack->moves[--stack->num_moves]; + return 0; + } + else + { + return 1; + } +} + +#if 0 +void fcs_move_stack_destroy(fcs_move_stack_t * stack) +{ + free(stack->moves); + free(stack); +} +#endif + +void freecell_solver_move_stack_swallow_stack( + fcs_move_stack_t * stack, + fcs_move_stack_t * src_stack + ) +{ + fcs_move_t move; + while (!fcs_move_stack_pop(src_stack, &move)) + { + fcs_move_stack_push(stack, move); + } + fcs_move_stack_destroy(src_stack); +} + +#if 0 +void fcs_move_stack_reset( + fcs_move_stack_t * stack + ) +{ + stack->num_moves = 0; +} +#endif + +int freecell_solver_move_stack_get_num_moves( + fcs_move_stack_t * stack + ) +{ + return stack->num_moves; +} + +#if 0 +/* + This function duplicates a move stack +*/ +fcs_move_stack_t * fcs_move_stack_duplicate( + fcs_move_stack_t * stack + ) +{ + fcs_move_stack_t * ret; + + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); + + ret->max_num_moves = stack->max_num_moves; + ret->num_moves = stack->num_moves; + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves); + memcpy(ret->moves, stack->moves, sizeof(fcs_move_t) * ret->max_num_moves); + + return ret; +} +#endif + +#if 0 +extern void fcs_derived_states_list_add_state( + fcs_derived_states_list_t * list, + fcs_state_with_locations_t * state, + fcs_move_stack_t * move_stack + ) +{ + if (list->num_states == list->max_num_states) + { + list->max_num_states += 16; + list->states = realloc(list->states, sizeof(list->states[0]) * list->max_num_states); + list->move_stacks = realloc(list->move_stacks, sizeof(list->move_stacks[0]) * list->max_num_states); + } + list->states[list->num_states] = state; + list->move_stacks[list->num_states] = move_stack; + list->num_states++; +} +#endif +/* + This function performs a given move on a state + + */ +void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num) +{ + fcs_state_t * state; + fcs_card_t temp_card; + int a; + int src_stack, dest_stack; + int src_freecell, dest_freecell; + int src_stack_len; + + state = (&(state_with_locations->s)); + + dest_stack = fcs_move_get_dest_stack(move); + src_stack = fcs_move_get_src_stack(move); + dest_freecell = fcs_move_get_dest_freecell(move); + src_freecell = fcs_move_get_src_freecell(move); + + + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + { + src_stack_len = fcs_stack_len(*state, src_stack); + for(a=0 ; a= 7) + return (fcn+3); + else + return fcn; +} + +char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation) +{ + return + freecell_solver_move_to_string_w_state( + NULL, 4, 8, 1, + move, + (standard_notation == 2)?1:standard_notation + ); +} + +char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation) +{ + char string[256]; + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + if ((standard_notation == 2) && + /* More than one card was moved */ + (fcs_move_get_num_cards_in_seq(move) > 1) && + /* It was a move to an empty stack */ + (fcs_stack_len(state->s, fcs_move_get_dest_stack(move)) == + fcs_move_get_num_cards_in_seq(move)) + ) + { + sprintf(string, "%i%iv%x", + 1+fcs_move_get_src_stack(move), + 1+fcs_move_get_dest_stack(move), + fcs_move_get_num_cards_in_seq(move) + ); + } + else if (standard_notation) + { + sprintf(string, "%i%i", + 1+fcs_move_get_src_stack(move), + 1+fcs_move_get_dest_stack(move) + ); + } + else + { + sprintf(string, "Move %i cards from stack %i to stack %i", + fcs_move_get_num_cards_in_seq(move), + fcs_move_get_src_stack(move), + fcs_move_get_dest_stack(move) + ); + } + break; + + case FCS_MOVE_TYPE_FREECELL_TO_STACK: + if (standard_notation) + { + sprintf(string, "%c%i", + ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))), + 1+fcs_move_get_dest_stack(move) + ); + } + else + { + sprintf(string, "Move a card from freecell %i to stack %i", + fcs_move_get_src_freecell(move), + fcs_move_get_dest_stack(move) + ); + } + + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FREECELL: + if (standard_notation) + { + sprintf(string, "%c%c", + ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))), + ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move))) + ); + } + else + { + sprintf(string, "Move a card from freecell %i to freecell %i", + fcs_move_get_src_freecell(move), + fcs_move_get_dest_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_STACK_TO_FREECELL: + if (standard_notation) + { + sprintf(string, "%i%c", + 1+fcs_move_get_src_stack(move), + ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move))) + ); + } + else + { + sprintf(string, "Move a card from stack %i to freecell %i", + fcs_move_get_src_stack(move), + fcs_move_get_dest_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_STACK_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ih", 1+fcs_move_get_src_stack(move)); + } + else + { + sprintf(string, "Move a card from stack %i to the foundations", + fcs_move_get_src_stack(move) + ); + } + + break; + + + case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ch", ('a'+convert_freecell_num(fcs_move_get_src_freecell(move)))); + } + else + { + sprintf(string, + "Move a card from freecell %i to the foundations", + fcs_move_get_src_freecell(move) + ); + } + + break; + + case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION: + if (standard_notation) + { + sprintf(string, "%ih", fcs_move_get_src_stack(move)); + } + else + { + sprintf(string, + "Move the sequence on top of Stack %i to the foundations", + fcs_move_get_src_stack(move) + ); + } + break; + + default: + string[0] = '\0'; + break; + } + + return strdup(string); +} diff --git a/kpat/freecell-solver/move.h b/kpat/freecell-solver/move.h new file mode 100644 index 00000000..a7501788 --- /dev/null +++ b/kpat/freecell-solver/move.h @@ -0,0 +1,172 @@ +/* + * move.h - header file for the move and move stacks functions of + * Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__MOVE_H +#define FC_SOLVE__MOVE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This include is done to prevent a warning in case stdlib.h defines + * max. (which is the case for the Microsoft C Compiler) + * */ +#include + +#include "state.h" +#include "fcs_move.h" + + +#if 0 +fcs_move_stack_t * fcs_move_stack_create(void); +int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move); +#endif + +#define fcs_move_stack_pop(stack,move) (freecell_solver_move_stack_pop(stack,move)) +extern int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move); + +#if 0 +void fcs_move_stack_destroy(fcs_move_stack_t * stack); +#endif + +#define fcs_move_stack_destroy(stack) \ +{ \ + free((stack)->moves); \ + free(stack); \ +} + +extern void freecell_solver_move_stack_swallow_stack(fcs_move_stack_t * stack, fcs_move_stack_t * src_stack); +#if 0 +void fcs_move_stack_reset(fcs_move_stack_t * stack); +#endif +#define fcs_move_stack_reset(stack) \ +{ \ + (stack)->num_moves = 0; \ +} + + + +#define fcs_move_stack_get_num_moves(stack) (freecell_solver_move_stack_get_num_moves(stack)) +extern int freecell_solver_move_stack_get_num_moves(fcs_move_stack_t * stack); + +#if 0 +fcs_move_stack_t * fcs_move_stack_duplicate(fcs_move_stack_t * stack); +#endif +#define fcs_move_stack_duplicate_into_var(final_ret,stack) \ +{ \ + fcs_move_stack_t * ret; \ + fcs_move_stack_t * temp_stack=(stack) ; \ + \ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \ + \ + ret->max_num_moves = temp_stack->max_num_moves; \ + ret->num_moves = temp_stack->num_moves; \ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves); \ + memcpy(ret->moves, temp_stack->moves, sizeof(fcs_move_t) * ret->max_num_moves); \ + \ + (final_ret) = ret; \ +} + + + +void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num); + +void freecell_solver_move_stack_normalize( + fcs_move_stack_t * moves, + fcs_state_with_locations_t * init_state, + int freecells_num, + int stacks_num, + int decks_num + ); + +extern char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation); + +extern char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation); + +struct fcs_derived_states_list_struct +{ + int num_states; + int max_num_states; + fcs_state_with_locations_t * * states; +}; + +typedef struct fcs_derived_states_list_struct fcs_derived_states_list_t; + +#if 0 +extern void fcs_derived_states_list_add_state( + fcs_derived_states_list_t * list, + fcs_state_with_locations_t * state, + fcs_move_stack_t * move_stack + ); +#endif + +#ifndef max +#define max(a,b) (((a)>(b))?(a):(b)) +#endif + +#define FCS_MOVE_STACK_GROW_BY 16 + +/* This macro allocates an empty move stack */ +#define fcs_move_stack_alloc_into_var(final_ret) \ +{ \ + fcs_move_stack_t * ret; \ + \ + /* Allocate the data structure itself */ \ + ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \ + \ + ret->max_num_moves = FCS_MOVE_STACK_GROW_BY; \ + ret->num_moves = 0; \ + /* Allocate some space for the moves */ \ + ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves); \ + \ + (final_ret) = ret; \ +} + + +#define fcs_move_stack_push(stack, move) \ +{ \ + /* If all the moves inside the stack are taken then \ + resize the move vector */ \ + \ + if (stack->num_moves == stack->max_num_moves) \ + { \ + int a, b; \ + a = (stack->max_num_moves >> 3); \ + b = FCS_MOVE_STACK_GROW_BY; \ + stack->max_num_moves += max(a,b); \ + stack->moves = realloc( \ + stack->moves, \ + stack->max_num_moves * sizeof(fcs_move_t) \ + ); \ + } \ + stack->moves[stack->num_moves++] = move; \ + \ +} + +#define fcs_derived_states_list_add_state(list,state) \ + \ +{ \ + if ((list)->num_states == (list)->max_num_states) \ + { \ + (list)->max_num_states += 16; \ + (list)->states = realloc((list)->states, sizeof((list)->states[0]) * (list)->max_num_states); \ + } \ + (list)->states[(list)->num_states] = (state); \ + (list)->num_states++; \ +} + + + + +#ifdef __cplusplus +} +#endif + +#endif /* FC_SOLVE__MOVE_H */ diff --git a/kpat/freecell-solver/ms_ca.h b/kpat/freecell-solver/ms_ca.h new file mode 100644 index 00000000..5c1b44ec --- /dev/null +++ b/kpat/freecell-solver/ms_ca.h @@ -0,0 +1,33 @@ +/* + * ms_ca.h - A header file for a (possibly inline) function that compactly + * allocates a move stack. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + * */ + +#include "inline.h" + +static GCC_INLINE fcs_move_stack_t * freecell_solver_move_stack_compact_allocate(freecell_solver_hard_thread_t * hard_thread, fcs_move_stack_t * old_move_stack_to_parent) +{ + char * ptr; + fcs_move_stack_t * new_move_stack_to_parent; + fcs_move_t * new_moves_to_parent; + + fcs_compact_alloc_typed_ptr_into_var( + ptr, + char, + hard_thread->move_stacks_allocator, + (sizeof(fcs_move_stack_t) + sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves) + ); + new_move_stack_to_parent = (fcs_move_stack_t *)ptr; + new_moves_to_parent = (fcs_move_t *)(ptr+sizeof(fcs_move_stack_t)); + new_move_stack_to_parent->moves = new_moves_to_parent; + new_move_stack_to_parent->num_moves = + new_move_stack_to_parent->max_num_moves = + old_move_stack_to_parent->num_moves; + memcpy(new_moves_to_parent, old_move_stack_to_parent->moves, sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves); + return new_move_stack_to_parent; +} + diff --git a/kpat/freecell-solver/pqueue.c b/kpat/freecell-solver/pqueue.c new file mode 100644 index 00000000..086cce96 --- /dev/null +++ b/kpat/freecell-solver/pqueue.c @@ -0,0 +1,173 @@ +/* + pqueue.c - implementation of a priority queue by using a binary heap. + + Originally written by Justin-Heyes Jones + Modified by Shlomi Fish, 2000 + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html + */ + +/* manage a priority queue as a heap + the heap is implemented as a fixed size array of pointers to your data */ + +#include +#include + +#include "jhjtypes.h" + +#include "pqueue.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define TRUE 1 +#define FALSE 0 + +/* initialise the priority queue with a maximum size of maxelements. maxrating is the highest or lowest value of an + entry in the pqueue depending on whether it is ascending or descending respectively. Finally the bool32 tells you whether + the list is sorted ascending or descending... */ + +void freecell_solver_PQueueInitialise( + PQUEUE *pq, + int32 MaxElements + ) +{ + pq->MaxSize = MaxElements; + + pq->CurrentSize = 0; + + pq->Elements = (pq_element_t*) malloc( sizeof( pq_element_t ) * (MaxElements + 1) ); + + if( pq->Elements == NULL ) + { + printf( "Memory alloc failed!\n" ); + } +} + +/* join a priority queue + returns TRUE if successful, FALSE if fails. (You fail by filling the pqueue.) + PGetRating is a function which returns the rating of the item you're adding for sorting purposes */ + +int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t r) +{ + uint32 i; + pq_element_t * Elements = pq->Elements; + + int32 CurrentSize = pq->CurrentSize; + + if (CurrentSize == pq->MaxSize ) + { + int new_size; + new_size = pq->MaxSize + 256; + pq->Elements = Elements = (pq_element_t *)realloc( Elements, sizeof(pq_element_t) * (new_size+1)); + pq->MaxSize = new_size; + } + + { + /* set i to the first unused element and increment CurrentSize */ + + i = (++CurrentSize); + + /* while the parent of the space we're putting the new node into is worse than + our new node, swap the space with the worse node. We keep doing that until we + get to a worse node or until we get to the top + + note that we also can sort so that the minimum elements bubble up so we need to loops + with the comparison operator flipped... */ + + { + + while( ( i==PQ_FIRST_ENTRY ? + (PQUEUE_MaxRating) /* return biggest possible rating if first element */ + : + (PGetRating(Elements[ PQ_PARENT_INDEX(i) ]) ) + ) + < r + ) + { + Elements[ i ] = Elements[ PQ_PARENT_INDEX(i) ]; + + i = PQ_PARENT_INDEX(i); + } + } + + /* then add the element at the space we created. */ + Elements[i].item = item; + Elements[i].rating = r; + } + + pq->CurrentSize = CurrentSize; + + return TRUE; + +} + +#define PQueueIsEmpty(pq) ((pq)->CurrentSize == 0) + +/* free up memory for pqueue */ +void freecell_solver_PQueueFree( PQUEUE *pq ) +{ + free( pq->Elements ); +} + +/* remove the first node from the pqueue and provide a pointer to it */ + +void *freecell_solver_PQueuePop( PQUEUE *pq) +{ + int32 i; + int32 child; + pq_element_t * Elements = pq->Elements; + int32 CurrentSize = pq->CurrentSize; + + pq_element_t pMaxElement; + pq_element_t pLastElement; + + if( PQueueIsEmpty( pq ) ) + { + return NULL; + } + + pMaxElement = Elements[PQ_FIRST_ENTRY]; + + /* get pointer to last element in tree */ + pLastElement = Elements[ CurrentSize-- ]; + + { + + /* code to pop an element from an ascending (top to bottom) pqueue */ + + /* UNTESTED */ + + for( i=PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX(i)) <= CurrentSize; i=child ) + { + /* set child to the smaller of the two children... */ + + if( (child != CurrentSize) && + (PGetRating(Elements[child + 1]) > PGetRating(Elements[child])) ) + { + child ++; + } + + if( PGetRating( pLastElement ) < PGetRating( Elements[ child ] ) ) + { + Elements[ i ] = Elements[ child ]; + } + else + { + break; + } + } + } + + Elements[i] = pLastElement; + pq->CurrentSize = CurrentSize; + + return pMaxElement.item; +} + + diff --git a/kpat/freecell-solver/pqueue.h b/kpat/freecell-solver/pqueue.h new file mode 100644 index 00000000..cf5f5372 --- /dev/null +++ b/kpat/freecell-solver/pqueue.h @@ -0,0 +1,71 @@ +/* + pqueue.h - header file for the priority queue implementation. + + Originally written by Justin-Heyes Jones + Modified by Shlomi Fish, 2000 + + This file is in the public domain (it's uncopyrighted). + + Check out Justin-Heyes Jones' A* page from which this code has + originated: + http://www.geocities.com/jheyesjones/astar.html +*/ + +#ifndef FC_SOLVE__PQUEUE_H +#define FC_SOLVE__PQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "jhjtypes.h" + +#define PQUEUE_MaxRating INT_MAX + +typedef int32 pq_rating_t; + +typedef struct struct_pq_element_t +{ + void * item; + pq_rating_t rating; +} pq_element_t; + +typedef struct _PQUEUE +{ + int32 MaxSize; + int32 CurrentSize; + pq_element_t * Elements; /* pointer to void pointers */ + pq_rating_t MaxRating; /* biggest element possible */ +} PQUEUE; + +/* given an index to any element in a binary tree stored in a linear array with the root at 1 and + a "sentinel" value at 0 these macros are useful in making the code clearer */ + +/* the parent is always given by index/2 */ +#define PQ_PARENT_INDEX(i) ((i)>>1) +#define PQ_FIRST_ENTRY (1) + +/* left and right children are index * 2 and (index * 2) +1 respectively */ +#define PQ_LEFT_CHILD_INDEX(i) ((i)<<1) +#define PQ_RIGHT_CHILD_INDEX(i) (((i)<<1)+1) + +void freecell_solver_PQueueInitialise( + PQUEUE *pq, + int32 MaxElements + ); + +void freecell_solver_PQueueFree( PQUEUE *pq ); + +int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t); + +void *freecell_solver_PQueuePop( PQUEUE *pq); + +#define PGetRating(elem) ((elem).rating) + +#ifdef __cplusplus +} +#endif + +#endif /* #ifdef FC_SOLVE__PQUEUE_H */ diff --git a/kpat/freecell-solver/prefix.h b/kpat/freecell-solver/prefix.h new file mode 100644 index 00000000..b3b5094e --- /dev/null +++ b/kpat/freecell-solver/prefix.h @@ -0,0 +1,4 @@ +#define FREECELL_SOLVER_PREFIX "/usr/local" + +#define FREECELL_SOLVER_PKG_DATA_DIR "/usr/local/share/freecell-solver/" + diff --git a/kpat/freecell-solver/preset.c b/kpat/freecell-solver/preset.c new file mode 100644 index 00000000..16a02f1d --- /dev/null +++ b/kpat/freecell-solver/preset.c @@ -0,0 +1,637 @@ +/* + * preset.c - game presets management for Freecell Solver + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + * + */ + + +#include +#include + +#include "fcs.h" +#include "preset.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +enum fcs_presets_ids +{ + FCS_PRESET_BAKERS_DOZEN, + FCS_PRESET_BAKERS_GAME, + FCS_PRESET_CRUEL, + FCS_PRESET_DER_KATZENSCHWANZ, + FCS_PRESET_DIE_SCHLANGE, + FCS_PRESET_EIGHT_OFF, + FCS_PRESET_FAN, + FCS_PRESET_FORECELL, + FCS_PRESET_FREECELL, + FCS_PRESET_GOOD_MEASURE, + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + FCS_PRESET_RELAXED_FREECELL, + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + FCS_PRESET_SEAHAVEN_TOWERS, + FCS_PRESET_SIMPLE_SIMON, + FCS_PRESET_YUKON, + FCS_PRESET_BELEAGUERED_CASTLE +}; + +static const fcs_preset_t fcs_presets[16] = +{ + { + FCS_PRESET_BAKERS_DOZEN, + 0, + 13, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_BAKERS_GAME, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_BELEAGUERED_CASTLE, + 0, + 8, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_CRUEL, + 0, + 12, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_DER_KATZENSCHWANZ, + 8, + 9, + 2, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 1, + FCS_ES_FILLED_BY_NONE, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_DIE_SCHLANGE, + 8, + 9, + 2, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_NONE, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_EIGHT_OFF, + 8, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FAN, + 0, + 18, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FORECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_FREECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_GOOD_MEASURE, + 0, + 10, + 1, + + FCS_SEQ_BUILT_BY_RANK, + 0, + FCS_ES_FILLED_BY_NONE, + + "0123456789", + "0123456789", + }, + { + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_RELAXED_FREECELL, + 4, + 8, + 1, + + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + 1, + FCS_ES_FILLED_BY_ANY_CARD, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + 4, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 1, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_SEAHAVEN_TOWERS, + 4, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_KINGS_ONLY, + + "[01][23456789]", + "0123456789", + }, + { + FCS_PRESET_SIMPLE_SIMON, + 0, + 10, + 1, + + FCS_SEQ_BUILT_BY_SUIT, + 0, + FCS_ES_FILLED_BY_ANY_CARD, + + "abcdefgh", + "abcdefgh", + }, +}; + +struct fcs_preset_name_struct +{ + const char name[32]; + int preset_id; +}; + +typedef struct fcs_preset_name_struct fcs_preset_name_t; + +static const fcs_preset_name_t fcs_preset_names[23] = +{ + { + "bakers_dozen", + FCS_PRESET_BAKERS_DOZEN, + }, + { + "bakers_game", + FCS_PRESET_BAKERS_GAME, + }, + { + "beleaguered_castle", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "citadel", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "cruel", + FCS_PRESET_CRUEL, + }, + { + "der_katzenschwanz", + FCS_PRESET_DER_KATZENSCHWANZ, + }, + { + "der_katz", + FCS_PRESET_DER_KATZENSCHWANZ, + }, + { + "die_schlange", + FCS_PRESET_DIE_SCHLANGE, + }, + { + "eight_off", + FCS_PRESET_EIGHT_OFF, + }, + { + "fan", + FCS_PRESET_FAN, + }, + { + "forecell", + FCS_PRESET_FORECELL, + }, + { + "freecell", + FCS_PRESET_FREECELL, + }, + { + "good_measure", + FCS_PRESET_GOOD_MEASURE, + }, + { + "ko_bakers_game", + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + }, + { + "kings_only_bakers_game", + FCS_PRESET_KINGS_ONLY_BAKERS_GAME, + }, + { + "relaxed_freecell", + FCS_PRESET_RELAXED_FREECELL, + }, + { + "relaxed_seahaven_towers", + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + }, + { + "relaxed_seahaven", + FCS_PRESET_RELAXED_SEAHAVEN_TOWERS, + }, + { + "seahaven_towers", + FCS_PRESET_SEAHAVEN_TOWERS, + }, + { + "seahaven", + FCS_PRESET_SEAHAVEN_TOWERS, + }, + { + "simple_simon", + FCS_PRESET_SIMPLE_SIMON, + }, + { + "streets_and_alleys", + FCS_PRESET_BELEAGUERED_CASTLE, + }, + { + "yukon", + FCS_PRESET_YUKON, + }, +}; + +static int fcs_get_preset_id_by_name( + const char * name +) +{ + int a; + int ret = -1; + int num_elems; + + num_elems = ( (int) (sizeof(fcs_preset_names)/sizeof(fcs_preset_names[0]))); + for(a=0;a= '0') && (c <= '9')) + { + return c-'0'; + } + else if ((c >= 'a') && (c <= 'h')) + { + return c-'a'+10; + } + else if ((c >= 'A') && (c <= 'Z')) + { + return c-'A'+18; + } + else + { + return 0; + } +} + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +struct internal_tests_order_struct +{ + int tests_order_num; + int tests_order[FCS_TESTS_NUM]; +}; + +typedef struct internal_tests_order_struct internal_tests_order_t; + +int freecell_solver_apply_tests_order( + fcs_tests_order_t * tests_order, + const char * string, + char * * error_string + ) + +{ + int a; + int len; + int test_index; + int is_group, is_start_group; + if (tests_order->tests) + { + free(tests_order->tests); + tests_order->max_num = 10; + tests_order->num = 0; + tests_order->tests = malloc(sizeof(tests_order->tests[0])*tests_order->max_num ); + } + +#if 0 + instance->tests_order_num = min(strlen(string), FCS_TESTS_NUM); +#endif + len = strlen(string); + test_index = 0; + is_group = 0; + is_start_group = 0; + for(a=0;(amax_num) + { + tests_order->max_num += 10; + tests_order->tests = realloc(tests_order->tests, sizeof(tests_order->tests[0]) * tests_order->max_num); + } + tests_order->tests[test_index] = (freecell_solver_char_to_test_num(string[a])%FCS_TESTS_NUM) | (is_group ? FCS_TEST_ORDER_FLAG_RANDOM : 0) | (is_start_group ? FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP : 0); + + test_index++; + is_start_group = 0; + } + if (a != len) + { + *error_string = strdup("The Input string is too long."); + return 4; + } + + tests_order->num = test_index; + *error_string = NULL; + + return 0; +} + +int freecell_solver_apply_preset_by_ptr( + freecell_solver_instance_t * instance, + const fcs_preset_t * preset_ptr + ) +{ + char * no_use; + +#define preset (*preset_ptr) + if (preset.freecells_num > MAX_NUM_FREECELLS) + { + return FCS_PRESET_CODE_FREECELLS_EXCEED_MAX; + } + if (preset.stacks_num > MAX_NUM_STACKS) + { + return FCS_PRESET_CODE_STACKS_EXCEED_MAX; + } + if (preset.decks_num > MAX_NUM_DECKS) + { + return FCS_PRESET_CODE_DECKS_EXCEED_MAX; + } + instance->freecells_num = preset.freecells_num; + instance->stacks_num = preset.stacks_num; + instance->decks_num = preset.decks_num; + + instance->sequences_are_built_by = preset.sequences_are_built_by; + instance->unlimited_sequence_move = preset.unlimited_sequence_move; + instance->empty_stacks_fill = preset.empty_stacks_fill; + + /* + * This code makes sure that all the tests in all the existing + * soft threads are acceptable by the new preset. + * */ + + { + int ht_idx, st_idx; + for(ht_idx = 0; ht_idx < instance->num_hard_threads ; ht_idx++) + { + for(st_idx = 0; st_idx < instance->hard_threads[ht_idx]->num_soft_threads; st_idx++) + { + freecell_solver_soft_thread_t * soft_thread = instance->hard_threads[ht_idx]->soft_threads[st_idx]; + + int num_valid_tests; + const char * s; + + /* Check every test */ + + for(num_valid_tests=0;num_valid_tests < soft_thread->tests_order.num; num_valid_tests++) + { + for(s = preset.allowed_tests;*s != '\0';s++) + { + /* Check if this test corresponds to this character */ + if ((soft_thread->tests_order.tests[num_valid_tests] & FCS_TEST_ORDER_NO_FLAGS_MASK) == ((freecell_solver_char_to_test_num(*s)%FCS_TESTS_NUM))) + { + break; + } + } + /* If the end of the string was reached, it means + * this test is unacceptable by this preset. */ + if (*s == '\0') + { + break; + } + } + if (num_valid_tests < soft_thread->tests_order.num) + { + freecell_solver_apply_tests_order( + &(soft_thread->tests_order), + preset.tests_order, + &no_use); + } + } + } + } + + /* Assign the master tests order */ + + { + freecell_solver_apply_tests_order( + &(instance->instance_tests_order), + preset.tests_order, + &no_use); + } +#undef preset + return FCS_PRESET_CODE_OK; +} + +static int fcs_get_preset_by_id( + int preset_id, + const fcs_preset_t * * preset_ptr + ) +{ + int preset_index; + int num_elems; + + num_elems = ( (int) (sizeof(fcs_presets)/sizeof(fcs_presets[0]))); + + for(preset_index=0 ; preset_index < num_elems ; preset_index++) + { + if (fcs_presets[preset_index].preset_id == preset_id) + { + *preset_ptr = &(fcs_presets[preset_index]); + return FCS_PRESET_CODE_OK; + } + } + + return FCS_PRESET_CODE_NOT_FOUND; +} + +int freecell_solver_get_preset_by_name( + const char * name, + const fcs_preset_t * * preset_ptr + ) +{ + int preset_id; + + preset_id = fcs_get_preset_id_by_name(name); + if (preset_id >= 0) + { + return fcs_get_preset_by_id( + preset_id, + preset_ptr + ); + } + else + { + return FCS_PRESET_CODE_NOT_FOUND; + } +} + +int freecell_solver_apply_preset_by_name( + freecell_solver_instance_t * instance, + const char * name + ) +{ + int ret; + const fcs_preset_t * preset_ptr; + + ret = freecell_solver_get_preset_by_name( + name, + &preset_ptr + ); + + if (ret != FCS_PRESET_CODE_OK) + { + return ret; + } + + return freecell_solver_apply_preset_by_ptr(instance, preset_ptr); +} diff --git a/kpat/freecell-solver/preset.h b/kpat/freecell-solver/preset.h new file mode 100644 index 00000000..553e9d07 --- /dev/null +++ b/kpat/freecell-solver/preset.h @@ -0,0 +1,62 @@ +/* + * fcs.h - header file of the preset management functions for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__PRESET_H +#define FC_SOLVE__PRESET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fcs.h" + +struct fcs_preset_struct +{ + int preset_id; + int freecells_num; + int stacks_num; + int decks_num; + + int sequences_are_built_by; + int unlimited_sequence_move; + int empty_stacks_fill; + + char tests_order[FCS_TESTS_NUM*3+1]; + char allowed_tests[FCS_TESTS_NUM*3+1]; +}; + +typedef struct fcs_preset_struct fcs_preset_t; + +extern int freecell_solver_apply_preset_by_ptr( + freecell_solver_instance_t * instance, + const fcs_preset_t * preset_ptr + ); + +extern int freecell_solver_apply_preset_by_name( + freecell_solver_instance_t * instance, + const char * name + ); + +extern int freecell_solver_apply_tests_order( + fcs_tests_order_t * tests_order, + const char * string, + char * * error_string + ); + +extern int freecell_solver_get_preset_by_name( + const char * name, + const fcs_preset_t * * preset_ptr + ); + +#define fcs_duplicate_preset(d,s) ((d) = (s)) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/rand.c b/kpat/freecell-solver/rand.c new file mode 100644 index 00000000..d5151e8e --- /dev/null +++ b/kpat/freecell-solver/rand.c @@ -0,0 +1,30 @@ +#include + +#ifdef DMALLOC +#include +#endif + +#include "rand.h" + +fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed) +{ + fcs_rand_t * ret; + + ret = malloc(sizeof(fcs_rand_t)); + ret->seed = (long)seed; + + return ret; +} + +void freecell_solver_rand_free(fcs_rand_t * rand) +{ + free(rand); +} + + +void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed) +{ + rand->seed = seed; +} + + diff --git a/kpat/freecell-solver/rand.h b/kpat/freecell-solver/rand.h new file mode 100644 index 00000000..0cecfafd --- /dev/null +++ b/kpat/freecell-solver/rand.h @@ -0,0 +1,49 @@ + +#ifndef FC_SOLVE__RAND_H +#define FC_SOLVE__RAND_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "inline.h" + +struct fcs_rand_struct +{ + unsigned long seed; +}; + +typedef struct fcs_rand_struct fcs_rand_t; + +extern fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed); +extern void freecell_solver_rand_free(fcs_rand_t * rand); + +extern void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed); + +static GCC_INLINE int freecell_solver_rand_rand15(fcs_rand_t * rand) +{ + rand->seed = (rand->seed * 214013 + 2531011); + return (rand->seed >> 16) & 0x7fff; +} + +/* + * + * This function constructs a larger integral number of out of two + * 15-bit ones. + * + * */ +static GCC_INLINE int freecell_solver_rand_get_random_number(fcs_rand_t * rand) +{ + int one, two; + one = freecell_solver_rand_rand15(rand); + two = freecell_solver_rand_rand15(rand); + + return (one | (two << 15)); +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/scans.c b/kpat/freecell-solver/scans.c new file mode 100644 index 00000000..5c579739 --- /dev/null +++ b/kpat/freecell-solver/scans.c @@ -0,0 +1,1170 @@ +/* + * scans.c - The code that relates to the various scans. + * Currently Hard DFS, Soft-DFS, Random-DFS, A* and BFS are implemented. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include +#include +#include +#include +#include + +#include "fcs_config.h" + +/* So FCS_STATE_STORAGE and friends would be defined */ +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#endif + +#include "state.h" +#include "card.h" +#include "fcs_dm.h" +#include "fcs.h" + +#include "fcs_isa.h" + +#include "test_arr.h" +#include "caas.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static pq_rating_t freecell_solver_a_star_rate_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations); + +#define freecell_solver_a_star_enqueue_state(soft_thread,ptr_state_with_locations) \ + { \ + freecell_solver_PQueuePush( \ + a_star_pqueue, \ + ptr_state_with_locations, \ + freecell_solver_a_star_rate_state(soft_thread, ptr_state_with_locations) \ + ); \ + } + + +#define freecell_solver_bfs_enqueue_state(soft_thread, state) \ + { \ + fcs_states_linked_list_item_t * last_item_next; \ + last_item_next = bfs_queue_last_item->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); \ + bfs_queue_last_item->s = state; \ + last_item_next->next = NULL; \ + bfs_queue_last_item = last_item_next; \ + } + +#define the_state (ptr_state_with_locations->s) + +int freecell_solver_hard_dfs_solve_for_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int depth, + int ignore_osins + ) + +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int a; + int check; + + int num_freestacks, num_freecells; + + int iter_num = instance->num_times; + + fcs_derived_states_list_t derived; + + int derived_state_index; + + int ret_value; + + int freecells_num, stacks_num; + + int calc_real_depth, scans_synergy; + + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + + derived.num_states = derived.max_num_states = 0; + derived.states = NULL; + + calc_real_depth = instance->calc_real_depth; + scans_synergy = instance->scans_synergy; + + /* + * If this state has not been visited before - increase the number of + * iterations this program has seen, and output this state again. + * + * I'm doing this in order to make the output of a stopped and + * resumed run consistent with the output of a normal (all-in-one-time) + * run. + * */ + if (!is_scan_visited(ptr_state_with_locations, soft_thread->id)) + { + if (instance->debug_iter_output) + { + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + iter_num, + depth, + (void*)instance, + ptr_state_with_locations, + 0 /* It's a temporary kludge */ + ); + } + /* Increase the number of iterations */ + instance->num_times++; + hard_thread->num_times++; + ptr_state_with_locations->visited_iter = iter_num; + } + + /* Mark this state as visited, so it won't be recursed into again. */ + set_scan_visited(ptr_state_with_locations, soft_thread->id); + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;afinal_state = ptr_state_with_locations; + + ret_value = FCS_STATE_WAS_SOLVED; + goto free_derived; + } + + calculate_real_depth(ptr_state_with_locations); + + for(a=0 ; + a < soft_thread->tests_order.num; + a++) + { + derived.num_states = 0; + + check = + freecell_solver_sfs_tests[soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK ] ( + soft_thread, + ptr_state_with_locations, + num_freestacks, + num_freecells, + &derived, + 0 + ); + + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + if (check == FCS_STATE_BEGIN_SUSPEND_PROCESS) + { + soft_thread->num_solution_states = depth+1; + + soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states); + } + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + for(derived_state_index=0;derived_state_indexvisited & + FCS_VISITED_DEAD_END) + ) && + (! is_scan_visited( + derived.states[derived_state_index], + soft_thread->id) + ) + ) + { + check = + freecell_solver_hard_dfs_solve_for_state( + soft_thread, + derived.states[derived_state_index], + depth+1, + ignore_osins + ); + + if ((check == FCS_STATE_SUSPEND_PROCESS) || + (check == FCS_STATE_BEGIN_SUSPEND_PROCESS)) + { + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + if (check == FCS_STATE_WAS_SOLVED) + { + ret_value = FCS_STATE_WAS_SOLVED; + + goto free_derived; + } + } + } + } + + if (check_if_limits_exceeded()) + { + soft_thread->num_solution_states = depth+1; + + soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states); + + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + + ret_value = FCS_STATE_SUSPEND_PROCESS; + + goto free_derived; + } + + ret_value = FCS_STATE_IS_NOT_SOLVEABLE; + + if (soft_thread->is_a_complete_scan) + { + mark_as_dead_end(ptr_state_with_locations); + } + + +free_derived: + if (derived.states != NULL) + { + free(derived.states); + } + + return ret_value; +} + + +int freecell_solver_hard_dfs_resume_solution( + freecell_solver_soft_thread_t * soft_thread, + int depth + ) +{ + fcs_state_with_locations_t * ptr_state_with_locations; + int check; + + ptr_state_with_locations = soft_thread->soft_dfs_info[depth].state; + + if (depth < soft_thread->num_solution_states-1) + { + check = freecell_solver_hard_dfs_resume_solution( + soft_thread, + depth+1 + ); + } + else + { + free(soft_thread->soft_dfs_info); + soft_thread->soft_dfs_info = NULL; + check = FCS_STATE_IS_NOT_SOLVEABLE; + } + + if (check == FCS_STATE_IS_NOT_SOLVEABLE) + { + check = freecell_solver_hard_dfs_solve_for_state( + soft_thread, + ptr_state_with_locations, + depth, + 1); + } + else if (check == FCS_STATE_WAS_SOLVED) + { + /* Do nothing - fall back to return check. */ + } + else + { + if ((check == FCS_STATE_SUSPEND_PROCESS) || (check == FCS_STATE_WAS_SOLVED)) + { + + soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations; + } + } + + return check; +} + +#undef state + + + + + +static void freecell_solver_increase_dfs_max_depth( + freecell_solver_soft_thread_t * soft_thread + ) +{ + int new_dfs_max_depth = soft_thread->dfs_max_depth + 16; + int d; + +#define MYREALLOC(what) \ + soft_thread->what = realloc( \ + soft_thread->what, \ + sizeof(soft_thread->what[0])*new_dfs_max_depth \ + ); \ + + MYREALLOC(soft_dfs_info); +#undef MYREALLOC + + for(d=soft_thread->dfs_max_depth ; dsoft_dfs_info[d].state = NULL; + soft_thread->soft_dfs_info[d].derived_states_list.max_num_states = 0; + soft_thread->soft_dfs_info[d].test_index = 0; + soft_thread->soft_dfs_info[d].current_state_index = 0; + soft_thread->soft_dfs_info[d].derived_states_list.num_states = 0; + soft_thread->soft_dfs_info[d].derived_states_list.states = NULL; + soft_thread->soft_dfs_info[d].derived_states_random_indexes = NULL; + soft_thread->soft_dfs_info[d].derived_states_random_indexes_max_size = 0; + } + + soft_thread->dfs_max_depth = new_dfs_max_depth; +} + +/* + freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume is the event loop of the + Random-DFS scan. DFS which is recursive in nature is handled here + without procedural recursion + by using some dedicated stacks for the traversal. + */ +#define the_state (ptr_state_with_locations->s) + +#define myreturn(ret_value) \ + soft_thread->num_solution_states = depth+1; \ + return (ret_value); + +int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume, + int to_randomize + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int depth; + fcs_state_with_locations_t * ptr_state_with_locations, + * ptr_recurse_into_state_with_locations; + int a; + int check; + int do_first_iteration; + fcs_soft_dfs_stack_item_t * the_soft_dfs_info; + int freecells_num, stacks_num; + int dfs_max_depth; + + int tests_order_num = soft_thread->tests_order.num; + int * tests_order_tests = soft_thread->tests_order.tests; + int calc_real_depth = instance->calc_real_depth; + int is_a_complete_scan = soft_thread->is_a_complete_scan; + int soft_thread_id = soft_thread->id; + int test_index, current_state_index; + fcs_derived_states_list_t * derived_states_list; + int to_reparent_states, scans_synergy; + + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + to_reparent_states = instance->to_reparent_states; + scans_synergy = instance->scans_synergy; + + if (!resume) + { + /* + Allocate some space for the states at depth 0. + */ + depth=0; + + freecell_solver_increase_dfs_max_depth(soft_thread); + + /* Initialize the initial state to indicate it is the first */ + ptr_state_with_locations_orig->parent = NULL; + ptr_state_with_locations_orig->moves_to_parent = NULL; + ptr_state_with_locations_orig->depth = 0; + + soft_thread->soft_dfs_info[0].state = ptr_state_with_locations_orig; + } + else + { + /* + Set the initial depth to that of the last state encountered. + */ + depth = soft_thread->num_solution_states - 1; + } + + the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]); + + + dfs_max_depth = soft_thread->dfs_max_depth; + test_index = the_soft_dfs_info->test_index; + current_state_index = the_soft_dfs_info->current_state_index; + ptr_state_with_locations = the_soft_dfs_info->state; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + + calculate_real_depth(ptr_state_with_locations); + + /* + The main loop. + */ + while (depth >= 0) + { + /* + Increase the "maximal" depth if it about to be exceeded. + */ + if (depth+1 >= dfs_max_depth) + { + freecell_solver_increase_dfs_max_depth(soft_thread); + + /* Because the address of soft_thread->soft_dfs_info may + * be changed + * */ + the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]); + dfs_max_depth = soft_thread->dfs_max_depth; + /* This too has to be re-synced */ + derived_states_list = &(the_soft_dfs_info->derived_states_list); + } + + /* All the resultant states in the last test conducted were covered */ + if (current_state_index == derived_states_list->num_states) + { + if (test_index >= tests_order_num) + { + /* Backtrack to the previous depth. */ + + if (is_a_complete_scan) + { + ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE; + mark_as_dead_end(ptr_state_with_locations); + } + + depth--; + + if (check_if_limits_exceeded()) + { + the_soft_dfs_info->test_index = test_index; + the_soft_dfs_info->current_state_index = current_state_index; + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + the_soft_dfs_info--; + /* + * depth (and evidently the_soft_dfs_info) might be invalid + * now, so we should check before we assign. + * */ + if (depth >= 0) + { + test_index = the_soft_dfs_info->test_index; + current_state_index = the_soft_dfs_info->current_state_index; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + ptr_state_with_locations = the_soft_dfs_info->state; + } + continue; /* Just to make sure depth is not -1 now */ + } + + derived_states_list->num_states = 0; + + /* If this is the first test, then count the number of unoccupied + freeceels and stacks and check if we are done. */ + if (test_index == 0) + { + int num_freestacks, num_freecells; + + if (instance->debug_iter_output) + { +#ifdef DEBUG + printf("ST Name: %s\n", soft_thread->name); +#endif + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + instance->num_times, + depth, + (void*)instance, + ptr_state_with_locations, + ((depth == 0) ? + 0 : + soft_thread->soft_dfs_info[depth-1].state->visited_iter + ) + ); + } + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;afinal_state = ptr_state_with_locations; + + myreturn(FCS_STATE_WAS_SOLVED); + } + /* + Cache num_freecells and num_freestacks in their + appropriate stacks, so they won't be calculated over and over + again. + */ + the_soft_dfs_info->num_freecells = num_freecells; + the_soft_dfs_info->num_freestacks = num_freestacks; + } + + /* Always do the first test */ + do_first_iteration = 1; + + while ( + /* Make sure we do not exceed the number of tests */ + (test_index < tests_order_num) && + ( + /* Always do the first test */ + do_first_iteration || + ( + /* This is a randomized scan. Else - quit after the first iteration */ + to_randomize && + /* We are still on a random group */ + (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_RANDOM) && + /* A new random group did not start */ + (! (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP)) + ) + ) + ) + { + do_first_iteration = 0; + + check = freecell_solver_sfs_tests[tests_order_tests[ + test_index + ] & FCS_TEST_ORDER_NO_FLAGS_MASK] ( + soft_thread, + ptr_state_with_locations, + the_soft_dfs_info->num_freestacks, + the_soft_dfs_info->num_freecells, + derived_states_list, + to_reparent_states + ); + + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + /* Have this test be re-performed */ + derived_states_list->num_states = 0; + the_soft_dfs_info->current_state_index = 0; + the_soft_dfs_info->test_index = test_index; + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + /* Move the counter to the next test */ + test_index++; + } + + + { + int a, j; + int swap_save; + int * rand_array, * ra_ptr; + int num_states = derived_states_list->num_states; + + if (num_states > + the_soft_dfs_info->derived_states_random_indexes_max_size) + { + the_soft_dfs_info->derived_states_random_indexes_max_size = + num_states; + the_soft_dfs_info->derived_states_random_indexes = + realloc( + the_soft_dfs_info->derived_states_random_indexes, + sizeof(the_soft_dfs_info->derived_states_random_indexes[0]) * the_soft_dfs_info->derived_states_random_indexes_max_size + ); + } + rand_array = the_soft_dfs_info->derived_states_random_indexes; + + for(a=0, ra_ptr = rand_array; a < num_states ; a++) + { + *(ra_ptr++) = a; + } + /* If we just conducted the tests for a random group - + * randomize. Else - keep those indexes as the unity vector. + * + * Also, do not randomize if this is a pure soft-DFS scan. + * */ + if (to_randomize && tests_order_tests[ test_index-1 ] & FCS_TEST_ORDER_FLAG_RANDOM) + { + a = num_states-1; + while (a > 0) + { + j = + ( + freecell_solver_rand_get_random_number( + soft_thread->rand_gen + ) + % (a+1) + ); + + swap_save = rand_array[a]; + rand_array[a] = rand_array[j]; + rand_array[j] = swap_save; + a--; + } + } + } + + /* We just performed a test, so the index of the first state that + ought to be checked in this depth is 0. + */ + current_state_index = 0; + } + + { + int num_states = derived_states_list->num_states; + fcs_state_with_locations_t * * derived_states = derived_states_list->states; + int * rand_array = the_soft_dfs_info->derived_states_random_indexes; + + while (current_state_index < + num_states) + { + ptr_recurse_into_state_with_locations = + (derived_states[ + rand_array[ + current_state_index + ] + ]); + + current_state_index++; + if ( + (! (ptr_recurse_into_state_with_locations->visited & + FCS_VISITED_DEAD_END) + ) && + (! is_scan_visited( + ptr_recurse_into_state_with_locations, + soft_thread_id) + ) + ) + { + instance->num_times++; + hard_thread->num_times++; + + the_soft_dfs_info->test_index = test_index; + the_soft_dfs_info->current_state_index = current_state_index; + + set_scan_visited(ptr_recurse_into_state_with_locations, soft_thread_id); + + ptr_recurse_into_state_with_locations->visited_iter = instance->num_times; +#if 0 + ptr_recurse_into_state_with_locations->parent = ptr_state_with_locations; +#endif + + /* + I'm using current_state_indexes[depth]-1 because we already + increased it by one, so now it refers to the next state. + */ + depth++; + the_soft_dfs_info++; + the_soft_dfs_info->state = + ptr_state_with_locations = + ptr_recurse_into_state_with_locations; + test_index = 0; + current_state_index = 0; + derived_states_list = &(the_soft_dfs_info->derived_states_list); + derived_states_list->num_states = 0; + + calculate_real_depth(ptr_recurse_into_state_with_locations); + + break; + } + } + } + } + + soft_thread->num_solution_states = 0; + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +#undef state +#undef myreturn + +#define FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT 1.3 +#define FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT 1.3 + +#define state (ptr_state_with_locations->s) + +void freecell_solver_a_star_initialize_rater( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + int a, c, cards_num; + fcs_card_t this_card, prev_card; + double cards_under_sequences; + int sequences_are_built_by = instance->sequences_are_built_by; + + + cards_under_sequences = 0; + for(a=0;astacks_num;a++) + { + cards_num = fcs_stack_len(state, a); + if (cards_num <= 1) + { + continue; + } + + c = cards_num-2; + this_card = fcs_stack_card(state, a, c+1); + prev_card = fcs_stack_card(state, a, c); + while (fcs_is_parent_card(this_card,prev_card) && (c >= 0)) + { + c--; + this_card = prev_card; + if (c>=0) + { + prev_card = fcs_stack_card(state, a, c); + } + } + cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT); + } + soft_thread->a_star_initial_cards_under_sequences = cards_under_sequences; +} + + +static pq_rating_t freecell_solver_a_star_rate_state( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + double ret=0; + int a, c, cards_num, num_cards_in_founds; + int num_freestacks, num_freecells; + fcs_card_t this_card, prev_card; + double cards_under_sequences, temp; + double seqs_over_renegade_cards; + int sequences_are_built_by = instance->sequences_are_built_by; + int freecells_num = instance->freecells_num; + int stacks_num = instance->stacks_num; + double * a_star_weights = soft_thread->a_star_weights; + int unlimited_sequence_move = instance->unlimited_sequence_move; + int decks_num = instance->decks_num; + + cards_under_sequences = 0; + num_freestacks = 0; + seqs_over_renegade_cards = 0; + for(a=0;a= 0) && fcs_is_parent_card(this_card,prev_card)) + { + c--; + this_card = prev_card; + if (c>=0) + { + prev_card = fcs_stack_card(state, a, c); + } + } + cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT); + if (c >= 0) + { + seqs_over_renegade_cards += + ((unlimited_sequence_move) ? + 1 : + pow(cards_num-c-1, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT) + ); + } + } + + ret += ((soft_thread->a_star_initial_cards_under_sequences - cards_under_sequences) + / soft_thread->a_star_initial_cards_under_sequences) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES]; + + ret += (seqs_over_renegade_cards / + pow(decks_num*52, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT) ) + * a_star_weights[FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS]; + + num_cards_in_founds = 0; + for(a=0;a<(decks_num<<2);a++) + { + num_cards_in_founds += fcs_foundation_value(state, a); + } + + ret += ((double)num_cards_in_founds/(decks_num*52)) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_OUT]; + + num_freecells = 0; + for(a=0;aempty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) + { + if (unlimited_sequence_move) + { + temp = (((double)num_freecells+num_freestacks)/(freecells_num+instance->stacks_num)); + } + else + { + temp = (((double)((num_freecells+1)<stacks_num))); + } + } + else + { + if (unlimited_sequence_move) + { + temp = (((double)num_freecells)/freecells_num); + } + else + { + temp = 0; + } + } + + ret += (temp * a_star_weights[FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE]); + + if (ptr_state_with_locations->depth <= 20000) + { + ret += ((20000 - ptr_state_with_locations->depth)/20000.0) * a_star_weights[FCS_A_STAR_WEIGHT_DEPTH]; + } + + return (int)(ret*INT_MAX); +} + + + + +/* + freecell_solver_a_star_or_bfs_do_solve_or_resume() is the main event + loop of the A* And BFS scans. It is quite simple as all it does is + extract elements out of the queue or priority queue and run all the test + of them. + + It goes on in this fashion until the final state was reached or + there are no more states in the queue. +*/ + +#define myreturn(ret_value) \ + /* Free the memory that was allocated by the \ + * derived states list */ \ + if (derived.states != NULL) \ + { \ + free(derived.states); \ + } \ + \ + soft_thread->bfs_queue_last_item = bfs_queue_last_item; \ + \ + return (ret_value); + + +int freecell_solver_a_star_or_bfs_do_solve_or_resume( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations_orig, + int resume + ) +{ + freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread; + freecell_solver_instance_t * instance = hard_thread->instance; + + fcs_state_with_locations_t * ptr_state_with_locations; + int num_freestacks, num_freecells; + fcs_states_linked_list_item_t * save_item; + int a; + int check; + fcs_derived_states_list_t derived; + int derived_index; + + int method; + int freecells_num, stacks_num; + int tests_order_num; + int * tests_order_tests; + int calc_real_depth = instance->calc_real_depth; + int soft_thread_id = soft_thread->id; + int is_a_complete_scan = soft_thread->is_a_complete_scan; + int to_reparent_states = + (instance->to_reparent_states || + (soft_thread->method == FCS_METHOD_OPTIMIZE) + ); + int scans_synergy = instance->scans_synergy; + fcs_states_linked_list_item_t * bfs_queue = soft_thread->bfs_queue; + PQUEUE * a_star_pqueue = soft_thread->a_star_pqueue; + fcs_states_linked_list_item_t * bfs_queue_last_item = soft_thread->bfs_queue_last_item; + + derived.num_states = 0; + derived.max_num_states = 0; + derived.states = NULL; + + tests_order_num = soft_thread->tests_order.num; + tests_order_tests = soft_thread->tests_order.tests; + + if (!resume) + { + /* Initialize the first element to indicate it is the first */ + ptr_state_with_locations_orig->parent = NULL; + ptr_state_with_locations_orig->moves_to_parent = NULL; + ptr_state_with_locations_orig->depth = 0; + } + + ptr_state_with_locations = ptr_state_with_locations_orig; + + method = soft_thread->method; + freecells_num = instance->freecells_num; + stacks_num = instance->stacks_num; + + /* Continue as long as there are states in the queue or + priority queue. */ + while ( ptr_state_with_locations != NULL) + { + /* + * If this is an optimization scan and the state being checked is not + * in the original solution path - move on to the next state + * */ + if ((method == FCS_METHOD_OPTIMIZE) && (!(ptr_state_with_locations->visited & FCS_VISITED_IN_SOLUTION_PATH))) + { + goto label_next_state; + } + + /* + * It the state has already been visited - move on to the next + * state. + * */ + if ((method == FCS_METHOD_OPTIMIZE) ? + (ptr_state_with_locations->visited & FCS_VISITED_IN_OPTIMIZED_PATH) : + ((ptr_state_with_locations->visited & FCS_VISITED_DEAD_END) || + (is_scan_visited(ptr_state_with_locations, soft_thread_id))) + ) + { + goto label_next_state; + } + + /* Count the free-cells */ + num_freecells = 0; + for(a=0;adebug_iter_output) && (!resume)) + { +#ifdef DEBUG + printf("ST Name: %s\n", soft_thread->name); +#endif + instance->debug_iter_output_func( + (void*)instance->debug_iter_output_context, + instance->num_times, + ptr_state_with_locations->depth, + (void*)instance, + ptr_state_with_locations, + ((ptr_state_with_locations->parent == NULL) ? + 0 : + ptr_state_with_locations->parent->visited_iter + ) + ); + } + + + if ((num_freestacks == stacks_num) && (num_freecells == freecells_num)) + { + instance->final_state = ptr_state_with_locations; + + myreturn(FCS_STATE_WAS_SOLVED); + } + + calculate_real_depth(ptr_state_with_locations); + + /* Do all the tests at one go, because that the way it should be + done for BFS and A* + */ + derived.num_states = 0; + for(a=0 ; + a < tests_order_num; + a++) + { + check = freecell_solver_sfs_tests[tests_order_tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK] ( + soft_thread, + ptr_state_with_locations, + num_freestacks, + num_freecells, + &derived, + /* + * We want to reparent the new states, only if this + * is an optimization scan. + * */ + to_reparent_states + ); + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || + (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) || + (check == FCS_STATE_SUSPEND_PROCESS)) + { + /* Save the current position in the scan */ + soft_thread->first_state_to_check = ptr_state_with_locations; + + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + } + + if (check_if_limits_exceeded()) + + { + soft_thread->first_state_to_check = ptr_state_with_locations; + + myreturn(FCS_STATE_SUSPEND_PROCESS); + } + + + if (is_a_complete_scan) + { + ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE; + } + + /* Increase the number of iterations by one . + * */ + { + instance->num_times++; + hard_thread->num_times++; + } + + /* Insert all the derived states into the PQ or Queue */ + + for(derived_index = 0 ; derived_index < derived.num_states ; derived_index++) + { + if (method == FCS_METHOD_A_STAR) + { + freecell_solver_a_star_enqueue_state( + soft_thread, + derived.states[derived_index] + ); + } + else + { + freecell_solver_bfs_enqueue_state( + soft_thread, + derived.states[derived_index] + ); + } + } + + if (method == FCS_METHOD_OPTIMIZE) + { + ptr_state_with_locations->visited |= FCS_VISITED_IN_OPTIMIZED_PATH; + } + else + { + set_scan_visited(ptr_state_with_locations, soft_thread_id); + + if (derived.num_states == 0) + { + if (is_a_complete_scan) + { + mark_as_dead_end(ptr_state_with_locations); + } + } + } + + ptr_state_with_locations->visited_iter = instance->num_times-1; + +label_next_state: + + /* + Extract the next item in the queue/priority queue. + */ + if ((method == FCS_METHOD_BFS) || (method == FCS_METHOD_OPTIMIZE)) + { + save_item = bfs_queue->next; + if (save_item != bfs_queue_last_item) + { + ptr_state_with_locations = save_item->s; + bfs_queue->next = save_item->next; + free(save_item); + } + else + { + ptr_state_with_locations = NULL; + } + } + else + { + /* It is an A* scan */ + ptr_state_with_locations = freecell_solver_PQueuePop(a_star_pqueue); + } + resume = 0; + } + + myreturn(FCS_STATE_IS_NOT_SOLVEABLE); +} + +#undef myreturn + +#undef state diff --git a/kpat/freecell-solver/simpsim.c b/kpat/freecell-solver/simpsim.c new file mode 100644 index 00000000..f603ba39 --- /dev/null +++ b/kpat/freecell-solver/simpsim.c @@ -0,0 +1,1716 @@ +/* + * simpsim.c - a module that contains Simple Simon Moves. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2001 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#include + +#include "fcs.h" + +#include "tests.h" + +#include "ms_ca.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#define fcs_is_ss_false_parent(parent, child) \ + (fcs_card_card_num(parent) == fcs_card_card_num(child)+1) + +#define fcs_suit_is_ss_true_parent(parent_suit, child_suit) \ + ((parent_suit) == (child_suit)) + +#define fcs_is_ss_true_parent(parent, child) \ + ( \ + fcs_is_ss_false_parent(parent,child) && \ + (fcs_suit_is_ss_true_parent(fcs_card_suit(parent),fcs_card_suit(child))) \ + ) + +/* + * Those are some macros to make it easier for the programmer. + * */ +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + + + +int freecell_solver_sfs_simple_simon_move_sequence_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + fcs_card_t temp_card; + + /* + * stack - the stack index from which to move cards to the founds. + * cards_num - the number of cards in "stack" + * suit - the suit of the complete sequence + * a - the height of the card + * */ + int stack, cards_num, suit, a; + /* + * card - the current card (at height a) + * above_card - the card above it. + * */ + fcs_card_t card, above_card; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + + for(stack=0;stack= 13) + { + card = fcs_stack_card(state,stack,cards_num-1); + + /* Check if the top 13 cards are a sequence */ + + for(a=2;a<=13;a++) + { + above_card = fcs_stack_card(state,stack,cards_num-a); + if (fcs_is_ss_true_parent(above_card, card)) + { + /* Do nothing - the card is OK for a propert sequence*/ + } + else + { + break; + } + card = above_card; + } + if (a == 14) + { + /* We can move this sequence up there */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + + suit = fcs_card_suit(card); + for(a=0;a<13;a++) + { + fcs_pop_stack_card(new_state, stack, temp_card); + fcs_increment_foundation(new_state, suit); + } + + + fcs_move_init(temp_move); + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_SEQ_TO_FOUNDATION); + fcs_move_set_src_stack(temp_move, stack); + fcs_move_set_foundation(temp_move,suit); + fcs_move_stack_push(moves,temp_move); + + sfs_check_state_end(); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + /* + * stack - the source stack index on which the sequence currently resides. + * cards_num - the number of cards in "stack". + * suit - the suit of the current card + * a - a temporary variable that designates a card height + * */ + int stack, cards_num, suit, a; + /* + * h - the current height in stack + * */ + int h; + /* + * card - the current card (at height h) + * above_card - the card above it. + * dest_card - the destination card on which to put the sequence + * */ + fcs_card_t card, temp_card, dest_card; + /* + * card_num - the card number (i.e: A, 2 ,3 ... K) of the card, or + * its previous one. + * num_true_seqs - the number of true sequences (i.e: sequences of a + * unified suit) in the source sequence. + * ds - the destination stack index. + * dest_cards_num - the number of cards in "ds". + * */ + int card_num, num_true_seqs, ds, dest_cards_num ; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + + for(stack=0;stack 0) + { + /* Loop on the cards in the stack and try to look for a true + * parent on top one of the stacks */ + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + for(h=cards_num-2;h>=-1;h--) + { + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + fcs_move_sequence(ds, stack, h+1, cards_num-1, a); + sfs_check_state_end(); + + } + } + } + } + + /* Stop if we reached the bottom of the stack */ + if (h == -1) + { + break; + } + + card = fcs_stack_card(state,stack,h); + /* If this is no longer a sequence - move to the next stack */ + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (! fcs_suit_is_ss_true_parent(suit, fcs_card_suit(card))) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + +int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + + int check; + + /* + * stack - the source stack index + * cards_num - number of cards in "stack" + * ds - the dest stack index + * dest_cards_num - number of cards in "ds". + * card - the current card + * card_num - its card number + * suit - its suit + * dest_card - the card at the top of "ds". + * h - the height of the current card on "stack" + * num_true_seqs - the number of true sequences on the current + * false sequence + * */ + int stack, cards_num, suit, a; + fcs_card_t card, temp_card, dest_card; + int card_num, num_true_seqs, h, ds, dest_cards_num ; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + /* Stop if we reached the bottom of the stack */ + for(h=cards_num-2;h>-1;h--) + { + card = fcs_stack_card(state,stack,h); + /* If this is no longer a sequence - move to the next stack */ + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + /* This means that the loop exited prematurely and the stack does + * not contain a sequence. */ + if (h != -1) + { + continue; + } + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ( + (fcs_is_ss_false_parent(dest_card, card)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + fcs_move_sequence(ds, stack, h+1, cards_num-1, a); + sfs_check_state_end(); + + } + } + } + } + + } + } + + return FCS_STATE_IS_NOT_SOLVEABLE; +} + + +int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ) +{ + tests_declare_accessors(); + + + fcs_move_t temp_move; + int check; + + /* + * stack - the source stack index + * cards_num - the number of cards in "stack" + * h - the height of the current card in "stack" + * card - the card in height "h" + * suit - its suit + * card_num - its card number + * ds - the destionation stack index + * dest_cards_num - the number of cards in "ds" + * dc - the index of the current card in "ds". + * num_separate_false_seqs - this variable tells how many distinct false + * sequences exist above the true parent + * above_num_true_seqs[] - the number of true sequences in each false + * sequence + * seq_points[] - the separation points of the false sequences (i.e: where + * they begin and end) + * stacks_map[] - a boolean map that indicates if one can place a card + * on this stack or is it already taken. + * junk_move_to_stacks[] - the stacks to move each false sequence of the + * junk to. + * false_seq_index - an iterator to hold the index of the current false + * sequence. + * after_junk_num_freestacks - this variable holds the number of stacks + * that remained unoccupied during and after the process of moving + * the junk sequences to different stacks. + * + * */ + int stack, cards_num, suit, a; + fcs_card_t card, temp_card, dest_card; + int card_num, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK], h, ds, dest_cards_num ; + int dc; + int seq_points[MAX_NUM_CARDS_IN_A_STACK]; + int num_separate_false_seqs; + int false_seq_index; + int num_true_seqs; + int stacks_map[MAX_NUM_STACKS]; + int after_junk_num_freestacks; + int junk_move_to_stacks[MAX_NUM_STACKS]; + + int state_stacks_num; + tests_define_accessors(); + + state_stacks_num = instance->stacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + + num_true_seqs = 1; + + + for(h=cards_num-2;h>=-1;h--) + { + for(ds=0;ds 0) + { + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + + /* + * above_c - the height of the card that is to be checked. + * above_card - the card at height above_c+1 + * up_above_card - the card at height above_c + * + * */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, ds, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs) + { + /* + * We can do it - so let's move everything. + * Notice that we only put the child in a different stack + * then the parent and let it move to the parent in the + * next iteration of the program + * */ + + sfs_check_state_begin(); + + my_copy_stack(ds); + my_copy_stack(stack); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + for( sc = cards_num-1 ; sc >= 0 ; sc-- ) + { + int above_c; + fcs_card_t above_card, up_above_card; + int end_of_src_seq; + + card = fcs_stack_card(state, stack, sc); + suit = fcs_card_suit(card); + card_num = fcs_card_card_num(card); + + num_true_seqs = 1; + + for (end_of_src_seq = sc+1; end_of_src_seq < cards_num ; end_of_src_seq++) + { + above_card = fcs_stack_card(state, stack, end_of_src_seq); + if (!fcs_is_ss_false_parent(card, above_card)) + { + break; + } + if (fcs_card_suit(above_card) != fcs_card_suit(card)) + { + num_true_seqs++; + } + card = above_card; + } + + if (end_of_src_seq == cards_num) + { + continue; + } + + /* Split the cards above it into false sequences */ + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, stack, cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cards_num-2 ; + above_c > end_of_src_seq-1 ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (end_of_src_seq-1 < cards_num-1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(ds=0;ds 0) + { + dest_card = fcs_stack_card(state, ds, dest_cards_num-1); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) > num_true_seqs) + { + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + + /* Let's boogie - we can move everything */ + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_src_junk_true_seqs = 1; + + + for(h=cards_num-2;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state, stack, h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_src_junk_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + + if (h != -1) + { + end_of_junk = h; + num_true_seqs = 1; + + for(;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state,stack,h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + + for(ds=0;ds 1) + { + /* Start at the card below the top one, so we will + * make sure there's at least some junk above it + * */ + for(dc=dest_cards_num-2;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ((fcs_card_suit(dest_card) == suit) && + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card)) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= the_num_true_seqs) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs+1) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move everything */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0;stack 0) + { + card = fcs_stack_card(state,stack,cards_num-1); + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + num_true_seqs = 1; + + for(h=cards_num-2;h>=-1;h--) + { + if (h == -1) + { + break; + } + card = fcs_stack_card(state,stack,h); + if (fcs_card_card_num(card) != card_num+1) + { + break; + } + if (fcs_card_suit(card) != suit) + { + num_true_seqs++; + } + card_num = fcs_card_card_num(card); + suit = fcs_card_suit(card); + } + if (h == -1) + { + for(ds=0;ds 0) + { + for(dc=dest_cards_num-1;dc>=0;dc--) + { + dest_card = fcs_stack_card(state, ds, dc); + if ( + (fcs_card_card_num(dest_card) == (card_num+1)) + ) + { + /* This is a suitable parent - let's check if there's a sequence above it. */ + int above_c; + fcs_card_t above_card, up_above_card; + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, ds, dest_cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = dest_cards_num-2 ; + above_c > dc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, ds, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (dc < dest_cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + for(a=0;a 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card)) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + /* This is a suitable parent - let's check if we + * have enough empty stacks to make the move feasible */ + if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs) + { + /* We can do it - so let's move */ + + sfs_check_state_begin(); + + my_copy_stack(stack); + my_copy_stack(ds); + + + /* Move the junk cards to their place */ + + for(false_seq_index=0; + false_seq_indexstacks_num; + + for(stack=0 ; stack < state_stacks_num ; stack++) + { + cards_num = fcs_stack_len(state, stack); + if (cards_num > 2) + { + /* Search for a parent card */ + for(pc=0; pc < cards_num-1 ; pc++) + { + parent_card = fcs_stack_card(state, stack, pc); + if ( + fcs_is_ss_true_parent( + parent_card, + fcs_stack_card(state, stack, pc+1) + ) + ) + { + continue; + } + + + for(cc = pc + 2 ; cc < cards_num ; cc++) + { + child_card = fcs_stack_card(state, stack, cc); + if (fcs_is_ss_true_parent( + parent_card, + child_card + ) + ) + { + /* We have a matching parent and child cards */ +#if 0 + printf("Stack %i, Parent %i, Child %i\n", stack, pc, cc); + fflush(stdout); +#endif + + /* + * Now let's try to find stacks to place the cards above + * the child card. + * */ + + int above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK]; + int seq_points[MAX_NUM_CARDS_IN_A_STACK]; + int stacks_map[MAX_NUM_STACKS]; + int junk_move_to_stacks[MAX_NUM_STACKS]; + int num_separate_false_seqs; + + fcs_card_t above_card, up_above_card; + int above_c; + + int end_of_child_seq; + int child_num_true_seqs; + + end_of_child_seq = cc; + child_num_true_seqs = 1; + while ((end_of_child_seq+1 < cards_num) && + fcs_is_ss_false_parent( + fcs_stack_card(state, stack, end_of_child_seq), + fcs_stack_card(state, stack, end_of_child_seq+1) + ) + ) + { + child_num_true_seqs += (!fcs_is_ss_true_parent( + fcs_stack_card(state, stack, end_of_child_seq), + fcs_stack_card(state, stack, end_of_child_seq+1) + )); + end_of_child_seq++; + } + + num_separate_false_seqs = 0; + above_card = fcs_stack_card(state, stack, cards_num-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cards_num-2; + above_c > end_of_child_seq ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (end_of_child_seq < cards_num - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + /* Add the child to the seq_points */ + child_seq_index = num_separate_false_seqs; + above_num_true_seqs[num_separate_false_seqs] = child_num_true_seqs; + seq_points[num_separate_false_seqs++] = cc; + + /* Add the cards between the parent and the child to the seq_points */ + + above_card = fcs_stack_card(state, stack, cc-1); + above_num_true_seqs[num_separate_false_seqs] = 1; + for(above_c = cc-2; + above_c > pc ; + above_c-- + ) + { + up_above_card = fcs_stack_card(state, stack, above_c); + if (! fcs_is_ss_false_parent(up_above_card, above_card)) + { + seq_points[num_separate_false_seqs++] = above_c+1; + above_num_true_seqs[num_separate_false_seqs] = 1; + } + above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card)); + above_card = up_above_card; + } + + if (pc < cc - 1) + { + seq_points[num_separate_false_seqs++] = above_c+1; + } + + + + for(a = 0 ; a < state_stacks_num ; a++) + { + stacks_map[a] = 0; + } + stacks_map[stack] = 1; + + after_junk_num_freestacks = num_freestacks; + + for(false_seq_index=0;false_seq_index 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + fcs_card_t clear_junk_dest_card; + + clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1); + if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index]))) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index]) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + } + + if (clear_junk_dest_stack == state_stacks_num) + { + clear_junk_dest_stack = -1; + } + + if (clear_junk_dest_stack == -1) + { + /* Check if there is a vacant stack */ + if (num_freestacks > 0) + { + if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index]) + { + /* Find an empty stack and designate it as the destination for the junk */ + for( + clear_junk_dest_stack = 0; + clear_junk_dest_stack < state_stacks_num; + clear_junk_dest_stack++ + ) + { + if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0)) + { + stacks_map[clear_junk_dest_stack] = 1; + break; + } + } + } + after_junk_num_freestacks--; + } + } + + if ((clear_junk_dest_stack == -1)) + { + break; + } + junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack; + } + + if (false_seq_index == num_separate_false_seqs) + { + /* Let's check if we can move the child after we are done moving all the junk cards */ + if (calc_max_sequence_move(0, after_junk_num_freestacks) >= child_num_true_seqs) + { + /* We can do it - so let's move everything */ + + sfs_check_state_begin(); + + /* Move the junk cards to their place */ + + my_copy_stack(stack); + + for(false_seq_index=0; + false_seq_index +#include +#include + +#include "fcs_config.h" +#include "state.h" +#include "card.h" +#include "fcs_enums.h" +#include "app_str.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + + +#ifdef DEBUG_STATES + +fcs_card_t freecell_solver_empty_card = {0,0}; + +#elif defined(COMPACT_STATES) || defined (INDIRECT_STACK_STATES) + +fcs_card_t freecell_solver_empty_card = (fcs_card_t)0; + +#endif + +static int fcs_card_compare(const void * card1, const void * card2) +{ + const fcs_card_t * c1 = (const fcs_card_t *)card1; + const fcs_card_t * c2 = (const fcs_card_t *)card2; + + if (fcs_card_card_num(*c1) > fcs_card_card_num(*c2)) + { + return 1; + } + else if (fcs_card_card_num(*c1) < fcs_card_card_num(*c2)) + { + return -1; + } + else + { + if (fcs_card_suit(*c1) > fcs_card_suit(*c2)) + { + return 1; + } + else if (fcs_card_suit(*c1) < fcs_card_suit(*c2)) + { + return -1; + } + else + { + return 0; + } + } +} + +#ifdef DEBUG_STATES +static int fcs_stack_compare(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((const fc_stack_t *)s1)->cards[0]; + fcs_card_t card2 = ((const fc_stack_t *)s2)->cards[0]; + + return fcs_card_compare(&card1, &card2); +} +#elif defined(COMPACT_STATES) +static int fcs_stack_compare(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((fcs_card_t*)s1)[1]; + fcs_card_t card2 = ((fcs_card_t*)s2)[1]; + + return fcs_card_compare(&card1, &card2); +} +#elif defined(INDIRECT_STACK_STATES) + + +#if MAX_NUM_DECKS == 1 +static int fcs_stack_compare_for_stack_sort(const void * s1, const void * s2) +{ + fcs_card_t card1 = ((fcs_card_t*)s1)[1]; + fcs_card_t card2 = ((fcs_card_t*)s2)[1]; + + return fcs_card_compare(&card1, &card2); +} +#endif + +int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2) +{ + const fcs_card_t * s1 = (const fcs_card_t *)v_s1; + const fcs_card_t * s2 = (const fcs_card_t *)v_s2; + + int min_len; + int a, ret; + + min_len = min(s1[0], s2[0]); + + for(a=0;a s2[0]) + { + return 1; + } + else + { + return 0; + } +} + +#endif + +#ifdef FCS_WITH_TALONS +static int fcs_talon_compare_with_context(const void * p1, const void * p2, fcs_compare_context_t context) +{ + fcs_card_t * t1 = (fcs_card_t *)p1; + fcs_card_t * t2 = (fcs_card_t *)p2; + + if (t1[0] < t2[0]) + { + return -1; + } + else if (t1[0] > t2[0]) + { + return 1; + } + else + { + return memcmp(t1,t2,t1[0]+1); + } +} +#endif + +#ifdef DEBUG_STATES +void freecell_solver_canonize_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num) +{ + int b,c; + + fc_stack_t temp_stack; + fcs_card_t temp_freecell; + int temp_loc; + + /* Insertion-sort the stacks */ + for(b=1;b0) && + (fcs_stack_compare( + &(state->s.stacks[c]), + &(state->s.stacks[c-1]) + ) < 0) + ) + { + temp_stack = state->s.stacks[c]; + state->s.stacks[c] = state->s.stacks[c-1]; + state->s.stacks[c-1] = temp_stack; + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + &(state->s.freecells[c]), + &(state->s.freecells[c-1]) + ) < 0) + ) + { + temp_freecell = state->s.freecells[c]; + state->s.freecells[c] = state->s.freecells[c-1]; + state->s.freecells[c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} + +#elif defined(COMPACT_STATES) + +void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num) +{ + int b,c; + + char temp_stack[(MAX_NUM_CARDS_IN_A_STACK+1)]; + fcs_card_t temp_freecell; + char temp_loc; + + /* Insertion-sort the stacks */ + + for(b=1;b0) && + (fcs_stack_compare( + state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), + state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1) + ) < 0) + ) + { + memcpy(temp_stack, state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1)); + memcpy(state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1)); + memcpy(state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), temp_stack, (MAX_NUM_CARDS_IN_A_STACK+1)); + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion-sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + state->s.data+FCS_FREECELLS_OFFSET+c, + state->s.data+FCS_FREECELLS_OFFSET+c-1 + ) < 0) + ) + { + temp_freecell = (state->s.data[FCS_FREECELLS_OFFSET+c]); + state->s.data[FCS_FREECELLS_OFFSET+c] = state->s.data[FCS_FREECELLS_OFFSET+c-1]; + state->s.data[FCS_FREECELLS_OFFSET+c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} +#elif defined(INDIRECT_STACK_STATES) +void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num) +{ + int b,c; + fcs_card_t * temp_stack; + fcs_card_t temp_freecell; + char temp_loc; + + /* Insertion-sort the stacks */ + for(b=1;b0) && + ( +#if MAX_NUM_DECKS > 1 + freecell_solver_stack_compare_for_comparison +#else + fcs_stack_compare_for_stack_sort +#endif + ( + (state->s.stacks[c]), + (state->s.stacks[c-1]) + ) + < 0 + ) + ) + { + temp_stack = state->s.stacks[c]; + state->s.stacks[c] = state->s.stacks[c-1]; + state->s.stacks[c-1] = temp_stack; + + temp_loc = state->stack_locs[c]; + state->stack_locs[c] = state->stack_locs[c-1]; + state->stack_locs[c-1] = temp_loc; + + c--; + } + } + + /* Insertion sort the freecells */ + + for(b=1;b0) && + (fcs_card_compare( + &(state->s.freecells[c]), + &(state->s.freecells[c-1]) + ) < 0) + ) + { + temp_freecell = state->s.freecells[c]; + state->s.freecells[c] = state->s.freecells[c-1]; + state->s.freecells[c-1] = temp_freecell; + + temp_loc = state->fc_locs[c]; + state->fc_locs[c] = state->fc_locs[c-1]; + state->fc_locs[c-1] = temp_loc; + + c--; + } + } +} + +#endif + +static void fcs_state_init( + fcs_state_with_locations_t * state, + int stacks_num +#ifdef INDIRECT_STACK_STATES + ,fcs_card_t * indirect_stacks_buffer +#endif + ) +{ + int a; + memset((void*)&(state->s), 0, sizeof(fcs_state_t)); + for(a=0;astack_locs[a] = a; + } +#ifdef INDIRECT_STACK_STATES + for(a=0;as.stacks[a] = &indirect_stacks_buffer[a << 7]; + memset(state->s.stacks[a], '\0', MAX_NUM_DECKS*52+1); + } + for(;as.stacks[a] = NULL; + } +#endif + for(a=0;afc_locs[a] = a; + } +} + + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT) +int freecell_solver_state_compare(const void * s1, const void * s2) +{ + return memcmp(s1,s2,sizeof(fcs_state_t)); +} + +int freecell_solver_state_compare_equal(const void * s1, const void * s2) +{ + return (!memcmp(s1,s2,sizeof(fcs_state_t))); +} + + +int freecell_solver_state_compare_with_context( + const void * s1, + const void * s2, + fcs_compare_context_t context + ) +{ + (void)context; + return memcmp(s1,s2,sizeof(fcs_state_t)); +} +#else +int freecell_solver_state_compare_indirect(const void * s1, const void * s2) +{ + return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t)); +} + +int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context) +{ + return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t)); +} +#endif + +static const char * const freecells_prefixes[] = { "FC:", "Freecells:", "Freecell:", ""}; +static const char * const foundations_prefixes[] = { "Decks:", "Deck:", "Founds:", "Foundations:", "Foundation:", "Found:", ""}; +static const char * const talon_prefixes[] = { "Talon:", "Queue:" , ""}; +static const char * const num_redeals_prefixes[] = { "Num-Redeals:", "Readels-Num:", "Readeals-Number:", ""}; + +#ifdef WIN32 +#define strncasecmp(a,b,c) (strnicmp((a),(b),(c))) +#endif + +int freecell_solver_initial_user_state_to_c( + const char * string, + fcs_state_with_locations_t * out_state, + int freecells_num, + int stacks_num, + int decks_num +#ifdef FCS_WITH_TALONS + ,int talon_type +#endif +#ifdef INDIRECT_STACK_STATES + , fcs_card_t * indirect_stacks_buffer +#endif + ) +{ + fcs_state_with_locations_t ret_with_locations; + + int s,c; + const char * str; + fcs_card_t card; + int first_line; + + int prefix_found; + const char * const * prefixes; + int i; + int decks_index[4]; + + fcs_state_init( + &ret_with_locations, + stacks_num +#ifdef INDIRECT_STACK_STATES + , indirect_stacks_buffer +#endif + ); + str = string; + + first_line = 1; + +#define ret (ret_with_locations.s) +/* Handle the end of string - shouldn't happen */ +#define handle_eos() \ + { \ + if ((*str) == '\0') \ + { \ + return FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT; \ + } \ + } + +#ifdef FCS_WITH_TALONS + if (talon_type == FCS_TALON_KLONDIKE) + { + fcs_klondike_talon_num_redeals_left(ret) = -1; + } +#endif + + for(s=0;s= decks_num) + { + decks_index[d] = 0; + } + } + s--; + continue; + } + +#ifdef FCS_WITH_TALONS + prefixes = talon_prefixes; + prefix_found = 0; + for(i=0;prefixes[i][0] != '\0'; i++) + { + if (!strncasecmp(str, prefixes[i], strlen(prefixes[i]))) + { + prefix_found = 1; + str += strlen(prefixes[i]); + break; + } + } + + if (prefix_found) + { + /* Input the Talon */ + int talon_size; + + talon_size = MAX_NUM_DECKS*52+16; + ret.talon = malloc(sizeof(fcs_card_t)*talon_size); + fcs_talon_pos(ret) = 0; + + for(c=0 ; c < talon_size ; c++) + { + /* Move to the next card */ + if (c!=0) + { + while( + ((*str) != ' ') && + ((*str) != '\t') && + ((*str) != '\n') && + ((*str) != '\r') + ) + { + handle_eos(); + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + } + + while ((*str == ' ') || (*str == '\t')) + { + str++; + } + + if ((*str == '\n') || (*str == '\r')) + { + break; + } + + card = fcs_card_user2perl(str); + + fcs_put_card_in_talon(ret, c+(talon_type==FCS_TALON_KLONDIKE), card); + } + fcs_talon_len(ret) = c; + + if (talon_type == FCS_TALON_KLONDIKE) + { + int talon_len; + + talon_len = fcs_talon_len(ret); + fcs_klondike_talon_len(ret) = talon_len; + fcs_klondike_talon_stack_pos(ret) = -1; + fcs_klondike_talon_queue_pos(ret) = 0; + } + + s--; + continue; + } + + prefixes = num_redeals_prefixes; + prefix_found = 0; + for(i=0;prefixes[i][0] != '\0'; i++) + { + if (!strncasecmp(str, prefixes[i], strlen(prefixes[i]))) + { + prefix_found = 1; + str += strlen(prefixes[i]); + break; + } + } + + if (prefix_found) + { + while ((*str < '0') && (*str > '9') && (*str != '\n')) + { + handle_eos(); + str++; + } + if (*str != '\n') + { + int num_redeals; + + num_redeals = atoi(str); + if (talon_type == FCS_TALON_KLONDIKE) + { + fcs_klondike_talon_num_redeals_left(ret) = + (num_redeals < 0) ? + (-1) : + ((num_redeals > 127) ? 127 : num_redeals) + ; + } + } + s--; + continue; + } +#endif + + for(c=0 ; c < MAX_NUM_CARDS_IN_A_STACK ; c++) + { + /* Move to the next card */ + if (c!=0) + { + while( + ((*str) != ' ') && + ((*str) != '\t') && + ((*str) != '\n') && + ((*str) != '\r') + ) + { + handle_eos(); + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + } + + while ((*str == ' ') || (*str == '\t')) + { + str++; + } + if ((*str == '\n') || (*str == '\r')) + { + break; + } + card = fcs_card_user2perl(str); + + fcs_push_card_into_stack(ret, s, card); + } + } + + *out_state = ret_with_locations; + return FCS_USER_STATE_TO_C__SUCCESS; +} + +#undef ret +#undef handle_eos + +int freecell_solver_check_state_validity( + fcs_state_with_locations_t * state_with_locations, + int freecells_num, + int stacks_num, + int decks_num, +#ifdef FCS_WITH_TALONS + int talon_type, +#endif + fcs_card_t * misplaced_card) +{ + int cards[4][14]; + int c, s, d, f; + + fcs_state_t * state; + + state = (&(state_with_locations->s)); + + /* Initialize all cards to 0 */ + for(d=0;d<4;d++) + { + for(c=1;c<=13;c++) + { + cards[d][c] = 0; + } + } + + /* Mark the cards in the decks */ + for(d=0;ds)); + + if (canonized_order_output) + { + for(a=0;astack_locs[a])] = a; + } + for(a=0;afc_locs[a])] = a; + } + } + + for(a=0;a max_num_cards) + { + max_num_cards = fcs_stack_len(*state, stack_locs[s]); + } + } + + for(card_num=0;card_num= fcs_stack_len(*state, stack_locs[s])) + { + freecell_solver_append_string_sprintf( + app_str, + " " + ); + } + else + { + freecell_solver_append_string_sprintf( + app_str, + "%3s ", + fcs_card_perl2user( + fcs_stack_card( + *state, + stack_locs[s], + card_num), + stack_card_, + display_10_as_t + ) + ); + } + } + freecell_solver_append_string_sprintf(app_str, "%s", "\n"); + } + } + else + { + freecell_solver_append_string_sprintf(app_str, "%s", "Foundations: "); + for(a=0;a(MAX_NUM_DECKS*52) +#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_DECKS*52) +#else +#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_INITIAL_CARDS_IN_A_STACK+12) +#endif + +#define MAX_NUM_SCANS_BUCKETS 1 +#define MAX_NUM_SCANS (MAX_NUM_SCANS_BUCKETS * (sizeof(int)*8)) + +/********** + * TODO: Change 5 to the log2 of sizeof(int)*8 + * + ************/ + +#define is_scan_visited(ptr_state, scan_id) (ptr_state->scan_visited[(scan_id)>>5] & (1 << ((scan_id)&((1<<(5))-1)))) +#define set_scan_visited(ptr_state, scan_id) { ptr_state->scan_visited[(scan_id)>>5] |= (1 << ((scan_id)&((1<<(5))-1))); } + + +#ifdef DEBUG_STATES + +struct fcs_struct_card_t +{ + short card_num; + char suit; + char flags; +}; + +typedef struct fcs_struct_card_t fcs_card_t; + +struct fcs_struct_stack_t +{ + unsigned int num_cards; + fcs_card_t cards[MAX_NUM_CARDS_IN_A_STACK]; +}; + +typedef struct fcs_struct_stack_t fc_stack_t; + +struct fcs_struct_state_t +{ + fc_stack_t stacks[MAX_NUM_STACKS]; + fcs_card_t freecells[MAX_NUM_FREECELLS]; + int foundations[MAX_NUM_DECKS*4]; +#ifdef FCS_WITH_TALONS + fcs_card_t * talon; + char talon_params[4]; +#endif +}; + +typedef struct fcs_struct_state_t fcs_state_t; + +#if 0 +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + int stack_locs[MAX_NUM_STACKS]; + int fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + int visited; + int visited_iter; + int num_active_children; + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; +#endif +typedef int fcs_locs_t; + +#define fcs_stack_len(state, s) \ + ( (state).stacks[(s)].num_cards ) + +#define fcs_stack_card(state, s, c) \ + ( (state).stacks[(s)].cards[(c)] ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_card_card_num(card) \ + ( (card).card_num ) + +#define fcs_card_suit(card) \ + ((int)( (card).suit )) + +#define fcs_card_get_flipped(card) \ + ( (card).flags ) + +#define fcs_freecell_card(state, f) \ + ( (state).freecells[(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define fcs_foundation_value(state, found) \ + ( (state).foundations[(found)] ) + +#define fcs_increment_foundation(state, found) \ + ( (state).foundations[(found)]++ ) + +#define fcs_set_foundation(state, found, value) \ + ( (state).foundations[(found)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1]; \ + (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1] = fcs_empty_card; \ + (state).stacks[(s)].num_cards--; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + { \ + (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (state).stacks[(ss)].cards[(sc)]; \ + (state).stacks[(ds)].num_cards++; \ + } + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (from); \ + (state).stacks[(ds)].num_cards++; \ + } + +#define fcs_duplicate_state(dest, src) \ + (dest) = (src) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).freecells[(f)] = (card) + +#define fcs_empty_freecell(state, f) \ + (state).freecells[(f)] = fcs_empty_card + +#define fcs_card_set_suit(card, d) \ + (card).suit = (d) + +#define fcs_card_set_num(card, num) \ + (card).card_num = (num) + +#define fcs_card_set_flipped(card, flipped) \ + (card).flags = (flipped) + +#define fcs_flip_stack_card(state, s, c) \ + fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), 0) + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) +#endif + +#define fcs_copy_stack(state, idx, buffer) {} + +#elif defined(COMPACT_STATES) /* #ifdef DEBUG_STATES */ + + + + + + + +typedef char fcs_card_t; +/* + * Card: + * Bits 0-3 - Card Number + * Bits 4-5 - Deck + * + */ + +struct fcs_struct_state_t +{ + char data[MAX_NUM_STACKS*(MAX_NUM_CARDS_IN_A_STACK+1)+MAX_NUM_FREECELLS+4*MAX_NUM_DECKS]; +#ifdef FCS_WITH_TALON + fcs_card_t * talon; + char talon_params[4]; +#endif +}; +/* + * Stack: 0 - Number of cards + * 1-19 - Cards + * Stacks: stack_num*20 where stack_num >= 0 and + * stack_num <= (MAX_NUM_STACKS-1) + * Bytes: (MAX_NUM_STACKS*20) to + * (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS-1) + * are Freecells. + * Bytes: (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS) to + * MAX_NUM_STACKS*20+MAX_NUM_FREECELLS+3 + * are Foundations. + * */ + +/* ===== Depracated Information ===== + * Stack: 0 - Number of cards 1-19 - Cards + * Stacks: stack_num*20 where stack_num >= 0 and stack_num <= 7 + * Bytes 160-163 - Freecells + * Bytes 164-167 - Decks + */ + +typedef struct fcs_struct_state_t fcs_state_t; + +#if 0 +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + char stack_locs[MAX_NUM_STACKS]; + char fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + int visited; + int visited_iter; + int num_active_children; + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; +#endif +typedef char fcs_locs_t; + +#define fcs_card_card_num(card) \ + ( (card) & 0x0F ) + +#define fcs_card_suit(card) \ + ( ((card) >> 4) & 0x03 ) + +#define fcs_stack_len(state, s) \ + ( (size_t)(state).data[s*(MAX_NUM_CARDS_IN_A_STACK+1)] ) + +#define fcs_stack_card(state, s, c) \ + ( (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)+(c)+1] ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define FCS_FREECELLS_OFFSET ((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1)) + +#define fcs_freecell_card(state, f) \ + ( (state).data[FCS_FREECELLS_OFFSET+(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define FCS_FOUNDATIONS_OFFSET (((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1))+(MAX_NUM_FREECELLS)) + +#define fcs_foundation_value(state, d) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)]) + +#define fcs_increment_foundation(state, d) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)]++ ) + +#define fcs_set_foundation(state, d, value) \ + ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \ + (state).data[((s)*(MAX_NUM_CARDS_IN_A_STACK+1))+1+(fcs_stack_len((state), (s))-1)] = fcs_empty_card; \ + (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)]--; \ + } + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)+1+fcs_stack_len((state), (ds))] = (from); \ + (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)]++; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc))) + +#define fcs_duplicate_state(dest, src) \ + (dest) = (src) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).data[FCS_FREECELLS_OFFSET+(f)] = (card); + +#define fcs_empty_freecell(state, f) \ + fcs_put_card_in_freecell((state), (f), fcs_empty_card) + +#define fcs_card_set_num(card, num) \ + (card) = (((card)&0xF0)|(num)); + +#define fcs_card_set_suit(card, suit) \ + (card) = (((card)&0x4F)|((suit)<<4)); + +#define fcs_card_set_flipped(card, flipped) \ + (card) = (((card)&((fcs_card_t)0x3F))|((fcs_card_t)((flipped)<<6))) + +#define fcs_card_get_flipped(card) \ + ( (card) >> 6 ) + + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) +#endif + +#define fcs_flip_stack_card(state, s, c) \ + (fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), ((fcs_card_t)0) )) + +#define fcs_copy_stack(state, idx, buffer) {} + +#elif defined(INDIRECT_STACK_STATES) /* #ifdef DEBUG_STATES + #elif defined(COMPACT_STATES) + */ + +typedef char fcs_card_t; + +struct fcs_struct_state_t +{ + fcs_card_t * stacks[MAX_NUM_STACKS]; + fcs_card_t freecells[MAX_NUM_FREECELLS]; + char foundations[MAX_NUM_DECKS*4]; +#ifdef FCS_WITH_TALONS + fcs_card_t * talon; + char talon_params[4]; +#endif +}; + +typedef struct fcs_struct_state_t fcs_state_t; + +#define fcs_card_card_num(card) \ + ( (card) & 0x0F ) + +#define fcs_card_suit(card) \ + ( ((card) >> 4) & 0x03 ) + +#define fcs_card_get_flipped(card) \ + ( (card) >> 6 ) + +#define fcs_standalone_stack_len(stack) \ + ( (size_t)(stack[0]) ) + +#define fcs_stack_len(state, s) \ + ( (unsigned int)(state).stacks[(s)][0] ) + +#define fcs_stack_card(state, s, c) \ + ( (state).stacks[(s)][c+1] ) + +#define fcs_stack_card_num(state, s, c) \ + ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) ) + +#define fcs_stack_card_suit(state, s, c) \ + ( fcs_card_suit(fcs_stack_card((state),(s),(c))) ) + +#define fcs_freecell_card(state, f) \ + ( (state).freecells[(f)] ) + +#define fcs_freecell_card_num(state, f) \ + ( fcs_card_card_num(fcs_freecell_card((state),(f))) ) + +#define fcs_freecell_card_suit(state, f) \ + ( fcs_card_suit(fcs_freecell_card((state),(f))) ) + +#define fcs_foundation_value(state, d) \ + ( (state).foundations[(d)] ) + +#define fcs_increment_foundation(state, d) \ + ( (state).foundations[(d)]++ ) + +#define fcs_set_foundation(state, d, value) \ + ( (state).foundations[(d)] = (value) ) + +#define fcs_pop_stack_card(state, s, into) \ + { \ + into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \ + (state).stacks[s][fcs_stack_len((state), (s))] = fcs_empty_card; \ + (state).stacks[s][0]--; \ + } + + +#define fcs_push_card_into_stack(state, ds, from) \ + { \ + (state).stacks[(ds)][fcs_stack_len((state), (ds))+1] = (from); \ + (state).stacks[(ds)][0]++; \ + } + +#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \ + fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc))) + +#define fcs_put_card_in_freecell(state, f, card) \ + (state).freecells[(f)] = (card) + +#define fcs_empty_freecell(state, f) \ + fcs_put_card_in_freecell((state), (f), fcs_empty_card) + +#define fcs_card_set_num(card, num) \ + (card) = (((card)&0xF0)|(num)) + +#define fcs_card_set_suit(card, suit) \ + (card) = (((card)&0x4F)|((suit)<<4)) + +#define fcs_card_set_flipped(card, flipped) \ + (card) = (fcs_card_t)(((card)&0x3F)|((fcs_card_t)(flipped<<6))) + +#ifdef FCS_WITH_TALONS +#define fcs_talon_len(state) \ + ((state).talon_params[0]) + +#define fcs_talon_pos(state) \ + ((state).talon_params[1]) + +#define fcs_put_card_in_talon(state, pos, card) \ + ((state).talon[pos] = (card)) + +#define fcs_get_talon_card(state, pos) \ + ((state).talon[pos]) +#endif + +#define fcs_flip_stack_card(state, s, c) \ + (fcs_card_set_flipped(fcs_stack_card(state,s,c), ((fcs_card_t)0) )) + + +#define fcs_duplicate_state(dest,src) \ + { \ + (dest) = (src); \ + (dest).stacks_copy_on_write_flags = 0; \ + } + +#define fcs_copy_stack(state, idx, buffer) \ + { \ + if (! ((state).stacks_copy_on_write_flags & (1 << idx))) \ + { \ + size_t stack_len; \ + (state).stacks_copy_on_write_flags |= (1 << idx); \ + stack_len = fcs_stack_len((state).s,idx); \ + memcpy(&buffer[idx << 7], (state).s.stacks[idx], stack_len+1); \ + (state).s.stacks[idx] = &buffer[idx << 7]; \ + } \ + } + + +typedef char fcs_locs_t; + +#endif /* #ifdef DEBUG_STATES - + #elif defined COMPACT_STATES - + #elif defined INDIRECT_STACK_STATES + */ + +struct fcs_struct_state_with_locations_t +{ + fcs_state_t s; + fcs_locs_t stack_locs[MAX_NUM_STACKS]; + fcs_locs_t fc_locs[MAX_NUM_FREECELLS]; + struct fcs_struct_state_with_locations_t * parent; + fcs_move_stack_t * moves_to_parent; + int depth; + /* + * This field contains global, scan-independant flags, which are used + * from the FCS_VISITED_T enum below. + * + * FCS_VISITED_VISITED - deprecated + * + * FCS_VISITED_IN_SOLUTION_PATH - indicates that the state is in the + * solution path found by the scan. (used by the optimization scan) + * + * FCS_VISITED_IN_OPTIMIZED_PATH - indicates that the state is in the + * optimized solution path which is computed by the optimization scan. + * + * FCS_VISITED_DEAD_END - indicates that the state does not lead to + * anywhere useful, and scans should not examine it in the first place. + * */ + int visited; + /* + * The iteration in which this state was marked as visited + * */ + int visited_iter; + /* + * This is the number of direct children of this state which were not + * yet declared as dead ends. Once this counter reaches zero, this + * state too is declared as a dead end. + * */ + int num_active_children; + /* + * This is a vector of flags - one for each scan. Each indicates whether + * its scan has already visited this state + * */ + int scan_visited[MAX_NUM_SCANS_BUCKETS]; +#ifdef INDIRECT_STACK_STATES + /* + * A vector of flags that indicates which stacks were already copied. + * */ + int stacks_copy_on_write_flags; +#endif +}; + +typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t; + + +extern fcs_card_t freecell_solver_empty_card; +#define fcs_empty_card freecell_solver_empty_card + + +#ifdef FCS_WITH_TALONS +#define fcs_klondike_talon_len(state) \ + ((state).talon[0]) + +#define fcs_klondike_talon_stack_pos(state) \ + ((state).talon_params[0]) + +#define fcs_klondike_talon_queue_pos(state) \ + ((state).talon_params[1]) + +#define fcs_klondike_talon_num_redeals_left(state) \ + ((state).talon_params[2]) + +#define fcs_klondike_talon_get_top_card(state) \ + ((state).talon[(int)fcs_klondike_talon_stack_pos(state)]) + +#define fcs_klondike_talon_queue_to_stack(state) \ + ( ((state).talon[(int)((++fcs_klondike_talon_stack_pos(state))+1)]) = \ + ((state).talon[(int)((fcs_klondike_talon_queue_pos(state)++)+1)]) ) + +#define fcs_klondike_talon_redeal_bare(state) \ + { \ + fcs_klondike_talon_stack_pos(state) = -1; \ + fcs_klondike_talon_queue_pos(state) = 0; \ + } + +#define fcs_klondike_talon_decrement_stack(state) \ + ((state).talon[(int)((fcs_klondike_talon_stack_pos(state)--)+1)] = fcs_empty_card) +#endif + + +extern void freecell_solver_canonize_state( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num + ); + +#define fcs_canonize_state(state,freecells_num,stacks_num) freecell_solver_canonize_state((state),(freecells_num),(stacks_num)) + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT) + +#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_LIBREDBLACK_TREE) +typedef void * fcs_compare_context_t; +#else +typedef const void * fcs_compare_context_t; +#endif + +extern int freecell_solver_state_compare(const void * s1, const void * s2); +extern int freecell_solver_state_compare_equal(const void * s1, const void * s2); +extern int freecell_solver_state_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context); +#else +extern int freecell_solver_state_compare_indirect(const void * s1, const void * s2); +extern int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context); +#endif + +#ifdef FCS_WITH_TALONS +extern int fcs_talon_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context); +#endif + +enum FCS_USER_STATE_TO_C_RETURN_CODES +{ + FCS_USER_STATE_TO_C__SUCCESS = 0, + FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT +}; + +int freecell_solver_initial_user_state_to_c( + const char * string, + fcs_state_with_locations_t * out_state, + int freecells_num, + int stacks_num, + int decks_num +#ifdef FCS_WITH_TALONS + ,int talon_type +#endif +#ifdef INDIRECT_STACK_STATES + , fcs_card_t * indirect_stacks_buffer +#endif + ); + + +extern char * freecell_solver_state_as_string( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num, + int decks_num, + int parseable_output, + int canonized_order_output, + int display_10_as_t + ); + +enum FCS_STATE_VALIDITY_CODES +{ + FCS_STATE_VALIDITY__OK = 0, + FCS_STATE_VALIDITY__EMPTY_SLOT = 3, + FCS_STATE_VALIDITY__EXTRA_CARD = 2, + FCS_STATE_VALIDITY__MISSING_CARD = 1, + FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT = 4 +}; + +extern int freecell_solver_check_state_validity( + fcs_state_with_locations_t * state, + int freecells_num, + int stacks_num, + int decks_num, +#ifdef FCS_WITH_TALONS + int talon_type, +#endif + fcs_card_t * misplaced_card + ); + +#ifdef __cplusplus +} +#endif + +enum FCS_VISITED_T +{ + FCS_VISITED_VISITED = 0x1, + FCS_VISITED_IN_SOLUTION_PATH = 0x2, + FCS_VISITED_IN_OPTIMIZED_PATH = 0x4, + FCS_VISITED_DEAD_END = 0x8, + FCS_VISITED_ALL_TESTS_DONE = 0x10 +}; + + +#endif /* FC_SOLVE__STATE_H */ diff --git a/kpat/freecell-solver/test_arr.h b/kpat/freecell-solver/test_arr.h new file mode 100644 index 00000000..cfc5cd12 --- /dev/null +++ b/kpat/freecell-solver/test_arr.h @@ -0,0 +1,136 @@ +/* + * test_arr.h - header file for some routines and macros involving tests and + * the like for Freecell Solver. + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002 + * + * This file is in the public domain (it's uncopyrighted). + * */ + +#ifndef FC_SOLVE__TEST_ARR_H +#define FC_SOLVE__TEST_ARR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*freecell_solver_solve_for_state_test_t)( + freecell_solver_soft_thread_t *, + fcs_state_with_locations_t *, + int, + int, + fcs_derived_states_list_t *, + int + ); + +extern freecell_solver_solve_for_state_test_t freecell_solver_sfs_tests[FCS_TESTS_NUM]; + +/* + * This macro determines if child can be placed above parent. + * + * The variable sequences_are_built_by has to be initialized to + * the sequences_are_built_by member of the instance. + * + * */ +#define fcs_is_parent_card(child, parent) \ + ((fcs_card_card_num(child)+1 == fcs_card_card_num(parent)) && \ + ((sequences_are_built_by == FCS_SEQ_BUILT_BY_RANK) ? \ + 1 : \ + ((sequences_are_built_by == FCS_SEQ_BUILT_BY_SUIT) ? \ + (fcs_card_suit(child) == fcs_card_suit(parent)) : \ + ((fcs_card_suit(child) & 0x1) != (fcs_card_suit(parent)&0x1)) \ + )) \ + ) + +/* + * This macro traces the path of the state up to the original state, + * and thus calculates its real depth. + * + * It then assigns the newly updated depth throughout the path. + * + * */ +#define calculate_real_depth(ptr_state_orig) \ +{ \ + if (calc_real_depth) \ + { \ + int this_real_depth = 0; \ + fcs_state_with_locations_t * ptr_state = (ptr_state_orig); \ + /* Count the number of states until the original state. */ \ + while(ptr_state != NULL) \ + { \ + ptr_state = ptr_state->parent; \ + this_real_depth++; \ + } \ + this_real_depth--; \ + ptr_state = (ptr_state_orig); \ + /* Assign the new depth throughout the path*/ \ + while (ptr_state->depth != this_real_depth) \ + { \ + ptr_state->depth = this_real_depth; \ + this_real_depth--; \ + ptr_state = ptr_state->parent; \ + } \ + } \ +} \ + +/* + * This macro marks a state as a dead end, and afterwards propogates + * this information to its parent and ancestor states. + * */ +#define mark_as_dead_end(ptr_state_input) \ +{ \ + if (scans_synergy) \ + { \ + fcs_state_with_locations_t * ptr_state = (ptr_state_input); \ + /* Mark as a dead end */ \ + ptr_state->visited |= FCS_VISITED_DEAD_END; \ + ptr_state = ptr_state->parent; \ + if (ptr_state != NULL) \ + { \ + /* Decrease the refcount of the state */ \ + ptr_state->num_active_children--; \ + while((ptr_state->num_active_children == 0) && (ptr_state->visited & FCS_VISITED_ALL_TESTS_DONE)) \ + { \ + /* Mark as dead end */ \ + ptr_state->visited |= FCS_VISITED_DEAD_END; \ + /* Go to its parent state */ \ + ptr_state = ptr_state->parent; \ + if (ptr_state == NULL) \ + { \ + break; \ + } \ + /* Decrease the refcount */ \ + ptr_state->num_active_children--; \ + } \ + } \ + } \ +} + +/* + * This macro checks if we need to terminate from running this soft + * thread and return to the soft thread manager with an + * FCS_STATE_SUSPEND_PROCESS + * */ +#define check_if_limits_exceeded() \ + ( \ + ((instance->max_num_times >= 0) && \ + (instance->num_times >= instance->max_num_times)) \ + || \ + ((hard_thread->ht_max_num_times >= 0) && \ + (hard_thread->num_times >= hard_thread->ht_max_num_times)) \ + || \ + ((hard_thread->max_num_times >= 0) && \ + (hard_thread->num_times >= hard_thread->max_num_times)) \ + || \ + ((instance->max_num_states_in_collection >= 0) && \ + (instance->num_states_in_collection >= \ + instance->max_num_states_in_collection) \ + ) \ + ) + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/kpat/freecell-solver/tests.h b/kpat/freecell-solver/tests.h new file mode 100644 index 00000000..ce0b35b5 --- /dev/null +++ b/kpat/freecell-solver/tests.h @@ -0,0 +1,307 @@ +/* + * fcs.h - header file of the test functions for Freecell Solver. + * + * The test functions code is found in freecell.c + * + * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000 + * + * This file is in the public domain (it's uncopyrighted). + */ + +#ifndef FC_SOLVE__TESTS_H +#define FC_SOLVE__TESTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "fcs_isa.h" +#include "fcs.h" + +#include "test_arr.h" + + +/* + * The number of cards that can be moved is + * (freecells_number + 1) * 2 ^ (free_stacks_number) + * + * See the Freecell FAQ and the source code of PySol + * + * */ +#define calc_max_sequence_move(fc_num, fs_num) \ + ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ? \ + ( \ + (instance->unlimited_sequence_move) ? \ + INT_MAX : \ + (((fc_num)+1)<<(fs_num)) \ + ) : \ + ((fc_num)+1) \ + ) + +#include "caas.h" + +/* + * These are some macros to make it easier for the programmer. + * */ +#define state_with_locations (*ptr_state_with_locations) +#define state (ptr_state_with_locations->s) +#define new_state_with_locations (*ptr_new_state_with_locations) +#define new_state (ptr_new_state_with_locations->s) + +#define sfs_check_state_begin() \ + fcs_state_ia_alloc_into_var(ptr_new_state_with_locations, hard_thread); \ + fcs_duplicate_state(new_state_with_locations, state_with_locations); \ + /* Some A* and BFS parameters that need to be initialized in \ + * the derived state. \ + * */ \ + ptr_new_state_with_locations->parent = ptr_state_with_locations; \ + ptr_new_state_with_locations->moves_to_parent = moves; \ + /* Make sure depth is consistent with the game graph. \ + * I.e: the depth of every newly discovered state is derived from \ + * the state from which it was discovered. */ \ + ptr_new_state_with_locations->depth = ptr_state_with_locations->depth + 1; \ + /* Mark this state as a state that was not yet visited */ \ + ptr_new_state_with_locations->visited = 0; \ + /* It's a newly created state which does not have children yet. */ \ + ptr_new_state_with_locations->num_active_children = 0; \ + memset(ptr_new_state_with_locations->scan_visited, '\0', \ + sizeof(ptr_new_state_with_locations->scan_visited) \ + ); \ + fcs_move_stack_reset(moves); \ + + + + +#define sfs_check_state_end() \ +/* The last move in a move stack should be FCS_MOVE_TYPE_CANONIZE \ + * because it indicates that the order of the stacks and freecells \ + * need to be recalculated \ + * */ \ +fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); \ +fcs_move_stack_push(moves, temp_move); \ + \ +{ \ + fcs_state_with_locations_t * existing_state; \ + check = freecell_solver_check_and_add_state( \ + soft_thread, \ + ptr_new_state_with_locations, \ + &existing_state \ + ); \ + if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || \ + (check == FCS_STATE_SUSPEND_PROCESS)) \ + { \ + /* This state is not going to be used, so \ + * let's clean it. */ \ + fcs_state_ia_release(hard_thread); \ + return check; \ + } \ + else if (check == FCS_STATE_ALREADY_EXISTS) \ + { \ + fcs_state_ia_release(hard_thread); \ + calculate_real_depth(existing_state); \ + /* Re-parent the existing state to this one. \ + * \ + * What it means is that if the depth of the state if it \ + * can be reached from this one is lower than what it \ + * already have, then re-assign its parent to this state. \ + * */ \ + if (reparent && \ + (existing_state->depth > ptr_state_with_locations->depth+1)) \ + { \ + /* Make a copy of "moves" because "moves" will be destroyed */\ + existing_state->moves_to_parent = \ + freecell_solver_move_stack_compact_allocate( \ + hard_thread, moves \ + ); \ + if (!(existing_state->visited & FCS_VISITED_DEAD_END)) \ + { \ + if ((--existing_state->parent->num_active_children) == 0) \ + { \ + mark_as_dead_end( \ + existing_state->parent \ + ); \ + } \ + ptr_state_with_locations->num_active_children++; \ + } \ + existing_state->parent = ptr_state_with_locations; \ + existing_state->depth = ptr_state_with_locations->depth + 1; \ + } \ + fcs_derived_states_list_add_state( \ + derived_states_list, \ + existing_state \ + ); \ + } \ + else \ + { \ + fcs_derived_states_list_add_state( \ + derived_states_list, \ + ptr_new_state_with_locations \ + ); \ + } \ +} + + +/* + This macro checks if the top card in the stack is a flipped card + , and if so flips it so its face is up. + */ +#define fcs_flip_top_card(stack) \ +{ \ + int cards_num; \ + cards_num = fcs_stack_len(new_state,stack); \ + \ + if (cards_num > 0) \ + { \ + if (fcs_card_get_flipped( \ + fcs_stack_card( \ + new_state, \ + stack, \ + cards_num-1) \ + ) == 1 \ + ) \ + { \ + fcs_flip_stack_card(new_state,stack,cards_num-1); \ + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FLIP_CARD); \ + fcs_move_set_src_stack(temp_move, stack); \ + \ + fcs_move_stack_push(moves, temp_move); \ + } \ + } \ +} + + +/* + * dest is the destination stack + * source is the source stack + * start is the start height + * end is the end height + * a is the iterator + * */ +#define fcs_move_sequence(dest, source, start, end, a) \ +{ \ + for ( a = (start) ; a <= (end) ; a++) \ + { \ + fcs_push_stack_card_into_stack(new_state, dest, source, a); \ + } \ + \ + for ( a = (start) ; a <= (end) ; a++) \ + { \ + fcs_pop_stack_card(new_state, source, temp_card); \ + } \ + \ + fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); \ + fcs_move_set_src_stack(temp_move, source); \ + fcs_move_set_dest_stack(temp_move, dest); \ + fcs_move_set_num_cards_in_seq(temp_move, (end)-(start)+1); \ + \ + fcs_move_stack_push(moves, temp_move); \ +} + +/* + * This test declares a few access variables that are used in all + * the tests. + * */ +#define tests_declare_accessors() \ + freecell_solver_hard_thread_t * hard_thread; \ + freecell_solver_instance_t * instance; \ + fcs_state_with_locations_t * ptr_new_state_with_locations; \ + fcs_move_stack_t * moves; \ + char * indirect_stacks_buffer; \ + int calc_real_depth; \ + int scans_synergy + +/* + * This macro defines these accessors to have some value. + * */ +#define tests_define_accessors() \ + hard_thread = soft_thread->hard_thread; \ + instance = hard_thread->instance; \ + moves = hard_thread->reusable_move_stack; \ + indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; \ + calc_real_depth = instance->calc_real_depth; \ + scans_synergy = instance->scans_synergy; + + + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_founds( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); +extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +extern int freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack( + freecell_solver_soft_thread_t * soft_thread, + fcs_state_with_locations_t * ptr_state_with_locations, + int num_freestacks, + int num_freecells, + fcs_derived_states_list_t * derived_states_list, + int reparent + ); + +#ifdef __cplusplus +} +#endif + +#define my_copy_stack(idx) fcs_copy_stack(new_state_with_locations, idx, indirect_stacks_buffer); + +#endif /* FC_SOLVE__TESTS_H */ diff --git a/kpat/freecell.cpp b/kpat/freecell.cpp new file mode 100644 index 00000000..7a72c1fc --- /dev/null +++ b/kpat/freecell.cpp @@ -0,0 +1,854 @@ +/*--------------------------------------------------------------------------- + + freecell.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + (C) 2000 Stephan Kulow + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "freecell.h" +#include +#include "deck.h" +#include +#include +#include +#include +#include +#include "cardmaps.h" + +#include "freecell-solver/fcs_user.h" +#include "freecell-solver/fcs_cl.h" + +const int CHUNKSIZE = 100; + +void FreecellPile::moveCards(CardList &c, Pile *to) +{ + if (c.count() == 1) { + Pile::moveCards(c, to); + return; + } + FreecellBase *b = dynamic_cast(dealer()); + if (b) { + b->moveCards(c, this, to); + } +} + +//-------------------------------------------------------------------------// + +FreecellBase::FreecellBase( int decks, int stores, int freecells, int fill, bool unlimit, + KMainWindow* parent, const char* name) + : Dealer(parent,name), +solver_instance(0), es_filling(fill), solver_ret(FCS_STATE_NOT_BEGAN_YET), +unlimited_move(unlimit) +{ + deck = Deck::new_deck(this, decks); + deck->hide(); + + kdDebug(11111) << "cards " << deck->cards().count() << endl; + Pile *t; + for (int i = 0; i < stores; i++) { + FreecellPile *p = new FreecellPile(1 + i, this); + store.append(p); + p->setAddFlags(Pile::addSpread | Pile::several); + p->setRemoveFlags(Pile::several); + p->setCheckIndex(0); + } + + for (int i = 0; i < freecells; i++) + { + t = new Pile (1 + stores +i, this); + freecell.append(t); + t->setType(Pile::FreeCell); + } + + for (int i = 0; i < decks * 4; i++) + { + t = new Pile(1 + stores + freecells +i, this); + target.append(t); + t->setType(Pile::KlondikeTarget); + // COOLO: I'm still not too sure about that t->setRemoveFlags(Pile::Default); + } + + setActions(Dealer::Demo | Dealer::Hint); +} + +FreecellBase::~FreecellBase() +{ + if (solver_instance) + { + freecell_solver_user_free(solver_instance); + solver_instance = NULL; + } +} +//-------------------------------------------------------------------------// + +void FreecellBase::restart() +{ + freeSolution(); + deck->collectAndShuffle(); + deal(); +} + +QString suitToString(Card::Suit s) { + switch (s) { + case Card::Clubs: + return "C"; + case Card::Hearts: + return "H"; + case Card::Diamonds: + return "D"; + case Card::Spades: + return "S"; + } + return QString::null; +} + +QString rankToString(Card::Rank r) +{ + switch (r) { + case Card::King: + return "K"; + case Card::Ace: + return "A"; + case Card::Jack: + return "J"; + case Card::Queen: + return "Q"; + default: + return QString::number(r); + } +} + +int getDeck(Card::Suit suit) +{ + switch (suit) { + case Card::Hearts: + return 0; + case Card::Spades: + return 1; + case Card::Diamonds: + return 2; + case Card::Clubs: + return 3; + } + return 0; +} + +static const char * freecell_solver_cmd_line_args[280] = +{ +"--method", "soft-dfs", "-to", "0123456789", "-step", +"500", "--st-name", "1", "-nst", "--method", +"soft-dfs", "-to", "0123467", "-step", "500", +"--st-name", "2", "-nst", "--method", "random-dfs", +"-seed", "2", "-to", "0[01][23456789]", "-step", +"500", "--st-name", "3", "-nst", "--method", +"random-dfs", "-seed", "1", "-to", "0[0123456789]", +"-step", "500", "--st-name", "4", "-nst", "--method", +"random-dfs", "-seed", "3", "-to", "0[01][23467]", +"-step", "500", "--st-name", "5", "-nst", "--method", +"random-dfs", "-seed", "4", "-to", "0[0123467]", +"-step", "500", "--st-name", "9", "-nst", "--method", +"random-dfs", "-to", "[01][23456789]", "-seed", "8", +"-step", "500", "--st-name", "10", "-nst", +"--method", "random-dfs", "-to", "[01][23456789]", +"-seed", "268", "-step", "500", "--st-name", "12", +"-nst", "--method", "a-star", "-asw", +"0.2,0.3,0.5,0,0", "-step", "500", "--st-name", "16", +"-nst", "--method", "a-star", "-to", "0123467", +"-asw", "0.5,0,0.3,0,0", "-step", "500", "--st-name", +"18", "-nst", "--method", "soft-dfs", "-to", +"0126394875", "-step", "500", "--st-name", "19", +"--prelude", +"350@2,350@5,350@9,350@12,350@2,350@10,350@3,350@9,350@5,350@18,350@2,350@5,350@4,350@10,350@4,350@12,1050@9,700@18,350@10,350@5,350@2,350@10,1050@16,350@2,700@4,350@10,1050@2,1400@3,350@18,1750@5,350@16,350@18,700@4,1050@12,2450@5,1400@18,1050@2,1400@10,6300@1,4900@12,8050@18", +"-ni", "--method", "soft-dfs", "-to", "01ABCDE", +"-step", "500", "--st-name", "0", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "1", +"-step", "500", "--st-name", "1", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "2", +"-step", "500", "--st-name", "2", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "3", +"-step", "500", "--st-name", "3", "-nst", "--method", +"random-dfs", "-to", "01[ABCDE]", "-seed", "268", +"-step", "500", "--st-name", "4", "-nst", "--method", +"a-star", "-to", "01ABCDE", "-step", "500", +"--st-name", "5", "-nst", "--method", "a-star", +"-to", "01ABCDE", "-asw", "0.2,0.3,0.5,0,0", "-step", +"500", "--st-name", "6", "-nst", "--method", +"a-star", "-to", "01ABCDE", "-asw", "0.5,0,0.5,0,0", +"-step", "500", "--st-name", "7", "-nst", "--method", +"random-dfs", "-to", "[01][ABD][CE]", "-seed", "1900", +"-step", "500", "--st-name", "8", "-nst", "--method", +"random-dfs", "-to", "[01][ABCDE]", "-seed", "192", +"-step", "500", "--st-name", "9", "-nst", "--method", +"random-dfs", "-to", "[01ABCDE]", "-seed", "1977", +"-step", "500", "--st-name", "10", "-nst", +"--method", "random-dfs", "-to", "[01ABCDE]", "-seed", +"24", "-step", "500", "--st-name", "11", "-nst", +"--method", "soft-dfs", "-to", "01ABDCE", "-step", +"500", "--st-name", "12", "-nst", "--method", +"soft-dfs", "-to", "ABC01DE", "-step", "500", +"--st-name", "13", "-nst", "--method", "soft-dfs", +"-to", "01EABCD", "-step", "500", "--st-name", "14", +"-nst", "--method", "soft-dfs", "-to", "01BDAEC", +"-step", "500", "--st-name", "15", "--prelude", +"1000@0,1000@3,1000@0,1000@9,1000@4,1000@9,1000@3,1000@4,2000@2,1000@0,2000@1,1000@14,2000@11,1000@14,1000@3,1000@11,1000@2,1000@0,2000@4,2000@10,1000@0,1000@2,2000@10,1000@0,2000@11,2000@1,1000@10,1000@2,1000@10,2000@0,1000@9,1000@1,1000@2,1000@14,3000@8,1000@2,1000@14,1000@1,1000@10,3000@6,2000@4,1000@2,2000@0,1000@2,1000@11,2000@6,1000@0,5000@1,1000@0,2000@1,1000@2,3000@3,1000@10,1000@14,2000@6,1000@0,1000@2,2000@11,6000@8,8000@9,3000@1,2000@10,2000@14,3000@15,4000@0,1000@8,1000@10,1000@14,7000@0,14000@2,6000@3,7000@4,1000@8,4000@9,2000@15,2000@6,4000@3,2000@4,3000@15,2000@0,6000@1,2000@4,4000@6,4000@9,4000@14,7000@8,3000@0,3000@1,5000@2,3000@3,4000@9,8000@10,9000@3,5000@8,7000@11,11000@12,12000@0,8000@3,11000@9,9000@15,7000@2,12000@8,16000@5,8000@13,18000@0,9000@15,12000@10,16000@0,14000@3,16000@9,26000@4,23000@3,42000@6,22000@8,27000@10,38000@7,41000@0,42000@3,84000@13,61000@15,159000@5,90000@9" +}; + +void FreecellBase::findSolution() +{ + kdDebug(11111) << "findSolution\n"; + + QString output = solverFormat(); + kdDebug(11111) << output << endl; + + int ret; + + /* If solver_instance was not initialized yet - initialize it */ + if (! solver_instance) + { + solver_instance = freecell_solver_user_alloc(); + + char * error_string; + int error_arg; + char * known_parameters[1] = {NULL}; + + + ret = freecell_solver_user_cmd_line_parse_args( + solver_instance, + sizeof(freecell_solver_cmd_line_args)/sizeof(freecell_solver_cmd_line_args[0]), + freecell_solver_cmd_line_args, + 0, + known_parameters, + NULL, + NULL, + &error_string, + &error_arg + ); + + + assert(!ret); + } + /* + * I'm using the more standard interface instead of the depracated + * user_set_game one. I'd like that each function will have its + * own dedicated purpose. + * + * Shlomi Fish + * */ +#if 0 + ret = freecell_solver_user_set_game(solver_instance, + freecell.count(), + store.count(), + deck->decksNum(), + FCS_SEQ_BUILT_BY_ALTERNATE_COLOR, + unlimited_move, + es_filling); + assert(!ret); +#else + freecell_solver_user_set_num_freecells(solver_instance,freecell.count()); + freecell_solver_user_set_num_stacks(solver_instance,store.count()); + freecell_solver_user_set_num_decks(solver_instance,deck->decksNum()); + freecell_solver_user_set_sequences_are_built_by_type(solver_instance, FCS_SEQ_BUILT_BY_ALTERNATE_COLOR); + freecell_solver_user_set_sequence_move(solver_instance, unlimited_move); + freecell_solver_user_set_empty_stacks_filled_by(solver_instance, es_filling); + +#endif + + freecell_solver_user_limit_iterations(solver_instance, CHUNKSIZE); + + solver_ret = freecell_solver_user_solve_board(solver_instance, + output.latin1()); + resumeSolution(); +} + +void FreecellBase::resumeSolution() +{ + if (!solver_instance) + return; + + emit gameInfo(i18n("%1 tries - depth %2") + .arg(freecell_solver_user_get_num_times(solver_instance)) + .arg(freecell_solver_user_get_current_depth(solver_instance))); + + if (solver_ret == FCS_STATE_WAS_SOLVED) + { + emit gameInfo(i18n("solved after %1 tries"). + arg(freecell_solver_user_get_num_times( + solver_instance))); + kdDebug(11111) << "solved\n"; + Dealer::demo(); + return; + } + if (solver_ret == FCS_STATE_IS_NOT_SOLVEABLE) { + int moves = freecell_solver_user_get_num_times(solver_instance); + freeSolution(); + emit gameInfo(i18n("unsolved after %1 moves") + .arg(moves)); + stopDemo(); + return; + } + + unsigned int max_iters = freecell_solver_user_get_limit_iterations( + solver_instance) + CHUNKSIZE; + freecell_solver_user_limit_iterations(solver_instance, + max_iters); + + if (max_iters > 120000) { + solver_ret = FCS_STATE_IS_NOT_SOLVEABLE; + resumeSolution(); + return; + } + + solver_ret = freecell_solver_user_resume_solution(solver_instance); + QTimer::singleShot(0, this, SLOT(resumeSolution())); + +} +MoveHint *FreecellBase::translateMove(void *m) { + fcs_move_t move = *(static_cast(m)); + uint cards = fcs_move_get_num_cards_in_seq(move); + Pile *from = 0; + Pile *to = 0; + + switch(fcs_move_get_type(move)) + { + case FCS_MOVE_TYPE_STACK_TO_STACK: + from = store[fcs_move_get_src_stack(move)]; + to = store[fcs_move_get_dest_stack(move)]; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_STACK: + from = freecell[fcs_move_get_src_freecell(move)]; + to = store[fcs_move_get_dest_stack(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FREECELL: + from = freecell[fcs_move_get_src_freecell(move)]; + to = freecell[fcs_move_get_dest_freecell(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_STACK_TO_FREECELL: + from = store[fcs_move_get_src_stack(move)]; + to = freecell[fcs_move_get_dest_freecell(move)]; + cards = 1; + break; + + case FCS_MOVE_TYPE_STACK_TO_FOUNDATION: + from = store[fcs_move_get_src_stack(move)]; + cards = 1; + to = 0; + break; + + case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION: + from = freecell[fcs_move_get_src_freecell(move)]; + cards = 1; + to = 0; + } + assert(from); + assert(cards <= from->cards().count()); + assert(to || cards == 1); + Card *c = from->cards()[from->cards().count() - cards]; + + if (!to) + to = findTarget(c); + assert(to); + return new MoveHint(c, to); +} + +QString FreecellBase::solverFormat() const +{ + QString output; + QString tmp; + for (uint i = 0; i < target.count(); i++) { + if (target[i]->isEmpty()) + continue; + tmp += suitToString(target[i]->top()->suit()) + "-" + rankToString(target[i]->top()->rank()) + " "; + } + if (!tmp.isEmpty()) + output += QString::fromLatin1("Foundations: %1\n").arg(tmp); + + tmp.truncate(0); + for (uint i = 0; i < freecell.count(); i++) { + if (freecell[i]->isEmpty()) + tmp += "- "; + else + tmp += rankToString(freecell[i]->top()->rank()) + suitToString(freecell[i]->top()->suit()) + " "; + } + if (!tmp.isEmpty()) + output += QString::fromLatin1("Freecells: %1\n").arg(tmp); + + for (uint i = 0; i < store.count(); i++) + { + CardList cards = store[i]->cards(); + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + " "; + output += "\n"; + } + return output; +} + +// Idea stolen from klondike.cpp +// +// This function returns true when it is certain that the card t is no longer +// needed on any of the play piles. +// +// To determine wether a card is no longer needed on any of the play piles we +// obviously must know what a card can be used for there. According to the +// rules a card can be used to store another card with 1 less unit of value +// and opposite color. This is the only thing that a card can be used for +// there. Therefore the cards with lowest value (1) are useless there (base +// case). The other cards each have 2 cards that can be stored on them, let us +// call those 2 cards *depending cards*. +// +// The object of the game is to put all cards on the target piles. Therefore +// cards that are no longer needed on any of the play piles should be put on +// the target piles if possible. Cards on the target piles can not be moved +// and they can not store any of its depending cards. Let us call this that +// the cards on the target piles are *out of play*. +// +// The simple and obvious rule is: +// A card is no longer needed when both of its depending cards are out of +// play. +// +// More complex: +// Assume card t is red. Now, if the lowest unplayed black card is +// t.value()-2, then t may be needed to hold that black t.value()-1 card. +// If the lowest unplayed black card is t.value()-1, it will be playable +// to the target, unless it is needed for a red card of value t.value()-2. +// +// So, t is not needed if the lowest unplayed red card is t.value()-2 and the +// lowest unplayed black card is t.value()-1, OR if the lowest unplayed black +// card is t.value(). So, no recursion needed - we did it ahead of time. + +bool FreecellBase::noLongerNeeded(const Card & t) +{ + + if (t.rank() <= Card::Two) return true; // Base case. + + bool cardIsRed = t.isRed(); + + uint numSame = 0, numDiff = 0; + Card::Rank lowSame = Card::King, lowDiff = Card::King; + for (PileList::Iterator it = target.begin(); it != target.end(); ++it) + { + if ((*it)->isEmpty()) + continue; + if ((*it)->top()->isRed() == cardIsRed) { + numSame++; + if ((*it)->top()->rank() < lowSame) + lowSame = static_cast((*it)->top()->rank()+1); + } else { + numDiff++; + if ((*it)->top()->rank() < lowDiff) + lowDiff = static_cast((*it)->top()->rank()+1); + } + } + if (numSame < target.count()/2) lowSame = Card::Ace; + if (numDiff < target.count()/2) lowDiff = Card::Ace; + + return (lowDiff >= t.rank() || + (lowDiff >= t.rank()-1 && lowSame >= t.rank()-2)); +} + +// This is the getHints() from dealer.cpp with one line changed +// to use noLongerNeeded() to decide if the card should be +// dropped or not. +// +// I would recommend adding a virtual bool noLongerNeeded(const Card &t) +// to the base class (Dealer) that just returns true, and then calling +// it like is done here. That would preserve current functionality +// but eliminate this code duplication +void FreecellBase::getHints() +{ + for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) + { + if (!takeTargetForHints() && (*it)->target()) + continue; + + Pile *store = *it; + if (store->isEmpty()) + continue; +// kdDebug(11111) << "trying " << store->top()->name() << endl; + + CardList cards = store->cards(); + while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin()); + + CardList::Iterator iti = cards.begin(); + while (iti != cards.end()) + { + if (store->legalRemove(*iti)) { +// kdDebug(11111) << "could remove " << (*iti)->name() << endl; + for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit) + { + Pile *dest = *pit; + if (dest == store) + continue; + if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target()) + continue; + if (!dest->legalAdd(cards)) + continue; + + bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards ); + if (!takeTargetForHints() && dest->target()) + newHint(new MoveHint(*iti, dest, noLongerNeeded(*(*iti)))); + else { + store->hideCards(cards); + // if it could be here as well, then it's no use + if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards)) + newHint(new MoveHint(*iti, dest)); + else { + if (old_prefer && !checkPrefering( store->checkIndex(), + store, cards )) + { // if checkPrefers says so, we add it nonetheless + newHint(new MoveHint(*iti, dest)); + } + } + store->unhideCards(cards); + } + } + } + cards.remove(iti); + iti = cards.begin(); + } + } +} + +void FreecellBase::demo() +{ + if (solver_instance && solver_ret == FCS_STATE_WAS_SOLVED) { + Dealer::demo(); + return; + } + towait = (Card*)-1; + unmarkAll(); + kdDebug(11111) << "demo " << (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE) << endl; + if (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE) + findSolution(); +} + +MoveHint *FreecellBase::chooseHint() +{ + if (solver_instance && freecell_solver_user_get_moves_left(solver_instance)) { + + emit gameInfo(i18n("%1 moves before finish").arg(freecell_solver_user_get_moves_left(solver_instance))); + + fcs_move_t move; + if (!freecell_solver_user_get_next_move(solver_instance, &move)) { + MoveHint *mh = translateMove(&move); + oldmoves.append(mh); + return mh; + } else + return 0; + } else + return Dealer::chooseHint(); +} + +void FreecellBase::countFreeCells(int &free_cells, int &free_stores) const +{ + free_cells = 0; + free_stores = 0; + + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) free_cells++; + if (es_filling == FCS_ES_FILLED_BY_ANY_CARD) + for (uint i = 0; i < store.count(); i++) + if (store[i]->isEmpty()) free_stores++; +} + +void FreecellBase::freeSolution() +{ + for (HintList::Iterator it = oldmoves.begin(); it != oldmoves.end(); ++it) + delete *it; + oldmoves.clear(); + + if (!solver_instance) + return; + freecell_solver_user_recycle(solver_instance); + solver_ret = FCS_STATE_NOT_BEGAN_YET; +} + +void FreecellBase::stopDemo() +{ + Dealer::stopDemo(); + freeSolution(); +} + +void FreecellBase::moveCards(CardList &c, FreecellPile *from, Pile *to) +{ + if (!demoActive() && solver_instance) { + freeSolution(); + } + + assert(c.count() > 1); + if (unlimited_move) { + from->Pile::moveCards(c, to); + return; + } + setWaiting(true); + + from->moveCardsBack(c); + waitfor = c.first(); + connect(waitfor, SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*))); + + PileList fcs; + + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) fcs.append(freecell[i]); + + PileList fss; + + if (es_filling == FCS_ES_FILLED_BY_ANY_CARD) + for (uint i = 0; i < store.count(); i++) + if (store[i]->isEmpty() && to != store[i]) fss.append(store[i]); + + if (fcs.count() == 0) { + assert(fss.count()); + fcs.append(fss.last()); + fss.remove(fss.fromLast()); + } + while (moves.count()) { delete moves.first(); moves.remove(moves.begin()); } + + movePileToPile(c, to, fss, fcs, 0, c.count(), 0); + + if (!waitfor->animated()) + QTimer::singleShot(0, this, SLOT(startMoving())); +} + +struct MoveAway { + Pile *firstfree; + int start; + int count; +}; + +void FreecellBase::movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs, uint start, uint count, int debug_level) +{ + kdDebug(11111) << debug_level << " movePileToPile" << c.count() << " " << start << " " << count << endl; + uint moveaway = 0; + if (count > fcs.count() + 1) { + moveaway = (fcs.count() + 1); + while (moveaway * 2 < count) + moveaway <<= 1; + } + kdDebug(11111) << debug_level << " moveaway " << moveaway << endl; + + QValueList moves_away; + + if (count - moveaway < (fcs.count() + 1) && (count <= 2 * (fcs.count() + 1))) { + moveaway = count - (fcs.count() + 1); + } + while (count > fcs.count() + 1) { + assert(fss.count()); + MoveAway ma; + ma.firstfree = fss[0]; + ma.start = start; + ma.count = moveaway; + moves_away.append(ma); + fss.remove(fss.begin()); + movePileToPile(c, ma.firstfree, fss, fcs, start, moveaway, debug_level + 1); + start += moveaway; + count -= moveaway; + moveaway >>= 1; + if ((count > (fcs.count() + 1)) && (count <= 2 * (fcs.count() + 1))) + moveaway = count - (fcs.count() + 1); + } + uint moving = QMIN(count, QMIN(c.count() - start, fcs.count() + 1)); + assert(moving); + + for (uint i = 0; i < moving - 1; i++) { + moves.append(new MoveHint(c[c.count() - i - 1 - start], fcs[i])); + } + moves.append(new MoveHint(c[c.count() - start - 1 - (moving - 1)], to)); + + for (int i = moving - 2; i >= 0; --i) + moves.append(new MoveHint(c[c.count() - i - 1 - start], to)); + + while (moves_away.count()) + { + MoveAway ma = moves_away.last(); + moves_away.remove(moves_away.fromLast()); + movePileToPile(c, to, fss, fcs, ma.start, ma.count, debug_level + 1); + fss.append(ma.firstfree); + } +} + +void FreecellBase::startMoving() +{ + kdDebug(11111) << "startMoving\n"; + if (moves.isEmpty()) { + if (demoActive() && towait) { + waitForDemo(towait); + } + setWaiting(false); + takeState(); + return; + } + + MoveHint *mh = moves.first(); + moves.remove(moves.begin()); + CardList empty; + empty.append(mh->card()); + assert(mh->card() == mh->card()->source()->top()); + assert(mh->pile()->legalAdd(empty)); + mh->pile()->add(mh->card()); + mh->pile()->moveCardsBack(empty, true); + waitfor = mh->card(); + kdDebug(11111) << "wait for moving end " << mh->card()->name() << endl; + connect(mh->card(), SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*))); + delete mh; +} + +void FreecellBase::newDemoMove(Card *m) +{ + Dealer::newDemoMove(m); + if (m != m->source()->top()) + m->disconnect(); +} + +void FreecellBase::waitForMoving(Card *c) +{ + if (waitfor != c) + return; + c->disconnect(); + startMoving(); +} + +bool FreecellBase::cardDblClicked(Card *c) +{ + // target move + if (Dealer::cardDblClicked(c)) + return true; + + if (c->animated()) + return false; + + if (c == c->source()->top() && c->realFace()) + for (uint i = 0; i < freecell.count(); i++) + if (freecell[i]->isEmpty()) { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, freecell[i]); + canvas()->update(); + return true; + } + return false; +} + +bool FreecellBase::CanPutStore(const Pile *c1, const CardList &c2) const +{ + int fcs, fss; + countFreeCells(fcs, fss); + + if (c1->isEmpty()) // destination is empty + fss--; + + if (!unlimited_move && int(c2.count()) > ((fcs)+1)<isEmpty()) + return true; + + Card *c = c2.first(); // we assume there are only valid sequences + + // ok if in sequence, alternate colors + return ((c1->top()->rank() == (c->rank()+1)) + && (c1->top()->isRed() != c->isRed())); +} + +bool FreecellBase::checkAdd(int, const Pile *c1, const CardList &c2) const +{ + return CanPutStore(c1, c2); +} + +//-------------------------------------------------------------------------// + +bool FreecellBase::checkRemove(int checkIndex, const Pile *p, const Card *c) const +{ + if (checkIndex != 0) + return false; + + // ok if just one card + if (c == p->top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = p->indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = p->at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->isRed() != before->isRed()))) + { + return false; + } + if (c == p->top()) + return true; + before = c; + } + + return true; +} + +//-------------------------------------------------------------------------// + +class Freecell : public FreecellBase +{ +public: + Freecell( KMainWindow* parent=0, const char* name=0); + virtual void deal(); +}; + +Freecell::Freecell( KMainWindow* parent, const char* name) + : FreecellBase(1, 8, 4, FCS_ES_FILLED_BY_ANY_CARD, false, parent, name) +{ + for (int i = 0; i < 8; i++) + store[i]->move(8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * i, 8 + cardMap::CARDY() * 11 / 10); + + const int right = 8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * 7 + cardMap::CARDX(); + + for (int i = 0; i < 4; i++) + freecell[i]->move(8 + ( cardMap::CARDX() * 13 / 12 ) * i, 8); + + for (int i = 0; i < 4; i++) + target[i]->move(right - (3-i) * ( cardMap::CARDX() * 13 / 12 ) -cardMap::CARDX() , 8); +} + +void Freecell::deal() +{ + int column = 0; + while (!deck->isEmpty()) + { + store[column]->add (deck->nextCard(), false, true); + column = (column + 1) % 8; + } +} + +static class LocalDealerInfo3 : public DealerInfo +{ +public: + LocalDealerInfo3() : DealerInfo(I18N_NOOP("&Freecell"), 3) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Freecell(parent); } +} ldi8; + +//-------------------------------------------------------------------------// + +#include"freecell.moc" diff --git a/kpat/freecell.h b/kpat/freecell.h new file mode 100644 index 00000000..aba311b6 --- /dev/null +++ b/kpat/freecell.h @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------- + + freecell.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + (C) 2000 Stephan Kulow + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#ifndef _FREECELL_H_ +#define _FREECELL_H_ + +#include "dealer.h" + +class FreecellPile : public Pile +{ +public: + FreecellPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {} + virtual void moveCards(CardList &c, Pile *to); +}; + +class FreecellBase : public Dealer +{ + Q_OBJECT + +public: + FreecellBase( int decks, int stores, int freecells, int es_filling, bool unlimited_move, + KMainWindow* parent=0, const char* name=0); + void moveCards(CardList &c, FreecellPile *from, Pile *to); + QString solverFormat() const; + virtual ~FreecellBase(); + +public slots: + virtual void deal() = 0; + virtual void restart(); + void waitForMoving(Card *c); + void startMoving(); + void resumeSolution(); + virtual void demo(); + +protected: + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + + bool CanPutStore(const Pile *c1, const CardList& c2) const; + bool CanRemove(const Pile *c1, const Card *c) const; + + void countFreeCells(int &free_cells, int &free_stores) const; + + virtual void getHints(); + void movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs, + uint start, uint count, int debug_level); + + Pile *pileForName(QString line) const; + void findSolution(); + + virtual MoveHint *chooseHint(); + MoveHint *translateMove(void *m); + void freeSolution(); + + virtual void stopDemo(); + virtual void newDemoMove(Card *m); + virtual bool cardDblClicked(Card *c); + +protected: + QValueList store; + PileList freecell; + PileList target; + Deck *deck; +private: + HintList moves; + HintList oldmoves; + Card *waitfor; + void *solver_instance; + int es_filling; + int solver_ret; + bool unlimited_move; + bool noLongerNeeded(const Card &); +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/gamestats.ui b/kpat/gamestats.ui new file mode 100644 index 00000000..2d1e5443 --- /dev/null +++ b/kpat/gamestats.ui @@ -0,0 +1,272 @@ + +GameStats + + + GameStats + + + + 0 + 0 + 310 + 211 + + + + Statistics + + + true + + + + unnamed + + + + layout3 + + + + unnamed + + + + textLabel1 + + + + 1 + 5 + 0 + 0 + + + + Game: + + + + + GameType + + + + 7 + 0 + 0 + 0 + + + + + + + + layout5 + + + + unnamed + + + + layout2 + + + + unnamed + + + + Won + + + %1 + + + AlignVCenter|AlignRight + + + + + WonPerc + + + (%1%) + + + + + textLabel7 + + + Longest winning streak: + + + + + textLabel2 + + + Games played: + + + + + LooseStreak + + + %1 + + + AlignVCenter|AlignRight + + + + + textLabel8 + + + Longest losing streak: + + + + + WinStreak + + + %1 + + + AlignVCenter|AlignRight + + + + + textLabel3 + + + Games won: + + + + + Played + + + %1 + + + AlignVCenter|AlignRight + + + + + + + spacer2 + + + Horizontal + + + Expanding + + + + 250 + 20 + + + + + + + + spacer3 + + + Vertical + + + Expanding + + + + 20 + 71 + + + + + + Layout1 + + + + unnamed + + + 0 + + + 6 + + + + Horizontal Spacing2 + + + Horizontal + + + Expanding + + + + 20 + 20 + + + + + + buttonOk + + + &OK + + + + + + true + + + true + + + + + + + + + buttonOk + clicked() + GameStats + accept() + + + GameType + activated(int) + GameStats + setGameType(int) + + + + setGameType(int) + + + diff --git a/kpat/gamestatsimpl.cpp b/kpat/gamestatsimpl.cpp new file mode 100644 index 00000000..a3a67a43 --- /dev/null +++ b/kpat/gamestatsimpl.cpp @@ -0,0 +1,57 @@ +#include "gamestatsimpl.h" +#include "dealer.h" +#include "version.h" + +#include +#include + +#include +#include +#include + +GameStatsImpl::GameStatsImpl(QWidget* aParent, const char* aname) + : GameStats(aParent, aname) +{ + QStringList list; + QValueList::ConstIterator it; + for (it = DealerInfoList::self()->games().begin(); + it != DealerInfoList::self()->games().end(); ++it) + { + // while we develop, it may happen that some lower + // indices do not exist + uint index = (*it)->gameindex; + for (uint i = 0; i <= index; i++) + if (list.count() <= i) + list.append("unknown"); + list[index] = i18n((*it)->name); + list[index].replace('&',""); + } + GameType->insertStringList(list); + showGameType(0); +} + +void GameStatsImpl::showGameType(int id) +{ + GameType->setCurrentItem(id); + setGameType(id); +} + +void GameStatsImpl::setGameType(int id) +{ + // Trick to reset string to original value + languageChange(); + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, scores_group); + unsigned int t = config->readUnsignedNumEntry(QString("total%1").arg(id),0); + Played->setText(Played->text().arg(t)); + unsigned int w = config->readUnsignedNumEntry(QString("won%1").arg(id),0); + Won->setText(Won->text().arg(w)); + if (t) + WonPerc->setText(WonPerc->text().arg(w*100/t)); + else + WonPerc->setText(WonPerc->text().arg(0)); + WinStreak->setText( + WinStreak->text().arg(config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(id),0))); + LooseStreak->setText( + LooseStreak->text().arg(config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(id),0))); +} diff --git a/kpat/gamestatsimpl.h b/kpat/gamestatsimpl.h new file mode 100644 index 00000000..192b7793 --- /dev/null +++ b/kpat/gamestatsimpl.h @@ -0,0 +1,16 @@ +#ifndef GAMESTATS_IMPL_H_ +#define GAMESTATS_IMPL_H_ + +#include "gamestats.h" + +class GameStatsImpl : public GameStats +{ + public: + GameStatsImpl(QWidget* aParent, const char* aname); + + virtual void setGameType(int i); + virtual void showGameType(int i); +}; + +#endif + diff --git a/kpat/golf.cpp b/kpat/golf.cpp new file mode 100644 index 00000000..71bdfbb5 --- /dev/null +++ b/kpat/golf.cpp @@ -0,0 +1,169 @@ +#include "golf.h" +#include +#include "deck.h" +#include +#include "cardmaps.h" + +HorRightPile::HorRightPile( int _index, Dealer* parent) + : Pile(_index, parent) +{ +} + +QSize HorRightPile::cardOffset( bool _spread, bool, const Card *) const +{ + if (_spread) + return QSize(+hspread(), 0); + + return QSize(0, 0); +} + +//-------------------------------------------------------------------------// + +Golf::Golf( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int pile_dist = 10 + 3 * cardMap::CARDY(); + + deck = Deck::new_deck( this); + deck->move(10, pile_dist); + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + + for( int r = 0; r < 7; r++ ) { + stack[r]=new Pile(1+r, this); + stack[r]->move(10+r*dist_x,10); + stack[r]->setAddFlags( Pile::addSpread | Pile::disallow); + stack[r]->setCheckIndex( 1 ); + } + + waste=new HorRightPile(8,this); + waste->move(10 + cardMap::CARDX() * 5 / 4, pile_dist); + waste->setTarget(true); + waste->setCheckIndex( 0 ); + waste->setAddFlags( Pile::addSpread); + + setActions(Dealer::Hint | Dealer::Demo); +} + +//-------------------------------------------------------------------------// + +bool Golf::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const +{ + if (checkIndex == 1) + return false; + + Card *c2 = cl.first(); + + kdDebug(11111)<<"check add "<< c1->name()<<" " << c2->name() <<" "<rank() != (c1->top()->rank()+1)) && (c2->rank() != (c1->top()->rank()-1))) + return false; + + return true; +} + +bool Golf::checkRemove( int checkIndex, const Pile *, const Card *c2) const +{ + if (checkIndex == 0) + return false; + return (c2 == c2->source()->top()); +} + +//-------------------------------------------------------------------------// + +void Golf::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +void Golf::deckClicked(Card *) +{ + if (deck->isEmpty()) { + return; + + } + Card *c = deck->nextCard(); + waste->add(c, true, true); + int x = int(c->x()); + int y = int(c->y()); + c->move(deck->x(), deck->y()); + c->flipTo(x, y, 8); +} + +//-------------------------------------------------------------------------// + +void Golf::deal() +{ + for(int r=0;r<7;r++) + { + for(int i=0;i<5;i++) + { + stack[r]->add(deck->nextCard(),false,true); + } + } + waste->add(deck->nextCard(),false,false); + +} + +Card *Golf::demoNewCards() +{ + deckClicked(0); + return waste->top(); +} + +bool Golf::cardClicked(Card *c) +{ + if (c->source()->checkIndex() !=1) { + return Dealer::cardClicked(c); + } + + if (c != c->source()->top()) + return false; + + Pile*p=findTarget(c); + if (p) + { + CardList empty; + empty.append(c); + c->source()->moveCards(empty, p); + canvas()->update(); + return true; + } + return false; +} + +bool Golf::isGameLost() const +{ + if( !deck->isEmpty()) + return false; + + bool onecard = false; + + for( int r = 0; r < 7; r++ ) { + if( !stack[r]->isEmpty()){ + onecard = true; + CardList stackTops; + stackTops.append(stack[r]->top()); + if(this->checkAdd(0,waste,stackTops)) + return false; + } + } + + return onecard; +} + + +static class LocalDealerInfo13 : public DealerInfo +{ +public: + LocalDealerInfo13() : DealerInfo(I18N_NOOP("Go&lf"), 12) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Golf(parent); } +} ldi13; + +//-------------------------------------------------------------------------// + +#include"golf.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/golf.h b/kpat/golf.h new file mode 100644 index 00000000..9c3b6421 --- /dev/null +++ b/kpat/golf.h @@ -0,0 +1,45 @@ +#ifndef _GOLF_H_ +#define _GOLF_H_ + +#include "dealer.h" + +class HorRightPile : public Pile +{ + Q_OBJECT + +public: + HorRightPile( int _index, Dealer* parent = 0); + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; +}; + +class Golf : public Dealer +{ + Q_OBJECT + +public: + Golf( KMainWindow* parent=0, const char* name=0); + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + +protected slots: + void deckClicked(Card *); + +protected: + virtual bool startAutoDrop() { return false; } + virtual Card *demoNewCards(); + virtual bool cardClicked(Card *c); + +private: // functions + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c2) const; + +private: + Pile* stack[7]; + HorRightPile* waste; + Deck* deck; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/grandf.cpp b/kpat/grandf.cpp new file mode 100644 index 00000000..9756b840 --- /dev/null +++ b/kpat/grandf.cpp @@ -0,0 +1,227 @@ +/***********************-*-C++-*-******** + + grandf.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + (C) 2000 Stephan Kulow + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + +#include "grandf.h" +#include +#include "deck.h" +#include +#include +#include "cardmaps.h" + +Grandf::Grandf( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + deck = Deck::new_deck(this); + deck->hide(); + + const int distx = cardMap::CARDX() * 14 / 10; + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+(i+1)*distx, 10); + target[i]->setType(Pile::KlondikeTarget); + } + + for (int i=0; i<7; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(10+distx*i, 10 + cardMap::CARDY() * 15 / 10); + store[i]->setAddFlags(Pile::addSpread | Pile::several); + store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop); + store[i]->setCheckIndex(1); + } + + setActions(Dealer::Hint | Dealer::Demo | Dealer::Redeal); +} + +void Grandf::restart() { + deck->collectAndShuffle(); + deal(); + numberOfDeals = 1; +} + +void Grandf::redeal() { + unmarkAll(); + + if (numberOfDeals < 3) { + collect(); + deal(); + numberOfDeals++; + } + if (numberOfDeals == 3) { + aredeal->setEnabled(false); + } + takeState(); +} + +Card *Grandf::demoNewCards() +{ + if (numberOfDeals < 3) { + redeal(); + return store[3]->top(); + } else + return 0; +} + +void Grandf::deal() { + int start = 0; + int stop = 7-1; + int dir = 1; + + for (int round=0; round < 7; round++) + { + int i = start; + do + { + Card *next = deck->nextCard(); + if (next) + store[i]->add(next, i != start, true); + i += dir; + } while ( i != stop + dir); + int t = start; + start = stop; + stop = t+dir; + dir = -dir; + } + + int i = 0; + Card *next = deck->nextCard(); + while (next) + { + store[i+1]->add(next, false , true); + next = deck->nextCard(); + i = (i+1)%6; + } + + for (int round=0; round < 7; round++) + { + Card *c = store[round]->top(); + if (c) + c->turn(true); + } + aredeal->setEnabled(true); + canvas()->update(); +} + +/***************************** + + Does the collecting step of the game + + NOTE: this is not quite correct -- the piles should be turned + facedown (ie partially reversed) during collection. + +******************************/ +void Grandf::collect() { + unmarkAll(); + + for (int pos = 6; pos >= 0; pos--) { + CardList p = store[pos]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it) + deck->add(*it, true, false); + } +} + +bool Grandf::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const { + assert (checkIndex == 1); + if (c1->isEmpty()) + return c2.first()->rank() == Card::King; + else + return (c2.first()->rank() == c1->top()->rank() - 1) + && c2.first()->suit() == c1->top()->suit(); +} + +QString Grandf::getGameState() const +{ + return QString::number(numberOfDeals); +} + +void Grandf::setGameState( const QString &s) +{ + numberOfDeals = s.toInt(); + aredeal->setEnabled(numberOfDeals < 3); +} + +bool Grandf::isGameLost() const +{ + // If we can redeal, then nothing's lost yet. + if (numberOfDeals <3) + return false; + + // Work through the stores, look for killer criteria. + for(int i=0; i < 7; i++) { + + /* If this store is empty, then iterate through the other stores and + * check if there is a (visible) King card. If so, then we could move + * that to the free store (which means a turn is possible, so the + * game is not lost yet). + */ + if(store[i]->isEmpty()){ + for(int i2=1; i2 < 7; i2++) { + int j=(i+i2) % 7; + CardList p = store[j]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){ + Card *c= *it; + if( it != p.begin() && c->realFace() && c->rank() == Card::King) + return false; + } + } + } + else{ + /* If this store has an Ace as it's top card, then we can start a + * new target pile! + */ + if(store[i]->top()->rank() == Card::Ace) + return false; + + /* Check whether the top card of this store could be added to + * any of the target piles. + */ + for(int j=0; j <4; j++) + if( !target[j]->isEmpty()) + if(store[i]->top()->suit() == target[j]->top()->suit()) + if( store[i]->top()->rank() == target[j]->top()->rank() +1) + return false; + + /* Check whether any (group of) cards from another store could + * be put onto this store's top card. + */ + for(int i2=1; i2 < 7; i2++) { + int j=(i+i2) % 7; + CardList p = store[j]->cards(); + for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){ + Card *c= *it; + if( c->realFace() && + c->rank() == (store[i]->top()->rank()-1) && + c->suit() == store[i]->top()->suit() ) + return false; + } + } + } + } + return true; // can't move. +} + +static class LocalDealerInfo1 : public DealerInfo +{ +public: + LocalDealerInfo1() : DealerInfo(I18N_NOOP("&Grandfather"), 1) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Grandf(parent); } +} gfdi; + +#include "grandf.moc" diff --git a/kpat/grandf.h b/kpat/grandf.h new file mode 100644 index 00000000..db24483f --- /dev/null +++ b/kpat/grandf.h @@ -0,0 +1,65 @@ +/***********************-*-C++-*-******** + + grandf.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + +// +// 7 positions, all cards on table, follow suit +// ( I don't know a name for this one, but I learned it from my grandfather.) + +****************************************/ + + +#ifndef P_GRANDF_7 +#define P_GRANDF_7 + +#include "dealer.h" + +class KAction; +class Pile; +class Deck; +class KMainWindow; + +class Grandf : public Dealer { + Q_OBJECT + +public: + Grandf( KMainWindow* parent=0, const char* name=0); + +public slots: + void redeal(); + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + + +protected: + void collect(); + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual QString getGameState() const; + virtual void setGameState( const QString & stream ); + virtual Card *demoNewCards(); + +private: + Pile* store[7]; + Pile* target[4]; + Deck *deck; + int numberOfDeals; + +}; + +#endif diff --git a/kpat/green.png b/kpat/green.png new file mode 100644 index 0000000000000000000000000000000000000000..26793078e2cee090bb8816700b0407a3b580b6d0 GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=CYaL71_rn>Pw5$X?><>&kwGn}da)p}a4$1Slly>Eak-aXL9ABjLw+2gZb?1p+-% h23ZGIFg71$U +#include "deck.h" +#include "cardmaps.h" + +Gypsy::Gypsy( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this, 2); + deck->move(10 + dist_x / 2 + 8*dist_x, 10 + 45 * cardMap::CARDY() / 10); + + connect(deck, SIGNAL(clicked(Card*)), SLOT(slotClicked(Card *))); + + for (int i=0; i<8; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+dist_x*(8+(i/4)), 10 + (i%4)*dist_y); + target[i]->setAddType(Pile::KlondikeTarget); + } + + for (int i=0; i<8; i++) { + store[i] = new Pile(9+i, this); + store[i]->move(10+dist_x*i, 10); + store[i]->setAddType(Pile::GypsyStore); + store[i]->setRemoveType(Pile::FreecellStore); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Gypsy::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Gypsy::dealRow(bool faceup) { + for (int round=0; round < 8; round++) + store[round]->add(deck->nextCard(), !faceup, true); +} + +void Gypsy::deal() { + dealRow(false); + dealRow(false); + dealRow(true); + takeState(); +} + +Card *Gypsy::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + dealRow(true); + return store[0]->top(); +} + +bool Gypsy::isGameLost() const { + if(!deck->isEmpty()) + return false; + + for(int i=0; i < 8; i++){ + if(store[i]->isEmpty()) + return false; + + if(store[i]->top()->rank() == Card::Ace) + return false; + + for(int j=0; j <8; j++){ + if(!target[j]->isEmpty() && + (store[i]->top()->suit()==target[j]->top()->suit()) && + (store[i]->top()->rank()==(target[j]->top()->rank()+1))) + return false; + } + } + + for(int i=0; i < 8; i++) { + Card *cnext=store[i]->top(); + int indexi=store[i]->indexOf(cnext); + + Card *cardi= 0; + do{ + cardi=cnext; + if (indexi>0) + cnext=store[i]->at( --indexi ); + + for(int k=0; k <8; k++) { + if (i == k) + continue; + + if((cardi->rank()+1 == store[k]->top()->rank()) && + cardi->isRed() != store[k]->top()->isRed()){ + + // this test doesn't apply if indexi==0, but fails gracefully. + if(cnext->rank() == store[k]->top()->rank() && + cnext->suit() == store[k]->top()->suit()) + break; //nothing gained; keep looking. + + return false;// TODO: look deeper, move may not be helpful. + } + } + + } while((indexi>=0) && (cardi->rank()+1 == cnext->rank()) && + (cardi->isRed() != cnext->isRed())); + } + + return true; +} + +static class LocalDealerInfo7 : public DealerInfo +{ +public: + LocalDealerInfo7() : DealerInfo(I18N_NOOP("Gy&psy"), 7) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Gypsy(parent); } +} gyfdi; + +#include "gypsy.moc" diff --git a/kpat/gypsy.h b/kpat/gypsy.h new file mode 100644 index 00000000..ea28b04e --- /dev/null +++ b/kpat/gypsy.h @@ -0,0 +1,34 @@ + +#ifndef GYPSY_H +#define GYPSY_H + +#include "dealer.h" + +class KAction; +class Pile; +class Deck; +class KMainWindow; + +class Gypsy : public Dealer { + Q_OBJECT + +public: + Gypsy( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void slotClicked(Card *) { dealRow(true); } + void deal(); + virtual void restart(); + +private: // functions + void dealRow(bool faceup); + virtual Card *demoNewCards(); + +private: + Pile* store[8]; + Pile* target[8]; + Deck *deck; +}; + +#endif diff --git a/kpat/hint.h b/kpat/hint.h new file mode 100644 index 00000000..1d1c2594 --- /dev/null +++ b/kpat/hint.h @@ -0,0 +1,28 @@ +#ifndef HINT_H +#define HINT_H + + +class Card; +class Pile; + + +class MoveHint +{ +public: + MoveHint(Card *card, Pile *to, bool d=true); + + bool dropIfTarget() const { return m_dropiftarget; } + Card *card() const { return m_card; } + Pile *pile() const { return m_to; } + +private: + Card *m_card; + Pile *m_to; + bool m_dropiftarget; +}; + + +typedef QValueList HintList; + + +#endif diff --git a/kpat/icons/Makefile.am b/kpat/icons/Makefile.am new file mode 100644 index 00000000..24597080 --- /dev/null +++ b/kpat/icons/Makefile.am @@ -0,0 +1,6 @@ + +EXTRA_DIST = kpat-lq.png + +KDE_ICON = kpat + + diff --git a/kpat/icons/hi128-app-kpat.png b/kpat/icons/hi128-app-kpat.png new file mode 100644 index 0000000000000000000000000000000000000000..5bb5cdc7d99d6cce4128168e94434c6335e7c481 GIT binary patch literal 10455 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 N002ovPDHLkV1kd4C5-?8 literal 0 HcmV?d00001 diff --git a/kpat/icons/hi16-app-kpat.png b/kpat/icons/hi16-app-kpat.png new file mode 100644 index 0000000000000000000000000000000000000000..a33e861ca61d9f64b312d41464ac1d500a15bbd3 GIT binary patch literal 721 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 literal 0 HcmV?d00001 diff --git a/kpat/icons/hi48-app-kpat.png b/kpat/icons/hi48-app-kpat.png new file mode 100644 index 0000000000000000000000000000000000000000..41ea79b490ddaab35bc214a8c0061b335cd6707e GIT binary patch literal 3308 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 literal 0 HcmV?d00001 diff --git a/kpat/idiot.cpp b/kpat/idiot.cpp new file mode 100644 index 00000000..3ac49ef5 --- /dev/null +++ b/kpat/idiot.cpp @@ -0,0 +1,234 @@ +/* + idiot.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation. + + This file is provided AS IS with no warranties of any kind. The author + shall have no liability with respect to the infringement of copyrights, + trade secrets or any patents by this file or any part thereof. In no + event will the author be liable for any lost revenue or profits or + other special, indirect and consequential damages. + + 4 positions, remove lowest card(s) of suit +*/ + + +#include "idiot.h" +#include +#include "deck.h" +#include "cardmaps.h" + + +Idiot::Idiot( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + // Create the deck to the left. + m_deck = Deck::new_deck( this ); + m_deck->move(10, 10); + + const int distx = cardMap::CARDX() + cardMap::CARDX() / 10 + 1; + + // Create 4 piles where the cards will be placed during the game. + for( int i = 0; i < 4; i++ ) { + m_play[i] = new Pile( i + 1, this); + + m_play[i]->setAddFlags( Pile::addSpread ); + m_play[i]->setRemoveFlags( Pile::disallow ); + m_play[i]->move(10 + cardMap::CARDX() * 18 / 10 + distx * i, 10); + } + + // Create the discard pile to the right + m_away = new Pile( 5, this ); + m_away->setTarget(true); + m_away->setRemoveFlags(Pile::disallow); + m_away->move(10 + cardMap::CARDX() * 5 / 2 + distx * 4, 10); + + setActions(Dealer::Hint | Dealer::Demo); +} + + +void Idiot::restart() +{ + m_deck->collectAndShuffle(); + deal(); +} + + +inline bool higher( const Card* c1, const Card* c2) +{ + // Sanity check. + if (!c1 || !c2 || c1 == c2) + return false; + + // Must be same suit. + if (c1->suit() != c2->suit()) + return false; + + // Aces form a special case. + if (c2->rank() == Card::Ace) + return true; + if (c1->rank() == Card::Ace) + return false; + + return (c1->rank() < c2->rank()); +} + + +bool Idiot::canMoveAway(Card *c) +{ + return ( higher( c, m_play[ 0 ]->top() ) || + higher( c, m_play[ 1 ]->top() ) || + higher( c, m_play[ 2 ]->top() ) || + higher( c, m_play[ 3 ]->top() ) ); +} + + +bool Idiot::cardClicked(Card *c) +{ + // If the deck is clicked, deal 4 more cards. + if (c->source() == m_deck) { + deal(); + return true; + } + + // Only the top card of a pile can be clicked. + if (c != c->source()->top()) + return false; + + bool didMove = true; + if ( canMoveAway(c) ) + // Add to 'm_away', face up, no spread + m_away->add(c, false, false); + else if ( m_play[ 0 ]->isEmpty() ) + // Add to pile 1, face up, spread. + m_play[0]->add(c, false, true); + else if ( m_play[ 1 ]->isEmpty() ) + // Add to pile 2, face up, spread. + m_play[1]->add(c, false, true); + else if ( m_play[ 2 ]->isEmpty() ) + // Add to pile 3, face up, spread. + m_play[2]->add( c, false, true); + else if ( m_play[ 3 ]->isEmpty() ) + // Add to pile 4, face up, spread. + m_play[3]->add(c, false, true); + else + didMove = false; + + return true; // may be a lie, but noone cares +} + + +// The game is won when: +// 1. all cards are dealt. +// 2. all piles contain exactly one ace. +// 3. the rest of the cards are thrown away (follows automatically from 1, 2. +// +bool Idiot::isGameWon() const +{ + // Criterium 1. + if (!m_deck->isEmpty()) + return false; + + // Criterium 2. + for (int i = 0; i < 4; i++) { + if (m_play[i]->cardsLeft() != 1 || m_play[i]->top()->rank() != Card::Ace) + return false; + } + + return true; +} + + +// This patience doesn't support double click. +// + +bool Idiot::cardDblClicked(Card *) +{ + return false; // nothing - nada +} + + +// Deal 4 cards face up - one on each pile. +// + +void Idiot::deal() +{ + if ( m_deck->isEmpty() ) + return; + + // Move the four top cards of the deck to the piles, faceup, spread out. + for ( int i = 0; i < 4; i++ ) + m_play[ i ]->add( m_deck->nextCard(), false, true ); +} + + +void Idiot::getHints() +{ + bool cardMoved = false; + for ( int i = 0; i < 4; i++ ) + if ( canMoveAway( m_play[i]->top() ) ) { + cardMoved = true; + newHint(new MoveHint(m_play[i]->top(), m_away)); + } + + if (cardMoved) + return; + + // now let's try to be a bit clever with the empty piles + for( int i = 0; i < 4; i++ ) { + if (m_play[i]->isEmpty()) { + // Find a card to move there + int biggestPile = -1; + int sizeBiggestPile = -1; + for( int j = 0; j < 4; j++ ) { + if ( i != j && m_play[j]->cardsLeft()>1 ) { + + // Ace on top of the pile? -> move it + if ( m_play[j]->top()->rank() == Card::Ace ) { + biggestPile = j; + break; + } + + // Otherwise choose the biggest pile + if ( m_play[j]->cardsLeft() > sizeBiggestPile ) { + sizeBiggestPile = m_play[j]->cardsLeft(); + biggestPile = j; + } + } + } + + if ( biggestPile != -1 ) { + newHint(new MoveHint(m_play[biggestPile]->top(), m_play[i])); + return; + } + } + } +} + + +Card *Idiot::demoNewCards() +{ + if ( m_deck->isEmpty() ) + return 0; + + deal(); + + return m_play[0]->top(); +} + + +static class LocalDealerInfo2 : public DealerInfo +{ +public: + LocalDealerInfo2() : DealerInfo(I18N_NOOP("&Aces Up"), 2) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Idiot(parent); } +} ldi4; + + +#include "idiot.moc" diff --git a/kpat/idiot.h b/kpat/idiot.h new file mode 100644 index 00000000..101957c0 --- /dev/null +++ b/kpat/idiot.h @@ -0,0 +1,59 @@ +/***********************-*-C++-*-******** + + idiot.h implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + + +#ifndef P_IDIOT +#define P_IDIOT + + +#include "dealer.h" + + +class Idiot: public Dealer +{ + Q_OBJECT + +public: + + Idiot( KMainWindow* parent = 0, const char* name = 0 ); + + virtual bool isGameWon() const; + +protected: + virtual bool cardClicked(Card *); + virtual bool cardDblClicked(Card *); + virtual void getHints(); + virtual Card *demoNewCards(); + virtual bool startAutoDrop() { return false; } + +public slots: + + virtual void restart(); + void deal(); + +private: + bool canMoveAway(Card *c); + + Pile *m_play[ 4 ]; + Pile *m_away; + Deck *m_deck; +}; + +#endif diff --git a/kpat/kings.cpp b/kpat/kings.cpp new file mode 100644 index 00000000..7654a88f --- /dev/null +++ b/kpat/kings.cpp @@ -0,0 +1,132 @@ +#include "kings.h" +#include +#include +#include "deck.h" +#include +#include "freecell-solver/fcs_enums.h" +#include "cardmaps.h" + +Kings::Kings( KMainWindow* parent, const char *name ) + : FreecellBase( 2, 8, 8, FCS_ES_FILLED_BY_KINGS_ONLY, true, parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + + for (int i=0; i<8; i++) { + target[i]->move((8 + i/4) * dist_x + 10 + cardMap::CARDX() * 4 / 10, 10 + (i % 4) * cardMap::CARDY() * 14 / 10 ); + store[i]->move(10+dist_x*i, 10 + cardMap::CARDY() * 5 / 4); + store[i]->setSpread(13); + freecell[i]->move(10 + dist_x * i, 10); + } +} + +void Kings::demo() +{ + Dealer::demo(); +} + +void Kings::deal() { + CardList cards = deck->cards(); + CardList::Iterator it = cards.begin(); + int cn = 0; + for (int stack = -1; stack < 8; ) + { + while (it != cards.end() && (*it)->rank() != Card::King) { + if (stack >= 0) { + store[stack]->add(*it, false, true); + cn++; + } + ++it; + } + if (it == cards.end()) + break; + cn++; + store[++stack]->add(*it, false, true); + if (stack == 0) { + cards = deck->cards(); // reset to start + it = cards.begin(); + } else + ++it; + } + assert(cn == 104); +} + +bool Kings::isGameLost() const { + int i,indexi; + Card *c,*cnext,*ctarget; + CardList targets,ctops; + + for(i=0; i < 8; i++){ + if(freecell[i]->isEmpty()) + return false; + if(store[i]->isEmpty()) + return false; + if(store[i]->top()->rank() == Card::Ace) + return false; + } + + for(i=0; i < 8; i++){ + if(!target[i]->isEmpty()) + targets.append(target[i]->top()); + + if(!store[i]->isEmpty()) + ctops.append(store[i]->top()); + } + + for(i=0; i < 8; i++){ + if(store[i]->isEmpty()) + continue; + + c=store[i]->top(); + for (CardList::Iterator it = targets.begin(); it != targets.end(); ++it) { + ctarget=*it; + if(c->rank()-1 == ctarget->rank() && + c->suit() == ctarget->suit()){ + kdDebug(11111)<< "test 1" << endl; + return false; + } + } + + for(indexi=store[i]->indexOf(store[i]->top()); indexi>=0;indexi--){ + c=store[i]->at(indexi); + if(indexi > 0) + cnext=store[i]->at(indexi-1); + + for (CardList::Iterator it = ctops.begin(); it != ctops.end(); ++it) { + ctarget=*it; + if(c->rank()+1 == ctarget->rank() && + c->isRed() != ctarget->isRed()){ + + if(indexi == 0) + return false; + + if(cnext->rank() != ctarget->rank() + || cnext->suit() != ctarget->suit()) + return false; + } + } + if(cnext->rank() != c->rank()+1 && + cnext->isRed() != c->isRed()) + break; + } + } + + return true; +} + +#if 0 +NOTE: When this is reenabled, renumber the following patiences back again: +Golf +Klondike, draw 3 +Spider Easy +Spider Medium +Spider Hard + +static class LocalDealerInfo12 : public DealerInfo +{ +public: + LocalDealerInfo12() : DealerInfo(I18N_NOOP("&The Kings"), 12) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Kings(parent); } +} gfdi12; +#endif + +#include "kings.moc" diff --git a/kpat/kings.h b/kpat/kings.h new file mode 100644 index 00000000..e3a60475 --- /dev/null +++ b/kpat/kings.h @@ -0,0 +1,22 @@ +#ifndef _KINGS_H_ +#define _KINGS_H_ + +#include "freecell.h" + +class Pile; +class Deck; +class KMainWindow; + +class Kings : public FreecellBase { + Q_OBJECT + +public: + Kings( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + virtual void deal(); + virtual void demo(); +}; + +#endif diff --git a/kpat/klondike.cpp b/kpat/klondike.cpp new file mode 100644 index 00000000..ff561fa3 --- /dev/null +++ b/kpat/klondike.cpp @@ -0,0 +1,495 @@ +/***********************-*-C++-*-******** + + klondike.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +// +// 7 positions, alternating red and black +// + +****************************************/ + +#include "klondike.h" +#include +#include "deck.h" +#include +#include +#include "cardmaps.h" + +class KlondikePile : public Pile +{ +public: + KlondikePile( int _index, Dealer* parent) + : Pile(_index, parent) {} + + void clearSpread() { cardlist.clear(); } + + void addSpread(Card *c) { + cardlist.append(c); + } + virtual QSize cardOffset( bool _spread, bool, const Card *c) const { + kdDebug(11111) << "cardOffset " << _spread << " " << (c? c->name() : "(null)") << endl; + if (cardlist.contains(const_cast(c))) + return QSize(+dspread(), 0); + return QSize(0, 0); + } +private: + CardList cardlist; +}; + +Klondike::Klondike( bool easy, KMainWindow* parent, const char* _name ) + : Dealer( parent, _name ) +{ + // The units of the follwoing constants are pixels + const int margin = 10; // between card piles and board edge + const int hspacing = cardMap::CARDX() / 6 + 1; // horizontal spacing between card piles + const int vspacing = cardMap::CARDY() / 4; // vertical spacing between card piles + + deck = Deck::new_deck(this); + deck->move(margin, margin); + + EasyRules = easy; + + pile = new KlondikePile( 13, this); + + pile->move(margin + cardMap::CARDX() + cardMap::CARDX() / 4, margin); + // Move the visual representation of the pile to the intended position + // on the game board. + + pile->setAddFlags(Pile::disallow); + pile->setRemoveFlags(Pile::Default); + + for( int i = 0; i < 7; i++ ) { + play[ i ] = new Pile( i + 5, this); + play[i]->move(margin + (cardMap::CARDX() + hspacing) * i, margin + cardMap::CARDY() + vspacing); + play[i]->setAddType(Pile::KlondikeStore); + play[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop | Pile::wholeColumn); + } + + for( int i = 0; i < 4; i++ ) { + target[ i ] = new Pile( i + 1, this ); + target[i]->move(margin + (3 + i) * (cardMap::CARDX()+ hspacing), margin); + target[i]->setAddType(Pile::KlondikeTarget); + if (EasyRules) // change default + target[i]->setRemoveFlags(Pile::Default); + else + target[i]->setRemoveType(Pile::KlondikeTarget); + } + + setActions(Dealer::Hint | Dealer::Demo); + + redealt = false; +} + +// This function returns true when it is certain that the card t is no longer +// needed on any of the play piles. This function is recursive but the +// recursion will not get deep. +// +// To determine wether a card is no longer needed on any of the play piles we +// obviously must know what a card can be used for there. According to the +// rules a card can be used to store another card with 1 less unit of value +// and opposite color. This is the only thing that a card can be used for +// there. Therefore the cards with lowest value (1) are useless there (base +// case). The other cards each have 2 cards that can be stored on them, let us +// call those 2 cards *depending cards*. +// +// The object of the game is to put all cards on the target piles. Therefore +// cards that are no longer needed on any of the play piles should be put on +// the target piles if possible. Cards on the target piles can not be moved +// and they can not store any of its depending cards. Let us call this that +// the cards on the target piles are *out of play*. +// +// The simple and obvious rule is: +// A card is no longer needed when both of its depending cards are out of +// play. +// +// But using only the simplest rule to determine if a card is no longer +// needed on any of the play piles is not ambitios enough. Therefore, if a +// depending card is not out of play, we test if it could become out of play. +// The requirement for getting a card out of play is that it can be placed on +// a target pile and that it is no longer needed on any of the play piles +// (this is why this function is recursive). This more ambitious rule lets +// us extend the base case with the second lowest value (2). +bool Klondike::noLongerNeeded(Card::Rank r, Card::Suit s) { + + if (r <= Card::Two) return true; // Base case. + + // Find the 2 suits of opposite color. "- 1" is used here because the + // siuts are ranged 1 .. 4 but target_tops is indexed 0 .. 3. (Of course + // the subtraction of 1 does not affect performance because it is a + // constant expression that is calculated at compile time). + unsigned char a = Card::Clubs - 1, b = Card::Spades - 1; + if (s == Card::Clubs || s == Card::Spades) + a = Card::Diamonds - 1, b = Card::Hearts - 1; + + const Card::Rank depending_rank = static_cast(r - 1); + return + (((target_tops[a] >= depending_rank) + || + ((target_tops[a] >= depending_rank - 1) + && + (noLongerNeeded + (depending_rank, static_cast(a + 1))))) + && + ((target_tops[b] >= depending_rank) + || + ((target_tops[b] >= depending_rank - 1) + && + (noLongerNeeded + (depending_rank, static_cast(b + 1)))))); +} + +bool Klondike::tryToDrop(Card *t) +{ + if (!t || !t->realFace() || t->takenDown()) + return false; + +// kdDebug(11111) << "tryToDrop " << t->name() << endl; + + Pile *tgt = findTarget(t); + if (tgt) { + newHint + (new MoveHint(t, tgt, noLongerNeeded(t->rank(), t->suit()))); + return true; + } + return false; +} + +void Klondike::getHints() { + + target_tops[0] = target_tops[1] = target_tops[2] = target_tops[3] + = Card::None; + + for( int i = 0; i < 4; i++ ) + { + Card *c = target[i]->top(); + if (!c) continue; + target_tops[c->suit() - 1] = c->rank(); + } + + + Card* t[7]; + for(int i=0; i<7;i++) + t[i] = play[i]->top(); + + for(int i=0; i<7; i++) + { + CardList list = play[i]->cards(); + + for (CardList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + if (!(*it)->isFaceUp()) + continue; + + CardList empty; + empty.append(*it); + + for (int j = 0; j < 7; j++) + { + if (i == j) + continue; + + if (play[j]->legalAdd(empty)) { + if (((*it)->rank() != Card::King) || it != list.begin()) { + newHint(new MoveHint(*it, play[j])); + break; + } + } + } + break; // the first face up + } + + tryToDrop(play[i]->top()); + } + if (!pile->isEmpty()) + { + Card *t = pile->top(); + if (!tryToDrop(t)) + { + for (int j = 0; j < 7; j++) + { + CardList empty; + empty.append(t); + if (play[j]->legalAdd(empty)) { + newHint(new MoveHint(t, play[j])); + break; + } + } + } + } +} + +Card *Klondike::demoNewCards() { + deal3(); + if (!deck->isEmpty() && pile->isEmpty()) + deal3(); // again + return pile->top(); +} + +void Klondike::restart() { + kdDebug(11111) << "restart\n"; + deck->collectAndShuffle(); + redealt = false; + deal(); +} + +void Klondike::deal3() +{ + int draw; + + if ( EasyRules ) { + draw = 1; + } else { + draw = 3; + } + + pile->clearSpread(); + + if (deck->isEmpty()) + { + redeal(); + return; + } + + // move the cards back on the deck, so we can have three new + for (int i = 0; i < pile->cardsLeft(); ++i) { + pile->at(i)->move(pile->x(), pile->y()); + } + + for (int flipped = 0; flipped < draw ; ++flipped) { + + Card *item = deck->nextCard(); + if (!item) { + kdDebug(11111) << "deck empty!!!\n"; + return; + } + pile->add(item, true, true); // facedown, nospread + if (flipped < draw - 1) + pile->addSpread(item); + // move back to flip + item->move(deck->x(), deck->y()); + + item->flipTo( int(pile->x()) + pile->dspread() * (flipped), int(pile->y()), 8 * (flipped + 1) ); + } + +} + +// Add cards from pile to deck, in reverse direction +void Klondike::redeal() { + + CardList pilecards = pile->cards(); + if (EasyRules) + // the remaining cards in deck should be on top + // of the new deck + pilecards += deck->cards(); + + for (int count = pilecards.count() - 1; count >= 0; --count) + { + Card *card = pilecards[count]; + card->setAnimated(false); + deck->add(card, true, false); // facedown, nospread + } + + redealt = true; +} + +void Klondike::deal() { + for(int round=0; round < 7; round++) + for (int i = round; i < 7; i++ ) + play[i]->add(deck->nextCard(), i != round, true); +} + +bool Klondike::cardClicked(Card *c) { + kdDebug(11111) << "card clicked " << c->name() << endl; + + if (Dealer::cardClicked(c)) + return true; + + if (c->source() == deck) { + pileClicked(deck); + return true; + } + + return false; +} + +void Klondike::pileClicked(Pile *c) { + kdDebug(11111) << "pile clicked " << endl; + Dealer::pileClicked(c); + + if (c == deck) { + deal3(); + } +} + +bool Klondike::startAutoDrop() +{ + bool pileempty = pile->isEmpty(); + if (!Dealer::startAutoDrop()) + return false; + if (pile->isEmpty() && !pileempty) + deal3(); + return true; +} + + +bool Klondike::isGameLost() const +{ + kdDebug( 11111 ) << "Is the game lost?" << endl; + + if (!deck->isEmpty()) { + kdDebug( 11111 ) << "We should only check this when the deck is exhausted." << endl; + return false; + } + + // Check whether top of the pile can be added to any of the target piles. + if ( !pile->isEmpty() ) { + for ( int i = 0; i < 4; ++i ) { + if ( target[ i ]->isEmpty() ) { + continue; + } + if ( pile->top()->suit() == target[ i ]->top()->suit() && + pile->top()->rank() - 1 == target[ i ]->top()->rank() ) { + kdDebug( 11111 ) << "No, the source pile's top card could be added to target pile " << i << endl; + return false; + } + } + } + + // Create a card list - srcPileCards - that contains all accessible + // cards in the pile and the deck. + CardList srcPileCards; + if ( EasyRules ) { + srcPileCards = pile->cards(); + } else { + /* In the draw3 mode, not every card in the source pile is + * accessible, but only every third one. + */ + for ( unsigned int i = 2; i < pile->cards().count(); i += 3 ) { + kdDebug( 11111 ) << "Found card "<< pile->cards()[i]->name()<< endl; + srcPileCards += pile->cards()[ i ]; + } + if ( !pile->cards().isEmpty() && pile->cards().count() % 3 != 0 ) { + kdDebug( 11111 ) << "Found last card "<< pile->cards()[pile->cards().count() - 1]->name()<< endl; + srcPileCards += pile->cards()[ pile->cards().count() - 1 ]; + } + } + + // Check all seven stores + for ( int i = 0; i < 7; ++i ) { + + // If this store is empty... + if ( play[ i ]->isEmpty() ) { + // ...check whether the pile contains a king we could move here. + CardList::ConstIterator it = srcPileCards.begin(); + CardList::ConstIterator end = srcPileCards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->rank() == Card::King ) { + kdDebug( 11111 ) << "No, the pile contains a king which we could move onto store " << i << endl; + return false; + } + } + + // ...check whether any of the other stores contains a (visible) + // king we could move here. + for ( int j = 0; j < 7; ++j ) { + if ( j == i || play[ j ]->isEmpty() ) { + continue; + } + const CardList cards = play[ j ]->cards(); + CardList::ConstIterator it = ++cards.begin(); + CardList::ConstIterator end = cards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->realFace() && ( *it )->rank() == Card::King ) { + kdDebug( 11111 ) << "No, store " << j << " contains a visible king which we could move onto store " << i << endl; + return false; + } + } + } + } else { // This store is not empty... + Card *topCard = play[ i ]->top(); + + // ...check whether the top card is an Ace (we can start a target) + if ( topCard->rank() == Card::Ace ) { + kdDebug( 11111 ) << "No, store " << i << " has an Ace, we could start a target pile." << endl; + return false; + } + + // ...check whether the top card can be added to any target pile + for ( int targetIdx = 0; targetIdx < 4; ++targetIdx ) { + if ( target[ targetIdx ]->isEmpty() ) { + continue; + } + if ( target[ targetIdx ]->top()->suit() == topCard->suit() && + target[ targetIdx ]->top()->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, store " << i << "'s top card could be added to target pile " << targetIdx << endl; + return false; + } + } + + // ...check whether the source pile contains a card which can be + // put onto this store. + CardList::ConstIterator it = srcPileCards.begin(); + CardList::ConstIterator end = srcPileCards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->isRed() != topCard->isRed() && + ( *it )->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, the pile contains a card which we could add to store " << i << endl; + return false; + } + } + + // ...check whether any of the other stores contains a visible card + // which can be put onto this store, and which is on top of an + // uncovered card. + for ( int j = 0; j < 7; ++j ) { + if ( j == i ) { + continue; + } + const CardList cards = play[ j ]->cards(); + CardList::ConstIterator it = cards.begin(); + CardList::ConstIterator end = cards.end(); + for ( ; it != end; ++it ) { + if ( ( *it )->realFace() && + ( *it )->isRed() != topCard->isRed() && + ( *it )->rank() == topCard->rank() - 1 ) { + kdDebug( 11111 ) << "No, store " << j << " contains a card which we could add to store " << i << endl; + return false; + } + } + } + } + } + kdDebug( 11111 ) << "Yep, all hope is lost." << endl; + return true; +} + +static class LocalDealerInfo0 : public DealerInfo +{ +public: + LocalDealerInfo0() : DealerInfo(I18N_NOOP("&Klondike"), 0) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(true, parent); } +} ldi0; + +static class LocalDealerInfo14 : public DealerInfo +{ +public: + LocalDealerInfo14() : DealerInfo(I18N_NOOP("Klondike (&draw 3)"), 13) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(false, parent); } +} ldi14; + + +#include "klondike.moc" diff --git a/kpat/klondike.h b/kpat/klondike.h new file mode 100644 index 00000000..b1b7e673 --- /dev/null +++ b/kpat/klondike.h @@ -0,0 +1,71 @@ +/***********************-*-C++-*-******** + + klondike.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + +// +// 7 positions, alternating red and black +// + + +****************************************/ + +#ifndef P_KLONDIKE +#define P_KLONDIKE + +#include "dealer.h" + +class KlondikePile; + +class Klondike : public Dealer { + Q_OBJECT + +public: + Klondike( bool easy, KMainWindow* parent=0, const char* name=0); + +public: + virtual void restart(); + virtual bool startAutoDrop(); + + void redeal(); // put pile back into deck + void deal(); + void deal3(); // move up to 3 cards from deck to pile + + virtual bool cardClicked(Card *); + virtual void pileClicked(Pile *c); + virtual void getHints(); + + virtual Card *demoNewCards(); + + bool tryToDrop(Card *t); + + virtual bool isGameLost() const; + +private: + bool EasyRules; + bool redealt; + + Pile* play[7]; + Pile* target[4]; + + KlondikePile *pile; + Deck* deck; + Card::Rank target_tops[4]; + bool noLongerNeeded(Card::Rank r, Card::Suit s ); +}; + +#endif diff --git a/kpat/kpat.desktop b/kpat/kpat.desktop new file mode 100644 index 00000000..f1df018b --- /dev/null +++ b/kpat/kpat.desktop @@ -0,0 +1,116 @@ +[Desktop Entry] +Name=Patience +Name[ar]=لعبة الصبر (Patience) +Name[az]=SÉ™bir +Name[be]=ПаÑÑŒÑÐ½Ñ +Name[bn]=পেশেনà§à¦¸ +Name[br]=Habaskter +Name[bs]=Pasijans +Name[ca]=Paciència +Name[cs]=Passiáns +Name[da]=Kabale +Name[de]=Patiencen +Name[el]=Πασιέντζα +Name[eo]=Solitero +Name[es]=Solitario +Name[et]=Kaardimängud +Name[eu]=Pazientzia +Name[fi]=Pasianssi +Name[fr]=Réussite +Name[gl]=Solitario +Name[hi]=पेशेनà¥à¤¸ +Name[hr]=Pasijans +Name[hu]=Pasziánsz +Name[is]=Kaplar +Name[it]=Solitario +Name[ja]=ソリティア +Name[lt]=Atkaklumas +Name[lv]=PacietÄ«ba +Name[mk]=ПаÑÐ¸Ñ˜Ð°Ð½Ñ +Name[mt]=PaÄ‹enzja +Name[nb]=Kabal +Name[ne]=धैरà¥à¤¯ +Name[nn]=Kabal +Name[pa]=ਪੇਟੀਨਸ਼ +Name[pl]=Pasjans +Name[pt]=Paciência +Name[pt_BR]=Paciência +Name[ro]=PasenÅ£e +Name[se]=Kabála +Name[sl]=Pasjansa +Name[sv]=Patiens +Name[ta]=பொறà¯à®®à¯ˆ +Name[tg]=ПаÑÑÐ½Ñ +Name[tr]=Sabır +Name[uk]=Ð¢ÐµÑ€Ð¿Ñ–Ð½Ð½Ñ +Name[ven]=Mukhondeleli +Name[wa]=Pacyince +Name[xh]= Nyamezela +Name[zh_CN]=è€å¿ƒ +Name[zh_TW]=Patience è€å¿ƒ +Name[zu]=Isineke +Exec=kpat %i %m -caption "%c" +Type=Application +Icon=kpat +DocPath=kpat/index.html +GenericName=Patience Card Game +GenericName[af]=Patience Kaart Speletjie +GenericName[be]=Картачны паÑÑŒÑÐ½Ñ +GenericName[bg]=ПаÑÐ¸Ð°Ð½Ñ +GenericName[bn]=পেশেনà§à¦¸ নামের তাস খেলা +GenericName[br]=C'hoari a habaskter +GenericName[bs]=Pasijans igra s kartama +GenericName[ca]=Partida de cartes paciència +GenericName[cs]=Karetní hra passiáns +GenericName[cy]=Gêm Cerdiau Patience +GenericName[da]=Kabale-kortspil +GenericName[de]=Patiencen legen +GenericName[el]=Παιχνίδι καÏτών patience +GenericName[eo]=Pacienca Kartludo +GenericName[es]=Juego de cartas de solitario +GenericName[et]=Kaardimäng +GenericName[eu]=Pazientziako karta-jokoa +GenericName[fa]=بازی Patience Card +GenericName[fi]=Pasianssikorttipeli +GenericName[fr]=Jeux de réussite +GenericName[gl]=Solitario, xogo de cartas +GenericName[he]=משחק ×§×œ×¤×™× +GenericName[hi]=पेशेंस ताश का खेल +GenericName[hr]=KartaÅ¡ka igra pasijansa +GenericName[hu]=Pasziánsz +GenericName[is]=Kaplar +GenericName[it]=Solitario +GenericName[ja]=Patience カードゲーム +GenericName[km]=ល្បែង​បៀ Patience +GenericName[ko]=ì¹´ë“œ ë†€ì´ +GenericName[lt]=KantrybÄ—s kortų žaidimas +GenericName[lv]=PacietÄ«bas kÄrÅ¡u spÄ“le +GenericName[mk]=ПаÑÐ¸Ñ˜Ð°Ð½Ñ - игра Ñо карти +GenericName[nb]=Kabalspill +GenericName[nds]=Patiencen leggen +GenericName[ne]=धैरà¥à¤¯ कारà¥à¤¡ खेल +GenericName[nl]=Patience-kaartspel +GenericName[nn]=Kabalspel +GenericName[pl]=Gra karciana Patience +GenericName[pt]=Jogo de Paciência +GenericName[pt_BR]=Jogo de Cartas Paciência +GenericName[ro]=Joc de pasenÅ£e +GenericName[ru]=ПаÑÑŒÑÐ½Ñ +GenericName[se]=Kabálaspeallu +GenericName[sk]=Kartová hra Patience +GenericName[sl]=Igra s kartami Patience +GenericName[sr]=Игра Ñа картама Patience +GenericName[sr@Latn]=Igra sa kartama Patience +GenericName[sv]=Patienskortspel +GenericName[ta]=பொறà¯à®®à¯ˆà®¯à®¾à®© சீடà¯à®Ÿà¯ விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии ПаÑÑнÑи Кортӣ +GenericName[tr]=Sabır Kart Oyunu +GenericName[uk]=Гра в карти Ð¢ÐµÑ€Ð¿Ñ–Ð½Ð½Ñ +GenericName[ven]=Mutambo wa Magarata wa Mukondeleli +GenericName[wa]=CwÃ¥rdjeu d' pacyince +GenericName[xh]=Ikhadi lomdlalo wokunyamezela +GenericName[zh_CN]=考验è€å¿ƒçš„ç‰Œç±»æ¸¸æˆ +GenericName[zh_TW]=è€å¿ƒçš„紙牌éŠæˆ² +GenericName[zu]=Umdlalo wesineke wamakhadi +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;CardGame; diff --git a/kpat/kpatui.rc b/kpat/kpatui.rc new file mode 100644 index 00000000..218d9c39 --- /dev/null +++ b/kpat/kpatui.rc @@ -0,0 +1,40 @@ + + + + + &Game + + + + + + + + + + + + + + + &Edit + + + &Settings + + + + + + + +Main Toolbar + + + + + + + + + diff --git a/kpat/main.cpp b/kpat/main.cpp new file mode 100644 index 00000000..58ad8652 --- /dev/null +++ b/kpat/main.cpp @@ -0,0 +1,73 @@ +/* + patience -- main program + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + */ + +#include +#include +#include + +#include "version.h" +#include "pwidget.h" + +static const char description[] = I18N_NOOP("KDE Patience Game"); + +static KCmdLineOptions options[] = +{ + { "+file", I18N_NOOP("File to load"), 0 }, + KCmdLineLastOption +}; + +int main( int argc, char **argv ) +{ + KAboutData aboutData( "kpat", I18N_NOOP("KPatience"), + KPAT_VERSION, description, KAboutData::License_GPL, + "(c) 1995, Paul Olav Tvete\n" + "(c) 2000 Stephan Kulow"); + aboutData.addAuthor("Paul Olav Tvete"); + aboutData.addAuthor("Mario Weilguni",0,"mweilguni@kde.org"); + aboutData.addAuthor("Matthias Ettrich",0,"ettrich@kde.org"); + aboutData.addAuthor("Rodolfo Borges",I18N_NOOP("Some Game Types"),"barrett@9hells.org"); + aboutData.addAuthor("Peter H. Ruegg",0,"kpat@incense.org"); + aboutData.addAuthor("Michael Koch", I18N_NOOP("Bug fixes"), "koch@kde.org"); + aboutData.addAuthor("Marcus Meissner", I18N_NOOP("Shuffle algorithm for game numbers"), + "mm@caldera.de"); + aboutData.addAuthor("Shlomi Fish", I18N_NOOP("Freecell Solver"), "shlomif@vipe.technion.ac.il"); + aboutData.addAuthor("Stephan Kulow", I18N_NOOP("Rewrite and current maintainer"), + "coolo@kde.org"); + aboutData.addAuthor("Erik Sigra", I18N_NOOP("Improved Klondike"), "sigra@home.se"); + aboutData.addAuthor("Josh Metzler", I18N_NOOP("Spider Implementation"), "joshdeb@metzlers.org"); + aboutData.addAuthor("Maren Pakura", I18N_NOOP("Documentation"), "maren@kde.org"); + aboutData.addAuthor("Inge Wallin", I18N_NOOP("Bug fixes"), "inge@lysator.liu.se"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions (options); + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (a.isRestored()) + RESTORE(pWidget) + else { + pWidget *w = new pWidget; + if (args->count()) + w->openGame(args->url(0)); + a.setMainWidget(w); + w->show(); + } + return a.exec(); +} diff --git a/kpat/mod3.cpp b/kpat/mod3.cpp new file mode 100644 index 00000000..c69aa8e4 --- /dev/null +++ b/kpat/mod3.cpp @@ -0,0 +1,312 @@ +/*--------------------------------------------------------------------------- + + mod3.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "mod3.h" +#include "cardmaps.h" +#include +#include "deck.h" +#include + +//-------------------------------------------------------------------------// + +Mod3::Mod3( KMainWindow* parent, const char* _name) + : Dealer( parent, _name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + const int margin = cardMap::CARDY() / 3; + + // This patience uses 2 deck of cards. + deck = Deck::new_deck( this, 2); + deck->move(8 + dist_x * 8 + 20, 8 + dist_y * 3 + margin); + + connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + + aces = new Pile(50, this); + aces->move(16 + dist_x * 8, 8 + dist_y / 2); + aces->setTarget(true); + aces->setCheckIndex(2); + aces->setAddFlags(Pile::addSpread | Pile::several); + + for ( int r = 0; r < 4; r++ ) { + for ( int c = 0; c < 8; c++ ) { + stack[r][c] = new Pile ( r * 10 + c + 1, this ); + stack[r][c]->move( 8 + dist_x * c, + 8 + dist_y * r + margin * ( r == 3 )); + + // The first 3 rows are the playing field, the fourth is the store. + if ( r < 3 ) { + stack[r][c]->setCheckIndex( 0 ); + stack[r][c]->setTarget(true); + } else { + stack[r][c]->setAddFlags( Pile::addSpread ); + stack[r][c]->setCheckIndex( 1 ); + } + } + } + + setTakeTargetForHints(true); + setActions(Dealer::Hint | Dealer::Demo ); +} + + +//-------------------------------------------------------------------------// + + +bool Mod3::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const +{ + // kdDebug(11111) << "checkAdd " << checkIndex << " " << c1->top()->name() << " " << c1->index() << " " << c1->index() / 10 << endl; + if (checkIndex == 0) { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return (c2->rank() == ( ( c1->index() / 10 ) + 2 ) ); + + kdDebug(11111) << "not empty\n"; + + if (c1->top()->suit() != c2->suit()) + return false; + + kdDebug(11111) << "same suit\n"; + if (c2->rank() != (c1->top()->rank()+3)) + return false; + + kdDebug(11111) << "+3 " << c1->cardsLeft() << " " << c1->top()->rank() << " " << c1->index()+1 << endl; + if (c1->cardsLeft() == 1) + return (c1->top()->rank() == ((c1->index() / 10) + 2)); + + kdDebug(11111) << "+1\n"; + + return true; + } else if (checkIndex == 1) { + return c1->isEmpty(); + } else if (checkIndex == 2) { + return cl.first()->rank() == Card::Ace; + } else return false; +} + + +bool Mod3::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const +{ + return (checkIndex == 0 && c1->isEmpty() + && c2.first()->rank() == (c1->index()+1)); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::restart() +{ + deck->collectAndShuffle(); + deal(); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::dealRow(int row) +{ + if (deck->isEmpty()) + return; + + for (int c = 0; c < 8; c++) { + Card *card; + + card = deck->nextCard(); + stack[row][c]->add (card, false, true); + } +} + + +void Mod3::deckClicked(Card*) +{ + kdDebug(11111) << "deck clicked " << deck->cardsLeft() << endl; + if (deck->isEmpty()) + return; + + unmarkAll(); + dealRow(3); + takeState(); +} + + +//-------------------------------------------------------------------------// + + +void Mod3::deal() +{ + unmarkAll(); + CardList list = deck->cards(); +/* for (CardList::Iterator it = list.begin(); it != list.end(); ++it) + if ((*it)->rank() == Card::Ace) { + aces->add(*it); + (*it)->hide(); + } +*/ + kdDebug(11111) << "init " << aces->cardsLeft() << " " << deck->cardsLeft() << endl; + + for (int r = 0; r < 4; r++) + dealRow(r); +} + +Card *Mod3::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + deckClicked(0); + return stack[3][0]->top(); +} + +bool Mod3::startAutoDrop() { + return false; +} + +bool Mod3::isGameLost() const +{ + int n,row,col; + kdDebug(11111) << "isGameLost ?"<< endl; + + bool nextTest=false; + + // If there is an empty stack or an ace below, the game is not lost. + for (col=0; col < 8; col++){ + if (stack[3][col]->isEmpty() + || stack[3][col]->at(0)->rank() == Card::Ace) + return false; + } + + // Ok, so no empty stack below. + // If there is neither an empty stack on the board (an ace counts + // as this) nor a card placed in the correct row, all is lost. + // Otherwise we have to do more tests. + for (n = 0; n < 24; n++) { + row = n / 8; + col = n % 8; + + // If there is a stack on the board that is either empty or + // contains an ace, the game is not finished. + if (stack[row][col]->isEmpty() + || stack[row][col]->at(0)->rank() == Card::Ace) { + nextTest = true; + break; + } + + // If there is a card that is correctly placed, the game is + // not lost. + if (stack[row][col]->at(0)->rank() == Card::Two + row) { + nextTest = true; + break; + } + } + if (!nextTest) + return true; + + // If there are more cards in the deck, the game is not lost. + if (!deck->isEmpty()) + return false; + + int n2, row2, col2, col3; + Card *ctop; + Card *card; + + // For all stacks on the board, check if: + // + for (n = 0; n < 24; n++){ + row = n / 8; + col = n % 8; + + // Empty stack: Can we move a card there? + if (stack[row][col]->isEmpty()) { + // Can we move a card from below? + for (col3=0; col3 < 8; col3++) { + if (stack[3][col3]->top()->rank() == (Card::Two+row)) + return false; + } + + // Can we move a card from another row? + for (n2 = 0; n2 < 16; n2++) { + row2 = (row + 1 + (n2 / 8)) % 3; + col2 = n2 % 8; + + if (stack[row2][col2]->isEmpty()) + continue; + if (stack[row2][col2]->top()->rank() == (Card::Two + row)) + return false; + } + } + else { + // Non-empty stack. + ctop = stack[row][col]->top(); + kdDebug(11111) << "considering ["<name() << flush; + + // Card not in its final position? Then we can't build on it. + if (stack[row][col]->at(0)->rank() != Card::Two + row) + continue; + + // Can we move a card from below here? + for (col3 = 0; col3 < 8; col3++) { + card = stack[3][col3]->top(); + if (card->suit() == ctop->suit() + && card->rank() == ctop->rank() + 3) + return false; + } + kdDebug(11111) <<" Can't stack from bottom row" << flush; + + // Can we move a card from another stack here? + for (int n_2 = 1; n_2 < 24; n_2++) { + n2 = (n + n_2) % 24; + row2 = n2 / 8; + col2 = n2 % 8; + + if (stack[row2][col2]->isEmpty()) + continue; + + card = stack[row2][col2]->top(); + + // Only consider cards that are not on top of other cards. + if (stack[row2][col2]->indexOf(card) != 0) + continue; + + if (card->suit() == ctop->suit() + && card->rank() == ctop->rank() + 3) + return false; + } + } + } + + return true; +} + + +static class LocalDealerInfo5 : public DealerInfo +{ +public: + LocalDealerInfo5() : DealerInfo(I18N_NOOP("M&od3"), 5) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Mod3(parent); } +} ldi5; + +//-------------------------------------------------------------------------// + +#include"mod3.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/mod3.h b/kpat/mod3.h new file mode 100644 index 00000000..17ff6aa2 --- /dev/null +++ b/kpat/mod3.h @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------- + + mod3.cpp implements a patience card game + + Copyright (C) 1997 Rodolfo Borges + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + (I don't know a name for this one, if you do, please tell me.) + +---------------------------------------------------------------------------*/ + +#ifndef _MOD3_H_ +#define _MOD3_H_ + +#include "dealer.h" + +class Mod3 : public Dealer +{ + Q_OBJECT + +public: + Mod3( KMainWindow* parent=0, const char* name=0); + + void deal(); + + virtual void restart(); + virtual bool isGameLost() const; + virtual bool startAutoDrop(); + +public slots: + void deckClicked(Card *c); + +protected: + virtual Card *demoNewCards(); + +private: // functions + virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + + void dealRow(int row); + +private: + Deck *deck; + + Pile *stack[4][8]; + Pile *aces; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/napoleon.cpp b/kpat/napoleon.cpp new file mode 100644 index 00000000..ffdf245c --- /dev/null +++ b/kpat/napoleon.cpp @@ -0,0 +1,204 @@ +/* + napoleon.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + + */ + +#include "napoleon.h" +#include +#include "deck.h" +#include "cardmaps.h" + +Napoleon::Napoleon( KMainWindow* parent, const char* _name ) + : Dealer( parent, _name ) +{ + deck = Deck::new_deck( this ); + connect(deck, SIGNAL(clicked(Card *)), SLOT(deal1(Card*))); + + pile = new Pile( 1, this ); + pile->setAddFlags( Pile::disallow ); + + for (int i = 0; i < 4; i++) + { + store[i] = new Pile( 2 + i, this ); + store[i]->setCheckIndex( 0 ); + target[i] = new Pile( 6 + i, this); + target[i]->setRemoveFlags( Pile::disallow ); + target[i]->setCheckIndex(2); + target[i]->setTarget(true); + } + + const int dist_store = cardMap::CARDX() * 55 / 100; + const int dist_target = dist_store / 2; + const int centre_x = 10 + cardMap::CARDX() + dist_store; + const int centre_y = 10 + cardMap::CARDY() + dist_store; + + deck->move( centre_x + cardMap::CARDX() * 47 / 10, centre_y + cardMap::CARDY() + dist_store); + pile->move( centre_x + cardMap::CARDX() * 33 / 10, centre_y + cardMap::CARDY() + dist_store); + + centre = new Pile( 10, this ); + centre->setRemoveFlags( Pile::disallow ); + centre->setCheckIndex(1); + centre->setTarget(true); + + store[0]->move( centre_x, centre_y - cardMap::CARDY() - dist_store ); + store[1]->move( centre_x + cardMap::CARDX() + dist_store, centre_y); + store[2]->move( centre_x, centre_y + cardMap::CARDY() + dist_store ); + store[3]->move( centre_x - cardMap::CARDX() - dist_store, centre_y); + target[0]->move( centre_x - cardMap::CARDX() - dist_target, centre_y - cardMap::CARDY() - dist_target ); + target[1]->move( centre_x + cardMap::CARDX() + dist_target, centre_y - cardMap::CARDY() - dist_target); + target[2]->move( centre_x + cardMap::CARDX() + dist_target, centre_y + cardMap::CARDY() + dist_target); + target[3]->move( centre_x - cardMap::CARDX() - dist_target, centre_y + cardMap::CARDY() + dist_target); + centre->move(centre_x, centre_y); + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Napoleon::restart() { + deck->collectAndShuffle(); + deal(); +} + +bool Napoleon::CanPutTarget( const Pile* c1, const CardList& cl) const { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return c2->rank() == Card::Seven; + else + return (c2->rank() == c1->top()->rank() + 1); +} + +bool Napoleon::CanPutCentre( const Pile* c1, const CardList& cl) const { + Card *c2 = cl.first(); + + if (c1->isEmpty()) + return c2->rank() == Card::Six; + + if (c1->top()->rank() == Card::Ace) + return (c2->rank() == Card::Six); + else + return (c2->rank() == c1->top()->rank() - 1); +} + +bool Napoleon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const +{ + switch (checkIndex) { + case 0: + return c1->isEmpty(); + case 1: + return CanPutCentre(c1, c2); + case 2: + return CanPutTarget(c1, c2); + default: + return false; + } +} + +void Napoleon::deal() { + if (deck->isEmpty()) + return; + + for (int i=0; i<4; i++) + store[i]->add(deck->nextCard(), false, false); +} + +void Napoleon::deal1(Card *) { + Card *c = deck->nextCard(); + if (!c) + return; + pile->add(c, true, false); + c->move(deck->x(), deck->y()); + c->flipTo(int(pile->x()), int(pile->y()), 8); +} + +Card *Napoleon::demoNewCards() +{ + if (deck->isEmpty()) + return 0; + deal1(0); + return pile->top(); +} + +void Napoleon::getHints() { + CardList cards; + for (int i = 0; i < 4; i++) + { + if (!store[i]->isEmpty()) + cards.append(store[i]->top()); + } + if (pile->top()) + cards.append(pile->top()); + + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + CardList empty; + empty.append(*it); + if (CanPutCentre(centre, empty)) { + newHint(new MoveHint(*it, centre)); + continue; + } + for (int i = 0; i < 4; i++) { + if (CanPutTarget(target[i], empty)) { + newHint(new MoveHint(*it, target[i])); + break; + } + } + } + if (pile->isEmpty()) + return; + + for (int i = 0; i < 4; i++) { + if (store[i]->isEmpty()) { + newHint(new MoveHint(pile->top(), store[i])); + return; + } + } +} + +bool Napoleon::isGameLost() const +{ + CardList cards; + for (int i = 0; i < 4; i++) + { + if (store[i]->isEmpty()) + return false; + else + cards.append(store[i]->top()); + } + + if (pile->top()) + cards.append(pile->top()); + + for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) { + CardList empty; + empty.append(*it); + if(CanPutCentre(centre,empty)) return false; + for(int i=0; i<4; i++) + if(CanPutTarget(target[i],empty)) return false; + } + + return (deck->isEmpty()); +} + + + +static class LocalDealerInfo4 : public DealerInfo +{ +public: + LocalDealerInfo4() : DealerInfo(I18N_NOOP("&Napoleon's Tomb"), 4) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Napoleon(parent); } +} ldi3; + +#include "napoleon.moc" diff --git a/kpat/napoleon.h b/kpat/napoleon.h new file mode 100644 index 00000000..a89f68b9 --- /dev/null +++ b/kpat/napoleon.h @@ -0,0 +1,56 @@ +/***********************-*-C++-*-******** + + napoleon.cpp implements a patience card game + + Copyright (C) 1995 Paul Olav Tvete + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +****************************************/ + + +#ifndef P_NAPOLEON +#define P_NAPOLEON + +#include "dealer.h" + +class Napoleon : public Dealer { + Q_OBJECT +public: + Napoleon (KMainWindow* parent=0, const char* name=0); + + virtual void restart(); + virtual void getHints(); + virtual Card *demoNewCards(); + virtual bool startAutoDrop() { return false; } + virtual bool isGameLost() const; + +public slots: + void deal1(Card *c); + +private: + void deal(); + + bool CanPutTarget( const Pile *c1, const CardList& c2) const; + bool CanPutCentre( const Pile* c1, const CardList& c2) const; + + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + + Pile* pile; + Pile* target[4]; + Pile* centre; + Pile* store[4]; + Deck* deck; +}; + +#endif diff --git a/kpat/pile.cpp b/kpat/pile.cpp new file mode 100644 index 00000000..dd419580 --- /dev/null +++ b/kpat/pile.cpp @@ -0,0 +1,463 @@ +#include "pile.h" +#include "dealer.h" +#include +#include +#include "cardmaps.h" +#include +#include "speeds.h" + +const int Pile::RTTI = 1002; + +const int Pile::Default = 0x0000; +const int Pile::disallow = 0x0001; +const int Pile::several = 0x0002; // default: move one card + +// Add-flags +const int Pile::addSpread = 0x0100; + +// Remove-flags +const int Pile::autoTurnTop = 0x0200; +const int Pile::wholeColumn = 0x0400; + + + +Pile::Pile( int _index, Dealer* _dealer) + : QCanvasRectangle( _dealer->canvas() ), + m_dealer(_dealer), + m_atype(Custom), + m_rtype(Custom), + myIndex(_index), + _target(false) +{ + // Make the patience aware of this pile. + dealer()->addPile(this); + + QCanvasRectangle::setVisible(true); // default + _checkIndex = -1; + m_addFlags = 0; + m_removeFlags = 0; + + setBrush(Qt::black); + setPen(QPen(Qt::black)); + + setZ(0); + initSizes(); +} + + +void Pile::initSizes() +{ + setSpread( cardMap::CARDY() / 5 + 1 ); + setHSpread( cardMap::CARDX() / 9 + 1 ); + setDSpread( cardMap::CARDY() / 8 ); + + setSize( cardMap::CARDX(), cardMap::CARDY() ); +} + +void Pile::setType(PileType type) +{ + setAddType(type); + setRemoveType(type); +} + +void Pile::setAddType(PileType _type) +{ + m_atype = _type; + switch (_type) { + case Custom: + case FreeCell: + break; + case KlondikeTarget: + setTarget(true); + break; + case KlondikeStore: + case GypsyStore: + case FreecellStore: + setAddFlags(Pile::addSpread | Pile::several); + break; + } +} + +void Pile::setRemoveType(PileType _type) +{ + m_rtype = _type; + switch (_type) { + case Custom: + break; + case KlondikeTarget: + setRemoveFlags(Pile::disallow); + break; + case KlondikeStore: + case GypsyStore: + case FreeCell: + break; + case FreecellStore: + setRemoveFlags(Pile::several | Pile::autoTurnTop); + break; + } +} + +Pile::~Pile() +{ + dealer()->removePile(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if ((*it)->source() != this) { + int i = -13; + if ((*it)->source()) + i = (*it)->source()->index(); + kdDebug(11111) << "pile doesn't match " << index() << " - " << i << endl; + } + (*it)->setSource(0); + } +} + +void Pile::resetCache() +{ + cache.resize(0, 0); + cache_selected.resize(0, 0); +} + +void Pile::drawShape ( QPainter & painter ) +{ + if (isSelected()) { + if (cache.isNull()) + dealer()->drawPile(cache, this, false); + painter.drawPixmap(int(x()), int(y()), cache); + } else { + if (cache_selected.isNull()) + dealer()->drawPile(cache_selected, this, true); + painter.drawPixmap(int(x()), int(y()), cache_selected); + } +} + +bool Pile::legalAdd( const CardList& _cards ) const +{ + if ( m_addFlags & disallow ) + return false; + + if ( !( m_addFlags & several ) && _cards.count() > 1 ) + return false; + + // getHint removes cards without turning, so it could be it + // checks later if cards can be added to a face down card + if (top() && !top()->realFace()) + return false; + + switch (addType()) { + case Custom: + return dealer()->checkAdd( checkIndex(), this, _cards ); + break; + case KlondikeTarget: + return add_klondikeTarget(_cards); + break; + case FreecellStore: + case KlondikeStore: + return add_klondikeStore(_cards); + break; + case GypsyStore: + return add_gypsyStore(_cards); + break; + case FreeCell: + return add_freeCell(_cards); + } + return false; +} + +bool Pile::legalRemove(const Card *c) const +{ + if ( m_removeFlags & disallow ) { + return false; + } + if ( !( m_removeFlags & several ) && top() != c) + return false; + + switch (removeType()) { + case Custom: + return dealer()->checkRemove( checkIndex(), this, c); + break; + case KlondikeTarget: + case GypsyStore: + case KlondikeStore: + break; + case FreecellStore: + return remove_freecellStore(c); + break; + case FreeCell: + return (top() == c); + break; + } + return true; +} + +void Pile::setVisible(bool vis) +{ + QCanvasRectangle::setVisible(vis); + dealer()->enlargeCanvas(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->setVisible(vis); + dealer()->enlargeCanvas(*it); + } +} + +void Pile::moveBy(double dx, double dy) +{ + QCanvasRectangle::moveBy(dx, dy); + dealer()->enlargeCanvas(this); + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->moveBy(dx, dy); + dealer()->enlargeCanvas(*it); + } +} + +int Pile::indexOf(const Card *c) const +{ + assert(c->source() == this); + return m_cards.findIndex(const_cast(c)); // the list is of non-const cards +} + +Card *Pile::at(int index) const +{ + if (index < 0 || index >= int(m_cards.count())) + return 0; + return *m_cards.at(index); +} + +// Return the top card of this pile. +// + +Card *Pile::top() const +{ + if (m_cards.isEmpty()) + return 0; + + return m_cards.last(); +} + +void Pile::clear() +{ + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + (*it)->setSource(0); + } + m_cards.clear(); +} + +void Pile::add( Card *_card, int index) +{ + if (_card->source() == this) + return; + + Pile *source = _card->source(); + if (source) { + _card->setTakenDown(source->target() && !target()); + source->remove(_card); + } + + _card->setSource(this); + + if (index == -1) + m_cards.append(_card); + else { + while (m_cards.count() <= uint(index)) + m_cards.append(0); + assert(m_cards[index] == 0); + m_cards[index] = _card; + } +} + + +// Return the number of pixels in x and y that the card should be +// offset from the start position of the pile. +// +// Note: Default is to only have vertical spread (Y direction). + +QSize Pile::cardOffset( bool _spread, bool _facedown, const Card *before) const +{ + if (_spread) { + if (_facedown) + return QSize(0, dspread()); + else { + if (before && !before->isFaceUp()) + return QSize(0, dspread()); + else + return QSize(0, spread()); + } + } + + return QSize(0, 0); +} + +/* override cardtype (for initial deal ) */ +void Pile::add( Card* _card, bool _facedown, bool _spread ) +{ + if (!_card) + return; + + // The top card + Card *t = top(); + + // If this pile is visible, then also show the card. + if (isVisible()) + _card->show(); + else + _card->hide(); + + _card->turn( !_facedown ); + + QSize offset = cardOffset(_spread, _facedown, t); + + int x2, y2, z2; + + if (t) { + x2 = int(t->realX() + offset.width()); + y2 = int(t->realY() + offset.height()); + z2 = int(t->realZ() + 1); + } else { + x2 = int(x()); + y2 = int(y()); + z2 = int(z() + 1); + } + + add(_card); + + if (_facedown || !isVisible()) { + _card->move( x2, y2 ); + _card->setZ( z2 ); + } else { + _card->moveTo(x2, y2, z2, STEPS_INITIALDEAL); + } + + dealer()->enlargeCanvas(_card); +} + +void Pile::remove(Card *c) +{ + assert(m_cards.contains(c)); + m_cards.remove(c); +} + +void Pile::hideCards( const CardList & cards ) +{ + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + m_cards.remove(*it); +} + +void Pile::unhideCards( const CardList & cards ) +{ + for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it) + m_cards.append(*it); +} + +CardList Pile::cardPressed(Card *c) +{ + CardList result; + + if (!legalRemove(c)) + return result; + + int below = -1; + + if (!c->isFaceUp()) + return result; + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if (c == *it) { + below = 0; + } + if (below >= 0) { + (*it)->setAnimated(false); + (*it)->setZ(128 + below); + below++; + result.append(*it); + } + } + return result; +} + +void Pile::moveCards(CardList &cl, Pile *to) +{ + if (!cl.count()) + return; + + for (CardList::Iterator it = cl.begin(); it != cl.end(); ++it) + to->add(*it); + + if (m_removeFlags & autoTurnTop && top()) { + Card *t = top(); + if (!t->isFaceUp()) { + t->flipTo(int(t->x()), int(t->y()), 8); + canvas()->update(); + } + } + + to->moveCardsBack(cl, false); +} + +void Pile::moveCardsBack(CardList &cl, bool anim) +{ + if (!cl.count()) + return; + + Card *c = cl.first(); + + Card *before = 0; + QSize off; + + int steps = STEPS_MOVEBACK; + if (!anim) + steps = 0; + + for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it) + { + if (c == *it) { + if (before) { + off = cardOffset(m_addFlags & Pile::addSpread, false, before); + c->moveTo( before->realX() + off.width(), + before->realY() + off.height(), + before->realZ() + 1, steps); + dealer()->enlargeCanvas(c); + } + else { + c->moveTo( int(x()), int(y()), int(z()) + 1, steps); + } + break; + } else + before = *it; + } + + before = c; + CardList::Iterator it = cl.begin(); // == c + ++it; + + off = cardOffset(m_addFlags & Pile::addSpread, false, 0); + + for (; it != cl.end(); ++it) + { + (*it)->moveTo( before->realX() + off.width(), + before->realY() + off.height(), + before->realZ() + 1, steps); + dealer()->enlargeCanvas(*it); + before = *it; + } +} + +bool Pile::cardClicked(Card *c) +{ + emit clicked(c); + return false; +} + +bool Pile::cardDblClicked(Card *c) +{ + emit dblClicked(c); + return false; +} + +#include "pile.moc" diff --git a/kpat/pile.h b/kpat/pile.h new file mode 100644 index 00000000..b2b553b0 --- /dev/null +++ b/kpat/pile.h @@ -0,0 +1,154 @@ +#ifndef _PILE_H +#define _PILE_H + + +#include "card.h" +#include + + +class Dealer; + + +/** + * + * Pile -- A pile on the board that can hold cards. + * + */ + +class Pile : public QObject, public QCanvasRectangle +{ + Q_OBJECT + +public: + + enum PileType { Custom, + KlondikeTarget, + KlondikeStore, + GypsyStore, + FreeCell, + FreecellStore}; + + // Add- and remove-flags + static const int Default; + static const int disallow; + static const int several; // default: move one card + + // Add-flags + static const int addSpread; + + // Remove-flags + static const int autoTurnTop; + static const int wholeColumn; + + Pile( int _index, Dealer* parent = 0); + virtual ~Pile(); + + Dealer *dealer() const { return m_dealer; } + CardList cards() const { return m_cards; } + + bool legalAdd(const CardList &c ) const; + bool legalRemove(const Card *c) const; + + virtual void moveCards(CardList &c, Pile *to = 0); + void moveCardsBack(CardList &c, bool anim = true); + + void setAddFlags( int flag ) { m_addFlags = flag; } + void setRemoveFlags( int flag ) { m_removeFlags = flag; } + + void setCheckIndex( int index ) { _checkIndex = index; } + virtual int checkIndex() const { return _checkIndex; } + + void setTarget(bool t) { _target = t; } + bool target() const { return _target; } + + CardList cardPressed(Card *c); + + Card *top() const; + + void add( Card *c, bool facedown, bool spread); // for initial deal + void add( Card *c, int index = -1); + void remove(Card *c); + void clear(); + + int index() const { return myIndex; } + bool isEmpty() const { return m_cards.count() == 0; } + + virtual void drawShape ( QPainter & p ); + static const int RTTI; + + virtual int rtti() const { return RTTI; } + + virtual void setVisible(bool vis); + virtual void moveBy(double dx, double dy); + + int cardsLeft() const { return m_cards.count(); } + + int indexOf(const Card *c) const; + Card *at(int index) const; + + void hideCards( const CardList & cards ); + void unhideCards( const CardList & cards ); + + virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const; + + void resetCache(); + virtual void initSizes(); + + void setType( PileType t); + void setAddType( PileType t); + void setRemoveType( PileType t); + PileType addType() const { return m_atype; } + PileType removeType() const { return m_rtype; } + + // pile_algorithms + bool add_klondikeTarget( const CardList& c2 ) const; + bool add_klondikeStore( const CardList& c2 ) const; + bool add_gypsyStore( const CardList& c2 ) const; + bool add_freeCell( const CardList& c2) const; + + bool remove_freecellStore( const Card *c) const; + + // The spread properties. + int spread() const { return _spread; } + void setSpread(int s) { _spread = s; } + int dspread() const { return _dspread; } + void setDSpread(int s) { _dspread = s; } + int hspread() const { return _hspread; } + void setHSpread(int s) { _hspread = s; } + +public slots: + virtual bool cardClicked(Card *c); + virtual bool cardDblClicked(Card *c); + +signals: + void clicked(Card *c); + void dblClicked(Card *c); + +protected: + int m_removeFlags; + int m_addFlags; + CardList m_cards; + +private: + // Reference to the patience this pile is a part of. + Dealer *m_dealer; + + // Properties of the pile. + PileType m_atype; // Addtype + PileType m_rtype; // Removetype + int _spread; + int _hspread; + int _dspread; + + int _checkIndex; + int myIndex; + bool _target; + + // Graphics + KPixmap cache; + KPixmap cache_selected; +}; + +typedef QValueList PileList; + +#endif diff --git a/kpat/pile_algorithms.cpp b/kpat/pile_algorithms.cpp new file mode 100644 index 00000000..7ca3469c --- /dev/null +++ b/kpat/pile_algorithms.cpp @@ -0,0 +1,69 @@ +#include "pile.h" +#include + +bool Pile::add_klondikeTarget( const CardList& c2 ) const +{ + Card *newone = c2.first(); + if (isEmpty()) + return (newone->rank() == Card::Ace); + + return (newone->rank() == top()->rank() + 1) + && (top()->suit() == newone->suit()); +} + +bool Pile::add_klondikeStore( const CardList& c2 ) const +{ + Card *newone = c2.first(); + if (isEmpty()) { + return (newone->rank() == Card::King); + } + + return (newone->rank() == top()->rank() - 1) + && (top()->isRed() != newone->isRed()); +} + +bool Pile::add_gypsyStore( const CardList& c2) const +{ + Card *newone = c2.first(); + if (isEmpty()) + return true; + + return (newone->rank() == top()->rank() - 1) + && (top()->isRed() != newone->isRed()); +} + +bool Pile::add_freeCell( const CardList & cards) const +{ + return (cards.count() == 1 && isEmpty()); +} + +bool Pile::remove_freecellStore( const Card *c) const +{ + // ok if just one card + if (c == top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->isRed() != before->isRed()))) + { + kdDebug(11111) << c->name() << " - " << before->name() << endl; + return false; + } + if (c == top()) + return true; + before = c; + } + + return true; +} + diff --git a/kpat/pwidget.cpp b/kpat/pwidget.cpp new file mode 100644 index 00000000..dae69f60 --- /dev/null +++ b/kpat/pwidget.cpp @@ -0,0 +1,560 @@ +/* + patience -- main program + Copyright (C) 1995 Paul Olav Tvete + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation. + + This file is provided AS IS with no warranties of any kind. The author + shall have no liability with respect to the infringement of copyrights, + trade secrets or any patents by this file or any part thereof. In no + event will the author be liable for any lost revenue or profits or + other special, indirect and consequential damages. + + + Heavily modified by Mario Weilguni +*/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwidget.h" +#include "version.h" +#include "dealer.h" +#include "cardmaps.h" +#include "speeds.h" +#include "gamestatsimpl.h" + + +static pWidget *current_pwidget = 0; + + +void saveGame(int) { + current_pwidget->saveGame(); +} + +pWidget::pWidget() + : KMainWindow(0, "pwidget"), dill(0) +{ + current_pwidget = this; + // KCrash::setEmergencySaveFunction(::saveGame); + KStdAction::quit(kapp, SLOT(quit()), actionCollection(), "game_exit"); + + undo = KStdAction::undo(this, SLOT(undoMove()), + actionCollection(), "undo_move"); + undo->setEnabled(false); + (void)KStdAction::openNew(this, SLOT(newGame()), + actionCollection(), "new_game"); + (void)KStdAction::open(this, SLOT(openGame()), + actionCollection(), "open"); + recent = KStdAction::openRecent(this, SLOT(openGame(const KURL&)), + actionCollection(), "open_recent"); + recent->loadEntries(KGlobal::config()); + (void)KStdAction::saveAs(this, SLOT(saveGame()), + actionCollection(), "save"); + (void)new KAction(i18n("&Choose Game..."), 0, this, SLOT(chooseGame()), + actionCollection(), "choose_game"); + (void)new KAction(i18n("Restart &Game"), QString::fromLatin1("reload"), 0, + this, SLOT(restart()), + actionCollection(), "restart_game"); + (void)KStdAction::help(this, SLOT(helpGame()), actionCollection(), "help_game"); + + games = new KSelectAction(i18n("&Game Type"), 0, this, + SLOT(newGameType()), + actionCollection(), "game_type"); + QStringList list; + QValueList::ConstIterator it; + uint max_type = 0; + + for (it = DealerInfoList::self()->games().begin(); + it != DealerInfoList::self()->games().end(); ++it) + { + // while we develop, it may happen that some lower + // indices do not exist + uint index = (*it)->gameindex; + for (uint i = 0; i <= index; i++) + if (list.count() <= i) + list.append("unknown"); + list[index] = i18n((*it)->name); + if (max_type < index) + max_type = index; + } + games->setItems(list); + + KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "kpat/backgrounds/"); + KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "ksnake/backgrounds/"); + wallpapers = new KSelectAction(i18n("&Change Background"), 0, this, + SLOT(changeWallpaper()), + actionCollection(), "wallpaper"); + list.clear(); + wallpaperlist.clear(); + QStringList wallpaperlist2 = KGlobal::dirs()->findAllResources("wallpaper", QString::null, + false, true, list); + QStringList list2; + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) { + QString file = *it; + int rindex = file.findRev('.'); + if (rindex != -1) { + QString ext = file.mid(rindex + 1).lower(); + if (ext == "jpeg" || ext == "png" || ext == "jpg") { + list2.append(file.left(rindex)); + wallpaperlist.append( file ); + } + } + } + + wallpapers->setItems(list2); + wallpapers->setCurrentItem(list2.findIndex("No-Ones-Laughing-3")); + + changeWallpaper(); + + (void)new cardMap(midcolor); + + backs = new KAction(i18n("&Switch Cards..."), 0, this, + SLOT(changeBackside()), + actionCollection(), "backside"); + stats = new KAction(i18n("&Statistics"), 0, this, SLOT(showStats()), + actionCollection(),"game_stats"); + + animation = new KToggleAction(i18n( "&Animation on Startup" ), + 0, this, SLOT(animationChanged()), + actionCollection(), "animation"); + dropaction = new KToggleAction(i18n("&Enable Autodrop"), + 0, this, SLOT(enableAutoDrop()), + actionCollection(), "enable_autodrop"); + dropaction->setCheckedState(i18n("Disable Autodrop")); + + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + + QString bgpath = config->readPathEntry("Background"); + kdDebug(11111) << "bgpath '" << bgpath << "'" << endl; + if (bgpath.isEmpty()) + bgpath = locate("wallpaper", "No-Ones-Laughing-3.jpg"); + background = QPixmap(bgpath); + + bool animate = config->readBoolEntry( "Animation", true); + animation->setChecked( animate ); + + bool autodrop = config->readBoolEntry("Autodrop", true); + dropaction->setChecked(autodrop); + + uint game = config->readNumEntry("DefaultGame", 0); + if (game > max_type) + game = max_type; + games->setCurrentItem(game); + + statusBar()->insertItem( "", 1, 0, true ); + + createGUI(QString::null, false); + KAcceleratorManager::manage(menuBar()); + + newGameType(); + adjustSize(); + setAutoSaveSettings(); +} + +pWidget::~pWidget() +{ + delete dill; +} + +void pWidget::undoMove() { + if( dill ) + dill->undo(); +} + +void pWidget::helpGame() +{ + if (!dill) + return; + kapp->invokeHelp(dill->anchorName()); +} + +void pWidget::undoPossible(bool poss) +{ + undo->setEnabled(poss); +} + +void pWidget::changeBackside() { + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + + QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck()); + QString cards = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + if (KCardDialog::getCardDeck(deck, cards, this, KCardDialog::Both) == QDialog::Accepted) + { + QString imgname = KCardDialog::getCardPath(cards, 11); + + QImage image; + image.load(imgname); + if( image.isNull()) { + kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << cards << "\n"; + return; + } + + bool change = false; + if (image.width() != cardMap::CARDX() || image.height() != cardMap::CARDY()) + { + change = true; + if (KMessageBox::warningContinueCancel(this, i18n("The cards you have chosen have a different " + "size than the ones you are currently using. " + "This requires the current game to be restarted.")) == KMessageBox::Cancel) + return; + } + setBackSide(deck, cards); + if (change) { + + newGameType(); + } + } + +} + +void pWidget::changeWallpaper() +{ + QString bgpath=locate("wallpaper", wallpaperlist[wallpapers->currentItem()]); + if (bgpath.isEmpty()) + return; + background = QPixmap(bgpath); + if (background.isNull()) { + KMessageBox::sorry(this, i18n("Couldn't load wallpaper
    %1
    ").arg(bgpath)); + return; + } + + QImage bg = background.convertToImage().convertDepth(8, 0); + if (bg.isNull() || !bg.numColors()) + return; + long r = 0; + long g = 0; + long b = 0; + for (int i = 0; i < bg.numColors(); ++i) + { + QRgb rgb = bg.color(i); + r += qRed(rgb); + g += qGreen(rgb); + b += qBlue(rgb); + } + r /= bg.numColors(); + b /= bg.numColors(); + g /= bg.numColors(); + midcolor = QColor(r, b, g); + + if (dill) { + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + + QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck()); + QString dummy = config->readEntry("Cards", KCardDialog::getDefaultCardDir()); + setBackSide(deck, dummy); + + config->writePathEntry("Background", bgpath); + dill->setBackgroundPixmap(background, midcolor); + dill->canvas()->setAllChanged(); + dill->canvas()->update(); + } +} + +void pWidget::animationChanged() { + bool anim = animation->isChecked(); + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + config->writeEntry( "Animation", anim); +} + +void pWidget::enableAutoDrop() +{ + bool drop = dropaction->isChecked(); + KConfig *config = kapp->config(); + KConfigGroupSaver cs(config, settings_group ); + config->writeEntry( "Autodrop", drop); + dill->setAutoDropEnabled(drop); +} + +void pWidget::newGame() +{ + // Check if the user is already running a game, and if she is, + // then ask if she wants to abort it. + if (!dill->isGameWon() && !dill->isGameLost() + && KMessageBox::warningContinueCancel(0, + i18n("You are already running an unfinished game. " + "If you abort the old game to start a new one, " + "the old game will be registered as a loss in " + "the statistics file.\n" + "What do you want to do?"), + i18n("Abort Current Game?"), + i18n("Abort Old Game"), + "careaboutstats" ) == KMessageBox::Cancel) + return; + + dill->setGameNumber(kapp->random()); + setGameCaption(); + restart(); +} + + +void pWidget::restart() +{ + statusBar()->clear(); + dill->startNew(); +} + +void pWidget::setGameCaption() +{ + QString name = games->currentText(); + QString newname; + QString gamenum; + gamenum.setNum( dill->gameNumber() ); + for (uint i = 0; i < name.length(); i++) + if (name.at(i) != QChar('&')) + newname += name.at(i); + + setCaption( newname + " - " + gamenum ); +} + +void pWidget::newGameType() +{ + delete dill; + dill = 0; + slotUpdateMoves(); + + uint id = games->currentItem(); + for (QValueList::ConstIterator it = DealerInfoList::self()->games().begin(); it != DealerInfoList::self()->games().end(); ++it) { + if ((*it)->gameindex == id) { + dill = (*it)->createGame(this); + QString name = (*it)->name; + name = name.replace(QRegExp("[&']"), ""); + name = name.replace(QRegExp("[ ]"), "_").lower(); + dill->setAnchorName("game_" + name); + connect(dill, SIGNAL(saveGame()), SLOT(saveGame())); + connect(dill, SIGNAL(gameInfo(const QString&)), + SLOT(slotGameInfo(const QString &))); + connect(dill, SIGNAL(updateMoves()), + SLOT(slotUpdateMoves())); + dill->setGameId(id); + dill->setupActions(); + dill->setBackgroundPixmap(background, midcolor); + dill->startNew(); + break; + } + } + + if (!dill) { + kdError() << "unimplemented game type " << id << endl; + dill = DealerInfoList::self()->games().first()->createGame(this); + } + + connect(dill, SIGNAL(undoPossible(bool)), SLOT(undoPossible(bool))); + connect(dill, SIGNAL(gameWon(bool)), SLOT(gameWon(bool))); + connect(dill, SIGNAL(gameLost()), SLOT(gameLost())); + + dill->setAutoDropEnabled(dropaction->isChecked()); + + // it's a bit tricky - we have to do this here as the + // base class constructor runs before the derived class's + dill->takeState(); + + setGameCaption(); + + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + config->writeEntry("DefaultGame", id); + + QSize min(700,400); + min = min.expandedTo(dill->minimumCardSize()); + dill->setMinimumSize(min); + dill->resize(min); + updateGeometry(); + setCentralWidget(dill); + dill->show(); +} + +void pWidget::showEvent(QShowEvent *e) +{ + if (dill) + dill->setMinimumSize(QSize(0,0)); + KMainWindow::showEvent(e); +} + +void pWidget::slotGameInfo(const QString &text) +{ + statusBar()->message(text, 3000); +} + +void pWidget::slotUpdateMoves() +{ + int moves = 0; + if ( dill ) moves = dill->getMoves(); + statusBar()->changeItem( i18n("1 move", "%n moves", moves), 1 ); +} + +void pWidget::setBackSide(const QString &deck, const QString &cards) +{ + KConfig *config = kapp->config(); + KConfigGroupSaver kcs(config, settings_group); + QPixmap pm(deck); + if(!pm.isNull()) { + cardMap::self()->setBackSide(pm, false); + config->writeEntry("Back", deck); + bool ret = cardMap::self()->setCardDir(cards); + if (!ret) { + config->writeEntry("Back", ""); + + } + config->writeEntry("Cards", cards); + cardMap::self()->setBackSide(pm, true); + } else + KMessageBox::sorry(this, + i18n("Could not load background image!")); + + if (dill) { + dill->canvas()->setAllChanged(); + dill->canvas()->update(); + } +} + +void pWidget::chooseGame() +{ + bool ok; + long number = KInputDialog::getText(i18n("Game Number"), i18n("Enter a game number (FreeCell deals are the same as in the FreeCell FAQ):"), QString::number(dill->gameNumber()), 0, this).toLong(&ok); + if (ok) { + dill->setGameNumber(number); + setGameCaption(); + restart(); + } +} + +void pWidget::gameWon(bool withhelp) +{ + QString congrats; + if (withhelp) + congrats = i18n("Congratulations! We have won!"); + else + congrats = i18n("Congratulations! You have won!"); +#if TEST_SOLVER == 0 + KMessageBox::information(this, congrats, i18n("Congratulations!")); +#endif + QTimer::singleShot(0, this, SLOT(newGame())); +#if TEST_SOLVER == 1 + dill->demo(); +#endif +} + +void pWidget::gameLost() +{ + QString dontAskAgainName = "gameLostDontAskAgain"; + + // The following code is taken out of kmessagebox.cpp in kdeui. + // Is there a better way? + KConfig *config = 0; + QString grpNotifMsgs = QString::fromLatin1("Notification Messages"); + + config = KGlobal::config(); + KConfigGroupSaver saver(config, + QString::fromLatin1("Notification Messages")); + QString dontAsk = config->readEntry(dontAskAgainName).lower(); + + // If we are ordered never to ask again and to continue the game, + // then do so. + if (dontAsk == "no") + return; + // If it says yes, we ask anyway. Just starting a new game would + // be incredibly annoying. + if (dontAsk == "yes") + dontAskAgainName = QString::null; + + if (KMessageBox::questionYesNo(this, i18n("You could not win this game, " + "but there is always a second try.\nStart a new game?"), + i18n("Could Not Win!"), + i18n("New Game"), + KStdGuiItem::cont(), + dontAskAgainName) == KMessageBox::Yes) { + + QTimer::singleShot(0, this, SLOT(newGame())); + } +} + +void pWidget::openGame(const KURL &url) +{ + QString tmpFile; + if( KIO::NetAccess::download( url, tmpFile, this ) ) + { + QFile of(tmpFile); + of.open(IO_ReadOnly); + QDomDocument doc; + QString error; + if (!doc.setContent(&of, &error)) + { + KMessageBox::sorry(this, error); + return; + } + uint id = doc.documentElement().attribute("id").toUInt(); + + if (id != (Q_UINT32)games->currentItem()) { + games->setCurrentItem(id); + newGameType(); + if (!dill) { + KMessageBox::error(this, i18n("The saved game is of unknown type!")); + games->setCurrentItem(0); + newGameType(); + } + } + dill->openGame(doc); + setGameCaption(); + KIO::NetAccess::removeTempFile( tmpFile ); + recent->addURL(url); + recent->saveEntries(KGlobal::config()); + } +} + +void pWidget::openGame() +{ + KURL url = KFileDialog::getOpenURL(); + openGame(url); +} + +void pWidget::saveGame() +{ + KURL url = KFileDialog::getSaveURL(); + KTempFile file; + QDomDocument doc("kpat"); + dill->saveGame(doc); + QTextStream *stream = file.textStream(); + *stream << doc.toString(); + file.close(); + KIO::NetAccess::upload(file.name(), url, this); + recent->addURL(url); + recent->saveEntries(KGlobal::config()); +} + +void pWidget::showStats() +{ + GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog"); + if (dill) + dlg->showGameType(dill->gameId()); + dlg->exec(); +} + +#include "pwidget.moc" + diff --git a/kpat/pwidget.h b/kpat/pwidget.h new file mode 100644 index 00000000..8781960f --- /dev/null +++ b/kpat/pwidget.h @@ -0,0 +1,90 @@ +/* -*- C++ -*- + * + * patience -- main program + * Copyright (C) 1995 Paul Olav Tvete + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + * + * + * Heavily modified by Mario Weilguni + * + */ + +#ifndef __PWIDGET__H__ +#define __PWIDGET__H__ + +#include + +class Dealer; +class KToggleAction; +class KSelectAction; +class KRecentFilesAction; +class KAction; +class QWidgetStack; +class QLabel; + +class pWidget: public KMainWindow { + Q_OBJECT + +public: + pWidget(); + ~pWidget(); + +public slots: + void undoMove(); + void changeBackside(); + void animationChanged(); + void newGameType(); + void restart(); + + void openGame(); + void openGame(const KURL &url); + void saveGame(); + + void newGame(); + void chooseGame(); + void undoPossible(bool poss); + void gameWon(bool withhelp); + void gameLost(); + void changeWallpaper(); + void slotGameInfo(const QString &); + void slotUpdateMoves(); + void helpGame(); + void enableAutoDrop(); + void showStats(); + +private: + void setGameCaption(); + void setBackSide(const QString &deck, const QString &dir); + virtual void showEvent(QShowEvent *e); + +private: + // Members + + Dealer *dill; // The current patience + + KSelectAction *games; + KSelectAction *wallpapers; + KAction *backs; + KAction *undo; + KToggleAction *animation; + KToggleAction *dropaction; + KAction *stats; + + QPixmap background; + QColor midcolor; + QStringList wallpaperlist; + KRecentFilesAction *recent; +}; + +#endif diff --git a/kpat/simon.cpp b/kpat/simon.cpp new file mode 100644 index 00000000..287ecd00 --- /dev/null +++ b/kpat/simon.cpp @@ -0,0 +1,156 @@ +#include "simon.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Simon::Simon( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + deck = Deck::new_deck(this); + deck->move(10, 10); + deck->hide(); + + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(10+(i+3)*dist_x, 10); + target[i]->setRemoveFlags(Pile::disallow); + target[i]->setAddFlags(Pile::several); + target[i]->setCheckIndex(0); + target[i]->setTarget(true); + } + + for (int i=0; i<10; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(15+dist_x*i, 10 + cardMap::CARDY() * 73 / 50); + store[i]->setAddFlags(Pile::addSpread | Pile::several); + store[i]->setRemoveFlags(Pile::several); + store[i]->setCheckIndex(1); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Simon::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Simon::deal() { + int piles = 3; + + for (int round = 0; round < 8; round++) + { + for (int j = 0; j < piles; j++) + { + store[j]->add(deck->nextCard(), false, true); + } + piles++; + } + assert(deck->isEmpty()); +} + +bool Simon::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const +{ + if (checkIndex == 1) { + if (c1->isEmpty()) + return false; + + return (c1->top()->suit() == c2.first()->suit()); + } else return false; // it's just important to keep this unique +} + +bool Simon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const +{ + if (checkIndex == 1) { + if (c1->isEmpty()) + return true; + + return (c1->top()->rank() == c2.first()->rank() + 1); + } else { + if (!c1->isEmpty()) + return false; + return (c2.first()->rank() == Card::King && c2.last()->rank() == Card::Ace); + } +} + +bool Simon::checkRemove(int checkIndex, const Pile *p, const Card *c) const +{ + if (checkIndex != 1) + return false; + + // ok if just one card + if (c == p->top()) + return true; + + // Now we're trying to move two or more cards. + + // First, let's check if the column is in valid + // (that is, in sequence, alternated colors). + int index = p->indexOf(c) + 1; + const Card *before = c; + while (true) + { + c = p->at(index++); + + if (!((c->rank() == (before->rank()-1)) + && (c->suit() == before->suit()))) + { + return false; + } + if (c == p->top()) + return true; + before = c; + } + + return true; +} + +bool Simon::isGameLost() const +{ + kdDebug(11111) <<"isGameLost" << endl; + for (int i=0; i<10; i++) { + if(store[i]->isEmpty()) + return false; + kdDebug(11111) <<"store["<top(); + int indexi=store[i]->indexOf(top); + while(--indexi >=0){ + kdDebug(11111) <name() << endl; + c=store[i]->at(indexi); + if(c->suit() == top->suit() && + (top->rank()+1) == c->rank()) + top=c; + else + break; + } + + kdDebug(11111) <<"selected: " << top->name() << endl; + for(int j=1; j <10; j++){ + int k=(i+j) % 10; + + if(store[k]->isEmpty()) + return false; + + kdDebug(11111) <<"vs "<top()->name() << endl; + if((top->rank() +1) == store[k]->top()->rank()) + return false; + } + } + + return true; +} + +static class LocalDealerInfo9 : public DealerInfo +{ +public: + LocalDealerInfo9() : DealerInfo(I18N_NOOP("&Simple Simon"), 9) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Simon(parent); } +} gfi9; + +#include "simon.moc" diff --git a/kpat/simon.h b/kpat/simon.h new file mode 100644 index 00000000..91a50a1b --- /dev/null +++ b/kpat/simon.h @@ -0,0 +1,29 @@ +#ifndef SIMON_H +#define SIMON_H + +#include "dealer.h" + +class Simon : public Dealer { + Q_OBJECT + +public: + Simon( KMainWindow* parent=0, const char* name=0); + +public slots: + void deal(); + virtual void restart(); + virtual bool isGameLost() const; + + +protected: + virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const; + virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const; + +private: + Pile* store[10]; + Pile* target[4]; + Deck *deck; +}; + +#endif diff --git a/kpat/speeds.h b/kpat/speeds.h new file mode 100644 index 00000000..9f2c646d --- /dev/null +++ b/kpat/speeds.h @@ -0,0 +1,26 @@ +#ifndef __SPEEDS_H_ +#define __SPEEDS_H_ + +#define TEST_SOLVER 0 + +#ifdef TEST_SOLVER +#define STEPS_AUTODROP 8 +#define STEPS_WON 20 +#define STEPS_DEMO 7 +#define STEPS_MOVEBACK 7 +#define STEPS_INITIALDEAL 10 + +#define TIME_BETWEEN_MOVES 200 +#else + +#define STEPS_AUTODROP 1 +#define STEPS_WON 20 +#define STEPS_DEMO 1 +#define STEPS_MOVEBACK 1 +#define STEPS_INITIALDEAL 1 + +#define TIME_BETWEEN_MOVES 2 + +#endif + +#endif diff --git a/kpat/spider.cpp b/kpat/spider.cpp new file mode 100644 index 00000000..262c49b9 --- /dev/null +++ b/kpat/spider.cpp @@ -0,0 +1,484 @@ +/*--------------------------------------------------------------------------- + + spider.cpp implements a patience card game + + Copyright (C) 2003 Josh Metzler + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#include "spider.h" +#include "cardmaps.h" +#include +#include "deck.h" +#include + +void SpiderPile::moveCards(CardList &c, Pile *to) +{ + Pile::moveCards(c, to); + + // if this is a leg pile, don't do anything special + if ( to->checkIndex() == 0 ) + return; + + // if the top card of the list I just moved is an Ace, + // the run I just moved is the same suit as the pile, + // and the destination pile now has more than 12 cards, + // then it could have a full deck that needs removed. + if (c.last()->rank() == Card::Ace && + c.first()->suit() == to->top()->suit() && + to->cardsLeft() > 12) { + Spider *b = dynamic_cast(dealer()); + if (b) { + b->checkPileDeck(to); + } + } +} + +//-------------------------------------------------------------------------// + +Spider::Spider(int suits, KMainWindow* parent, const char* _name) + : Dealer(parent, _name) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this, 2, suits); + + // I deal the cards into 'redeal' piles, so hide the deck + deck->setVisible(false); + + // Dealing the cards out into 5 piles so the user can see how many + // sets of 10 cards are left to be dealt out + for( int column = 0; column < 5; column++ ) { + redeals[column] = new Pile(column + 1, this); + redeals[column]->move(8 + dist_x / 3 * (23 + column), 8 + dist_y * 4.5); + redeals[column]->setZ(5-column); + redeals[column]->setCheckIndex(0); + redeals[column]->setAddFlags(Pile::disallow); + redeals[column]->setRemoveFlags(Pile::disallow); + connect(redeals[column], SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*))); + } + + // The 10 playing piles + for( int column = 0; column < 10; column++ ) { + stack[column] = new SpiderPile(column + 6, this); + stack[column]->move(8 + dist_x * column, 8); + stack[column]->setZ(20); + stack[column]->setCheckIndex(1); + stack[column]->setAddFlags(Pile::addSpread | Pile::several); + stack[column]->setRemoveFlags(Pile::several | + Pile::autoTurnTop | Pile::wholeColumn); + } + + // The 8 'legs' so named by me because spiders have 8 legs - why + // else the name Spider? + for( int column = 0; column < 8; column++ ) { + legs[column] = new Pile(column + 16, this); + legs[column]->move(8 + dist_x / 3 * column, 8 + dist_y * 4.5); + legs[column]->setZ(column+1); + legs[column]->setCheckIndex(0); + legs[column]->setAddFlags(Pile::disallow); + legs[column]->setRemoveFlags(Pile::disallow); + legs[column]->setTarget(true); + } + + // Moving an A-K run to a leg is not really an autoDrop - the + // user should have no choice. Also, it must be moved A first, ... + // up to K so the King will be on top. + setAutoDropEnabled(false); + setActions(Dealer::Hint | Dealer::Demo ); +} + +//-------------------------------------------------------------------------// + +bool Spider::checkAdd(int /*checkIndex*/, const Pile *c1, const CardList& c2) const +{ + // assuming the cardlist is a valid unit, since I allowed + // it to be removed - can drop any card on empty pile or + // on any suit card of one higher rank + if (c1->isEmpty() || c1->top()->rank() == c2.first()->rank()+1) + return true; + + return false; +} + +bool Spider::checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const +{ + // if the pile from c up is decreasing by 1 and all the same suit, ok + // note that this is true if c is the top card + const Card *before; + int index = p->indexOf(c); + while (c != p->top()) { + before = c; + c = p->at(++index); + if (before->suit() != c->suit() || before->rank() != c->rank()+1) + return false; + } + return true; +} + +void Spider::getHints() +{ + kdDebug(11111) << "get hints" << endl; + // first, get runs from each stack + CardList cl[10]; + + Pile* empty = NULL; + for (int column = 0; column < 10; column++) { + if (stack[column]->isEmpty()) + empty = stack[column]; + else + cl[column] = getRun(stack[column]->top()); + } + + // if I can build a run from Ace->King in one suit then + // hint those moves + HintList hl; + for (int s = Card::Clubs; s <= Card::Spades; s++) { + bool bGrowing = true; + int vTopNew = 0; + int colNew = -1; + while (bGrowing && vTopNew < 13) { + bGrowing = false; + int col = colNew; + int vTop = vTopNew; + for (int column = 0; column < 10; column++) { + if (cl[column].isEmpty() || col == column) + continue; + if (cl[column].last()->suit() == s && + cl[column].last()->rank() <= vTop+1 && + cl[column].first()->rank() > vTop) + { + bGrowing = true; + if (cl[column].first()->rank() > vTopNew) { + colNew = column; + vTopNew = cl[column].first()->rank(); + } + } + } + if (bGrowing && vTop) + hl.append(new MoveHint(cl[col][vTop- + cl[colNew].last()->rank()+1], stack[colNew])); + } + if (vTopNew == 13) + hints += hl; + else + for (HintList::Iterator it = hl.begin(); it != hl.end(); ++it) + delete *it; + hl.clear(); + } + + // now check to see if a run from one column can go on the end + // of a run from another stack + for (int column = 0; column < 10; column++) { + if (cl[column].isEmpty()) + continue; + + // if there is an empty column and this stack is on + // another card, hint + if (empty && cl[column].count() < (uint)stack[column]->cardsLeft()) { + newHint(new MoveHint(cl[column].first(), empty)); + continue; + } + + // now see if I can move this stack to any other column + for (int c2 = 0; c2 < 10; c2++) { + if (c2 == column || cl[c2].isEmpty()) + continue; + + if (cl[c2].last()->rank() == cl[column].first()->rank()+1) + { + // I can hint this move - should I? + int index = stack[column]->indexOf(cl[column].first()); + + // if target pile is the same suit as this card, + // or if there are no cards under this one, + // or if it couldn't move to where it is now, + // or if the card under this one is face down, hint + if (cl[c2].last()->suit() == cl[column].first()->suit() || + index == 0 || stack[column]->at(index-1)->rank() != + cl[column].first()->rank()+1 || + !(stack[column]->at(index-1)->realFace())) + newHint(new MoveHint(cl[column].first(), stack[c2])); + } + } + } +} + +MoveHint *Spider::chooseHint() +{ + kdDebug(11111) << "choose 1 of " << hints.count() << " hints" << endl; + if (hints.isEmpty()) + return 0; + + // first, choose a card that is moving to the same suit + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if (!(*it)->pile()->isEmpty() && + (*it)->pile()->top()->suit() == (*it)->card()->suit()) + return *it; + } + + // second, choose a card that is moving from the base + for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) + { + if ((*it)->card()->source() && + (*it)->card()->source()->at(0) == (*it)->card()) + return *it; + } + + // otherwise, go with a random hint + return hints[randseq.getLong(hints.count())]; +} + +//-------------------------------------------------------------------------// + +QString Spider::getGameState() const +{ + return QString::number(m_leg*10 + m_redeal); +} + +void Spider::setGameState(const QString &stream) +{ + int i = stream.toInt(); + + if (m_leg > i/10) { + for (m_leg--; m_leg > i/10; m_leg--) + legs[m_leg]->setVisible(false); + legs[m_leg]->setVisible(false); + } else + for (; m_leg < i/10; m_leg++) + legs[m_leg]->setVisible(true); + + if (m_redeal > i%10) { + for (m_redeal--; m_redeal > i%10; m_redeal--) + redeals[m_redeal]->setVisible(true); + redeals[m_redeal]->setVisible(true); + } else + for (; m_redeal < i%10; m_redeal++) + redeals[m_redeal]->setVisible(false); +} + +//-------------------------------------------------------------------------// + +void Spider::restart() +{ + deck->collectAndShuffle(); + deal(); +} + +//-------------------------------------------------------------------------// + +CardList Spider::getRun(Card *c) const +{ + CardList result; + + Pile *p = c->source(); + if (!p || p->isEmpty()) + return result; + + result.append(c); + + Card::Suit s = c->suit(); + int v = c->rank(); + + int index = p->indexOf(c); + c = p->at(--index); + while (index >= 0 && c->realFace() && + c->suit() == s && c->rank() == ++v) + { + result.prepend(c); + c = p->at(--index); + } + + return result; +} + +void Spider::checkPileDeck(Pile *check) +{ + kdDebug(11111) << "check for run" << endl; + if (check->isEmpty()) + return; + + if (check->top()->rank() == Card::Ace) { + // just using the CardList to see if this goes to King + CardList run = getRun(check->top()); + if (run.first()->rank() == Card::King) { + legs[m_leg]->setVisible(true); + + // remove this full deck from this pile + CardList cl; + for (int i = 0; i < 13; i++ ) { + cl.append(check->cards().last()); + check->moveCards(cl, legs[m_leg]); + cl.clear(); + } + m_leg++; + } + } +} + +void Spider::dealRow() +{ + if (m_redeal > 4) + return; + + for (int column = 0; column < 10; column++) { + stack[column]->add(redeals[m_redeal]->top(), false, true); + + // I may put an Ace on a K->2 pile so it could need cleared. + if (stack[column]->top()->rank() == Card::Ace) + checkPileDeck(stack[column]); + } + + redeals[m_redeal++]->setVisible(false); +} + +//-------------------------------------------------------------------------// + +void Spider::deal() +{ + unmarkAll(); + + m_leg = 0; + m_redeal = 0; + + int column = 0; + // deal face down cards (5 to first 4 piles, 4 to last 6) + for (int i = 0; i < 44; i++ ) { + stack[column]->add(deck->nextCard(), true, true); + column = (column + 1) % 10; + } + // deal face up cards, one to each pile + for (int i = 0; i < 10; i++ ) { + stack[column]->add(deck->nextCard(), false, true); + column = (column + 1) % 10; + } + // deal the remaining cards into 5 'redeal' piles + for (int column = 0; column < 5; column++ ) + for (int i = 0; i < 10; i++ ) + redeals[column]->add(deck->nextCard(), true, false); + + // make the leg piles invisible + for (int i = 0; i < 8; i++ ) + legs[i]->setVisible(false); + // make the redeal piles visible + for (int i = 0; i < 5; i++ ) + redeals[i]->setVisible(true); +} + +Card *Spider::demoNewCards() +{ + if (m_leg > 4) + return 0; + deckClicked(0); + return stack[0]->top(); +} + +void Spider::deckClicked(Card*) +{ + kdDebug(11111) << "deck clicked " << m_redeal << endl; + if (m_redeal > 4) + return; + + unmarkAll(); + dealRow(); + takeState(); +} + +bool Spider::isGameLost() const +{ + kdDebug(11111) << "isGameLost ?"<< endl; + + // if there are still cards to deal out, you have not lost + if (m_redeal < 5) + return false; + + // first, get runs from each stack - returning if empty + CardList cl[10]; + + for (int column = 0; column < 10; column++) { + if (stack[column]->isEmpty()) + return false; + cl[column] = getRun(stack[column]->top()); + } + + // from this point on, I know that none of the columns is empty + // now check to see if a run from one column can go on the end + // of a run from another stack + for (int column = 0; column < 10; column++) + for (int c2 = 0; c2 < 10; c2++) { + if (c2 == column) + continue; + + // if I can move this run to another pile, I'm not done + if (cl[c2].last()->rank() == cl[column].first()->rank()+1) + return false; + } + + // if you can build a run from Ace->King in one suit then + // you can clear it and keep playing + for (int s = Card::Clubs; s <= Card::Spades; s++) { + bool bGrowing = true; + int vTop = 0; + while (bGrowing && vTop < 13) { + bGrowing = false; + int column = 0; + while (column < 10 && !bGrowing) { + if (cl[column].last()->suit() == s && + cl[column].last()->rank() <= vTop+1 && + cl[column].first()->rank() > vTop) + { + bGrowing = true; + vTop = cl[column].first()->rank(); + } + column++; + } + } + // if you can build such a pile, you can continue + if (vTop == 13) + return false; + } + + return true; +} + +static class LocalDealerInfo15 : public DealerInfo +{ +public: + LocalDealerInfo15() : DealerInfo(I18N_NOOP("S&pider (Easy)"), 14) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(1, parent); } +} ldi15; + +static class LocalDealerInfo16 : public DealerInfo +{ +public: + LocalDealerInfo16() : DealerInfo(I18N_NOOP("Spider (&Medium)"), 15) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(2, parent); } +} ldi16; + +static class LocalDealerInfo17 : public DealerInfo +{ +public: + LocalDealerInfo17() : DealerInfo(I18N_NOOP("Spider (&Hard)"), 16) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Spider(4, parent); } +} ldi17; + +//-------------------------------------------------------------------------// + +#include "spider.moc" + +//-------------------------------------------------------------------------// + diff --git a/kpat/spider.h b/kpat/spider.h new file mode 100644 index 00000000..ea2cc165 --- /dev/null +++ b/kpat/spider.h @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------- + + spider.cpp implements a patience card game + + Copyright (C) 2003 Josh Metzler + + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + +---------------------------------------------------------------------------*/ + +#ifndef _SPIDER_H_ +#define _SPIDER_H_ + +#include "dealer.h" + +class SpiderPile : public Pile +{ +public: + SpiderPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {} + virtual void moveCards(CardList &c, Pile *to); + CardList getRun(); +}; + +class Spider : public Dealer +{ + Q_OBJECT + +public: + Spider(int suits, KMainWindow *parent=0, const char *name=0); + void deal(); + void dealRow(); + void checkPileDeck(Pile *to); + virtual void restart(); + virtual bool isGameLost() const; + +public slots: + void deckClicked(Card *c); + +protected: + virtual bool checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const; + virtual bool checkAdd(int /*checkIndex*/, const Pile *c1, const CardList &c2) const; + virtual QString getGameState() const; + virtual void setGameState(const QString &stream); + virtual void getHints(); + virtual MoveHint *chooseHint(); + virtual Card *demoNewCards(); + +private: + CardList getRun(Card *c) const; + + SpiderPile *stack[10]; + Pile *legs[8]; + int m_leg; + Pile *redeals[5]; + int m_redeal; + Deck *deck; +}; + +#endif + +//-------------------------------------------------------------------------// diff --git a/kpat/version.h b/kpat/version.h new file mode 100644 index 00000000..b5eae065 --- /dev/null +++ b/kpat/version.h @@ -0,0 +1,6 @@ +#ifndef KPAT_VERSION +#define KPAT_VERSION "2.2.2" +#endif + +#define settings_group "General Settings" +#define scores_group "Scores" diff --git a/kpat/yukon.cpp b/kpat/yukon.cpp new file mode 100644 index 00000000..859b3746 --- /dev/null +++ b/kpat/yukon.cpp @@ -0,0 +1,130 @@ +#include "yukon.h" +#include +#include +#include "deck.h" +#include +#include "cardmaps.h" + +Yukon::Yukon( KMainWindow* parent, const char *name ) + : Dealer( parent, name ) +{ + const int dist_x = cardMap::CARDX() * 11 / 10 + 1; + const int dist_y = cardMap::CARDY() * 11 / 10 + 1; + + deck = Deck::new_deck(this); + deck->move(10, 10+dist_y*3); + deck->hide(); + + for (int i=0; i<4; i++) { + target[i] = new Pile(i+1, this); + target[i]->move(20+7*dist_x, 10+dist_y *i); + target[i]->setType(Pile::KlondikeTarget); + } + + for (int i=0; i<7; i++) { + store[i] = new Pile(5+i, this); + store[i]->move(15+dist_x*i, 10); + store[i]->setAddType(Pile::KlondikeStore); + store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop); + } + + setActions(Dealer::Hint | Dealer::Demo); +} + +void Yukon::restart() { + deck->collectAndShuffle(); + deal(); +} + +void Yukon::deal() { + for (int round = 0; round < 11; round++) + { + for (int j = 0; j < 7; j++) + { + bool doit = false; + switch (j) { + case 0: + doit = (round == 0); + break; + default: + doit = (round < j + 5); + } + if (doit) + store[j]->add(deck->nextCard(), round < j && j != 0, true); + } + } +} + +bool Yukon::isGameLost() const { + int i,j,k,l,indexi,freeStore=0; + Card *c, *cNewTop; + + kdDebug(11111) <<"isGameLost" << endl; + + for(i=0; i < 7; i++){ + if( store[i]->isEmpty() ){ + freeStore++; + continue; + } + + if(store[i]->top()->rank() == Card::Ace || + ! store[i]->top()->isFaceUp()) + return false; + + for(indexi=store[i]->indexOf(store[i]->top()); indexi >=0; indexi--){ + + c=store[i]->at(indexi); + if( !c->isFaceUp() ) + break; + + if(freeStore > 0 && indexi > 0 && c->rank() == Card::King) + return false; + + for(j=0; j < 4;j++){ + if(!target[j]->isEmpty() && + c->rank()-1 == target[j]->top()->rank() && + c->suit() == target[j]->top()->suit()) + return false; + } + + for(j=1; j < 7; j++){ + k=(i+j) % 7; + if( !store[k]->isEmpty() ) { + if(c->rank()+1 == store[k]->top()->rank() && + (c->isRed() != store[k]->top()->isRed())){ + + if(indexi == 0) + return false; + else{ + cNewTop=store[i]->at(indexi-1); + if(!cNewTop->isFaceUp()) + return false; + if(cNewTop->rank() == Card::Ace) + return false; + if(cNewTop->rank() != store[k]->top()->rank() || + cNewTop->isRed() != store[k]->top()->isRed()) + return false; + + for(l=0; l < 4;l++){ + if(!target[l]->isEmpty() && + cNewTop->rank()-1 == target[l]->top()->rank() && + cNewTop->suit() == target[l]->top()->suit()) + return false; + } + } + } + } + } + } + } + return (freeStore!=7); +} + +static class LocalDealerInfo10 : public DealerInfo +{ +public: + LocalDealerInfo10() : DealerInfo(I18N_NOOP("&Yukon"), 10) {} + virtual Dealer *createGame(KMainWindow *parent) { return new Yukon(parent); } +} gfi10; + +#include "yukon.moc" diff --git a/kpat/yukon.h b/kpat/yukon.h new file mode 100644 index 00000000..9b407ac3 --- /dev/null +++ b/kpat/yukon.h @@ -0,0 +1,23 @@ +#ifndef YUKON_H +#define YUKON_H + +#include "dealer.h" + +class Yukon : public Dealer { + Q_OBJECT + +public: + Yukon( KMainWindow* parent=0, const char* name=0); + virtual bool isGameLost() const; + +public slots: + void deal(); + virtual void restart(); + +private: + Pile* store[7]; + Pile* target[4]; + Deck *deck; +}; + +#endif diff --git a/kpoker/AUTHORS b/kpoker/AUTHORS new file mode 100644 index 00000000..866b0428 --- /dev/null +++ b/kpoker/AUTHORS @@ -0,0 +1,11 @@ +Codeing: +Jochen Tuchbreiter +Mario Weilguni +Andreas Beckermann +Inge Wallin + +Helping persons: +Chris Holmes - idea of writing this game and parts of the visual appearence +John Fitzgibbon - provided the card images +Nico Schirwing - drew the backsides of the cards + diff --git a/kpoker/ChangeLog b/kpoker/ChangeLog new file mode 100644 index 00000000..f38991bf --- /dev/null +++ b/kpoker/ChangeLog @@ -0,0 +1,306 @@ +2005-09-15 Inge Wallin + + Bump version number for release of KDE 3.5 + * version.h (KPOKER_RELEASE_DATE): Bump version from 1.0 to 1.1 + +2005-02-18 Inge Wallin + + * version.h (KPOKER_VERSION): Bumped version to 1.0 for KDE 3.4. + +2005-01-26 Inge Wallin + + ----------------- CVS commit on stuff below -------------------- + + Code cleaning + * Make all blink parameters follow the m_ convention. + +2005-01-18 + + Continue the code cleaning. + * kpoker.h (PokerGameType): new type + * (PokerGame): All public members now private. + * Make the player array owned by kpok instead of PokerGame. + + ----------------- CVS commit on stuff below -------------------- + + Continue making more members of PokerGame private. + * kpoker.h (PokerGame::m_activePlayers): now private + + ----------------- CVS commit on stuff below -------------------- + + * Bump version to 0.8alpha in anticipation of KDE 3.4. + + Make members of PokerGame private. + * kpoker.cpp (Pokergame::newGame): new method + (PokerGame::m_minBet and m_maxBet): now private + +2004-11-20 Inge Wallin + + Fix bug 93636: When "Fold" is pressed, the current round should be + ended immediately. + * kpoker.cpp (out): Call drawClick(), i.e. go to next game state, + instead of setting up the drawButton to let the user do it. + + ----------------- CVS commit on stuff below -------------------- + + Fix bug 93635: The status bar is wrong in many places. + * top.cpp (PokerWindow): connect the signal to clear the status + bar to the proper slot. + +2004-11-03 Inge Wallin + + Rename the game states into something more logical + * kpoker.{h,cpp}: Renaming of poker game states + + Exchange the faulty term "mix" for the correct one "shuffle". + * poker.{h,cpp}: mix() -> shuffle() + * misc files: call shuffle() instead. + +2004-09-12 Inge Wallin + + Fix bug 88584: Use proper poker terminology: + * kpoker.cpp (kpok::misc): Set text on "Draw" button according to + game state + + Code cleaning: + * kpoker.{h,cpp} (kpok): remove member newGameDlg + * kpoker.{h,cpp} (PokerGameState): StateDraw -> StateStartRound + * kpoker.{h,cpp} (PokerGameState): uppercase state names. + * misc: added some comments + + ----------------- CVS commit on stuff below -------------------- + + Continue separation of class kpok into model/view + * kpoker.h (PokerGame): make m_deck, m_state, m_pot, m_isDirty + private members and create accessor methods for them. + * kpoker.cpp, kpoker.cpp: Use the accessor methods. + * kpoker.{h,cpp} (PokerGame::newRound): new method + +2004-09-11 Inge Wallin + + Introduced a class CardDeck + * poker.{h,cpp} (CardDeck): new class + * kpoker.{h,cpp} (PokerGame): new class + * kpoker.{h,cpp} (kpok::done): removed + + Started separation of kpok into a poker game class and a view: + * kpoker.{h,cpp} (PokerGame): new class + + Some cleanup + * player.{h,cpp}: Renamed class Player into PokerPlayer + * kpoker.{h,cpp}: references to Player -> PokerPlayer + + ----------------- CVS commit on stuff below -------------------- + + Remove a lot of unused methods and other cleanup + * poker.{h,cpp} (cleanFoundCards()): make protected + * player.h (Player::getCard()): don't convert to int. + * player.h (Player::cleanFoundCard()): remove + * playerbox.{h,cpp}: Prefix all members with m_. + (PlayerBox::getCashLabel{X,Y}): remove + + ----------------- CVS commit on stuff below -------------------- + + Break out all poker and card stuff into its own file + * poker.{h,cpp}: New files. + * player.{h,cpp}(card/poker stuff): moved to poker.{h,cpp} + + ----------------- CVS commit on stuff below -------------------- + + Remove all debug traces: + * player.h(Player::takeCard()) + * player.cpp(PokerHand::operator<()) + * kpoker.cpp(kpok::winner()) + + Move loading of card images to class CardImages: + * kpaint.{h,cpp}(CardImages::loadCards(),CardImages::loadDeck()): new methods. + * kpoker.{h,cpp}(loadCards,loadDeck): removed + + Remove somestuff.cpp: + * somestuff.cpp: removed + * kpok::initWindow(): moved to kpoker.cpp + * Makefile.am(kpoker_SOURCES): remove somestuff.cpp + + ----------------- CVS commit on stuff below -------------------- + + Use the constants from the card classes. + * defines.h: Removed highestCard and CARDS + * player.h(numCards): new constant + * kpaint.cpp, kpoker.cpp, player.cpp, playerbox.cpp, + somestuff.cpp: Use numCards and PokerHandSize instead + + Rename initSomeStuff() into initWindow() + * somestuff.cpp: Rename + * kpoker.cpp(kpok::kpok): Call initWindow() instead + + Make the card images private. + * kpaint.h, kpaint.cpp(CardImages): make m_deck and m_cardImages + private + * kpoker.cpp (loadCards, loadDeck): Call the loading of cards + differently + +2004-09-05 Inge Wallin + + Start structural cleanup + * kpoker.cpp (winner): Move from testers.cpp to kpoker.cpp. + * testers.cpp: deleted + +2004-09-03 Inge Wallin + + Fix bug 88548: Kpoker: When you save a game and then immediately + quit, kpoker asks if you want to save the game anyway + * kpoker::isDirty: New member + * misc: set and use isDirty + +2004-09-01 Inge Wallin + + * Removed the old code from the time before the switch of the + poker evaluation code. This was all contained in #if 0 - #endif + pairs. + +2004-08-31 Inge Wallin + + Finish the great code cleanup. + * kpoker.h, kpoker.cpp (only visually, though. Structure remains + to be cleaned further.) + + * main.cpp (main): Fixed copyright notice in About dialog. + +2004-08-30 Inge Wallin + + Continue the great code cleanup. + * optionsdlg.h, optionsdlg.cpp + * newgamedlg.h, newgamedlg.cpp + +2004-08-29 Inge Wallin + + Continue the great code cleanup. + * sound.cpp + * top.h, top.cpp + * defines.h + * kpaint.h, kpaint.cpp + * playerbox.h, playerbox.cpp + * kpoker.h (Status): Renamed into PokerGameState + * betbox.h, betbox.cpp + * somestuff.cpp + * testers.cpp + * player.h, player.cpp + +2004-08-28 Inge Wallin + + Start of the great code cleanup + * global.h + * main.cpp + +2004-08-26 Inge Wallin + + Rewrote the whole evaluation of a poker hand. + * player.h, player.cpp (PokerHand): New class + * kpaint.cpp, kpoker.cpp, kpoker.h, playerbox.cpp, testers.cpp: + use the new code. + +2004-08-22 Inge Wallin + + Substituted the cardHelp array with a function Player::rank() + * player.h (cardHelp): removed + * player.cpp (initCardHelp): removed + * player.cpp (rank): new function + * kpoker.cpp (misc): don't call initCardHelp(). + + +---------------------------------------------------------------- +FIXME: The rest of this file should be named NEWS instead, since it is + only about releases. + +Version 0.7 (Andreas Beckermann ) + +- added computer player +- fixed some bugs, added many more +- added bet/raise +- added a QGroupBox for every player +- improved status +- added possibility to start the game without installing +- added an optionsdialog +- added further options which are not in the dialog +- moved clickToHold and LHLabel to the new statusbar +- started API documenting +- much more + +------------------------------------------------------------------------------ +Version 0.6 (Mario Weilguni ) +- made it work with new KDE-2 API +- made it work with --enable-final + +------------------------------------------------------------------------------ +Version 0.5 + +- fixed a problem with the recognition of straights (the combo ace-2-3-4-5 was + not recognized in all versions <0.4.1 + +- cleaned up the code a little + +- KPoker is now considered to be stable - I fixed all reported bugs and I did + not find any new ones :) + +------------------------------------------------------------------------------ +Version 0.4.1 + +Silly me deleted some important lines that were initializing a timer while +merging my version of 0.4 with the one in the CVS. This caused 0.4 to +segfault when you press "Draw Cards" :-( + +- [Robert William] put version.h back in. + +- integrated version.h so that there is only one #define for the version & + release date (and not multiple ones like before) + +- put epilogue into the documentation + +------------------------------------------------------------------------------ +Version 0.4 + +- deleted version.h because it is of no use +- put in that nice "wave" effect +- added sm-support +- some internal stuff (as usual :) + +------------------------------------------------------------------------------ +Version 0.3.1 + +- [Robert Williams] added getHelpMenu() +- [Robert Williams] added version.h +- changed minor look&feel stuff + +------------------------------------------------------------------------------ +Version 0.3 + +- kpoker got adapted to the new fsstnd (thanks Coolo !) +- kpoker now supports KLocale (only works in the kdegames-distribution) +- added german language file (kdegames distribution only) +- kpoker should now run on DEC Alphas (thanks Uwe Thiem :) + +------------------------------------------------------------------------------ +Version 0.2 + +- reduced number of colors needed by the images for the backsides of cards +- KPoker documentation is now written in .sgml format +- lots of changes in makefiles +- fixed that "#include " bug. +- sound support (experimental - *please* report problems) + +------------------------------------------------------------------------------ +Version 0.1.2 + +- made kpoker use KTopLevelWidget + the new menubars +- fixed silly problem with cardImage +- finished the helpfile +- built in new icon +- built in new backsides of cards (1000 thanks to our "icon/graphics Man") + +------------------------------------------------------------------------------ +Version 0.1.1 + +- made kpoker more "KDE conform" (like inserted separator between + help/help and help/about) +- fixed a few silly bugs/problems +- started to do the helpfile (halfway done right now) diff --git a/kpoker/DESCRIPTION b/kpoker/DESCRIPTION new file mode 100644 index 00000000..3f679e18 --- /dev/null +++ b/kpoker/DESCRIPTION @@ -0,0 +1,2 @@ +A little clone of those highly addictive, simple pocket poker games. + diff --git a/kpoker/Makefile.am b/kpoker/Makefile.am new file mode 100644 index 00000000..8c72a34f --- /dev/null +++ b/kpoker/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = sounds + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO +KDE_ICON = kpoker + +bin_PROGRAMS = kpoker +kpoker_SOURCES = kpoker.cpp kpaint.cpp top.cpp main.cpp betbox.cpp \ + optionsdlg.cpp player.cpp playerbox.cpp newgamedlg.cpp poker.cpp +kpoker_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kpoker_LDADD = $(LIB_KDEGAMES) +kpoker_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kpoker.desktop + +rcdir = $(kde_datadir)/kpoker +rc_DATA = kpokerui.rc + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kpoker.pot diff --git a/kpoker/README b/kpoker/README new file mode 100644 index 00000000..923ece84 --- /dev/null +++ b/kpoker/README @@ -0,0 +1,20 @@ +This is version 0.8 (nearly) of KPoker a not that simple poker clone +for the K Desktop Environment . + +This version is considered to be pretty stable. Please take a look at +the file ChangeLog to see what was changed recently. + +Have fun with the game and report any problems / bugs suggestions. + + + Andreas Beckerman and Inge Wallin + + +Copyright (c) 1997-2000, by Jochen Tuchbreiter +This Program is distributed under the GPL. See the file COPYING for details + +This game is maintained by +Andreas Beckermann +Inge Wallin + + diff --git a/kpoker/TODO b/kpoker/TODO new file mode 100644 index 00000000..fb954169 --- /dev/null +++ b/kpoker/TODO @@ -0,0 +1,38 @@ + * Now + + + * Soon + + + * Further code cleanups: + + - Separate the kpok class into a game object that holds all the + players, what state the game is in, the pot, etc, and a view of + the same. + + This should result in a new class PokerGame and another one + PokerGameView. The class kpok (why the name abbreviation?) must + die. + + - Separate card blinking into the card widget. + + - Separate the waving motion into the winner box widget. + + - CardImages shouldn't inherit QWidget. + + - Make CardWidget::heldLabel private. + + * Betting figures are funny. The program indicates that we have bet + even before the "Draw" or "Fold" buttons are pressed. Then the bet + amount decreases again. The correct behaviour is to not add the + amount until the user OKs it, i.e. presses "See". + + Also, the "Adjust Bet" button doesn't look like it does anything as + it is now, although it actually does. + + * Rewrite the README file. It is totally out of date. + + * The documentation is totally out of sync with the program. + + * Hiscore table? + diff --git a/kpoker/betbox.cpp b/kpoker/betbox.cpp new file mode 100644 index 00000000..e152573f --- /dev/null +++ b/kpoker/betbox.cpp @@ -0,0 +1,136 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include +#include + +#include "betbox.h" + + +BetBox::BetBox(QWidget* parent, const char* name) + : QGroupBox(parent, name) +{ + QVBoxLayout* topLayout = new QVBoxLayout(this, 1, 1); + QGridLayout* g = new QGridLayout(topLayout, 2, 2, 1); + QHBoxLayout* l = new QHBoxLayout(topLayout, 1); + + bet5Up = new QPushButton(this); + g->addWidget(bet5Up, 0, 0); + bet10Up = new QPushButton(this); + g->addWidget(bet10Up, 0, 1); + bet5Down = new QPushButton(this); + g->addWidget(bet5Down, 1, 0); + bet10Down = new QPushButton(this); + g->addWidget(bet10Down, 1, 1); + + adjustBet = new QPushButton(this); + l->addWidget(adjustBet, 0); + l->addStretch(1); + foldButton = new QPushButton(this); + l->addWidget(foldButton, 0); + + bet5Up->setText(QString("+%1").arg(KGlobal::locale()->formatMoney(5))); + bet10Up->setText(QString("+%1").arg(KGlobal::locale()->formatMoney(10))); + bet5Down->setText(QString("-%1").arg(KGlobal::locale()->formatMoney(5))); + bet10Down->setText(QString("-%1").arg(KGlobal::locale()->formatMoney(10))); + adjustBet->setText(i18n("Adjust Bet")); + foldButton->setText(i18n("Fold")); + + //connects + connect(bet5Up, SIGNAL(clicked()), SLOT(bet5UpClicked())); + connect(bet10Up, SIGNAL(clicked()), SLOT(bet10UpClicked())); + connect(bet5Down, SIGNAL(clicked()), SLOT(bet5DownClicked())); + connect(bet10Down, SIGNAL(clicked()), SLOT(bet10DownClicked())); + connect(foldButton, SIGNAL(clicked()), SLOT(foldClicked())); + connect(adjustBet, SIGNAL(clicked()), SLOT(adjustBetClicked())); + + stopRaise(); +} + + +BetBox::~BetBox() +{ + delete bet5Up; + delete bet10Up; + delete bet5Down; + delete bet10Down; + delete adjustBet; + delete foldButton; +} + + +void BetBox::bet5UpClicked() +{ + emit betChanged(5); +} + +void BetBox::bet10UpClicked() +{ + emit betChanged(10); +} + +void BetBox::bet5DownClicked() +{ + emit betChanged(-5); +} + +void BetBox::bet10DownClicked() +{ + emit betChanged(-10); +} + + +void BetBox::adjustBetClicked() +{ + emit betAdjusted(); +} + + +void BetBox::foldClicked() +{ + emit fold(); +} + + +void BetBox::beginRaise() +{ + adjustBet->setEnabled(true); + foldButton->setEnabled(true); + + bet5Up->setEnabled(false); + bet10Up->setEnabled(false); + bet5Down->setEnabled(false); + bet10Down->setEnabled(false); +} + + +void BetBox::stopRaise() +{ + adjustBet->setEnabled(false); + foldButton->setEnabled(false); + + bet5Up->setEnabled(true); + bet10Up->setEnabled(true); + bet5Down->setEnabled(true); + bet10Down->setEnabled(true); +} + + +#include "betbox.moc" diff --git a/kpoker/betbox.h b/kpoker/betbox.h new file mode 100644 index 00000000..2aa54894 --- /dev/null +++ b/kpoker/betbox.h @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef BETBOX_H +#define BETBOX_H + +#include + +class QPushButton; + + +/** + * This class provides a QGroupBox with several button + * + * The bet up / down buttons are used to change the player bet directly, + * the adjustBet and out buttons depend on the computers bet + * @short This is a box with several buttons used to bet / raise + **/ +class BetBox : public QGroupBox +{ + Q_OBJECT + + public: + BetBox(QWidget* parent = 0, const char* name = 0); + ~BetBox(); + + + /** + * Disables the usual bet up/down buttons and shows the adjust bet and fold buttons + * + * Used when the compputer player raised the players bet / raise + **/ + void beginRaise(); + + /** + * Hides the Adjust bet / Fold buttons and enables the usual bet up / down buttons + **/ + void stopRaise(); + + signals: + /** + * This signal is emitted when the user clicks on a bet up / down button + * + * The Value of the Button is sent as a parameter (change) + **/ + void betChanged(int change); + + /** + * This signal is emitted when the user clicks on the adjust bet button + **/ + void betAdjusted(); + + /** + * This signal is emitted when the user clicks on the fold button + **/ + void fold(); + + protected slots: + /** + * Emits the signal @ref betChanged(5) + **/ + void bet5UpClicked(); + + /** + * Emits the signal @ref betChanged(10) + **/ + void bet10UpClicked(); + + /** + * Emits the signal @ref betChanged(-5) + **/ + void bet5DownClicked(); + + /** + * Emits the signal @ref betChanged(-10) + **/ + void bet10DownClicked(); + + /** + * Emits the signal @ref betAdjusted() + **/ + void adjustBetClicked(); + + /** + * Emits the signal @ref fold() + **/ + void foldClicked(); + + + private: + QPushButton *bet5Up; + QPushButton *bet10Up; + QPushButton *bet5Down; + QPushButton *bet10Down; + QPushButton *adjustBet; + QPushButton *foldButton; +}; + + +#endif diff --git a/kpoker/defines.h b/kpoker/defines.h new file mode 100644 index 00000000..3da071f6 --- /dev/null +++ b/kpoker/defines.h @@ -0,0 +1,75 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +/* Some defines for the outlook etc. */ +#ifndef DEFINES_H +#define DEFINES_H + + +// ---------------------------------------------------------------- +// Graphical layout + + +#define cardHeight 96 +#define cardWidth 72 + +#define cardDistFromTop 20 +#define cardDistFromBottom 10 +#define cardHDist 10 + +#define CLHBorderDistance 5 +#define CLHDistFromTop 180 +#define CLHWidth 140 + +// Some derived constants +#define PLAYERBOX_WIDTH ((cardWidth + cardHDist) * PokerHandSize + cardHDist + 135) +#define PLAYERBOX_HEIGHT (cardHeight + cardDistFromTop + cardDistFromBottom) +#define DISTANCE_FROM_2ND_BOX 100 + +#define wonLabelVDist (cardHeight + cardDistFromTop + cardDistFromBottom + 10) +#define clickToHoldVDist 130 + +#define BORDER 10 +#define PLAYERBOX_BORDERS 15 +#define PLAYERBOX_HDISTANCEOFWIDGETS 10 + + +#define MAX_PLAYERS 2 // TODO: maximal 4-> see lskat for computer players + + +// ---------------------------------------------------------------- +// Default values + + +#define START_MONEY 100 +#define DEFAULT_PLAYERS 1 // change to 2 later + +// Will be overridden by config file: +#define SOUND_DEFAULT true +#define BLINKING_DEFAULT true +#define SHOWNEWGAME_DEFAULT false +#define ADJUST_DEFAULT true +#define LOADGAME_DEFAULT true + +// changeable in OptionsDlg (and will be overridden by config file): +#define DRAWDELAY 300 +#define MIN_BET 5 +#define MAX_BET 20 +#define CASH_PER_ROUND 5 + + +#endif diff --git a/kpoker/global.h b/kpoker/global.h new file mode 100644 index 00000000..d4755abd --- /dev/null +++ b/kpoker/global.h @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __GLOBAL__H__ +#define __GLOBAL__H__ + +#include +#include + +extern KLocale *locale; +extern KConfig *conf; + +#endif + diff --git a/kpoker/hi128-app-kpoker.png b/kpoker/hi128-app-kpoker.png new file mode 100644 index 0000000000000000000000000000000000000000..e2a5570a2c70820e6ba98183af09b8c181439e82 GIT binary patch literal 8605 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 Bj<^5- literal 0 HcmV?d00001 diff --git a/kpoker/hi16-app-kpoker.png b/kpoker/hi16-app-kpoker.png new file mode 100644 index 0000000000000000000000000000000000000000..38b3a8c0bdfedd49434d3e9d713461aab0d13591 GIT binary patch literal 646 zcmV;10(t$3P)fE{azHbXZASjphz;E}Q=bQ%){BMb& zfq|j!mX;oqN*O$lIF5faSZf6!Ha9nm6B83-XS#;(bqqeZU&_xfetPxju^t^6c{rF( z-}>3shH7X)SbI2m7(i`pEnmN^{CGE4DEEv$e_7vrS~bcWw<|U73GFsDx!*fGSzcO# zLg9~Rwzjsiv%R$~&vjfcUQ8=L3BrRYbFE+2c%F0)j8}VmxwNoAW@?HnZEf`S_XFU% zEjmH5zcFv0+zm=yePTPR?#(?L(^_kU5CD9b zo`!68pY`>90P8rQoG6)0Hnnd|UpPCvOkHervEE+RLI||hWHK3~l!$|^)))X{-^a|% z5V>ykWGaKLZ*U+5Lu!Sk^o_Z5E4Meg@_7P6crJiNL9pw)e1;Xm069{HJUZAPk55R%$-RIA z6-eL&AQ0xu!e<4=008gy@A0LT~suv4>S3ILP<0Bm`DLLvaF4FK%)Nj?Pt*r}7;7Xa9z9H|HZjR63e zC`Tj$K)V27Re@400>HumpsYY5E(E}?0f1SyGDiY{y#)Yvj#!WnKwtoXnL;eg03bL5 z07D)V%>y7z1E4U{zu>7~aD})?0RX_umCct+(lZpemCzb@^6=o|A>zVpu|i=NDG+7} zl4`aK{0#b-!z=TL9Wt0BGO&T{GJWpjryhdijfaIQ&2!o}p04JRKYg3k&Tf zVxhe-O!X z{f;To;xw^bEES6JSc$k$B2CA6xl)ltA<32E66t?3@gJ7`36pmX0IY^jz)rRYwaaY4 ze(nJRiw;=Qb^t(r^DT@T3y}a2XEZW-_W%Hszxj_qD**t_m!#tW0KDiJT&R>6OvVTR z07RgHDzHHZ48atvzz&?j9lXF70$~P3Knx_nJP<+#`N z#-MZ2bTkiLfR>_b(HgWKJ%F~Nr_oF3b#wrIijHG|(J>BYjM-sajE6;FiC7vY#};Gd zST$CUHDeuEH+B^pz@B062qXfFfD`NpUW5?BY=V%GM_5c)L#QR}BeW8_2v-S%gfYS= zB9o|3v?Y2H`NVi)In3rTB8+ej^> zQ=~r95NVuDChL%G$=>7$vVg20myx%S50Foi`^m%Pw-h?Xh~i8Mq9jtJloCocWk2Nv zrJpiFnV_ms&8eQ$2&#xWpIS+6pmtC%Q-`S&GF4Q#^mhymh7E(qNMa}%YZ-ePrx>>xFPTiH1=E+A$W$=bG8>s^ zm=Bn5Rah$aDtr}@$`X}2l~$F0mFKEdRdZE8)p@E5RI61Ft6o-prbbn>P~)iy)E2AN zsU20jsWz_8Qg>31P|s0cqrPALg8E|(vWA65poU1JRAaZs8I2(p#xiB`SVGovRs-uS zYnV-9TeA7=Om+qP8+I>yOjAR1s%ETak!GFdam@h^# z)@rS0t$wXH+Irf)+G6c;?H29p+V6F6oj{!|o%K3xI`?%6x;DB|x`n#ibhIR?(H}Q3Gzd138Ei2)WAMz7W9Vy`X}HnwgyEn!VS)>mv$8&{hQn>w4zwy3R}t;BYlZQm5)6pty=DfLrs+A-|>>;~;Q z_F?uV_HFjh9n2gO9o9Q^JA86v({H5aB!kjoO6 zc9$1ZZKsN-Zl8L~mE{`ly3)1N^`o1+o7}D0ZPeY&J;i;i`%NyJ8_8Y6J?}yE@b_5a zam?eLr<8@mESk|3$_SkmS{wQ>%qC18))9_|&j{ZT zes8AvOzF(F2#DZEY>2oYX&IRp`F#{ADl)1r>QS^)ba8a|EY_^#S^HO&t^Rgqwv=MZThqqEWH8 zxJo>d=ABlR_Bh=;eM9Tw|Ih34~oTE|= zX_mAr*D$vzw@+p(E0Yc6dFE}(8oqt`+R{gE3x4zjX+Sb3_cYE^= zgB=w+-tUy`ytONMS8KgRef4hA?t0j zufM;t32jm~jUGrkaOInTZ`zyfns>EuS}G30LFK_G-==(f<51|K&cocp&EJ`SxAh3? zNO>#LI=^+SEu(FqJ)ynt=!~PC9bO$rzPJB=?=j6w@a-(u02P7 zaQ)#(uUl{HW%tYNS3ItC^iAtK(eKlL`f9+{bJzISE?u8_z3;~C8@FyI-5j_jy7l;W z_U#vU3hqqYU3!mrul&B+{ptt$59)uk{;_4iZQ%G|z+lhASr6|H35TBkl>gI*;nGLU zN7W-nBaM%pA0HbH8olyl&XeJ%vZoWz%6?Y=dFykl=imL}`%BMQ{Mhgd`HRoLu6e2R za__6DuR6yg#~-}Tc|Gx_{H@O0eebyMy5GmWADJlpK>kqk(fVV@r_fLLKIeS?{4e)} z^ZO;zpECde00d`2O+f$vv5tKEQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(` z>RI+y?e7jKeZ#YO-C14T(hK~#9!?3GVwTtytmKfjsTec890WH(~WpKXe@L{bW& zskzickoH<84oqv6biYRn*%>)VCMIkAK%~aHzVGA4r3?|X&f3@Dq$GBw!C}+5S$xs93+v5 zWZ!RY-muP5sx@a{IJR=i6}`Ok+vbn9|FC1;I)DD7&o5qlFW%lppFN9AO+ir%;Ue

    v6H}871paEb+uyBUc-vHKwdvC0qIpmhghhexbdY_~ z?t%B#d7d*iHpci^EwolmH{ZE(O`SZ_=wLyhGW|+(=FJbUn(=x)EE)~e@#9R+&SLW% zj6s3`ya#I`%RmGXAxRSQJZB^@&U;mRRIfMg*1@1bz?Gm|ZM>w)<+9cygyk|y1U)i> zbUJh|U#7dZhTq=CjEK|lu}O}OC?mA zl5cE~-n_}~>MHim9ZV1mp~jde!idZA$5Gec7hYB{V=^ z0Ph?lXU*eR<*J;??Pi(FtcQ;vC23rjPvn>WL4A;2;P{)AVV1rRj4U zqkrobK~XR`ecC(k$E~#|qHd2$rGkjiPgAtkOU7D@wU&Cleqgs1MS)pbB8?(OXJ+uu zNgT(%TCGy8R?%9MrYTVrdE>puIY+P8!&*D+E~OOS`)ABrOBMv!i3uL;?069wL{XGQ zQN-Nb96=D!YPCp`BsFQ8+B8kcvWzUtD2l=Z@;s=umZB)Uwbt+M?hb%Q{eJ(eEX&^S z^?Eqx7z_qPQS_yRVYsxgu&@e{=Xvg&vj<4x0Vg8K*4EZFAgtADm*(f^Ul)<0)oOhc u$MJRfk6uX)i~|mM40!qf2RU@`KLY^yW3mbxxJJ|f0000oA;MUmNgn?;g8z!Ezkwya2?h(!Q{5(#M(ivV#@BEm$CvCNop2F>^}(>?C4 z=Y3?+k9lA(Od_%ICza|})vfb==iKw%D!7mP_>Lj^=Q7{{?*9yF%*_1gwbQ3xq1`T9t(_PhMbv6IV*qfj7spz=g=ui#Ifu0t zYc0kYp_D2vUcC76@#Du|1k%w%FFZf}lT*JLo1ECKd_`VpzPj?or+@#$uU<;8{^>93 z@R1`k--ms-=H@8!9P#9n`1|)mmfbl;A?|uV2m-<|L~Gpus=@Brqfh<(^x1mD_flbS z(jkQ6dyT`7iw9nL^TKbR{jqXI(crPi$ZIt&HJj8Qd=Pv2@;#HsaZIDpU}|cLEKL%i zvFoYlUs6+}UKyA7;cC8F0q3{C`608GV2ogY7ADB?JS}g>SLLl#yaTr3H;kGhvHDEF*7XOo}K@g=BVHj=@3xV`~ zvbj0pGiM-<37>g}@`)1&&jYP{7U~fW4;Wg@@QV+p{!8KCRbvrwK9)9>{AJ$ zl{&t@k`iC(AR=Ssz+R?b)UnrVkbUq0W_cNxB)B|h=c!Xv zo_h{yZLe?o^R2bR3iOJNHebH}`}T)#H*JyGV)Z)h%a^g;RrKv#w=Vqdf^ATiBdKZHRJ!9vd6`<>Vt%2T}u4ZeAkA{vRBC zcH!#cPlpuyCFfIIZa}Eit)wFD}b4##o>pMbS)}rbJORtdMgKtu;ka zP%f8`4h2L}#KD6H?}>Pr{n8Sxx8A~k^ilFs2|GUSfH2SVAW0IOb2#VleIIKrS(cIK zIo8@pX{}jUSol`NTX6CuxBJ7+=br@X9mj3q&&@;a?2Bnk(5~b9tbB=4*t`S8MBO@bZS;mbUHwc0NA;e7;1cBPKXAfFy zthG4jkW%8D8#WDW{E!SXAI#NSqqW8uLs1m?zK;+>0dcq6ecL%VTPl^jJkLqf6z3e} za+xGaT5%lzS)Ds~Zm!em?3Pk0=bY18+x7MJ-1V@9d@}}uH+8ltiXxX%%K7>EzX2`a zoo2JSw_Gm2&}cLsl2Vc+3C(8n%lY~Fmw}H3unU+30($HcAu^D<}V`9IJ9nVB;%k9o{v9`pErhWc+C@CE1!SKhfs zF5cYlybh_2PR(v00Cw)&xqaj2&07-Zr1#zH@4ax& zd8JfkczF23*Is*V5b#-By>#2=6&t>{d->XLe!q9g(q%yq$f@ZmGjZnRUrrtQ!>ecC z`STw5q?!@vI)3cfn`_pty^%9#z&R+Fp-_P3%P-4amkRGa&N-A)96EGpWaGw-Hv!`S z;rbu%zV)u3?^&^|Yk`yohrN+gPk3HkdwM_wKJ{q8$qVn*k3xn7j&uD<#zV^2Ow z?)KYp`}ZUI`iLHV6s!gBKi423#AQ9tBALtO@ZLvZ81}_+Jg%01d&kxv{NnkZz91?~ zi`N>V1P!5p^9XI&aO+LCSI2(++tC;A`H@yi6}3{`a>ouj*RJK<>C;Shb`mUGh8!No zw-C6PL6=I*dyf!;{{DWHO4Bb%65+ki1Np@p?)ypi%7sydIwH^zNQ9D2`>`Hx9m;xE z?zrooGjG527ahlOC?F8tlke~6*zx1&)vLK?%^H$w6)wT(OC=_R;NZc7^z`&lEEbVc z0(dYEmadQ1Zoa+Rp%Aiw2o)$0veg6N9eCr(uM*_1y=9kPwQ5x{iXx5osa&k3yHH?R zUmx+TB)*&ov}-G+B#NTu`<6BW`uod;E_F&PauV_ZggPZ4M4I8&LOp?+6VwEVd|1#c zSFS8ZQKZtzM&PA9|b6z7LkLA7fw@u|71<4eT(KGT5F;x zYUd@;8h`E_rNA=buOR_JWl7>1oPOKaD$kmIaSI zf+!U5wc4C-Tl<^czYvKB&^-lN`u7z11&aP{3oshAP6=2*IVhcsIrYa+8Zy=?fmW?2 zNz%-PRwINE2y2OMyN!5o5PRxW`XUGrT2p=TMSP_~=K~KAtX-RCTpI#W6rr_l-dk(& z9syKsY3kEpVQy6-6{81gEbP{F6%|53rR*3x6H`qRI@Wa&pR7YNJ|$-ll0~6arx_uf zBfRlOa$B|_wEiq{g+M4peQ=O?a1bG7TG~W2pUug0ABnDLKGzP$fUnHk<{=*5Z(0%|Y6O!>Lz@W!NJ1c+k@0v0{< z3=8hRA7L%>B2H+PX$w41t`F^frS{Q@ii|p#9u<_0Bq|7s1+byy{D>s3XjCqudi1Hi zaPptJ)%&ycIY&Z(UcNllex;Hob*+Y4vV=vCKTdx8_H-}RCVw*HTP2#kYSb0X*gNO; z{_vMR`qkZ6ZNK5#!r?(NF*$?vkP8K!1K;4(bl=qJryhREj=sDXnAFTd7LB&ksQ2VI zZ>Ia9hp6q}55}ODE~T(*7lm7HN#)=@qTTe{CW&XE4tzrC#NWa{{q$QsLz}*SRhJX_ zFhEF2VhocXzC~$dZ05|+8;30X3phWA!0bUUxc44}pm^VX6z{zkZ!J>m2Dda5Gg)11 zC7$K|RyZJ8y?XV-+qT?%M?M$ggu-NfsT_+UShTQX#Z`UZd1P?#jepc?wbRWU$e@%W zpU+<~U~}LsM;hN`#KQ1a7$D28=Q7H*#MBZgP^zC!S!<&YhUSL245d{S0IhX%y|C6|jGvuAKd~{!1JlmA<67$tQ4}#UGJ;Zy z?(S~FFvNS$#KZ)(T8-Y`UaHk9aU2h8t+jN{31bX#95)w_4B4Qqv%$sW(vCnJ$IV%- z)p+L|`FtL&wGM&+W6Zg7x%_%ZN5=+hEnyh4V8McBK1eC4)oLV3LKH=uKYxD6TKk$F z8yhPZ3I$m#7O#Xl0OmL$d*+}lIak*Shs?}=J8jaSv-I1dA zo_f9B^z!U)E0s#w7-ImTl(I*U9vuXxfeLV}Ua$Y=t+(DP#c{l8%a$#@ixw>+m&@Ut zV`ylowr}6Q;rHHq?*-tmz@z~BfUf~dxbU#olO|Wfm4l~oz|gpkfiYkdC<7s|99Rcj z-__N1T~ANXRr!2AS1y+)Mn^|Ku-1MEya#*)Obb8*5fA{gj**%JUP%I3q0zT#l|A literal 0 HcmV?d00001 diff --git a/kpoker/hi64-app-kpoker.png b/kpoker/hi64-app-kpoker.png new file mode 100644 index 0000000000000000000000000000000000000000..9ad09b3b760bbd4c58567c9fe0b11544daeda9a7 GIT binary patch literal 3413 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 literal 0 HcmV?d00001 diff --git a/kpoker/kpaint.cpp b/kpoker/kpaint.cpp new file mode 100644 index 00000000..c6322aee --- /dev/null +++ b/kpoker/kpaint.cpp @@ -0,0 +1,209 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include + +// KDE includes +//#include +#include +#include + +// own includes +#include "defines.h" +#include "poker.h" +#include "kpaint.h" + + +// ================================================================ +// class CardImages + + +QPixmap *CardImages::m_cardPixmaps; +QPixmap *CardImages::m_deck; + + +CardImages::CardImages(QWidget* parent, const char* name) + : QWidget(parent, name) +{ + m_cardPixmaps = new QPixmap[numCards]; + m_deck = new QPixmap; + + // Hide the window. + // FIXME: Why is this a QWidget? + hide(); + // loadCards(); +} + + +CardImages::~CardImages() +{ + delete[] m_cardPixmaps; + delete m_deck; +} + + +QPixmap * +CardImages::getCardImage(int card) const +{ + if (card == 0) + return m_deck; + else + return &m_cardPixmaps[card-1]; +} + + +// Load all the card images from the directory 'cardDir'. + +void +CardImages::loadCards(QString cardDir) +{ + for (int i = 0; i < numCards; i++) { + QString card = KCardDialog::getCardPath(cardDir, i + 1); + + if (card.isEmpty() || !m_cardPixmaps[i].load(card)) { + if (!card.isEmpty()) + kdWarning() << "Could not load " << card << " trying default" << endl; + card = KCardDialog::getCardPath(KCardDialog::getDefaultCardDir(), i+1); + if (!m_cardPixmaps[i].load(card)) { + kdError() << "Could not load " << card << endl; + } + } + } +} + + +// Load the backside of the card deck from the file name in 'path'. + +void +CardImages::loadDeck(QString path) +{ + if (!m_deck->load(path)) { + kdWarning() << "Could not load deck - loading default deck" << endl; + path = KCardDialog::getDefaultDeck(); + if (!m_deck->load(path)) + kdError() << "Could not load deck" << endl; + } +} + + +// ================================================================ +// class CardWidget + + +extern CardImages *cardImages; + + +CardWidget::CardWidget( QWidget *parent, const char *name ) + : QPushButton( parent, name ) +{ + m_held = false; + + setBackgroundMode( NoBackground ); // disables flickering + connect(this, SIGNAL(clicked()), this, SLOT(ownClick())); + + setFixedSize(cardWidth, cardHeight); +} + + +void CardWidget::paintCard(int cardType) +{ + // Remap the card from the natural poker card values to the names + // used by the card decks in KDE. + // + // FIXME: This is an ugly hack. The correct way would be to add a + // method paintCard(CardValue card), but that will have to + // wait until we break out the card stuff to its own file so + // that there is something to include. + int card; + int rank; + int suit; + + if (cardType == 0) + card = 0; + else { + suit = (cardType - 1) % 4; + rank = (cardType - 1) / 4; + + rank = 12 - rank; // ace-two --> two-ace + switch (suit) { + case 0: break; // Clubs + case 1: suit = 3; break; // Diamonds + case 2: suit = 1; break; // Spades + case 3: suit = 2; break; // Hearts + } + card = rank * 4 + suit + 1; + } + + // Select the pixmap to use. +#if 0 + if (card == 0) { + m_pm = &cardImage->m_deck; + } else { + m_pm = &cardImage->m_cardPixmaps[card-1]; + } +#else + m_pm = cardImages->getCardImage(card); +#endif + + // Set the pixmap in the QPushButton that we inherit from. + if ( m_pm->size() != QSize( 0, 0 ) ) { // is an image loaded? + setPixmap(*m_pm); + } +} + + +void CardWidget::repaintDeck() +{ + setPixmap(*m_pm); + setFixedSize(cardImages->getWidth(), cardImages->getHeight()); + + ((QWidget*) parent())->layout()->invalidate(); + ((QWidget*) parent())->setFixedSize( ((QWidget*) parent())->sizeHint()); +} + + +/* Emit the pClicked signal. + */ + +void CardWidget::ownClick() +{ + emit pClicked(this); +} + + +bool CardWidget::getHeld() +{ + return m_held; +} + + +void CardWidget::setHeld(bool newheld) +{ + m_held = newheld; +} + + +bool CardWidget::toggleHeld() +{ + m_held = !m_held; + return m_held; +} + + +#include "kpaint.moc" diff --git a/kpoker/kpaint.h b/kpoker/kpaint.h new file mode 100644 index 00000000..ae4c1d29 --- /dev/null +++ b/kpoker/kpaint.h @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __CardWidget__ +#define __CardWidget__ + + +#include + +#include +#include + + +class QLabel; + + +/** + * This class loads all pictures, first. So they don't have to be loaded after the start again + * @short A help class for loading the pictures + **/ + +class CardImages : public QWidget +{ + Q_OBJECT + + public: + CardImages( QWidget *parent = 0, const char *name = 0 ); + ~CardImages(); + + // FIXME: Use CardValue instead of int when the cards are in their + // own file. + QPixmap *getCardImage(int card) const; + QPixmap *getDeck() const { return m_deck; } + + int getWidth() const { return m_cardPixmaps[0].width(); } + int getHeight() const { return m_cardPixmaps[0].height(); } + + void loadDeck(QString path); + void loadCards(QString cardDir); + + private: + static QPixmap *m_cardPixmaps; + static QPixmap *m_deck; +}; + + +/** + * This class extends the QPushButton by some methods / variables to provide a card with held labels and so on + * + * @short The cards + **/ +class CardWidget : public QPushButton +{ + Q_OBJECT + + public: + CardWidget( QWidget *parent=0, const char *name=0 ); + + /** + * Paints the deck if cardType = 0 or the card specified in cardType + * @param cardType the card to be painted. 0 is the deck + **/ + void paintCard(int cardType); + + /** + * @return The held status of this card + **/ + bool getHeld(); + + /** + * Sets the new held + * @param newHeld specifies the new held + **/ + void setHeld(bool newheld); + + /** + * Toggle the boolean member m_held. + **/ + bool toggleHeld(); /* returns the new value of held*/ + + + void repaintDeck(); + + signals: + /** + * This signal is emitted by @ref ownClick() + * @param CardWidget is a this pointer + **/ + void pClicked(CardWidget *); + + protected slots: + /** + * Emits the signal @ref pClicked() when the player clicks on the card + **/ + void ownClick(); + + + private: + QPixmap *m_pm; // the loaded pixmap + bool m_held; + + public: + QLabel *heldLabel; +}; + + +#endif diff --git a/kpoker/kpoker.cpp b/kpoker/kpoker.cpp new file mode 100644 index 00000000..2956b42c --- /dev/null +++ b/kpoker/kpoker.cpp @@ -0,0 +1,1484 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +// QT includes +#include +#include +#include +#include +#include + +// KDE includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// own includes +#include "betbox.h" +#include "kpaint.h" +#include "optionsdlg.h" +#include "newgamedlg.h" +#include "player.h" +#include "playerbox.h" +#include "version.h" +#include "kpoker.h" +#include "defines.h" + + +// ================================================================ +// Class PokerGame + + +PokerGame::PokerGame(KRandomSequence *random) + : m_deck(random) +{ + // We don't need to save if we just started. + m_isDirty = false; + + // Some defaults. + m_type = SinglePlayer; +} + + +PokerGame::~PokerGame() +{ + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +void +PokerGame::dealCards(PokerPlayer *player, bool skip[]) +{ + CardValue card; + + for (int i = 0; i < PokerHandSize; i++) { + if (skip[i]) + continue; + + card = m_deck.getTopCard(); + + player->takeCard(i, card); + } +} + + +void +PokerGame::activatePlayer(PokerPlayer *player) +{ + if (!m_activePlayers.contains(player)) { + m_activePlayers.append(player); + m_removedPlayers.remove(player); + } +} + + +void +PokerGame::inactivatePlayer(PokerPlayer *player) +{ + if (m_activePlayers.contains(player)) { + m_activePlayers.remove(player); + m_removedPlayers.append(player); + } +} + + +void +PokerGame::newGame(PokerGameType type, + int numPlayers, PokerPlayer *players, + int minBet, int maxBet) +{ + m_type = type; + + // Store the players. These are never changed in the current implementation. + m_numPlayers = numPlayers; + m_players = players; + + setBettingLimits(minBet, maxBet); + + setState(StateStartRound); + + // Initiate player arrays. Make all players active from the start. + m_activePlayers.clear(); + m_removedPlayers.clear(); + for (unsigned int i = 0; i < m_numPlayers; i++) + m_activePlayers.append(&m_players[i]); + + // Inform players how much they may bet. + for (unsigned int i = 0; i < m_activePlayers.count(); i++) + m_activePlayers.at(i)->setBetDefaults(m_minBet, m_maxBet); +} + + +void +PokerGame::newRound() +{ + // Reset the pot to zero. + m_pot = 0; + + // Collect the cards and shuffle them. + m_deck.reset(); + m_deck.shuffle(); + + // Clear the list of active and removed players. + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +// ================================================================ +// Global variables + + +CardImages *cardImages; + + +// ================================================================ +// Class kpok + + +kpok::kpok(QWidget *parent, const char *name) + : QWidget(parent, name), + m_game(&m_random) +{ + QString version; + + m_random.setSeed(0); + + // This class owns the players. Create them here. + // Currently we always allocate two - one human and one computer. + m_numPlayers = 2; // FIXME: Hard coded!! + m_players = new PokerPlayer[m_numPlayers]; + + // Initialize the first one to human... + m_players[0].setHuman(); + m_players[0].setName(i18n("You")); + + // ...and the rest to computer players. + for (int unsigned i = 1; i < m_numPlayers; i++) + m_players[i].setName(QString("Computer %1").arg(i-1)); + + lastHandText = ""; + + version = kapp->caption() + " " + KPOKER_VERSION; + setCaption( version ); + + mOptions = 0; + + playerBox = 0; + adjust = false; + + initWindow(); // FIXME: Change this name!!! + initSound(); + + if (!readEntriesAndInitPoker()) + exit(0); +} + + +kpok::~kpok() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + + drawDelay = mOptions->getDrawDelay(); + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + } + + KConfig* conf = kapp->config(); + conf->setGroup("General"); + conf->writeEntry("MinBet", m_game.getMinBet()); + conf->writeEntry("MaxBet", m_game.getMaxBet()); + // conf->writeEntry("CashPerRound", cashPerRound); + conf->writeEntry("DrawDelay", drawDelay); + + + delete[] m_players; + + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox; + + delete mOptions; +} + + +// Init the main window and load the configuration and card images. +// +// Only called by kpok::kpok() once. + +void kpok::initWindow() +{ + m_blinking = true; + m_blinkStat = 0; + m_blinkingBox = 0; + + // General font stuff. Define myFixedFont and wonFont. + QFont myFixedFont; + myFixedFont.setPointSize(12); + QFont wonFont; + wonFont.setPointSize(14); + wonFont.setBold(true); + + topLayout = new QVBoxLayout(this, BORDER); + QVBoxLayout* topInputLayout = new QVBoxLayout; + topLayout->addLayout(topInputLayout); + + QHBoxLayout* betLayout = new QHBoxLayout; + inputLayout = new QHBoxLayout; + inputLayout->addLayout(betLayout); + topInputLayout->addLayout(inputLayout); + + // The draw button + drawButton = new QPushButton(this); + drawButton->setText(i18n("&Deal")); + connect(drawButton, SIGNAL(clicked()), this, SLOT(drawClick())); + inputLayout->addWidget(drawButton); + inputLayout->addStretch(1); + + // The waving text + QFont waveFont; + waveFont.setPointSize(16); + waveFont.setBold(true); + QFontMetrics tmp(waveFont); + + // The widget where the winner is announced. + mWonWidget = new QWidget(this); + inputLayout->addWidget(mWonWidget, 2); + mWonWidget->setMinimumHeight(50); //FIXME hardcoded value for the wave + mWonWidget->setMinimumWidth(tmp.width(i18n("You won %1").arg(KGlobal::locale()->formatMoney(100))) + 20); // workaround for width problem in wave + QHBoxLayout* wonLayout = new QHBoxLayout(mWonWidget); + wonLayout->setAutoAdd(true); + + wonLabel = new QLabel(mWonWidget); + wonLabel->setFont(wonFont); + wonLabel->setAlignment(AlignCenter); + wonLabel->hide(); + inputLayout->addStretch(1); + + // The pot view + potLabel = new QLabel(this); + potLabel->setFont(myFixedFont); + potLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + inputLayout->addWidget(potLabel, 0, AlignCenter); + + // Label widget in the lower left. + clickToHold = new QLabel(this); + clickToHold->hide(); + + // Timers + blinkTimer = new QTimer(this); + connect( blinkTimer, SIGNAL(timeout()), SLOT(bTimerEvent()) ); + + waveTimer = new QTimer(this); + connect( waveTimer, SIGNAL(timeout()), SLOT(waveTimerEvent()) ); + + drawTimer = new QTimer(this); + connect (drawTimer, SIGNAL(timeout()), SLOT(drawCardsEvent()) ); + + // and now the betUp/Down Buttons + betBox = new BetBox(this, 0); + betLayout->addWidget(betBox); + connect(betBox, SIGNAL(betChanged(int)), this, SLOT(betChange(int))); + connect(betBox, SIGNAL(betAdjusted()), this, SLOT(adjustBet())); + connect(betBox, SIGNAL(fold()), this, SLOT(out())); + + // some tips + QToolTip::add(drawButton, i18n("Continue the round")); + QToolTip::add(potLabel, i18n("The current pot")); + + // Load all cards into pixmaps first -> in the constructor. + cardImages = new CardImages(this, 0); + + // The configuration + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + // Load the card deck. + if (conf->readBoolEntry("RandomDeck", true)) { + cardImages->loadDeck(KCardDialog::getRandomDeck()); + } else { + cardImages->loadDeck(conf->readPathEntry("DeckPath", KCardDialog::getDefaultDeck())); + } + if (conf->readBoolEntry("RandomCardDir", true)) { + cardImages->loadCards(KCardDialog::getRandomCardDir()); + } else { + cardImages->loadCards(conf->readPathEntry("CardPath", + KCardDialog::getDefaultCardDir())); + } +} + + +// Start a new complete game (not a round). + +void kpok::newGame() +{ + NewGameDlg* newGameDlg; + + // Get a "New Game" dialog. + lastHandText = ""; + newGameDlg = new NewGameDlg(this); + newGameDlg->hideReadingFromConfig(); + + if (newGameDlg->exec()) { + stopBlinking(); + stopDrawing(); + stopWave(); + + // Delete the old values. + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox;// necessary? + + int numPlayers = newGameDlg->getPlayers(); + + // Most things will be done in initPoker. + initPoker(numPlayers); + + // Set/show the name and money of all players. + for (unsigned int i = 0; i < m_numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + playerBox[i]->showName(); + m_players[i].setCash((newGameDlg->money() >= m_game.getMinBet()) + ? newGameDlg->money() : START_MONEY); + m_game.setDirty(); + } + + // Show the money for all players and the pot. + paintCash(); + + // Activate the Draw button. + drawButton->setText(i18n("&Deal")); + drawButton->setEnabled(true); + } + + delete newGameDlg; +} + + +void kpok::newRound() +{ + bool onePlayerGame = false; + + m_game.newRound(); + + playerBox[0]->setHeldEnabled(false); + + if (m_numPlayers == 1) + onePlayerGame = true; + + readOptions(); // maybe some options have changed so check em + + if (m_players[0].getCash() < m_game.getMinBet()) + noMoney(); + else { + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (m_players[i].getCash() >= m_game.getMinBet()) + m_game.activatePlayer(&m_players[i]); + else + removePlayerFromRound(&m_players[i]); + } + } + + if (m_game.getNumActivePlayers() == 1 && m_game.getType() != SinglePlayer) + switchToOnePlayerRules(); + + m_blinkingBox = 0; + wonLabel->hide(); + stopBlinking(); + stopWave(); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->newRound(); + + // We are beginning a new round so every card is available. + drawAllDecks(); + playerBox[0]->showHelds(false); + + // Deal first cards of the round + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + drawCards(m_game.getActivePlayer(i), skip); + + if (m_game.getNumActivePlayers() > 1) { + findHumanPlayer()->changeBet(m_game.getMinBet()); + m_game.bet(m_game.getMinBet()); + betBox->show(); + } + else { + m_game.getActivePlayer(0)->changeBet(cashPerRound); + betBox->hide(); + } + + paintCash(); + + drawTimer->start(drawDelay, TRUE); +} + + +void kpok::bet() +{ + // The players will bet, now. Player 1 (the human one ;-)) has + // already bet using the betBox ALL players (except player 1 who has + // already had) will get a chance to raise the value if nobody + // raises further that player will not get another chance, else he + // will. + + bool raised = false; + int oldMustBet = 0; + if (m_game.getState() == StateBet2) + oldMustBet = currentMustBet; + + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + // The betting has just begun. This can be either the first or + // the second betting phase (state == StateBet1 or StateBet2). + // FIXME: These state names must be changed! + + // Find out how much the other players must bet. + currentMustBet = findHumanPlayer()->getCurrentBet(); + + // First bet as usual. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + int playerBet = 0; + + if (player->getHuman()) + continue; + + if (m_game.getState() == StateBet1) // first bet phase + playerBet = player->bet(currentMustBet); + else if (m_game.getState() == StateBet2) // 2nd bet phase + playerBet = player->raise(currentMustBet); + + if (playerBet < currentMustBet) { + removePlayerFromRound(player); + // FIXME: This should be bet from the beginning! + if (m_game.getState() == StateBet1) + m_game.bet(m_game.getMinBet()); + i--; + } + else { + // The player is seeing or has raised. + + if (playerBet > currentMustBet) + raised = true; + + currentMustBet = playerBet; + m_game.bet(currentMustBet - oldMustBet); + } + } + } + else if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + + // The bet has been raised. + paintCash(); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + + // human player + if (player->getCurrentBet() < currentMustBet && player->getHuman()) { + removePlayerFromRound(player); + i--; + } + } + } + + paintCash(); + + + oldBet_raise = findHumanPlayer()->getCurrentBet(); // used by out() only + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + if (raised) { + if (m_game.getState() == StateBet1) + m_game.setState(StateRaise1); + else + m_game.setState(StateRaise2); + + if (adjust) + adjustBet(); + else + out(); // not necessary + betBox->setEnabled(true); + betBox->beginRaise(); + if (adjust) + emit statusBarMessage(i18n("Clicking on draw means you adjust your bet")); + else + emit statusBarMessage(i18n("Clicking on draw means you are out")); + } + else { + if (m_game.getState() == StateBet1) + m_game.setState(StateExchangeCards); + else + m_game.setState(StateSee); + } + } + else if (m_game.getState() == StateRaise1 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateExchangeCards); + } + else if (m_game.getState() == StateRaise2 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateSee); + } + + + // Check if player 1 is out -> for players > 2 + // TODO: maybe if (!m_activePlayers.contains(humanPlayer)) + // {exchangeCards(); bet(); displayerWinner_computer();return;} + + + // don't continue game if player 1 is alone + // TODO: port to players > 2 + // this is ONLY working for players <= 2 + if (m_game.getNumInactivePlayers() >= 1 + && m_game.getNumActivePlayers() == 1) + displayWinner_Computer(m_game.getActivePlayer(0), true); +} + + +void kpok::out() +{ + // weWillAdjustLabel->hide(); + emit clearStatusBar(); + + m_game.bet(oldBet_raise - findHumanPlayer()->getCurrentBet()); + findHumanPlayer()->changeBet(oldBet_raise + - findHumanPlayer()->getCurrentBet()); + paintCash(); + + // Go to next state immediately. (Previously we told the user to + // click the action button.) + drawClick(); + //drawButton->setText(i18n("&End Round")); +} + + +void kpok::adjustBet() +{ + emit clearStatusBar(); + + betChange(currentMustBet - findHumanPlayer()->getCurrentBet()); + paintCash(); +} + + +// Initiate for a completely new game (not a round). +// +// This is called from newGame(), readEntriesAndInitPoker(), loadGame(). +// + +void kpok::initPoker(unsigned int numPlayers) +{ + m_numPlayers = numPlayers; + + // Tell the game about the players also. + PokerGameType gametype = (numPlayers == 1) ? SinglePlayer : MultiPlayer; + + // Read some defaults. + kapp->config()->setGroup("General"); + int minBet = kapp->config()->readNumEntry("MinBet", MIN_BET); + int maxBet = kapp->config()->readNumEntry("MaxBet", MAX_BET); + + // Start a new poker game using the data found out above. + m_game.newGame(gametype, m_numPlayers, m_players, minBet, maxBet); + + // Not (yet) configurable + cashPerRound = CASH_PER_ROUND; + drawDelay = kapp->config()->readNumEntry("DrawDelay", DRAWDELAY); + + m_blinkingBox = 0; + currentMustBet = m_game.getMinBet(); + + // --- Graphics --- + + // Make all labels / boxes / cardwidgets for every player. + playerBox = new PlayerBox *[numPlayers]; + + for (int unsigned i = 0; i < numPlayers; i++) { + playerBox[i] = new PlayerBox(i == 0, this); + playerBox[i]->setPlayer(&m_players[i]); + if (i == 0) + topLayout->insertWidget(0, playerBox[i]); + else + topLayout->addWidget(playerBox[i]); + playerBox[i]->showName(); + + // If it has been deleted and created again it hasn't be shown + // correctly - hiding and re-showing solves the problem. + playerBox[i]->hide(); + playerBox[i]->show(); + } + + // Connects for player 1 + // + // FIXME: Make CardWidget::toggleHeld() work. + playerBox[0]->activateToggleHeld(); + connect(playerBox[0], SIGNAL(toggleHeld()), this, SLOT(toggleHeld())); + + // hide some things + playerBox[0]->showHelds(false); + wonLabel->hide(); + emit showClickToHold(false); + + // Different meaning of the status for single and multi player games. + if (m_game.getNumActivePlayers() > 1) { + setHand(i18n("Nobody"), false); + betBox->show(); + betBox->setEnabled(false); + potLabel->show(); + } + else { + setHand(i18n("Nothing")); + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + } + + // Some final inits. + drawStat = 0; + waveActive = 0; + fCount = 0; + + // Finally clear the pot and show the decks/cash - in one word: begin :-) + m_game.clearPot(); + drawAllDecks(); + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->repaintCard(); + } + paintCash(); + playerBox[0]->setHeldEnabled(false); +} + + +void kpok::paintCash() +{ + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->showCash(); + } + potLabel->setText(i18n("Pot: %1").arg(KGlobal::locale()->formatMoney(m_game.getPot()))); +} + + +void kpok::updateLHLabel() +{ + if (!lastHandText.isEmpty()) + setHand(lastHandText); + else if (m_game.getNumActivePlayers() > 1) + setHand(i18n("Nobody"), false); + else + setHand(i18n("Nothing")); +} + + +void kpok::setHand(const QString& newHand, bool lastHand) +{ + emit changeLastHand(newHand, lastHand); + + lastHandText = newHand; +} + + +void kpok::toggleHeld() +{ + if (m_game.getState() == StateBet1 || m_game.getState() == StateRaise1) + playSound("hold.wav"); +} + + +void kpok::drawClick() +{ + if (!drawButton->isEnabled()) + return; + + // If this is the start of a new round, then deal new cards. + if (m_game.getState() == StateStartRound) { + drawButton->setEnabled(false); + betBox->setEnabled(false); + newRound(); + } + else if (m_game.getState() == StateBet1) { // bet + emit showClickToHold(false); + + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateRaise1) { // continue bet + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateExchangeCards) { // exchange cards + drawButton->setEnabled(false); + playerBox[0]->setHeldEnabled(false); + betBox->setEnabled(false); + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()) + m_game.getActivePlayer(i)->exchangeCards(skip); + else { + for (int i = 0; i < PokerHandSize; i++) { + skip[i] = playerBox[0]->getHeld(i); + if (!skip[i]) + playerBox[0]->paintDeck(i); + } + } + drawCards(m_game.getActivePlayer(i), skip); + } + + if (playerBox[0]->getHeld(0)) + drawTimer->start(0, TRUE); + else + drawTimer->start(drawDelay, TRUE); + } + else if (m_game.getState() == StateBet2) { // raise + setBetButtonEnabled(false); + bet(); + + if (m_game.getState() == StateSee)//should be set in bet()->if no one has raised + drawClick(); + } + else if (m_game.getState() == StateRaise2) { + bet(); + if (m_game.getState() == StateSee) + drawClick(); + } + else if (m_game.getState() == StateSee) + winner(); +} + + +void kpok::drawCards(PokerPlayer* p, bool skip[]) +{ + m_game.dealCards(p, skip); +} + + +void kpok::displayWinner_Computer(PokerPlayer* winner, bool othersPassed) +{ + // Determine the box that contains the winner. + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (&m_players[i] == winner) + m_blinkingBox = i; + } + + // Give the pot to the winner. + winner->setCash(winner->getCash() + m_game.getPot()); + m_game.setDirty(); + + // Generate a string with winner info and show it. + QString label; + if (winner->getHuman()) + label = i18n("You won %1").arg(KGlobal::locale()->formatMoney(m_game.getPot())); + else + label = i18n("%1 won %2").arg(winner->getName()).arg(KGlobal::locale()->formatMoney(m_game.getPot())); + wonLabel->setText(label); + + // Start the waving motion of the text. + QFont waveFont; + waveFont.setBold(true); + waveFont.setPointSize(16); + QFontMetrics tmp(waveFont); + mWonWidget->setMinimumWidth(tmp.width(label) + 20); + + // Play a suitable sound. + if (winner->getHuman()) { + playSound("win.wav"); + wonLabel->hide(); + startWave(); + } + else { + playSound("lose.wav"); + wonLabel->show(); + } + + m_game.clearPot(); + m_game.setState(StateStartRound); + drawButton->setEnabled(true); + setHand(winner->getName(), false); + paintCash(); + + // Only start blinking if player 1 is still in. + if (m_game.isActivePlayer(&m_players[0]) && !othersPassed) + startBlinking(); + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::showComputerCards() +{ + // Don't show cards of 'out' players. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()){ + playerBox[i]->paintCard(drawStat); //TODO change: + + if (i == 1) //TODO : CHANGE! + playSound("cardflip.wav");//perhaps in playerbox or even in cardwidget + } + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + drawStat = 0; + } else { // only inc drawStat if not done with displaying + drawStat++; + showComputerCards(); + } +} + + +void kpok::setBetButtonEnabled(bool enabled) +{ + betBox->setEnabled(enabled); +} + + +void kpok::drawCardsEvent() +{ + if (!playerBox[0]->getHeld(drawStat)) { + playerBox[0]->paintCard(drawStat); + + playSound("cardflip.wav");//maybe in playerbox or even in cardwidget + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + betBox->setEnabled(true); + drawStat = 0; + + if (m_game.getState() == StateExchangeCards) { + if (m_game.getType() == SinglePlayer) + result(); + else { + // Now give players the chance to raise. + drawButton->setText(i18n("&See!")); + m_game.setState(StateBet2); + } + } else if (m_game.getState() == StateStartRound) { + playerBox[0]->setHeldEnabled(true); + + emit showClickToHold(true); + //clickToHold->show(); + //TODO: + m_game.setState(StateBet1); + drawButton->setText(i18n("&Draw New Cards")); + } + + } else { // only inc drawStat if not done with displaying + drawStat++; + // look at next card and if it is held instantly call drawCardEvent again + if (playerBox[0]->getHeld(drawStat)) + drawTimer->start(0,TRUE); + else + drawTimer->start(drawDelay,TRUE); + } +} + + +// Called to display the result in a single player game. + +void kpok::result() +{ + int testResult = m_game.getActivePlayer(0)->testHand(); + switch (testResult) { + case Pair: + if (m_game.getActivePlayer(0)->getHand().get_firstRank() < JACK) { + displayWin(i18n("Nothing"), 0); + break; + } + + displayWin(i18n("One Pair"), 5); + break; + + case TwoPairs: + displayWin(i18n("Two Pairs"), 10); + break; + + case ThreeOfAKind: + displayWin(i18n("3 of a Kind"), 15); + break; + + case Straight: + displayWin(i18n("Straight"), 20); + break; + + case Flush: + displayWin(i18n("Flush"), 25); + break; + + case FullHouse: + displayWin(i18n("Full House"), 40); + break; + + case FourOfAKind: + displayWin(i18n("4 of a Kind"), 125); + break; + + case StraightFlush: + displayWin(i18n("Straight Flush"), 250); + break; + + case RoyalFlush: + displayWin(i18n("Royal Flush"), 2000); + break; + + default: + displayWin(i18n("Nothing"), 0); + break; + } + + startBlinking(); + m_game.setState(StateStartRound); + + if (m_game.getActivePlayer(0)->getCash() < cashPerRound) + noMoney(); +} + + +// Display winner and give money and so on. + +void kpok::winner() +{ + PokerPlayer *winner; + + showComputerCards(); + + winner = m_game.getActivePlayer(0); + for (int i = 1; i < m_game.getNumActivePlayers(); i++) { + if (winner->getHand() < m_game.getActivePlayer(i)->getHand()) { + //kdDebug() << "Hand 2 is better." << endl; + winner = m_game.getActivePlayer(i); + } + else { + //kdDebug() << "Hand 1 is better." << endl; + } + } + + displayWinner_Computer(winner, false); +} + + +void kpok::noMoney() +{ + KMessageBox::sorry(0, i18n("You Lost"), i18n("Oops, you went bankrupt.\n" + "Starting a new game.\n")); + newGame(); +} + + +void kpok::startBlinking() +{ + blinkTimer->start(650); +} + + +void kpok::stopBlinking() +{ + blinkTimer->stop(); + m_blinkStat = 1; + m_blinkingBox = 0; +} + + +void kpok::startWave() +{ + waveTimer->start(40); + waveActive = true; +} + + +void kpok::stopWave() +{ + waveTimer->stop(); + fCount = -1; /* clear image */ + repaint ( FALSE ); + waveActive = false; +} + + +void kpok::stopDrawing() +{ + drawTimer->stop(); +} + + +void kpok::waveTimerEvent() +{ + fCount = (fCount + 1) & 15; + repaint( FALSE ); +} + + +void kpok::bTimerEvent() +{ + if (m_blinking) { + if (m_blinkStat != 0) { + playerBox[m_blinkingBox]->blinkOn(); + m_blinkStat = 0; + } else { + playerBox[m_blinkingBox]->blinkOff(); + m_blinkStat = 1; + } + } +} + + +void kpok::displayWin(const QString& hand, int cashWon) +{ + QString buf; + + setHand(hand); + m_game.getActivePlayer(0)->setCash(m_game.getActivePlayer(0)->getCash() + + cashWon); + m_game.setDirty(); + paintCash(); + + if (cashWon) { + playSound("win.wav"); + buf = i18n("You won %1!").arg(KGlobal::locale()->formatMoney(cashWon)); + } else { + playSound("lose.wav"); + buf = i18n("Game Over"); // locale + } + wonLabel->setText(buf); + + if (!cashWon) + wonLabel->show(); + else { + wonLabel->hide(); + startWave(); + } + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::paintEvent( QPaintEvent *) +{ + /* NOTE: This was shamelessy stolen from the "hello world" example + * coming with Qt Thanks to the Qt-Guys for doing such a cool + * example 8-) + */ + + if (!waveActive) { + return; + } + + QString txt = wonLabel->text(); + wonLabel->hide(); + + static int sin_tbl[16] = { + 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38}; + + if ( txt.isEmpty() ) { + return; + } + + QFont wonFont; + wonFont.setPointSize(18); + wonFont.setBold(true); + + QFontMetrics fm = QFontMetrics(wonFont); + + int w = fm.width(txt) + 20; + int h = fm.height() * 2; + while (w > mWonWidget->width() && wonFont.pointSize() > 6) {// > 6 for emergency abort... + wonFont.setPointSize(wonFont.pointSize() - 1); + fm = QFontMetrics(wonFont); + w = fm.width(txt) + 20; + h = fm.height() * 2; + } + + int pmx = mWonWidget->width() / 2 - w / 2; + int pmy = 0; + // int pmy = (playerBox[0]->x() + playerBox[0]->height() + 10) - h / 4; + + QPixmap pm( w, h ); + pm.fill( mWonWidget, pmx, pmy ); + + if (fCount == -1) { /* clear area */ + bitBlt( mWonWidget, pmx, pmy, &pm ); + return; + } + + QPainter p; + int x = 10; + int y = h/2 + fm.descent(); + unsigned int i = 0; + p.begin( &pm ); + p.setFont( wonFont ); + p.setPen( QColor(0,0,0) ); + + while ( i < txt.length() ) { + int i16 = (fCount+i) & 15; + + p.drawText( x, y-sin_tbl[i16]*h/800, QString(txt[i]), 1 ); + x += fm.width( txt[i] ); + i++; + } + p.end(); + + // 4: Copy the pixmap to the Hello widget + bitBlt( mWonWidget, pmx, pmy, &pm ); +} + + +void kpok::drawAllDecks() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + for (int i2 = 0; i2 < PokerHandSize; i2++) { + m_game.getActivePlayer(i)->takeCard(i2, 0); + playerBox[i]->paintCard(i2); + } + } +} + + +void kpok::removePlayerFromRound(PokerPlayer* removePlayer) +{ + removePlayer->setOut(true); + m_game.inactivatePlayer(removePlayer); + + for (int i = 0; i < m_game.getNumPlayers(); i++) + playerBox[i]->showCash(); +} + + +void kpok::switchToOnePlayerRules() +{ + KMessageBox::information(0, i18n("You are the only player with money!\n" + "Switching to one player rules..."), + i18n("You Won")); + + // Hide all computer players. + for (unsigned int i = 1; i < m_numPlayers; i++) { + playerBox[i]->hide(); + } + + m_game.setType(SinglePlayer); + + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + setHand(i18n("Nothing")); +} + + +PokerPlayer* kpok::findHumanPlayer() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (m_game.getActivePlayer(i)->getHuman()) + return m_game.getActivePlayer(i); + } + + return m_game.getActivePlayer(0);//error +} + + +bool kpok::readEntriesAndInitPoker() +{ + NewGameDlg *newGameDlg = NULL; + KConfig *conf = kapp->config(); + int numPlayers = DEFAULT_PLAYERS; + + conf->setGroup("NewGameDlg"); + bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", false); + bool aborted = false; + bool oldGame = false; + + if (showNewGameDlg) { + newGameDlg = new NewGameDlg(this); + if (!newGameDlg->exec()) + return false; // exit game + } + + if (!aborted && (!showNewGameDlg || showNewGameDlg && + newGameDlg->readFromConfigFile())) { + // Try starting an old game. + //kdDebug() << "Trying to load old game" << endl; + oldGame = loadGame(); + if (oldGame) + return true; + } + + if (!aborted && showNewGameDlg && !oldGame) { + // Don't use config file - just take the values from the dialog. + // (This is also config - the dialog defaults are from config.) + numPlayers = newGameDlg->getPlayers(); + if (numPlayers <= 0) + aborted = true; + + for (int i = 0; i < numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + m_players[i].setCash(newGameDlg->money()); + m_game.setDirty(); + } + } + initPoker(numPlayers); + + if (newGameDlg != 0) { + delete newGameDlg; + } + + return true; +} + + +void kpok::betChange(int betChange) +{ + PokerPlayer* p = findHumanPlayer(); + + switch(m_game.getState()){ + case StateBet1: + if (p->getCurrentBet() + betChange >= m_game.getMinBet() && + p->getCurrentBet() + betChange <= m_game.getMaxBet()) { + // Bet at least getMinBet() but not more than getMaxBet(). + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + case StateBet2: + case StateRaise2: + case StateRaise1: + if (p->getCurrentBet() + betChange >= currentMustBet + && p->getCurrentBet() + betChange <= currentMustBet + m_game.getMaxBet()) { + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + default: + break; + } + + paintCash(); +} + + +void kpok::saveGame() +{ + kapp->config()->setGroup("Save"); + saveGame(kapp->config()); +} + + +void kpok::saveGame(KConfig* conf) +{ + // kdWarning() << "save game" << endl; + int players = m_game.getNumPlayers(); + + conf->writeEntry("players", players); + conf->writeEntry("lastHandText", lastHandText); + + for (int i = 0; i < players; i++) { + conf->writeEntry(QString("Name_%1").arg(i), m_players[i].getName()); + conf->writeEntry(QString("Human_%1").arg(i), m_players[i].getHuman()); + conf->writeEntry(QString("Cash_%1").arg(i), m_players[i].getCash()); + } + + m_game.clearDirty(); +} + + +void kpok::toggleSound() { setSound(!sound); } +void kpok::toggleBlinking() { setBlinking(!m_blinking); } +void kpok::toggleAdjust() { setAdjust(!adjust); } + + +void kpok::slotPreferences() +{ + if ( mOptions==0 ) + mOptions = new OptionsDlg(this, 0, m_numPlayers); + + if (m_numPlayers > 1) + mOptions->init(drawDelay, m_game.getMaxBet(), m_game.getMinBet()); + else + mOptions->init(drawDelay, cashPerRound); + + if (!mOptions->exec()) { + delete mOptions; + mOptions = 0; + } +} + + +void kpok::setAdjust(bool ad) +{ + adjust = ad; + + //update guessed money statusbar if we currently are in need of adjust + if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + if (adjust) + adjustBet(); + else + out(); + } +} + + +bool kpok::initSound() +{ + sound = true; + return true; +} + + +void kpok::playSound(const QString &soundname) +{ + if (sound) + KAudioPlayer::play(locate("data", QString("kpoker/sounds/")+soundname)); +} + + +void kpok::setSound(bool s) +{ + sound = s; +} + + +void kpok::readOptions() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) { + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->setBetDefaults(m_game.getMinBet(), + m_game.getMaxBet()); // inform players how much they may bet + } + drawDelay = mOptions->getDrawDelay(); + // kdDebug() << cashPerRound << endl; + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + delete mOptions; + mOptions = 0; + } +} + + +bool kpok::loadGame() +{ + kapp->config()->setGroup("Save"); + return loadGame(kapp->config()); +} + + +bool kpok::loadGame(KConfig* conf) +{ + int numPlayers = DEFAULT_PLAYERS; + + // conf->setGroup("Save"); + numPlayers = conf->readNumEntry("players", -1); + + if (numPlayers > 0) { + for (int i = 0; i < numPlayers; i++) { + QString buf = conf->readEntry(QString("Name_%1").arg(i), + "Player"); + m_players[i].setName(buf); + bool human = conf->readBoolEntry(QString("Human_%1").arg(i), + false); + if (human) + m_players[i].setHuman(); // i == 0 + int cash = conf->readNumEntry(QString("Cash_%1").arg(i), + START_MONEY); + m_players[i].setCash(cash); + m_game.setDirty(); + } + initPoker(numPlayers); + + // after initPoker because initPoker does a default initialization + // of lastHandText + conf->setGroup("Save"); + lastHandText = conf->readEntry("lastHandText", ""); + + return true; + } + + return false; +} + + +// These slots are called from keyboard shortcuts ('1'..'5') + +void kpok::exchangeCard1() { playerBox[0]->cardClicked(1); } +void kpok::exchangeCard2() { playerBox[0]->cardClicked(2); } +void kpok::exchangeCard3() { playerBox[0]->cardClicked(3); } +void kpok::exchangeCard4() { playerBox[0]->cardClicked(4); } +void kpok::exchangeCard5() { playerBox[0]->cardClicked(5); } + + +// This slot is called when the user wants to change some aspect of +// the card deck (front or back). +// +// FIXME: Maybe the slot should be moved to top.cpp instead and simply +// call a setDeck() or setBack() method in kpok. + +void kpok::slotCardDeck() +{ + kapp->config()->setGroup("General"); + QString deckPath = kapp->config()->readPathEntry("DeckPath", 0); + QString cardPath = kapp->config()->readPathEntry("CardPath", 0); + bool randomDeck, randomCardDir; + + // Show the "Select Card Deck" dialog and load the images for the + // selected deck, if any. + if (KCardDialog::getCardDeck(deckPath, cardPath, this, KCardDialog::Both, + &randomDeck, &randomCardDir) + == QDialog::Accepted) { + + // Load backside and front images. + if (playerBox && m_blinking && (m_blinkStat == 0)) + bTimerEvent(); + + cardImages->loadDeck(deckPath); + cardImages->loadCards(cardPath); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + playerBox[i]->repaintCard(); + } + + // Save selected stuff in the configuration. + kapp->config()->writePathEntry("DeckPath", deckPath); + kapp->config()->writeEntry("RandomDeck", randomDeck); + kapp->config()->writePathEntry("CardPath", cardPath); + kapp->config()->writeEntry("RandomCardDir", randomCardDir); + } +} + + +#include "kpoker.moc" diff --git a/kpoker/kpoker.desktop b/kpoker/kpoker.desktop new file mode 100644 index 00000000..000f2aca --- /dev/null +++ b/kpoker/kpoker.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Name=KPoker +Name[af]=Kpoker +Name[be]=Покер +Name[bn]=কে-পোকার +Name[ca]=Pòquer +Name[eo]=Pokero +Name[hi]=के-पोकर +Name[is]=Póker +Name[ne]=केडीई पोकर +Name[pa]=ਕੇ-ਪੋਕਰ +Name[pl]=Poker +Name[sv]=Kpoker +Name[ta]=Kபோகà¯à®•à®°à¯ +Name[tg]=KПокер +Name[th]=โป๊à¸à¹€à¸à¸­à¸£à¹Œ - K +Name[tr]=Poker +Name[uk]=Покер +Name[zh_TW]=KPoker 紙牌éŠæˆ² +Exec=kpoker %i %m -caption "%c" +Icon=kpoker +DocPath=kpoker/index.html +GenericName=Poker Card Game +GenericName[be]=ÐšÐ°Ñ€Ñ‚Ð°Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž покер +GenericName[bg]=Покер +GenericName[bn]=পোকার নামের তাস খেলা +GenericName[bs]=Igra pokera +GenericName[ca]=Joc de Pòquer +GenericName[cs]=Karetní hra poker +GenericName[cy]=Gêm Cerdiau Poker +GenericName[da]=Poker kortspil +GenericName[de]=Poker Kartenspiel +GenericName[el]=Παιχνίδι καÏτών Poker +GenericName[eo]=Pokera Kartludo +GenericName[es]=Juego de cartas de Poker +GenericName[et]=Pokker +GenericName[eu]=Poker jokoa +GenericName[fa]=بازی Poker Card +GenericName[fi]=Pokeri-korttipeli +GenericName[fr]=Jeu de Poker +GenericName[he]=משחק פוקר +GenericName[hr]=KartaÅ¡ka igra Pokera +GenericName[hu]=Póker +GenericName[is]=Pókerspil +GenericName[it]=Gioco del Poker +GenericName[ja]=ãƒãƒ¼ã‚«ãƒ¼ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​បៀផែ +GenericName[ko]=ì¹´ë“œ ë†€ì´ +GenericName[lt]=Pokeris (kortų žaidimas) +GenericName[lv]=Pokera spÄ“le +GenericName[mk]=Играта покер +GenericName[nb]=Kortspillet poker +GenericName[nds]=Pokern +GenericName[ne]=पोकर कारà¥à¤¡ खेल +GenericName[nl]=Poker-kaartspel +GenericName[nn]=Kortspelet poker +GenericName[pa]=ਪੋਕਰ ਤਾਸ਼ ਖੇਡ +GenericName[pl]=Poker +GenericName[pt]=Jogo de Cartas de Póquer +GenericName[pt_BR]=Jogo de Poker +GenericName[ru]=Покер +GenericName[se]=Goartaspeallu poker +GenericName[sk]=Kartová hra Poker +GenericName[sl]=Igra s kartami pokra +GenericName[sr]=Покер, игра Ñа картама +GenericName[sr@Latn]=Poker, igra sa kartama +GenericName[sv]=Pokerkortspel +GenericName[ta]=போகà¯à®•à®°à¯ அடà¯à®Ÿà¯ˆ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Карти - гра в покер +GenericName[zh_TW]=紙牌éŠæˆ² +Terminal=false +Type=Application +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;CardGame; diff --git a/kpoker/kpoker.h b/kpoker/kpoker.h new file mode 100644 index 00000000..21a41ebc --- /dev/null +++ b/kpoker/kpoker.h @@ -0,0 +1,364 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef KPOKER_H +#define KPOKER_H + + +// QT includes +#include +#include + +// KDE includes +#include + +// own includes +#include "poker.h" + + +// QT classes +class QPushButton; +class QLineEdit; +class QLabel; +class QFrame; +class QLineEdit; +class QFrame; +class QHBoxLayout; +class QVBoxLayout; + +// KDE classes +class KConfig; + + +// own classes +class BetBox; +class CardWidget; +class OptionsDlg; +class NewGameDlg; +class PokerPlayer; +class PlayerBox; + + +// ================================================================ +// Poker Game + + +enum PokerGameType { + SinglePlayer, + MultiPlayer +}; + + +enum PokerGameState { + StateStartRound=0, // Before dealing. Deal cards to switch state. + + StateBet1, + StateRaise1, + + StateExchangeCards, + + StateBet2, + StateRaise2, + + StateSee +}; + + +// A PokerGame would be the main class in any poker game. It controls +// the game flow and has control over all the players. +// +// FIXME: This is very much in flux right now. It is in the process +// of being separated from class kpok. A lot of things need to +// be done. Among them are: +// - Finish separation from kpok. + +class PokerGame { +public: + PokerGame(KRandomSequence *random); + ~PokerGame(); + + PokerGameType getType() const { return m_type; } + void setType(PokerGameType type) { m_type = type; } + + // The state of the current round. + PokerGameState getState() const { return m_state; } + void setState(PokerGameState state) { m_state = state; } + + // Money in the game. + void clearPot() { m_pot = 0; } + int getPot() const { return m_pot; } + void bet(int amount) { m_pot += amount; } + void bet(PokerPlayer * player, int amount); + + int getMinBet() const { return m_minBet; } + int getMaxBet() const { return m_maxBet; } + void setBettingLimits(int minBet, int maxBet) + { m_minBet = minBet; m_maxBet = maxBet; } + + // Players + int getNumPlayers() const { return m_numPlayers; } + int getNumActivePlayers() const { return m_activePlayers.count(); } + int getNumInactivePlayers() const { return m_removedPlayers.count(); } + PokerPlayer * getActivePlayer(unsigned int nr) { return m_activePlayers.at(nr); } + void activatePlayer(PokerPlayer *player); + void inactivatePlayer(PokerPlayer *player); + bool isActivePlayer(PokerPlayer *player) const { return m_activePlayers.contains(player); } + + // Misc + // FIXME: clearDirty should only be called by a save method + // The isDirty flag should only be set internally. + void setDirty() { m_isDirty = true; } + void clearDirty() { m_isDirty = false; } + bool isDirty() const { return m_isDirty; } + + // Some more complex methods. FIXME: These must be expanded! + void newGame(PokerGameType type, + int numPlayers, PokerPlayer *players, + int minBet, int MaxBet); + void newRound(); + + void dealCards(PokerPlayer *player, bool skip[]); + + + private: + + // ---------------------------------------------------------------- + // Properties of the entire game, not just one round: + + PokerGameType m_type; // The current type of game + unsigned int m_numPlayers; // Used for constructing and deleting only + PokerPlayer *m_players; // The players (owned by kpok) + + int m_minBet; // the money the player will bet if he wants or not + int m_maxBet; // max total bet including minBet. + + // True if we need to save before exiting. + // This is the case if the cash has changed for any of the players. + bool m_isDirty; + + // ---------------------------------------------------------------- + // Properties of the current round: + + PokerGameState m_state; // The current phase of the game round + CardDeck m_deck; // The card deck we are using + int m_pot; // The amount of money people have bet. + + // The players in the game. + QPtrList m_activePlayers; // players still in the round + QPtrList m_removedPlayers; // players out of this round +}; + + +// ================================================================ +// Poker Game View + + +class kpok : public QWidget +{ + Q_OBJECT + + public: + kpok(QWidget * parent = 0, const char *name = 0); + virtual ~kpok(); + + QString getName (int playerNr); + void paintCash(); + + bool isDirty() const { return m_game.isDirty(); } + + void setBlinking(bool bl) { m_blinking = bl; } + void setAdjust(bool ad); + void setSound(bool s); + + void updateLHLabel();//temporary function, only called once + + bool getSound() const { return sound; } + bool getBlinking() const { return m_blinking; } + bool getAdjust() const { return adjust; } + + signals: + void changeLastHand(const QString &newHand, bool lastHand = true); + void showClickToHold(bool show); + void statusBarMessage(QString); + void clearStatusBar(); + + protected: + void initWindow(); // called only once + void readOptions(); + void drawCards(PokerPlayer* p, bool skip[]); + void newRound(); + void noMoney(); + void paintEvent( QPaintEvent * ); + void playSound(const QString &filename); + void setBetButtonEnabled(bool enabled); + void setHand(const QString& newHand, bool lastHand = true); + void setLastWinner(const QString& lastWinner); + void startBlinking(); + void stopBlinking(); + void stopDrawing(); + void result(); + void winner(); + + void bet(); + + void displayWin(const QString& hand, int cashWon); + + /** + * Displays the winner, adds the pot to his money + * + * othersPassed = true means all the other players didn't bet + **/ + void displayWinner_Computer(PokerPlayer* winner, bool othersPassed); + + void removePlayerFromRound(PokerPlayer* removePlayer); + void switchToOnePlayerRules(); + + /** + * @return The human player if he is in the game + **/ + PokerPlayer* findHumanPlayer(); + + /** + * This method first reads the config file then starts a + * newGame Dialog if it wasn't forbidden in config + * + * After all options and defaults were set the method starts + * @ref initPoker() with only the number of the players as an + * argument or with a complete player class, depending on the + * options the player chose @return True if successful false + * if player clicked 'cancel' in the new game dialog + **/ + bool readEntriesAndInitPoker(); + + /** + * This should only be done in the see phase and shows the + * cards of the computer players + **/ + void showComputerCards(); + + /** + * The main method for starting the game + * + * It constructs the players if only the number of players + * are given or uses an existing array Then all player boxes + * are being constructed as well as some smaller things like + * the pot + * @param players the number of the players + * @param ownAllPlayers This is used if there is already an array of players existing e.g. from @ref readEntriesAndInitPoker() + **/ + void initPoker(unsigned int numPlayers); + + /** + * Gives all players the deck as a card + **/ + void drawAllDecks(); + + + public slots: + void slotCardDeck(); + void toggleSound(); + void toggleAdjust(); + void toggleBlinking(); + void slotPreferences(); + + bool initSound(); + + /** + * Just as the name says: This method/slot saves the current + * game (to the config file) + * + * The game can be loaded on startup by activating the button + * 'read from config' + **/ + void saveGame(KConfig* conf); + + bool loadGame(KConfig* conf); + bool loadGame(); + // void commandCallback(int id); + void newGame(); + void saveGame(); + + void exchangeCard1(); + void exchangeCard2(); + void exchangeCard3(); + void exchangeCard4(); + void exchangeCard5(); + void drawClick(); + + protected slots: + void bTimerEvent(); + void drawCardsEvent(); + void waveTimerEvent(); + + void betChange(int); + void adjustBet(); + void out(); + + void startWave(); + void stopWave(); + void toggleHeld(); // play a sound + + private: + // The "document" - the game itself + PokerGame m_game; // The game that this widget is showing. + unsigned int m_numPlayers; + PokerPlayer *m_players; // The players + + int cashPerRound; // single player game: the ante + int currentMustBet; // the minimum bet amount + int oldBet_raise; // used for raising only + + int drawDelay; + + // Graphical layout. + QVBoxLayout *topLayout; + QHBoxLayout *inputLayout; + QLabel *potLabel; + BetBox *betBox; + QPushButton *drawButton; // the main Button + QLabel *wonLabel; // the winner + QLabel *clickToHold; + QWidget *mWonWidget; + + PlayerBox **playerBox; //one box per player + + // Dialogs + OptionsDlg* mOptions; + + // Other stuff + KRandomSequence m_random; + + QTimer *blinkTimer; // the winning cards will blink + QTimer *drawTimer; // delay between drawing of the cards + QTimer *waveTimer; // for displaying of the win (if winner == human) + + bool adjust; // allow user to adjust the bet. + int drawStat; // status of drawing (which card already was drawn etc. + + bool sound; + + bool m_blinking; // True if card should blink when winning. + int m_blinkStat; // status of blinking + int m_blinkingBox; // box of winning player + + bool waveActive; + int fCount; + + QString lastHandText; +}; + +#endif diff --git a/kpoker/kpokerui.rc b/kpoker/kpokerui.rc new file mode 100644 index 00000000..3ad1ddcb --- /dev/null +++ b/kpoker/kpokerui.rc @@ -0,0 +1,30 @@ + + + + +

    &Settings + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kpoker/main.cpp b/kpoker/main.cpp new file mode 100644 index 00000000..b4a27509 --- /dev/null +++ b/kpoker/main.cpp @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "version.h" +#include "top.h" + +static const char description[] = I18N_NOOP("KDE Poker Game"); + +int main( int argc, char *argv[] ) +{ + KAboutData aboutData("kpoker", I18N_NOOP("KPoker"), + KPOKER_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2003 Jochen Tuchbreiter, Andreas Beckermann\n" + "(c) 2004 Jochen Tuchbreiter, Andreas Beckermann, Inge Wallin", + I18N_NOOP("For a full list of credits see helpfile\nAny suggestions, bug reports etc. are welcome")); + + aboutData.addAuthor("Inge Wallin", + I18N_NOOP("Code for poker rules"), "inge@lysator.liu.se"); + aboutData.addAuthor("Andreas Beckermann", + I18N_NOOP("Current maintainer"), "b_mann@gmx.de"); + aboutData.addAuthor("Jochen Tuchbreiter", + I18N_NOOP("Original author"), "whynot@mabi.de"); + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication myApp; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if (myApp.isRestored()) + RESTORE(PokerWindow) + else { + PokerWindow *MainScreen = new PokerWindow; + myApp.setMainWidget( MainScreen ); + MainScreen->show(); + } + return myApp.exec(); +} diff --git a/kpoker/newgamedlg.cpp b/kpoker/newgamedlg.cpp new file mode 100644 index 00000000..002833b6 --- /dev/null +++ b/kpoker/newgamedlg.cpp @@ -0,0 +1,204 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include +#include +#include +#include + +// KDE includes +#include +#include +#include +#include +#include + +// own includes +#include "defines.h" +#include "newgamedlg.h" + + +NewGameDlg::NewGameDlg(QWidget* parent) + : KDialogBase(Plain, i18n("New Game"), + Ok|Cancel, Ok, parent, 0, true, true) +{ + QVBoxLayout *topLayout = new QVBoxLayout(plainPage(), spacingHint()); + QHBoxLayout *l = new QHBoxLayout(topLayout); + + KConfig* conf = kapp->config(); + conf->setGroup("NewGameDlg"); + bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", + SHOWNEWGAME_DEFAULT); + bool readConfig = conf->readBoolEntry("readFromConfig", + LOADGAME_DEFAULT); + int playerNr = conf->readNumEntry("players", DEFAULT_PLAYERS); + int money = conf->readNumEntry("startMoney", START_MONEY); + + readFromConfig = new QCheckBox(i18n("Try loading a game"), plainPage()); + readFromConfig->adjustSize(); + readFromConfig->setChecked(readConfig); + l->addWidget(readFromConfig); + + readFromConfigLabel = new QLabel(i18n("The following values are used if loading from config fails"), plainPage()); + if (!readFromConfig->isChecked()) + readFromConfigLabel->hide(); + readFromConfigLabel->adjustSize(); + l->addWidget(readFromConfigLabel); + connect(readFromConfig, SIGNAL(toggled(bool)), + this, SLOT(changeReadFromConfig(bool))); + + players = new KIntNumInput(playerNr, plainPage()); + players->setRange(1, MAX_PLAYERS); + players->setLabel(i18n("How many players do you want?")); + topLayout->addWidget(players); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Your name:"), plainPage())); + player1Name = new QLineEdit(plainPage()); + l->addWidget(player1Name); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Players' starting money:"), plainPage())); + moneyOfPlayers = new QLineEdit(QString("%1").arg(money), plainPage()); + moneyOfPlayers->setValidator( new KIntValidator( 0,999999,moneyOfPlayers ) ); + + l->addWidget(moneyOfPlayers); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("The names of your opponents:"), plainPage())); + computerNames = new QComboBox(true, plainPage()); + computerNames->setInsertionPolicy(QComboBox::AtCurrent); + l->addWidget(computerNames); + + l = new QHBoxLayout(topLayout); + l->addWidget(new QLabel(i18n("Show this dialog every time on startup"), + plainPage())); + showDialogOnStartup = new QCheckBox(plainPage()); + showDialogOnStartup->setChecked(showNewGameDlg); + l->addWidget(showDialogOnStartup); + + setPlayerNames(); +} + + +NewGameDlg::~NewGameDlg() +{ + if (result() == Accepted) { + KConfig* conf = kapp->config(); + conf->setGroup("NewGameDlg"); // defaults for the newGameDlg only + conf->writeEntry("showNewGameDlgOnStartup", showOnStartup()); + conf->writeEntry("readFromConfig", readFromConfigFile()); // just a default! + conf->writeEntry("players", getPlayers()); + conf->writeEntry("startMoney", money()); + } + + //delete the visible elements: + delete readFromConfigLabel; + delete readFromConfig; + delete players; + delete moneyOfPlayers; + delete showDialogOnStartup; + delete player1Name; + delete computerNames; + +} + + +void NewGameDlg::setPlayerNames(int no, QString playerName) +{ + if (no < 0) { + kapp->config()->setGroup("Save"); + player1Name->setText(kapp->config()->readEntry("Name_0", i18n("You"))); + computerNames->clear(); + for (int i = 1; i < MAX_PLAYERS; i++) { + computerNames->insertItem(kapp->config()->readEntry(QString("Name_%1").arg(i), i18n("Computer %1").arg(i))); + } + } else if (no == 0) { + player1Name->setText(playerName); + } else { + if (computerNames->count() > no) + computerNames->insertItem(playerName, no-1); + else + computerNames->changeItem(playerName, no-1); + } +} + + +void NewGameDlg::changeReadFromConfig(bool show) +{ + if (show) + readFromConfigLabel->show(); + else + readFromConfigLabel->hide(); +} + + +bool NewGameDlg::showOnStartup() +{ + return showDialogOnStartup->isChecked(); +} + + +int NewGameDlg::getPlayers() +{ + return players->value(); +} + + +bool NewGameDlg::readFromConfigFile() +{ + return readFromConfig->isChecked(); +} + + +int NewGameDlg::money() +{ + bool ok = true; + int money = moneyOfPlayers->text().toInt(&ok); + if (ok) + return money; + else + return START_MONEY; +} + + +QString NewGameDlg::name(int nr) +{ + if (computerNames->currentText() != computerNames->text(computerNames->currentItem())) + computerNames->changeItem(computerNames->currentText(), computerNames->currentItem()); + + if (nr == 0) + return player1Name->text(); + + if (nr <= computerNames->count()) + return computerNames->text(nr-1); + + return i18n("Player"); +} + + +void NewGameDlg::hideReadingFromConfig() +{ + readFromConfig->hide(); + readFromConfigLabel->hide(); + readFromConfig->setChecked(false); +} + + +#include "newgamedlg.moc" diff --git a/kpoker/newgamedlg.h b/kpoker/newgamedlg.h new file mode 100644 index 00000000..5588f0aa --- /dev/null +++ b/kpoker/newgamedlg.h @@ -0,0 +1,104 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef NEWGAMEDLG_H +#define NEWGAMEDLG_H + + +#include + + +class KIntNumInput; + +class QLineEdit; +class QCheckBox; +class QLabel; +class QComboBox; + + +/** + * This dialog is shown on startup or when a new game is started + * + * Here you can set some options like the default money, players, the names and so on + * @short The dialog shown on startup or when a new game is started + **/ +class NewGameDlg : public KDialogBase +{ + Q_OBJECT + + public: + NewGameDlg(QWidget* parent = 0); + ~NewGameDlg(); + + /** + * @return The player number the user has set + **/ + int getPlayers(); + + /** + * @return True if the user wants to read options from the config file false if not + **/ + bool readFromConfigFile(); + + /** + * You can write the return into a config file which choses to start this dialog on startup + * @return True if the user wants this dialog to be shown on startup + **/ + bool showOnStartup(); + + /** + * @return The start money of all players + **/ + int money(); + + /** + * @param nr The number of the player + * @return The name of the player specified in nr + **/ + QString name(int nr); + + /** + * This method hides the button where the user can choose to read values from config file + * + * It is used when a new game is started - you can load with config file on startup only + **/ + void hideReadingFromConfig(); + + void setPlayerNames(int no = -1, QString playerName = 0); + + + protected slots: + /** + * This slot hides the readFromConfigLabel when the user does not want to read from config or shows it if the user does + * + * The label warns that the other values are used as default values if reading from config fails + **/ + void changeReadFromConfig( bool ); + + + private: + QLabel *readFromConfigLabel; + QCheckBox *readFromConfig; + KIntNumInput *players; + QLineEdit *moneyOfPlayers; + QCheckBox *showDialogOnStartup; + QLineEdit *player1Name; + QComboBox *computerNames; +}; + + +#endif diff --git a/kpoker/optionsdlg.cpp b/kpoker/optionsdlg.cpp new file mode 100644 index 00000000..e009c3d2 --- /dev/null +++ b/kpoker/optionsdlg.cpp @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +// QT includes +#include +#include + +// KDE includes +#include +#include + +// own includes +#include "optionsdlg.h" +#include "defines.h" + + +OptionsDlg::OptionsDlg(QWidget* parent, const char* name, int _players) + : KDialogBase(Plain, i18n("Options")/*?*/, Ok|Cancel, Ok, + parent, name, true, true) +{ + QVBoxLayout* topLayout = new QVBoxLayout(plainPage(), spacingHint()); + maxBet = 0; + minBet = 0; + + if (_players <= 0) + players = DEFAULT_PLAYERS; + else + players = _players; + + topLayout->addWidget(new QLabel(i18n("All changes will be activated in the next round."), plainPage())); + + drawDelay = new KIntNumInput(0, plainPage()); + drawDelay->setLabel(i18n("Draw delay:")); + topLayout->addWidget(drawDelay); + + if (players > 1) { + maxBet = new KIntNumInput(0, plainPage()); + maxBet->setLabel(i18n("Maximal bet:")); + topLayout->addWidget(maxBet); + + minBet = new KIntNumInput(0, plainPage()); + minBet->setLabel(i18n("Minimal bet:")); + topLayout->addWidget(minBet); + } +} + + +OptionsDlg::~OptionsDlg() +{ +} + + +void OptionsDlg::init(int _drawDelay, int _maxBetOrCashPerRound, int _minBet) +{ + if (_minBet < 0) + defaultMinBet = MIN_BET; + if (_maxBetOrCashPerRound < 0 && players > 1) + defaultMaxBet = MAX_BET; + else if (_maxBetOrCashPerRound < 0) + defaultCashPerRound = CASH_PER_ROUND; + if (_drawDelay < 0) + _drawDelay = DRAWDELAY; + + drawDelay->setValue(_drawDelay); + if (maxBet) + maxBet->setValue(_maxBetOrCashPerRound); + if (minBet && players > 1) + minBet->setValue(_minBet); +} + + +int OptionsDlg::getMaxBet() +{ + if (!maxBet || players <= 1) + return defaultMaxBet; + + return maxBet->value(); +} + + +int OptionsDlg::getMinBet() +{ + if (!minBet || players <= 1) + return defaultMinBet; + + return minBet->value(); +} + + +int OptionsDlg::getCashPerRound() +{ + if (!maxBet || players > 1) + return defaultCashPerRound; + + return maxBet->value(); +} + + +int OptionsDlg::getDrawDelay() +{ + return drawDelay->value(); +} + + +#include "optionsdlg.moc" diff --git a/kpoker/optionsdlg.h b/kpoker/optionsdlg.h new file mode 100644 index 00000000..c1573133 --- /dev/null +++ b/kpoker/optionsdlg.h @@ -0,0 +1,68 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef OPTIONSDLG_H +#define OPTIONSDLG_H + +#include + + +class QLineEdit; +class QCheckBox; +class QLabel; +class QComboBox; + +class KIntNumInput; + + +/** + * This dialog is will set some options which mostly become active when a new + * round is started + * + * Here you can set some options like the maxBet, maybe names and so on + * @short The options dialog + **/ +class OptionsDlg : public KDialogBase +{ + Q_OBJECT + + public: + OptionsDlg(QWidget* parent = 0, const char* name = 0, int _players = 1); + ~OptionsDlg(); + + void init(int _drawDelay, int _maxBetOrCashPerRound, int minBet = -1); + int getMaxBet(); + int getMinBet(); + int getCashPerRound(); + int getDrawDelay(); + + private: + int players; + int defaultMaxBet; + int defaultMinBet; + int defaultCashPerRound; + int defaultDrawDelay; + // QLineEdit* maxBet; + // QLineEdit* minBet; + // QLineEdit* drawDelay; + KIntNumInput* maxBet; + KIntNumInput* minBet; + KIntNumInput* drawDelay; +}; + + +#endif diff --git a/kpoker/player.cpp b/kpoker/player.cpp new file mode 100644 index 00000000..9fb4e5d8 --- /dev/null +++ b/kpoker/player.cpp @@ -0,0 +1,292 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include "player.h" +#include "defines.h" + + +// ================================================================ +// class Player + + +PokerPlayer::PokerPlayer() + : m_hand() +{ + m_name = "Player"; + m_isHuman = false; + + m_money = START_MONEY; + currentBet = 0; + + isOut = false; + // random.setSeed(0); +} + + +PokerPlayer::~PokerPlayer() +{ +} + + +// ---------------------------------------------------------------- + + +void PokerPlayer::giveCardsBack() +{ + m_hand.clear(); +} + + +void PokerPlayer::giveCardBack(int cardNr) +{ + m_hand.clear(cardNr); +} + + +/* Set skip[i] to true if the corresponding card should *not* be + * exchanged. + */ + +void PokerPlayer::exchangeCards(bool skip[]) +{ + //TODO: improve! + // this is still a VERY simple method! + + m_hand.analyze(); + for (int i = 0; i < PokerHandSize; i++) { + skip[i] = m_hand.getFoundCard(i); + } +} + + +/* Prepare the player for a new round. + */ + +void PokerPlayer::newRound() +{ + giveCardsBack(); + + currentBet = 0; + isOut = false; +} + + +int PokerPlayer::bet(int origBet, bool mayRaise) +{ + // NOT useable for (status == continueBet) !!! + // needs a rewrite for games with > 2 players + int bet = origBet; + + // first bet minBet + currentBet = minBet; + m_money -= currentBet; + bet -= currentBet; + int newBet = bet; // bet at least the old bet + + if (bet > getCash())// we don't have the money :-( + return 0; + + // calculate the chances and bet any value + int chance = sortedResult(); + + if (chance < 350) { // 3 of a kind or better!! + newBet = maxBet - (int)random.getLong(maxBet /2);//we subtract a + // random number to hide our cards (every player would + // know we have good cards if we would bet maxBet) + } + else if (chance < 400) { // 2 pairs + newBet = bet + (int)random.getLong(maxBet /2 + origBet); + if (newBet > maxBet) + newBet = maxBet; + } + else if (chance < 500) { // one pair + newBet = bet + (int)random.getLong(maxBet /4 + 1); + if (newBet > getCash() - 2 * minBet) + newBet = bet; + if (bet >= getCash() /3) + newBet = 0; + } + else if (chance < 506) { // best card is at least a ten + newBet = bet; + if (bet >= getCash() /3) + newBet = 0; + } + else { // bad cards + if (getCash() - bet >= bet) {// we would still have some money + newBet = bet; + if (bet >= getCash() /4) + newBet = 0; + } + else + newBet = 0; + } + + // and now a final re-check + if (newBet > bet) { + if (random.getLong(20) == 0) + newBet = bet; + else if (random.getLong(30) <= 1) + newBet = bet + (newBet - bet) /2; + } + + if (newBet > getCash()) + newBet = bet; // maybe raise only a little bit but by now just do not raise + + if (!mayRaise && newBet > bet) + newBet = bet; + + if (!changeBet(newBet)) + return 0; // BIG error + + return currentBet; +} + + +int PokerPlayer::raise(int origRaise) +{ + // NOT useable for (status == continueRaise) !!! + // needs a rewrite for games with > 2 players + int raise = origRaise - getCurrentBet(); + int newRaise = raise; + + if (newRaise > getCash())// we don't have the money :-( + return 0; + + // Calculate the chances and bet any value. + int chance = sortedResult(); + + if (chance < 350) { // 3 of a kind or better!! + newRaise = maxBet - (int)random.getLong(maxBet - maxBet /2); + // we subtract a random number to hide our cards + // (every player would know we have good cards if + // we would bet maxBet) + } + else if (chance < 400) { // 2 pairs + newRaise = raise + (int)random.getLong(maxBet /2 + origRaise + 10); + if (newRaise > maxBet) + newRaise = maxBet; + } + else if (chance < 500) { // one pair + newRaise = raise + (int)random.getLong(maxBet /4 + 1); + if (newRaise > getCash() - 2 * minBet) + newRaise = raise; + if (raise >= getCash() /2) + newRaise = 0; + } + else if (chance < 506) { // best card is at least a ten + newRaise = raise; + if (raise >= getCash() /2) + newRaise = 0; + } + else { // bad cards + if (getCash() - raise >= raise && raise <= minBet * 2) { // we would still have some money + if (raise > getCash() /2) + newRaise = 0; + else + newRaise = raise; + } + else + newRaise = 0; + } + + // And now a final re-check. + if (newRaise > raise) { + if (random.getLong(20) == 0) + newRaise = raise; + else if (random.getLong(30) <= 1) + newRaise = raise + (newRaise - raise) /2; + } + + if (newRaise > getCash()) + newRaise = raise; // maybe raise only a little bit but by now just do not raise + + if (!changeBet(newRaise)) + return 0; // BIG error + + return currentBet; +} + + +bool PokerPlayer::changeBet(int betChange) +{ + if (currentBet + betChange >= 0 && getCash() - betChange >= 0) { + setCash(getCash() - betChange); + currentBet += betChange; + return true; + } + return false; +} + + +int PokerPlayer::sortedResult() +{ + PokerHandType result = m_hand.analyze(); + + //Ok, the result produced by testHand() is a little bit... uncomfortable + //so lets sort it for use in displayWinner_Computer() + //additionally we extend the values e.g. by bestCard and so on + + int newResult = m_hand.getCardScore(); + + // FIXME: Change this so that scores are higher for better hands. + // Don't forget to change m_hand.getCardScore() as well. + switch (result) { + case RoyalFlush: + newResult += 0; // the royal flush is the best you can get + break; + case StraightFlush: + newResult += 50; // straight flush + break; + case FourOfAKind: + newResult += 100; // 4 of a kind + break; + case FullHouse: + newResult += 150; // full house + break; + case Flush: + newResult += 200; // flush + break; + case Straight: + newResult += 250; // straight + break; + case ThreeOfAKind: + newResult += 300; // 3 of a kind + break; + case TwoPairs: + newResult += 350; // two pairs + break; + case Pair: + newResult += 400; // one pair + break; + case HighCard: + { + CardValue bestCard = m_hand.findNextBest(ROOF, false); + newResult = 500 + ((int) H_ACE - (int) bestCard); + } + break; + + default: + // Shouldn't get here. + assert(0); + } + + return newResult; + // The lowest newResult is now the best. +} diff --git a/kpoker/player.h b/kpoker/player.h new file mode 100644 index 00000000..7d3988cf --- /dev/null +++ b/kpoker/player.h @@ -0,0 +1,220 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef PLAYER_H +#define PLAYER_H + +// QT includes +#include + +// KDE includes +#include +#include + +// own includes +#include "poker.h" + + +// ================================================================ +// Player classes + + +class PokerPlayer +{ + public: + PokerPlayer(); + ~PokerPlayer(); + + /** + * Calculates the money which the computer will bet + * + * use @ref changeBet() for human players + * @param bet The minimum bet that the player has to bet. probably the same as the bet the human player has bet before + * @param mayRaise Specifies if the player may raise + * @return The player's bet + **/ + int bet(int bet, bool mayRaise = true); + + /** + * Same as @ref bet() but should bet a little bit more if the cards are good enough (they were already exchanged) + **/ + int raise(int raise); + + /** + * This method changes the player's bet by betChange + * @param Specifies the change + * @return true if successful, false if not + **/ + bool changeBet(int betChange); + + /** + * Only used by computer players + * + * Calculates which cards shall be exchanged + * @param skip[] Will be set true if the card shall be exchanged (if the 1st card shall be exchanged so skip[0] will be true) + **/ + void exchangeCards(bool skip[]); + + /** + * This method will return all cards to the pile which means that all cards will be set to 0 (=deck) + **/ + void giveCardsBack(); + + /** + * Returns a card to the pile + * @param cardNr specifies the card which will be returned to the pile + **/ + void giveCardBack(int cardNr); + + /** + * Begins a new round + **/ + void newRound(); + + /** + * Sets the player's cash to newCash + * @param newCash The new cash + **/ + void setCash(int newCash) { m_money = newCash; } + + /** + * This makes the player human + **/ + void setHuman() { m_isHuman = true; } + + /** + * Sets a new name + * @param newName The new name of the player + **/ + void setName(const QString &newName) { m_name = newName; } + + /** + * Informs the player that he is out (or is not out anymore) + * @param newOut true if player is out or false if player is back to the game + **/ + void setOut(bool newOut) { isOut = newOut; } + + /** + * Takes a card + * @param nr The number of the card (0 = first card) + * @param value The card itself + **/ + void takeCard(int nr, int value) { +#if 0 + const char *suitnames[] = {"C_", "D_", "S_", "H_"}; + if (value > 0) + kdDebug() << "Got card " << suitnames[(value - 1) % 4] + << (value - 1) / 4 + 2 << endl; +#endif + m_hand.setCard(nr, (CardValue) value); + } + + /** + * Informs the player about new rules + * @param min The minimum possible bet + * @param max The maximum possible bet + **/ + void setBetDefaults(int min, int max) { minBet = min; maxBet = max; } + + /** + * @param cardNr The number of the card (0 = first card) + * @return The card + **/ + CardValue getCard(int cardNr) const { return m_hand.getCard(cardNr);} + + PokerHand &getHand() { return m_hand; } + + /** + * @return The money of the player + **/ + int getCash() const { return m_money; } + + /** + * @return How much the player has bet + **/ + int getCurrentBet() const { return currentBet; } + + /** + * Returns the found card at nr + * + * The found cards specify the best cards the player has, e.g. if the player has one pair both cards will be found here + * @param nr The number of the wanted foundCard + * @return The found card number nr + **/ + bool getFoundCard(int nr) const { return m_hand.getFoundCard(nr); } + + /** + * @return If the player is human or not + **/ + bool getHuman() const { return m_isHuman; } + + /** + * @return The name of the player + **/ + QString getName() const { return m_name; } + + // FIXME: Rename to hasFolded? + /** + * @return True if the player is out or false if not + **/ + bool out() { return isOut; } + + + /** + * This test the cards of the player; searches for the result + * + * Used by @ref sortedResult() and in one player games + * @return The result (10 = the best, 0 = nothing) + **/ + PokerHandType testHand() { return m_hand.analyze(); } + + + protected: + + /** + * This sorts the result generated by @ref testHand() a little bit to be used in games with more than one player + * @return The points of the hand (a royal flush is e.g. 0, a best card is 500 + the best card) + **/ + int sortedResult(); + + + private: + // Basic data: + QString m_name; // The name of the player. + bool m_isHuman; // True if the player is human. + + // The hand itself + PokerHand m_hand; + + // The financial situation + int m_money; + int currentBet; + + // True if we are out of the game (have folded). + bool isOut; + + // Properties of the game. + // FIXME: Move this to the game itself. + // FIXME: Add a pointer to the poker game object. + int minBet; + int maxBet; + + // Extra stuff + KRandomSequence random; +}; + +#endif diff --git a/kpoker/playerbox.cpp b/kpoker/playerbox.cpp new file mode 100644 index 00000000..cdfb216c --- /dev/null +++ b/kpoker/playerbox.cpp @@ -0,0 +1,262 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "player.h" +#include "playerbox.h" +#include "defines.h" +#include "kpaint.h" + + +PlayerBox::PlayerBox(bool playerOne, QWidget* parent, const char* name) + : QGroupBox(parent, name) +{ + QHBoxLayout* l = new QHBoxLayout(this, PLAYERBOX_BORDERS, + PLAYERBOX_HDISTANCEOFWIDGETS); + + // The card and "held" label arrays. + m_cardWidgets = new CardWidget *[PokerHandSize]; + m_heldLabels = new QLabel *[PokerHandSize]; + + QFont myFixedFont; + myFixedFont.setPointSize(12); + + // Generate the 5 cards + for (int i = 0; i < PokerHandSize; i++) { + QVBoxLayout* vl = new QVBoxLayout(0); + l->addLayout(vl, 0); + + QHBox* cardBox = new QHBox(this); + vl->addWidget(cardBox, 0); + cardBox->setFrameStyle(Box | Sunken); + m_cardWidgets[i] = new CardWidget(cardBox); + cardBox->setFixedSize(cardBox->sizeHint()); + + // Only add the "held" labels if this is the first player (the human one). + if (playerOne) { + QHBox* b = new QHBox(this); + m_heldLabels[i] = new QLabel(b); + m_heldLabels[i]->setText(i18n("Held")); + b->setFrameStyle(Box | Sunken); + b->setFixedSize(b->sizeHint()); + m_cardWidgets[i]->heldLabel = m_heldLabels[i]; + + QHBoxLayout* heldLayout = new QHBoxLayout(0); + heldLayout->addWidget(b, 0, AlignCenter); + vl->insertLayout(0, heldLayout, 0); + vl->insertStretch(0, 1); + vl->addStretch(1); + } + } + + // Add the cash and bet labels. + { + QVBoxLayout* vl = new QVBoxLayout; + l->addLayout(vl); + vl->addStretch(); + + m_cashLabel = new QLabel(this); + m_cashLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + m_cashLabel->setFont(myFixedFont); + vl->addWidget(m_cashLabel, 0, AlignHCenter); + vl->addStretch(); + + m_betLabel = new QLabel(this); + m_betLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + m_betLabel->setFont(myFixedFont); + vl->addWidget(m_betLabel, 0, AlignHCenter); + vl->addStretch(); + } + + QToolTip::add(m_cashLabel, + i18n("Money of %1").arg("Player"));//change via showName() + + // Assume that we have a multiplayer game. + m_singlePlayer = false; +} + + +PlayerBox::~PlayerBox() +{ + delete[] m_cardWidgets; + delete[] m_heldLabels; +} + + +// ---------------------------------------------------------------- + + + +void PlayerBox::resizeEvent(QResizeEvent* e) +{ + QGroupBox::resizeEvent(e); + + showCash(); + showName(); +} + + +void PlayerBox::showCash() +{ + // Show the amount of cash the player has. + m_cashLabel->setText(i18n("Cash: %1") + .arg(KGlobal::locale()->formatMoney(m_player->getCash()))); + + // Show how much we have bet during this round. + if (m_player->out()) + m_betLabel->setText(i18n("Out")); + else { + if (m_singlePlayer) + m_betLabel->setText(i18n("Cash per round: %1") + .arg(KGlobal::locale()->formatMoney(m_cashPerRound))); + else + m_betLabel->setText(i18n("Bet: %1") + .arg(KGlobal::locale()-> formatMoney(m_player->getCurrentBet()))); + } +} + + +// Sshow the name of the player. Suppose that the players name has +// changed. + +void PlayerBox::showName() +{ + setTitle(m_player->getName()); + QToolTip::remove(m_cashLabel); + QToolTip::add(m_cashLabel, i18n("Money of %1").arg(m_player->getName())); +} + + +// Show or unshow all the held labels depending on the 'on' parameter. + +void PlayerBox::showHelds(bool on) +{ + for (int i = 0; i < PokerHandSize; i++) { + if (on) + m_cardWidgets[i]->heldLabel->show(); + else { + m_cardWidgets[i]->heldLabel->hide(); + m_cardWidgets[i]->setHeld(on); + } + } +} + + +void PlayerBox::paintCard(int nr) +{ + m_cardWidgets[nr]->paintCard(m_player->getCard(nr)); + m_cardWidgets[nr]->show(); +} + + +// Activate the held labels for this player (human player). + +void PlayerBox::activateToggleHeld() +{ + for (int i = 0; i < PokerHandSize; i++) { + connect(m_cardWidgets[i], SIGNAL(pClicked(CardWidget*)), + this, SLOT(cardClicked(CardWidget*))); + } +} + + +void PlayerBox::cardClicked(CardWidget* MyCW) +{ + emit toggleHeld(); + if (m_enableHeldLabels && MyCW->toggleHeld()) + MyCW->heldLabel->show(); + else + MyCW->heldLabel->hide(); +} + + +void PlayerBox::paintDeck(int nr) +{ + m_player->giveCardBack(nr); + paintCard(nr); +} + + +void PlayerBox::blinkOn() +{ + for (int i = 0; i < PokerHandSize; i++) { + if (m_player->getFoundCard(i)) + hideCard(i); + } +} + + +void PlayerBox::blinkOff() +{ + for (int i = 0; i < PokerHandSize; i++) { + if (!m_cardWidgets[i]->isVisible()) + paintCard(i); + } +} + + +void PlayerBox::setHeldEnabled(bool on) +{ + m_enableHeldLabels = on; + if (!on) { + for (int i = 0; i < PokerHandSize; i++) + m_heldLabels[i]->hide(); + } +} + + +void PlayerBox::singlePlayerGame(int newCashPerRound) +{ + m_singlePlayer = true; + m_cashPerRound = newCashPerRound; +} + + +void PlayerBox::hideCard(int nr) +{ + m_cardWidgets[nr]->hide(); +} + + +bool PlayerBox::getHeld(int nr) const +{ + return m_cardWidgets[nr]->getHeld(); +} + + +void PlayerBox::cardClicked(int no) +{ + cardClicked(m_cardWidgets[no-1]); +} + + +void PlayerBox::repaintCard() +{ + for (int i = 0; i < PokerHandSize; i++) + m_cardWidgets[i]->repaintDeck(); +} + + +#include "playerbox.moc" diff --git a/kpoker/playerbox.h b/kpoker/playerbox.h new file mode 100644 index 00000000..4d769170 --- /dev/null +++ b/kpoker/playerbox.h @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef PLAYERBOX_H +#define PLAYERBOX_H + + +#include + + +class QLabel; + + +class PokerPlayer; +class CardWidget; + + +class PlayerBox : public QGroupBox +{ + Q_OBJECT + + public: + PlayerBox(bool playerOne, QWidget* parent = 0, const char* name = 0); + ~PlayerBox(); + + void cardClicked(int no); + + /** + * Show the name of the player who owns this box. Use m_player to + * get the name. + **/ + void showName(); + + /** + * Paints the cash + **/ + void showCash(); // and some more + + /** + * Sets the player which is used e.g. by @ref showCash() + * @param p The guy who owns this box + **/ + void setPlayer(PokerPlayer* p) { m_player = p; } + + /** + * Hides the card nr (0 = first card) + * + * Used to let the cards blink. + * @param nr The number of the card which will be hidden + **/ + void hideCard(int nr); + + /** + * @param nr The number of the card (where 0 = first card) + * @return If the card nr shall be held + **/ + bool getHeld(int nr) const; + + /** + * shows all held labels or hides them all + * @param on Shows all all labels if true and hides them if false + **/ + void showHelds(bool on); + + /** + * Enables the held labels if on is true or disables them + * (e.g. after exchange phase) if false + * @param e Enables held labels if true, disables if false + **/ + void setHeldEnabled(bool on); + + // FIXME: Combine these two into paintCard(int nr, bool showFront); + /** + * Paints the card nr + * @param nr The number of the card (where 0 = first card) + **/ + void paintCard(int nr); + + /** + * Paints the deck + * @param nr The number of the card (where 0 = first card) + **/ + void paintDeck(int nr); + + /** + * Starts a one player game + * @param newCashPerRound The cash that the player has to pay every round + **/ + void singlePlayerGame(int newCashPerRound); + + /** + * Activates the held labels for this player (human player) + **/ + void activateToggleHeld(); + + /** + * Begins blinking of the winning cards + **/ + void blinkOn(); + + /** + * Stops blinking of the winning cards + **/ + void blinkOff(); + + void repaintCard(); + + + protected: + virtual void resizeEvent( QResizeEvent* e ); + + + protected slots: + void cardClicked(CardWidget* ); + + + signals: + void toggleHeld(); + + private: + // The player that acts on the hand. + PokerPlayer *m_player; // The player object. + + // Properties of the game + bool m_singlePlayer; // True if this is the only player in the game. + int m_cashPerRound; // one player game only FIXME: Rename into "ante"? + + // The card widgets and "held" widgets + CardWidget **m_cardWidgets; + QLabel **m_heldLabels; + bool m_enableHeldLabels; // True if held labels are enabled. + + // The labels at the right hand side of the box. + QLabel *m_cashLabel; + QLabel *m_betLabel; + + +}; +#endif + diff --git a/kpoker/poker.cpp b/kpoker/poker.cpp new file mode 100644 index 00000000..6143d342 --- /dev/null +++ b/kpoker/poker.cpp @@ -0,0 +1,531 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include +#include + +#include "poker.h" + + +// ================================================================ +// Card classes + + +CardDeck::CardDeck(KRandomSequence *random) +{ + m_random = random; + + reset(); +} + + +CardDeck::~CardDeck() +{ +} + + +// ---------------------------------------------------------------- + + +void +CardDeck::reset() +{ + int i; + CardValue card; + + for (i = 0, card = lowestCard; i < numCards; i++, card = card + 1) + m_cards[i] = card; + m_topCard = 0; + +} + + +void +CardDeck::shuffle() +{ + CardValue tmp; + int card; + + if (m_topCard == numCards) + return; + + for (int i = m_topCard; i < numCards - 1; i++) { + // Choose a random card from the not-yet-shuffled ones. + card = m_random->getLong(numCards - i) + i; + + // Exchange the random card with the current card. + tmp = m_cards[i]; + m_cards[i] = m_cards[card]; + m_cards[card] = tmp; + } +} + + +CardValue +CardDeck::getTopCard() +{ + if (m_topCard == numCards) + return DECK; + + return m_cards[m_topCard++]; +} + + +// ================================================================ +// Poker types + + +QString PokerHandNames[] = { + "High Card", + "Pair", + "Two Pairs", + "Three Of A Kind", + "Straight", + "Flush", + "Full House", + "Four Of A Kind", + "Straight Flush", + "Royal Flush" +}; + + +PokerHand::PokerHand() +{ + clear(); +} + + +PokerHand::~PokerHand() +{ +} + + +// Compare two poker hands, and return true if the first one is less +// valuable than the second one. Otherwise return false. + +bool +PokerHand::operator<(PokerHand &hand2) +{ + CardValue card1; + CardValue card2; + int i; + + // Make sure all relevant fields are initialized. + if (m_changed) + analyze(); + if (hand2.m_changed) + hand2.analyze(); + +#if 0 + kdDebug() << "Hand 1: " << PokerHandNames[(int) m_type] + << " (" << ((int) m_firstRank) + 2 + << ", " << ((int) m_secondRank) + 2 + << ")" << endl; + kdDebug() << "Hand 2: " << PokerHandNames[(int) hand2.m_type] + << " (" << ((int) hand2.m_firstRank) + 2 + << ", " << ((int) hand2.m_secondRank) + 2 + << ")" << endl; +#endif + + // 1. If we have a better hand, then it is simple. + if (m_type != hand2.m_type) + return m_type < hand2.m_type; + + // 2. If the hands are equal, check the internal parts of the hand + // type (like the first and second pair of two pairs). + switch (m_type) { + case HighCard: + case Pair: + case ThreeOfAKind: + case Straight: + case FourOfAKind: + case StraightFlush: + case RoyalFlush: + if (m_firstRank != hand2.m_firstRank) + return m_firstRank < hand2.m_firstRank; + break; + + case TwoPairs: + case FullHouse: + // Compare the first rank first, and then the second. + if (m_firstRank != hand2.m_firstRank) + return m_firstRank < hand2.m_firstRank; + if (m_secondRank != hand2.m_secondRank) + return m_secondRank < hand2.m_secondRank; + break; + + case Flush: + card1 = ROOF; + card2 = ROOF; + for (i = 0; i < PokerHandSize; i++) { + card1 = findNextBest(card1, true); + card2 = hand2.findNextBest(card2, true); + + if (card1 != card2) + return card1 < card2; + } + // If we get here all the card ranks are the same in both hands. + // This means that they have to be of different suits. + break; + + default: + // Shouldn't get here. + assert(0); + } + + // 3. Ok, the hands themselves are the same. Now check if the cards + // outside the hands differ. + card1 = ROOF; + card2 = ROOF; + while (card1 != DECK) { + card1 = findNextBest(card1, false); + card2 = hand2.findNextBest(card2, false); + + if (card1 != card2) + return card1 < card2; + } + + // 4. *Every* rank is the same. Then they must differ in suit. + + card1 = rank2card(m_firstRank + 1); + card2 = rank2card(hand2.m_firstRank + 1); + + return card1 < card2; +} + + +// ---------------------------------------------------------------- +// Ordinary methods + + +// Clear the hand - set all entries to DECK. +void +PokerHand::clear() +{ + for (int i = 0; i < PokerHandSize; i++) + m_cards[i] = DECK; + + m_changed = true; +} + + +/* Throw away card no 'cardno'. + */ + +void +PokerHand::clear(int cardno) +{ + m_cards[cardno] = DECK; + + m_changed = true; +} + + +CardValue +PokerHand::getCard(int cardno) const +{ + return m_cards[cardno]; +} + + +void +PokerHand::setCard(int cardno, CardValue card) +{ + m_cards[cardno] = card; + + m_changed = true; +} + + +bool +PokerHand::findRank(CardRank the_rank) const +{ + for (int i = 0; i < PokerHandSize; i++) + if (rank(m_cards[i]) == the_rank) + return true; + + return false; +} + + +bool +PokerHand::findSuit(CardSuit the_suit) const +{ + for (int i = 0; i < PokerHandSize; i++) + if (suit(m_cards[i]) == the_suit) + return true; + + return false; +} + + +// Return the next lower card value below the card 'roof'. +// +// If 'getFound' is true, then search only those cards that are marked +// as found, i.e. are among those that determine the hand type. +// Otherwise search the cards that are *not* among these. +// + +CardValue +PokerHand::findNextBest(CardValue roof, bool getFound) const +{ + CardValue next; + + next = DECK; + for (int i = 0; i < PokerHandSize; i++) { + if (m_cards[i] > next && m_cards[i] < roof + && (getFound == m_foundCards[i])) + next = m_cards[i]; + } + + return next; +} + + +int +PokerHand::testStraight() +{ + CardRank lowest = ACE; + + /* Set ranks[i] to the value of each card and find the lowest value. */ + for (int i = 0; i < PokerHandSize; i++) { + if (rank(m_cards[i]) < lowest) + lowest = rank(m_cards[i]); + } + + // Look for special cases ace-2-3-4-5. + // (very ugly but fast to write): + if ((findRank(ACE) && findRank(TWO) + && findRank(THREE) && findRank(FOUR) + && findRank(FIVE))) { + m_firstRank = FIVE; + } + else { + + for (int i = 0; i < PokerHandSize; i++) + if (!findRank(lowest + i)) + return 0; // did not find a straight + + m_firstRank = lowest + 4; + } + + // Found a straight. Record it in foundCards[]. + for (int i = 0; i < PokerHandSize; i++) + addFoundCard(i); + + // Check for a royal flush + if (lowest == TEN) + return 2; + + // An ordinary straight: return 1. + return 1; +} + + +bool +PokerHand::testFlush() +{ + int theSuit; + CardValue highest_card; + + highest_card = m_cards[0]; + theSuit = suit(m_cards[0]); + for (int i = 1; i < PokerHandSize; i++) { + if (theSuit != suit(m_cards[i])) + return 0; + + if (m_cards[i] > highest_card) + highest_card = m_cards[i]; + } + + // Found a flush. Now record the cards. + for (int i = 0; i < PokerHandSize; i++) + addFoundCard(i); + m_firstRank = rank(highest_card); + + return true; +} + + +PokerHandType +PokerHand::analyze() +{ + if (m_changed) + m_type = do_analyze(); + + return m_type; +} + + +PokerHandType +PokerHand::do_analyze() +{ + CardValue card1; + CardValue card2; + int i; + int j; + + cleanFoundCards(); + m_changed = 0; + + int isStraight = testStraight(); + + // Detect special cases; + if (isStraight) { + if (testFlush()) { + if (isStraight == 2) + return RoyalFlush; + else + return StraightFlush; + } + else + return Straight; + } + + if (testFlush()) + return Flush; + + /* Find number of matches. */ + int matching = 0; + for (i = 0; i < PokerHandSize; i++) { + for (j = i + 1; j < PokerHandSize; j++) + if (rank(m_cards[i]) == rank(m_cards[j])) { + matching++; + addFoundCard(i); + addFoundCard(j); + } + } + + // The algorithm above gives the following results for each case below. + switch (matching) { + case 0: // High card + card1 = findNextBest(ROOF, false); + m_firstRank = rank(card1); + m_secondRank = (CardRank) -1; + + // In this case, there are no marked cards. Since we need to mark + // the best card, we have to search for it and then mark it. + for (i = 0; i < PokerHandSize; i++) { + if (m_cards[i] == card1) { + addFoundCard(i); + break; + } + } + return HighCard; + + case 1: // Pair + m_firstRank = rank(findNextBest(ROOF, true)); + m_secondRank = (CardRank) -1; + return Pair; + + case 2: // Two pairs + card1 = findNextBest(ROOF, true); + + // Must do this twice, since the first card we get is the second + // card of the first pair. + card2 = findNextBest(card1, true); + card2 = findNextBest(card2, true); + + m_firstRank = rank(card1); + m_secondRank = rank(card2); + return TwoPairs; + + case 3: // 3 of a kind + m_firstRank = rank(findNextBest(ROOF, true)); + return ThreeOfAKind; + + case 4: // Full house + // This is the only tricky case since the value is determined more + // by the 3 of a kind than by the pair. The rank of the 3 of a + // kind can be lower then the pair, though. + + // Get the best and third best cards into val1 and val2. + card1 = findNextBest(ROOF, true); + card2 = findNextBest(card1, true); + card2 = findNextBest(card2, true); + + // Now we have one of two different cases: + // 1. rank(card1) == rank(card2): the 3 of a kind is biggest. + // 2. rank(card1) > rank(card2): the pair is biggest. + if (rank(card1) == rank(card2)) { + m_firstRank = rank(card1); + m_secondRank = rank(findNextBest(card2, true)); + } + else { + m_firstRank = rank(card2); + m_secondRank = rank(card1); + } + return FullHouse; + + case 6: // 4 of a kind + m_firstRank = rank(findNextBest(ROOF, true)); + return FourOfAKind; + + default: + break; + } + + // Shouldn't get here. + assert(0); + + return (PokerHandType) -1; +} + + +// FIXME: When we fix the scores, in raise() and bet() to be higher +// with better hands, then change the "rank(C_ACE) -" stuff +// below. +// +int +PokerHand::getCardScore() const +{ + int score = 0; + + for (int i = 0; i < PokerHandSize; i++) { + if (m_foundCards[i]) + score += rank(C_ACE) - rank(m_cards[i]); + } + + return score; +} + + +bool +PokerHand::getFoundCard(int cardNum) const +{ + return m_foundCards[cardNum]; +} + + + +// ---------------------------------------------------------------- +// PokerHand protected methods + + +void +PokerHand::cleanFoundCards() +{ + for (int i = 0; i < PokerHandSize; i++) + m_foundCards[i] = false; +} + + +void +PokerHand::addFoundCard(int cardNum) +{ + m_foundCards[cardNum] = true; +} diff --git a/kpoker/poker.h b/kpoker/poker.h new file mode 100644 index 00000000..5a944d99 --- /dev/null +++ b/kpoker/poker.h @@ -0,0 +1,234 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef POKER_H +#define POKER_H + +#include +#include +#include + + +// ================================================================ +// Card classes + + +// FIXME: DECK should be renamed to NO_CARD and moved to -1. (or maybe not) +// FIXME: Add Joker as well. +// + +typedef enum { + DECK=0, + C_TWO, D_TWO, S_TWO, H_TWO, + C_THREE, D_THREE, S_THREE, H_THREE, + C_FOUR, D_FOUR, S_FOUR, H_FOUR, + C_FIVE, D_FIVE, S_FIVE, H_FIVE, + C_SIX, D_SIX, S_SIX, H_SIX, + C_SEVEN, D_SEVEN, S_SEVEN, H_SEVEN, + C_EIGHT, D_EIGHT, S_EIGHT, H_EIGHT, + C_NINE, D_NINE, S_NINE, H_NINE, + C_TEN, D_TEN, S_TEN, H_TEN, + C_JACK, D_JACK, S_JACK, H_JACK, + C_QUEEN, D_QUEEN, S_QUEEN, H_QUEEN, + C_KING, D_KING, S_KING, H_KING, + C_ACE, D_ACE, S_ACE, H_ACE, + ROOF // to get a roof on the value. +} CardValue; + +const CardValue lowestCard = C_TWO; +const CardValue highestCard = H_ACE; +const int numCards = (int) H_ACE; + + +typedef enum { + Clubs = 0, // The least valuable suit + Diamonds, + Spades, + Hearts // The most valuable suit +} CardSuit; + + +typedef enum { + TWO = 0, // The least valuable rank + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + TEN, + JACK, + QUEEN, + KING, + ACE // The most valuable rank +} CardRank; + + + +inline CardValue +operator+(CardValue card, int offset) +{ + return (CardValue) ((int) card + offset); +} + + + +// Get the suit from a card. + +inline CardSuit +suit(CardValue card) +{ + // Need to subtract one since DECK == 0. + return (CardSuit) (((int) card - 1) % 4); +} + + +// Get the rank from a card + +inline CardRank +rank(CardValue card) +{ + // Need to subtract one since DECK == 0. + return (CardRank) (((int) card - 1) / 4); +} + + +// Add an offset (can be negative) to a rank. +// +// NOTE: No overflow check is done. This is mainly used for type +// conversion reasons. + +inline CardRank +operator+(CardRank rank1, int offset) +{ + return (CardRank) ((int) rank1 + offset); +} + + +// Get the first card of a certain rank. This is the same rank of clubs. + +inline CardValue +rank2card(CardRank rank) +{ + return (CardValue) (((int) rank) * 4 + 1); +} + +// ---------------------------------------------------------------- +// Class CardDeck + + +class CardDeck { + public: + CardDeck(KRandomSequence *random); + ~CardDeck(); + + void reset(); + void shuffle(); + CardValue getTopCard(); + + private: + CardValue m_cards[numCards]; + int m_topCard; + + KRandomSequence *m_random; +}; + + +// ---------------------------------------------------------------- +// Poker related + + +typedef enum { + HighCard, + Pair, + TwoPairs, + ThreeOfAKind, + Straight, + Flush, + FullHouse, + FourOfAKind, + StraightFlush, + RoyalFlush +} PokerHandType; + +// Name strings for all the hands +extern QString PokerHandNames[]; + + +// Number of cards in the hand. +const int PokerHandSize = 5; + +class PokerHand { + public: + PokerHand(); + ~PokerHand(); + + // Operators + bool operator<(PokerHand &hand2); + + // Clear the hand or just one card - set the entries in question to DECK. + void clear(); + void clear(int cardno); + + // Card stuff + CardValue getCard(int cardno) const; + void setCard(int cardno, CardValue card); + bool findRank(CardRank rank) const; + bool findSuit(CardSuit suit) const; + CardValue findNextBest(CardValue roof, bool onlyFound) const; + + // Poker stuff + int testStraight(); + bool testFlush(); + PokerHandType analyze(); + CardRank get_firstRank() const { return m_firstRank; } + CardRank get_secondRank() const { return m_secondRank; } + + bool getFoundCard(int cardNum) const; + int getCardScore() const; + + + protected: + // Clear the foundCards array. + void cleanFoundCards(); + + // Handle the "found" cards, i.e. those that comprise the scoring part. + void addFoundCard(int cardNum); + + // Analyze the poker hand. This is the most important function of all. + PokerHandType do_analyze(); + + + private: + // Primary data: The cards themselves. + CardValue m_cards[PokerHandSize]; + + // ---------------------------------------------------------------- + // Secondary data: can be derived from the Primary data. + bool m_changed; // true if something has changed since the + // secondary data was last recalculated. + + PokerHandType m_type; // Pair, Two Pairs, etc + CardRank m_firstRank; // Rank of first component + CardRank m_secondRank; // Rank of second component + + bool m_foundCards[PokerHandSize]; // True for all scoring cards +}; + + +#endif diff --git a/kpoker/sound.cpp b/kpoker/sound.cpp new file mode 100644 index 00000000..f029d03a --- /dev/null +++ b/kpoker/sound.cpp @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include + +// sound support +#include + +#include "global.h" +#include "kpoker.h" + + +bool kpok::initSound() +{ + sound = true; + return true; +} + +void kpok::playSound(const char *soundname) +{ + if (!sound) + return; + + KAudioPlayer::play(locate("data", QString("kpoker/sounds/")+soundname)); + +} + +void kpok::setSound(bool s) +{ + sound = s; +} + diff --git a/kpoker/sounds/Makefile.am b/kpoker/sounds/Makefile.am new file mode 100644 index 00000000..6ded1881 --- /dev/null +++ b/kpoker/sounds/Makefile.am @@ -0,0 +1,6 @@ + +wavsdir = $(kde_datadir)/kpoker/sounds +wavs_DATA = cardflip.wav hold.wav lose.wav win.wav + +EXTRA_DIST = $(wavs_DATA) + diff --git a/kpoker/sounds/cardflip.wav b/kpoker/sounds/cardflip.wav new file mode 100644 index 0000000000000000000000000000000000000000..37e7ceb12d4fd5aad819db33a05752995109b5c2 GIT binary patch literal 1172 zcmW-hOOqQl5XYS=4jkbdaOYESAXS{GqNwD+0hVkw@p`?s=b_QF)q2m%W3TNEyGexv z2z*ZDNSdlHsWtj{|62O#ho?`+FJ`mn-#z>OYFT~r)oeC9HzAtYN$BB8JFoRjPT_678UfLmal8 zy6NP(PS;nQ#x2tkH?1op7=^shy@a5-K&Rb9H})H4OygY1u#XiOqS=D`)*ndaR@Q6Jw$gLmFI-w?tKuVYWSLRnK~hY&5qkhaE1LP-tIMyCX7uC?ogq8ue) z91qE`g0QU2U?3Z^wx>7uRy5}w3=UKBWYKVc;HsBJ^Xqk3HE$n3>@ZC3m7L3FN`LvA zC7J)imY-JBIKA%{dj?{CV&v67@4C7v3vZ8D%~!RT3oP|<@{SRCHw3U|YucblRdya! zn`F(jl+;9}aoun#rPDEvZQ_dNP~6}E6fi)=q_>2dF7b-wP^_vMv%a6kVHmm~8&)@z zHwt1Oqescvd4k&11#~f4t0@ygaj7L*mjh2JAZR586QH!Q&UveZ00*dr)FwDF2wDJmb|XW<%^}fKRtH+&QUn$uybXl zQe;`bpp71nt;>Z`6_6+xcr~xnxIaDQ>~Bh@(%o(!WW8$2H7RSBVte{`U#@OTtniO} zv5E7SLc}BdxvMLDn11~2?!UiZTpRlHv|B!YDyDd~X0^J#>_6`BZWqBVMke2VeBUA6 zJgezEz*gTdr`#Jh*2|UM-gIwCSnci$77p%4#ZAB4@8tb(t$)YnC5&6OfSO2pn6xyy zroeF#|oZanhFoI(O!f2&fSrJ-Dbkg^`emEYtMjKL7spzI$Q^BYjCx@={(S$Z7 zsRgGsLmyE!rXPNS!FuYt9|O?H?O$Lp*sWZC zEr9*sTd<#DH0lWT+mA3e5V1tnE=Pl!WegDbq9J-xiXxxPHjZm+uaB1TV3O<6(S zojZ4n9w1tK0HM|yN@R}DFRw1okM=f0vugezz8PLoQdCs>5Q%;YiVU7e`rY}}_2*9~ zyD_gxIz)U@TUmO)xU3S1>ZbA4c7H5=aB_Bfe2|VUS`=&w?s3h-ib_OFCzY$T`Qv-X z=NA`ehc6=@y|5nxzyIg&zuo?;6p0%YoBYY_+11U>#mR2eqvcUrb7uL4WpH#aSL2Rm zPCtM9_n(j1%>@I$7gb$&=a0OS+)88)rBANDe7!!~i#X&=e0@b>UVh1=RuV_!Uf(@F z|M>pRi$$Yo01Gd<_g8*dZ3jbU3#M{{Cz<%XmP>AZR9bMq@-cxs<=;BI$ekahdrOcx11~Ksu0~OWMqg|z zom%tcplC#OO;gv9(zTH}J%68xdDKu(GrXpugQ4&wj<3F4pCsK1S{t$&f$kH{tnHs) ze%K4?m~E8>dByc4fi1j~*^SSu8EsVscZ=X1kj|GnIC_^3nfQeI^5RO=fYO`H9=}a` zRRA^z!2qg-%;vGyO7XO1#jotB0H&o7DWGD)zm-k-AKhe^1g=h@`geE+Wzt0 zie{kc!Gq=@b2RfNI?cq?HWDS?^ieiC1+>@Mv}~% zdKtCzx=|R0ekHXr`wWZBNw~t{c@+&^k7Jou5)mteK{PR}u~*wZ1+|^Px2=R7LOimL zr1Ztt?4x8nO|!fcx3SuBOiTQA+(4BM1UXnjFJqfacq-m(o7=u@$DK-htoMY3@z zwgq%1*W{gO=JVvbvKzy+uUIGulq|eCg@5$a7WZ*l>H$w;f!&OqjIEA!U`9hR6G)Z> zBSx@?l0m&=Q)k; zc;4F}4o+eza}EMlFWq~}(GX??myx8hi1r(EWyhVY~O}DD~!Drez$ZVy%Ae9|lQksIiB{j#- z@r|t6j8VN^?4I$9m$c$VXh~^it4(}=zkxe5;gn2@$7Vz>vYBfKOcEpLU|R*cK{Mzg zy8BI3BUL0C6|hIxVvxZDWIdW59Z}OhO_cTnBC>%p!GgwnHNDo6F&4wvDHpP+G?H|P SOQVbv^h98yk0U1wl>Y&)vWAWT literal 0 HcmV?d00001 diff --git a/kpoker/sounds/lose.wav b/kpoker/sounds/lose.wav new file mode 100644 index 0000000000000000000000000000000000000000..9f9c72c34f4e2c9652294dbcb155aabdf773fd3b GIT binary patch literal 5964 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 E3wKk9zW@LL literal 0 HcmV?d00001 diff --git a/kpoker/sounds/win.wav b/kpoker/sounds/win.wav new file mode 100644 index 0000000000000000000000000000000000000000..d7cc5b2a5e899da4b1a7e56ac49480e12941fa3e GIT binary patch literal 21831 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
    9kW1zf-wYhJ{z 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 Wv!*m@sITQ;klo4o{}0| +#include + +// KDE includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// own includes +#include "top.h" +#include "kpoker.h" +#include "defines.h" +#include "version.h" + + +PokerWindow::PokerWindow() +{ + m_kpok = new kpok(this, 0); + setCentralWidget( m_kpok ); + m_kpok->show(); + + clickToHoldIsShown = false; + + LHLabel = new QLabel(statusBar()); + LHLabel->adjustSize(); + + connect(m_kpok, SIGNAL(changeLastHand(const QString &, bool)), + this, SLOT(setHand(const QString &, bool))); + connect(m_kpok, SIGNAL(showClickToHold(bool)), + this, SLOT(showClickToHold(bool))); + connect(m_kpok, SIGNAL(clearStatusBar()), + this, SLOT(clearStatusBar())); + connect(m_kpok, SIGNAL(statusBarMessage(QString)), + this, SLOT(statusBarMessage(QString))); + + statusBar()->addWidget(LHLabel, 0, true); + m_kpok->updateLHLabel(); + //FIXME: LHLabel is shown twize until the bar is repainted! + + initKAction(); + readOptions(); +} + + +PokerWindow::~PokerWindow() +{ +} + + +// ---------------------------------------------------------------- + + +void PokerWindow::initKAction() +{ + //Game + KStdGameAction::gameNew(m_kpok, SLOT(newGame()), actionCollection()); + KStdGameAction::save(m_kpok, SLOT(saveGame()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + + //Settings + showMenubarAction = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + + soundAction = new KToggleAction(i18n("Soun&d"), 0, m_kpok, + SLOT(toggleSound()), actionCollection(), "options_sound"); + if (m_kpok->getSound()) + m_kpok->toggleSound(); + blinkingAction = new KToggleAction(i18n("&Blinking Cards"), 0, m_kpok, + SLOT(toggleBlinking()), actionCollection(), "options_blinking"); + if (m_kpok->getBlinking()) + m_kpok->toggleBlinking(); + adjustAction = new KToggleAction(i18n("&Adjust Bet is Default"), 0, + m_kpok, SLOT(toggleAdjust()), actionCollection(), "options_adjust"); + if (m_kpok->getAdjust()) + m_kpok->toggleAdjust(); + + showStatusbarAction = + KStdAction::showStatusbar(this, SLOT(toggleStatusbar()), actionCollection()); + + KStdAction::saveOptions(this, SLOT(saveOptions()), actionCollection()); + KStdGameAction::carddecks(m_kpok, SLOT(slotCardDeck()), actionCollection()); + KStdAction::preferences(m_kpok, SLOT(slotPreferences()), actionCollection()); + + // Keyboard shortcuts. + (void)new KAction(i18n("Draw"), KShortcut(Qt::Key_Return), m_kpok, + SLOT(drawClick()), actionCollection(), "draw"); + (void)new KAction(i18n("Exchange Card 1"), KShortcut(Qt::Key_1), m_kpok, + SLOT(exchangeCard1()), actionCollection(), "exchange_card_1"); + (void)new KAction(i18n("Exchange Card 2"), KShortcut(Qt::Key_2), m_kpok, + SLOT(exchangeCard2()), actionCollection(), "exchange_card_2"); + (void)new KAction(i18n("Exchange Card 3"), KShortcut(Qt::Key_3), m_kpok, + SLOT(exchangeCard3()), actionCollection(), "exchange_card_3"); + (void)new KAction(i18n("Exchange Card 4"), KShortcut(Qt::Key_4), m_kpok, + SLOT(exchangeCard4()), actionCollection(), "exchange_card_4"); + (void)new KAction(i18n("Exchange Card 5"), KShortcut(Qt::Key_5), m_kpok, + SLOT(exchangeCard5()), actionCollection(), "exchange_card_5"); + + setupGUI( KMainWindow::Save | StatusBar | Keys | Create); +} + + +void PokerWindow::readOptions() +{ + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + if (m_kpok->getSound() != conf->readBoolEntry("Sound", true)) + soundAction->activate(); + + if (m_kpok->getBlinking() != conf->readBoolEntry("Blinking", true)) + blinkingAction->activate(); + + if (m_kpok->getAdjust() != conf->readBoolEntry("Adjust", true)) + adjustAction->activate(); + + if ( showMenubarAction->isChecked() != + conf->readBoolEntry("ShowMenubar", true)) + showMenubarAction->activate(); + + if ( showStatusbarAction->isChecked() != + conf->readBoolEntry("ShowStatusbar", true)) + showStatusbarAction->activate(); +} + + +void PokerWindow::toggleMenubar() +{ + if (!menuBar()->isHidden()) + menuBar()->hide(); + else + menuBar()->show(); +} + + +void PokerWindow::toggleStatusbar() +{ + if (!statusBar()->isHidden()) + statusBar()->hide(); + else + statusBar()->show(); +} + + +/* Ask the user if he/she wants to save the game. This virtual method + * is called from the Quit KAction (I think). + */ + +bool PokerWindow::queryClose() +{ + if (!m_kpok->isDirty()) + return true; + + // Only ask if the game is changed in some way. + switch(KMessageBox::warningYesNoCancel(this, i18n("Do you want to save this game?"), QString::null, KStdGuiItem::save(), KStdGuiItem::dontSave())) { + case KMessageBox::Yes : + m_kpok->saveGame(); + return true; + case KMessageBox::No : + return true; + default : + return false; + } +} + + +/* Show the hand or winner in the status bar at the lower right. + * + * Which is shown depends on wether this is a one player game or a two + * player game. + */ + +void PokerWindow::setHand(const QString &newHand, bool lastHand) +{ + if (lastHand) + LHLabel->setText(i18n("Last hand: ") + newHand); + else + LHLabel->setText(i18n("Last winner: ") + newHand); + LHLabel->adjustSize(); +} + + +void PokerWindow::showClickToHold(bool show) +{ + if (show) { + statusBar()->clear(); + statusBar()->message(i18n("Click a card to hold it")); + clickToHoldIsShown = true; + } else if (clickToHoldIsShown) { + statusBar()->clear(); + clickToHoldIsShown = false; + } +} + + +void PokerWindow::statusBarMessage(QString s) +{ + clearStatusBar(); + statusBar()->message(s); + clickToHoldIsShown = false; +} + + +void PokerWindow::clearStatusBar() +{ + if (!clickToHoldIsShown) + statusBar()->clear(); +} + + +void PokerWindow::saveOptions() +{ + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + conf->writeEntry("Sound", soundAction->isChecked()); + conf->writeEntry("Blinking", blinkingAction->isChecked()); + conf->writeEntry("Adjust", adjustAction->isChecked()); + conf->writeEntry("ShowMenubar", showMenubarAction->isChecked()); + conf->writeEntry("ShowStatusbar", showStatusbarAction->isChecked()); +} + + +bool PokerWindow::eventFilter(QObject*, QEvent* e) +{ + if (e->type() == QEvent::MouseButtonPress) { + + if (((QMouseEvent*)e)->button() == RightButton) { + QPopupMenu* popup = (QPopupMenu*) factory()->container("popup", this); + if (popup) + popup->popup(QCursor::pos()); + return true; + } else + return false; + } + + return false; +} + +#include "top.moc" + diff --git a/kpoker/top.h b/kpoker/top.h new file mode 100644 index 00000000..715aa20d --- /dev/null +++ b/kpoker/top.h @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef __PokerWindow__ +#define __PokerWindow__ + + +#include + + +class QLabel; +class KToggleAction; +class kpok; + + +class PokerWindow : public KMainWindow +{ + Q_OBJECT + + public: + PokerWindow(); + ~PokerWindow(); + + protected: + virtual bool queryClose(); + bool eventFilter(QObject*, QEvent*); + void initKAction(); + void readOptions(); + + protected slots: + // void saveProperties(KConfig*); + // void readProperties(KConfig*); + void setHand(const QString &newHand, bool lastHand = true); + void showClickToHold(bool show); + void statusBarMessage(QString); + void clearStatusBar(); + void saveOptions(); + void toggleMenubar(); + void toggleStatusbar(); + + private: + kpok *m_kpok; + + KToggleAction *soundAction; + KToggleAction *blinkingAction; + KToggleAction *adjustAction; + KToggleAction *showMenubarAction; + KToggleAction *showStatusbarAction; + + // statusbar elements: + QLabel *LHLabel; + + bool clickToHoldIsShown; +}; + +#endif diff --git a/kpoker/version.h b/kpoker/version.h new file mode 100644 index 00000000..5d3e1503 --- /dev/null +++ b/kpoker/version.h @@ -0,0 +1,3 @@ +#define KPOKER_VERSION "1.1" +#define KPOKER_RELEASE_DATE "2005-09-15" + diff --git a/kreversi/AUTHORS b/kreversi/AUTHORS new file mode 100644 index 00000000..3964c051 --- /dev/null +++ b/kreversi/AUTHORS @@ -0,0 +1,2 @@ +Mario Weilguni Initial coding +Inge Wallin Cleanups, lots of enhancements diff --git a/kreversi/ChangeLog b/kreversi/ChangeLog new file mode 100644 index 00000000..ad743814 --- /dev/null +++ b/kreversi/ChangeLog @@ -0,0 +1,553 @@ +2006-07-03 Inge Wallin + + * kreversi.cpp (slotGameOver): Set state back to Ready after the + game is finished, and before showing highscore. + +2006-07-03 Inge Wallin + + * version.h (KREVERSI_VERSION): Update version to 1.7.1 for KDE + 3.5.4. + +2006-07-03 Inge Wallin + + * Position.cpp (undoMove): Keep track of score when undoing a + move. + + * qreversigameview.h (removeMove): show game status after removing + a move. + +2006-07-02 Inge Wallin + + * qreversigameview.cpp (moveMade): Print color Red/Blue in + addition to White/Black into the game view if non-BW color is + chosen in the preferences. + + + ---------------------------------------------------------------- + New start of ChangeLogging + ---------------------------------------------------------------- + + +2005-09-15 Inge Wallin + + Bump version number for the release of KDE 3.5 + * version.h: Bump version from 1.6 to 1.7 + +2005-04-04 Inge Wallin + + Fix bug where hint and 'show legal moves' didn't work together. + * board.cpp (showHint): call drawSmallCircle if showLegalMoves is true. + (drawSmallCircle): new private method + New feature: show last move. + * board.cpp (setShowLastMove): new method + + + Refactoring: make showing of legal moves simpler + * board.cpp (showLegalMoves): Take bool for on/off instead of Movelist + (quitShowLegalMoves): removed + (m_legalMovesShowing): new bool member instead of m_legalMoves + * kreversi.cpp (misc): don't call showLegalMoves were not necessary + Fix a bug with 'show legal moves': old ones were never erased. + * board.cpp (showLegalMoves): new method broken out of updateBoard + + Some code cleaning and documentation + * DESIGN: Made documentation up-to-date + * qreversigame.{h,cpp} (updateBoard,turn): removed signals + * qreversigameview.{cpp} (slotNewGame): renamed into newGame + (updateBoard): new method + (updateMovelist): new method (empty yet) + (misc proxy methods): simplified. + + Move over more view stuff to the gameview. + * kreversi.cpp (showMove): Renamed into handleMove, most of it + moved to the view + (slotStateChange): removed slot + (turn(Color), score, stateChange): removed signals + (setState): Do the job of slotStateChange. + * qreversigameview.cpp (moveMade): do the job of showMove + + More control of the view by signals + * kreversi.cpp (showTurn): now catches sig_newGame and sig_update + from the game + (showTurn): new slot + + Let the game view be updated by signals from the game instead of + by explicit calls. + * kreversi.cpp (misc): Don't call updateboard et al. + (showColor): Removed + * qreversigame.cpp (sig_score): Removed. + * qreversigameview.{h,cpp} (slotNewGame, moveMade): new slots + +2005-04-03 Inge Wallin + + Clean up the signals from the game and change some explicit calls + to update the view into signal/slots instead. + * kreversi.{h, cpp} (showScore): removed + * qreversigame.{h,cpp} (sig_newGame, sig_update): new signals + (gameOver): signal renamed into sig_gameOver + * qreversigameview.{h,cpp} (StatusWidget::setText): new method + (createView): New private method. + (updateView): new slot + (updateStatus): new slot + (setHumanColor): new method. + + + Move the status info from the toolbar to the gameview. + * kreversi.{h,cpp} (StatusWidget): Removed class + (createStatusBar): Removed. + (m_krgame): renamed into m_game + * qreversigameview.{h,cpp} (StatusWidget): Added class + + Move the movelist to the gameview. + + Refactor: Create a new class QReversiGameView that will comprise + the entire view. + * Unfortunately the details of the change got lost in some stupid + mistake of mine. + +2005-04-02 Inge Wallin + + Fix bug 102890: The result is not put into the higscore if not all + squares are filled at the end of the game + * kreversi.cpp (KReversi): call slotNewGame + +2005-04-01 Inge Wallin + + Fix bug 102297: I am playing in KReversi as "expert" but it saves + statistics to the "beginner" records + * kreversi.h (m_lowestStrength): Should be uint instead of bool. + +2005-03-31 Inge Wallin + + Implement wish 102813: Should be able to show last move + * board.{h,cpp} (m_showLastMove, lastMoveShown): new members + (setShowLastMove, showLastMove): new methods + (updateBoard): show last move. + * kreversi.{h,cpp} (showLastMoveAction): new toggleaction + (slotShowLastMove): new slot + * kreversiui.rc (show_last_move): new action + + Some consecutive small, but important changes (latest at the top). + * Position::undoMove(): new method. + * Remove m_lastPosition from class Game. + * Rename makeMove() to doMove() and takeBackMove() to undoMove(). + + Big changes in the lower levels of the program. Mostly + simplifications. + * Move.h, Move.cpp (SimpleMove): renamed from Move + (Move): new class with undo information + * Position.h, Position.cpp: allow Move and SimpleMove in various + places. + * Game.h, Game.cpp: allow Move and SimpleMove in various places + +2005-03-30 Inge Wallin + + Continue on wish 82900 + * kreversiui.rc (viewToolBar): new toolbar for the views. + * kreversi.{h,cpp} (showLegalMovesAction): new toggleaction + (slotShowLegalMOves): new slot + (misc): check status of toggle action before showing legal moves + NOTE: This change adds a new toolbar. Before testing you must + make install. + + ---------------------------------------------------------------- + + Start on wish 82900: Show possible moves in the current position + * Move.{h,cpp} (Move): new copy constructor + * Move.h (MoveList): new type + * qreversigame.h (position): new method. + * Position.{h,cpp} (generateMoves): new method + * board.{h,cc} (showLegalMoves, quitShowLegalMoves): new methods + (setMarks): Show also legal moves. + * kreversi.cpp: call showLegalMoves() in various places. + + ---------------------------------------------------------------- + + Implement wish 82517: show moves of the game in a view + * Game.h (Game): Make members protected. + (asString): new method + * qreversigame.cpp (makeMove): emit new signal sig_move . + * kreversi.{h,cpp} (m_movesView): new member + (showMove): new slot + +2005-03-29 Inge Wallin + + Implement wish 82519: Label the board with A-H, 1-8 + * board.cpp (OFFSET): new macro + (m_marksShowing): new member + (setMarks): new method + (mousePressEvent): take into account offset. + (updateBoard): draw markings if m_marksShowing is true + (drawOnePiece): take into account offset. + (adjustSize): take into account markings + + Some cleaning + + ---------------------------------------------------------------- + + Move KReversiGame out to its own file, and remove it. + * qreversigame.{h,cpp}: new files. + (class): Inherit from Game instead of containing it. + (signal score): Rename into sig_score(). + * Makefile.am: include new files. + * board.cpp: Remove class KReversiGame + (all methods): Rename to QReversiGame + + Code cleaning + * Game.{h,cpp} (~Game): new method + +2005-03-28 Inge Wallin + + * kreversi.cpp (KReversi): Fix faulty connect(). + + +================================================================ + KDE 3.4 released +================================================================ + + +2005-02-18 Inge Wallin + + * version.h (KREVERSI_VERSION): Bumped version to 1.6 + +2004-10-31 Inge Wallin + + Better fix for bug 91055. + * kreversi.cpp (slotNewGame): Reimplement dialog using + KMessageBox::warningYesNo(). This solves the FIXME in the header. + +2004-10-15 Inge Wallin + + Fix bug 90472: KReversi: When you interrupt the computers move and + then switch sides, the program gets confused + * kreversi.cpp (slotSwitchSides): Don't allow the user to switch + sides if the computers move is interrupted. + +2004-10-11 Inge Wallin + + Code cleaning + * kreversi.{h,cpp}: Make all members follow the m_ convention. + Also added some comments. + + ----------------- CVS commit on stuff below -------------------- + + Fix bug 91055 - KReversi: If you start a new game when a game is + playing, the user is never asked for confirmation. + * kreversi.cpp (slotNewGame): Show a dialog that asks for + confirmation from the user. + +2004-10-09 Inge Wallin + + Fix bug 90203: KReversi: It should be visible when the user + interrupts the computers thinking. + * kreversi.cpp (slotInterrupt): call showTurn(). + (showTurn): Show "(interrupted)" if it is. + NOTE: This fix can't be backported easily since there is a string + freeze for BRANCH_3_3. + + ----------------- CVS commit on stuff below -------------------- + + Fix a bug that made the score unset at startup. + * kreversi.cpp (KReversi): show the score at startup. + + ----------------- CVS commit on stuff below -------------------- + + Finally make KReversi a proper Model/View program (step I.4 and + I.5 from the plan in the TODO file). + * board.{h,cpp} (KReversiGame): new class + * board.{h,cpp} (Board): new name KReversiBoardView + * Lots of minor cleanup + * DESIGN: (class diagram): new info + + ----------------- CVS commit on stuff below -------------------- + + Some minor cleanup. + +2004-10-03 Inge Wallin + + * DESIGN: New document + + ----------------- CVS commit on stuff below -------------------- + + Simplify saving of the game + * Game.{h,cpp} (move(uint)): New method. + * kreversi.cpp (saveGame): Use the new method, and don't call + loadGame to restore the Game object. + +2004-09-29 Inge Wallin + + Continue to make KReversi a proper model/view program: + Step I.1 of the plan (see TODO): Fix the class Game + * Game.h (Game): Convert to store moves instead of positions. + * Game.cpp (Game): Code cleanup and convert as above. + * Game.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp}: Follow naming conventions from the rest of the + program. + * Position.{h,cpp} (Position::operator=): new method. + (Position::makeMove): new method. + + Added myself in the credits in the about window. + (Will add myself to the real authors when we have KGame and + network play ready. :-) ) + +2004-09-27 Inge Wallin + + Continue to make KReversi a proper model/view program: + * Transfer ownership of Game and Engine to kreversi from Board. + board.h, board.cpp, kreversi.h, kreversi.cpp: lots of changes. + + Some other cleanup: + * SuperEngine.h (interrupt): renamed to interrupted() + +2004-09-26 Inge Wallin + + Fix bug 90195: KReversi: Changing the skill level late in a game + doesn't count as cheating: + * board.h (Board::m_lowestStrength): new member + * board.cpp (Board::newGame): set m_lowestStrength + * board.cpp (Board::setStrength): update m_lowestStrength and + update highscore type. + + Fix Bug 90190: KReversi: Switch sides and then Undo gets the + program out of sync. + * board.cpp (doUndo): If it is the computers turn to move after an + undo, call computerMakeMove(). + (doUndo): Fix repainting so that it looks nice. + +2004-09-25 Inge Wallin + + Transfer the rest of the slots for KActions to kreversi.cpp + * Board::interrupt() -> KReversi::slotInterrupt() + * Board::doContinue() -> KReversi::slotContinue() + + Rename some slots for clarity + * KReversi::switchSides() -> KReversi::slotSwitchSides() + * KReversi::showSettings() -> KReversi::slotEditSettings() + + Make a trivial function inline: + * Board::interrupt() + +2004-09-24 Inge Wallin + + Start the work to port KReversi to KGame/Kplayer: + + Transfer the slots for most KActions to kreversi.cpp + * Board::undo() -> KReversi::slotUndo() + (Board::doUndo()): Do the real work of undoing. + * Board->hint() -> KReversi::slotHint() + (Board::showHint): do the actual work of showing the hint. + + Rename some slots for clarity + * KReversi::newGame -> KReversi::slotNewGame + * KReversi::openGame -> KReversi::slotOpenGame + * KReversi::save -> KReversi::slotSave + + Make some trivial functions inline: + * Board::whoseTurn() + * Board::moveNumber() + * Board::score(Color) + * Board::interrupted() + * Board::strength() + +2004-09-23 Anne-Marie Mahfouf + + Cleaned some previously left lines of code in board.cpp + Change CustomAdditions=false back in prefs.kcfg to fix compilation + Tested Inges fix and found it works well. + +2004-09-22 Inge Wallin + + Fix bug 89829: "KReversi: When you save a game, the color for + Human and Computer is not saved" again. See the discussion on the + KDE bugzilla for details + (http://bugs.kde.org/show_bug.cgi?id=89829). + * board.cpp (Board::saveGame): Save m_humanColor as HumanColor. + * Remove saving of the side to move since this is implicit + anyway. + * (Board::loadGame): Fix loading of m_humanColor and + m_competitiveGame + * Fix emit of signal turn, and the condition to call + computerMakeMove(). + * prefs_addons.h: Removed + + +2004-09-18 Anne-Marie Mahfouf (ChangeLog entry by Inge Wallin) + + Fix bug 89829. (See above, though) + * prefs_addons.h: New file + * board.cpp (saveGame): Some changes + +2004-09-18 Inge Wallin + + Some cleaning: remove unused members, add m_ to members and some + comments. + * Engine.h (Engine::lastYield): removed + * Board.h (Board::nopaint): removed + * kreversi.{h,cpp} (KReversi::board): renamed into m_board. + +2004-09-17 Inge Wallin + + Some further cleanup: + * board.{h,cpp} (game, engine, human): renamed into m_game, + m_engine and m_humanColor. + +2004-09-16 Inge Wallin + + Support Casual and Competitive play: + * SuperEngine.cpp (SuperEngine::computeMove()): new parameter + 'competitive' + * Engine.cpp (Engine::computeMove()): new parameter 'competitive' + * board.cpp (m_competitiveGame): new member + (saveGame, loadGame): Save competitive in config file. + * kreversi.cpp (slotGameEnded): Only store result in highscore + file if the game was competitive all the time. + * kreversi.kcfg (CompetitiveGameChoice): new setting + * settings.ui: redesigned + + +2004-08-17 Inge Wallin + + Finish the big code cleanup: + * board.h, board.cpp: cleaned up + * highscores.h, highscores.cpp, + * kzoommainwindow.h, kzoommainwindow.cpp: + Converted to same coding style as rest of kreversi. + + +2004-08-16 Inge Wallin + + Continue the big code cleanup: + * Engine.h, Engine.cpp + * kreversi.h kreversi.cpp + + +2004-08-15 Inge Wallin + + Continue the big code cleanup: + * SuperEngine.h, SuperEngine.cpp + * Game.h Game.cpp + +2004-08-14 Inge Wallin + + Start of the big code cleanup: + * Move.h, Move.cpp + * Score.h, Score.cpp + * main.cpp + * Position.h, Position.cpp + + Step 2 in the plan to use KGame from libkdegames: + * Code cleaning: Add some comments, reduce complexity, improve + indentation + * Add m_to_move to class Position. + + + Step 1 in the plan to use KGame from libkdegames: + * Code cleaning: Change "enum Player" into "enum Color", since that + is really what it describes. + +2004-06-29 (1.5) Nicolas Hadacek + * use KZoomMainWindow + +2004-05-29 (1.4) Nicolas Hadacek + * fix statusbar + cleanup code + * use notify framework for sounds + +1999-06-20 Mario Weilguni + * fixed bugs with those damned KStdDirs + * removed the private wallpapers and use the KDE ones instead + * use kimgio + * fixed locating toolbar icons + * compiles now with --enable-final + +1999-06-16 Mario Weilguni + * adapted to the upcoming KDE-2 + +1999-02-01 Mario Weilguni + * fixed a warning (egcs) + +1.0 + * I finally decided that it´s stable enough. This is the final + version (if no further bugs are detected and I do not have a + good idea what to improve) + + * ChangeLog reversed + +0.6.6 + * [Robert Williams] Changed Reversi.kdelnk to kreversi.kdelnk + * [Robert Williams] Add -caption "%c" to kreversi.kdelnk + * [Robert Williams] Added kapp->getCaption() + * [Robert Williams] getHelpMenu(true, 0) -> Uses own About dialog + +0.6.5 Support for non GNUC++ compilers. + +0.6.4 fixed that get-hit-and-then-doubleclick bug + + fixed a bug that caused the computer to switch sides if no + computer move is possible instead of getting another human move + +0.6.3 sound fix: when the animation is finished, the correct piece gets + redrawn before doing a sound-sync (how could I ever believe 0.6.2 + would be the last change :-) + +0.6.2 animation fixed (hope this will be the last change) + +0.6.1 fixed that newly introduced highscore bug + computer continues now if a game was saved while thinking + +0.6: better integration into new FSSTND + fixes for new kdecore + layout management for all dialogs + mini-icon and icon + locale-strings changed - partial translation required for + other languages than english and german + removed both the kfixedtopwidget and ktablistbox + drawing a border around the reversi board + session-management - what an overkill for kreversi :-)) + you can save (and load) exactly ONE game + + +0.5: added klocale to support translation + added german translation + fixed a few bugs + tried to remove absolute widget placing/sizing + removed the date field from the HOF + grayscale support + fixed those CPU busy bug + removed the -finline-functions optimize flag (not portable) + ported to new KDE file system standards (well, mostly) + fixed segfaulting on exit + +0.4: interims release - no changelog + +0.3: Sound support (using libmediatool) + When switching sides, the Quit -> the computer made on move. FIXED + Fixed a few typos + Cursor changes when thinking + +0.2.1: times(NULL) does not work with FreeBSD (fixed) + fixed a bug (reported by Stephan Kulow) where pixmaps of pieces + are not initialized properly (could't reproduce the bug with + my system) + New "About" dialog showing all (well, most) of the contributors + All xpm's have now 8 bits per color component instead of 16. + 16 bits seems to confuse the XPM loader of Qt. + +0.2: better pieces + animations + fixed a small bug: when someone made it in the hall of fame, + he was'nt notified of this + some improvements in the Hall Of Fame + +0.1.2: background color selectable via dialog + background pixmaps implemented, selectable via menu + background pixmaps are scaled to fit size + pieces are drawn at runtime instead of pixmaps + some accelerators added + toolbar buttons for help and hint added + +0.1.1: now pixmaps are installed + a kdelnk file is installed + version numbering changed + +0.1 : First release diff --git a/kreversi/DESIGN b/kreversi/DESIGN new file mode 100644 index 00000000..d2a13202 --- /dev/null +++ b/kreversi/DESIGN @@ -0,0 +1,121 @@ +This file describes the overall design of KReversi. Some of the +classes have different names at this point, but that is indicated by a +(now: XXX) tag. These names will be changed when the KDE project has +converted from CVS to Subversion since Subversion has so much better +support for renaming files and directories. + +Almost all of the classes hold more members than are mentioned here, +but those are implementation details and would only obscure the +overall picture. This file is only to give a helicopter view of the +program, not to serve as detailed documentation. + + +Classes +======= + +Class Diagram (See details below) +------------- + +KReversi----------------------------------------------+ + | | +--------------------------------+ | + | +-----------------+ | | + | | | | + | v v v + | QReversiGameView Engine StatusBar + | | | | (shows whose turn it is) + | +- - - - - - - + | +----------+ + | | v v + | +- - - -QReversiBoardView Other widgets + | | | (movelist, score) + v v v +QReversiGame + X + v + Game + I +=========+ + I I + v v +Position Move[] + + +Legend: +XXXXXXXX> Inherits +========> Contains +--------> Ownership pointer +- - - - > Reference pointer + + +Details +------- + +ReversiPosition (now: Position) + Holds a Reversi position. This is the object that implements the + Reversi rules. + + +ReversiMove (now: Move) + A move in a Reversi game. + + +ReversiGame (now: Game) + Holds all the moves of the game being played. + Contains: ReversiPosition Holds the current position. + Move[60] + FIXME: Remove the ReversiPosition and let it be implicit? + FIXME: Implement variations (later) + + +Engine + Can generate a move, given a ReversiPosition. + + +QReversiGame + The "document" for KReversi. + Handles a game being played and sends signals to all its views + when something changes. Basically, the only difference between + this class and the more basic ReversiGame is that it sends signals + to the views. + Inherits: ReversiGame The actual game being played + + +QReversiGameView (status: PLANNED) + + Shows a view of a complete game. Currently this comprises a board + view, a listbox with a list of the moves, two status widgets + showing the current score. + + Contains: *QReversiBoardView + *QListBox + *StatusWidget (two of them) + + +QReversiBoardView + + A view for a Reversi board. The rest of the game view is + implemented in the class QReversiGameView. + + Inherits: QWidget. + Contains: *QReversiGame (not owner) + + FIXME: Enhance the view with timing information, clock, etc. + + +KReversi + The main class for the KReversi program + Contains: KActions + *QReversiGame (owner) + *KReversiGameView (owner) + *Engine (owner) + Statusbar + + FIXME: Let the class also own two players. + + +EngineView (status: PLANNED) + View of the internal thought processes of an Engine (trace, etc). + + +Some notes +---------- + +* KReversi follows the Model/View principle strictly. diff --git a/kreversi/Engine.cpp b/kreversi/Engine.cpp new file mode 100644 index 00000000..da7750ce --- /dev/null +++ b/kreversi/Engine.cpp @@ -0,0 +1,787 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + + +#include + +#include "Engine.h" + + +// ================================================================ +// Class ULONG64 + + +#if !defined(__GNUC__) + + +ULONG64::ULONG64() : QBitArray(64) +{ + fill(0); +} + + +// Initialize an ULONG64 from a 32 bit value. +// + +ULONG64::ULONG64( unsigned int value ) : QBitArray(64) +{ + fill(0); + for(int i = 0; i < 32; i++) { + setBit(i, (bool)(value & 1)); + value >>= 1; + } +} + + +// Shift an ULONG64 left one bit. +// + +void ULONG64::shl() +{ + for(int i = 63; i > 0; i--) + setBit(i, testBit(i - 1)); + setBit(0, 0); +} + +#endif + + +// ================================================================ +// Classes SquareStackEntry and SquareStack + + +// A SquareStack is used to store changes to the squares on the board +// during search. + + +inline void SquareStackEntry::setXY(int x, int y) { + m_x = x; + m_y = y; +} + + +SquareStackEntry::SquareStackEntry() +{ + setXY(0,0); +} + + +// ---------------------------------------------------------------- + + +SquareStack::SquareStack() { + init(0); +} + + +SquareStack::SquareStack(int size) { + init(size); +} + + +void SquareStack::resize(int size) +{ + m_squarestack.resize(size); +} + + +// (Re)initialize the stack so that is empty, and at the same time +// resize it to 'size'. +// + +void SquareStack::init(int size) +{ + resize(size); + + m_top = 0; + for (int i = 0; i < size; i++) + m_squarestack[i].setXY(0,0); +} + + + +inline SquareStackEntry SquareStack::Pop() +{ + return m_squarestack[--m_top]; +} + + +inline void SquareStack::Push(int x, int y) +{ + m_squarestack[m_top].m_x = x; + m_squarestack[m_top++].m_y = y; +} + + +// ================================================================ +// Class MoveAndValue + + +// Class MoveAndValue aggregates a move with its value. +// + + +inline void MoveAndValue::setXYV(int x, int y, int value) +{ + m_x = x; + m_y = y; + m_value = value; +} + + +MoveAndValue::MoveAndValue() +{ + setXYV(0,0,0); +} + + +MoveAndValue::MoveAndValue(int x, int y, int value) +{ + setXYV(x, y, value); +} + + +// ================================================================ +// The Engine itself + + +// Some special values used in the search. +const int Engine::LARGEINT = 99999; +const int Engine::ILLEGAL_VALUE = 8888888; +const int Engine::BC_WEIGHT = 3; + + +Engine::Engine(int st, int sd) : SuperEngine(st, sd) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine(int st) : SuperEngine(st) +{ + SetupBcBoard(); + SetupBits(); +} + + +Engine::Engine() : SuperEngine(1) +{ + SetupBcBoard(); + SetupBits(); +} + + +// keep GUI alive +void Engine::yield() +{ + qApp->processEvents(); +} + + +// Calculate the best move from the current position, and return it. + +Move Engine::computeMove(Game *game, bool competitive) +{ + Color color; + + // A competitive game is one where we try our damnedest to make the + // best move. The opposite is a casual game where the engine might + // make "a mistake". The idea behind this is not to scare away + // newbies. The member m_competitive is used during search for this + // very move. + m_competitive = competitive; + + // Suppose that we should give a heuristic evaluation. If we are + // close to the end of the game we can make an exhaustive search, + // but that case is determined further down. + m_exhaustive = false; + + // Get the color to calculate the move for. + color = game->toMove(); + if (color == Nobody) + return Move(Nobody, -1, -1); + + // Figure out the current score + m_score.set(White, game->score(White)); + m_score.set(Black, game->score(Black)); + + // Treat the first move as a special case (we can basically just + // pick a move at random). + if (m_score.score(White) + m_score.score(Black) == 4) + return ComputeFirstMove(game); + + // Let there be room for 3000 changes during the recursive search. + // This is more than will ever be needed. + m_squarestack.init(3000); + + // Get the search depth. If we are close to the end of the game, + // the number of possible moves goes down, so we can search deeper + // without using more time. + m_depth = m_strength; + if (m_score.score(White) + m_score.score(Black) + m_depth + 3 >= 64) + m_depth = 64 - m_score.score(White) - m_score.score(Black); + else if (m_score.score(White) + m_score.score(Black) + m_depth + 4 >= 64) + m_depth += 2; + else if (m_score.score(White) + m_score.score(Black) + m_depth + 5 >= 64) + m_depth++; + + // If we are very close to the end, we can even make the search + // exhaustive. + if (m_score.score(White) + m_score.score(Black) + m_depth >= 64) + m_exhaustive = true; + + // The evaluation is a linear combination of the score (number of + // pieces) and the sum of the scores for the squares (given by + // m_bc_score). The earlier in the game, the more we use the square + // values and the later in the game the more we use the number of + // pieces. + m_coeff = 100 - (100* + (m_score.score(White) + m_score.score(Black) + + m_depth - 4)) / 60; + + // Initialize the board that we use for the search. + for (uint x = 0; x < 10; x++) + for (uint y = 0; y < 10; y++) { + if (1 <= x && x <= 8 + && 1 <= y && y <= 8) + m_board[x][y] = game->color(x, y); + else + m_board[x][y] = Nobody; + } + + // Initialize a lot of stuff that we will use in the search. + + // Initialize m_bc_score to the current bc score. This is kept + // up-to-date incrementally so that way we won't have to calculate + // it from scratch for each evaluation. + m_bc_score.set(White, CalcBcScore(White)); + m_bc_score.set(Black, CalcBcScore(Black)); + + ULONG64 colorbits = ComputeOccupiedBits(color); + ULONG64 opponentbits = ComputeOccupiedBits(opponent(color)); + + int maxval = -LARGEINT; + int max_x = 0; + int max_y = 0; + + MoveAndValue moves[60]; + int number_of_moves = 0; + int number_of_maxval = 0; + + setInterrupt(false); + + ULONG64 null_bits; + null_bits = 0; + + // The main search loop. Step through all possible moves and keep + // track of the most valuable one. This move is stored in + // (max_x, max_y) and the value is stored in maxval. + m_nodes_searched = 0; + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + // Don't bother with non-empty squares and squares that aren't + // neighbors to opponent pieces. + if (m_board[x][y] != Nobody + || (m_neighbor_bits[x][y] & opponentbits) == null_bits) + continue; + + + int val = ComputeMove2(x, y, color, 1, maxval, + colorbits, opponentbits); + + if (val != ILLEGAL_VALUE) { + moves[number_of_moves++].setXYV(x, y, val); + + // If the move is better than all previous moves, then record + // this fact... + if (val > maxval) { + + // ...except that we want to make the computer miss some + // good moves so that beginners can play against the program + // and not always lose. However, we only do this if the + // user wants a casual game, which is set in the settings + // dialog. + int randi = m_random.getLong(7); + if (maxval == -LARGEINT + || m_competitive + || randi < (int) m_strength) { + maxval = val; + max_x = x; + max_y = y; + + number_of_maxval = 1; + } + } + else if (val == maxval) + number_of_maxval++; + } + + // Jump out prematurely if interrupt is set. + if (interrupted()) + break; + } + } + + // long endtime = times(&tmsdummy); + + // If there are more than one best move, the pick one randomly. + if (number_of_maxval > 1) { + int r = m_random.getLong(number_of_maxval) + 1; + int i; + + for (i = 0; i < number_of_moves; i++) { + if (moves[i].m_value == maxval && --r <= 0) + break; + } + + max_x = moves[i].m_x; + max_y = moves[i].m_y; + } + + // Return a suitable move. + if (interrupted()) + return Move(Nobody, -1, -1); + else if (maxval != -LARGEINT) + return Move(color, max_x, max_y); + else + return Move(Nobody, -1, -1); +} + + +// Get the first move. We can pick any move at random. +// + +Move Engine::ComputeFirstMove(Game *game) +{ + int r; + Color color = game->toMove(); + + r = m_random.getLong(4) + 1; + + if (color == White) { + if (r == 1) return Move(color, 3, 5); + else if (r == 2) return Move(color, 4, 6); + else if (r == 3) return Move(color, 5, 3); + else return Move(color, 6, 4); + } + else { + if (r == 1) return Move(color, 3, 4); + else if (r == 2) return Move(color, 5, 6); + else if (r == 3) return Move(color, 4, 3); + else return Move(color, 6, 5); + } +} + + +// Play a move at (xplay, yplay) and generate a value for it. If we +// are at the maximum search depth, we get the value by calling +// EvaluatePosition(), otherwise we get it by performing an alphabeta +// search. +// + +int Engine::ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, ULONG64 colorbits, + ULONG64 opponentbits) +{ + int number_of_turned = 0; + SquareStackEntry mse; + Color opponent = ::opponent(color); + + m_nodes_searched++; + + // Put the piece on the board and incrementally update scores and bitmaps. + m_board[xplay][yplay] = color; + colorbits |= m_coord_bit[xplay][yplay]; + m_score.inc(color); + m_bc_score.add(color, m_bc_board[xplay][yplay]); + + // Loop through all 8 directions and turn the pieces that can be turned. + for (int xinc = -1; xinc <= 1; xinc++) + for (int yinc = -1; yinc <= 1; yinc++) { + if (xinc == 0 && yinc == 0) + continue; + + int x, y; + + for (x = xplay + xinc, y = yplay + yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If we found the end of a turnable row, then go back and turn + // all pieces on the way back. Also push the squares with + // turned pieces on the squarestack so that we can undo the move + // later. + if (m_board[x][y] == color) + for (x -= xinc, y -= yinc; x != xplay || y != yplay; + x -= xinc, y -= yinc) { + m_board[x][y] = color; + colorbits |= m_coord_bit[x][y]; + opponentbits &= ~m_coord_bit[x][y]; + + m_squarestack.Push(x, y); + + m_bc_score.add(color, m_bc_board[x][y]); + m_bc_score.sub(opponent, m_bc_board[x][y]); + number_of_turned++; + } + } + + int retval = -LARGEINT; + + // If we managed to turn at least one piece, then (xplay, yplay) was + // a legal move. Now find out the value of the move. + if (number_of_turned > 0) { + + // First adjust the number of pieces for each side. + m_score.add(color, number_of_turned); + m_score.sub(opponent, number_of_turned); + + // If we are at the bottom of the search, get the evaluation. + if (level >= m_depth) + retval = EvaluatePosition(color); // Terminal node + else { + int maxval = TryAllMoves(opponent, level, cutoffval, opponentbits, + colorbits); + + if (maxval != -LARGEINT) + retval = -maxval; + else { + + // No possible move for the opponent, it is colors turn again: + retval = TryAllMoves(color, level, -LARGEINT, colorbits, opponentbits); + + if (retval == -LARGEINT) { + + // No possible move for anybody => end of game: + int finalscore = m_score.score(color) - m_score.score(opponent); + + if (m_exhaustive) + retval = finalscore; + else { + // Take a sure win and avoid a sure loss (may not be optimal): + + if (finalscore > 0) + retval = LARGEINT - 65 + finalscore; + else if (finalscore < 0) + retval = -(LARGEINT - 65 + finalscore); + else + retval = 0; + } + } + } + } + + m_score.add(opponent, number_of_turned); + m_score.sub(color, number_of_turned); + } + + // Undo the move. Start by unturning the turned pieces. + for (int i = number_of_turned; i > 0; i--) { + mse = m_squarestack.Pop(); + m_bc_score.add(opponent, m_bc_board[mse.m_x][mse.m_y]); + m_bc_score.sub(color, m_bc_board[mse.m_x][mse.m_y]); + m_board[mse.m_x][mse.m_y] = opponent; + } + + // Now remove the new piece that we put down. + m_board[xplay][yplay] = Nobody; + m_score.sub(color, 1); + m_bc_score.sub(color, m_bc_board[xplay][yplay]); + + // Return a suitable value. + if (number_of_turned < 1 || interrupted()) + return ILLEGAL_VALUE; + else + return retval; +} + + +// Generate all legal moves from the current position, and do a search +// to see the value of them. This function returns the value of the +// most valuable move, but not the move itself. +// + +int Engine::TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits) +{ + int maxval = -LARGEINT; + + // Keep GUI alive by calling the event loop. + yield(); + + ULONG64 null_bits; + null_bits = 0; + + for (int x = 1; x < 9; x++) { + for (int y = 1; y < 9; y++) { + if (m_board[x][y] == Nobody + && (m_neighbor_bits[x][y] & colorbits) != null_bits) { + int val = ComputeMove2(x, y, opponent, level+1, maxval, opponentbits, + colorbits); + + if (val != ILLEGAL_VALUE && val > maxval) { + maxval = val; + if (maxval > -cutoffval || interrupted()) + break; + } + } + } + + if (maxval > -cutoffval || interrupted()) + break; + } + + if (interrupted()) + return -LARGEINT; + + return maxval; +} + + +// Calculate a heuristic value for the current position. If we are at +// the end of the game, do this by counting the pieces. Otherwise do +// it by combining the score using the number of pieces, and the score +// using the board control values. +// + +int Engine::EvaluatePosition(Color color) +{ + int retval; + + Color opponent = ::opponent(color); + + int score_color = m_score.score(color); + int score_opponent = m_score.score(opponent); + + if (m_exhaustive) + retval = score_color - score_opponent; + else { + retval = (100-m_coeff) * + (m_score.score(color) - m_score.score(opponent)) + + m_coeff * BC_WEIGHT * (m_bc_score.score(color) + - m_bc_score.score(opponent)); + } + + return retval; +} + + +// Calculate bitmaps for each square, and also for neighbors of each +// square. +// + +void Engine::SetupBits() +{ + //m_coord_bit = new long[9][9]; + //m_neighbor_bits = new long[9][9]; + + ULONG64 bits = 1; + + // Store a 64 bit unsigned it with the corresponding bit set for + // each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_coord_bit[i][j] = bits; +#if !defined(__GNUC__) + bits.shl(); +#else + bits *= 2; +#endif + } + + // Store a bitmap consisting of all neighbors for each square. + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + m_neighbor_bits[i][j] = 0; + + for (int xinc=-1; xinc<=1; xinc++) + for (int yinc=-1; yinc<=1; yinc++) { + if (xinc != 0 || yinc != 0) + if (i + xinc > 0 && i + xinc < 9 && j + yinc > 0 && j + yinc < 9) + m_neighbor_bits[i][j] |= m_coord_bit[i + xinc][j + yinc]; + } + } +} + + +// Set up the board control values that will be used in evaluation of +// the position. +// + +void Engine::SetupBcBoard() +{ + // JAVA m_bc_board = new int[9][9]; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) { + if (i == 2 || i == 7) + m_bc_board[i][j] = -1; + else + m_bc_board[i][j] = 0; + + if (j == 2 || j == 7) + m_bc_board[i][j] -= 1; + } + + m_bc_board[1][1] = 2; + m_bc_board[8][1] = 2; + m_bc_board[1][8] = 2; + m_bc_board[8][8] = 2; + + m_bc_board[1][2] = -1; + m_bc_board[2][1] = -1; + m_bc_board[1][7] = -1; + m_bc_board[7][1] = -1; + m_bc_board[8][2] = -1; + m_bc_board[2][8] = -1; + m_bc_board[8][7] = -1; + m_bc_board[7][8] = -1; +} + + +// Calculate the board control score. +// + +int Engine::CalcBcScore(Color color) +{ + int sum = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) + sum += m_bc_board[i][j]; + + return sum; +} + + +// Calculate a bitmap of the occupied squares for a certain color. +// + +ULONG64 Engine::ComputeOccupiedBits(Color color) +{ + ULONG64 retval = 0; + + for (int i=1; i < 9; i++) + for (int j=1; j < 9; j++) + if (m_board[i][j] == color) retval |= m_coord_bit[i][j]; + + return retval; +} + diff --git a/kreversi/Engine.h b/kreversi/Engine.h new file mode 100644 index 00000000..a84be895 --- /dev/null +++ b/kreversi/Engine.h @@ -0,0 +1,245 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Engine produces moves from a Game object through calls to the +// function ComputeMove(). +// +// First of all: this is meant to be a simple example of a game playing +// program. Not everything is done in the most clever way, particularly not +// the way the moves are searched, but it is hopefully made in a way that makes +// it easy to understand. The function ComputeMove2() that does all the work +// is actually not much more than a hundred lines. Much could be done to +// make the search faster though, I'm perfectly aware of that. Feel free +// to experiment. +// +// The method used to generate the moves is called minimax tree search with +// alpha-beta pruning to a fixed depth. In short this means that all possible +// moves a predefined number of moves ahead are either searched or refuted +// with a method called alpha-beta pruning. A more thorough explanation of +// this method could be found at the world wide web at http: +// //yoda.cis.temple.edu:8080/UGAIWWW/lectures96/search/minimax/alpha-beta.html +// at the time this was written. Searching for "minimax" would also point +// you to information on this subject. It is probably possible to understand +// this method by reading the source code though, it is not that complicated. +// +// At every leaf node at the search tree, the resulting position is evaluated. +// Two things are considered when evaluating a position: the number of pieces +// of each color and at which squares the pieces are located. Pieces at the +// corners are valuable and give a high value, and having pieces at squares +// next to a corner is not very good and they give a lower value. In the +// beginning of a game it is more important to have pieces on "good" squares, +// but towards the end the total number of pieces of each color is given a +// higher weight. Other things, like how many legal moves that can be made in a +// position, and the number of pieces that can never be turned would probably +// make the program stronger if they were considered in evaluating a position, +// but that would make things more complicated (this was meant to be very +// simple example) and would also slow down computation (considerably?). +// +// The member m_board[10][10]) holds the current position during the +// computation. It is initiated at the start of ComputeMove() and +// every move that is made during the search is made on this board. It should +// be noted that 1 to 8 is used for the actual board, but 0 and 9 can be +// used too (they are always empty). This is practical when turning pieces +// when moves are made on the board. Every piece that is put on the board +// or turned is saved in the stack m_squarestack (see class SquareStack) so +// every move can easily be reversed after the search in a node is completed. +// +// The member m_bc_board[][] holds board control values for each square +// and is initiated by a call to the function private void SetupBcBoard() +// from Engines constructor. It is used in evaluation of positions except +// when the game tree is searched all the way to the end of the game. +// +// The two members m_coord_bit[9][9] and m_neighbor_bits[9][9] are used to +// speed up the tree search. This goes against the principle of keeping things +// simple, but to understand the program you do not need to understand them +// at all. They are there to make it possible to throw away moves where +// the piece that is played is not adjacent to a piece of opposite color +// at an early stage (because they could never be legal). It should be +// pointed out that not all moves that pass this test are legal, there will +// just be fewer moves that have to be tested in a more time consuming way. +// +// There are also two other members that should be mentioned: Score m_score +// and Score m_bc_score. They hold the number of pieces of each color and +// the sum of the board control values for each color during the search +// (this is faster than counting at every leaf node). +// + +// The classes SquareStackEntry and SquareStack implement a +// stack that is used by Engine to store pieces that are turned during +// searching (see ComputeMove()). +// +// The class MoveAndValue is used by Engine to store all possible moves +// at the first level and the values that were calculated for them. +// This makes it possible to select a random move among those with equal +// or nearly equal value after the search is completed. + +#ifndef __ENGINE__H__ +#define __ENGINE__H__ + +#include "SuperEngine.h" +#include "Position.h" +#include "Game.h" +#include "Move.h" +#include "Score.h" +#include +#include +#include + + +// Class ULONG64 is used as a bitmap for the squares. + +#if defined(__GNUC__) +#define ULONG64 unsigned long long int +#else +class ULONG64 : public QBitArray { +public: + ULONG64(); + ULONG64( unsigned int ); + void shl(); +}; +#endif + + +// SquareStackEntry and SquareStack are used during search to keep +// track of turned pieces. + +class SquareStackEntry +{ +public: + SquareStackEntry(); + + void setXY(int x, int y); + +public: + int m_x; + int m_y; +}; + + +class SquareStack +{ +public: + SquareStack(); + SquareStack(int size); + + void resize(int size); + void init(int size); + SquareStackEntry Pop(); + void Push(int x, int y); + +private: + QMemArray m_squarestack; + int m_top; +}; + + +// Connect a move with its value. + +class MoveAndValue +{ +public: + MoveAndValue(); + MoveAndValue(int x, int y, int value); + + void setXYV(int x, int y, int value); + +public: + int m_x; + int m_y; + int m_value; +}; + + +// The real beef of this program: the engine that finds good moves for +// the computer player. +// +class Engine : public SuperEngine { +public: + Engine(int st, int sd); + Engine(int st); + Engine(); + + Move computeMove(Game *game, bool competitive); + +private: + Move ComputeFirstMove(Game *game); + int ComputeMove2(int xplay, int yplay, Color color, int level, + int cutoffval, + ULONG64 colorbits, ULONG64 opponentbits); + + int TryAllMoves(Color opponent, int level, int cutoffval, + ULONG64 opponentbits, ULONG64 colorbits); + + int EvaluatePosition(Color color); + void SetupBcBoard(); + void SetupBits(); + int CalcBcScore(Color color); + ULONG64 ComputeOccupiedBits(Color color); + + void yield(); + +private: + static const int LARGEINT; + static const int ILLEGAL_VALUE; + static const int BC_WEIGHT; + + Color m_board[10][10]; + int m_bc_board[9][9]; + Score m_score; + Score m_bc_score; + SquareStack m_squarestack; + + int m_depth; + int m_coeff; + int m_nodes_searched; + bool m_exhaustive; + bool m_competitive; + + ULONG64 m_coord_bit[9][9]; + ULONG64 m_neighbor_bits[9][9]; +}; + +#endif diff --git a/kreversi/Game.cpp b/kreversi/Game.cpp new file mode 100644 index 00000000..e389fdd3 --- /dev/null +++ b/kreversi/Game.cpp @@ -0,0 +1,265 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean makeMove(Move &move) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#include + +#include "Game.h" + + + +Game::Game() +{ + newGame(); +} + +Game::~Game() +{ +} + + +// Start a new game and reset the position to before the first move. +// + +void Game::newGame() +{ + m_position.setupStartPosition(); + m_moveNumber = 0; +} + + +// Return the last move made in the game. +// + +Move Game::lastMove() const +{ + // If no moves where made, return a NULL move. + if (m_moveNumber == 0) + return Move(); + + return m_moves[m_moveNumber - 1]; +} + + +Move +Game::move(uint moveNo) const +{ + assert(moveNo < m_moveNumber); + + return m_moves[moveNo]; +} + + + +// Return true if the move is legal in the current position. +// + +bool Game::moveIsLegal(SimpleMove &move) const +{ + return m_position.moveIsLegal(move); +} + + +// Return true if the color can make a move in the current position. + +bool Game::moveIsPossible(Color color) const +{ + return m_position.moveIsPossible(color); +} + + +// Return true if any side can make a move in the current position. +// + +bool Game::moveIsAtAllPossible() const +{ + return m_position.moveIsAtAllPossible(); +} + + +// Make a move in the game, resulting in a new position. +// +// If everything went well, return true. Otherwise return false and +// do nothing. + +bool Game::doMove(Move &move) +{ + Position lastPos = m_position; + + // Some sanity checks. + if (move.color() == Nobody) + return false; + + if (toMove() != move.color()) + return false; + + // Make the move in the position and store it. Don't allow illegal moves. + if (!m_position.doMove(move)) + return false; + + m_moves[m_moveNumber++] = move; + + return true; +} + + +bool Game::doMove(SimpleMove &smove) +{ + Move move(smove); + + return doMove(move); +} + + +// Take back the last move. +// +// Note: The removed move is not remembered, so a redo is not possible. +// + +bool Game::undoMove() +{ + if (m_moveNumber == 0) + return false; + +#if 0 + m_position.setupStartPosition(); + m_moveNumber--; + for (uint i = 0; i < m_moveNumber; i++) + m_position.doMove(m_moves[i]); +#else + m_position.undoMove(m_moves[--m_moveNumber]); +#endif + + return true; +} + + +// ---------------------------------------------------------------- +// Reversi specific methods + + +// Return true if the square at (x, y) was changed during the last move. +// + +bool Game::squareModified(uint x, uint y) const +{ + // If the move number is zero, we want to redraw all squares. + // That's why we return true here. + if (m_moveNumber == 0) + return true; + + return m_moves[m_moveNumber - 1].squareModified(x, y); +} + + +// Return true if the piece at square (x, y) was turned during the last move. +// + +bool Game::wasTurned(uint x, uint y) const +{ + // Nothing turned before the first move. + if (m_moveNumber == 0) + return false; + + Color color = m_position.color(x, y); + + if (color == Nobody) + return false; + + return m_moves[m_moveNumber - 1].wasTurned(x, y); +} diff --git a/kreversi/Game.h b/kreversi/Game.h new file mode 100644 index 00000000..c33a2096 --- /dev/null +++ b/kreversi/Game.h @@ -0,0 +1,143 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Game represents a complete or incomplete Othello game. It uses +// the classes Score and Move (and internally Position). +// You can make moves, take back one move at a time, reset to initial position +// and get certain data on the current position. + +// Public functions: + +// public Game() +// Creates a game with the initial position. + +// public void Reset() +// Resets to the initial position. + +// public boolean MakeMove(Move m) +// Makes the move m. Returns false if the move is not legal or when called +// with a move where the player is Score.NOBODY. + +// public boolean TakeBackMove() +// Takes back a move. Returns true if not at the initial position. + +// public int GetSquare(int x, int y) +// Returns the piece at (x, y). Returns Score.NOBODY if the square is not +// occupied. + +// public int GetScore(int player) +// Returns the score for player. + +// public Move GetLastMove() +// Returns the last move. Returns null if at the initial position. + +// public boolean MoveIsLegal(Move m) +// Checks if move m is legal. + +// public boolean MoveIsPossible(int player) +// Checks if there is a legal move for player. + +// public boolean MoveIsAtAllPossible() +// Checks if there are any legal moves at all. + +// public int GetMoveNumber() +// Returns move number. + +// public int GetWhoseTurn() +// Returns the player in turn to play (if there are no legal moves +// Score.NOBODY is returned). + +// public Move[] TurnedByLastMove() +// Returns a vector of the squares that were changed by the last move. +// The move that was actually played is at index 0. At the initial +// position the length of the vector returned is zero. (Could be used +// for faster updates of a graphical board). + + +#ifndef __GAME__H__ +#define __GAME__H__ + + +#include "Score.h" +#include "Move.h" +#include "Position.h" + + +class Game { +public: + Game(); + ~Game(); + + void newGame(); + + Color color(uint x, uint y) const { return m_position.color(x, y); } + uint score(Color color) const { return m_position.score(color); } + Move lastMove() const; + Move move(uint moveNo) const; + + bool moveIsLegal(SimpleMove &move) const; + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool doMove(Move &move); + bool doMove(SimpleMove &move); + bool undoMove(); + + const Position &position() const { return m_position; } + uint moveNumber() const { return m_moveNumber; } + Color toMove() const { return m_position.toMove(); } + + // Reversi specific methods + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +protected: + Move m_moves[60]; + Position m_position; // The current position in the game + uint m_moveNumber; +}; + + +#endif diff --git a/kreversi/Makefile.am b/kreversi/Makefile.am new file mode 100644 index 00000000..ca13e36f --- /dev/null +++ b/kreversi/Makefile.am @@ -0,0 +1,101 @@ +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +bin_PROGRAMS = kreversi + +kreversi_SOURCES = \ + kzoommainwindow.cpp \ + Score.cpp \ + Move.cpp \ + Position.cpp \ + Game.cpp \ + qreversigame.cpp \ + qreversigameview.cpp \ + SuperEngine.cpp \ + Engine.cpp \ + board.cpp \ + settings.ui \ + highscores.cpp \ + kreversi.cpp \ + main.cpp \ + prefs.kcfgc +kreversi_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kreversi_LDADD = $(LIB_KDEGAMES) $(LIB_KIO) +kreversi_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +rcdir = $(kde_datadir)/kreversi +rc_DATA = kreversiui.rc + +noinst_HEADERS = \ + kzoommainwindow.h \ + Engine.h \ + Game.h \ + qreversigame.h \ + qreversigameview.h \ + kreversi.h \ + Move.h \ + board.h \ + Position.h \ + Score.h \ + version.h \ + SuperEngine.h \ + highscores.h + +SUBDIRS = . pics sounds icons + +xdg_apps_DATA = kreversi.desktop +kde_kcfg_DATA = kreversi.kcfg + + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kreversi.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + echo "********************************************************" ;\ + echo "" ;\ + echo "This game is installed sgid \"games\" to use the" ;\ + echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." ;\ + echo "" ;\ + echo "If the system-wide highscore file does not exist, it is" ;\ + echo "created with the correct ownership and permissions. See the" ;\ + echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." ;\ + echo "" ;\ + echo "********************************************************" ;\ + fi + +install-exec-hook: + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + mkdir -p $(DESTHIGHSCORES) && \ + chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + chown $(highscore_user):$(highscore_group) $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test ${setgid} = true; then \ + chmod 2755 $(DESTBIN) \ + || echo "Error: Could not install the game with correct permissions !!" ;\ + fi + + @if test x$(HIGHSCORE_DIRECTORY) != x; then \ + touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" ;\ + fi + diff --git a/kreversi/Move.cpp b/kreversi/Move.cpp new file mode 100644 index 00000000..3a616647 --- /dev/null +++ b/kreversi/Move.cpp @@ -0,0 +1,118 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include "Move.h" + + +SimpleMove::SimpleMove(Color color, int x, int y) +{ + m_color = color; + m_x = x; + m_y = y; +} + + +SimpleMove::SimpleMove(const SimpleMove &move) +{ + *this = move; +} + + +QString SimpleMove::asString() const +{ + if (m_x == -1) + return QString("pass"); + else + return QString("%1%2").arg(" ABCDEFGH"[m_x]).arg(" 12345678"[m_y]); +} + + +// ================================================================ + + +Move::Move() + : SimpleMove() +{ + m_turnedPieces.clear(); +} + + +Move::Move(Color color, int x, int y) + : SimpleMove(color, x, y) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const Move &move) + : SimpleMove((SimpleMove&) move) +{ + m_turnedPieces.clear(); +} + + +Move::Move(const SimpleMove &move) + : SimpleMove(move) +{ + m_turnedPieces.clear(); +} + + +// ---------------------------------------------------------------- + + +bool Move::squareModified(uint x, uint y) const +{ + return (m_x == (int) x && m_y == (int) y) || wasTurned(x, y); +} + + +bool Move::wasTurned(uint x, uint y) const +{ + // findIndex returns the first index where the item is found, or -1 + // if not found. + return (m_turnedPieces.findIndex(10 * x + y) != -1); +} diff --git a/kreversi/Move.h b/kreversi/Move.h new file mode 100644 index 00000000..e205f279 --- /dev/null +++ b/kreversi/Move.h @@ -0,0 +1,124 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// This file defines the two classes SimpleMove and Move. +// +// The class Move is used to represent an Othello move with a player value +// (see class Score) and a pair of coordinates on an 8x8 Othello board. +// Each coordinate can have values between 1 and 8, inclusive. +// +// The difference between a Move and a SimpleMove is that a SimpleMove +// can be done (performed) in a Position, but a Move can be both done +// and undone. In addition to the info in SimpleMove, the class Move +// stores information that is used in undoing the move and visualizing +// it. +// +// The reason for the class SimpleMove is that it saves memory. The +// class Game stores an array of Moves, since the BoardView needs +// information about which pieces were turned by the move. +// + + +#ifndef __MOVE__H__ +#define __MOVE__H__ + + +#include "qvaluelist.h" +#include "qstring.h" + +#include "Score.h" + + +class Position; + + +class SimpleMove +{ +public: + SimpleMove() { m_color = Nobody; m_x = -1; m_y = -1; } + SimpleMove(Color color, int x, int y); + SimpleMove(const SimpleMove &move); + + //Move &operator=(Move &move); + + Color color() const { return m_color; } + int x() const { return m_x; } + int y() const { return m_y; } + + QString asString() const; + +protected: + Color m_color; + int m_x; + int m_y; +}; + + +// Note: This class is not memory optimized. The list of turned +// pieces can surely be made much smaller. + +class Move : public SimpleMove +{ + friend class Position; + +public: + Move(); + Move(Color color, int x, int y); + Move(const Move &move); + Move(const SimpleMove &move); + + bool squareModified(uint x, uint y) const; + bool wasTurned(uint x, uint y) const; + +private: + QValueList m_turnedPieces; +}; + + +typedef QValueList MoveList; + + +#endif diff --git a/kreversi/NEWS b/kreversi/NEWS new file mode 100644 index 00000000..8e815ffa --- /dev/null +++ b/kreversi/NEWS @@ -0,0 +1,20 @@ +v0.1pl168: Initial release + +06/14/97 released version 0.1.2 + +06/16/97 released version 0.2 + +06/25/97 released version 0.2.1 + +07/30/97 released version 0.3 + +???????? released version 0.4 (unware about date) + +07/10/97 released version 0.5 + +10/09/97 released 0.6 + +10/12/97 released 0.6.1 (bugfix release) + +see ChangeLog for details + diff --git a/kreversi/Position.cpp b/kreversi/Position.cpp new file mode 100644 index 00000000..22ffb3cf --- /dev/null +++ b/kreversi/Position.cpp @@ -0,0 +1,366 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + + +#include "kdebug.h" + +#include "Position.h" +#include + + +Position::Position() +{ + setupStartPosition(); +} + + +Position::Position(Position &pos, SimpleMove &move) +{ + constrCopy(pos, move); +} + + +Position &Position::operator=(Position &pos) +{ + // Copy the position itself. + for (uint row = 0; row < 10; row++) + for (uint col = 0; col < 10; col++) + m_board[row][col] = pos.m_board[row][col]; + m_toMove = pos.m_toMove; + + m_score = pos.m_score; + + return *this; +} + + +// ---------------------------------------------------------------- +// Helpers to the constructors + + +// Construct a Position by copying another one and then make a move. +// + +void Position::constrCopy(Position &pos, SimpleMove &move) +{ + *this = pos; + doMove(move); +} + + +// Setup the Position by setting it to the initial Reversi position. + +void Position::setupStartPosition() +{ + // Initialize the real board + for (uint i = 0; i < 10; i++) + for (uint j = 0; j < 10; j++) + m_board[i][j] = Nobody; + + // The initial position + m_board[4][4] = White; + m_board[5][5] = White; + m_board[5][4] = Black; + m_board[4][5] = Black; + + // Black always starts the game in Reversi. + m_toMove = Black; + + // Each side starts out with two pieces. + m_score.set(White, 2); + m_score.set(Black, 2); +} + + +// ---------------------------------------------------------------- +// Access methods + + +Color Position::color(uint x, uint y) const +{ + if (x < 1 || x > 8 || y < 1 || y > 8) + return Nobody; + + return m_board[x][y]; +} + + +// ---------------------------------------------------------------- +// Moves in the position + + +// Return true if the move is legal. +// +// NOTE: This function does *NOT* test wether the move is done +// by the color to move. That must be checked separately. +// + +bool Position::moveIsLegal(SimpleMove &move) const +{ + if (m_board[move.x()][move.y()] != Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Check in all directions and see if there is a turnable row of + // opponent pieces. If there is at least one such row, then the + // move is legal. + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + if (xinc == 0 && yinc == 0) + continue; + + // Find the end of such a row of pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row ends with a piece of our own and there was at + // least one opponent piece between it and the move point, then + // we have found a turnable row. + if (m_board[x][y] == color + && (x - xinc != move.x() || y - yinc != move.y())) + return true; + } + } + + return false; +} + + +// See if 'color' can make a move in the current position. This is +// independent of wether it is 'color's turn to move or not. + +bool Position::moveIsPossible(Color color) const +{ + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) + for (uint j = 1; j < 9; j++) { + SimpleMove move(color, i, j); + + if (moveIsLegal(move)) + return true; + } + + return false; +} + + +// Return true if any side can move. (If not, the game is over.) + +bool Position::moveIsAtAllPossible() const +{ + return (moveIsPossible(White) || moveIsPossible(Black)); +} + + +// Make a move in the position. +// +// Return true if the move was legal, otherwise return false. +// +bool Position::doMove(SimpleMove &move, QValueList *turned) +{ + if (move.color() == Nobody) + return false; + + Color color = move.color(); + Color opponent = ::opponent(color); + + // Put the piece on the board + m_board[move.x()][move.y()] = color; + m_score.inc(color); + + // Turn pieces. + uint scoreBefore = m_score.score(color); + for (int xinc = -1; xinc <= 1; xinc++) { + for (int yinc = -1; yinc <= 1; yinc++) { + int x, y; + + // Skip the case where both xinc and yinc == 0, since then we + // won't move in any direction at all. + if (xinc == 0 && yinc == 0) + continue; + + // Find the end point (x, y) of a possible row of turnable pieces. + for (x = move.x()+xinc, y = move.y()+yinc; m_board[x][y] == opponent; + x += xinc, y += yinc) + ; + + // If the row was indeed turnable, then do it. + if (m_board[x][y] == color) { + for (x -= xinc, y -= yinc; x != move.x() || y != move.y(); + x -= xinc, y -= yinc) { + // Turn the piece. + m_board[x][y] = color; + if (turned) + turned->append(10 * x + y); + + // Make the piece count correct again. + m_score.inc(color); + m_score.dec(opponent); + } + } + } + } + + // If nothing was turned, the move wasn't legal. + if (m_score.score(color) == scoreBefore) { + m_board[move.x()][move.y()] = Nobody; + m_score.dec(color); + + return false; + } + + // Set the next color to move. + if (moveIsPossible(opponent)) + m_toMove = opponent; + else if (moveIsPossible(color)) + m_toMove = color; + else + m_toMove = Nobody; + + return true; +} + + +bool Position::doMove(Move &move) +{ + move.m_turnedPieces.clear(); + return doMove((SimpleMove &) move, &move.m_turnedPieces); +} + + +bool Position::undoMove(Move &move) +{ + Color color = move.color(); + Color other = opponent(color); + + // Sanity checks + // 1. The move must be on the board and be of the right color. + if (color != m_board[move.x()][move.y()]) { + //kdDebug() << "move on the board is wrong color: " << (int) color << "[" + // << move.x() << "," << move.y() << "]" << endl; + return false; + } + + // 2. All turned pieces must be on the board anb be of the right color. + QValueList::iterator it; + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + if (m_board[sq / 10][sq % 10] != color) { + //kdDebug() << "turned piece the board is wrong color: [" + // << sq / 10 << "," << sq % 10 << "]" << endl; + return false; + } + } + + // Ok, everything seems allright. Let's do it! + // 1. Unturn all the turned pieces. + for (it = move.m_turnedPieces.begin(); + it != move.m_turnedPieces.end(); + ++it) { + int sq = *it; + + m_board[sq / 10][sq % 10] = other; + m_score.dec(color); + m_score.inc(other); + } + + // 2. Remove the move itself. + m_score.dec(color); + m_board[move.x()][move.y()] = Nobody; + + + return true; +} + + +MoveList Position::generateMoves(Color color) const +{ + MoveList moves; + + // Make it simple: Step through all squares and see if it is a legal move. + for (uint i = 1; i < 9; i++) { + for (uint j = 1; j < 9; j++) { + Move move(color, i, j); + + if (moveIsLegal(move)) + moves.append(move); + } + } + + return moves; +} + + +QString Position::asString() const +{ + QString result; + + for (uint y = 1; y < 9; ++y) { + for (uint x = 1; x < 9; ++x) { + switch (m_board[x][y]) { + case Nobody: result.append(' '); break; + case Black: result.append('*'); break; + case White: result.append('o'); break; + default: result.append('?'); break; + } + } + + result.append('\n'); + } + + return result; +} diff --git a/kreversi/Position.h b/kreversi/Position.h new file mode 100644 index 00000000..7269c2e6 --- /dev/null +++ b/kreversi/Position.h @@ -0,0 +1,98 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class Position is used to represent an Othello position as white and +// black pieces and empty squares (see class Score) on an 8x8 Othello board. + +#ifndef __POSITION__H__ +#define __POSITION__H__ + + +#include "Move.h" +#include "Score.h" + + +class Position +{ +public: + Position(); + Position(Position &pos, SimpleMove &move); + Position(Position &pos, Move &move); + + Position &operator=(Position &pos); + + void constrCopy(Position &pos, SimpleMove &move); + + void setupStartPosition(); + + // Access methods + Color toMove() const { return m_toMove; } + Color color(uint x, uint y) const; + uint score(Color color) const { return m_score.score(color); } + + // Moves in the current position. + bool moveIsPossible(Color color) const; + bool moveIsAtAllPossible() const; + bool moveIsLegal(SimpleMove &move) const; + bool doMove(SimpleMove &move, QValueList *turned = 0); + bool doMove(Move &move); + bool undoMove(Move &move); + + MoveList generateMoves(Color color) const; + + QString asString() const; + +private: + // The actual position itself. Use the simplest representation possible. + Color m_board[10][10]; + Color m_toMove; + + // Some extra data + Score m_score; // The number of pieces for each side. +}; + + +#endif diff --git a/kreversi/Score.cpp b/kreversi/Score.cpp new file mode 100644 index 00000000..f508152b --- /dev/null +++ b/kreversi/Score.cpp @@ -0,0 +1,70 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "Score.h" + + +Score::Score() +{ + m_score[White] = 0; + m_score[Black] = 0; +} + + +/* Return the opponent color for 'color'. + */ + +Color opponent(Color color) +{ + switch (color) { + case White: return Black; + case Black: return White; + case Nobody: break; + } + + return Nobody; +} diff --git a/kreversi/Score.h b/kreversi/Score.h new file mode 100644 index 00000000..947272dc --- /dev/null +++ b/kreversi/Score.h @@ -0,0 +1,78 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __SCORE__H__ +#define __SCORE__H__ + +#include + +enum Color { White = 0, Black = 1, NbColors = 2, Nobody = NbColors }; + +Color opponent(Color color); + + +/* This class keeps track of the score for both colors. Such a score + * could be either the number of pieces, the score from the evaluation + * function or anything similar. + */ + +class Score { +public: + Score(); + + uint score(Color color) const { return m_score[color]; } + + void set(Color color, uint score) { m_score[color] = score; } + void inc(Color color) { m_score[color]++; } + void dec(Color color) { m_score[color]--; } + void add(Color color, uint s) { m_score[color] += s; } + void sub(Color color, uint s) { m_score[color] -= s; } + +private: + uint m_score[NbColors]; +}; + +#endif diff --git a/kreversi/SuperEngine.cpp b/kreversi/SuperEngine.cpp new file mode 100644 index 00000000..3da7326f --- /dev/null +++ b/kreversi/SuperEngine.cpp @@ -0,0 +1,133 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + + +#include "SuperEngine.h" + + +SuperEngine::SuperEngine(int st) +{ + m_strength = st; + m_random.setSeed(0); + m_interrupt = false; +} + + +SuperEngine::SuperEngine(int st, int sd) +{ + m_strength = st; + m_random.setSeed(sd); + m_interrupt = false; +} + + +void SuperEngine::setSeed(int sd) +{ + m_random.setSeed(sd); +} + diff --git a/kreversi/SuperEngine.h b/kreversi/SuperEngine.h new file mode 100644 index 00000000..d3faf6a9 --- /dev/null +++ b/kreversi/SuperEngine.h @@ -0,0 +1,142 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * Created 1997 by Mario Weilguni . This file + * is ported from Mats Luthman's JAVA applet. + * Many thanks to Mr. Luthman who has allowed me to put this port + * under the GNU GPL. Without his wonderful game engine kreversi + * would be just another of those Reversi programs a five year old + * child could beat easily. But with it it's a worthy opponent! + * + * If you are interested on the JAVA applet of Mr. Luthman take a + * look at http://www.sylog.se/~mats/ + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +// The class SuperEngine is a super class for engines that produce moves. +// It implements functionality that move engines have in common, which is +// useful if you want to use several different engines in the same program +// (for instance when you are test playing different strategies against each +// other). +// +// SuperEngine implements: +// +// Random number handling (engines should not play exactly the same games +// repeatedly). +// +// Setting playing strength level. +// +// Functionality for telling the engine to interrupt calculation. +// + +// Public and protected functions: + +// public SuperEngine(int st) +// Creates an engine playing at level st. All integers greater than 0 +// should be possible levels. There need not be any actual difference in +// playing strength between levels, but if there is, higher number should +// mean greater playing strength. + +// public SuperEngine(int st, int sd) +// The same as above, but uses sd as the seed for the random generator. +// This means that the engine always behaves in exactly the same way +// (practical when testing). + +// public synchronized final void SetInterrupt(boolean intr) +// This function could be called when ComputeMove() (see below) is +// executing. It tells the engine to interrupt calculation as +// soon as possible and return null from ComputeMove(). + +// protected synchronized final boolean GetInterrupt() +// Returns true when SetInterrupt() has been called. Should be called +// with short intervals from ComputeMove(). + +// public void SetStrength(int st) +// Sets playing strength level. + +// public int GetStrength() +// Gets playing strength level. + +// public void SetSeed(int sd) +// Changes the random seed. + +// public abstract Move ComputeMove(Game g) +// This function should produce a move. If SetInterrupt() is called +// during its execution it should return null as soon as possible. + +// Protected members: + +// protected int m_strength +// Is set and read by SetStrength() and GetStrength(). + +// protected Random m_random +// Could (and should in a good engine) be used to prevent the engine from +// repeating itself, always playing the same moves in the same positions. +// If this is not done, winning once would make it possible to play the +// same moves and win every time against the program. + +#ifndef __SUPERENGINE__H__ +#define __SUPERENGINE__H__ + +#include "Game.h" + +#include + +class SuperEngine { +public: + SuperEngine(int st); + SuperEngine(int st, int sd); + virtual ~SuperEngine() {} + + void setInterrupt(bool intr) { m_interrupt = intr; } + bool interrupted() const { return m_interrupt; } + + enum Strength { MinStrength = 1, MaxStrength = 7, + NbStrengths = MaxStrength }; + void setStrength(uint st) { m_strength = st; } + uint strength() const { return m_strength; } + + void setSeed(int sd); + + virtual Move computeMove(Game *game, bool competitive) = 0; + +protected: + uint m_strength; + KRandomSequence m_random; + +private: + bool m_interrupt; +}; + +#endif diff --git a/kreversi/TODO b/kreversi/TODO new file mode 100644 index 00000000..4e81f5d1 --- /dev/null +++ b/kreversi/TODO @@ -0,0 +1,87 @@ +TODO-list for KREVERSI +====================== + +Next +---- + +* + + +================================================================ + + +* Implement the plans in DESIGN + + Implement the QReversiGameView class DONE + - Move board view to it done + - Move movelist to it done + - Move status widgets to it done + + Move all showing of legal moves into the BoardView class DONE + + Implement the QEngineView class ---- + +* More cleaning / refactoring + + class KReversi is still a bit of a mess. Separate it more DONE + +* Enhancements to the view + + Letters A-H and figures 1-8 on the board view. DONE + + + Show possible moves in the current position DONE + - Actually show them on the board done + - Create a toggle action to toggle it on/off done + - Make an icon for the toggle action -- + - Bug: legal moves don't get updated if one side has to pass done + - Bug: legal moves don't work together with hint. done + + Show moves made during the game DONE + + + Navigate in the list of moves ---- + + + Wish 102813: Should be able to show last move DONE + - Make an icon for the toggle action -- + - Bug: When turned on, should show last move immediately done + - Bug: When turned off, should unshow last move immediately done + + + Save settings of toggleactions in config file. ---- + +* Convert KReversi to use KGame / KPlayer + I. Convert KReversi to a proper Model/View program. + 1. Fix a ReversiGame (formerly known as Game) DONE + - Clean it up. (Only store the moves). + - Add a few necessary methods. + 2. Move all the slots for KActions to kreversi.cpp DONE + 3. Move the ownership of the engine and the game to kreversi. DONE + 4. Create a new class QReversiGame, that inherits ReversiGame DONE + and sends a lot of signals. + - Split out a lot of methods from the current class Board. done + 5. Create QReversiBoardView from the rest of the current Board DONE + - Clean it done + + II. Introduce a class ReversiPlayer + + III. Convert everything to KGame + 1. Let KReversiGame inherit from KGame + 2. Let ReversiPlayer inherit from KPlayer. + + IV. ... + + V. Profit! + + + + +Old TODO items, partially done/not done +======================================= + +* undo/redo + undo works, but I'll probably do not make a redo function + +* Sound support: + I'm not happy with + the sound files I have so if + you have better sounds, mail them to me + (uuencoded). I need sounds for the following actions: + - game won + - game lost + - game drawn + - turning a piece + - putting a piece + - something for the hall of fame (trumpets???) + diff --git a/kreversi/board.cpp b/kreversi/board.cpp new file mode 100644 index 00000000..9d367a38 --- /dev/null +++ b/kreversi/board.cpp @@ -0,0 +1,576 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "prefs.h" +#include "Engine.h" +#include "qreversigame.h" + + +#ifndef PICDATA +#define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x) +#endif + +const uint HINT_BLINKRATE = 250000; +const uint ANIMATION_DELAY = 3000; +const uint CHIP_OFFSET[NbColors] = { 24, 1 }; +const uint CHIP_SIZE = 36; + +#define OFFSET() (zoomedSize() * 3 / 4) + + +// ================================================================ +// class KReversiBoardView + + +QReversiBoardView::QReversiBoardView(QWidget *parent, QReversiGame *krgame) + : QWidget(parent, "board"), + chiptype(Unloaded) +{ + m_krgame = krgame; + + m_marksShowing = true; + m_legalMovesShowing = false; + m_legalMoves.clear(); + + m_showLastMove = false; + m_lastMoveShown = SimpleMove(); +} + + +QReversiBoardView::~QReversiBoardView() +{ +} + + +// ---------------------------------------------------------------- + + +// Start it all up. +// + +void QReversiBoardView::start() +{ + updateBoard(true); + adjustSize(); +} + + +void QReversiBoardView::loadChips(ChipType type) +{ + QString name("pics/"); + name += (type==Colored ? "chips.png" : "chips_mono.png"); + + QString s = KGlobal::dirs()->findResource("appdata", name); + bool ok = allchips.load(s); + + Q_ASSERT( ok && allchips.width()==CHIP_SIZE*5 + && allchips.height()==CHIP_SIZE*5 ); + chiptype = type; + update(); +} + + +// Negative speed is allowed. If speed is negative, +// no animations are displayed. +// + +void QReversiBoardView::setAnimationSpeed(uint speed) +{ + if (speed <= 10) + anim_speed = speed; +} + + +// Handle mouse clicks. +// + +void QReversiBoardView::mousePressEvent(QMouseEvent *e) +{ + // Only handle left button. No context menu. + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + + int offset = m_marksShowing ? OFFSET() : 0; + int px = e->pos().x()- 1 - offset; + int py = e->pos().y()- 1 - offset; + + if (px < 0 || px >= 8 * (int) zoomedSize() + || py < 0 || py >= 8 * (int) zoomedSize()) { + e->ignore(); + return; + } + + emit signalSquareClicked(py / zoomedSize(), px / zoomedSize()); +} + + +void QReversiBoardView::showHint(Move move) +{ + // Only show a hint if there is a move to show. + if (move.x() == -1) + return; + + // Blink with a piece at the location where the hint move points. + // + // The isVisible condition has been added so that when the player + // was viewing a hint and quits the game window, the game doesn't + // still have to do all this looping and directly ends. + QPainter p(this); + p.setPen(black); + m_hintShowing = true; + for (int flash = 0; + flash < 100 && m_hintShowing && isVisible(); + flash++) + { + if (flash & 1) { + // FIXME: Draw small circle if showLegalMoves is turned on. + drawPiece(move.y() - 1, move.x() - 1, Nobody); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); + } + else + drawPiece(move.y() - 1, move.x() - 1, m_krgame->toMove()); + + // keep GUI alive while waiting + for (int dummy = 0; dummy < 5; dummy++) { + usleep(HINT_BLINKRATE / 5); + qApp->processEvents(); + } + } + m_hintShowing = false; + + // Draw the empty square again. + drawPiece(move.y() - 1, move.x() - 1, m_krgame->color(move.x(), move.y())); + if (m_legalMovesShowing) + drawSmallCircle(move.x(), move.y(), p); +} + + +// Set the member m_hintShowing to false. This will make showHint() +// end, if it is running. +// + +void QReversiBoardView::quitHint() +{ + m_hintShowing = false; +} + + +void QReversiBoardView::setShowLegalMoves(bool show) +{ + m_legalMovesShowing = show; + updateBoard(true); +} + +void QReversiBoardView::setShowMarks(bool show) +{ + m_marksShowing = show; + updateBoard(true); +} + + +void QReversiBoardView::setShowLastMove(bool show) +{ + m_showLastMove = show; + updateBoard(true); +} + + +// ================================================================ +// Functions related to drawing/painting + + +// Flash all pieces which are turned. +// +// NOTE: This code is quite a hack. Should make it better. +// + +void QReversiBoardView::animateChanged(Move move) +{ + if (anim_speed == 0) + return; + + // Draw the new piece. + drawPiece(move.y() - 1, move.x() - 1, move.color()); + + // Animate row by row in all directions. + for (int dx = -1; dx < 2; dx++) + for (int dy = -1; dy < 2; dy++) + if ((dx != 0) || (dy != 0)) + animateChangedRow(move.y() - 1, move.x() - 1, dy, dx); +} + + +bool QReversiBoardView::isField(int row, int col) const +{ + return ((0 <= row) && (row < 8) && (0 <= col) && (col < 8)); +} + + +void QReversiBoardView::animateChangedRow(int row, int col, int dy, int dx) +{ + row = row + dy; + col = col + dx; + while (isField(row, col)) { + if (m_krgame->wasTurned(col+1, row+1)) { + KNotifyClient::event(winId(), "click", i18n("Click")); + rotateChip(row, col); + } else + return; + + col += dx; + row += dy; + } +} + + +void QReversiBoardView::rotateChip(uint row, uint col) +{ + // Check which direction the chip has to be rotated. If the new + // chip is white, the chip was black first, so lets begin at index + // 1, otherwise it was white. + Color color = m_krgame->color(col+1, row+1); + uint from = CHIP_OFFSET[opponent(color)]; + uint end = CHIP_OFFSET[color]; + int delta = (color==White ? 1 : -1); + + from += delta; + end -= delta; + + for (uint i = from; i != end; i += delta) { + drawOnePiece(row, col, i); + kapp->flushX(); // FIXME: use QCanvas to avoid flicker... + usleep(ANIMATION_DELAY * anim_speed); + } +} + + +// Redraw the board. If 'force' is true, redraw everything, otherwise +// only redraw those squares that have changed (marked by +// m_krgame->squareModified(col, row)). +// + +void QReversiBoardView::updateBoard (bool force) +{ + QPainter p(this); + p.setPen(black); + + // If we are showing legal moves, we have to erase the old ones + // before we can show the new ones. The easiest way to do that is + // to repaint everything. + // + // FIXME: A better way, perhaps, is to do the repainting in + // drawPiece (which should be renamed drawSquare). + if (m_legalMovesShowing) + force = true; + + // Draw the squares of the board. + for (uint row = 0; row < 8; row++) + for (uint col = 0; col < 8; col++) + if ( force || m_krgame->squareModified(col + 1, row + 1) ) { + Color color = m_krgame->color(col + 1, row + 1); + drawPiece(row, col, color); + } + + // Draw a border around the board. + int offset = m_marksShowing ? OFFSET() : 0; + p.drawRect(0 + offset, 0 + offset, + 8 * zoomedSize() + 2, 8 * zoomedSize() + 2); + + // Draw letters and numbers if appropriate. + if (m_marksShowing) { + QFont font("Sans Serif", zoomedSize() / 2 - 6); + font.setWeight(QFont::DemiBold); + QFontMetrics metrics(font); + + p.setFont(font); + uint charHeight = metrics.ascent(); + for (uint i = 0; i < 8; i++) { + QChar letter = "ABCDEFGH"[i]; + QChar number = "12345678"[i]; + uint charWidth = metrics.charWidth("ABCDEFGH", i); + + // The horizontal letters + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset - charHeight / 2 + 2, + QString(letter)); + p.drawText(offset + i * zoomedSize() + (zoomedSize() - charWidth) / 2, + offset + 8 * zoomedSize() + offset - charHeight / 2 + 2, + QString(letter)); + + // The vertical numbers + p.drawText((offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + p.drawText(offset + 8 * zoomedSize() + (offset - charWidth) / 2 + 2, + offset + (i + 1) * zoomedSize() - charHeight / 2 + 2, + QString(number)); + } + } + + // Show legal moves. + if (m_legalMovesShowing) + showLegalMoves(); + + // Show last move + int ellipseSize = zoomedSize() / 3; + SimpleMove lastMove = m_krgame->lastMove(); + if (m_showLastMove && lastMove.x() != -1) { + // Remove the last shown last move. + int col = m_lastMoveShown.x(); + int row = m_lastMoveShown.y(); + if (col != -1 && row != -1) { + if (lastMove.x() != col || lastMove.y() != row) { + //kdDebug() << "Redrawing piece at [" << col << "," << row + // << "] with color " << m_krgame->color(col, row) + // << endl; + drawPiece(row - 1, col - 1, m_krgame->color(col, row)); + } + } + + p.setPen(yellow); + p.setBackgroundColor(yellow); + p.setBrush(SolidPattern); + + //kdDebug() << "Marking last move at [" + // << lastMove.x() << "," << lastMove.y() << "]" + // << endl; + int px = offset + (lastMove.x() - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (lastMove.y() - 1) * zoomedSize() + zoomedSize() / 2; + p.drawEllipse(px - ellipseSize / 2 + 1, py - ellipseSize / 2 + 1, + ellipseSize - 1, ellipseSize - 1); + + m_lastMoveShown = lastMove; + + p.setPen(black); + p.setBackgroundColor(black); + p.setBrush(NoBrush); + } +} + + +// Show legal moves on the board. + +void QReversiBoardView::showLegalMoves() +{ + QPainter p(this); + p.setPen(black); + + // Get the legal moves in the current position. + Color toMove = m_krgame->toMove(); + MoveList moves = m_krgame->position().generateMoves(toMove); + + // Show the moves on the board. + MoveList::iterator it; + for (it = moves.begin(); it != moves.end(); ++it) + drawSmallCircle((*it).x(), (*it).y(), p); +} + + +void QReversiBoardView::drawSmallCircle(int x, int y, QPainter &p) +{ + int offset = m_marksShowing ? OFFSET() : 0; + int ellipseSize = zoomedSize() / 3; + + int px = offset + (x - 1) * zoomedSize() + zoomedSize() / 2; + int py = offset + (y - 1) * zoomedSize() + zoomedSize() / 2; + + p.drawEllipse(px - ellipseSize / 2, py - ellipseSize / 2, + ellipseSize, ellipseSize); +} + + + +QPixmap QReversiBoardView::chipPixmap(Color color, uint size) const +{ + return chipPixmap(CHIP_OFFSET[color], size); +} + + +// Get a pixmap for the chip 'i' at size 'size'. +// + +QPixmap QReversiBoardView::chipPixmap(uint i, uint size) const +{ + // Get the part of the 'allchips' pixmap that contains exactly that + // chip that we want to use. + QPixmap pix(CHIP_SIZE, CHIP_SIZE); + copyBlt(&pix, 0, 0, &allchips, (i%5) * CHIP_SIZE, (i/5) * CHIP_SIZE, + CHIP_SIZE, CHIP_SIZE); + + // Resize (scale) the pixmap to the desired size. + QWMatrix wm3; + wm3.scale(float(size)/CHIP_SIZE, float(size)/CHIP_SIZE); + + return pix.xForm(wm3); +} + + +uint QReversiBoardView::zoomedSize() const +{ + return qRound(float(CHIP_SIZE) * Prefs::zoom() / 100); +} + + +void QReversiBoardView::drawPiece(uint row, uint col, Color color) +{ + int i = (color == Nobody ? -1 : int(CHIP_OFFSET[color])); + drawOnePiece(row, col, i); +} + + +void QReversiBoardView::drawOnePiece(uint row, uint col, int i) +{ + int px = col * zoomedSize() + 1; + int py = row * zoomedSize() + 1; + QPainter p(this); + + // Draw either a background pixmap or a background color to the square. + int offset = m_marksShowing ? OFFSET() : 0; + if (bg.width()) + p.drawTiledPixmap(px + offset, py + offset, + zoomedSize(), zoomedSize(), bg, px, py); + else + p.fillRect(px + offset, py + offset, + zoomedSize(), zoomedSize(), bgColor); + + // Draw a black border around the square. + p.setPen(black); + p.drawRect(px + offset, py + offset, zoomedSize(), zoomedSize()); + + // If no piece on the square, i.e. only the background, then return here... + if ( i == -1 ) + return; + + // ...otherwise finally draw the piece on the square. + p.drawPixmap(px + offset, py + offset, chipPixmap(i, zoomedSize())); +} + + +// We got a repaint event. We make it easy for us and redraw the +// entire board. +// + +void QReversiBoardView::paintEvent(QPaintEvent *) +{ + updateBoard(true); +} + + +void QReversiBoardView::adjustSize() +{ + int w = 8 * zoomedSize(); + + if (m_marksShowing) + w += 2 * OFFSET(); + + setFixedSize(w + 2, w + 2); +} + + +void QReversiBoardView::setPixmap(QPixmap &pm) +{ + if ( pm.width() == 0 ) + return; + + bg = pm; + update(); + setErasePixmap(pm); +} + + +void QReversiBoardView::setColor(const QColor &c) +{ + bgColor = c; + bg = QPixmap(); + update(); + setEraseColor(c); +} + + +// Load all settings that have to do with the board view, such as +// piece colors, background, animation speed, an so on. + +void QReversiBoardView::loadSettings() +{ + // Colors of the pieces (red/blue or black/white) + if ( Prefs::grayscale() ) { + if (chiptype != Grayscale) + loadChips(Grayscale); + } + else { + if (chiptype != Colored) + loadChips(Colored); + } + + // Animation speed. + if ( !Prefs::animation() ) + setAnimationSpeed(0); + else + setAnimationSpeed(10 - Prefs::animationSpeed()); + + // Background + if ( Prefs::backgroundImageChoice() ) { + QPixmap pm( Prefs::backgroundImage() ); + if (!pm.isNull()) + setPixmap(pm); + } else { + setColor( Prefs::backgroundColor() ); + } +} + + +#include "board.moc" + diff --git a/kreversi/board.h b/kreversi/board.h new file mode 100644 index 00000000..4f9d1603 --- /dev/null +++ b/kreversi/board.h @@ -0,0 +1,150 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __BOARD__H__ +#define __BOARD__H__ + +#include +#include + +#include "Position.h" +//#include "Game.h" +#include "Move.h" + + +class KConfig; + +class QReversiGame; + +// The class Board is the visible Reversi Board widget. +// + +class QReversiBoardView : public QWidget { + Q_OBJECT + +public: + + QReversiBoardView(QWidget *parent, QReversiGame *game); + ~QReversiBoardView(); + + // starts all: emits some signal, so it can't be called from + // constructor + void start(); + + // Used by the outer KZoomMainWindow class. + void adjustSize(); + + // Show a hint to the user. + void showHint(Move move); + void quitHint(); + + // Turn on or off some special features. + void setShowLegalMoves(bool show); + void setShowMarks(bool show); + void setShowLastMove(bool show); + + // View methods called from the outside. + void updateBoard(bool force = FALSE); + void animateChanged(Move move); + void setAnimationSpeed(uint); + + void loadSettings(); + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const; + + +signals: + void signalSquareClicked(int, int); + + +protected: + + // event stuff + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + + +private: + uint zoomedSize() const; + void drawPiece(uint row, uint col, Color); + void drawOnePiece(uint row, uint col, int i); + void animateChangedRow(int row, int col, int dy, int dx); + void rotateChip(uint row, uint col); + bool isField(int row, int col) const; + + void setColor(const QColor &); + QColor color() const { return bgColor; } + void setPixmap(QPixmap &); + + // Methods for handling images of pieces. + enum ChipType { Unloaded, Colored, Grayscale }; + void loadChips(ChipType); + ChipType chipType() const { return chiptype; } + QPixmap chipPixmap(uint i, uint size) const; + + // Private drawing methods. + void showLegalMoves(); + void drawSmallCircle(int x, int y, QPainter &p); + + +private: + QReversiGame *m_krgame; // Pointer to the game object (not owner). + + // The background of the board - a color and a pixmap. + QColor bgColor; + QPixmap bg; + + // the pieces + ChipType chiptype; + QPixmap allchips; + uint anim_speed; + + // Special stuff used only in smaller areas. + bool m_hintShowing; + MoveList m_legalMoves; + bool m_legalMovesShowing; + bool m_marksShowing; + + bool m_showLastMove; + SimpleMove m_lastMoveShown; +}; + + +#endif + diff --git a/kreversi/highscores.cpp b/kreversi/highscores.cpp new file mode 100644 index 00000000..88317a87 --- /dev/null +++ b/kreversi/highscores.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "highscores.h" + +#include +#include +#include + + +namespace KExtHighscore +{ + +const ExtManager::Data ExtManager::DATA[SuperEngine::NbStrengths] = { + { I18N_NOOP("1 (Beginner)"), "beginner" }, + { I18N_NOOP("2"), 0 }, + { I18N_NOOP("3"), 0 }, + { I18N_NOOP("4 (Average)"), "average" }, + { I18N_NOOP("5"), 0 }, + { I18N_NOOP("6"), 0 }, + { I18N_NOOP("7 (Expert)"), "expert" } +}; + + +ExtManager::ExtManager() + : Manager(SuperEngine::NbStrengths) +{ + setShowStatistics(true); + setShowDrawGamesStatistic(true); + + const uint RANGE[6] = { 0, 32, 40, 48, 56, 64 }; + QMemArray s; + s.duplicate(RANGE, 6); + setScoreHistogram(s, ScoreBound); +} + + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Data &data = DATA[gameType]; + switch (type) { + case Icon: return data.icon; + case Standard: return QString::number(gameType); + case I18N: return i18n(data.label); + case WW: break; + } + + return QString::null; +} + + +void ExtManager::convertLegacy(uint gameType) +{ + // Since there is no information about the skill level + // in the legacy highscore list, consider they are + // for beginner skill ... + qDebug("convert legacy %i", gameType); + + if ( gameType!=0 ) + return; + + KConfigGroupSaver cg(kapp->config(), "High Score"); + + for (uint i = 1; i <= 10; i++) { + QString key = "Pos" + QString::number(i); + QString name = cg.config()->readEntry(key + "Name", QString::null); + + if ( name.isEmpty() ) + name = i18n("anonymous"); + + uint score = cg.config()->readUnsignedNumEntry(key + "NumChips", 0); + if ( score==0 ) + continue; + + QString sdate = cg.config()->readEntry(key + "Date", QString::null); + QDateTime date = QDateTime::fromString(sdate); + Score s(Won); + + s.setScore(score); + s.setData("name", name); + if ( date.isValid() ) + s.setData("date", date); + submitLegacyScore(s); + } +} + + +} // Namespace diff --git a/kreversi/highscores.h b/kreversi/highscores.h new file mode 100644 index 00000000..a599d12d --- /dev/null +++ b/kreversi/highscores.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef HIGHSCORES_H +#define HIGHSCORES_H + + +#include +#include +#include "SuperEngine.h" + + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + + struct Data { + const char *label; + const char *icon; + }; + static const Data DATA[SuperEngine::NbStrengths]; +}; + +} + +#endif diff --git a/kreversi/icons/Makefile.am b/kreversi/icons/Makefile.am new file mode 100644 index 00000000..da1319dc --- /dev/null +++ b/kreversi/icons/Makefile.am @@ -0,0 +1 @@ +KDE_ICON = AUTO \ No newline at end of file diff --git a/kreversi/icons/cr16-action-lastmoves.png b/kreversi/icons/cr16-action-lastmoves.png new file mode 100644 index 0000000000000000000000000000000000000000..5543a10499b1e50b7244dd654dd3b4e27b16e26c GIT binary patch literal 593 zcmV-X05 zkuhjfQ51&%|Ga0D4r&lx9TaClDMD3*8VUuSDlyh-K#Ela3xZQA#bO3GU93n%611&a zgo+@BxTzp6iga*wl_FFcEAPGYJ0yjWrV#(-o_p@S-}&!3lmq~HGXCrsjkl91IeoEo z?l-zr2mtffUYrQF*&7JG2!nk9zIF@X!u7=g0k0vYFxoOp`PkK#~^NZVT8?vj^!NsZZrgbD6CHz%2l4G&)JA0U$wRLV`3q7=%_U<;&~{ zID2<^*oJWniI8T8Q`=LcTRA}f-tb$wCIHkSEsxCR-jCeRT~4Cp z0LULCV8)vro47GX04U|l;n+eUZoTmI>hnGtKSH9%zH{_per=P34YfZ8fb+At&j6-K z1~eXNKMy;TBU?)n+U-XHdaHfiK82hfB-0^A$1x8@{xb2a0hFkmT+ z6TKwW=iaEOCddWJg=TfR1T?n$jm!x8l>ig}-Gjn(ZOM#7NupDgnQXPIDYPop6TFHd fU#!ezs{rs1+MfEaVJfLk00000NkvXXu0mjf*2w+2 literal 0 HcmV?d00001 diff --git a/kreversi/icons/cr16-action-legalmoves.png b/kreversi/icons/cr16-action-legalmoves.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef60f95cfb7ddc75d8bb4d45eeb07dbfd2338fc GIT binary patch literal 573 zcmV-D0>b@?P)5 zlQC#iK@f)jncaJb9%48m5epl!QZNRtFc5^7NHmD8Hbzi1jl^(7G(;2+Vj*aeQWW$M zD+w6UcxNOw7Gh%>8+!|zL^KKBy`A6UE+TsO61Ulz+5P^1cG(q@NE@Rs5A+-DLVnAx zv!%GQ^7p!K0N(^;UPL;(yN;~2+9T~c7@O<4C(v`o@Y%cQ;GMzuEd^S3P<&Om{A6}%v0N^9tIu=-4@S5ocu-6!J1eR*HHog%I0D3*1{kT2q8g}A;0LZDh zpT=;1dT8*!SqUil0+)b%HAZrZxlr765=;reg%es#z%C9(?z$9dPExeE+G5 zYT+M&@xI4P+oH~WB620ov7XqdbMp4&L}fn!$jH>2A|O2^L4qV;T zluc|@WfX>=_q&5b7nYv|i6&ygMmNTQJB@@z#c09?#kK|lHpS?m3nB7jG+K;A76d~4 zS#(vT({V76z{;Q{tk`rx*oj*f1Y;s0nG%?LUKjUHTcoue@gz6-e$IKmH|L&nWSfqS zzu#OfaT56P?eE^Yz#~|tvuJ$y)0&j&7n|p>R{@U%mTl^_=98(CCNa<0#%2n0aCvX_ z`fH!Ra-*lG$BU!>0j}?9KAo~OX;_C1h7t}2>^ruvMebueMhDI<6`WEf)s(bO0wfSf zqSyn{<47;1OlME;Zd^V!R9~?qFwzvllDrUH5=bc1MrG;J*sk%_i-P-)Z$0DiVT5L3 z3&T^U^=Vo5{o;y&{u6I}VKY7lcEQ0)ho5!$BjAq*lqYTbVR+(&e*@2yTed8{6Zc~+ z;$@5nzj^aEa2xpT^t+8wCGA&A2P9QV2&XTB*B1h7Zi6)Uom&;{IO%pY2fy9+P#xE6 z%`Kp_K*bT$(|_IV9d7JwFA83Y)%Tp)3mn6c-_Kv%F*9o3~HD zl}~6~RTRd5-+hlv?ZT3P;KnT62`!cgA{0dGbb_s5yOOF4=}L;ARTRWfXtXhhT?*tKhB1Md@PiN6%cF z=?8@k_W12fyN0D~9@qkIMYK;>g#zR!ue|uc@5?uzE#3a+Cq=gojq5$%sbEg@yf zpU6|~i%j`;p@F2d0vz2e6d)bnGq-5sfKUNSK!#Kk!gfX9h-enVx*U3v)U=5(>2y>{8LNfb6~(wl%)?}oM@cYtCREJWtkqei7pu1 zbnCGpY;5dq*IrI%1cM{IrMq54a4?tHCdd)1FaWKYOLVFwuu(^4jp@# zKi@ThSDkit-$A#$=bGNEaXs#H-F zQhHO_3R`m5f?`n{Yg$31L1|wUeGvsqiXyf#F-g1MH-8^?=gaOUF_&iJVPSU8ocYd= zb7syQNz5pl*9ZkKfB1PVpAn%0) z2uNi{2s0!lWpxF>pe()){GhY({krS74A9;B!D^CUfmnx>Z{$LRdMh{;q+v;iW%bsM zcZz^`3jv@c^geewLBj^5^DSkBeJ<%pot07R+scNMwHN0V0RSj2E7}R+!YxIDSZ+L? zjK>mYs+(B=s$bdcTi;d#_$e0xR(oxrE!Jueq?aL_2N6u44&GC=ym0q?0J17!PuuCc zjK_-r*0^+7-&VzrY(Ja1sdG=?S{fWBS)H2>Nxx4FPu#t$r}3XTN5Dz!@U~xNRSjUw zMPfRse{{GVG_AJ^N$drzz-epg|TQdd#(S2v{D~Om& zk_II_*8SS1u}s@Vjery98~{9zB_>rL9wPa1RqL68yaQwk0Sgi-2vpo&R@hb5dL}G$ z5ddg9G;oI@J*P|t7Oo+isURkOtCD+qH z@H+==r5^*U4IQsmUIUh>4!Cpx7y|IPbG{@1#?Vi9O2by#lsH)NTh@BM0fzn@|32y4 jc0aIHiGvkCd2-_QM?-+9ix zvQ4#Z^^xh5@^N^D@igo`yPJOex!l~eTJh$?Pt@Dq2TTKyl8ZoFx%r_=@s~w76cnZj zA#`y-$uxMN3w-+l}r{XPm<=~^bul9i*LCI z(EN^hXTGjl){;s*Fk5#%BRJa9ibUxJWje67^XY{_Ne9RRNx`6V`Iui(jW~dI z^}U*$zj}5FFc`^qw>O;p^VWbjJ7>;LLQOY#g6&{z@Q`6vd&9}^s|Hxt^=xZRxaVyX zu^ml3Y{!l7*MHOhR@bb@g3?n$Q;cN{Hj#XDd&8*@suc%FYr=#}03v{5fY2m`dF#8M zKbnTx$3geo&dLsq4~>^NKxnP&-eKaWMTo4zC4~l&JR(G)*kz$%q&4Fu4p|XSOsBjE zK6V>KQJ4%uz}~I($Ip#Bz=r2O&lxWOe>WP03g7-pqV%2d6fXe)Y^v+IHk2P&2)ZT| zZ-qdT@wT=(y_fD70Kk@pI znx3YiPoj4;h=08c2J_cn&&8oVgZXP~$0`nxB$^ls7sw3YU}N)_HMh=}eH)IoE!}W* zW|D-H*u=tB`q`x`LyJ2*nhe6Ml-3u%S(5lV7YiiT5-4xCnF5L7*V~TyWvpGt=?Ga;~#btS;#}u51_SOS!_3PBU z<*Oq}P-7|KZbC#a4MqQ?f{iMUQOU_-AF`mEF~wIkBc2DTls9^e2!)Ub1Ms{42MEI# zm#+@(PJ^^^IOl%bDH6_(Ma*1N;dAME=3vpuY;1!{)+2DlnbN}&EjXjGiEu(_ac4(U zyyM64rFVOtOoIB0z|lUNkDmQ;>zvC_s4x8$?fU&2 y>wXMAW@DX(fGXo0po6&uQ< z1wjn7V0SI0L<0#ag)jK{A{v55V{ z`Ik^cPlNS~8&;7*l^_rS0AYkwwcOOWmYl=|@D?9lD-^LApkN0XS0hv5Sh8Y^)#+Vw zcs(h!8VEYgOaM64NTGL0NYpprUE2653HlHKppUVB$zYAw_}c<|0Qd`ip7xY_8$%)h ze9IcQlAsc>=|Ni|O!^E+mV6V;zp~^Xq~tTK@fpwl+_sbh_?9*96pC1)Kj|JJU~22* zY^bU%xk+lW{OvE@zo!J?>lH_;iKsZz-+n*@+nUt4LF?Aa-#L)?=U@RS8G&aW2XFvJ zl7W#kO_@jUH1%29WN7MbK|~pfB8tv^bKXCGX2y?$0U){q-4g%^BD1$iKFLJ>!p`9kpig9HJBVi)Cg1>R2v9KczX-6d7|CNsCC0Agibp?BMW z0sw$($DBV{at?s5L5Hx3<+bzc=M5A<#5&MW(zK8S?a|xJ<}@@=*$H6D7dvirj4Skc zI{OO{u@`byW$%@eD-F6BkzmGN67-h+PTNeILs>wAD#7>`Kzp*0AVCi~?sBZ?uk%2h z?R3NYO-G4nmfki;LP{=nZJ)i%I_Ka*-!Pk9nFqqV08LC73CY*gE^TaoSM8X)QMFTb zGxJ%!ZH+`M$=~a0ch}_XaaZR|lA6p1&>EZ^ClZ22Dt589KhDPm$gOd9fH57w#oj>Z zlDFG=NbOXgWXZ>Lk7GfQprX1$@0fo0AHN=OR%Kt7s(cfGQGF2+h;S(x!}(vhFX!%Z z&X5{!(>;!YAR40B#H_eRl8_!Nvbo!5pJ&N)NzkQ`6F=WSZq)JK+#qkavxFrd2+fa% z5aOj?du@^)0RX^Rm3?B&_y->oikOhK`qk%ND}K4Vw>R;(1`1fB2|yGC5hnTl{v=<{ zXh~Sq50vb4)!K&LH}pFYCIX1aPQynJe`H44w14_yH(>+@N(y}*m4PAv8i0sM_*?x` z`f4|20059%lk*FJ{no{fCBN8Ln<)hV0IAAVAhekwnE8di+Dkb=-sf|#LUMV;wgY%# zpR2Y{A9Bh80HFP;s!kF*c9Phnsy32f|Y?^(Wnta+T0&EmnX87vNYNo$zRiQ9^j`KfTnS&chei2;_2R!2qY3BFo-|^6ooeA zmNvf^Pd6)gcmvj6osfZZ}rJ(R^2oO07q7F1R^+y zaM)eiJUK#l(Z*9F>^89$jInxW=m=Cl&ec1O!b;bHRWGuBjz|xNUg(0%dd#5(%}~z* zaF3jVQ zQp*{F0UHD23ImhB+K}~^5Q6SCoto%Y!&`O&DEdzhW1Nj^>#<9y>e7{Awih}eKyaRM zLSrfVD3pm}yiTJDXz`StdcgPwZbOJiRZV7#c*fHn0(652&4LK>HiYOR!kk0vvn~it zasx95ZX*Z@prCGD=1Bm-z0mxM7Kg(o?vBuoA(Th~Kxen?{pKp#>}dj?j+LF5d1Y-y z<`!dWI&S9XSDgAu5It*6v66XbZAIoXvu?Vs#8Iyk14pj3b}YB*rX>IXX@&rB#->p2 ziJjAP`(dzYDXKlO#Qbf-TwjnoT;}Xs*^bY0000)*TktkM3{i=sG|aX&OF)&^lTV zpt!fbvo2uKkx>N#*(;)kfdl~v1R|ahZunPjI7x@aN1mRdpqc?0CJ;ab0~#n&Zx}z1&>s^JfXShA(IdWsjYTJJ^qy*J^h&_;#_`1@4*>9)gaL<$8-y;{H2L&J zSD2>39f#10ritSKd?pZ|dBqMKhzJQiJKN-eI}}hBnlu8$mjczlb9S{$+c!-Xa$k{L>fzTK^_2P z;h~WuAIKYe8bH2DfFKwn^#M``s{*Y*SjKBwru2 jzqCA`$V4Z)!t>6Kac2p3Id1 z?}Pb|JOdz~q&b@a1n`P@UWRyHTG{mcVNwT&_J8tB)ClhbFjhq3iPh?3eapt$q7ykI z4*)70YY&mUA(Gf~A+cjrN<1$re!c;#b4eZmR83LaZ!sO2 zPO>A72na+V@tOP(TgNRhKZe`oHnxeKZa>(0; zy^L1)TlO0|RtkW(BSIh=V9r*ZWOT{{75?V!n#Ninu^H=51i^jPfp1RbhHh7MNn{%e zLqSbl2lPY)!X%gHglQV2%)~JrDFtyA$SeZDuIfPRFWDxU2`L3s2U>5&7+H$W4{EXq zP3YPjaLkCbd!b7IS9?e!luiW6P?f(m>zlRika07ospkXuFO>*`u}xWL*8^k}P~mU> zSu^^xp)XViS{rgfusd|SoxayQu!7)^P7#`VYc@%hls7FaDQ~(qb7kX-?rggi zQ1;xun4u#J1fs=!YrJmg$PRa?riKV!6~SPoq-s`KW5y)eqZgVAf6K9`4hKnnHEgu6 z{GjOgEqADOBY^6PQPs)+s(6 z@fE+7v@7vJNG7fWc7aTEx8B;|@L_qT`6_6&fdoY`jldww866!-To=K_PuKg;1Ar{3 zY+04(Q$ae2nIg2^EqvUF;<%2o&fPJia5#4j}opSB&$gei^-N#XRUCL3p!H4Re9 z&hH`-kBeZ2{Se=6A^@}lXi@?gshsz55m6E|c5m=sQUE?D*>Sgs5CIL%H+FCE9|d4$ z;(6;r_uS)C1CRlZUl-HjR#e}9T9>X>5-k8m0bN%o!_Z~B*83}M`@Xo9n76L^TM>*& zUSbW+ZFRxIg-+W(nTY`~Ih-`P|Jq;S(qzLvDZn~hL!oSnm_Q^ZY-Vt4r=kpv7M~nk|YbIaG aWcxqDGL*?2kA2Ai0000cVvZ literal 0 HcmV?d00001 diff --git a/kreversi/icons/crsc-action-lastmoves.svgz b/kreversi/icons/crsc-action-lastmoves.svgz new file mode 100644 index 0000000000000000000000000000000000000000..c2b1c4bb11e813279bd03d726cbef16c0dbb9c4e GIT binary patch literal 2303 zcmVr>Ud&P9%$^gtQ1pVme6;Oi!@%F6{KbP@S7t+Kpa zT`oSne{*gZQPU=6mK0U#FBfIC`1$IGi{@eVLli|ITsF%rynvb`XH93^O<#aH%)zfB#$CYV$Qjk@|g2;m~r0h z$~JjAFPj%LQ6P9qlv7G$7@KB(-K^zPk(ZyIB@;&H*1>@r_#FjcxJi?ZC*4}_K+==a zxAE)uuj66%IVD+}&D0E1dv-@vXuV9yP-a$eWf7?%2x}1a_B;?&WEC$+OD^ zOlRR^Sln_Hq(ty;Ue9BEnR;3-mv~JS#j@)^XH0`>2@C~y1d?aw2G$0V)S8E?< zNm=gV>)+nRpEAEfZ^fT#9}LEM3Jcw5;-5BE-JVZ=nQM`jY|7P1fv0Q(!8V-G@SL2p zJD76?lP*TyXfAum{UeHYKE!n_y3b9r@@Z96_2uHlU3mI|+p5ldJq+pa>~Yn?k089_D8s}u9v~Ux7B8t3+dbL3dlAD&qFYmFTBQ|mjUX7w1lnGB@cVK zn1rjlyT-R6g0u9SXrKyi+_09fGv8&|q50mxDeLUZ*2QT3BHs7Qx4MyP@xOB;)#{tv zD2+~U#ew;g?mKPGd@r{#rs>zaEqc)hdXIPnF@AZ1C+KFU9Qhw79DBwZKk2}~P#RyJqnf=0m;sQaN#IQo}HBHCOwIy8~D` zA{3Y0PLp|uTBYQK?s%s{FyqW*c-5QxQ06T(OWVfR@37(cXZZn6r@lJISP;Y8+P6uT zw8>N}2ZYI?U_#Tj{MYL@V`}`nsy|Kf#L?uo+JdhRf{*i<8Iu^Ra=L8uw(wWKCUpy$ z5ViL{dQ&8A^b4Hr(RJr^Xc?j(-`{VeSKC#@b;RUS*d=8V^m+0pAU|l?Q=IuGt@BNg zj`xR}?d|WNcLMuG{XJRDu^uh*xjcI7Xl~$eq!mqVwztrJ-TQh^!>iRS$G+sY!F54^ zxy1p!VdaMe!}J4sCzzmoTKlA}z6U+do}+j58vC#46-2W<6|UzfA8|vCH8u^+_uwnO zH{U~QAq|t(ALbuiAJ$2Gm9L>c!+Xl#;JOeL^aJ}FWVi8inz*vz&UNzMmZj?)h2nQ@ zUKGEek;NW^B2RtU{JGNL{`>(N*Yz4Y?V!JxZQ`4^zuv@rxEao!+fU(Y&%GyoF{s;L zagmg(EwH}oy+3{r1t-QzGJd7$^7#t%|Ks?JxF1d~K5UzLVo0qx$YlV?&BXRJ1&*Pt}z=1gcJt&^7S>y<IemrU*o%-p>Axs7nJVHHTK0Y0OYiA@QwO3i7g7i&|j`fn>xLF>2rnv?R0ILQ6@MqFhkq zZ3t^(G{;I8br-ONzt z2vbN7bt(cN?B{?A%7GhTmO2VW2{9%V6%o_JdH|!rO(<=MX52`yX`w)J2m7C4Ndqsb zFq2XTdyjs@T(+ONREEN{njZ*`0geY@oonaU!+DTht*|=^-l!J3cM22u_wS+Y?JU(F4 zNW>g0hK4w*&gZ5l(Z{hz`<+JnI0EWmy&RUKBohKLL7@_Zc38N?J8;OMaSx1)FmvG& z=PabSmDE~c!ZDyFq&>sUfRvPSr#Io}2#%L|kj8BUDCKaHSadT*&_xnYO#=sL2#8Ql zNyK_xVtl&^Xy-mTn(a@|Kzu^4aXAoo+^-!9OXw+D4H+n5G1|h0o%0T7a?pRX2fJy< z&QvIh$sQ`gXiEhzg12X^)L3TbD>y{E5Oy6#?<+tJHE_pNmOuz0kr9J4k3*ut<(qOY zghHPV(vFM?fE^5!2C5O2#%Qe&voajjcy$MUoa5qn7bj(hB6EZ9y(h7c5&b3sDa9qG zC)kvbAI#1%j1-)>d@ij*xeYqOQoreOi;WopR)qk5AtNMoIT|t8>JkrmiM1AX!pVF( Z>XA5AG8}vt*jZow@CPhhf@PZ;00701Zd3pO literal 0 HcmV?d00001 diff --git a/kreversi/icons/crsc-action-legalmoves.svgz b/kreversi/icons/crsc-action-legalmoves.svgz new file mode 100644 index 0000000000000000000000000000000000000000..db2acad9fbdf445a117926aa079affc77b813d08 GIT binary patch literal 2457 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 literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi128-app-kreversi.png b/kreversi/icons/hi128-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..26c22bc825487c872e14fd553fe9ca5ce9cfc5d3 GIT binary patch literal 12369 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 literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi16-app-kreversi.png b/kreversi/icons/hi16-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..9dda53b2fc9f7f02732fc6bc3898a338a76175e9 GIT binary patch literal 865 zcmV-n1D^beP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ;*GWV{RCwB?lRblb{!0mIYAnM zP6eP@SXiJw`P@lErAF}k{p;LXTgJ>6P*Rh#HNSkRs1#k2afZ0r;h7VXaiGyD#8E{;wGN-csYMQ1RKhxkz!TNNC`G!45q`iFLU zi=j5LC=q+Rc=4?&C=MeEu89;;%RnsEvgR1=J)u>G1%ADa8jYG>k@a{o>3S)kS+Kl4Tw`X>e1Nh?l zS#&YvlPl*z!Gq?Q%S-Q4b*rX$|H5lm=8oMz-ne}SBd#&C*Cdy@vnfW4R zbBc}aKj<|Te%4~~=n+=0iSLb8|Bju%t8pxs;-p0q+9+vJuN`K-t}vBFG7|Wa&tsF% zQ>%FRVF_<5qTlPf#`@n|6}w(09!T2K1JwnQ?(7sGv>?+N0!Xz$Bmz*VLYL-dI4OWB r;02)1&X4{N8Ylz53avGd!ovXoAcb6ut$12&00000NkvXXu0mjfV3CWv literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi22-app-kreversi.png b/kreversi/icons/hi22-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..6f77f5a96e32c22902e22c2390f524071f494613 GIT binary patch literal 3931 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 literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi32-app-kreversi.png b/kreversi/icons/hi32-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..360f1bda7e0a56bdb9d7236a2cc557f8f95157fb GIT binary patch literal 2265 zcmV;~2qyQ5P)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ^Pf0{URCwC#ms^ZpRTam7YoC42`Of!!^UZuSowm~s z?LeU|5Nv870-zg4Ok5y)cY${x&iJ+~~zefmts&2_OPhg=Axlt-Z66mk2!kcZ6pZBj{YJt@?flD)Zh(>f-zugfKejAIl&bm0f->1J0pOK;3`Mq zh6LwnWNlhghP&^%=h?shnvYn3#7=Tiwx1)N4sjeKIUoj+(K1yaC>Q~o3Q_D?IuxL& z;Wdy=6^u_?ilHGm9mQ2fKoE#UNJS`JMO$aMq&3HIc+X}43y{?Mo+?swIEPcC4+K}_U0O~ zkX``LY}P5uA*CCD2}ooRrx**WpkcK7C{|wmi^s3FF;i>c1Vpi^<(*e*B;zY=*->ZE z@woL|@Hz^IRN&Mx%sUt_v^Lr%xOE0<4GPz1;ChHr2#OKV3RDbM6tzMz3>^OF3R`Bu zuSJNAGEWSwbb8340h@L!xeo}Vutk7VK{ODYBY00y2Bv20#03DgW{qLdr>MF_79@=5 z*GRE|7l>>`z;pcg0(sS^(acDa1c1C4vbd1r$2~z)Dqms%w+_H7D1r|T2_-HJX*R}y z91ECwEh8^_RIW!}39&J#D0pzuh(c2s#>NZ-J%#2M;j*0Gau-#lJvl`&Eb!@BoNlCa zLsURSC_}&~_yECsf*ML!P#;S7ur1X zy=VFO{!g=G=NKZEQ%9HSB~SC=TX(U%xI&2=ZAq0;4qX6Hp-1i%JX0@N+gtfdL7YaOa;K(F4%g&`(b zOzcQ)6YE=`AXbQc#ECPf>H3qzV>PPaICl0l6Y(~ps0Pjx))`zIEU?;NLVW^Sk|Y^* zvx&kFiINzEmIWHdSW17EZuBO}+V#xI%~%;@5SCc1Jx)1!l(^hN>kfaB?>=<}ldZsF|2S7qeTBPjd+{ zSYmlW8Q(olYpPE@nxx+;;QZkf0%cJV6|k|f6BXFj+CyGI>AILm5D`QS-R>$BO{VIX zag;!NGn*_C^(?Kk(tC%~Cqa`Av9&}|M9_dMJS(S#rsiZ(%(>;WWUVb!VL+PJ@ZM8a zIgy=UY56Sm1UmUD%gY5(b4X+j)>^8nBB-P4M67m}$u`yS6?K!OG{mLUW>+|Q?k%R8 zTd7>ZVA!W9hqRm9In#ZO_H2X(!-1E7MJpX+qTM3PG8)YWQ&XE5uEKMNA7aDA4Cnf< zaPW;6*x24kbF5C5WsJ3&Y;4c4aN;P379V4>z7=9>Mgt9(3$vGuV|M?GSN}23j!oCF zbM|Vkn!AblBMWS}{&hA?Of!*f;l*?J^VBoXktTK0G^Li-NJ`5?Pkx`J^jTWT9PQ~Q zzy14HnSX0QEj>Rkqkjf|`oxz=TOCr{A}E<{OkiDr?zzBiceZ)uz#n<(^c!rR+D>Qf z9U3pym2Pj1r(S-TZu5Dr*tLhe9~e*Ps4hRk zy${cE*6b!KlwIK9Q+Bz#(AbE?!cFQ8e^n(V%oIumdZJb(s11;=E#SO^m`|n z9dFP~w~?<1K2#V{qN2{;YcA!)+28Za7k>ei42i;}v=jBloCvoUGaYz-4TA|Eka3nX!y>4_8xf)hbDO z0rd_=3G4D)6c8a2LB(QpRFpgsRt-vo!D5Vo*kj@pBT;78&V^nW#<;S=w|58Wnk;&PLXiv<5Kf1#w!zNV}^=y|8j7tO~GMV(!*}JKs;d$NYcM_C)Xh nB{n2g#gF8FzBl|ofPW7F4X7KA4nxZ}00000NkvXXu0mjf89fzj literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi48-app-kreversi.png b/kreversi/icons/hi48-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..b310d52ff9910995045d43e1d35a8d44c3f09d28 GIT binary patch literal 3484 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 KMNUMnLSTZ!4XWAz literal 0 HcmV?d00001 diff --git a/kreversi/icons/hi64-app-kreversi.png b/kreversi/icons/hi64-app-kreversi.png new file mode 100644 index 0000000000000000000000000000000000000000..5ca5cb5d28c70bed89657eb55b846e6a2b92ce60 GIT binary patch literal 4754 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 gvh($ literal 0 HcmV?d00001 diff --git a/kreversi/kreversi.cpp b/kreversi/kreversi.cpp new file mode 100644 index 00000000..b0a5ddc8 --- /dev/null +++ b/kreversi/kreversi.cpp @@ -0,0 +1,841 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Score.h" +#include "kreversi.h" + +// Automatically generated headers +#include "prefs.h" +#include "settings.h" + +#include "kreversi.moc" + + +// ================================================================ +// class KReversi + + +#ifndef PICDATA +#define PICDATA(x) \ + KGlobal::dirs()->findResource("appdata", QString("pics/") + x) +#endif + + +KReversi::KReversi() + : KZoomMainWindow(10, 300, 5, "kreversi"), + m_gameOver(false) +{ + QWidget *w; + QGridLayout *top; + + KNotifyClient::startDaemon(); + + // The game. + m_game = new QReversiGame(); + m_cheating = false; + m_gameOver = false; + m_humanColor = Black; + + // The Engine + m_engine = new Engine(); + setStrength(1); + + // The visual stuff + w = new QWidget(this); + setCentralWidget(w); + + top = new QGridLayout(w, 2, 2); + + // The reversi game view. + m_gameView = new QReversiGameView(w, m_game); + top->addMultiCellWidget(m_gameView, 0, 1, 0, 0); + + // Populate the GUI. + createKActions(); + addWidget(m_gameView); + + // Connect the signals from the game with slots of the view + // + // The only part of the view that is left in this class is the + // indicator of whose turn it is in the status bar. The rest is + // in the game view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(handleMove(uint, Move&))); // Calls showTurn(). + connect(m_game, SIGNAL(sig_update()), this, SLOT(showTurn())); + connect(m_game, SIGNAL(sig_gameOver()), this, SLOT(slotGameOver())); + + // Signal that is sent when the user clicks on the board. + connect(m_gameView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(slotSquareClicked(int, int))); + + loadSettings(); + + setupGUI(); + init("popup"); + m_gameView->start(); + + slotNewGame(); +} + + +KReversi::~KReversi() +{ + delete m_game; + delete m_engine; +} + + + +// Create all KActions used in KReversi. +// + +void KReversi::createKActions() +{ + // Standard Game Actions. + KStdGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection(), + "game_new"); + KStdGameAction::load(this, SLOT(slotOpenGame()), actionCollection()); + KStdGameAction::save(this, SLOT(slotSave()), actionCollection()); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::hint(this, SLOT(slotHint()), actionCollection(), + "game_hint"); + KStdGameAction::undo(this, SLOT(slotUndo()), actionCollection(), + "game_undo"); + + // Non-standard Game Actions: Stop, Continue, Switch sides + stopAction = new KAction(i18n("&Stop Thinking"), "game_stop", Qt::Key_Escape, + this, SLOT(slotInterrupt()), actionCollection(), + "game_stop"); + continueAction = new KAction(i18n("&Continue Thinking"), "reload", 0, + this, SLOT(slotContinue()), actionCollection(), + "game_continue"); + new KAction(i18n("S&witch Sides"), 0, 0, + this, SLOT(slotSwitchSides()), actionCollection(), + "game_switch_sides"); + + // Some more standard game actions: Highscores, Settings. + KStdGameAction::highscores(this, SLOT(showHighScoreDialog()), actionCollection()); + KStdAction::preferences(this, SLOT(slotEditSettings()), actionCollection()); + + // Actions for the view(s). + showLastMoveAction = new KToggleAction(i18n("Show Last Move"), "lastmoves", 0, + this, SLOT(slotShowLastMove()), + actionCollection(), + "show_last_move"); + showLegalMovesAction = new KToggleAction(i18n("Show Legal Moves"), "legalmoves", 0, + this, SLOT(slotShowLegalMoves()), + actionCollection(), + "show_legal_moves"); +} + + +// ---------------------------------------------------------------- +// Methods for the engine + + +// Set the strength for the engine. We keep track of the lowest +// strength that was used during a game and if the user wins, this is +// the strength that we will store in the highscore file. + +void KReversi::setStrength(uint strength) +{ + // FIXME: 7 should be MAXSTRENGTH or something similar. + Q_ASSERT( 1 <= strength && strength <= 7 ); + + strength = QMAX(QMIN(strength, 7), 1); + m_engine->setStrength(strength); + if (m_lowestStrength < strength) + m_lowestStrength = strength; + KExtHighscore::setGameType(m_lowestStrength-1); +} + + +// ---------------------------------------------------------------- +// Slots for KActions + + +// A slot that is called when the user wants a new game. +// + +void KReversi::slotNewGame() +{ + // If already playing, ask the player if he wants to abort the old game. + if ( isPlaying() ) { + if (KMessageBox + ::warningYesNo(0, + i18n("You are already running an unfinished game. " + "If you abort the old game to start a new one, " + "the old game will be registered as a loss in " + "the highscore file.\n" + "What do you want to do?"), + i18n("Abort Current Game?"), + i18n("Abort Old Game"), + i18n("Continue Old Game")) == KMessageBox::No) + return; + + KExtHighscore::submitScore(KExtHighscore::Lost, this); + } + + m_gameOver = false; + m_cheating = false; + + m_game->newGame(); + m_competitiveGame = Prefs::competitiveGameChoice(); + m_lowestStrength = strength(); + //kdDebug() << "Competitive: " << m_competitiveGame << endl; + + // Set the state to waiting for the humans move. + setState(Ready); + + // Black always makes first move. + if (m_humanColor == White) + computerMakeMove(); +} + + +// Open an earlier saved game from the config file. +// +// FIXME: Should give a choice to load from an ordinary file (SGF?) +// + +void KReversi::slotOpenGame() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + + if (loadGame(config)) + Prefs::setSkill(m_engine->strength()); + m_gameView->setHumanColor(humanColor()); +} + + +// Save a game to the config file. +// +// FIXME: Should give a choice to save as an ordinary file (SGF?) +// + +void KReversi::slotSave() +{ + KConfig *config = kapp->config(); + config->setGroup("Savegame"); + saveGame(config); + + KMessageBox::information(this, i18n("Game saved.")); +} + + +void KReversi::slotHint() +{ + Move move; + + if (state() != Ready) + return; + + setState(Thinking); + move = m_engine->computeMove(m_game, m_competitiveGame); + + setState(Hint); + m_gameView->showHint(move); + + setState(Ready); +} + + +// Takes back last set of moves +// + +void KReversi::slotUndo() +{ + if (state() != Ready) + return; + + // Can't undo anything if no moves are made. + if (m_game->moveNumber() == 0) + return; + + // Undo all moves of the same color as the last one. + Color last_color = m_game->lastMove().color(); + while (m_game->moveNumber() != 0 + && last_color == m_game->lastMove().color()) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + } + + // Take back one more move. + if (m_game->moveNumber() > 0) { + m_game->undoMove(); + m_gameView->removeMove(m_game->moveNumber()); + + // FIXME: Call some method in m_gameView. + m_gameView->setCurrentMove(m_game->moveNumber() - 1); + } + + if (m_game->toMove() == computerColor()) { + // Must repaint so that the new move is not shown before the old + // one is removed on the screen. + m_gameView->repaint(); + computerMakeMove(); + } + else + m_gameView->update(); +} + + +// Interrupt thinking of game engine. +// + +void KReversi::slotInterrupt() +{ + m_engine->setInterrupt(TRUE); + + // Indicate that the computer was interrupted. + showTurn(); +} + + +// Continues a move if it was interrupted earlier. +// + +void KReversi::slotContinue() +{ + if (interrupted()) + computerMakeMove(); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLastMove() +{ + m_gameView->setShowLastMove(showLastMoveAction->isChecked()); +} + + +// Turn on or off showing of legal moves in the board view. + +void KReversi::slotShowLegalMoves() +{ + m_gameView->setShowLegalMoves(showLegalMovesAction->isChecked()); +} + + +void KReversi::slotSwitchSides() +{ + if (state() != Ready) + return; + + if (interrupted()) { + KMessageBox::information(this, i18n("You cannot switch sides in the middle of the computer's move."), + i18n("Notice")); + return; + } + + // It's ok to change sides before the first move. + if (m_game->moveNumber() != 0) { + int res = KMessageBox::warningContinueCancel(this, + i18n("If you switch side, your score will not be added to the highscores."), + QString::null, QString::null, "switch_side_warning"); + if ( res==KMessageBox::Cancel ) + return; + + m_cheating = true; + } + + m_humanColor = opponent(m_humanColor); + + // Update the human color in the window. + m_gameView->setHumanColor(m_humanColor); + + kapp->processEvents(); + computerMakeMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game IO + + +// Handle mouse clicks. +// + +void KReversi::slotSquareClicked(int row, int col) +{ + // Can't move when it is the computers turn. + if ( interrupted() ) { + illegalMove(); + return; + } + + if (state() == Ready) + humanMakeMove(row, col); + else if (state() == Hint) { + m_gameView->quitHint(); + setState(Ready); + } + else + illegalMove(); +} + + +// ---------------------------------------------------------------- +// Slots for the game view + + +// Show the move in the move view. +// FIXME: Move this to the gameview. + +void KReversi::handleMove(uint /*moveno*/, Move& /*move*/) +{ + showTurn(); +} + + +// A slot that is called when it is time to show whose turn it is. +// + +void KReversi::showTurn() +{ + showTurn(m_game->toMove()); +} + +void KReversi::showTurn(Color color) +{ + // If we are not playing, do nothing. + if (m_gameOver) + return; + + if (color == humanColor()) + statusBar()->message(i18n("Your turn")); + else if (color == computerColor()) { + QString message = i18n("Computer's turn"); + + // We can't use the interrupted() test here since we might be in a + // middle state when called from slotInterrupt(). + if (m_state == Thinking) + message += i18n(" (interrupted)"); + statusBar()->message(message); + } + else + statusBar()->clear(); +} + + +// A slot that is called when the game ends. +// + +void KReversi::slotGameOver() +{ + uint black = m_game->score(Black); + uint white = m_game->score(White); + + setState(Ready); + + if (black > white) + showGameOver(Black); + else if (black < white) + showGameOver(White); + else + showGameOver(Nobody); + + showTurn(Nobody); +} + + +// ---------------------------------------------------------------- +// Private methods + + +// Handle the humans move. +// + +void KReversi::humanMakeMove(int row, int col) +{ + if (state() != Ready) + return; + + Color color = m_game->toMove(); + + // Create a move from the mouse click and see if it is legal. + // If it is, then make a human move. + Move move(color, col + 1, row + 1); + if (m_game->moveIsLegal(move)) { + // Do the move. The view is automatically updated. + m_game->doMove(move); + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + if (color != m_game->toMove()) + computerMakeMove(); + } else + illegalMove(); +} + + +// Make a computer move. +// + +void KReversi::computerMakeMove() +{ + MoveList moves; + + // Check if the computer can move. + Color color = m_game->toMove(); + Color opponent = ::opponent(color); + + if (!m_game->moveIsPossible(color)) + return; + + // Make computer moves until the human can play or until the game is over. + setState(Thinking); + do { + Move move; + + if (!m_game->moveIsAtAllPossible()) { + setState(Ready); + slotGameOver(); + return; + } + + move = m_engine->computeMove(m_game, m_competitiveGame); + if (move.x() == -1) { + setState(Ready); + return; + } + usleep(300000); // Pretend we have to think hard. + + // Do the move on the board. The view is automatically updated. + //playSound("click.wav"); + m_game->doMove(move); + } while (!m_game->moveIsPossible(opponent)); + + setState(Ready); + + if (!m_game->moveIsAtAllPossible()) { + slotGameOver(); + return; + } +} + + +// Handle an attempt to make an illegal move by the human. + +void KReversi::illegalMove() +{ + KNotifyClient::event(winId(), "illegal_move", i18n("Illegal move")); +} + + +// Show things when the game is over. +// + +void KReversi::showGameOver(Color color) +{ + // If the game already was over, do nothing. + if (m_gameOver) + return; + + statusBar()->message(i18n("End of game")); + + // Get the scores. + uint human = m_game->score(humanColor()); + uint computer = m_game->score(computerColor()); + + KExtHighscore::Score score; + score.setScore(m_game->score(humanColor())); + + // Show the winner in a messagebox. + if ( color == Nobody ) { + KNotifyClient::event(winId(), "draw", i18n("Draw!")); + QString s = i18n("Game is drawn!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Draw); + } + else if ( humanColor() == color ) { + KNotifyClient::event(winId(), "won", i18n("Game won!")); + QString s = i18n("Congratulations, you have won!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Won); + } + else { + KNotifyClient::event(winId(), "lost", i18n("Game lost!")); + QString s = i18n("You have lost the game!\n\nYou : %1\nComputer: %2") + .arg(human).arg(computer); + KMessageBox::information(this, s, i18n("Game Ended")); + score.setType(KExtHighscore::Lost); + } + + // Store the result in the highscore file if no cheating was done, + // and only if the game was competitive. + if (!m_cheating && m_competitiveGame) { + KExtHighscore::submitScore(score, this); + } + + m_gameOver = true; +} + + +// Saves the game in the config file. +// +// Only one game at a time can be saved. +// + +void KReversi::saveGame(KConfig *config) +{ + // Stop thinking. + slotInterrupt(); + + // Write the data to the config file. + config->writeEntry("State", state()); + config->writeEntry("Strength", strength()); + config->writeEntry("Competitive", (int) m_competitiveGame); + config->writeEntry("HumanColor", (int) m_humanColor); + + // Write the moves of the game to the config object. This object + // saves itself all at once so we don't have to write the moves + // to the file ourselves. + config->writeEntry("NumberOfMoves", m_game->moveNumber()); + for (uint i = 0; i < m_game->moveNumber(); i++) { + Move move = m_game->move(i); + + QString moveString; + QString idx; + + moveString.sprintf("%d %d %d", move.x(), move.y(), (int) move.color()); + idx.sprintf("Move_%d", i + 1); + config->writeEntry(idx, moveString); + } + + // Actually write the data to file. + config->sync(); + + // Continue with the move if applicable. + slotContinue(); +} + + +// Loads the game. Only one game at a time can be saved. + +bool KReversi::loadGame(KConfig *config) +{ + slotInterrupt(); // stop thinking + + uint nmoves = config->readNumEntry("NumberOfMoves", 0); + if (nmoves==0) + return false; + + m_game->newGame(); + uint movenumber = 1; + while (nmoves--) { + // Read one move. + QString idx; + idx.sprintf("Move_%d", movenumber++); + + QStringList s = config->readListEntry(idx, ' '); + uint x = (*s.at(0)).toUInt(); + uint y = (*s.at(1)).toUInt(); + Color color = (Color)(*s.at(2)).toInt(); + + Move move(color, x, y); + m_game->doMove(move); + } + + m_humanColor = (Color) config->readNumEntry("HumanColor"); + m_competitiveGame = (bool) config->readNumEntry("Competitive"); + + m_gameView->updateBoard(TRUE); + setState(State(config->readNumEntry("State"))); + setStrength(config->readNumEntry("Strength", 1)); + + if (interrupted()) + slotContinue(); + else { + // Computer makes first move. + if (m_humanColor != m_game->toMove()) + computerMakeMove(); + } + + return true; +} + + +// ---------------------------------------------------------------- + + +void KReversi::saveProperties(KConfig *c) +{ + saveGame(c); +} + + +void KReversi::readProperties(KConfig *config) { + loadGame(config); + m_gameOver = false; + m_cheating = false; // FIXME: Is this true? It isn't saved. +} + + +void KReversi::showHighScoreDialog() +{ + KExtHighscore::show(this); +} + + +void KReversi::slotEditSettings() +{ + // If we are already editing the settings, then do nothing. + if (KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), + KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + + +void KReversi::configureNotifications() +{ + KNotifyDialog::configure(this); +} + + +void KReversi::loadSettings() +{ + m_humanColor = (Color) Prefs::humanColor(); + setStrength(Prefs::skill()); + + // m_competitiveGame is set at the start of a game and can only be + // downgraded during the game, never upgraded. + if ( !Prefs::competitiveGameChoice() ) + m_competitiveGame = false; + + m_gameView->loadSettings(); + + // Update the color of the human and the computer. + m_gameView->setHumanColor(humanColor()); +} + + +bool KReversi::isPlaying() const +{ + return ( m_game->moveNumber() != 0 && !m_gameOver ); +} + + +void KReversi::setState(State newState) +{ + m_state = newState; + + if (m_state == Thinking){ + kapp->setOverrideCursor(waitCursor); + stopAction->setEnabled(true); + } + else { + kapp->restoreOverrideCursor(); + stopAction->setEnabled(false); + } + + continueAction->setEnabled(interrupted()); +} + + +bool KReversi::queryExit() +{ + if ( isPlaying() ) + KExtHighscore::submitScore(KExtHighscore::Lost, this); + + return KZoomMainWindow::queryExit(); +} + + +void KReversi::writeZoomSetting(uint zoom) +{ + Prefs::setZoom(zoom); + Prefs::writeConfig(); +} + + +uint KReversi::readZoomSetting() const +{ + return Prefs::zoom(); +} + + +void KReversi::writeMenubarVisibleSetting(bool visible) +{ + Prefs::setMenubarVisible(visible); + Prefs::writeConfig(); +} + + +bool KReversi::menubarVisibleSetting() const +{ + return Prefs::menubarVisible(); +} diff --git a/kreversi/kreversi.desktop b/kreversi/kreversi.desktop new file mode 100644 index 00000000..e32a3bc4 --- /dev/null +++ b/kreversi/kreversi.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Exec=kreversi %i %m -caption "%c" +Name=KReversi +Name[af]=Kreversi +Name[be]=РÑверÑÑ– +Name[bn]=কে-রিভারà§à¦¸à¦¿ +Name[ca]=Reversi +Name[eo]=Renverso +Name[hi]=के-रिवरà¥à¤¸à¥€ +Name[is]=Viðsnúningur +Name[ne]=केडीई रिभरà¥à¤¸à¥€ +Name[pa]=ਕੇ-ਰੀਵਰਸੀ +Name[pl]=Reversi +Name[sv]=Kreversi +Name[ta]=Kரிவரà¯à®¸à®¿ +Name[tg]=KРеверÑи +Name[th]=หมาà¸à¸«à¸™à¸µà¸š - K +Name[zh_TW]=KReversi 黑白棋 +Type=Application +DocPath=kreversi/index.html +GenericName=Reversi Board Game +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñž Ñ€ÑверÑÑ– +GenericName[bg]=Игра на дъÑка +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা রিভারà§à¦¸à¦¿ +GenericName[br]=Ur c'hoari taolenn Reversi +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula Reversi +GenericName[cs]=Desková hra Reversi +GenericName[cy]=Gêm Fwrdd Reversi +GenericName[da]=Reversi brætspil +GenericName[de]=Reversi Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι Reversi +GenericName[eo]="Renverso"-Tabuloludo +GenericName[es]=Juego de tablero Reversi +GenericName[et]=Lauamäng Reversi +GenericName[eu]=Reversi mahai-jokoa +GenericName[fa]=بازی Reversi Board +GenericName[fi]=Othello +GenericName[fr]=Jeu de plateau Reversi +GenericName[he]=רברסי, משחק לוח +GenericName[hr]=Reversi igra na ploÄi +GenericName[hu]=Reversi +GenericName[is]=Reversi borðleikur +GenericName[it]=Reversi, gioco da tavolo +GenericName[ja]=リãƒãƒ¼ã‚·ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ដារ Reversi +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Reversi stalo žaidimas +GenericName[lv]=ReversÄ galda spÄ“le +GenericName[mk]=Игра на табла Reversi +GenericName[nb]=Brettspillet reversi +GenericName[nds]=Reversi-Brettspeel +GenericName[ne]=रिभरà¥à¤¸à¥€ बोरà¥à¤¡ खेल +GenericName[nl]=Reversi-bordspel +GenericName[nn]=Brettspelet reversi +GenericName[pl]=Gra planszowa Reversi +GenericName[pt]=Jogo de Tabuleiro Reversi +GenericName[pt_BR]=Jogo de tabuleiro como Reversi +GenericName[ru]=РеверÑи +GenericName[se]=Duolbbášspeallu reversi +GenericName[sk]=Stolová hra Reversi +GenericName[sl]=Namizna igra Reversi +GenericName[sr]=Игра на табли Reversi +GenericName[sr@Latn]=Igra na tabli Reversi +GenericName[sv]=Othello brädspel +GenericName[ta]=ரிவரà¯à®šà®¿ பலகை விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на дошці (реверÑÑ–) +GenericName[wa]=Djeu d' platea Reversi +GenericName[zh_TW]=黑白棋棋盤éŠæˆ² +Terminal=false +Icon=kreversi +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kreversi/kreversi.h b/kreversi/kreversi.h new file mode 100644 index 00000000..17599fa2 --- /dev/null +++ b/kreversi/kreversi.h @@ -0,0 +1,178 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#ifndef KREVERSI_H +#define KREVERSI_H + + +#include "kzoommainwindow.h" + +#include "Score.h" +#include "Game.h" +#include "Engine.h" +//#include "board.h" +#include "qreversigame.h" +#include "qreversigameview.h" + + +class QLabel; + +class KAction; + + +class KReversi : public KZoomMainWindow +{ + Q_OBJECT + +public: + + enum State { Ready, Thinking, Hint}; + + KReversi(); + ~KReversi(); + + bool isPlaying() const; + + // Methods that deal with the game + Color toMove() const { return m_game->toMove(); } + Color humanColor() const { return m_humanColor; } + Color computerColor() const { return opponent(m_humanColor); } + + // Methods that deal with the engine. + void setStrength(uint); + uint strength() const { return m_engine->strength(); } + void interrupt() { m_engine->setInterrupt(TRUE); } + bool interrupted() const { return (m_game->toMove() == computerColor() + && m_state == Ready); } + + // State of the program (Hint, Ready, Thinking, etc). + void setState(State); + State state() const { return m_state; } + +private: + // Initialisation + void createKActions(); + + // View functions. + QString getPlayerName(); + + virtual void writeZoomSetting(uint zoom); + virtual uint readZoomSetting() const; + virtual void writeMenubarVisibleSetting(bool visible); + virtual bool menubarVisibleSetting() const; + + virtual void saveProperties(KConfig *); + virtual void readProperties(KConfig *); + virtual bool queryExit(); + + +private slots: + + // Slots for KActions. + void slotNewGame(); + void slotOpenGame(); + void slotSave(); + void slotHint(); + void slotUndo(); + void slotSwitchSides(); + + // Interrupt and continue the engines thinking (also KActions). + void slotInterrupt(); + void slotContinue(); + void slotShowLastMove(); + void slotShowLegalMoves(); + + // Slots for game IO + void slotSquareClicked(int, int); + + // Misc slots. + void configureNotifications(); + + // Some dialogs and other misc stuff. + void showHighScoreDialog(); + void slotEditSettings(); + void loadSettings(); + + public slots: + // Slots for the view. + void handleMove(uint moveno, Move &move); + void showTurn(); + void showTurn(Color color); + void slotGameOver(); + +private: + + // Private methods + void humanMakeMove(int row, int col); + void computerMakeMove(); + void illegalMove(); + void showGameOver(Color); + + void saveGame(KConfig *); + bool loadGame(KConfig *); + + +private: + // Some Actions that need to be manipulated. + KAction *stopAction; + KAction *continueAction; + + KToggleAction *showLastMoveAction; + KToggleAction *showLegalMovesAction; + + // The game itself and game properties + QReversiGame *m_game; // The main document - the game + + Color m_humanColor; // The Color of the human player. + bool m_gameOver; // True if the game is over + bool m_cheating; // True if the user has changed sides + uint m_lowestStrength; // Lowest strength during the game. + bool m_competitiveGame;// True if the game has been + // competitive during all moves so far. + + State m_state; // Ready, Thinking, Hint + Engine *m_engine; // The AI that creates the computers moves. + + // Widgets + QReversiGameView *m_gameView; // The board widget. +}; + + +#endif + diff --git a/kreversi/kreversi.kcfg b/kreversi/kreversi.kcfg new file mode 100644 index 00000000..c1dc6e52 --- /dev/null +++ b/kreversi/kreversi.kcfg @@ -0,0 +1,66 @@ + + + kstandarddirs.h + kglobal.h + + + + + false + + + + 1 + + + + 0 + + + + true + + + + 4 + 1 + 10 + + + + 100 + 10 + 300 + + + + true + + + + 1 + 1 + 7 + + + + true + + + + #ffffff + + + + #define PICDATA(x) KGlobal::dirs()->findResource("appdata", QString("pics/")+ x) + PICDATA("background/Light_Wood.png") + + + + true + + + diff --git a/kreversi/kreversiui.rc b/kreversi/kreversiui.rc new file mode 100644 index 00000000..304b87b2 --- /dev/null +++ b/kreversi/kreversiui.rc @@ -0,0 +1,45 @@ + + + + + + + + + &Game + + + &Move + + + + + + + + + + +Main Toolbar + + + + + + +View Toolbar + + + + + + + + + + + + + + + diff --git a/kreversi/kzoommainwindow.cpp b/kreversi/kzoommainwindow.cpp new file mode 100644 index 00000000..4da50935 --- /dev/null +++ b/kreversi/kzoommainwindow.cpp @@ -0,0 +1,135 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "kzoommainwindow.h" +#include "kzoommainwindow.moc" + +#include +#include +#include +#include + + +KZoomMainWindow::KZoomMainWindow(uint min, uint max, uint step, + const char *name) + : KMainWindow(0, name), m_zoomStep(step), m_minZoom(min), m_maxZoom(max) +{ + installEventFilter(this); + + m_zoomInAction = + KStdAction::zoomIn(this, SLOT(zoomIn()), actionCollection()); + m_zoomOutAction = + KStdAction::zoomOut(this, SLOT(zoomOut()), actionCollection()); + m_menu = + KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); +} + + +void KZoomMainWindow::init(const char *popupName) +{ + // zoom + setZoom(readZoomSetting()); + + // menubar + m_menu->setChecked( menubarVisibleSetting() ); + toggleMenubar(); + + // context popup + if (popupName) { + QPopupMenu *popup = + static_cast(factory()->container(popupName, this)); + Q_ASSERT(popup); + if (popup) + KContextMenuManager::insert(this, popup); + } +} + +void KZoomMainWindow::addWidget(QWidget *widget) +{ + widget->adjustSize(); + + QWidget *tlw = widget->topLevelWidget(); + KZoomMainWindow *zm = + static_cast(tlw->qt_cast("KZoomMainWindow")); + + Q_ASSERT(zm); + zm->m_widgets.append(widget); + connect(widget, SIGNAL(destroyed()), zm, SLOT(widgetDestroyed())); +} + + +void KZoomMainWindow::widgetDestroyed() +{ + m_widgets.remove(static_cast(sender())); +} + + +bool KZoomMainWindow::eventFilter(QObject *o, QEvent *e) +{ + if ( e->type()==QEvent::LayoutHint ) + setFixedSize(minimumSize()); // because K/QMainWindow + // does not manage fixed central widget + // with hidden menubar... + return KMainWindow::eventFilter(o, e); +} + + +void KZoomMainWindow::setZoom(uint zoom) +{ + m_zoom = zoom; + writeZoomSetting(m_zoom); + + QPtrListIterator it(m_widgets); + for (; it.current(); ++it) + (*it)->adjustSize(); + + m_zoomOutAction->setEnabled( m_zoom > m_minZoom ); + m_zoomInAction->setEnabled( m_zoom < m_maxZoom ); +} + + +void KZoomMainWindow::zoomIn() +{ + setZoom(m_zoom + m_zoomStep); +} + + +void KZoomMainWindow::zoomOut() +{ + Q_ASSERT( m_zoom >= m_zoomStep ); + setZoom(m_zoom - m_zoomStep); +} + + +void KZoomMainWindow::toggleMenubar() +{ + if ( m_menu->isChecked() ) + menuBar()->show(); + else + menuBar()->hide(); +} + + +bool KZoomMainWindow::queryExit() +{ + writeMenubarVisibleSetting(m_menu->isChecked()); + + return KMainWindow::queryExit(); +} diff --git a/kreversi/kzoommainwindow.h b/kreversi/kzoommainwindow.h new file mode 100644 index 00000000..dee04139 --- /dev/null +++ b/kreversi/kzoommainwindow.h @@ -0,0 +1,138 @@ +/* + This file is part of the KDE games library + Copyright (C) 2004 Nicolas Hadacek (hadacek@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#ifndef KZOOMMAINWINDOW_H +#define KZOOMMAINWINDOW_H + + +#include + + +class KToggleAction; + + +/** + * KZoomMainWindow is a main window of fixed size. Its size can be + * modified with the "zoom in"/"zoom out" actions. + * + * It manages one or several widgets: their adjustSize() method is + * called whenever the zoom level is changed. + * The usual implementation for those widget is to redefine adjustSize() + * with code like: + * /code + * setFixedSize(newsize); + * /endcode + * + * This class also has a "show/hide menubar" action and allows the use + * of a context popup menu (useful to restore the menubar when hidden). + */ + +class KZoomMainWindow : public KMainWindow +{ + Q_OBJECT +public: + /** Constructor. */ + KZoomMainWindow(uint minZoom, uint maxZoom, uint zoomStep, + const char *name = 0); + + /** Add a widget to be managed i.e. the adjustSize() method of the + * widget is called whenever the zoom is changed. + * This function assumes that the topLevelWidget() is the KZoomMainWindow. + */ + static void addWidget(QWidget *widget); + + uint zoom() const { return m_zoom; } + +public slots: + void zoomIn(); + void zoomOut(); + void toggleMenubar(); + +protected: + /** You need to call this after the createGUI or setupGUI method + * is called. + * @param popupName is the name of the context popup menu as defined in + * the ui.rc file. + */ + void init(const char *popupName = 0); + + virtual void setZoom(uint zoom); + virtual bool eventFilter(QObject *o, QEvent *e); + virtual bool queryExit(); + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setZoom(zoom); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeZoomSetting(uint zoom) = 0; + + /** Youneed to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * return Settings::zoom(); + * /endcode + */ + virtual uint readZoomSetting() const = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::setMenubarVisible(visible); + * Settings::writeConfig(); + * /endcode + */ + virtual void writeMenubarVisibleSetting(bool visible) = 0; + + /** You need to implement this method since different application + * use different setting class names and keys. + * Use something like: + * /code + * Settings::menubarVisible(); + * /endcode + */ + virtual bool menubarVisibleSetting() const = 0; + +private slots: + void widgetDestroyed(); + +private: + uint m_zoom; + uint m_zoomStep; + uint m_minZoom; + uint m_maxZoom; + + QPtrList m_widgets; + + KAction *m_zoomInAction; + KAction *m_zoomOutAction; + KToggleAction *m_menu; + + class KZoomMainWindowPrivate; + KZoomMainWindowPrivate *d; +}; + + +#endif diff --git a/kreversi/main.cpp b/kreversi/main.cpp new file mode 100644 index 00000000..345e97da --- /dev/null +++ b/kreversi/main.cpp @@ -0,0 +1,87 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include +#include +#include +#include +#include + +#include "version.h" +#include "kreversi.h" +#include "highscores.h" + + +static const char description[] = I18N_NOOP("KDE Board Game"); + +int main(int argc, char **argv) +{ + KHighscore::init("kreversi"); + + KAboutData aboutData( "kreversi", I18N_NOOP("KReversi"), + KREVERSI_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2000, Mario Weilguni"); + aboutData.addAuthor("Mario Weilguni",0, "mweilguni@sime.com"); + aboutData.addAuthor("Benjamin Meyer",0, "ben+kreversi@meyerhome.net"); + aboutData.addCredit("Mats Luthman", I18N_NOOP("Game engine, ported from his JAVA applet."), 0); + aboutData.addCredit("Stephan Kulow", I18N_NOOP("Comments and bugfixes."), 0); + aboutData.addCredit("Arne Klaassen", I18N_NOOP("Raytraced chips."), 0); + aboutData.addCredit("Inge Wallin", I18N_NOOP("Cleaning, bugfixes, some enhancements."), 0); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + // used for loading background pixmaps + KImageIO::registerFormats(); + KExtHighscore::ExtManager highscores; + + if (a.isRestored()){ + RESTORE(KReversi) + } + else { + KReversi *kreversi = new KReversi; + a.setMainWidget(kreversi); + kreversi->show(); + } + + return a.exec(); +} + diff --git a/kreversi/pics/Makefile.am b/kreversi/pics/Makefile.am new file mode 100644 index 00000000..e962996b --- /dev/null +++ b/kreversi/pics/Makefile.am @@ -0,0 +1,8 @@ +pics_DATA = chips.png chips_mono.png + +picsdir = $(kde_datadir)/kreversi/pics + +SUBDIRS = background + +EXTRA_DIST = $(pics_DATA) + diff --git a/kreversi/pics/background/Dark_Wood.png b/kreversi/pics/background/Dark_Wood.png new file mode 100644 index 0000000000000000000000000000000000000000..cf487169c4e97ea95cd9d94825a69ab3dc85440e GIT binary patch literal 1330 zcmV-21x z5;)8Tyfq+!RUTYWnjk_p7fw@9l-D>+f%>iqy-^n?bL zY`bMMPyJPWNXx{qWwC%VzyUkD;j`T4ru0(~ey@tnR`T`uqTOtDZ8=zkxiwrbkHR0W zy%_*i6tx}EC2cy}=?+=D6Q~q!B5z2-SQ49-f-2M~526^&Q}oZ)sL%X}tu^c5@<3^9 z+CI!9(D^9M>9<)%zx0p>p@k46ya{rZ(twGgVnL~43=axIz1@9Pf)<%c;YM`TyPyik zgJC{Oi)(2)hYXc9yANR4a2^1#&!}Op4aH)$0z(y2x0bom@UFe3WHNh%DCja;ezrwT zG87OQHn&g3H$1&Dko9&jcg90ki||*{99O|-1nVoHc?Wq6V_T!NzUPD*R&S{OiUq~Y zgdbEOXjL|@BT9euevEMzTQ%o)WhpM3q-t)zeW~UQI`;xT#Zhby; zg+I4G9n5!iASgBQrICADElMH9fED2+Yw0l ztDi^}m^+EbFE->PaK5uDtpM^pvq$dxSXRQVK+Wf%<hXp4QLxRb|`r)HZAkez1kA;|b$LRP(KkT2;oXN$PsN>r#7xYk&Bi=f?mFBhxpdf6Hd zpG5@Hg<{9zGHn_I~ZsgDJy9KAq2ew{lz0I_a9ijW376 zIJmw_0j86Xe+_|-#d04O(m1RNZ|nbo+`)ECNNNA)=-EP9>YLE~WZ+rwvJ(+t@_lqZ z4Hy6Z0p5SZ@D^3qhbP7XO~Wj;Y#~qgJ0$YNh?D*{dhD}u4($Fnhjt0GwyeIN;&pDC o)(#<$FCHx*2&Dn5)nu{qFR{LnNg=@e0RR9107*qoM6N<$f-)d{{r~^~ literal 0 HcmV?d00001 diff --git a/kreversi/pics/background/Earth.png b/kreversi/pics/background/Earth.png new file mode 100644 index 0000000000000000000000000000000000000000..fd9d4f822b64c050fc89061fb2fc71c568e57e7b GIT binary patch literal 23427 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⁣SbSXAXv)ckhf3Q>v?{`x%Xq)Pc3sm}GIp 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νCpB1*sNlG=VY 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 literal 0 HcmV?d00001 diff --git a/kreversi/pics/background/Ocean.png b/kreversi/pics/background/Ocean.png new file mode 100644 index 0000000000000000000000000000000000000000..6e420d6e52f4ae1875ce86e287a316703d7cab48 GIT binary patch literal 12421 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 literal 0 HcmV?d00001 diff --git a/kreversi/pics/background/Puzzle.png b/kreversi/pics/background/Puzzle.png new file mode 100644 index 0000000000000000000000000000000000000000..4033fd80e3655832a1794f8344dc57bbb416019b GIT binary patch literal 11478 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 literal 0 HcmV?d00001 diff --git a/kreversi/pics/background/Stones.png b/kreversi/pics/background/Stones.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba7dc7f49df79e9576cbd8daf2c3a549d14b8bc GIT binary patch literal 29782 zcmV*{Kq$Y7P)Z+~Bww&{m7sfl|9jQfH0WcbkF8u8$jnVeN?mWLEBKg^Tv_2|l z<>ux{YPooM8^CCNbm`J1S30GXRvG{$G+G~RoIO(<+D4jkRyuY7+}uqjlV$r#snKWz z06wc)B>Q0HlG+>c>DR! z{r)qqa-%E7Sf_P{!JQVS&{wb;2=3GP{2TZ?oGpmXo4dV^VM>qeTM(!DqI zYN?|W$>fFc_Osjdt-8J2hK9(TYP7l=)rP0NT6l zbUMHMjVocWSEAMGF4HQ(x7rXQR9y4zI*BN{6Ro}F!H2(ZqjP_!bAN{`CW-;rgMIx# zI<>aG!D!|3pS*DCkH^dQL;nvB58AfX$^`2>=c84$ccb#ZzHL7paII-HEz{>3vx-VMK*V^0d-hMGZngeKGZC6jMMp4wBKzpm@>&(~r z5B|4{Pyg;epT0B={T_`*KmT(-Pek_6ws+gIEcd>(*S4)OCZ@Qq>*d_nN(bu$XruuU zMN#!#c8iQcN|H=x5r<%FIs5@0*Wb^jP9kXsxJN8OBXL*oKB}<)^BcZ{*!<7E1eT7U;FykjOu*g zM?bO}t4jxPXJRh6!s&1YA>d1Yyu&CSi< z`JaAEorwP6U;IHQ!AJg!pIElv-fg+!_HL`SMvd|&ckb;Z_a*HrNJWo--{$O_T0I1pZ(2Gzw-ySMC3pD!5@70-#G88qpGZ=m14iR z@{Mu3(wc5BI!Vk`S(Pn;7}+TA>E6me{?*?o=k~&TE(EMEY+?5oSVj>&`N1cje9whv zzI4&mE^f6_WQHbN%ODLX-np`K@eeO9r5)+YvQaT@Pj>qGRe|T}(K1GzT=K!))0Z>YBj5Y(@ z1IW_MmiAYF?bFNlm;Dc&30LS_Id={4%vYWnkH^aoZ$7aZ)+K;cr(2J1rGs?k_kQPJ zT>Mwt7yj1yVt?|?SDyLHrvK{2V&56kP2^(%BAq{y0RVf)VumIiq?5hLGhcef>#bh#BcJyOE{?RAj_kIA|U%hzoxgGXQ5v2pAi2yju0P;~5 zhT+nsOUo~YVGesT5e0K7r6!ZfYfW?EU7H{Ksh=(m?e2KHnAm)jl~dn{0LV_8@OyUJ zY>YOy|Kc#@X|{Q8;R0lNu1OUK1%T~Kby4^y=%CbbH#oUKJgPzedOc+-G5yi z9z65Ee!aHy=(g}Z&?hub#7a@k^?X`YEJRVw^~w!iTVH?b?|eAAmu4@`!c3n|r}d3G z=_CPUi~YUJ|N8R$<$18w$N$?;{KC)uwCd>gZW~i<4-aO4I%{je$td}AGX0+J8vke9 z)@{8aNGdr-krQ9j!*M)@>uAQ_A{pIe4+qN`3ePAHXpvRW(np79x23dmmnVYc5#% z!WWvw*q_kdw9tMO{@4SH#P&wjFBL?@$_+jV_IHh*CA z-S2vLRaIqKEZ$+yLr=c%{X5U^ z@R5Y2zxCLbJ+RqYmY>Qz`=#T1>C(39W_Dfx07IvA?%esZ;$QvEUwthC1Gq=OK`%!Y zE74c~<+FT)qr1^+yIS3CaaY7~TZ?&{F>lIdFO~BfWxI+_8_DfeqUhoEwd~Ye0L)*h zp8ox>)pq_PKlaohGhsC>7V_!rH-7n37vA;mXTSQ)-nS;@d{#@{-j>e2ot2fSzQIQ? z9dTW^ciX5#S*m;Y>-x5@UKOyk5yJa7Z}GwNMSgnlp^tnt%q9R~8&pbt?8iU$3;)B< zg6r+Cf6Y!0q>}3YX8rok;K70JYLzItE!}Rf;;Lrpsm}VD^@rBaT=_Sbg`48g9yzWn zzB1V}Z=|32ul}3hi3LyTwe8z!`OT{Z-;hitvLd}VYNLAc=QR+~$w+FcZ`7UpJ0dcj zTHIZ!swz4eNh|d&S^u7V@Fw%b_isM-{hNog!?5$WciVe<-QNHF&;Hrt-~YI0KmEU| z`F`2n0#PK=)>0x;^$AXTL%tz{lW0|k|Ib;wiVm6dv>6Pu#~*t*Sb6rcYp>TU_jD%L z#_cQhQ$P6=&p!JsfOF^0J@t{NF8<;FQta>Lr_QY2o76WrAXR&}MSBOp>?OxilOXEQ z#_2Nvs_REwOA7=yEP7o;66utP;=}#Dtw%O~aN~x+mp$`^kDGjtk>0d?yjyztI568 z-fkUxe&r~+zr!_-ZZKc3a2HuW%id}_Cp(?{JJWf&8m*4jNAG;%@yTRQL?Qqz;;vqc z0R>TtBoeexikv6_sxv%)I&&NR*aLxZWa2h4+EIV;1z z&Ya4={P{0tr;X{F(dK43523FG!0Z>V3Urf%Bu`ZU3INoABSDL-mo_2{(IC?`%)U6h zW(LFao1`z@q(_ zC_j}g?q0F0TYAA1<>*L^7){z$`C7KMxdk8su>IWjr+(>^04{#%%dQ1j15k=aXR}zV z#xJ%Z*CMZ(5(TBfu6V(^smsphO4ITNZtBjzeFMPckGI46MO|jyEK9TCs13eH zh9S}lZh|_b6e$&Mt;1)Ak^-VSA;PJfYN&rX@p{u(%U7c6cd1$2!NeA0MNuoixof-u@|oFa4lcXbkClMM0~O5 zeJK*jw5y1;h(CAkJOHUhM4Yfv3$Gf0Rwlgikw`6Bnr+L@VgT4Pfa1`4&%*2#n>;fK zm2+1-SLBamU?-8}sc};WIA&=gFGt)&%FL;(HGniBpK2*RCCX^pevSd;c}_hQEam2o z*|QvTU4R#nn-ZKHG{{aH08Pzd1t_JJf-N06=!{02Tht)9Tjk8TxmA@`N-;B6Y@W#M zUcoghrA?M`%_>mFy1CnVcBce;@nCEv3QBK3w+%o|34ksA=#ev0dN;SFD@B1AnO5m~9s_o- z;3PBM)Rx{}EzA=mYb^jG%F--6OtCjv0xJM#nS;02Apx>f@o_wlQ;cr z*-jWFUy88TN;9*!&RNSfo53(V+xhog2t!`ZT?oP8nPL?aH6n6TP)ppSbg1pVD{nH6 zcY=k}nJlKQ>1S^4Ojog&$qQr8K5YAfSSrr?X@eN>XS$geG0H4GQIi^J2|$bn6iY1t zJUgun9XB3E;sgL@cI=DN5ko6YgCX@ZH+Ae;+_4vYleIJfGyo7o6cFjGZ_>Vj(n^!j z1b|s~E-!-8Gp902$n=e~wm7^NOq_Ky)6d8(T=)R)Uc`&|Qk2r?&t1sZa!o2fm4~59 z2gdYGnEnC87(xmuDFBn>05u?3xOOk^6nm4jZ%o&yB&L{5SF0or3qmQ?h)8w3uS5%@ z$Pycw0T^Yp(so`J`;&nCf&B37&z(D$uW!U?G?D6NQj3T$EB5S-()sC;>Bazvv?5{= z$DT+9>QF1Kl{S5&Q+;A_KQA(yDy8zl$dHz~4I86UYIHVZmJ%GehsAgpMGOFT?#j7K z2dR0@!!NIVeZ%e*p1mCJLvf-=#Q?+!fJMmDfLX*z*se|AqEBvjsHJ986j!(=y^0zh5LPMfTk zW&@KBH2`X9IsE}-gEZ@Bo_*l@wbCXvM6~tjR&aqD#J&`+EC5$=_zWeaH07uA{8XL} zQcB3PV=rMhF01cA+$bMK;kU^P<18DhuF1~kQnQ;nw4MandLA1C6vSW_OkJ2JssSkl zAh?^GnD6W&jc+xLW~&6bLbt(wfxn<(+Ij2Vg7f zOWAsd0Z>c}uGv*iDNP*)FzlM;xK1m_>?hAp0E9J8DmH^*;~9XI9!+^b>{K>d&!raUye4uAu3~CQB3W+KM(G&v z!TWd(a+6_M$i;?Vl3yJwIN+PiHVwQGYMKz#2ifL?zfwU{>#Mo)}Z+xFnw zbKDjFbFOYw;y4mOQyZ(g6>8B@orl-Y0I)B)x@06RUoXqD96U5Is@q1WuUFB1^p?Sz zv~Bf@BZ}ihMR%pW(c->Hfe4XKDNZ7ynCqI}*m>wJ4;}o;UM=-X)DGUZ*4~o&%d_hG zks!`e)84+R6Y3zS7=p2M;KvbbG5UUoN>kYERq|Zupg|zERJnbGh9H^e}Ji zcDhk?ceQtack6GQS6%(;pS5W39A29he|iWYYT?Ao+_op<$G-ouhu0sL+gOFr-A?bp z2UpR~UUm}6|98<|ta?pp{b)sq*l0m#ngMw5AB1fT)ztW=8BFy}*O?KI$z zI93YMQFx7~94e^0Q~cj7SGYj#!;KBYe#X>y%7?vBCOj87Iin z%*~y|$nqS39I>mM7Xc%zhJf|1at0Wk%E$W?Tj9bf-J2xij5#f@=VjrV!$mb63tv zFETotF=#RsU`j@rBy$5V?E=f#A2$tQ7S+n7(gHAjldt6@u$5g7kD{2G1;a-Q8B~7i zj2R5Q@MOG$2r8ugN-Ia4uaBIy+gEqR2^l)`Xihz)NtwZLcQTF>MLk7^un&7M!Q0PB zC0Z+cR0gb1J1QUMW#pu?AvEKf^I;y36exBFh21NPLyNEpE7Dq}LoJmA+Sw>mRIG`l zhU$?~I_qcITE-%cG?XaSRjNm3xp4=yQO7BPSqTOWwFo0g4G2KmRcTk16?=9eu^p*c z0gzS{#E=f#tkh!qrmWaa9e{W}kAb2K9KeuLid+eVM`E#8i(M=tVE}yWQEWZ7m3EaE z&VX{}cCaJIF1D*2vk#_E`^N4Sc6v;;_Y||AjCTM8p0b>=ibxYFiZ!XmbF7u=8tN%( z00sbNR*707aB`mDHk1iDF+lTd?UGGZ@;vg4r)!#>sg6+W0_|^1{IowzAGLb%<{Y zmBRf7vnlnI2%Evs&87mJc{IN?8HfH$Bf+Ux5pe>gv@1*M5&%1K0Jv^0{qYWfKtD($ zS}T*L0DR4EHck7cu?r5|D9sdKJG4YvD_vA}TDja9MHCEOR`BfdETe=bdy~=nCP~Q`-W?l{c0Z^)C zZg-yD*?D%Sc)^;)1W@A2<*CWe=1LJqQE~0K;r(`o_(slktuh z0cjm6Ibt`P;+O~B7;Oe*{ zj++W`mdP@uv~L%T8^sh8Nh7F*V%mCabm51e%+_<^CVZuwdej0SKbzaRqeSJW7GzgU zaR|;A;CxV|gEXeNI4s8F3DJVcvgKmKvon@@!E8Eqc3vJopxeE=D>YN1i1Z0#^de9$ zy%`!Qy<>5;D@uptm?oS$)iqmBj6zooX%hi}KeFQ3Bw3soi9tpIaJ37)baNMiCQW3z zO0AjYlvhgUYkAr?7e4s@%}2HXkic{oQQppTyOZ&b9C3K0hNQg7Ap#HYgkflER*9mX z3Zc`~O-(;bNu?dNBsjM(U3Au#Q(xX>X+#>aM>zK~#@I02PP?0G_=)ut?IZZ#=unq_A_Vy2i0Du3jTj`RS1tDd*O7l}>52 zKH54~cX<&&n7KGCTveun^h6^~2=kaZTU*f1%xog$-4sCa{4TRxc<=eNZ{XP|Qi>31 zC!U$JwUIiWzzS59E+o+NwQT#+#p0k;5ho1cS)PaC5r$Ga7=n5# zP85z~=CH!TJHV8iBg)%ip!aM@;w)xe9wivt0sFBJ1dRS zN|V&mNaIBs#9`NMq_K0$mE)QKG^N)>S>K$PT>egqHNc!}5wIu?;i#Pk6H6@&qJEa0 z%>($19@{+k-pznf3baHdib#`U_S7MYiP@Jk2f$Sv*dFSs#7ykBn0Cukk85^ploO%E{QVT*NBGX^6W`Sa{b7$waIIsaN^2~_v_NDD5NhL9) z7D^P0uop@wrWlNTZHU11hlj=$VI72xdI>MZ z;Wdy}1ZH2(SoM@R(HS)*!UsS=3tM_|%&p{V5eXK1yG_leYorq2^5pW4D;H@m6%j*J zM8U+)+CY%l%DNd-O!+W3S@ui+=vM)lQwv`_rkE14b63tvMX+E@4;Q6So%Ga{2Z_0{`{Yg+xif7eP=4=K)#5-#vBHB2cm%g<7c6?>5 zV#Q$9f-CG?h7+%1Rg{h^he#`B0hpojt;@QZot5tLWT~H!n-b6t?oI zI6?qMn_D3okRw(U2Ljm^MZhJNMn}q4*0b{rX@oiiAGRpbDY?1zX;23^)qO-00t3bblO$r++91gngCWhB_gGj4(`8JI_qb4ZUJQdoGVMD z%UYuI=gy~H^~K+R2EbA=0pL5p^w<3fV2tr!dGGjN<1rsCq9@<7D=fv7oi;jBS(;hc zFb339i|kcM4oibTqEUvqwcwB}6CfQZW)UkALn%@X1scv-XGk-PBs$y|z@@+1*Kn2p zFYg0IXz% zBTY=Pht_I@Ax-2(3_*1>r77!Y_gwg)|Mc(w;@=0Ll=_Wd`_%YfUelfjpL&R2_ER@+r8W1dY}i{x>naYC6Xx3V zR2-qBNkppaM>x5%!fkuv&g>=IyIsf`rlRdWeZ>>YUx#y zwvApF)gsiP=yg$hyG>qC8>b(hzj(yAYMhMP>-D?}l=$YSe(hI^Z+)}ZADn-9BOi@6 zMjH>WKm5+e9)Bo5^UlW}e<;fanHltR;aQ8Qmqe@7DQ6|$<|Ix?5G52vQP3Jsr_;{8 zwDz{O`c_-tsOQxjkoNAXNL##EP%G8-T5h*m4LY4T$l^rg@UUp_u5{BubP`6H=p=Dn zx3?uts6CkgkXy`L>-%;00hRTQR{EZ=UD}IJwg7(bbDz0%X`B9hMQ*n8??x2Ov2?`hip_-Fv{u>)OuA&XpI&%wBg*bYf*CYOCvYd%Fd& z_T637sBcT}b;`V{SNtCba<>IgymSD7JDu5;001BWNkl@!Fs_Hwnw5aQ9C5oH2jc!U=&8i!(wr#soqi~1qi8jx2O;|}fZB+}}Uawb`ReQJH z`&O~_#5>!ztzMbQ?Y691TxY&rn>VP={ieQEL;N5!S$`lRVb)KlwmpHzM;pm~kvb8P zPTIRsbdm(er04ZAx)lx9dQn9B{?2q>-ZOZU2Jo>T|ES!NZ*E_yZ%KW#1`w@AZQFJ_ zN%r6%x_>3cDuA`qYY%2?(aDuoTKn>0c}pth{_DHF*Q<1>*Uzl4|G?vNom*+ctP~N` zG4)Yhi$s4GF+`5!q4oUSx$~Wq9i8ajz|=QtqL|s^rc|%;^zc9_y?T;5cZqLtbYE0m z=jcSV_SV5l)YgC2_PSJEKdM>Oo0EeVZQz7|=4bu^-z+Cr546(jqjkQCa(=+K>ge_z zxl3&ubyiV%Ddq=U&W^aQ>w11vqD9^tlIlpi;@Qt{meESoixPTcC+Ij`=?48Zxy9`b zbf{iQIskAo>U9Slip%R9lRjRn^{>`i8^Z zmHLLuvO(|7x|}(=b!RXzzxc^tx~JFejYgx7eE7p(_>C{j>q@xJ*RuG{JsqvC+!Lik zojsWKRB!*=R&KX(6zKs2u~W`x(za5!X-Azo{ZMuAo$(f+t`QJ+IF(PSH0+Pe;ku;Z%g+9Rn4Z8 zYkU5>FDtHYRx)>OEbB32d|52kvmu!q`lQRF21ol zcqm_8Y4;BIqLa~lKELvfD**2K%x6Bc|8K6$%cHVpoQweQt@@~}Y8KPe@%{05XV?Av zs%_giCim~F>GdkQi+q%s#Q2t_5`UwvZ`Sf>?ex$d>>tc*dF1(_w`6PiU~;tpP~WI~ zZ|EhjPuf)|(AE#W({afMu3izHXvgL3CENS%fgEuyHMe!|4L!Ldd*9l1mc#a&4+jAQ zqZ3$-Vfxv#|8^V-U_SA4A5ZR0?%ZANbYi1=bH|f^efeN+d%a#K>2&TT^VWy1?C;qZ zEjQerXjMlM5lih#XN(#22QkH~b=&FcwNnoplXN<2XL9ArpIi}Ml&wYoLC(MHo#iWx zyDMk%wN59Vd}|UUD$$8nM066VH}+n=eW$)9y*KFMPiN6d5^0OWz3v;kZ+Y;+=md(F zroFDIrCqsQXW8HjpMN?ue!?gnBw08vQUt)v!7-gYf6dxy_}w0r+=_~bVlrJ5vS!q! zAS)|xviBT5W4b!%LC%df0Z1#Za#^afROz7splP9vSZaZ6Q7kg-413jv&@8;86fJqW z5Jm4^o`jI)!h0@kJ-QVVSZP&00QrZS^|e2A5#~m zYs#r#4orB>dgygvz^&&Pl2V>{39PyvC6Vc zfgs+O61aq6*8p%{0v{fjt!0Mm)4%<>WeYB(U>QMrMMR#xV<*Cj$g_u5ln@b_u5#>l zuU-Q%>SrPzKshTziGxz=$@jhgo)5hLKhyUnbtC$|B#x+qx)a5s z5D6rL+^9{aIwz7KIIuUW_Ii!mt%%@cL?@y+rZ|db*XQl6Ho7;;1||TJotA?)ceDOm zMw^?-{hhu239~nYLCxHIL$7mhXa4d`>elpoS^q6}PDIr!j*seEWbKVcceR}!*pd-p zd}R_A??3#Ne~8=deo^4w-fFz+XR}K$Oy+jp-ffjukH3AZzTIv=x6PgdpRBaXI%&_C zwr%TKRYf($s=86NZL5`jX#HVdS5-NyZ?LqerG{Wx+6JJis-Ef%24=iFUb$Vf)N+e8 zC{~@Ss@mIa97TgS58B)99jvss+U%_+x_xIlFIQTTI}&x2eR*yk=zRLOek-*3nP2~n z%YXE>NVpk2vE^H44x$AtB$DkyJ&AwmKUPFT97O>Zd5NSYKlXvl0~8_ zALbu=?}wiL!>6T{?ePQv^^_d3-FHw3>6)NmEe=YF1SOcgEm>}gv;u;wJU`zQZVI5- zbKzbW-g9B&?3qvh_Gd*T1Eglhj)(%Ip7m3LY(KvnWUnCPdYC`DA>ywc+R~SFj2`ep zMs1$XJv%vKFX9DkOB`p~vtbqr|0a_OH|*FIR~PAPZWuZR3xKJeE^4GmhiYuhAtJ7H zu5{|q#uU4%S(Xod^n*`2_Qj!H`NzNb3DY%p-`bg5qUQq3qD3AGKv488wZuyuhl+@q zgPv)DV;yC>TB1f6=}@0PccIvueEN64_}A7g{r|f{(Zr{J`?IceSvMP<%>Xb+XVn(HQ#N*tW4jtebo0T$}ikiZMd;qYe=Fc%Xmkx)x%M@b_{ zhybN1ES;E$xFeU7Jm1006zS~PkIrf@(f4^X*eq)SNZPMU1kpYjQs3IsDMyPZJgbpM7gR2 zkgpA6ioJX}Fvdn{^Thd37Px(Br!1yU{8m1Sfx@~;F;!Xx@ucah@aE1pw(Y?rPcvwe zSlBZGs;jgz?x>K6h@g~W&z`*@J$ho(D3iWMD*B=KUYHzOb)1)nc#zKeX*w`jnl0t& z;qvUmu@C?zwdT64crq_&+nG1@0ouy0Rd>}IIzU8_JnsR9u zsqX+;Dk5pb&YZ3n$=H}kxUpO`o`cFWT*I~8UbIhd`g4w*35kojE9W-tXUiwFrJYO)KXv8JMKML5X6OFq7AT(l@kJ1*0fV{Wm|`yy z&I8Hw+@wZCf4UBocXHARx!Y6^AsR z3^`~%G%2NYNZdEhO)F>TWh2e|-*Z7JZA-hXdCpFo5C7Oxk+U{b3I>mpTS-ltpUQm8 z3K%_lMkOlN%2iH<1>u;0*Gh=Pw^T$biB%G3r?VjbDGqJ0{?niTB91GNR9CsG5d}Lb zT-99oTj!M~sVP5gihaioD^Xk@;}7ZZ%C(!i!j&$l!2pcM6NCbrNDOI}r5k5+i80;V zE!@myl~!6oysHp0i`$pRVQK=< zL@H4d!yZ_7)iRFj#SfY01b`AU$`sbQ>7--{s@{#WdD>qH8pD03mCJ{@XCD>Qg4r(x zGa0GTqh|o1;!s{&&K>5?bd9ZSd6NS`aVr#43JQfKssVr?fdUZfgo6G@Y6;6CU(eYK zkyeS58B~@^i>JTxxjf5>DC=kRh@QNFunxpA1LfT2=gy@7SFRXOO=euxgf-eo6IMg2 zQ>_Uqc5`dz7waEhL_mBhpd}6oG}0m^#GZaNrFTii(@kGejvhI-TXiJWNCu@s(Zl zzh-pJWIT>S;c}>mYedqaUe+lF*a=lAcw6-Mw0rzD%KDlz0}6jZ;}{%=pscs2kN)mc7*C0jaE%Ua+X}2}3Rd7DkEutWjNM z7^AaUY!>-4&)yvre7s8+6B~d%3ez*JfS5=sv61zqgaxiZfisvqe{CtB4_7Hn3)d(M z1zFj8F0~ZXqMW<5Z%p45dz0eOhJ;^!+5|OH+Rs$ia}^i1Fx~w8+c&((;sr}FgKRP+|P3cQtdiJ9%KleAcVq=1M zM3ZU&O7)O%=2QmY+EoihrmI}V!P6(AGp8~Jg=l${<#DPDTabW=R5$bNzEf`A&!uJ| z?<)4}xdlZaI(B_D!3%?jCZ$&5M5$p6t(Mu!;;^vnvwo&i&CKKd$&>H9;3^*l3<)a$ z%Syp8q^+PsJJ0O`2%5_Pg%nc>rUgr*Ria+I)0(bPi3*wn;U*pGkUZXcY-{Jrjsfzu zA^Q}4G}5@zsjf20 ze(lg6AI<;;L4&17Fj~*bxhoF5lpX|?91o3Nx`+(I)RkP5O1$Ztt+jknwl{auEKUo% z@SgKOc;SbZ=88goRTyCtUWUPoX?~NJE`Qt$l_#J!4zLq&e_0S}vdmc-`p(rcBgKt zQ*Bb?Dh@1-4x*hH($JE(ky=8CM+!hpfKUu>8iI)M5ie!xGMi2bu>g-P-l2r@>^tRo ziva@xh?LTSPy>+lQUD9c4lZdc#1JI_(xDEC^{_+aX-X;%YQu%66iO8VN~p0MN+Ck5 zU``o3E4gx6KU125+?o=V^|hUQLwb5tFtd4w$=8M=p05^;U$YjEv8%8}F`k4oI=JF+ zJY2Sx*#o=t{Dd1OG}IN;WQJJ-Oy+h{Odz0$s6*Va04i2m>Cq?$#a@IMNXWCFy6M8r z6_9{P$<#Mk%fzy5RS_SgFlDk14n|i#o_)BJ3{xw)C8nP*pAEu45)xyfX*d`tj(_!lbN zvr}2%DoU1RtgZ;uHH5|}BAuo>4TBf%xRkr$ST^C<7l%b4M*+CGr8YjHl-@X-Ypt?@ z2@cQF`b!`(U1PdhD%rhSg#1?{QcP^no%vGiUg72rfKocZ5ddM7H;--FH$h8W&aHD^ zX(G}A+*N1jN=HVCLO4os(TA2@)p(I`?w!(Pj6VPNjVxPqa(sELhzjd=pWESv^E}^r zbhP!@mdUa#%^=O>&_XcEhFgzrZEkL!-`pHMvbph&t*ytlVv3!$N){47kQT%yf`|~qv!{rHuO&yk^ZYT}5AbX{nH0+nYxnT$S7zGbdNyiiQ#P0o60b~>~Bwh?h<=`URbz%>IX zFYkl~(~b(Gk@fT9V49!GgDMzEgF=S9q5jFTFKmHhNA|S< zJ<5j0=wfeoXS^^UCCaf70fd+vq$IVt$_e7oJ#Hb6op`M@=@i`fu6PjxDh_R!*(}W> z%+B-icr3MSj5ecO4+S}GiM6iyb9M$*ng$I5jR6LH<3 zZd4MZ3*lz>YJuaE!LU4qLp*i!w4ZV1G!Z3Ag8^h`^I~3b!>XeY=m%v&349|>N?A~r zg=z=@rkjr5wORb`qKp7Qswf|AE)k>gT-H(?7Ae%|k{*}}dYbH^pO>5=`;}&>X=#^2!vM`$3q0+fsWae{&cu~<5nQ%Q)LkwX z^JOuvRIEQziIdBfzbbXMQWeMcl`EE9{t#6apQ(yfw{%&~!ZBUDiowGmS_BPi*XoI8 z!nY6GMnA^3(GOQIp#1Ul3|6u#l~lS|z+h%L)AN2j@AL8d{T}s>2q~hSnJW&tt*og9 z)8Zu>sae@3m52dWkhdk1pG~tHnM@~WDZ9aN7BE5xXyPWJH}?D5{{^N+Jcz9|uAI}G z#+mKXu$x#iECSNFO0rv-6jfGDF1>JJe0?X&G7K<%?jnmcd7e!&36M)>5Hdu7VQd;{ z#4#l#hk}-8;i-*T<|g)1OJI23BoCWJneQi{M;6SrXg zYm_%khU4oy@tljQ#s>h%V2B}$B>fZsGRU~$7%02tC<5?IAvvkyvasFQAhyWX)x;pG z^%IeJU_-JjQn8Z;x4*nKaT5R-rK3tD-^)=TT~EoN+snrLIRGG3h-P#;3Fn$RP0dWE z;!03#-y+g+A;e@Q*}1x-`=fn+zFihmlEw*IQq!*=7)B%_m5Ngx3e+M541kA-9aKk7 zY7T_klqUd~fRb}<+Q{X35jgZ*Oe)Q?*>0Te=dF_(p%9=>EFt<303>L#{X9H&`se3m!TSZgq%kO?u&-@KO3GD{X} zbY)?!(ah3#Jg(U7+{$)d-`@Pv6$&sJCN{RCm4v4ggZS8x(rf_ z1c(vRiZ$O!MMJNxFKq?bBV`H#KsDiOH!@cgO6~yw;U+nkHWh#FcRrJibN2GEMC{wz znNqH>iwh6cc1?0RY56As>iWI#!lm(>+etr32C;gUjWl|0ic~xih;jfRqKTVOXNZ=# zh7cw0C%H6SvL#DHRKMub-@iD%dIbOwNiBXmE+PmG_EI@uU7_M7-^(SCG)^vl|0Qe9 z&ed%=xVD0Aj4+TLl0H z0lS1=1i&CRbN&+`STuqF2bj!N{7>l zqJ!E@T!dP$rv{W=UIlE1R!XTVW-sgKQ%#Hh{^ws)`!gPA6;~S{xR?`ZSdgqHjWlx< z07xy_!5H^i5di*VosjsGP9+plO;Sn&h;V0Fl9*jR`|1Fn)6z z4H_~U6G$xr;hB|K0qfFWc5CcPSIHcb6B2ZY0zrWg5hxtP7yyEs`}u_nuwV)jfGaB% z(xHg*Tlsi$GtVbZDhmbxpcYL*yRy<_83~ezTwUnIq{EbJ&Tr*wBZCpkA9Uf)?e zySDjzuThs^4*;MNQKtwa)C#yXOoL$@ff&)eP%+DkSx#MAoZ*Yf(^x?*yNOL|hH-2( z8pw99X$-trbjkTNJ-604m+fb5*&P4{V3;WJx|q4~^_?nru)dD5;qj#Xx|L*fE(J(g z9S1}Jpgb>-lwS`3P0a$3^yA@bs&(LEnt@b#!CbMfTIvxZglMQsnl9CI_+(wzfACwM z2|^RXa_~qHJQ6GgK6tQvaJRT$`?{`GYNQt#M3eyFR6vhLMZfS$C=j|TbIE9x4q4|Z?l!6^w(EPGfk z4-do(g_NF2o;~xtNG%U!5GN2U*KbKxAB2wtz3u=2>RPh>-B&;N+q$x5vzbit=l|}- z^$XA2!77HqY+C&Jm7Q7y5Ig`NjMC8uzxTX5sK>WeW~2L ze^9eO5v)Ghi<8w50D|SZF8$GRafj==77-tSKjk9{dOaNU2T3xR%*sFb*PHtJ0RTv3 zu8eo88ZW;v+W5XT04OFE)Qv=K%Jqwn@Zc{8u)OMCh6$8bu!460 zf9j`g-)_Be?bXkJcG0~8101cSVA@_A$uPONzFrCQf#kD%JRZk3wuIwsqFU(}*VkPo zuYUfs3SwB?=V&zg)K9#^BAc&oZoa;`7%v^4OD|oL2K5dru590q`blW5s*`Cg-j$s6 z6UWZYoaU=t>5exu_|#9n0stpt+g&@Ge)u0;o|O|WSwv=KQ8F(-65@#<@L&wto{?|( z`bhjcfDdbWG+I43*m(c*AN6n-P@nu6j zZOfq2)m*aN;~?zXZph5yks^Tm(ChYASCe8|_#^3c2Uo9W;i+YRB!BR)|6ua9TkV*h zPJZPVe(B>s@?)LHIslNo+kWI9UK%`|&Sn#4_7D85Eb6+p)}C7#BrBti=>P=f?djgv zaxXr47gYlJB=0@dGh~w0Xa4AqH?terqD9DPH2S5V`MHhr&u@NlYxmk7y)*jAKm5^7 zC*0fL1CrxdFsPV!Nt!H}^%|hq1eCx{A-(L0C-q;31s@k?7$mKJ~l^ovH`Q=0WXreT+#c_2I~H9j#}ndwz~UuM(+d|ad|ST( z{rogdsY_h)!ueT%$QDDRoat5u7%Ayn%^_*@c1+&!(?dUNmD5MH=pEro@*B2 zxIuX3$6scyw%-`rp-l#{!fL9DRTWI8697;wQ+9o8^Gn~-Rw7v)QKUZ_3?QvoH*?2( z9;m@FWU?DKT_qTbG)M-oT)DFJ(#KxH1|AB<_XrU$y?9B4Kl|#d$ERVmlIrr`xtc9n z8E7AdiYqnuQdB^pf)9a3YeRg%O5oNjP0~v*UfRC8{kPjbD1!C4tzQiNcsI{)WTO{G zYiG|aKxa~m(=1`%X8Tz&b@{A7#P#*{zr7o@?L(gD$8Eh1eU`iJKiuR1Z264KEVE z`e6P1`sEid88V;w{O68O!|_XpUeyJa|DW_@)dpT%Isul9rinD-3o*QK;ezHzovV&d z!|`kV^yrnJdL=LNSAXqQ0Elg@P6q%$K$@CO`|0J6UE0|hZ+~g407eEJ4BOj#?o7Hm zymIBr@oD&$&!122t6%sm2&})bt`cI|ezx=ab_MeBk6zxqawSmwu)@2jOM03&(l8)` z7^F{q@>3PN#WXAo4gh5PxpS4OW>z9nI)MHqO_t!+t$&I5#^LodXDfB{|lp^Ju$o49uLErDl?L|tp78NV^U@Z9Lqip zY#l$_7DNB@|KiisBlL&{6Kb^##yi{F**Hm(1^}Tsq3CT(ZRefP&ZSvHF=PS5MjAx= z)F(gHe)>^$vw^A(A>Mf0|ofBfa~ZkFw5tr%6+0{jf(_U?E*&hq_huf4wc+UGa3y?i0n$tBN$Vy!@} z3Y};K`?LS$PwU%~)5*s_`SPp3v-xYE{WTqZwB$VS;RFJ*0pMU51Cb$%1|xJc$1JT< zFaQKZU2BIH^uqx`n#(U<`rY6D4dns>z^8ulm0$hM&;07Y{B*LKAfkA&v8BPVD0ypV zM@bSE2`bJ@2$UyP38t%wA? zQ%VOA0yhFu0Bgwt5dn=zm1issUVNd=%8hr&i$mMF%1vB6813wg9Xrheg%~OapSlE* z<&psbp#>xtCA+dwz3g@YwX$vu0Hbrs0?hK#M?bOo`QLZ*<}4~QCDREFhAM=Yt{_j0 z)&zH6-*PPAE3^cTp+jkGEujka1Ax>FHfcfNqmgd1Y(LAgOndZA@(CHDE`6CD;SN_M6+;t*n|j4iH;O zYd}GmxyA7dO7d~smg_=7M8&<@E)ht!pMx}$y^QzW0&~Y@4g|%txOU^(Ly{0TLNI!6 zoy)uuN&0QRcCi}L(>A*Wi?y#w^@<%L0Z^7d1i56@1Q}u?Gf13NGM(UHXweX%C3JHs zHYWfRz%D>wAOIW?05A)C*8Vxk*;HmZNGvt7JnXw54JJuqhSn8MYxL*^FdV;mvmO2Q z)*rL64FG_84@VyxkPx8a@vTfmMk`5(p(XR$>zgn~2v=NMePaa%3C-0b3-O8GiF9aO}bSeAAj+O32~>)4I-k+ zXF1L_;k6|~>y=`Q0a1NI5eBg1>n*}rW;ulfA=qJp9$f*kjcsfJzz!`&1e~?f#fOZ2 zBPD>ZYyc_}=0qtTxG{+1zJ2XR2Gb(0xTqi29l6Za94YymdXx^+d@s+ciA579L+zc# z4O_4QbrVBISBeO0?qTQp_Bfx6(vf1`d0u8o5XRJ{k$xQR1y1&Sq+#*lU} zvgv75sLR8Z7#qis&z$azjr9#6W1|EBl8CYi%XAX1wr7uaN0`eWfrKFC9G}n)K^Kb3 z)p~H1#C>2EndQ(=RBgLmDO5S_w#M0HXkyzXX|Muk$YwiZ05FMdoXclLg8%|hiPj}R6TkBc^|ycwY`jRCD|?L) z5P%^BBcKc%1|cb2(JDf^;?j#3-OPm;Dwa0swtCr0tbpTs*>k?Gp2ii62(yGD41gG+ z1(WTLTZCbkFe2?m=O)h8Ia&f=0a%sbv;p8DbB6#B4;i#D=f#zPhYTS=Ko*UYif&|S zVu_GTsk#CH)U~Rsl-@kw%kc z?N$I>;H%m-vZ%Sw^?$)v0AuoLN7=e&M5+k~q=)=*9K*wXGhi<8bQHlp~ej{@e=UCh< z*X0a>Awud}+fOJUi$+Jh{Rjf`qH^Q}sBcbBkCYErmKkv z=S80FUfaI9O=OZmY{=%bjPqiYj?&S3#V)W00YjCzPk3m~;<-y9ZMba5=Eg#?JPQTwCQgArc&!WF&$Vp^ye zGZ8Wfc}Rx@WLnj07E)2p67s1Qx`8LcASzi708Q5PK@1oI7;=t@ zA&Q|cAd1_-O=Wr=+hKwcq8F|Oq)Ph9a5a6{(?BGjt z0JL2c$XEhytNEC$CeBqN)glm6>txARWuEO-qjMvhq-uP1@g~KqRRjfMm9OW z=3Hf|+Pan70GJJS!FdpZFtSpDt8)NI6}eP6Rq)(?V+;TrFN{DMH*tCrAOR6Bkk0^+ z&k999T5S8zj>nmV5EH7XcRYZg<&%&N0n94J1Lgw}sT92)0!Yn^egGmI&dsPzlC`b3pNK)4cVii#Ts58y$(Afk2=Ks2Kyy)xcuL4dX7 zv&`C!g)$OF8F7&dqy2si|rBp6|}&mvVN0%eY23(?jS_{>UT z=A*AoH~>KVx+wWit3QTJvYIr`5Re3Fkx~G-@@I2-0uTa}g_k|gHj)?OT@0c4@epPWG~(nm8C*1`I`jD>YkRuo=@KR{;pzx{<%C_v8y~uO>BWm%Z(Q5@!fR@W#W4?t&_RgA&D{1IV{r@! z9Lm~riJLg>ZN$vBOOmEZKfd`UOqxQ*oQ%?BB?S=bCK!r>^#bLsX*4jN^pXquNygqV9>m%8d?DXX-Q?10s)wtUM0YcS&|ny1nC)J>xu2szVEZ;6 z*kW2-yRj`!Bm@SA3UG!7(U66?8oILmZQmj^hzJd+yU7g?!+7IIE?<0M4b(T7=fViV zv8zhquBcLph!{W-xppHnfF?HWSuIe!dF?obdS_>xuA~?d^Z@{n4%yJX2IoZFSG$KYr*;!2X$#E@y*mb5uF000OK2UQx|5JTIyNk7eQ zIq5)lJA5w9ZA;DA!w|!K zFVAO%L6F!4>1v{|R#!ShW`QOHfnr*;<_KVz47dMqGmMQT3uNWZYZ4MR00xtlM5`;> zWzCh2rPWc80Ht)yP_O_uA%%K!`vXG>F+#Ok`Lrc%D<$Nv;D*lt(!RYWi1*Y?TzPTw`yuu?g9bBAPxcY zy*!`gh=>s-WP@xkAMcDMkVSPnO;hEV7p|zd%CeSYEdaQ^ zOlzoVzwLn&>|EU$4u*z|%ko0n^aUbqzPhxMhY`AxrKo7drew4QPsP=E zXD8dgDGWd%04RwN0JIpO3}%IIa$dBFQbREBKBtIUK*J&xx4Sr>LC;}mr4$#CyW5@zXQ*mDLq;!kq zXq(f=kRp^?fI=)}0<6A4RaD2Z+Kn_#QR!wu0YO((h4!;l&%KxyICn$GyE&H*QK*MN z5BY?uP2NtD$WVKDP7CZ>gGfCDfMl2yGq*V2nFVTr0E=3(>zRgHPB`t`_EmPe{MGW1 z7Yveb!C=~s767{W7N)GI#m$^<(6zH`)I)_06w@MEO+NY2 zmn4MXv%fd5h9hLV+18(sh-AANMp(STdV-OmFe1cCM9>R10ER@E%iPVJmQB!7h@lp| zM4*|&AhdBojAYXmai7l$SGYobEe42CwLC4M?I$hxJI|PT^NX8?%+A%Fhpqv@xNEV4 z5RmPL6;~=1vDjEUxbqN!F+YKsIrOI3&47$0L`;erxz3q^LNsV}z_PLcGVy7PC7{Rn z^jdM_CU`?#RAD$hKbWOD$VGuO0TH3p+?cq%hl;bpDf0QXFTM7VVl9$ox`xn@afN&J z4;=sm3xrR5?YDmm%L1IEdUa`%8Zr{#q;v!bHLC?DB7*>E9u7S;ddGq-K1Hb$m52%? zal<-l$FVTOu2o1tHb<*1GQ|_4DhNta?EJIRMx$X~9H9ODl6EA`1s7RUwtcHpcl@ z000)Ka`%vOg%lIoVf%x%imR1tBKp)%y|TECd2#Xo^Yvfe{}+GZXLokSs>GA@jl#SEz?FsV#wu45 zVtDe>4_?-1CzCzq(mZC)q^r^6R2MS6or_1g+j@%3?3aV|yOZGP{t`gvG0R6%->n<) zbzKKR;Jq(hnSVWBj>3?_VHg7;GnX~XJuYj`i>cJI9EDMj5HSdXAPAOEEL+=q<~`4d z$kqRC`?df1ivV!>qn8os%FdU9rJy`0m5tSjI*0Wk0C?|l8N~~D0EAj<>QJy8P=|u0 z01>_SA_55FC5%GwAR=YW=E0KbC5Qn4Si;rw=lFJ6-YvTwG8pkP;vv_EAOP|WVS&yv z)OYKzT)R5ECo-MXfkcL$dFC1PP~YYI4?^%2J#GMiOIQ6>F_EH}6#($5pZt_xg1xWr z^&*q>ZE?E@kkZ7KTz4Lclp?nrEIU`V9kmsWJkG{jU%sZi(b4yfh=>m-D6o)c7>~!= z%yhV#BD9+TKo)ENI7n~O6h$`4^L*jjYk^)B^#C4fKVj1*w6&ED5nX=q(yO2UEQO@& zdYBAtY*o-i50w_~IKMQpq|X;s!3~q4*u=4khjzHon#@Z_gm!3^t6uD8%mM+~enNA3 z>gpgL|Jk^h7S@`$Z<~t4$hHtJ2CgBF&<-sJ*aQ^>MuUc`VhN!&KBtppHF@>-HhChB z9Y%zRn}58ub#<)UtC7aJN;NZ1UjFEh0zhyIZ9F*q>#F#x$@KrKR^O9YYr-JJcVN&# zL|nopWe-$!{bN7!!+0v-x9hjX%iRO4e20RsW=yhas(%=;OBQ8qm1YzL41X8{Y z`CqoIrr-GGU-|EU^;h|?it?za4}5<{P?BDh}va{K2@N{aQGWYNK@=mEr%Rvy-_hnHZSH*>{ zjcw2NmZP4vK@j+5Kogm`XS#+STjpB)K`l$(dp|3EwmTkwWefl>{gapKyK?p4T!jGQ zUfk>UB8sM8pSA>XgsP}Q3|mO+GHRN$u50goUDt>hMUjrUKBFc}3a&-`13xKd=}LO; zeMx=W)$EytzsdfDum5dPhf(K2CwwaO2Q`0_h5g$3XFHE|ve|6uZqPYp>IWbvg8D6% zyIkM#ogj=)+j7Q-w_Wj9v!W;f;HUqKpEZw$0?T~(4LM>w33Xj_UIZq_cMv^>ldm5P zo;2s)m-r>W94w!_^n;iEkzhn3J~)MgCr$jWgzuE)ozkE1;fZC82!iF}t*LGT0NA}T z{_x+sm^-d+PXs~{DW*kzuLcC$vtbYhCj$H4K_~Es;>((KTzcK!d-`_pxG~*iRur5U z8j*SL`EQEp*QWk~Plm~V`XB#Czx*$MEw=G$vU+CajCsuL?eEPFW^KjQgNTF_>Pf2y z2{V@m<#aY(!li*7JbU`t`d&*%34#Csn0Z>d;8dVb7PpJF^Uw2Jt`;xi>mwGAy+^Tr z0E4c<6Igu1!vk4<&@t8oC!j1!FM=5KqE3gF{oP8AmH_~}0C&BJum8H-o8H!)JtqnygzyQqdyv)LaF)S zF4y;JL*2o@2~ueU;6dm`gLmu zYiHMz_gZ{37=LAVf4aX2#6;%hd&DS;f*=r)@~{LCA%(GtH$HIj!#}W5-e$hb-g`Zn z%ENLFnnU#_T;cGP(Naw3l<5Q^oCs?#y{Kb{R!S-V&k4Q*xUA7c_1$_lDG`I@Y}&!F zye&;oEgy-O5F$UlQ|)~{tMAnq;BWoLuV1;c6P!W}1OFgMR+DMv#7nRg^q%Z_sf(gm zJ^d`-IoQ3Go5y>>GMrq0;e*Q$d_0KDJ6z0i6Loq|_2dZT=zb7{hPt{lm`hfXo&XlV zq4ssK6vWF!y)FvWUP{j(;3lr~NGIw<9qIu9*IY|o%!-4GLkypNZteVNZSbyv+?~N+ zgK*o2>XrZC6aJ(Z56L?d02qH|_v)MDx~_2<1w_}rDw0G|)QLLF!Lr(!U@0I&Q5Yd& z2Z1_f^7TD;`#{%9c~}CpM^PArQj34!i=qe~4eAHbi^J#?A_fxF{@<2MCp*2^c#+Ay znUpnr)6XVc7VMe9P|fW zYfNMSAiFVLenj55K331+Km7Mz-v5hPuRGYiIo|u)UW+x4tsx_SBWih6h@%(x-~p&h z8U%r!w|Yy}-MZkiD9W-dxnZg29i)Pm8`62C(}vk4ul9ONzJnN8LVpzSosz%F$Ou&7r_dEs-}B{P><`|z+I!L##X&LKTl(Zrd{T9)($iKuJ!=wJb@dee zVXbkl+WzvEg00mb1z>MidJfwNre_CTEh}zs0ONi52Aj~B3@g7sMVC|98@(X{T9Rr^IVgO z4Kg^X@-AAVZU6ui`AI}URGH|ji_4I~IjCLru@(fy-~H|1`1e2gpI9`Pe&ljIh`ACC zX>B)_nz#OJ%f>eDCn8n8pC>DcC9BB$AXz!1l~^u0yX7wZ{flkON^2Uyw!y8dtToBX z8LmO&$reTHMff{}*8p(*d4`86gzuQNx%A?teCDz@uQdn?Sp)z=u>F(_s!V-5b|P)| ze+~tcRxbRZz+T0bzKVzh#0>+&ac!R@N%A~rW)rm)`QvxBcx)ozZ~b)--&P!d8eaK{ zSMr%Nu@Qjdxix8l__2*`mS=H48J!yeKz1Xu-)AjZL=zaq3XCPhh-j8={^4tve(*9_ z)9NvpV+*+(%jqgRb}OUN=;a@O`8!U)|I15h(3`Jsj-FeuK$3pDG^7^OP13}&NV+;# ziHMW&ZYH%LqOSL%D)c~Z5je**Hy6|^g0|UYy92NlqyqpV`njL^x&J@+|C!(X%y>Nh z^#AbbU;G!pc=cQD(L{q70P?+D=OgJSxPsI}Z4I&@Zm1#BKNrnA?*EgLhl|q}~0>pO<9`9)eRCoWi}YWgQHIQ|OPRzE{u6LRZj7{+&zH znbR|{_qYw-;m@V3wrBlG-|Hp^rQ@1)AeMt=09X!|7bhwJfCuW3x)6iJrs=sLK)zG@ zQ=uih`tE__s=PgAu0^EtNFet`8Z67=us&>Kt}s$XCmm?1rTr&DGDw~o4*Y{9xDU0j zsYAHj79NUhQq~LrFMsTxa$OUK^7c3Uk;e#oPxkh{zPERCS40p)8`LkEqkIeWh$2z^oq96$6p7%@e%*uNP?;OZC#E?Dw>q_5g`&RBrBaP{r zM5Kt^}6q$z-Mu zs77I(sH4#U*Q|Lr02mI2y|||{2LQO-CjRueF1Tc74Df@0cfGt*R$NM1^W9RohLZu; zT+F5bV7fMn!ir0tGM}h}2LS-Uqk({j$`2Cz%=^v-CzfCP;veV&l6&Hhh_-M zF*DyOCT`*mn)Gx!cvshZpY$z;(T%+uTyk(g!c<*)GJ5fhO>bee=f<=B5mr?GC=^A(dBBGgEb&$OGWmz)w zNB+SJ;{DD2?CsJ|?-ljksuOjV-+^!_ov7m<`0}<>^@y@y??LG0yX^uD4PtN#JC8(l&2YkZu#>-4!2_u!@JDRlnH=2C zPo6t>PCQ}+;fZCg52lm6mO3~Qc~~lLAL2U*Jl8dI&9`pel6&4i2>$d>wnQWuBds5$%*v9X9`e`?FyIP{sh-uv3u01)?L zt~JQxjrHfpySuRDmx55d|D*qPOZ#*ABY#jH0Kf;&KX3aspA_!4(;Pz^374h!UaM>& zhSZ@biq_7q4F?}JCe&SRbMRCs3POn3_9$dBI~`v*Qfw^rYAVGsle;=Po<^CXpb zN_?WT`rfoC59+(D9nIjR1dj$Ho&ZgZVHBVbA@FaDPrv)w?9JUtF{|&^8|OEshfRIA zw6P5_Zv3U4Y~WNj zPp>S`;+zU#Z6d<+_S+bW?OQiA>Im&b4>D$<@ua7p~zp?%1Hlj%f zC=HIzC5Ft_8`oUf7EF?Ua_xpYvyu{qEDd3($eQ)@8@6xb!V-qqBRT+DIini>+D-=m zw5JAPh!LuEXgNom;>U{ z+0!E%K{3mP0QI*uOd+*@ItBt8+lPAkqcq#gmFF(CNG+pLn#2|WlD-w;d{*4t&t3VN z*5b1~Yim`_L#JEWcUmu1h_xbo+E;7Q(I=v$pA;oKS1npDG4sN|Y)2m1xYA|2W81fKzzUoylo)c_0>Ij69RS>P z(#qJTsnoJ@cJ0cQw%(iXjZNG-1q;?H4u;u{%*~wbTSTKspM1i=0s#g%7=q_~!ql~E z=Td-mS2pQsi+Cn~^V*~-)Qy=mEw<-ahaH7DIQ7%By_^pOS6TrL9Ze--xVCW}ELN*J z>H2UXQ~walHg_vqM^=D{Frs`f-+psjegE&c_5mPSNvMafy}tP^`Va(K-9$tM`l%W* zd$9n*&1iic0Gf)iN5#}_y}rrP#6S{)ww4^NZzOR70E)eAzf1dL{G}bVGjPV$YvbG}0c zz6rf<@65^>_$Cxm4(x#MO7YetIh~vzonJays_)i(=fEE=FM~g**_d839BlqCe>9uT z6f+`TYN_?eZJjv;om3sQ&SRb6WKa}^N^llMp;BwdRX&nI(!o$iUz2e~RaA@SbMP=n z{ruVocW>;v!j*@m9tHpq1c4R?f*>f0LPXZr*Zn=Nzu`@U_MPuErst3R{Uw;>c~f(J zukJl&dQZfYJg@Jv?I#Cs%@BgC*LUiBbr?~7m(hp86DSAVd%U;%)jbhe`<^s;&$%8M zLtOyqJQ8`34leUuk$Y@o`~3Qb9Le6-oCuff<%nyk1B}~EAtIWaAkv0%U;uPhI&{`9jGuj8UvMc)toL`Jq)lm!j|h&aB!(^@TM<& z=3J@3X@fnggqxaeKT#dxo$K2GpzwDh!(8QAmZWJCC$_t#^x)K*MvyDf%DXalsB6b> zZs)U#x;9-&5%YXDN1Za9Ja_J#ebU0IFgOLh?tlzp7|dp~y{~2gb~=wlWl@a3GB%GH z>YCjf`S{=L0KoI-pI?S$dZNpBN>}pW=`{K-@JGwj+j%jYGS~aleE?X*FW;7{34;)p zz#sV#!}^|R^KdTt(uXc(U(KjP$uN2L^t0#x&iOEaZ+rvPiNs|LmV(x(bSMg=TIw(g zwQx2&m<3BgUDr_*wQ0EfE$j}LvDfXXcFtrn0f3MEy${xRtF`Bzz4Sx>sPkB-_hfJ9 z+Mj>rdKN~3?I*p*jK3%4ovOU;)E63!o+s=q``}R6ABk=P5m|zuzEh5GWz4*{{ih%P zM?btAEZ4P^cc$Tq1F1BoWbEDG`Z?iZ>rJ?ZlDlH3OI_1^y8;OT+w zcBi*XYkI+{U^2lUNmX(=zIIi8*~JN*B!lPIHvqujlk#@4_tvz&?~#Hf@0TD5j_%LinjV6@XSf=D zS4X_a0q6UBgZCz#Q=O~VcSIyWfO={0^x*!9pyE=SRzI62)xEa|!z91HTl+dm5_}YA zZ{_B(UaxEOzpCU2f=7WH?!2R8$kzAjfFfW9+YN!{S})eU6d=D<(f(c-9jHmw`ZzHV>r-CqnY{U`#x0u{Md7}m5C`KW-WlR5s!%SW(sdoE6vO$ zmS(?MJhirS(k=zz)Kw^~n8@+vkFNy?(lv=*kTfYreT17saQU6boD@ioBwQUC8J2?& z8_^G05BIIwu+Lm@zVqRd?Nn>6n4?r=0ARHtEO8CoGpDtlo3QfjS<4*dW#$B94k~qt z?-+m;qW$hLteiV@SdlnaU`3`br5K`;pwAn+nM&kyEC_VycqL5;Z~%S#-wxX+RNn- zd21oC8!|>4zw(GmjxaN)HFC$2-NuyU2+OdWY`T1^t(yn{GqCKk$<3I7-T7(Z zyLBEL29S?oYTf?nJ$N|F4r=YY_v+Qlts_`XGD^;!e*a-9!WyG{G}R=`y1VACBqwtG z1!0dICD3ul2@xwKqR25^jtz;nxo4Nl<%p_}eV#ljx>*_D7ecb$GM{oQ29cQss>>2w zZ3_U6&G7QupWiIq1p*pNiGd+1M9?|Nz{Uq=-Lq|)b!6^Y0+E{=0MIkzk1qVmNi@@Q zYB$u1uyUr>q!2)F7@07hU_G<4=VS-~ZvF86v%cf{4yoHE^mxUW1(x{b%@QRdg%gUw0t{Ywi7mhgcLuRS4|=@_pYMZ6{bx>-pmYTYW_#guP<#$%7{`#$gx~ zsiG)`&%*@EsZvul8Ey>|RD8|37VGs{YudbxSjEKcfp_IZBCE*#Xin1zY z6s(@sN+|&YckDSV? z008f}=QL619q*OUUvslK1b|`yd(i9*kWw8{AZ;5@&ttzW3t17#?dLw%$+1>2sqNc2oWDhX5IMR%4!|*Z#3=J3-$Mzov zW+2Doz z5xG~fTdo5HGAk=H-WTV7_uO;0^T7jqU=QqpJ+KG%z#iB=H}(An@WAf7sj;sBGWPX; z+KJ-%-A3q1&dJ*SY`FcfWO3Y6JdMWVj^h;O!!W2DV_cEQQ>tfzU~?d%2S5Ns!bg=KE%i#*>cY6z79L*OSO~?HGwn?HOB9|LTlHBPT6ZU{`__`-}78jRl zTU+zuKY$Ew!UlVUj@AetOH%{0kJ<-cu9RjZ_IpM zm1lyW(U_U3|7FK1J|f~|@Vx#1gkkf$5L^Y5#S%5R#6Q<))IO6W z3l9g~y6O!Y7_=&v*C-S=@%_7Dv-$0Rm?RtD)Vg~^M6Z%M$`Z;P7fYk@K}V^|d=TW% zHtMsV>9;Rj?)2xCK}ZQ5lJ=z*_4;Z!7`*;}CDG~|hTgRdkt`m4Ts+HsTE*2j&g2*R ze|%%(=|9-%oU1#IyP%ZuMPiidiAb1eJ!loZJH1eU;~#0W`fUi_0=WaMv6D#0##49R zIDg&`fAMSH`(IogoXtCqEGVUYkpW62A~Hylq}%kot9P~j-ai*v`xcn%Ko(E1AD;Id zp_iAJ9b zTAM}xPA}B2e^Hy2*TGx?ZUL*L#6AXzUQ=cM{c@H37h2xMXG2q{iQtOFr2I^VA35c! zqGyEGhUcx^)%wkUF0%dxn0F@NIbyVaO_lji7i&1L1)O=Q>y!$Kh!Tl1SS(LNpc6x9 z&2_*2FO0c!MI^iqa&?I3E-A66;&G1#^7~_z%1e&#&o9r+Ec~_2%~$_S5Iprh-#^>P z=Vx5c%OkiLV-PWjI2fZaCa*;Dr6{V^qUg*;Xm^!r`(SdUY}3Qnp@)3W`=sNjhaOVq zp)d3Xk9{Q3GYzc^t_Y-!r88eOpb#+%@}a~rHA9&UGAD^9@yXc-fcZRvS8u@(sQMA@*Z5VMCz+g;HLB0a>7t7_OD$>-(BtGdV zm2k-6wc!1*<2sifQttb|fOFwxFE{TirO@Mau(&iJE;tn^E*vdoqWDP5cC-g1_VKf)&U|mDncerR-npB zQkgSma|K!nh)?QeI~m$Hr*1Sp==k2nWk)W(R;j=5<%LtrzV9o7pihzvNQd{B(a2~U zEt>*r()`-3K=mAS)}W^WheMM3)mL9tozBJuIRDFz^Vln%SM-ylhe$YlD~rW`yex@? z5#b#LCht0}Ye;V}^bPDoqDfY%e4ys%ihnPP-}eKkl5b3xl*CywTLRm@lEtF|?HMVY zg5FIS1f+xxf%fq0F5kQ-2wkbXa57qo!{qK9Zmp}IY_kYkY zmn-yo8+5x{#Ia2|v!tZe}oKJJxtRjdw6z2KOxVZa`#5(d^RR4&ONYfqOStKL2zzpP%`B9G_jTR!^zr zdLohxSFQNirI>(mz%ZXGxaTmGis3ZI`(%iL=3M97om^ zP$(+B_z6X24yX*V%&-rMmY080Ie9aeFTYTlUzk;K+)F$5j@&&<-f0-@&jEZlj%S|{ zDbz?wGe=6GG6xvdSDg>LTi%PybEnVw$)Jl)Z0cjNWOMjzJB@cRGyLd|uTA|ELVlLC zeNGKAz4r)RDqX79YRNAt7#Z*~>Qo%lt8=_0?I57yK|*blP{(+OCXREYo~3DN4tr&&$a?-a(-oQG;$RLb)qqStG&vJ&Aaq0=4EYqsczU4keeG9Wv0Lz@?llV^LS!FAor zMUmWXk#IoTU^UY9*KiV^pnV3%tIQePIqUs<_EyS-rZ(+}k2LB<<)8 z(zW1;6Ec8AMO8nz`AY@AbK&-#>vVz+Mr`UmX#zM2M;4F$9i?2?vA!u}@U*hNdDjy< z=+;rHfzd(~1+-c%dc7{h5n&P%1buYWBQl_koy}*-oOMt(QUc&gRC*Hf--NJ5I)vre zBO->Tj*=?fUZ0hf8*FWD&<_U~*(=qU78$dqNIRIfB;wv0o@r>1jvT8)@VhJuJY3gf zYjcbKASQ~kQSczqdEB-~F;^o0N$|c2x;^xbzn5NP_Z~opObT4dx%pfR?1hm_rlmdwm(L`~G(PkK#Cy_bpsNEBwn0ygj z+ekLSw%_mau;3!1=yW>tdP&-$Otzt>r5e-ndb9n`ASk75hjKVT2P<}ClnQBv>+~Ws z%;U59YPLh~HMhuOvA)R_!O1>*-wC1d(iM|VyF(Bdw9ck8`$gxy@CZtg^^M(Zd2gj0 zM(mPOj2KN27?euaDMci+JU5J z;_RCFE=;>Rm}AoS2?lKvQS4mV7+-UqM(lAc+27fQaFEl3L!W4(s6&$2W#dWFIs1%h z1Rp2$4qs$n+xD&}bh*5aQZfC05o2b3=di-^z8 z4aV`*#{MiDea0Izn^&fzi-BQeox=L>j_;bYyp}eIE67bVq~N1USzg5PNHpR zX~*w>*XXE^+2N=?oYo$`zc<7(o@!*DHy#bAZE9>sXk$!rThfy|GWJFjF}a0w#30aM zES^cxd0M(NX%eP^c<7tN`esiY+ZNpwkz8NQLk4orP9h&=l4}2LhQ2WoOvm%CU$)ET z?npj=X8>KZ^9+$Ch@FhV?#P@?HL}~R_84-gjr89ZN$iBndfdL*F016`ZFU-ML(DQ*yb@KJ?A>+1Q_O$@(ZVI!SEj zvcAdYQWI^n`yY~ic>b%N)~K}Nnetm*M>R9;mG#Xe5|7U-?5VsE2x7dM^S>QuZ{GEU z25{Z(W)NKYnIu_XNr%>Kq#h@-jmnb^Wz3on$L9*Zu;ky*iqtX30Eh zR2WY+vfS+rG~urcdUN=}+N75un(W~lYl^$wNZ<_x7;1K&FfB^QW4~ix0iXk8 z;+wF&*x#S#oA<|5_xtHvEr1>20HHLqF}n^PV7d zYR$M<^j3<+{!$$09|k9%o;k1$XUT1emCct2r0voM)&{=6@`lLfH$ituhxF^fJESSc z)NNgBLC`cN&fO4+Z)nNC0B&{IU~1=xk?mymdz{AnxwxPb0I%)78G;{iaC&brECF7Hnql^v3bLhX@V19I-K*8*$uRg~DZ^0I>s+ z8ad0p>xdyxlstnpm&s&d=RJNmM7Yo`zzYCo& z!Qkrf*;}NY{W=q(;a(O5ZPD6U6Ok1W@0Ij6j5rXh>7CqmvOdt&9<828|0^cF*lg3n z8$&#sOqlDurwAR#14)wpDh%F$;4RNxX&G_PLt?KRDF{{ieFtc3XeY29q*3|ym!WwD zbdxmtT_+{b9YzO$4q=^Gslhu+4c-!2yAIyNkmS?6F&H*p!ssk|59n&xj6Jsc!seHt z^Sun2uaT1ZE{Et`89WJaMXBH|-)~lpQKumGAac@{yG_~XRzhk8Tw$?!t1}%}ut{QBT4q4Yp z(~JR!yfZguIVjG+;>SUr@cq(Daa?-=+|no=GC)uJe)HQp8GK75>I&g1NGpTqDk*u6 zK<12doLs?i${*Ca_)*U*el(6}Uj(l-9QKD1J2Wsb9?eJ-{sx3=l9p7kcy0~x*v@sx z&b|Xr-f=3=YhC#G5YI~~o=~NjLLe{@8jo(I34a|G-w@F)QigAlj-XdagU?hv?^QxW ziaiI^9LIg1BKM@2{DQ$PfGCFuoTw)f_lN7u0jYD=NlCLt+HiKJd{i=9bASRkHP7N6C+_yKV0xt#lqQb}FJ zh%w16og_^NV?o>1ovk5`)#1zHIWn0u=7XwvUg_y1DZBux;=1~j>#9dUd}HDvo;c2Q ztR`uD-5uiD*bmQpnb6jTM4~!;RhWDT-6oDUcRp@Zo@Dm0Oks$_Vw!})V`m1>{7_R#FNF6C%p+%Chcgs;WO<`>J~dY`n~dx zlfh$epJeew89bKA2g38ah0s~s?=#)2Mw({tCOlbkdyF>L?nlQuEFQbTa40;#8wvfu z4rY@^jPIwt+weTF2ll`o*aLfD5A1Egq_5saMFD<8Tan{z1 zx$@8AsQoP?y&I6&t<&rd&xtz8YMS)BUNww!t`NIyYnsF=)imi#lHBeY6WFr#_q3xm z8c#Z|TbT<2Q;kE%ixbxsjPm^ft~*eU(~~e<>uDX{7d&UrUZ}QO3v>N`H7DY@Y!liZ zR*EPVwyG1|nxfg!jr2EuEitR#gzz0Ow-^!o zK=qgR#WU123B%%9b>w0iD)Yyvab%! z0SNuyI3*gFYSjL2%e(kYXo}SXYIfYw`pw@ES^v*qu9BXTvM1Y)?4VTBWMR2bF8`g4 zjZ2S;R8JpJ)1({f*MCi$mH#xvb9;#ASSQJrmzN#KDb3uz{pi_Z@#0@?w;z90slw@l zX}$?ie_sfc~^E6 zR#hkkN(sjFjZ(Ka=jOiF>vjL_7YD($uaoM&+-4u$mp#uzd)_Adem0ar*Q;v$L}li#2k&9L8wk*i*(7OHnjii=vt3^x{F&c=mC8pP*FAqpsVBc+%&89_QnO=77^e)y^Qqjzz>t25Y-m5QgHlbCN0ycQ z_-n_|G$}*vJoL7pV}_4EQYU-$)mK#zlrHr9PyF)w`p19J_aB=(hUQB}q-l&v?0JT4 zM?0`R^UTNdaXj-s-n#W)|EVx~Xj!R}m(LfOpPy%W`5X^Fe3na><~Vn*!Q9*o#iENb zLKMYl4O;s{O!Xzj)+V%sW9YsBR4Vl|aQ5%D+fRNhj^}-m?7eIO9P;@b)oP7Xr{*|w z<}3>fr*PdoVQ5qs<;vxJvSeiKjxl}v>;Y3GGA0swd3o8%=TBc$>fv8@oF_hfJk1Wt zxlZ#ssql+G{DVG+IQKi!G+90gO_MTImY}l%y@Z{e?7*a+nYmCc6pEi+U;o$-6$(!j zPe}7ca|F#7NEN5C+xd+>>C^1|uX)-ylXADmkjDL9j+ zY)X0L^B%=wo<^h2++2yXXIsjnmq`nIPTSQ zx$5WR`0PXXQ1fM(RAXhA?83M``|K%vQ4FK(Z9iJ@;b1KD+r7{pj>%k-WRJ)YuMh z62}oy6wvK<2?9+Rg6}I9778pZ%;NhM2hPnxy#%Vj9ugV5)}fb|e^T{($wIFDLUDd! zR&8#ros?$B)3Ehzh+9K2^BkaXNvU+Hs+9R9rOqzh15J}x72OS?6OeW?yLLXcI?(d+ zPpb8GeJTjvciH!MYQFS(=GdAqu(b)RAt`~jNDsO1xpa2*%?}lk%8w^WwI)OEr{s(g z2?@gv8yhZAA(u;ISP(?CT3hsbEy6G$No1_%sjpyR&U5Q?TCZ*i<_sg*SG$ZfwcT#t zex_hd?kBV<4l!l#GuWQvAp*u2!Z4)O8nCw3rBtdB$1ZU!R4lLtyU&s+M4)+-&6 zRzTXhRvA;4-9=~-r&1_(KHr$BpF3gAj!#XYML~Lx*a}gZ?BL>#HqPr6!mJ=s`E=>=$+4MzV!PE?RJa1cQ?i~U$OyUQjtQ7 zX{7mLjM@+ptNCKrU#G5mXw|Ls`j5?}8z*Ts(m0OEE~tz5ShJ%@I&9R2 z&Y2n!gU%wZJAXelO{%25ej3!*m+yn-OF)_eSxJhCnlH~i_d>qgee1uwa^?BC zBw_nmA`r(A1Z|XxiK38TkjHTqh!95caN^o$Fz61`w`@wF0N;t@>KVxANQd@)6$Pwm z-C%SHB}?e6rC8&b;Tm)crG&e813H~H#u!?yE>RQ>|Gwxcyqu(OY_D$RhYv9@fzaW= z2gSYC>}Vd;IgzQOq;@|vP4c9Kt~07ww9A4{9aQ;#YQE&zBWJyR`%+P){Nri0q>&Fq z5~67IyeJF^9A}$Wkr=cI3B!Oe42CNlliD`tf}1@Da=`OHb`ov_PWFb?&fhb}VoghJ zh=jD;X__(^bcWafJb%X1uk^I)gy{-}6%MlH(qtE*%bf^~x!0N$Y;b{4A;MK+cQQPjnlG`mqKs zUCH~PX#zVNYa)Tnc8TJ9uKAM1w1;IsTfFPnjqwOW2c@!~GsT$V&0d7h7E?Tl@*pzy z{rIFXYaYfpgkiY7+9DD&s{L-yOKq!%IHW|jDaY_##;jKrw7KV+9fz>AbgoE_-82K* z(Jw|sWj>*>6XSU2vzW3~K#xM~h;-14*g5D;>$oY=MQm$~`$i)~I#A9^G9?{^2}7$} z3~}-iI5rYZi_X)A^YICoFx-IAj^F<-PSW4s8Ch!&UzW7vsRryMQas`N@lBuY2%Xh5 z8GoaR;j&QJq)^xx9yA)SQ@tOVt&Mjw>$7O+qbTd6sVfNpTDLm={_UU9I$qtK=Gi~D zpE(=pvbD9dWVX@Sme;lAvJY$H<%Rd_6w`3p05$*yx);m06JzXdngzM-++7hmpWhJI z)q}LKc+Qy~ou{Q5d3I=aOj~x?W#!ye6q^i^9NHAv-2f+`X~GWKg?%(lZVQzA zuw=k@*i}kmyfbI9lEIrTaXV4-B@2A2pEfstBY{8u+i7XwWO47bZv3sr@nsKo8PZDr z8?hl2v$Lb`+ips24bxnov{;hGHEnV@9w@WCiWn9{6_YCdTE=fNlI##By|d%Q-FExt zFKFFgA62Qej-M3uCjBg17kQUvhb?HbFL?l}jV-CnYz*#`ritCim>QD8@K(3mz4LST zSo6hx$gGd1lBf((nOj{&&y4&&>6PthW@c{n z9VaxSHp>Q-Nv|w+`+K%BSQC$1u|wZtu+^1zC@$=`~dJi=_Y}I^A zo~cfX^?Ik*zx#8>tgYTdO%t0}HQ7hgB$cG_{>|LnmAm&?^JTzDG-%H;U$Ndvc>8Ar zD@ieFi#dyJQi^2BY>$w%fVDuI-c7;SKd>Ea-$PtV-R(xv?Y|(VzdCyVEI;<@Z1(r< zxuF5jij}izJD1I4v$uVKY0Fb;Ycnx#{XD9*e!`j^8^d2{G14>Hbp$YL8ynw>l?uKx zGxO$m@1drNJ?wHYO%u1f8HQK?(mmFEX%2m~Z>R6_j}zsE|8Lp9aaYkcqjNUd(PJE2 zwynm=+y^#d$65JJ=xnmHgB?gXXmj&lCni*1E%_^V6rBl}vMwI)Z1#6!MnzF?2eD~q zJC}`?JNPqk!9FpordmI>R;}{MDDvk|ShJ(Us0w?N0~|*z7E7T{dRxl)uiPU|lg%Nf zn;f9<`CxEoSS?@QxCfdqKr5 zd;bbqzeXw?WOY_{DGN;9fhrWrQ5<(Sg1~<<)et!m&5i--R`*rX>pcgm3LgYbqZ8+b zNPI_2;e+5-NaYsT8FjKGvftZzz@C~W*GP+LA4p>*wN_j#dN+&3{@FOrUp^tt7duL< zFsf94ARk5giqWF3LvkH*FQhw%j+#syVMOF?@UhKvvn$;ElF+#UV&@h%2yJn|WsgW- zG1dC~M!6h*Jc|7D#yHLhQ`R@x@3&VphCW)w8gq2@6bXPL}abxHso5F6}KIdBdF~n~} zu;TlziZRYSN7L-+4j+D%Q6xOj;r^Z0ixja zGxKp{3NN|tnNKE3Wf}7BXh+`$p?2&zt6vdm--eFGWXWuGR_<`jo8O*S$a`Mp17SGx zdtCSYk0;6eLr}~O&)G3*w~mJO$aQXf1=ilyCTx;2ze(ER-yY(ci07aOD-=)w)o|Uy z3tAVS0;iVCxlb#lD^oN(EOFKtX=H3Np;sNz9H5k9DnF-n{uxk35D$#QNYkWiOwwT} zd`B7ySBIERQqyGE(Wp6&`-CF*bgKE{`O+1M2N}&5tFdA=O{|8IjiY z$MN7Dk)WGiyh!ZKusy_bg>>|q+PMyUQphEhG^hd%*Y%zRIVU2%i0Gka$2f7;*(r5! z66CR_NQqq}JzIScjg{s-#KQzUdugoL`vf#$+SJ+_&A_o zbc`>%NpF`wvWvs_%l7+B_Y(`e`|*qudi?u)jl~}Du(x8Y*|ojw2ywrC^}rt31AAZ( z?14S72ll`o*zbBf&ZKbCdt|@=eoQhR*ni=6R6?u8#ieSud#=&zRdOPZ14j97N9FPx z5`;Hy@kqgy=27p25T_scZzI~IUtKCNK-~THli4gew0)*gP)4M3w;Q{+ zwV?^pE)hjJg0D7@7yJ$*NdcslKue|4nPM^d6Iws_6NAA+HLc44?lXos~%(=Zu+eK&1}lx{;ZB z5G*#lX!cP79qE2Ozm7n_dZnS z&exvi&gxZy`$D8fJyoDTP!_W;_BZHa35z> z$H5kqnRa9ISSI3BT(|StB*|SC@iEj*#XZ%kg#1Lg%h|6saY=XVa+%jX!LqpcDW!F} zKImU~teTgia$*eP8hD@N_+*?g54-T(6D)qMIkTF(__@$2{Yc+Pu_N1O?VLn76~p@m zaQgbFeGc7L2TeItsZ=^6a{3EfTkm^03}^k_#j9a-hRJsjXR+wN`1?h-2+d8KYY6F~ zpc6?y8jVk@_4Rr^Nh*&eNwK*5H(VkX-3^|9EN1XAmyLNny0)=SdPbnjNcZK~5qx?1 z52$9-)cgIp$3)yBcxX_+qaU7sTE1Ftdc7BNhFWAWq(m}fSSD7cKdKqyRtkl+&zfNF z^25O!GFvNre@Kdr63_+w#sdRU3wUQ6G(u%VPosZ_-G z@=C|~@}uV3QpDEnL3%;f9$q?;6LDl4RtkdJ|EzTR{8RGV@j`GrS&zw?2HUM)1yszZ7L$x2%#X*HO->oXe*d6SkDUdmunjLvmT#B`tt zefRDM3rf{~EDYv(-Mb=gm=XU!8vyt4YEBbSilG)y@K9-|K zk?_u%7n7yf!1Ha9z!c6-@%hfQq{)(k(S=Q=#$ z7_OR8BtA!aP{@+{I-_EUN5t`yie{%)>Z(&`^;SufD0@i5M!Af6PRw%-e1}maK0Cyd zW9NObUH;iOC#h6E=!BtJXt&SJiR8v2x+V1plGc#V7kTKRC6*t0f*}9?+y!bsQpaB; zjYOwN9p-V2b#M_?sZ?%%)^U={qfWNdl!APrM5S?_+tr8Yx#jGUNspA!OTdL8wtcJj z?`N&(9PQ`~X1i>lLX<<0o5OK(1VfC+#!~8q@8|-!g3ZI92Ot3JlxlvTUETV>yaJ)=e`Zb?-Sz5Y)X4&z{*WBrjn&$+Fr18=C$adw; zG!4r&E^*hLCBjjjLrLv^jt|>~;OiWdL;!JIag(HUSz(H0|1O@2ve6lpmV#}log=k9 zid$k-FZ)<+)!Dr4cAF1fE`x8Bk?ifkYyq+ixgFo3=Iq4fkyaaBB8}KpCKXEB4@|p# z%5Ak4mPK5DG+2%2 z#>+YRNmUBRW}be*@TxazE9;*kX@tJO6g`{+RAMipb%}IX&z=Dvcgtw58ioVb*VkyZ zBAi@-K%Z6)g_?uPlRBxvXxKQCB}ttmsj*Y{Zu@_+{XQ+&t_b`&HvNJZDrcNX_C`mY zG*3HnWE7!NtKIml?+=EXJE@Nx2MUEEp5p z^h>rJ4{2sL=~*^PTcw2~r(+erO~vm}$tAf+DP=2{WQ!SOO@VTtK4Ijrq@Fr*>`CZK zzdpxA;$@35S~G~mXgy$keU+7!I}ADjMwk3~U3j&CKSO#;VU}Y$$*z5;e?FdW zq|GIgiqWjDHR%PuBCZydc$4b7W}{JoBSxdRi<|W2GGa0l!k9P?38Oy77ywsAJn5S4 zRgLivl_%|3JL(=E#WpwR*xG84==7t;Bt(qlpX~5n%5lka$S!+kM~o`ROvRS-SGkb8 ziZ{50*71&tDMy%Tx{R*471>V(D{J}uQms%hpHC8VPHRZrU^!^{uzRH=ib9&rEx^zj z^dOxGH5`=3s8aqh8!S7O7*97W$vw0V*jT?q949CoH!EJ2u~Z@|jYfAEDJvbyj0Piu zNOn~+RY6!^zr#!%qtbf+yBZ>cBO4W`YP}r9lB5nexXQr@SS_<`9t|i*QJ9d^!{}O@ zjT}d^oQiNI;8u5oo{lq~8QCGOHDyPRIvF_%*oV+cIW9QMN;&>+!dSCc&}@qAV2q*D z=@3OBQPe@qj(q|<%pdcl-yrjSo>6DB9SqWf=8Da&b#&aPzSCv)AJg*JY>stE%T|{X zL^SR823qe^b8+-xj-9uT-ZCvB-X3^#MU6-2gP4`;I5L*aZjK_Fh%nVYXLl9v1Fqp( zALSU090!lkBg;N&JR;lwx6smOYV9!?Xhixr=|?o*COX-n>Ok{SdNj_lf; z94BnH;ZERKttGXKh(i?ihH@7BnvII?@o@~WQ-s8f%DEh3=&?C{i8K{EQaOV}+lqg( zUAt+I3E@G^u2Ajr(SE&7Kn5bQ*(psiDSsaRdggazxIiNeB=q`St&)3uvTJ7!om#5P zA_ptua1!Uk*_jTdc#LQSbMMd5?y#L@Y{xxw06U?HVHcrCgqgUyaReLRqTT^`ss+LI zZm+lUi(1F)JJ*}Dk#JJ;*eW=-+G&VUNi1rYmdPhdgZxG-(jy?Gh_Fv4bldAmvcZg)lIqI^g4^&JxGIvVE`zZ0KH` zd^<5>OE}rqKJw`6s6I+%nC$C@%kOqP=T7{ShJb zX@c}oVw@8zt+l6l(!w)s6fxQlh?Lph_RbKMG+?K>%~m2Qes}Ru;+hW8G z+x>GUF`hFD9$7iwG-DNdqSaO_@RK)_n8CeDVy|BPvVlK&J*^XGt9z!ngtiRaeh*`O znLNawycsQs3Pl1Vj&?%Sb~*MeQ{-TQ<;a1cR9MB6$OKVWLNO}4JvYU)_|I@(S$o^_yg+bv9FUrld+JblTOXmV_9b*Tcq=x#@UKgL zb1l`%uvyQzYLOwEH;;-&-WYUs?<=urD;>~hq-AnsC%L&O9E6o$Ge)nCuvk)OE0Oh3 zw15>NJ!sv_p%tK#-;i>CD|A#~ECyj9T5WYBi9|r|^$>3@>mx0i2*+r4%*@kBn5IDE zmJOuGs&nV%q8`fp?5)C?Yk`yB5e}#1&pOga^U*d@UMSc)4eUDF*vJ!b!5AG^{b-${#M!FY&xp1cPa?jDy+TXqi#vrYi&6s z2Z@Tunx9k~>tVUt{8gpQT4t0mhByw=#X~Lk>0;{Dt6!CNtJH)}VpL!@{N(mPNt7k@ zK9SHclnmn-HMFl@{jv!Lg=+&Rzur_OoeXI=sfP%N;+k~iW)x%)3? zXRm+H^VZ_gO&BZcVqYDG(htBLJg)SUWaArVx3ZH}zd9CqTqo{={FD%V3j#Ab_id#p zTEHeQH{-sJe=8Q6d%WF=tRCfxgD_0|Uo(>2g;0i=9O8aGLL~9|j!_dM;ytmMN=s(S zt*+|~l2Dl>U|S_w1mZ}uwMQg0jDpY(J-{Pa*Gc+6)iQjekE16{PVK*Y9nDvwz1zzE z`Yt>0i7wyIMbK@PO6JKpF4m24+>tCrR#&b~TI2j$SGfKqGm;b7lbmWyN`}acHHf{q5AL1w9xz zw}L?b9x!uk%VbyurR!Z`<4cCOdKak z|C&g5)tI#v$GN>ClAM9I2X_;XYXir*@-L0t`FGH{&Tes@z{VKxK!+db$^S+dJU!Zd$O z+ws^F*}AJj28v6QPS=77@s$~!f%BsNA|;%m37GX1U$|3Q)|`)cPyk*9o@e*17)ziwBLdyxNYZ!Ht-vA*!+=$Xw1mP(NlO@vADv|Q z>{Z~~!1qX_;)(fXxdEc#S_dC^1^6)2ujSc!PLH%>eUDU~Yu8-tIM;ma38XG|IU$`5 zu8}%ojZx8(ef6_X!h+xqQFO~<6Vj_*6($uB8Et8{NqhP=4k$x>7#oClN!wtXwDz`6 zRtmySO05v+ZSKKnme3`kXP?<8Jw7*S<;5HHynf!p>L5os>8h}kxP1G*%ouL#bxYz=S@yvmE{oTk=EYG z7TvLVs$KCpkv)S0VzD(+Y@r)_*2s3U+oYrE)Mek_?SzKW9mWbf@ABDQuoJCqcL1}h zVHI|EFq^)eSUtG|nw!C6uYJWsEcSHd{lb#HBy4?SQ=zPX?neiEU=QqpJ+KFMqITSr zW3RveehdlI@O(dqr%5yR2e#YxT3S8Uo9t;nIFI8(^-hO^7gEsRN5G0=(2*UQDkpur zW~yB*Q<#aV-O|vJbct?YcAQYWAM(RlQ!{09gNQ;9<1jjcnLSW$?S7JczxKYbSmvwv zv8JRAZW!an=`_zC3ObTJ^Px&{oh>*;ua`S_X(sqnRiDLr$dd*t0Yt9ipInD`dcvDJ zUFz8&U4A-|TF{g1_klC#GJgd=yzKGg1-Q0p=o^rxp{?PTR@~_d>n2^p956a4ed3sw zE2{8VPG;Q>#SNFowsKtVYGP$r&BM9_-8IE}OSZ3|-Sf`&$PD>Q4s-O$#~N3~C?gR2 zs20TUBn*0^>H_z?<3Cm=|F~w(NhoYMJiKmr!npXxL3IN{fz?gJl`!SbCg~(_ta{mW zJ8*>lp*g%LaDU{ne)TV&b2^W7jCKY>Apmy(rVXtwbiM;$ybE8KbiME@_u%}--&jU{ zz00g@a{7|tuLDmQMYSu0Jut%p%Q}GgR*u)doAB>84Byb{vY72?CyME@vQt{}_{2*! z^V4&8Ijthx5Ih4W1`~pGA^e)dKYmkqyAL<|!c9rBY_YT9j%|<5kiQHIxIF)SiT|b6 z;k1gu??Y@LC}D2H+rJg?uhtB2H{oiS`eq!KdMYN%-k{pGDu` zqHtqSIz2FNS|x%nMf~4a629Gn??t2Fcpyv%+F1ECi>f$CmOmWFpPeUqJ`^cNkn4jp zERCuXC~BzY;9>x+VD#YZHSl9Q=hG~zl{mTha54Fn#Q{H+H@x44x`tc=B?DzQeN`0H za+;-i;i-Olb={TpGoOfv(a--Vs_s&Me}~gm)JI<#ct3Zcub(U?%(zhXq3l7$hpG#; zBGl)F$1lN)1)uW+COc+!}$=lq7h+Egw&sD6x2- z2%rHakn;fpDE!x;=7MAJY{G1D*sWAX z#k{K(!`ZXK`?}C;r#M|k`QwrN^f#7K7jn!OLO%Ax9e&|*CWVS$<1SxSWho>CyFoH3=MNNou&mwgsGknafv8LdlC?fCl=`XwsP8KU!#(*M&S zb5>8n7Ol1~dnMG8ItNQ3tVN>-N10ze5`kXuo%(54e&wM!__%M3-yh=0?z?BnWN$f= zmW(ZzV5twAgKWLsBa-@uPUHDKKK685{n?8;f2o=)R*h0lY|J*1vo%reUi;8b%7dH- zl^R^=LbH>e2Zm#G{tG$gA94BLKhSsnhl|DP#pT7b-sv-Esm{&e<@0IB(i)?6);AbX zc_PbTh(bISP_r8HUSsliId7A3Lk4x7lBWz`6wM~|aDSPuq`v+s`f+4G26 zkK^B$y8rP)asJ}r#T8c~=tU8|B-t4$Qiel`xr=T-Sm`>*)hRgIfz8gnm?rLM&&*LS zg}nBB&-tgPr{@o!ee$e0bn&9Bt*)waq2NSeNUz@~2m*pMB~{8M^bnqD#cC7Qw{<5g zWH|P9vH7JHDs%Aj-?z@sEX6!g62dE1tIRJivvT+_)zhaF=hP`FR#v1lGb266QKcwy z!#E}gL*gg`rG%$P*mB<|+EBE@!A6D|m?zJ+JLI34@x8gvzb0Z;VmlcW@G!xCqKKr>J0VTR!x4FGd zqtl@q1cXUKoD~PLrbDJ3g#*=DI8uk3?LoYf`EP;6Q`;9FsCe;y$jd3AW*s;O)kz* zC)@Rs?1|)x^Mt%LvsiJQ)OCg?c;jj)nRm{ruul~nW*vPtpBD_8*xtSYGA{cUS5F?9 znx6I}$JxPPI+H!o8?|yUq;|lo#Js<#SVX6Cu4cH58rH7%`boToVqCO-OC|9$XA5&nQ-@bo97XQ><|fT< zNRnNjw(*$gyjy1zKmp1}Twa|LEHmi*dt&;j^SDiTZefcvRk!F>YBii<5!Gm**4EtK zojcxEz0P*GOD_lrqlj3kq1;R?ye4tnh2nYmp(RbA8DIr1th8s{>iQPt+BQ!VQVPQA zDM^yh4MT!P!)0T`Q@3t8>gG*H-MZz5>l^;njSj7LhcpaPS;sTxfvCZJ1u8<9_FZJq z$sFr-qb*2V(>IfopIe<>J9&FO5babB)g>|y7@ZNYS*d8ZWCewPBG!Aul-|WJ7yVNDjsQPLZ&Jeezx3X+3|c5$02Su zUANnHTa5-=^*ZZ~2AzJNZjd5VITU`)B zHO`;iVk(sb;Si-1K@`z#w-G{6R?01QI<9aWq9D+nZM#FK+a?GCBCF#WD?s6-$A>pl z+D&~pwTwRJ=wz-|V+pNDJte(UaS-~wxKFI)$XZEi=k@nK&;D&hY0Nf|(QTfnLjGpI zD5f2!I3ttAjrBFZ)9(-}xodPDXLd(}9ceYYqwN}b&M!FbJE1y%u`kNWB5dBgDQ@4n zOV|uiQjVQ!jEjc3X$JzlOv=nhN1mw+*Vwshsfk+=tU5|~5zy*(k+*J<3PHEsX1m*^ zlPw035eaI?XX84W2~GmsDVO5~#hpNxE?Yr&usUNp*_nW1TI44@!HiON349PzBKmz| zpx5i+778E)VH6RhDZL<|Zx*eVKaDxyR1%IB29hRo*~64r7*A*bsU&L~>x5A__6RFK z-7t-3RuVE3rAZR%iKK*%gf;X6ao1=FFjqL`dqRC+JKQcucka-O!d;`Y!8C5#F=jP7 zQK{I)1tpSoIbH6D-gcuwuiK%%y-gYiBuWi+uKZLZUl)1Q=;4DiRp4|hp&n~r&9aBq zij%Eu6A3{S(cIpq)9va-dl=FS0=hv8G9s4g&rlpk)Sk>gv{?j z$dh)jX-9qm#BiZ=^B9fEQ78~dNvpL@;yARzkRV5tQB#e4XCwE2SpX1X;42e>W*A&K zy2t#k4(Db_r`19Tp+{4t2%`v@N|M}k$oRs>Gor^he$0GMtaR6oFC;=JDycr48AwTG zgcY7gDy4ptzOFzPD@CdlQ5++sBvQjz^Q6vU8N#$Dq1$0ZoQ41VW7HI5kE|b;!SFuO zqA(G+;)vwUPS||EN)s<+a7vjcE$qHHe9$C`%{FRAp+W_%Fw~LP+WxX5oguc3&3=tN zy5vWi%xqtcoeCv#5NI4FkwHKzbp2DM)DFJ2bb?=`~ zMCep(%S1+-kxUYo;W@l{!%3pEO&n`Fv@d)67V7;T^$3!FwgNXhKcgaDeyU-f=l98X zhGjsiJg;{jIl?NqkXHIABqFkiVZZlYWVYMSD#NJ~kCseHtWVyx-TFHIeWRT6L={SQ zPmDp=KFxe*`)d?l`}g_N;Bhv-=Q~%Ui2wT+sBX`Es2y3%??z2Gat)mJV=gCh=iZ(z zgtVt^1RG)0-}oz~l3so#ZoX`Op1Ru{+RF3TeXcXw=woGW$L*!1SNc#lL9!5t^H>_{0 zvU06ShRM|G7>ql+*jBJ5&%~W?1kegVnd!!;9ge-W?#%P-@9j39*lH!(H;R0z3*G$C zXh*tnWX?xvdq@pHM!I^C@l}IC)z6W3JEiw#z<=o|8g@rDpJgXt8!KHi`2cFF)F*8J<3VJKVVk%UD9+>D`Ii zy~bZ}tEq033BA~ndDLObsG&m*Pl*EbzON}6FbSDdxk&qZkuUe5m;anRX-7qALt^WZ z=E~Zt++(c$MN05?!tY1AF5FP!(IjLZ6`eEbBNclzq}X{I<1153dnB&k?2%3*hko|#RY zgu!UGul3kFF>)^D>mk%bkoM<~np(sRlPJH?!DA~`JEO$DMT{b-UxHuRgu7jICCL4d z*`6<&p|s~w51_7g0ybMrKpueKgE)l$-Ga4V7NzrZ(|lC6aplR}1?tdSPk1vNv|S5* zfUzg0uLejZc=Jw?KWahOoG_2WWfR;qNRL5wR_2Uo&EYE@?H3c98rfq9&#p(%CA_pv zxDD=yJ(6mQ+SwtnC+>0Xc?MKD9veMf54pvl(B4h%qaflb2-l%rhF2Y^#yTl}h`jkm zvKJo4F%k4Tia!n1PT5?$p4>#Kt?z=Wdfdh5cp>GyfI@zF&yzX30I}0(X8UnlKe}6r zFLV{Rv;V5I&yu>wAgaTi0=y!i7K1RKWvBO=z+>-!8$TJ%gH7n)fIr%TYf9tMmE$$0 ziR#=PE)dmII@6L*6k$f`l;WZ78iQlh=waHdr}3yYkM&!Mzv<~f6fr2Ndy~Pz)9!@c zB9OC)%`)zfmq`{CO4!7;i98}J-6l?`aSah?UyHFTpypiGJ&%vNl2yPppFb+n*o|b7 zIg&n?x{^!jKvTms)V}J;6}h~bt8FFN`fnlOtzo5W_JZ-P(Epv^Wx-Ga^y#TT23+iFk;(D-N*>%@7_B%x`% zu!n4T_%A|Ii$IvnyZlgM$SfdYKpc|;ZHRNROE2ERsGS!Bm(vnsG>;%<5j^Y@#b!-dA)swkc7ea&GqE>IUhJ3Y?HIl~}{8 zoeGwbSq0EfeJ-^nZ}d|()L;kU27^dAadbA;g*bpKJ-BikZn$tVg+c^Qn32f%$Qshq zHbgCtjV;9&+HhG8WW#O%moqjVP=R8*2X6)N&JDQk!HEP)rgI1SGAvzCEl9T^ z*@R%z=hAJ(m$%?*qKVr?M`lwgzsZE7g>5Z}W4ICuu5^5ANR}iN0_~%rdJcVbM{hv9 z4!x@mU%I9Eo2KHH+}U{HCVI5Eg*DYMHfc_>n1Y!(_y-mEMWORhy%5|;-}nZI6-2`4 zEsHp2adQP-q5kBz5Xgr!695eJ?=1pmm>`MNv9eb(X9p3mFOluMza6)5UTH%bFd;`bPk z`MyT6%pCM}v&B{Tu;cQfip#~a;*=k=pukByT5X5xEy>kxN}~_0Q2VEcj)JCitx3;( zpc>yLL1n?^gO!wzo^biGQo_jsxMn_`b$r$FXs#*V*iO0I(tY1bO#S7DU1!)W!dwl0P{IY*Wf?)W z=x`(fFOb{`Q>}LwYT_8pxlMG(TFah4EZuDvUAACp9zHDLNu~3Kyfm8@Ngx(8z7Hte8ML$(TqrJhihE9rlm=#D%J}e$` zcu7j0cA%uPRARI4hy_VkbI44oO#renjU==A()%$1R0S*)U0x*RX*4J&fo3|q#;7w) z9CP&jFioJZDb2fri@=kvU|NBnDx&NHNq}~MX{ZgfgG~DB!|GrLkBiP_o<*n0*0@({ z!K{tU)Tf!@IS8c#fzEQAWsr_q;9eOD`{2v=&%eRd3_agC}1|>dpvyN(}+J+{BI_AnNO z*-E9g~@|? zBs&aG&QUz)G3RtCL@BNmh)s{=y_m2W?qG?np9hNP%sj;<$@Cci*jOIb4vbi=;Y&p^ zN%4XhC(=0!dEX4(06Az(d;I^taqO#TBEabirDtGaTkyh@MgH+g#j~EI6#4{d%749{ z@=edBz7FfHlot9>!{q5qdhKdrJ6EOnG%R!-UVOI9&rK&Riv*zr>CG~iivfRmHRWzS z;k`)b0vHL1fCFeegQo@0ohtKBPAHx;{z+2)+qINS1()?*{PR!`pq{Bwcq(BojVZ4= zoY_n{CmnoQ!0AZ(3f8tH*8+XA+e9CV*>|5Gsy($x;Yo)%*`XMvxFSL%3X%9aJMW!@ zen$=YV_#S%j?ky8l%7#6gbpu#Km4o3KC1a)<==l2@%AmI(yL7O z6wiK0@k=v3Ry>I}O4Hf59=_2K( zqW)JN-~4LI-`tkGlMY0LHh4f~tY4o+TyZ!)Ti`#>)Oo6wvQ$=hjv9O^k(FcB7e1w| zE4J!ozx#(#`a5qX@~g=p=lFq@x}Kh;d`$8B_ow{wLZ72kils^Y^VcbF+>u;Pb(-dt z4ATQ8asBjD%p5E73oBRodo$b2%qo@&2+tJ`_&!nyq9`Jel2|C&kh-AlAH#3IE%-)H z@t)K{$R_rE<9TJ7;-ccU54!x@Lk*5q6ibd0ULrwBMWi5gP+djXQnWkJ{WJLOtMHvR zd^a9+vOS5tXFl}#St`d9e(;BS{L0ao_tg~BC57Vxj^j|QR+*cgW`1#z+1Xj9rlxRG zDcm@zEK9X=L~*VG?FOW3IGggmGPpi>5^*Eq_;SRrFLijWmN4TgeeCW6t`HQ9MXED1 zOwY|RJv~j)^+YjADup3Pa0Vm|t@F2A}O^YoNr<^lWz z!`JKfjmOqBS?%$}J~d6Atoj^kX-$)Y8l*Gi*FzK8=T0Inl$dwAyz;&FI!pDE0&A(ffoG&Up=l0N%r&z7BFgwfA z$_gt-kIK2#Rk^UTqNd7aCzVpjASn1!PR+ovHCSs7vZSmO;mPf@=MgspjxPv)eYwM{ zr5NATA66NzLb1qnt;WLgGKUWxVtI8{F3ipfR|w)>PdI5>_2BqAY_tXr*|VU_IR7lC zeB|{OpE)AA@BscfdKd0)52E0{@#Hj3e)Vw7nFr7`xwr-G=Ab&OxjV5hQeP-Bf7IbO zp6xpS?0C6$@&PnoWCKd6&SDe{UkDnqT^EI`J?1YK`1E0i^94ElTH*T?OC>7hGSjsh z{>nPMu0P zdQ?)WD53~=ZA~skk>86V`e8^AMU>N20M+AAIP3FlJ)zrDJKrlo5B(GAth};J@m7Zy zo=u!*PfgDsId$p@HGSel>J*E_&891F-J;X&B7`6n0%_HPwo>t&IXLuu$`5xTzM>EhoQNnf8`CgCZ%}ux2 z>rw4=3LlcZz7EaH!xxMvE;#eq^N9Ky3)5ZB6k`05f8lxHI4B{ILV)WkN+l)c=2W3j z;MnG-v(aoay|q<%LCV+f!q!#YK&u&^4ik2;k1gUgLY|-MapnO0vx>fMWc;&hPV#5Z zBbt|4UXJ+Ge8dSC9O3(vDix+@W+*Q$rPOMYYE@Av0HDfcqpya$o#8{pQ)xS<(t?3@F+wB-KSpQ8 zNK))6-(hK*oI3zblNAfk+$8>4Xfpev!!IB4oQscI^JOHNUO$ev(dDI=nw<6(UYvac z4|bH;l~P1eL^}v5^?Eq#>t0z1=yW7X3EAm*N-2a8_&^EVlQGY{rB|*E^qsp|23PfW zjf(ZVl;{Q(RXlaEiW<>o}Cbe@gQD zO{m}2J>B5h!nzhe&Ocjyp090j=79XOjh4xnV*;6s`WEFgaAusQNu|+nD~^Ne^&~<- z6p64Ocvm;O^e59aDJxB&MXS9=M(LkAkGt9Andcgu&1=5U?LM&P%QpP?b-nYl!eASC z=T7KILOrL>FoWgr=oA;k@P_`F=<_~OK076Q-oemqWOQe)r3q#j&9NNt$ zLDqOKh#8ox==#e(I#N$rc*YK^AM&V{tNd)ad0_tW)d0_~k`zDrKHT@#xOjGp>H%w- z^wCjzgTc1qJ#F^BgsNNO=gaEAHD3-hm~xmqk)tIk{4Q=ma9Dz8dI{DCBmh$Cp)Ama zDI=g4hIo#nE8s*C&0ddw7!qZUDuDll$158ttu0;mVK!^DUF9jxq?F2Ke$?A$W>m8d z;mV{BT0-cfrYNG{ZXw?hu<`HPVh};7hL+DJmBUL&G340i` z%>$flF+b0#9{>OdJxN4CR1t>Z&MMh7Seo^EbM%#+pW2!o6RA(3zjP8aV4FQ>9f?cI zk(fd?G{mW{U@IO}1eyp>B6W@FN9Z5B#^F7t)Fan48SA4Y`=f{h*L)d?V)5bLd zQ!a;8g@Q0QbJ&wW-3m#hc)J{M`VshN*ACW=h$G22%PFT1Skq)rG6Mut;40GcqtkpD zvXMhs>>>k)W(w(U`HbcR&BJzrUAf)VE$c(tb+{gj@ye*9>-t&NKOW28M||LofJzY)@Zv(Ik?ie?_mI6v6+ne(NQjc+E^G|5}fA>I~z z;}K}SjP%V&LVqnHcr)Y=x{9`mXyYoyDC_@60XGiK3Ma~z*>L3niwp*;<}vB zzZCNMN1*vK63<9N|7}E4O89C7+aaiYZ{EvMcDh)^pc;Dv($+w;!!8!OS8Ad<1pO{_ z1CVyFofMNz<}N!G36s8}m#ytg8LT}8js83!ttR}R1eyovA2XHRvx-r*MEhEiKRaMe z6W9fVvES^GR$|_aVf}zLU!?WcJNy~9dREl}+t-VHr4PM)=%2(<=F@uYkVkEMG{V74 zOu4X$6tS*B`&;mt7HsBQc@j(}l3IAuHJ`V8TK~oP)K;C^t?|@ciPp^`UkadmK>jg! z_LR!_N=k4kI6zI4lo13PWm8ap3;y*1YQC7xevjtM*zK~mK(-}nS@DqqObZ4T zG*k4-z^({y12p6@S{d8GUFcs{{OP9Rj?x%SvGIwdAl?Oa3hE2+V;(G;>hzM~eH*)x zY)ob$XtG*vKyU@VysdWjc(Y%Rmm#*TBakggv>^HT0r)3G9~N9gS8v_5-0zk|tvYNM zeU6t?&inc#cI0Qnw)#XDZw9-yu=nX*PL`{zL~@gY>4)rMr^1;z2;V zjVAOq6I9&Xk{~4;DXh=H%MaNS+0P;W?0E(2Fa%rB%rr8pX;vYB+yclnR7ZbvbrZVR;7{ssRSxEb_bmm{fv6); zj?c+b${_{C1VlbMn&Gp@phfDsvAWmcPqN-VN~C9BcuqpF0UIv71WY}Ef3C?vP_l)L zCf+q`qa`vTxZ`Wxmjl-{+0B}YcpKUcMKlekmDYT5BkoJ{rOnQO#x-<%-7}i*N$A@k zdvL7}n~BF6DXAvl$t*I)*26SZ3)C7!S3K6QE51;N%jsbHdY!>#(1~t8o^-ypp_Bi(d{xQ>OgJ+HVxB=dU zD1`R{!S#+$O-L3~D2A4Q46*mnG_lG2E@L!J_Od|H&NN@XdmV1Na6EzHJ~dz3+HKrM z^XC>;LX!P?la?_MWtd%Z_+(M>+!HQ8Je6>~0GSP@Q*pr7HRU6E1(hoN!`7O%ua4 zgXapnXqrrD4p1$?@=m?#R~7T+4p88B@uBKb4aNFy+DWA&1w5`CrqV8X7VF0~Vku8GkRG5NIjOCB3f)q~{~hgTiR`J&5m#pSdkC`g5D zOYU@18X<&TXeMfBL~HYKMS2XLiU-Sw9bQ3lvEXuO7yoR@oq)X~-(mdoAY``jls#Bp zc6eDyE;?DFlgbjAQc#wri9G{pp$ubB)4d6Ui12GNd-du?2b#%0IToipgirn^{xm z&tMM144&c`Jokm=ehF>=z018~G-kKRK3O^_$Ltr6eXw`Xm<%4HLt`h#o6|0{_4=s% zWB(jwD);jsZ7)3g#`2(q&cBcSevi^Id3%iKu|2lO_ShcVWBWU0{|{Ci$ID|ts8JBU^IV%@JWbc(dGE#@^b@nP8KXGJ-jIzVoD@kRq zWJLD=)BpMZJ8DoXnPIh!rdaHfv}g#XSy%lBH(dHc}7Y>tqAN3;5_HA%fFM}P0Khext;xeYyS zq(`#tyhu5T;S&p}7|oIcP;hr6w6YwIgCaMu}PU#j}~a`S)3H z{5^BiAp8oPTmuyd{f0pmAS*>*NNe}1rWl9ZXrl+&SIRBByVlKT9(+ zGgBZsmmI9wUbO_D_>MZ&F-P8Hcf+Ca*XIu}a8OS;j5}s!`z6QUH#o5bUS@_NfwW=u zc-A-duDVyI*H6xsdOU<+SZFsRxIeN>Fp>hkXq`O7Q8)*g&QoIk{iomC1!>fokfoBH zo=VZS4A$igZ}7N83}?JG3SOAPt9$O+FdK~XI`=OAL2M`U_YNi4d2xhz)2Z$9|mpWurEr@FvrEl(~UG@5>Mo%QXgE@#Hq9zu~$YtMX! z_MU76e0z+?N&9XxYVJTFx}pTeA+7`j^2^o%5mV~@tZ?N08-0GWcE&PVq>(H}5v~dm zK|5w*OaxNV&8$5EEx2U)-|bg?yA^`!&I1;c)KMj4Ge4Ss*@x^GvY*v%G>yepnaE~w zvel8nu+&KY*s6$)CI$ig;>(-zM!~CZB2R-)4rZ7T%4d7gocl>!>Z?(-yuOo8bsceP z3!#Lmjmo{?&`)~^J7an>m`I!&iiQJ@$6`$EVkbOyQbqXD)6fWpPLWqZ$i|_c3fw_{gNh9!Bo7qzqLp~c)MshVZN}u zM-*>Z;z;j%{OlwKwuC$`9PwYVv2qCvWNzK=sCR{k+;3Iq;D9mUaU$J8Q$BtV&}E17 zp=}?Qvz)bt7>t&G2ZG@^WOq&X;m4&)bZH=DYT5Eb2d2)=&RyUzAb5*~|+ieBpRfT~jelqOwa&DpzC zq!U-v@kO68VhDVaR#As>?75ADjZW}lFSQB5(9f`JVphs!`svpGeyuiqD>x)?U|Dp8 zdR6r)68`6I{}+C&^ilhjo4w%k^#oJK(@j0E%Y>Z%J@9Ni&5VkQ+Hjez9}5bmPD3Qs zV0F|bFTd`T2|fP1v!JxjVnGrWy&1)u_n%Ds-n}}Lt$Cv~TQ4F*9E~yJYGFixAbL#= zzO;8!{;x%b?_V-S@nEx`*^fHAKZAk!AI2$W`^5e2gcJVmY@}QehJ~J<`!jL~z*X8T`oi8!MtQb&> zya)fa2-#Xv4eruBf*wfM(bk?Z zo?qnj_{I<;$YyXat$*oaq`F)ivJR0dNl186AE*iq3=C{q5BSz&HZ`|)BA=O)gWYI) zfDZGD*C~0)7oDb}4$*uP)~ScbID&&64o|lV6~3ooG(MieAGlNH;4Sk0cl-B?fXm@6&4)Eylg2LCV+L_NQ%CYzR4} z2lagR+eLKa#=+`R`tsV%W0dSP6?a%~5$wL)%JtxtT)$E2_VoGZZH-?o zFe*VU+}x;c+_>S;FdGvQ1PSlt!DjSS7?w>nSIEVMa5FRES>n_ts>|E{Gc03}Vy1K; zjJQZqlYHc)iDSxr*3GzQI@R*6x*X0uV$b!QR4s9Jb)*mr6{`!_TlQ>u!rOiA3Qv); zXvQHNKj4vb?*ZBwg%S_PDq5?qX=h7S#j)J`m6MfK9dmh*W7p1naH^Q#GTWLbDiu`} zELEKGBmnCs!EDey-NT2U)pDYafMpHx=;X?Vi`x^w4`hnJDj?INyG7%qNUrEIoA*wz zjH+E#EXS2ZBn+lE;=#HsP1Kq4afROj+g(&+qYTzk`gcuCfmfdX8gpPS#))g@<6SOT zeGF`iuE#^0Y)5qdP)%xEp5iM1!@eB9i0sT?8ak!<;K zn~%rfmmh7n(bemYA1Ly|xcO7jpcQi=o8TS6avsL!42=*>8$di4?e=Ar_3l+hu1D=N z$y)K@-x3Trz}~x6qLYiHQ;k`^wV<-XInSXH9_#*9$)6a2e27*eTU+Yi@@3iE+mrTL zr(0fL9(HROacg)MFYx(I%Y7AVj@9>@D@s%zcSBLS~OA?dV8e$=TR+Jj zb$dIupf@i&TbZ?|KbqL_`vJOv8c9L5kgbZ9U%AOE+MVcT|I#bots!PpP(C}KlC!4j z5-mpp)QUOEV&_dkn5NyFTf;pjHVJ+kxnXI?YY-?>D^iS``O8PGth#saAn>K(s2vds zRrmwqy>4FRK^F&~=DBZgQ;t4(6zq|`un)E_Y`?dHc(X?>rTe--a{hFPGTKmv>q_er z8@ua*6v_~z+F41Rw@K~;E?4YANn_;Fj}DWm?Bs{OGwmPJFXCIahphWTb&0&n8m=L_ zj1l))Ul#1WqvlR1foLAA&B*9Fz)1M;U^F&N%Ef*-3SH?-_3;d}K%wD1-!7$KUSOJR zG+c&PGKU62b6por<4f)A?Dn4{Jfn=$u69pzdeW(`6c7z+-z1r7K0^1=^%~howLNsE zgsE)%(fh1=@RrWhkD4SP@}3fDP0)=F9i2H-B2v*`m+$X2d$v7xB82`|_sgoPkW_YX z@jp8Uw$ytJcsA9zEH`OB{CJGXhIE$&^Dshl;CrtX(}9d?r_eeA4KKxgKDOhdvd+~} ze~50bcNIW83bXZA7EyGFz7}5ze)wu7C!bDDuV_J<3Jc}e{koZXeec_AeXf(NnY!eh zy`IDEdG=!VlHvJdq5ZdgxiOPDkxPRwBaU2Da2S6064So!r3glk&Q!08aHts!%X}+0 zM#SbUc`iB)gT{|9tc5*WNJ#2XgOY4^?v zu-Zu~JG4_qA#+}yO~q}2q~`YH+O2Gz*Zyb@FF_CPFmEd}JdU5!i#i~>RfzE}2ll&( zv)8dOiO4mJ+kH$a$e4xq6HayKTq{A;VWh4;3@z#GKgSNMbNl~xLmfD(+?&>TK%l1J zMa))xJU26?B;y^jh(2=)Dom^$$=nyEW@#6AK74OJNZdY_Z6#+YK6G8w4@LIG0gTeQHE(%Vl8^mJvKD4Bd-WT6)1vStL^3J)B|(UTWiJeW z_+jNf()sKGltj44qdZ#n-Oj}8!`()wKpD=ozI)k3ALii8l#&F=n=f9o^;C(qDX;{D z{m_$5P)GNLLwj%-@Ih=Pt&W_27D$fvyS zl*OGBlJsI~?Lq9zP7bq+=|!x!1ntu!Nid%)i30-zur)1h?glk2d-kkA5~bl^-m;<4 zo}E3rPq_&nI==8ZueEiI-&=Mv@=J7oLXB-*E6(;Wn`rX0wES`SlEXjAt71@8&ufBT z^R1F{B@9d^rH0uvG;5FWYlT{c1nlr7H3=iup1m#K6AO@pgn3m|FAju;+*YQK`AFO2 zAs4?;iH7SI`x7Dl)EfG%Ow^%k=^+{--RBzUBhReJo2Q!vho01W{P@;NTp-nT%gfw0 z@=;h6m!F~==;=2&S^9_{^W7d_iOGS93ZzHsXeKh)uSe)XOAmgj$QN8il^J`(?jd{G zNB3HH;#MFoSDs$S*Q$s;3=&_o&rNgXgONzDkadWVb^IOyNfoQ~)7RlPs8G^&S`;S! zlUKcQ^ZMJe@s67cr{$P+WAxe+w4(uUx}`=)c;SE4H+qAFS5D3C?d-mwnAc0`hWb2y zL)xtq?$bwF{>{Ox%OX*CWA4n3R4ca~`SEOp;i@}iE{^|opnX2+Z;50o1^PFazMG#f znXAyL?m;Tv;M^eXyZH*Mk}uWy@gTKpyDu(2rZn2PBIYj}fB>=Rm3ENDd@AIV1sSHG zwF?8R(*UgpztdNIdnQLJUEHr}?pOMle#fGPFqg-0jFH;Qt(h8i#Od!2U4DFC5~ei` zin*HDvefcl%B}BfNiy*ZoMPD{R)UDi(6fyRkh}bioji7gOmWCCsrz`ISW{c>2GoD# z!n=48+c@z-PV=q7my(lyf@wtCS+yDG9#k0e_eZ3z6CuA4_at)vv>M~2p)Sr?ef}e- zUpih6`RZgWW{~G@kW`m=FNXdpufLw#V>rAt1gdmnf;b~TY~?JB5M!E`Roeu zXaVy%0M?0QPJO((GBCuUA%0nKk7!EF>4>~}4>9!Y+Vi=3*S4bEn%!i05?!OeBd+0k7i7p+o5T{5gwm>%gzGwF|DYyEK!NH%zyA5AQRgsg> zC@&otY*1sLm)==#G5qq=o3{~0UaTwpkVha925a?x3JP8~*;EsHan6&hGp>|e*qZZE z+&4?~Qx1q}Ck6YPqA|yINEB$UdwlqHNG)#nDtze!E*VgUjEP)vc>K)1sK24sa_r2% zsC=eA_zuiQ7}k01T@og+bn=TK+IA-+UNWJm<%!Nt&h7A(R4oT2+39Q^LDm6%`PKsNFBig^n-e3#sP)-8 z`u9NLc79!F%Hg06ve)_2`=(GGF68LQp1+m_bM(rDqQh%l?i<0oi`Q+4Y8kneRrQ`z zObOjTf5JDycw_KbZpybDFx*S|s4SGMVSQm`dZ+_wLSt}p7p?toM$Uxrl^lf}iQ-HJ zsqRFx)`7!{hFnemU84c)5Hghgr;B%adFGt_OZ!$xLOZ$aWLfyIu^bE5EFN4G;ZvJW zLX_;;bK>3)U>PFf<4Mb@^B#3h9tOy{FZ>@N%jMk9c~$d+kAm2IN~?VFEFPw3c*jt;>&4}8Mx$2z3XL)&IGyK_?gT1bW9rJeL)_BnXNA= z!WpDGl#3KS-hmg_M6bll{rV4sE*TAEr+~llG2gq2LC=AEX!z>c9~~u85&X+5fA4O8 zQ~TbBL;t)(_q+XXnYD8ER=XI?Pmv{KMECR>U!Yo-CK~p5 zgbW^@u{<1%kK>!%bQl^OoTDZAcpbh(D=scB{91RjYF#*?I<~lve*W;LOngm$wV+J` zI*Tg_UFzWLd$2d>cG!%3WlzN_Z9&w^*sF7{u6y<`qvua8hL}DOO1~)^Un4-ERv&$- z0f-w#$LeBVO8(6k-}Q(`V=@Tmf}_jaS7+`I%~E_1Zrz^Wl-2>M*f;9kfflri9$m}+ zsHQnc+(;j$p_bEn--5)^+Q!n-QXIC}(z@L?wfEr$qqRYyf1(v8XRlF&Vs=RWWOKgG zguwok7#j4wJ&YkKN0g~lS$y~JDnskuIUyg`74l36-$Vcv zn1;(HH{YN9YSymWncetvyp8K$n!;!C{JE6HLz}nKLfre%pb75-2n3}zW;4Mo5U2Js zD%foZt^rZ3viO0;WH~ms6)qk6c#%C=^OLiL#I)FS@T(4B$_oVg0kSNnE*AxfhAYdGCsW?a$9h+={)=}zVH4Rr zSQ{`1UsBy4AFIQ#Iup4T`*tMreG=4NC} z%ZQ44%<@ZU^T#ik@?oXD05MBF-x652`G=E^mhZJSwqQzwia}567mj3@gfGX0UYs44 zIaf4x=hz$7FW-O!{V6lQJ5zraR$5v*4^QuxyW3xl#?!3-kp=MT`C#@vYrsO{)UJT0 zQFnNVxOTlNc(%Tw%LCyD2CuV(z9&-y@s@d9;7+_^}8MIbLM#ghn zW7Lr5BQn_RCy}7F^|DH-d^U^cDr&HQ5b^BWVQWL7VOg-9m6g>zzoZ;1@kqQUf&J## z?P83Gb^eKD7EkMTWq7d$*fL#_uf}d7en0P(oSrUf-Kc1O8^6HnB*Cm8E+ex>NLr3D zjat5u$fpB9-_*vRo6Qx^;MTD+@mJ;#M;s*HS$2?)J4x#E-vSu^$t{Ca`VRxRD?FwC z!;>Nu=I@W!%LZ3ia#O41W?BxvJubktR+W^o6hoP=tY8|(k9ekz1xXT4d3yHb>=Q8)i_xrJW z0+c9j_~)fv-YL`=_Dg*Wd_tm;F>1f#4vPP80>0|sgg}zb0Op(LRJVR!Bxw!-u*c1- zRR7p{w!RW2dC|`JazXkx;%59py9cXCcc4s}>uk)oYspeIf-(Mi_W+D~%hVUIjfxM5 z$JZpE6CQ$o;psMVjop?E3R?-t$jRxv%3%6Duq7T-y67_f?uXfuX8_l6_!_UM+ttva zJ;C|IO0XuWJH|=K6@xWYmIlM(nJ|7+6s#M8!FMIKjlf!Qh2~9OZfS~|6{xQOVgry{ zivydaxi9C4k@2WQK=a|ZXPOWxlsT(%-p!#^VmYHlBOkMRC-KtFShy2w<<-Nu{yH+%#LJxK!;`=PZia zLiGe1SZ6mHU%g>E9CQiN=03Do>K!IIUv5lC zYDtH#EmAmM$_{y@2=9rd-m!_F6&NYu1~X?riz>1+8TqE(%pyE zo;>D`m>NCeFcrgo7#zC6?Ch(L8KkDp&-Kbef%usoBH_Ubak^7c?x)F=-J z3jX6H#9c*04)F)!4g$(zvLLQt`Z<*O=pN{6HY0Ozr+x24^@m7M>+Jcq+9^tYkZ0%c zRJ7bs@^U}78VVD1)Nd~2K@7bfrND5cL>4AOF|y`8>{KVBpfFEsEbFyQ0eD~2oX`u7 z)g;+;a=q?pdUWZGEo&*C#X$YmlV-|l!O2=OsBA73s4G45WLLWmLA`h!-G#hZH-yL7 zU(Xz1A9s;@Rx`h(vI4V{kkjF4^b}9?inWd{)~J~=y{VnT0CwgFNaU*Q@vWG&3=Qf5 zkr_b1)^4t_da!auC$kS$k~3+k2DE-mNwbm;Ta-702a_(Br`ZO?R-JmFD1$iHj`GP@TUA=Kl zGO6OhU07`rjgkFeJW?ji(wAOByE|Hur$n6FSbZ?PiW?4jfb7NUQ2iC;@65~nz_NoE zq*5+Y5>S;1x?;-_Ett@CI*T~k9*n1&S-;@%za-M%PP=L@Pb9w<&c|7M^tsx z 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 YMu1;8Wm{Ah&=io7-hJI#s8iJc0FG)dG5`Po literal 0 HcmV?d00001 diff --git a/kreversi/prefs.kcfgc b/kreversi/prefs.kcfgc new file mode 100644 index 00000000..173c0e55 --- /dev/null +++ b/kreversi/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kreversi.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +CustomAdditions=false +Mutators=skill,Zoom,MenubarVisible diff --git a/kreversi/qreversigame.cpp b/kreversi/qreversigame.cpp new file mode 100644 index 00000000..d31bac4a --- /dev/null +++ b/kreversi/qreversigame.cpp @@ -0,0 +1,93 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include "qreversigame.h" + + +// ================================================================ +// class QReversiGame + + +QReversiGame::QReversiGame(QObject *parent) + : QObject(parent), Game() +{ +} + + +QReversiGame::~QReversiGame() +{ +} + + +void QReversiGame::newGame() +{ + Game::newGame(); + + emit sig_newGame(); +} + + +bool QReversiGame::doMove(Move move) +{ + bool retval = Game::doMove(move); + if (!retval) + return false; + + emit sig_move(m_moveNumber, move); + + if (!Game::moveIsAtAllPossible()) + emit sig_gameOver(); + + return retval; +} + + +bool QReversiGame::undoMove() +{ + bool retval = Game::undoMove(); + + // Update all views. + emit sig_update(); + + return retval; +} + + +#include "qreversigame.moc" + diff --git a/kreversi/qreversigame.h b/kreversi/qreversigame.h new file mode 100644 index 00000000..d1712832 --- /dev/null +++ b/kreversi/qreversigame.h @@ -0,0 +1,101 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAME__H__ +#define __QREVERSIGAME__H__ + +#include +#include + +#include "Position.h" +#include "Game.h" +#include "Move.h" + + +class KConfig; + + + +// The main document class in the reversi program. The thing that +// makes this a QReversiGame instead of just a ReversiGame is that it +// emits signals that can be used to update a view. +// +// Signals: +// updateBoard() +// score() +// turn(Color) +// gameOver() +// + +class QReversiGame : public QObject, public Game { + Q_OBJECT + + public: + QReversiGame(QObject *parent = 0); + ~QReversiGame(); + + // Methods dealing with the game + void newGame(); + bool doMove(Move move); + bool undoMove(); +#if 0 + void loadSettings(); + + bool loadGame(KConfig *, bool noupdate = FALSE); + void saveGame(KConfig *); +#endif + + signals: + void sig_newGame(); + void sig_move(uint, Move&); // A move has just been done. + void sig_update(); // Some other change than a move has been done + // Example: loadFile(), undoMove(); + void sig_gameOver(); // The game is over. + + // FIXME: To be removed: + //void updateBoard(); + //void sig_score(); + //void turn(Color); + +private: + // No members. +}; + + +#endif + diff --git a/kreversi/qreversigameview.cpp b/kreversi/qreversigameview.cpp new file mode 100644 index 00000000..92812657 --- /dev/null +++ b/kreversi/qreversigameview.cpp @@ -0,0 +1,299 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#include +#include +#include + +#include +#include + +#if 0 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "board.h" +#include "Engine.h" +#endif +#include "prefs.h" + +#include "qreversigame.h" +#include "qreversigameview.h" + + +// ================================================================ +// class StatusWidget + + +StatusWidget::StatusWidget(const QString &text, QWidget *parent) + : QWidget(parent, "status_widget") +{ + QHBoxLayout *hbox = new QHBoxLayout(this, 0, KDialog::spacingHint()); + QLabel *label; + + m_textLabel = new QLabel(text, this); + hbox->addWidget(m_textLabel); + + m_pixLabel = new QLabel(this); + hbox->addWidget(m_pixLabel); + + label = new QLabel(":", this); + hbox->addWidget(label); + + m_scoreLabel = new QLabel(this); + hbox->addWidget(m_scoreLabel); +} + + +// Set the text label +// + +void StatusWidget::setText(const QString &string) +{ + m_textLabel->setText(string); +} + + +// Set the pixel label - used to show the color. +// + +void StatusWidget::setPixmap(const QPixmap &pixmap) +{ + m_pixLabel->setPixmap(pixmap); +} + + +// Set the score label - used to write the number of pieces. +// + +void StatusWidget::setScore(uint s) +{ + m_scoreLabel->setText(QString::number(s)); +} + + +// ================================================================ +// class QReversiGameView + + +QReversiGameView::QReversiGameView(QWidget *parent, QReversiGame *game) + : QWidget(parent, "gameview") +{ + // Store a pointer to the game. + m_game = game; + + // The widget stuff + createView(); + + // Other initializations. + m_humanColor = Nobody; + + // Connect the game to the view. + connect(m_game, SIGNAL(sig_newGame()), this, SLOT(newGame())); + connect(m_game, SIGNAL(sig_move(uint, Move&)), + this, SLOT(moveMade(uint, Move&))); + connect(m_game, SIGNAL(sig_update()), this, SLOT(updateView())); + // The sig_gameOver signal is not used by the view. + + // Reemit the signal from the board. + connect(m_boardView, SIGNAL(signalSquareClicked(int, int)), + this, SLOT(squareClicked(int, int))); +} + + +QReversiGameView::~QReversiGameView() +{ +} + + +// Create the entire view. Only called once from the constructor. + +void QReversiGameView::createView() +{ + QGridLayout *layout = new QGridLayout(this, 4, 2); + + // The board + m_boardView = new QReversiBoardView(this, m_game); + m_boardView->loadSettings(); // Load the pixmaps used in the status widgets. + layout->addMultiCellWidget(m_boardView, 0, 3, 0, 0); + + // The status widgets + m_blackStatus = new StatusWidget(QString::null, this); + m_blackStatus->setPixmap(m_boardView->chipPixmap(Black, 20)); + layout->addWidget(m_blackStatus, 0, 1); + m_whiteStatus = new StatusWidget(QString::null, this); + m_whiteStatus->setPixmap(m_boardView->chipPixmap(White, 20)); + layout->addWidget(m_whiteStatus, 1, 1); + + // The "Moves" label + QLabel *movesLabel = new QLabel( i18n("Moves"), this); + movesLabel->setAlignment(AlignCenter); + layout->addWidget(movesLabel, 2, 1); + + // The list of moves. + m_movesView = new QListBox(this, "moves"); + m_movesView->setMinimumWidth(150); + layout->addWidget(m_movesView, 3, 1); +} + + +// ---------------------------------------------------------------- +// Slots + + +// Recieves the sig_newGame signal from the game. + +void QReversiGameView::newGame() +{ + m_boardView->updateBoard(true); + m_movesView->clear(); + updateStatus(); +} + + +// Recieves the sig_move signal from the game. + +void QReversiGameView::moveMade(uint moveNum, Move &move) +{ + //FIXME: Error checks. + QString colorsWB[] = { + i18n("White"), + i18n("Black") + }; + QString colorsRB[] = { + i18n("Red"), + i18n("Blue") + }; + + // Insert the new move in the listbox and mark it as the current one. + m_movesView->insertItem(QString("%1. %2 %3") + .arg(moveNum) + .arg(Prefs::grayscale() ? colorsWB[move.color()] + : colorsRB[move.color()]) + .arg(move.asString())); + m_movesView->setCurrentItem(moveNum - 1); + m_movesView->ensureCurrentVisible(); + + // Animate all changed pieces. + m_boardView->animateChanged(move); + m_boardView->updateBoard(); + + // Update the score. + updateStatus(); +} + + +// Recieves the sig_update signal from the game, and can be called +// whenever a total update of the view is required. + +void QReversiGameView::updateView() +{ + m_boardView->updateBoard(true); + updateMovelist(); + updateStatus(); +} + + +// Only updates the status widgets (score). + +void QReversiGameView::updateStatus() +{ + m_blackStatus->setScore(m_game->score(Black)); + m_whiteStatus->setScore(m_game->score(White)); +} + + +// Only updates the status board. + +void QReversiGameView::updateBoard(bool force) +{ + m_boardView->updateBoard(force); +} + + +// Only updates the movelist. This method regenerates the list from +// scratch. + +void QReversiGameView::updateMovelist() +{ + // FIXME: NYI +} + + +// This special slot is just because the external program doesn't have +// access to the internal board view. +// + +void QReversiGameView::squareClicked(int row, int col) +{ + emit signalSquareClicked(row, col); +} + + +// ---------------------------------------------------------------- +// Other public methods. + + +void QReversiGameView::setHumanColor(Color color) +{ + m_humanColor = color; + + if (color == Black) { + m_blackStatus->setText(i18n("You")); + m_whiteStatus->setText(""); + } + else { + m_blackStatus->setText(""); + m_whiteStatus->setText(i18n("You")); + } +} + + +#include "qreversigameview.moc" + diff --git a/kreversi/qreversigameview.h b/kreversi/qreversigameview.h new file mode 100644 index 00000000..a3059a25 --- /dev/null +++ b/kreversi/qreversigameview.h @@ -0,0 +1,160 @@ +/* Yo Emacs, this -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KREVERSI + * + * + ******************************************************************* + * + * A Reversi (or sometimes called Othello) game + * + ******************************************************************* + * + * created 2005 by Inge Wallin + * + ******************************************************************* + * + * This file is part of the KDE project "KREVERSI" + * + * KREVERSI is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KREVERSI is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KREVERSI; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __QREVERSIGAMEVIEW__H__ +#define __QREVERSIGAMEVIEW__H__ + + +#include + +#include "Score.h" +#include "Move.h" +#include "board.h" + + +class KConfig; + + +class QLabel; + +class QReversiGame; + + +class StatusWidget : public QWidget +{ + Q_OBJECT + +public: + StatusWidget(const QString &text, QWidget *parent); + + void setText(const QString &string); + void setPixmap(const QPixmap &pixmap); + void setScore(uint score); + +private: + QLabel *m_textLabel; + QLabel *m_pixLabel; + QLabel *m_scoreLabel; +}; + + +// The main game view + +class QReversiGameView : public QWidget { + Q_OBJECT + +public: + + QReversiGameView(QWidget *parent, QReversiGame *game); + ~QReversiGameView(); + + // Proxy methods for the board view + void showHint(Move move) { m_boardView->showHint(move); } + void quitHint() { m_boardView->quitHint(); } + + void setShowLegalMoves(bool show){ m_boardView->setShowLegalMoves(show); } + void setShowMarks(bool show) { m_boardView->setShowMarks(show); } + void setShowLastMove(bool show){ m_boardView->setShowLastMove(show); } + + void setAnimationSpeed(uint speed){m_boardView->setAnimationSpeed(speed);} + + // To get the pixmap for the status view + QPixmap chipPixmap(Color color, uint size) const + { return m_boardView->chipPixmap(color, size); } + + // Proxy methods for the movelist + // FIXME: Not all of these need to be externally reachable + void insertMove(QString moveString) { m_movesView->insertItem(moveString); } + void removeMove(int moveNum) { + m_movesView->removeItem(moveNum); + updateStatus(); + } + void setCurrentMove(int moveNum) { + m_movesView->setCurrentItem(moveNum); + m_movesView->ensureCurrentVisible(); + } + + // The status widgets. + void setHumanColor(Color color); + + // Starts all: emits some signal, so it can't be called from + // constructor + void start() { m_boardView->start(); } + + // Used by the outer KZoomMainWindow class. + void adjustSize() { m_boardView->adjustSize(); } + + void loadSettings() { m_boardView->loadSettings(); } + + +public slots: + void newGame(); + void moveMade(uint moveNum, Move &move); + + void updateView(); // Update the entire view. + void updateStatus(); // Update the status widgets (score) + void updateBoard(bool force = FALSE); // Update the board. + void updateMovelist(); // Update the move list. + +signals: + void signalSquareClicked(int, int); + +private slots: + // Internal slot to reemit the boards signal. + void squareClicked(int, int); + +private: + void createView(); + +private: + + // Pointer to the game we are displaying + QReversiGame *m_game; // Pointer to the game object (not owner). + + Color m_humanColor; + + // Widgets in the view. + QReversiBoardView *m_boardView; + QListBox *m_movesView; + StatusWidget *m_blackStatus; + StatusWidget *m_whiteStatus; +}; + + +#endif + diff --git a/kreversi/settings.ui b/kreversi/settings.ui new file mode 100644 index 00000000..b7c59ff5 --- /dev/null +++ b/kreversi/settings.ui @@ -0,0 +1,329 @@ + +Settings + + + Settings + + + + 0 + 0 + 431 + 323 + + + + Settings + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + spacer2 + + + Vertical + + + Expanding + + + + 20 + 20 + + + + + + kcfg_Grayscale + + + &Grayscale chips + + + + + kcfg_CompetitiveGameChoice + + + Play Game + + + + unnamed + + + + CasualGameChoice + + + Casually + + + + + + true + + + + + CompetitiveGameChoice + + + Competitively + + + + + + + + + + skillGroup + + + &Computer Skill + + + + unnamed + + + + kcfg_skill + + + 1 + + + 7 + + + 1 + + + Horizontal + + + NoMarks + + + + + Beginner + + + Beginner + + + AlignVCenter|AlignLeft + + + + + Expert + + + Expert + + + AlignVCenter|AlignRight + + + + + Average + + + Average + + + AlignCenter + + + + + + + groupBox2 + + + Animation Speed + + + + unnamed + + + + textLabel1 + + + Slow + + + AlignVCenter|AlignLeft + + + + + textLabel3 + + + Fast + + + AlignVCenter|AlignRight + + + + + kcfg_AnimationSpeed + + + 1 + + + 10 + + + 1 + + + 1 + + + Horizontal + + + NoMarks + + + + + + + kcfg_Animation + + + &Animation + + + true + + + + + kcfg_BackgroundImageChoice + + + &Background + + + + unnamed + + + + BackgroundColorChoice + + + Color: + + + + + kcfg_BackgroundImage + + + + + BackgroundImageChoice + + + &Image: + + + true + + + + + kcfg_BackgroundColor + + + false + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + kcfg_Animation + toggled(bool) + groupBox2 + setEnabled(bool) + + + BackgroundColorChoice + toggled(bool) + kcfg_BackgroundColor + setEnabled(bool) + + + BackgroundImageChoice + toggled(bool) + kcfg_BackgroundImage + setEnabled(bool) + + + + + kurlrequester.h + klineedit.h + kpushbutton.h + kcolorbutton.h + + diff --git a/kreversi/sounds/Makefile.am b/kreversi/sounds/Makefile.am new file mode 100644 index 00000000..83348417 --- /dev/null +++ b/kreversi/sounds/Makefile.am @@ -0,0 +1,7 @@ +soundsdir = $(kde_datadir)/kreversi/sounds +sounds_DATA = reversi-click.wav reversi-won.wav + +EXTRA_DIST = $(sounds_DATA) + +appdatadir = $(kde_datadir)/kreversi +appdata_DATA = eventsrc \ No newline at end of file diff --git a/kreversi/sounds/eventsrc b/kreversi/sounds/eventsrc new file mode 100644 index 00000000..cc525204 --- /dev/null +++ b/kreversi/sounds/eventsrc @@ -0,0 +1,566 @@ +[!Global!] +IconName=kreversi +Comment=KReversi +Comment[be]=РÑверÑÑ– +Comment[bn]=কে-রিভারà§à¦¸à¦¿ +Comment[ca]=Reversi +Comment[is]=Viðsnúningur +Comment[ne]=केडीई रिभरà¥à¤¸à¥€ +Comment[sv]=Othello +Comment[zh_TW]=KReversi 黑白棋 + +[click] +Name=Click +Name[be]=ПÑтрычка +Name[bg]=Щракване +Name[bn]=কà§à¦²à¦¿à¦• +Name[br]=Klik +Name[bs]=Klikni +Name[ca]=Clic +Name[cs]=Kliknutí +Name[cy]=Clicio +Name[da]=Klik +Name[de]=Klick +Name[el]=Κλικ +Name[eo]=Kliki +Name[es]=Clic +Name[et]=Klõps +Name[eu]=Klikatu +Name[fa]=Ùشار +Name[fi]=Napsauta +Name[fr]=Clic +Name[ga]=Cliceáil +Name[he]=לחיצה +Name[hr]=Klik +Name[hu]=Kattintás +Name[is]=Smella +Name[it]=Clic +Name[ja]=クリック +Name[km]=ចុច +Name[ko]=í´ë¦­ +Name[lt]=Paspausti +Name[lv]=KlikÅ¡Ä·is +Name[mk]=Кликање +Name[nb]=Klikk +Name[nds]=Klick +Name[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Klik +Name[nn]=Klikk +Name[pa]=ਦਬਾਓ +Name[pl]=Dobierz +Name[pt]=Carregar +Name[pt_BR]=Clicar +Name[ru]=Щелчок +Name[se]=Coahkkal +Name[sk]=Kliknutie +Name[sl]=Klik +Name[sr]=Кликни +Name[sr@Latn]=Klikni +Name[sv]=Klick +Name[ta]=சொடà¯à®•à¯à®•à¯ +Name[tg]=Ðнгуштзанӣ +Name[tr]=Tık +Name[uk]=Клац +Name[zh_CN]=å•å‡» +Name[zh_TW]=é»žé¸ +Comment=Click +Comment[be]=ПÑтрычка +Comment[bg]=Щракване +Comment[bn]=কà§à¦²à¦¿à¦• +Comment[br]=Klik +Comment[bs]=Klik +Comment[ca]=Clic +Comment[cs]=Kliknutí +Comment[cy]=Clicio +Comment[da]=Klik +Comment[de]=Klick +Comment[el]=Κλικ +Comment[eo]=Kliki +Comment[es]=Pulsación +Comment[et]=Klõps +Comment[eu]=Klikatu +Comment[fa]=Ùشار +Comment[fi]=Napsauta +Comment[fr]=Clic +Comment[ga]=Cliceáil +Comment[he]=לחיצה +Comment[hr]=Klik +Comment[hu]=Kattintás +Comment[is]=Smella +Comment[it]=Clic +Comment[ja]=クリック +Comment[km]=ចុច +Comment[lt]=Paspausti +Comment[lv]=KlikÅ¡Ä·is +Comment[mk]=Вртење +Comment[nb]=Klikk +Comment[nds]=Klick +Comment[ne]=कà¥à¤²à¤¿à¤• गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Klik +Comment[nn]=Klikk +Comment[pa]=ਦਬਾਓ +Comment[pl]=KlikniÄ™cie +Comment[pt]=Carregar +Comment[pt_BR]=Clicar +Comment[ru]=Щелчок +Comment[se]=Coahkkal +Comment[sk]=Kliknutie +Comment[sl]=Klik +Comment[sr]=Кликни +Comment[sr@Latn]=Klikni +Comment[sv]=Klick +Comment[ta]=சொடà¯à®•à¯à®•à¯ +Comment[tg]=Ðнгушт задан +Comment[tr]=Tık +Comment[uk]=Клац +Comment[zh_CN]=å•å‡» +Comment[zh_TW]=é»žé¸ +default_presentation=1 +default_sound=reversi-click.wav + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κεÏδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हà¥à¤ˆ +Comment[hr]=Igra je dobivena +Comment[hu]=GyÅ‘zelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームã«å‹ã¡ +Comment[km]=ល្បែង​បាន​ឈ្នះ +Comment[ko]=게임ì—ì„œ ì´ê¹€ +Comment[lt]=Žaidimas laimÄ—tas +Comment[lv]=SpÄ“le ir uzvarÄ“ta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जितà¥à¤¨à¥ भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²ç²å‹ +default_presentation=1 +default_sound=reversi-won.wav + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल में हार हà¥à¤ˆ +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームã«è² ã‘ +Name[km]=ល្បែង​បាន​ចាញ់ +Name[ko]=게임ì—ì„œ ì§ +Name[lt]=Žaidimas pralaimÄ—tas +Name[lv]=SpÄ“le zaudÄ“ta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हारà¥à¤¨à¥ भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²å¤±æ•— +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיי×, הפסדת +Comment[hi]=खेल में हार हà¥à¤ˆ +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームã«è² ã‘ +Comment[km]=ល្បែង​បាន​ចាញ់ +Comment[ko]=게임ì—ì„œ ì§ +Comment[lt]=Žaidimas pralaimÄ—tas +Comment[lv]=SpÄ“le ir zaudÄ“ta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हारà¥à¤¨à¥ भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegraÅ‚eÅ› +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²å¤±æ•— +default_presentation=0 + +[draw] +Name=Draw +Name[be]=ÐÑ–Ñ‡Ñ‹Ñ +Name[bg]=РавенÑтво +Name[bn]=অমীমাংসিত +Name[br]=Tresañ +Name[bs]=Crtaj +Name[ca]=Empat +Name[cs]=Remíza +Name[cy]=Arlunio +Name[da]=Træk +Name[de]=Ziehen +Name[el]=Ισοπαλία +Name[eo]=EgaliÄo +Name[es]=Dibujar +Name[et]=Viik +Name[eu]=Berdinketa +Name[fa]=قرعه‌کشی +Name[fi]=Tasapeli +Name[fr]=Égalité +Name[ga]=Tarraing +Name[he]=תיקו +Name[hr]=Crtanje +Name[hu]=Döntetlen +Name[is]=Jafntefli +Name[it]=Pari +Name[ja]=引ã分㑠+Name[km]=គូស +Name[ko]=비김 +Name[lt]=PieÅ¡ti +Name[lv]=Vilkt +Name[mk]=Ðерешено +Name[nb]=Uavgjort +Name[nds]=Trecken +Name[ne]=कोरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Tekenen +Name[nn]=Uavgjort +Name[pa]=ਬਰਾਬਰ +Name[pl]=Rysuj +Name[pt]=Empate +Name[pt_BR]=Empate +Name[ru]=ÐÐ¸Ñ‡ÑŒÑ +Name[sk]=Remíza +Name[sl]=Poteg +Name[sr]=Вуци +Name[sr@Latn]=Vuci +Name[sv]=Oavgjort +Name[ta]=வரை +Name[tg]=Кашидан +Name[tr]=Berabere +Name[uk]=ÐÑ–Ñ‡Ð¸Ñ +Name[zh_CN]=平局 +Name[zh_TW]=平手 +Comment=Draw +Comment[be]=ÐÑ–Ñ‡Ñ‹Ñ +Comment[bg]=РавенÑтво +Comment[bn]=অমীমাংসিত +Comment[br]=Tresañ +Comment[bs]=Crtaj +Comment[ca]=Empat +Comment[cs]=Remíza +Comment[cy]=Arlunio +Comment[da]=Træk +Comment[de]=Ziehen +Comment[el]=Ισοπαλία +Comment[eo]=EgaliÄo +Comment[es]=Dibujar +Comment[et]=Viik +Comment[eu]=Berdinketa +Comment[fa]=قرعه‌کشی +Comment[fi]=Tasapeli +Comment[fr]=Égalité +Comment[ga]=Tarraing +Comment[he]=תיקו +Comment[hr]=Crtanje +Comment[hu]=Döntetlen +Comment[is]=Jafntefli +Comment[it]=Pari +Comment[ja]=引ã分㑠+Comment[km]=គូស +Comment[lt]=PieÅ¡ti +Comment[lv]=Vilkt +Comment[mk]=Ðерешено +Comment[nb]=Uavgjort +Comment[nds]=Trecken +Comment[ne]=कोरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Tekenen +Comment[nn]=Uavgjort +Comment[pa]=ਬਰਾਬਰ +Comment[pl]=Dobranie karty +Comment[pt]=Empate +Comment[pt_BR]=Empate +Comment[ru]=ÐÐ¸Ñ‡ÑŒÑ +Comment[sk]=Remíza +Comment[sl]=Poteg +Comment[sr]=Вуци +Comment[sr@Latn]=Vuci +Comment[sv]=Oavgjort +Comment[ta]=வரை +Comment[tg]=Кашидан +Comment[tr]=Beraberlik +Comment[uk]=Ð½Ñ–Ñ‡Ð¸Ñ +Comment[zh_CN]=平局 +Comment[zh_TW]=平手 +default_presentation=0 + +[illegal_move] +Name=Illegal Move +Name[be]=ÐÑправільны ход +Name[bg]=Ðевалиден ход +Name[bn]=অবৈধ চাল +Name[bs]=Nedozvoljen potez +Name[ca]=Moviment il·legal +Name[cs]=Neplatný tah +Name[cy]=Symudiad Angyfreithlon +Name[da]=Ulovligt træk +Name[de]=Unerlaubter Zug +Name[el]=Μη έγκυÏη κίνηση +Name[eo]=Nepermesita movo +Name[es]=Movimiento ilegal +Name[et]=Lubamatu käik +Name[eu]=Legez kanpoko mugimendua +Name[fa]=حرکت غیر مجاز +Name[fi]=Laiton siirto +Name[fr]=Déplacement interdit +Name[ga]=Beart Neamhcheadaithe +Name[he]=מהלך ×œ× ×—×•×§×™ +Name[hr]=Nepravilan potez +Name[hu]=Szabálytalan lépés +Name[is]=Ógildur leikur +Name[it]=Mossa non lecita +Name[ja]=ä¸æ­£ãªç§»å‹• +Name[km]=ការ​ផ្លាស់ទី​មិន​ážáŸ’រឹមážáŸ’រូវ +Name[ko]=ìž˜ëª»ëœ ì›€ì§ìž„ +Name[lt]=Negalimas Ä—jimas +Name[lv]=NelegÄls gÄjiens +Name[mk]=Ðедозволен потег +Name[nb]=Ugyldig trekk +Name[nds]=Tog nich verlöövt +Name[ne]=अवैध चाल +Name[nl]=Foutieve zet +Name[nn]=Ugyldig trekk +Name[pa]=ਗਲਤ ਚਾਲ +Name[pl]=NieprawidÅ‚owy ruch +Name[pt]=Jogada Inválida +Name[pt_BR]=Movimento Ilegal +Name[ru]=Ðеправильный ход +Name[se]=Gustohis sirdin +Name[sk]=Neplatný Å¥ah +Name[sl]=Neveljavna poteza +Name[sr]=Ðедозвољен потез +Name[sr@Latn]=Nedozvoljen potez +Name[sv]=Felaktigt drag +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Ҳаракати ÐодуруÑÑ‚ +Name[tr]=Gecersiz Hamle +Name[uk]=Ðедозволений хід +Name[zh_CN]=无效移动 +Name[zh_TW]=ä¸åˆæ³•çš„移動 +Comment=Illegal move +Comment[be]=ÐÑправільны ход +Comment[bg]=Ðевалиден ход +Comment[bn]=অবৈধ চাল +Comment[bs]=Nedozvoljen potez +Comment[ca]=Moviment il·legal +Comment[cs]=Neplatný tah +Comment[cy]=Symudiad angyfreithlon +Comment[da]=Ulovligt træk +Comment[de]=Unerlaubter Zug +Comment[el]=Μη έγκυÏη κίνηση +Comment[eo]=Nepermesita movo +Comment[es]=Movimiento ilegal +Comment[et]=Lubamatu käik +Comment[eu]=Legez kanpoko mugimendua +Comment[fa]=حرکت غیر مجاز +Comment[fi]=Laiton siirto +Comment[fr]=Déplacement interdit +Comment[ga]=Beart neamhcheadaithe +Comment[he]=מהלך ×œ× ×—×•×§×™ +Comment[hr]=Nepravilan potez +Comment[hu]=Szabálytalan lépés +Comment[is]=Ógildur leikur +Comment[it]=Mossa non lecita +Comment[ja]=ä¸æ­£ãªç§»å‹• +Comment[km]=ការ​ផ្លាស់ទី​មិន​ážáŸ’រឹមážáŸ’រូវ +Comment[ko]=ìž˜ëª»ëœ ì›€ì§ìž„ +Comment[lt]=Negalimas Ä—jimas +Comment[lv]=Å Äds gÄjiens nav atļauts +Comment[mk]=Ðедозволен потег +Comment[nb]=Ugyldig trekk +Comment[nds]=Tog nich verlöövt +Comment[ne]=अवैध चाल +Comment[nl]=Foutieve zet +Comment[nn]=Ugyldig trekk +Comment[pa]=ਗਲਤ ਚਾਲ +Comment[pl]=NieprawidÅ‚owy ruch +Comment[pt]=Jogada inválida +Comment[pt_BR]=Movimento ilegal +Comment[ru]=Ðеправильный ход +Comment[se]=Gustohis sirdin +Comment[sk]=Neplatný Å¥ah +Comment[sl]=Neveljavna poteza +Comment[sr]=Ðедозвољен потез +Comment[sr@Latn]=Nedozvoljen potez +Comment[sv]=Felaktigt drag +Comment[ta]=தவறான நகரà¯à®¤à¯à®¤à®²à¯ +Comment[tr]=Geçersiz hamle +Comment[uk]=Ðедозволений хід +Comment[zh_CN]=无效移动 +Comment[zh_TW]=ä¸åˆæ³•çš„移動 +default_presentation=0 diff --git a/kreversi/sounds/reversi-click.wav b/kreversi/sounds/reversi-click.wav new file mode 100644 index 0000000000000000000000000000000000000000..0b3a296451a2fabe964ac31dd20b17cf3263ced9 GIT binary patch literal 2474 zcmYL~!HQi)5QeM!Tr-HE<3bQzq?ZcfBZQLRNJJD&FxwuJF&T(N#zDxg9$_A#@d@$> z{Rs022VA)j0$C)JJNF#Fzi-sjoSy3HzyA8Gy6>4E?i?Nc@vhnZFMs^%=U>jtp_wIy z-!zL`X4wwy;qm$L=U~j{RdcNUt%YsHT+Z=0>uR2Qnwbl;r8f2*E37k&HEdGu^;}c>IY}=>U)g2lS z;$^=PtC&6YO|0=+`}W2T;;Qaq+d!3F9KiFsOg{B(S$q@8ww}AeI+6JuU&4Ijt6yI( zb?p=DwWmJSr7nfhQ_DRcuD%UAY1GuRZ;78gl@y!HrL30g(yeb=>gx0^j1JwH*OCs` z$Q`~J#3kANs;zy~lPym6i^WsTeQU3MiYv3{{jWA(*0cHK7@L|ra@<2#>b^EbZf zdEPx=`c#&Ns=j%_Zfq=@wM^y|xiQxzIG3K{s?q(DjWh1MzO88XYv-iGW@|77y`Vb! z=w?>4O*1E`^KYtl_3Y_|P}pMksjt-n_CmE`Jv&Uk&YZKSV#fS}yHjPEv)^_z>@cNR zXCe-{Kb|hl@%j0R6}YgnE8c7rU$8@A~NhbMs+Ru z6hwJ${6>Ulk31c&8I2Ww^pLNBvQ(%&m|d(?6I5s8VYNqD9X%RRqgUubt%;SG;I}rb zI?e_bPD$$ME?5n+k9w8et$`;?!q2y&Fns9j+_a8W(AI>|iOQBHn7i%=Ik{h>&O%O4 zO&R#zcdDtZhj8_sn_%>edH9GO$PUjT%;JD~@JW>^Pj*$4%1}0&ff7?=^l?`_7xjd} zL#*)p)>FZf7p3YWd`|V+EdcAlI~*O&W`&*39C(7Ya{B0tIKaR-*ucSsPD>516CKPA zlLTO|l%j z!R5T5$tn!?=z}z9z4vO>+0$GGs*c@?1~?h2zmLCFF{2aC+Q;Z)9~95tx92PD?!Cfk zrAM8Jp{7bNkBbvsLKGrY*}h++CfMcE0LjZ&@9nAEz&D+S{`X}fxhrk0*hOE2?1QtC zrKn2w*ykQxE)~{>4n0C)?N6A##ZcB^m$FcPAr+}ThE7oGP^5Yo4Q+Moh_x^bpAV1l zhNVuMVu+sk1;?-pa~b<}?E2`ZaN3`V2@U>M{uX8*>oJcw;r_=LY+r;qF(hI~Y0 literal 0 HcmV?d00001 diff --git a/kreversi/sounds/reversi-won.wav b/kreversi/sounds/reversi-won.wav new file mode 100644 index 0000000000000000000000000000000000000000..8f5c0703a62cb7ae0fb98468e0f8b46e272c45d6 GIT binary patch literal 40200 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 I_RsbI0%t_!p8x;= literal 0 HcmV?d00001 diff --git a/kreversi/version.h b/kreversi/version.h new file mode 100644 index 00000000..d95b83ed --- /dev/null +++ b/kreversi/version.h @@ -0,0 +1 @@ +#define KREVERSI_VERSION "1.7.1" diff --git a/ksame/ChangeLog b/ksame/ChangeLog new file mode 100644 index 00000000..bb66b357 --- /dev/null +++ b/ksame/ChangeLog @@ -0,0 +1,17 @@ +1999-06-20 Mario Weilguni + + * made it work again with Qt-2 + * fixed some old-style (K&R-C) declartions to C++ decl. + * removed debug messages when compiling with --enable-final + +1999-06-07 Petter Reinholdtsen + * knotes.cpp: Changed text Quit to Exit to match KDE style guide. + +0.5: Mario Weilguni + + * fixes for Qt 2.0 (fixed highscore list, added layout) + +0.2.1 + * [Robert Williams] Add -caption "%c" to ksame.kdelnk + * [Robert Williams] getHelpMenu(true, 0) + * [Robert Williams] Added version.h and ChangeLog diff --git a/ksame/KSameWidget.cpp b/ksame/KSameWidget.cpp new file mode 100644 index 00000000..e9f2d730 --- /dev/null +++ b/ksame/KSameWidget.cpp @@ -0,0 +1,246 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "KSameWidget.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "StoneWidget.h" + +static int default_colors=3; + +#define Board KScoreDialog::Custom1 + +KSameWidget::KSameWidget(QWidget *parent, const char* name, WFlags fl) : + KMainWindow(parent,name,fl) +{ + KStdGameAction::gameNew(this, SLOT(m_new()), actionCollection(), "game_new"); + restart = new KAction(i18n("&Restart This Board"), CTRL+Key_R, this, + SLOT(m_restart()), actionCollection(), "game_restart"); + KStdGameAction::highscores(this, SLOT(m_showhs()), actionCollection(), "game_highscores"); + KStdGameAction::quit(this, SLOT(close()), actionCollection(), "game_quit"); + undo = KStdGameAction::undo(this, SLOT(m_undo()), actionCollection(), "edit_undo"); + + random = new KToggleAction(i18n("&Random Board"), 0, 0, 0, actionCollection(), "random_board"); + showNumberRemaining = new KToggleAction(i18n("&Show Number Remaining"), 0, this, SLOT(showNumberRemainingToggled()), actionCollection(), "showNumberRemaining"); + + KStdAction::configureNotifications(this, SLOT(configureNotifications()), + actionCollection()); + + status=statusBar(); + status->insertItem(i18n("Colors: XX"),1,1); + status->insertItem(i18n("Board: XXXXXX"),2,1); + status->insertItem(i18n("Marked: XXXXXX"),3,1); + status->insertItem(i18n("Score: XXXXXX"),4,1); + + stone = new StoneWidget(this,15,10); + + connect( stone, SIGNAL(s_gameover()), this, SLOT(gameover())); + connect( stone, SIGNAL(s_colors(int)), this, SLOT(setColors(int))); + connect( stone, SIGNAL(s_board(int)), this, SLOT(setBoard(int))); + connect( stone, SIGNAL(s_marked(int)), this, SLOT(setMarked(int))); + connect( stone, SIGNAL(s_score(int)), this, SLOT(setScore(int))); + connect( stone, SIGNAL(s_remove(int,int)), this, SLOT(stonesRemoved(int,int))); + + connect(stone, SIGNAL(s_sizechanged()), this, SLOT(sizeChanged())); + + sizeChanged(); + setCentralWidget(stone); + + // Once the main view can scale then use defualt setupGUI() and remove the + // noMerge="1" from the uirc file + // StatusBar | ToolBar + setupGUI(KMainWindow::Save | Keys | Create ); + + random->setChecked(true); + setScore(0); + + if (!kapp->isRestored()) + newGame(kapp->random(),default_colors); + + KConfig *cfg = kapp->config(); + if (cfg->readBoolEntry("showRemaining")) + { + showNumberRemaining->setChecked(true); + showNumberRemainingToggled(); + } +} + +void KSameWidget::readProperties(KConfig *conf) { + Q_ASSERT(conf); + stone->readProperties(conf); +} + +void KSameWidget::saveProperties(KConfig *conf) { + Q_ASSERT(conf); + stone->saveProperties(conf); + conf->sync(); +} + +void KSameWidget::sizeChanged() { + stone->setFixedSize(stone->sizeHint()); +} + +void KSameWidget::showNumberRemainingToggled() +{ + if(showNumberRemaining->isChecked()){ + QStringList list; + for(int i=1;i<=stone->colors();i++) + list.append(QString("%1").arg(stone->count(i))); + QString count = QString(" (%1)").arg(list.join(",")); + status->changeItem(i18n("%1 Colors%2").arg(stone->colors()).arg(count),1); + } + else status->changeItem(i18n("%1 Colors").arg(stone->colors()),1); + + KConfig *cfg = kapp->config(); + cfg->writeEntry("showRemaining", showNumberRemaining->isChecked()); + cfg->sync(); +} + +void KSameWidget::newGame(unsigned int board,int colors) { + while (board>=1000000) board-=1000000; + // kdDebug() << "newgame board " << board << " colors " << colors << endl; + stone->newGame(board,colors); + setScore(0); +} + +bool KSameWidget::confirmAbort() { + return stone->isGameover() || + stone->isOriginalBoard() || + (KMessageBox::questionYesNo(this, i18n("Do you want to resign?"), + i18n("New Game"),i18n("Resign"),KStdGuiItem::cancel()) == KMessageBox::Yes); +} + +void KSameWidget::m_new() { + if (random->isChecked()) { + if (confirmAbort()) + newGame(kapp->random(),default_colors); + } else { + KDialogBase dlg(this, "boardchooser", true, + i18n("Select Board"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok); + + QVBox *page = dlg.makeVBoxMainWidget(); + + KIntNumInput bno(0, page); + bno.setRange(0, 1000000, 1); + bno.setLabel(i18n("Select a board:")); + bno.setFocus(); + bno.setFixedSize(bno.sizeHint()); + bno.setValue(stone->board()); + + if (dlg.exec()) newGame(bno.value(),default_colors); + } +} + +void KSameWidget::m_restart() { + if (confirmAbort()) + newGame(stone->board(),default_colors); +} + +void KSameWidget::m_undo() { + Q_ASSERT(stone); + stone->undo(); +} + +void KSameWidget::m_showhs() { + Q_ASSERT(stone); + stone->unmark(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score, this); + d.addField(Board, i18n("Board"), "Board"); + d.exec(); +} + +void KSameWidget::setColors(int colors) { + status->changeItem(i18n("%1 Colors").arg(colors),1); +} + +void KSameWidget::setBoard(int board) { + status->changeItem(i18n("Board: %1").arg(board, 6), 2); +} + +void KSameWidget::setMarked(int m) { + status->changeItem(i18n("Marked: %1").arg(m, 6),3); + m_markedStones=m; +} + +void KSameWidget::stonesRemoved(int,int) { + KNotifyClient::event(winId(),"stones removed", + i18n("One stone removed.","%n stones removed.",m_markedStones)); +} + +void KSameWidget::setScore(int score) { + if(showNumberRemaining->isChecked()){ + QStringList list; + for(int i=1;i<=stone->colors();i++) + list.append(QString("%1").arg(stone->count(i))); + QString count = QString(" (%1)").arg(list.join(",")); + status->changeItem(i18n("%1 Colors%2").arg(stone->colors()).arg(count),1); + } + status->changeItem(i18n("Score: %1").arg(score, 6),4); + undo->setEnabled(stone->undoPossible()); + restart->setEnabled(!stone->isOriginalBoard()); +} + +void KSameWidget::gameover() { + if (stone->hasBonus()) { + KNotifyClient::event(winId(), "game won", + i18n("You even removed the last stone, great job! " + "This gave you a score of %1 in total.").arg(stone->score())); + } else { + KNotifyClient::event(winId(), "game over", + i18n("There are no more removeable stones. " + "You got a score of %1 in total.").arg(stone->score())); + } + stone->unmark(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Score, this); + d.addField(Board, i18n("Board"), "Board"); + + KScoreDialog::FieldInfo scoreInfo; + scoreInfo[Board].setNum(stone->board()); + + if (d.addScore(stone->score(), scoreInfo)) + d.exec(); +} + +void KSameWidget::configureNotifications() { + KNotifyDialog::configure(this); +} + +#include "KSameWidget.moc" diff --git a/ksame/KSameWidget.h b/ksame/KSameWidget.h new file mode 100644 index 00000000..ff02e1c4 --- /dev/null +++ b/ksame/KSameWidget.h @@ -0,0 +1,79 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _KSAMEWIDGET +#define _KSAMEWIDGET + +#include + +class KToggleAction; +class StoneWidget; + +class KSameWidget: public KMainWindow { + Q_OBJECT + +public: + KSameWidget(QWidget *parent=0, const char* name=0, WFlags fl=0); + +private slots: + /* File Menu */ + void m_new(); + void m_restart(); + void m_showhs(); + void m_undo(); + + void configureNotifications(); + + void sizeChanged(); + void gameover(); + void setColors(int colors); + void setBoard(int board); + void setScore(int score); + void setMarked(int m); + void stonesRemoved(int, int); + + void showNumberRemainingToggled(); + +protected: + void newGame(unsigned int board, int colors); + + virtual void saveProperties(KConfig *conf); + virtual void readProperties(KConfig *conf); + + bool confirmAbort(); + +private: + // Remember how many stones were last marked, since when + // the stones are removed, board->marked() suddenly becomes + // 0 (or doesn't otherwise reflect the number of stones just removed). + int m_markedStones; + + StoneWidget *stone; + KStatusBar *status; + KToggleAction *random; + KToggleAction *showNumberRemaining; + KAction *restart; + KAction *undo; + +}; + +#endif // _KSAMEWIDGET + diff --git a/ksame/Makefile.am b/ksame/Makefile.am new file mode 100644 index 00000000..593a88f4 --- /dev/null +++ b/ksame/Makefile.am @@ -0,0 +1,24 @@ + +bin_PROGRAMS = ksame +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +ksame_SOURCES= main.cpp StoneField.cpp StoneWidget.cpp KSameWidget.cpp +ksame_LDFLAGS = $(all_libraries) $(KDE_RPATH) +ksame_LDADD = $(LIB_KDEGAMES) $(LIB_KIO) +ksame_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +METASOURCES = AUTO + +noinst_HEADERS = StoneField.h StoneWidget.h KSameWidget.h version.h + +xdg_apps_DATA = ksame.desktop + +appdir = $(kde_datadir)/ksame +app_DATA = stones.png ksameui.rc eventsrc + +KDE_ICON = ksame + +EXTRA_DIST = $(app_DATA) + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/ksame.pot diff --git a/ksame/StoneField.cpp b/ksame/StoneField.cpp new file mode 100644 index 00000000..3862d7fb --- /dev/null +++ b/ksame/StoneField.cpp @@ -0,0 +1,399 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "StoneField.h" +#include +#include + +StoneFieldState::StoneFieldState(const StoneField &stonefield) +{ + field=new unsigned char[stonefield.maxstone]; + for (int i=0;i0); + Q_ASSERT(height>0); + + if (undoenabled) undolist=new QPtrList; + else undolist=0; + + sizex=width; + sizey=height; + maxstone=sizex*sizey; + field=new Stone[maxstone]; + newGame(board,colors); + m_gotBonus= false; +} + +StoneField::~StoneField() { + delete[] field; + delete undolist; + kdDebug() << "~StoneField\n" << endl; +} + +int StoneField::count(int color){ + int c = 0; + Stone *stone=field; + for (int i=0;icolor == color) + c++; + } + return c; +} + +int +StoneField::width() const { + return sizex; +} + +int +StoneField::height() const { + return sizey; +} + +void +StoneField::newGame(unsigned int board,int colors) { + kdDebug() << "StoneField::newgame board " + << board << " colors " << colors << endl; + if (colors<1) colors=3; + if (colors>7) colors=7; + this->colors=colors; + this->board=board; + reset(); +} + +void +StoneField::reset() { + random.setSeed(board); + + Stone *stone=field; + for (int i=0;icolor=1+random.getLong(colors); + stone->marked=false; + stone->changed=true; + } + + gameover=-1; + score=0; + marked=0; + + if (undolist) { + undolist->setAutoDelete(true); + undolist->clear(); + } + + int c[7]; + int j; + for (j=0;j<7;j++) c[j]=0; + + for (j=0,stone=field;jcolor]++; + } + kdDebug() << "red " << c[1] << endl; + kdDebug() << "blue " << c[2] << endl; + kdDebug() << "yellow " << c[3] << endl; + kdDebug() << "green " << c[4] << endl; +} + +int +StoneField::map(int x,int y) { + assert (!(x<0||y<0||x>=sizex||y>=sizey)); + return x+y*sizex; +} + +int +StoneField::mark(int x,int y,bool force) { + int index=map(x,y); + + if (index<0) { + unmark(); + return 0; + } + + if (field[index].marked) return -1; + unmark(); + + mark(index,field[index].color); + + if (marked==1&&!force) { + field[index].marked=false; + marked=0; + } + return marked; +} + +void +StoneField::mark(int index,unsigned char color) { + if ( index<0 || index>=maxstone ) return; + + Stone &stone=field[index]; + + if (stone.marked) return; + + if (!stone.color || stone.color!=color) return; + + stone.changed=true; + stone.marked=true; + marked++; + + // mark left + if ((index%sizex)!=0) mark(index-1,color); + // mark right + if (((index+1)%sizex)!=0) mark(index+1,color); + // mark upward + if (index>=sizex) mark(index-sizex,color); + // mark downward + if (index<(sizex-1)*sizey) mark(index+sizex,color); +} + +void +StoneField::unmark() { + if (!marked) return; + + Stone *stone=field; + for (int i=0;imarked=false; + stone->changed=true; + } + marked=0; +} + +int +StoneField::remove(int x,int y,bool force) { + int index=map(x,y); + + if (index<0) return 0; + + if (!field[index].marked) { + mark(x,y,force); + } + + if (!marked) return 0; + + // remove a single stone?? + if (marked==1&&!force) return 0; + + // add current field to undolist + if (undolist) + undolist->append(new StoneFieldState(*this)); + + // increase score + if (marked>2) + score+=(marked-2)*(marked-2); + + // remove marked stones + Stone *stone=field; + for (int i=0;imarked) { + stone->color=0; + stone->changed=true; + stone->marked=false; + } + } + int removed=marked; + marked=0; + + for (int col=0;col=0 && field[i1].color ) i1-=sizex; + int i2=i1; + while (i2>=0) { + while ( i2>=0 && !field[i2].color ) i2-=sizex; + while ( i2>=0 && field[i2].color ) { + field[i1].color=field[i2].color; + field[i1].changed=true; + field[i2].color=0; + field[i2].changed=true; + i1-=sizex; + i2-=sizex; + } + } + } + + // find the last column that has something + int lastcol = sizex; + while (lastcol > 0 && !field[map(lastcol-1, sizey-1)].color) { + lastcol--; + } + + for (int col=0;col sizex - 1) + break; // we're ready + + for (int row=0; row < sizey; row++) { + int source = map(nextfullcol, row); + int dest = map(col, row); + field[dest].color=field[source].color; + field[dest].changed=true; + field[source].color=0; + field[source].changed=true; + } + } + + // add a bonus, if field is empty + if (!field[map(0, sizey-1)].color) { + score+=1000; + m_gotBonus= true; + } + + // gameover is undefined + gameover=-1; + return removed; +} + +bool StoneField::undoPossible() const { + return !(!undolist||undolist->isEmpty()); +} + +int +StoneField::undo(int count) { + if (!undoPossible()) + return 0; + if (count <= 0) + return 0; + int undocount=1; + StoneFieldState *state=0; + undolist->setAutoDelete(true); + while (--count>0) { + if (undolist->count()==1) break; + undolist->removeLast(); + undocount++; + } + state=undolist->getLast(); + Q_ASSERT(state); + state->restore(*this); + undolist->removeLast(); + return undocount; +} + +bool +StoneField::isGameover() const { + register int i=maxstone-1;; + register unsigned char color; + + if (gameover>=0) return (bool)gameover; + // kdDebug() << "-->gameover" << endl; + + while (i>=0) { + // kdDebug() << i << " " << field[i].color << endl; + // ignore empty fields + while ( i>=0 && field[i].color==0 ) i--; + // Wenn Stein gefunden, + // dann die Nachbarn auf gleiche Farbe pruefen. + while ( i>=0 && (color=field[i].color) ) { + // check left + if ( (i%sizex)!=0 && field[i-1].color==color) + goto check_gameover; + // check upward + if ( i>=sizex && field[i-sizex].color==color) + goto check_gameover; + i--; + } + } + check_gameover: + gameover=(i<0); + // kdDebug() << "<--gameover" << endl; + return (bool)gameover; +} + +bool StoneField::gotBonus() const { + return m_gotBonus; +} + +int +StoneField::getBoard() const { + return board; +} + +int +StoneField::getScore() const { + return score; +} + +int +StoneField::getColors() const { + return colors; +} + +int +StoneField::getMarked() const { + return marked; +} + +int +StoneField::getFieldSize() const { + return maxstone; +} + +struct Stone * +StoneField::getField() const { + return field; +} + + + + + + + diff --git a/ksame/StoneField.h b/ksame/StoneField.h new file mode 100644 index 00000000..df199605 --- /dev/null +++ b/ksame/StoneField.h @@ -0,0 +1,111 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _STONEFIELD +#define _STONEFIELD + +#include +#include + +struct Stone { + unsigned char color; + bool changed; + bool marked; +}; + +class StoneField; +class StoneWidget; + +class StoneFieldState { +private: + unsigned char *field; + + int colors; + unsigned int board; + unsigned int score; + int gameover; + +public: + StoneFieldState(const StoneField &stonefield); + ~StoneFieldState(); + void restore(StoneField &stonefield) const; +}; + + +class StoneField { + friend class StoneFieldState; + friend class StoneWidget; +private: + + int sizex; + int sizey; + int maxstone; + + struct Stone *field; + + int colors; + unsigned int board; + unsigned int score; + mutable int gameover; + bool m_gotBonus; + int marked; + + KRandomSequence random; + QPtrList *undolist; +public: + StoneField(int width=15,int height=10, + int colors=3,unsigned int board=0, + bool undoenabled=true); + ~StoneField(); + + int width() const; + int height() const; + + void newGame(unsigned int board,int colors); + + void reset(); + + int mark(int x,int y,bool force=false); + void unmark(); + + int remove(int x,int y,bool force=false); + + int undo(int count=1); + + bool isGameover() const; + bool gotBonus() const; + bool undoPossible() const; + int getBoard() const; + int getScore() const; + int getColors() const; + int getMarked() const; + + int count(int color); +protected: + int getFieldSize() const; + struct Stone *getField() const; + + int map(int x,int y); + void mark(int index,unsigned char color); +}; + +#endif // _STONEFIELD + diff --git a/ksame/StoneWidget.cpp b/ksame/StoneWidget.cpp new file mode 100644 index 00000000..3d4114de --- /dev/null +++ b/ksame/StoneWidget.cpp @@ -0,0 +1,345 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "StoneWidget.moc" + + +struct StoneSlice { + QPixmap stone; +}; + + +StoneWidget::StoneWidget( QWidget *parent, int x, int y ) + : QWidget(parent,"StoneWidget"), stonefield(x,y) +{ + setBackgroundPixmap(QPixmap(locate("wallpaper", "Time-For-Lunch-2.jpg"))); + QPixmap stonemap(locate("appdata", "stones.png")); + + assert(!stonemap.isNull()); + + slice=0; + maxslices=30; + maxcolors=4; + + sizex=x; + sizey=y; + + stone_width=stonemap.width()/(maxslices+1); + stone_height=stonemap.height()/maxcolors; + + map = new StoneSlice*[maxcolors]; + QBitmap mask; + for (int c = 0; c < maxcolors; c++) { + map[c] = new StoneSlice[maxslices]; + + for (int s = 0; s < maxslices; s++) { + map[c][s].stone.resize(stone_width, stone_height); + assert(!map[c][s].stone.isNull()); + bitBlt(&map[c][s].stone, 0, 0, + &stonemap, stone_width * s, + c*stone_height, + stone_width,stone_height,CopyROP,false); + QImage im = map[c][s].stone.convertToImage(); + mask = im.createHeuristicMask(); + map[c][s].stone.setMask(mask); + } + } + + field_height=stone_height*sizey; + field_width=stone_width*sizex; + + setMouseTracking(true); + + // QColor c(115,115,115); + // setBackgroundColor(c); + + // emit s_sizechanged(); + startTimer( 100 ); + history.setAutoDelete(true); +} + +StoneWidget::~StoneWidget() { + for (int c = 0; c < maxcolors; c++) { + delete [] map[c]; + } + delete [] map; + + setMouseTracking(false); + killTimers(); +} + +unsigned int +StoneWidget::board() { + return stonefield.getBoard(); +} + +int +StoneWidget::score() { + return stonefield.getScore(); +} + +int +StoneWidget::count(int color) { + return stonefield.count(color); +} + +int +StoneWidget::marked() { + return stonefield.getMarked(); +} + +QSize +StoneWidget::size() { + return QSize(sizex,sizey); +} + +int +StoneWidget::colors() { + return stonefield.getColors(); +} + +QSize +StoneWidget::sizeHint () const { + return QSize(field_width,field_height); +} + +void +StoneWidget::newGame(unsigned int board,int colors) { + stonefield.newGame(board,colors); + history.clear(); + modified= false; + emit s_newgame(); + emit s_colors(stonefield.getColors()); + emit s_board(stonefield.getBoard()); +} + +void +StoneWidget::reset() { + stonefield.reset(); + history.clear(); + emit s_newgame(); +} + +void +StoneWidget::unmark() { + stonefield.unmark(); + emit s_marked(0); +} + +bool StoneWidget::undoPossible() const { + if (stonefield.isGameover()) return false; + return stonefield.undoPossible(); +} + +int +StoneWidget::undo(int count) { + if (stonefield.isGameover()) return 0; + + int ret_val=stonefield.undo(count); + + QPoint p=mapFromGlobal(cursor().pos()); + int x=p.x(); + int y=p.y(); + if (x<0||y<0||x>=field_width||y>=field_height) { + emit s_score(stonefield.getMarked()); + return ret_val; + } + + int marked=stonefield.mark(x/stone_width,y/stone_height); + emit s_marked(marked); + slice=0; + emit s_score(stonefield.getScore()); + modified= (stonefield.getScore()>0); + return ret_val; +} + +bool StoneWidget::isGameover() { + return stonefield.isGameover(); +} + +bool StoneWidget::hasBonus() { + return stonefield.gotBonus(); // don't ask me why the names differ... ;-| [hlm] +} + +bool StoneWidget::isOriginalBoard() { + return !modified; +} + +void StoneWidget::readProperties(KConfig *conf) { + Q_ASSERT(conf); + + history.clear(); + + if (!conf->hasKey("Board")|| + !conf->hasKey("Colors")|| + !conf->hasKey("Stones")) { + return; + } + newGame(conf->readNumEntry("Board"),conf->readNumEntry("Colors")); + + QStrList list; + conf->readListEntry("Stones",list); + + for (const char *item=list.first();item;item=list.next()) { + int x=-1,y=-1; + if (sscanf(item,"%02X%02X",&x,&y)!=2) break; + history.append(new QPoint(x,y)); + stonefield.remove(x,y); + } +} + + +void +StoneWidget::saveProperties(KConfig *conf) { + Q_ASSERT(conf); + + QStrList list(true); + QString tmp; + + for (QPoint *item=history.first();item;item=history.next()) { + tmp.sprintf("%02X%02X",item->x(),item->y()); + list.append(tmp.ascii()); + } + + conf->writeEntry("Stones",list); + conf->writeEntry("Board",stonefield.getBoard()); + conf->writeEntry("Colors",stonefield.getColors()); +} + +void +StoneWidget::timerEvent( QTimerEvent * ) { + QPoint p=mapFromGlobal(cursor().pos()); + int x=p.x(); + int y=p.y(); + if (x<0||y<0||x>=field_width||y>=field_height) + stonefield.unmark(); + slice=(slice+1)%maxslices; + paintEvent(0); +} + +void +StoneWidget::paintEvent( QPaintEvent *e ) { + + Stone *stone=stonefield.getField(); + + for (int y=0;ymarked||stone->changed; + + if (!redraw&&e) { + QRect r(cx,cy,stone_width,stone_height); + redraw=r.intersects(e->rect()); + } + if (redraw) { + stone->changed=false; + if (stone->color) { + + int tslice = stone->marked?slice:0; + bitBlt(this,cx,cy, + &map[stone->color-1][tslice].stone, + 0, 0, + stone_width,stone_height,CopyROP,FALSE); + + } else { + erase(cx, cy, stone_width, stone_height); + } + } + stone++; // naechster Stein. + } + } +} + +void +StoneWidget::mousePressEvent ( QMouseEvent *e) { + + if (stonefield.isGameover()) return; + + int x=e->pos().x(); + int y=e->pos().y(); + if (x<0||y<0||x>=field_width||y>=field_height) return; + + int sx=x/stone_width; + int sy=y/stone_height; + + if (stonefield.remove(sx, sy)) { + history.append(new QPoint(sx, sy)); + + emit s_remove(sx, sy); + + stonefield.mark(sx,sy); + emit s_marked(stonefield.getMarked()); + modified= true; + + emit s_score(stonefield.getScore()); + if (stonefield.isGameover()) emit s_gameover(); + } +} + +void +StoneWidget::mouseMoveEvent ( QMouseEvent *e) +{ + if (stonefield.isGameover()) { + stonefield.unmark(); + emit s_marked(0); + return; + } + + int x=e->pos().x(); + int y=e->pos().y(); + if (x<0||y<0||x>=field_width||y>=field_height) return; + + int marked=stonefield.mark(x/stone_width,y/stone_height); + if (marked>=0) { + emit s_marked(marked); + slice=0; + } +} + + + + + + + + diff --git a/ksame/StoneWidget.h b/ksame/StoneWidget.h new file mode 100644 index 00000000..05276924 --- /dev/null +++ b/ksame/StoneWidget.h @@ -0,0 +1,107 @@ +/* Yo Emacs, this is -*- C++ -*- */ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _STONEWIDGET +#define _STONEWIDGET + +#include +#include "StoneField.h" + +struct StoneSlice; + +class StoneWidget : public QWidget { + Q_OBJECT + + int modified; + // int marked; // # of marked stones + + int stones_x, stones_y; + int sizex, sizey; + int field_width, field_height; + + QPtrList history; + StoneField stonefield; + + // picture number of stonemovie + int slice; + + StoneSlice **map; + +public: + StoneWidget( QWidget *parent=0, int x=10,int y=10); + ~StoneWidget(); + + unsigned int board(); + int score(); + int marked(); + QSize size(); + int colors(); + virtual QSize sizeHint() const; + + bool undoPossible() const; + + void newGame(unsigned int board, int colors); + void reset(); + void unmark(); + int undo(int count=1); + + // test for game end + bool isGameover(); + // if isGameover(): finished with bonus? + bool hasBonus(); + // test for unchanged start position + bool isOriginalBoard(); + + virtual void readProperties(KConfig *conf); + virtual void saveProperties(KConfig *conf); + int count(int color); + +protected: + void timerEvent( QTimerEvent *e ); + void paintEvent( QPaintEvent *e ); + void mousePressEvent ( QMouseEvent *e); + void mouseMoveEvent ( QMouseEvent *e); + + // properties of the stone picture + int stone_width,stone_height; // size of one stone + int maxcolors; // number of different stones (Y direction) + int maxslices; // number of pictures per movie (X direction) + +signals: + // A new game begins + void s_newgame(); + + void s_colors(int colors); + void s_board(int board); + void s_score(int score); + void s_marked(int m); + + void s_gameover(); + + // The stone (x,y) was clicked(removed), + // all neighbor stones disappear without further signals + void s_remove(int x,int y); + + void s_sizechanged(); +}; + +#endif // _STONEWIDGET + diff --git a/ksame/eventsrc b/ksame/eventsrc new file mode 100644 index 00000000..75b222b2 --- /dev/null +++ b/ksame/eventsrc @@ -0,0 +1,391 @@ +[!Global!] +IconName=ksame +Comment=SameGame +Comment[af]=Samegame +Comment[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Same +Comment[bn]=সেইমগেম +Comment[br]=Heñvelded +Comment[ca]=Joc iguals +Comment[cs]=Hra Same +Comment[cy]=YrUnGêm +Comment[da]=Samspil (ksame) +Comment[eo]=Samludo +Comment[es]=Juego Iguales +Comment[et]=Sama mäng +Comment[fa]=همین بازی +Comment[fr]=Jeu Same +Comment[gl]=Iguais +Comment[hi]=सेमगेम +Comment[hr]=Ista Igra +Comment[hu]=KSame +Comment[ja]=ã•ã‚ãŒã‚ +Comment[lt]=Žaidimas Same +Comment[lv]=TÄ pati spÄ“le +Comment[mk]=ИÑтата игра +Comment[ne]=उसà¥à¤¤à¥ˆ खेल +Comment[nso]=Papadi yago Swana +Comment[pa]=ਸੇਮ ਖੇਡ +Comment[pl]=ToSamo +Comment[pt]=Iguais +Comment[pt_BR]=Mesmo Jogo +Comment[ro]=Identice +Comment[sk]=Hra Same +Comment[sl]=Igra istih +Comment[sv]=Samegame +Comment[ta]=அதே விளையாடà¯à®Ÿà¯ +Comment[tg]=Бозии Same +Comment[th]=เà¸à¸¡à¸šà¸­à¸¥à¸—ี่คล้ายà¸à¸±à¸™à¸‚อง K +Comment[tr]=AynıOyun +Comment[uk]=Така Ñама гра +Comment[ven]=Mutambo Wonoula +Comment[wa]=Djeu Same Game +Comment[xh]=Umdlalo OfanayoSameGame +Comment[zu]=Umdlalo ofanayo + +[stones removed] +Name=Removed some stones +Name[af]=Verwyder sommige klippe +Name[ar]=لقد أزيلت بعض الأحجار +Name[az]=Bir neçə daÅŸ É™ksildi +Name[be]=Ð’Ñ‹ выдалілі некалькі камÑнёў +Name[bg]=Премахнати Ñа топки +Name[bn]=কিছৠপাথর সরানো হয়েছে +Name[bs]=Neke strijene su uklonjene +Name[ca]=Elimina algunes pedres +Name[cs]=Byly odebrány nÄ›jaké kameny +Name[cy]=Gwaredir rhai cerrig +Name[da]=Fjernede nogle sten +Name[de]=Es wurden Steine entfernt +Name[el]=ΑφαιÏέθηκαν μεÏικές πέτÏες +Name[eo]=Forigis kelkajn Åtonojn +Name[es]=Eliminadas algunas piedras +Name[et]=Eemaldati mõned kivid +Name[eu]=Kendutako harri batzuk +Name[fa]=برخی از سنگهای حذÙ‌‌‌شده +Name[fi]=Poistettiin kiviä +Name[fr]=Des pierres ont été enlevées +Name[gl]=Algunhas pedras eliminadas +Name[he]=הסרה של מספר ××‘× ×™× +Name[hi]=कà¥à¤› पतà¥à¤¥à¤° हटाठ+Name[hr]=Neki su kamenÄići pomaknuti +Name[hu]=Levettem néhány bábut +Name[id]=Pindahkan beberapa batu +Name[is]=Fjarlægði nokkra steina +Name[it]=Rimosse alcune bilie +Name[ja]=石をå–り除ã +Name[km]=បាន​យក​ចáŸáž‰â€‹ážáŸ’ម​ážáŸ’លះ +Name[ko]=íƒ‘ì„ ì—†ì• ì‹­ì‹œì˜¤ +Name[lt]=PaÅ¡alinti kai kurie akmenys +Name[lv]=AizvÄkti daži akmeņi +Name[mk]=ОтÑтранети Ñе неколку камчиња +Name[mt]=Tneħħew xi cagħaq +Name[nb]=Fjernet noen stener +Name[nds]=En poor Steen wöörn wegdaan +Name[ne]=केही ढà¥à¤™à¥à¤—ाहरू हटाइयो +Name[nl]=Enkele stenen verwijderd +Name[nn]=Fjerna nokre steinar +Name[nso]=Tlositse maswika a mangwe +Name[pa]=ਕà©à¨ ਪੱਥਰ ਹਟਾਓ +Name[pl]=UsuniÄ™to kilka kamieni +Name[pt]=Removeu algumas pedras +Name[pt_BR]=Algumas pedras foram removidas +Name[ro]=Au fost eliminate piese +Name[ru]=Ð’Ñ‹ удалили неÑколько камней +Name[se]=Válddii moadde geaÄ‘ggi eret +Name[sk]=Odstránené niektoré kamene +Name[sl]=Odstranjenih je bilo nekaj kamnov +Name[sr]=Уклоњено је неколико каменчића +Name[sr@Latn]=Uklonjeno je nekoliko kamenÄića +Name[sv]=Tog bort nÃ¥gra stenar +Name[ta]=சில கறà¯à®•à®³à¯ நீகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®© +Name[tg]=Хориҷкунии баъзе Ñангҳо +Name[th]=ลบบอลบางลูà¸à¸­à¸­à¸ +Name[tr]=Bir kaç taÅŸ eksildi +Name[uk]=Вилучено декілька каменів +Name[ven]=Ho bviswa manwe matombo +Name[vi]=Äã di chuyển vài hòn đá +Name[xh]=Susa amatye athile +Name[zh_CN]=消去了一些石头 +Name[zh_TW]=移除一些石頭 +Name[zu]=Kususwe amanye amatshe +Comment=You clicked on some stones and made them disappear +Comment[be]=Ð’Ñ‹ пÑтрыкнулі па каменьчыках, Ñ– Ñны зніклі. +Comment[bg]=Премахнати Ñа топки +Comment[bn]=আপনি কিছৠপাথরের ওপর কà§à¦²à¦¿à¦• করে তাদের অদৃশà§à¦¯ করে দিয়েছেন। +Comment[bs]=Kliknuli ste na neke stijene i uÄinili da nestanu +Comment[ca]=Heu clicat a sobre d'algunes pedres i les heu fet desaparèixer +Comment[cs]=Klikli jste na pár kamenů a ony zmizely +Comment[cy]=Naethoch chi glicio ar rai cerrig, a gwneud iddynt ddiflannu. +Comment[da]=Du klikkede pÃ¥ nogle sten og fik dem til at forsvinde +Comment[de]=Sie haben auf einige Steine geklickt und sie so verschwinden lassen +Comment[el]=Κάνατε κλικ σε μεÏικές πέτÏες και τις κάνατε να εξαφανιστοÏν +Comment[eo]=Vi klakis sur kelkaj Åtonoj kaj malaperigis ilin. +Comment[es]=Pinchó sobre algunas piedras y las hizo desaparecer +Comment[et]=Sa klõpsasid mõnel kivil ja põhjustasid nende kadumise +Comment[eu]=Harri batzutan klikatu duzu eta desagertarazi dituzu +Comment[fa]=روی برخی از سنگها Ùشار دادید Ùˆ آنها را ناپدید کردید +Comment[fi]=Napsautit joitakin kiviä ja sait ne katoamaan. +Comment[fr]=Vous avez cliqué sur des pierres et les avez fait disparaître. +Comment[he]=לחצת על מספר ××‘× ×™× ×•×’×¨×ž×ª להן ×œ×”×™×¢×œ× +Comment[hr]=Kliknuli ste neke kamenÄiće i oni su nestali +Comment[hu]=Rákattintott néhány bábura és eltüntette Å‘ket +Comment[is]=Þú smelltir á nokkra steina og þeir hurfu +Comment[it]=Hai fatto clic su alcune bilie e le hai fatte scomparire +Comment[ja]=石をクリックã™ã‚‹ã¨ãれらを消ã›ã¾ã™ +Comment[km]=អ្នក​បាន​ចុច​លើ​ážáŸ’ម​ážáŸ’លះ និង​បាន​ធ្វើ​ឲ្យ​ពួក​វា​បាážáŸ‹ +Comment[lt]=JÅ«s paspaudÄ—te ant keleto akmenų ir juos panaikinote +Comment[lv]=JÅ«s noklikÅ¡Ä·inÄjÄt uz dažiem akmeņiem un padarijÄt tos neredzamus +Comment[mk]=Кликнавте на неколку камчиња и направивте тие да иÑчезнат +Comment[nb]=Du klikket pÃ¥ noen stener og fikk de til Ã¥ forsvinne. +Comment[nds]=Du hest op en poor Steen klickt un so verswinnen laten +Comment[ne]=तपाईà¤à¤²à¥‡ केही ढà¥à¤™à¥à¤—ाहरूमा कà¥à¤²à¤¿à¤• गरेर तिनीहरूलाई अदृशà¥à¤¯ बनाउनॠभयो +Comment[nl]=U klikte op enkele stenen en liet ze verdwijnen +Comment[nn]=Du klikka pÃ¥ nokre steinar og fekk dei til Ã¥ forsvinna. +Comment[pa]=ਤà©à¨¸à©€à¨‚ ਕà©à¨ ਪੱਥਰਾਂ ਨੂੰ ਹਟਾਇਆ ਅਤੇ ਉਹ ਗà©à¨†à¨š ਗਠ+Comment[pl]=KliknÄ…Å‚eÅ› na kilka kamieni i spowodowaÅ‚eÅ› ich znikniÄ™cie +Comment[pt]=Carregou em algumas pedras e elas desapareceram +Comment[pt_BR]=Você clicou em algumas pedras e fez elas desaparecerem +Comment[ru]=Ð’Ñ‹ щёлкнули по камешкам, и они иÑчезли. +Comment[se]=Don coahkkalit moadde geaÄ‘ggi ja jávkkahit daid +Comment[sk]=Klikli ste na kamene a tie sa odstránili +Comment[sl]=Kliknili ste nekaj kamnov in ti so izginili +Comment[sr]=Кликнули Ñте на неке каменчиће и учинили да неÑтану +Comment[sr@Latn]=Kliknuli ste na neke kamenÄiće i uÄinili da nestanu +Comment[sv]=Du klickade pÃ¥ nÃ¥gra stenar och fick dem att försvinna +Comment[ta]= நீஙà¯à®•à®³à¯ சில கறà¯à®•à®³à¯ மீத௠அழà¯à®¤à¯à®¤à®¿ அதை மறைய வைதà¯à®¤à¯à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[uk]=Ви клацнули на камінцÑÑ… Ñ– вони зникли +Comment[zh_CN]=您点击了æŸäº›çŸ³å¤´å¹¶è®©å®ƒä»¬æ¶ˆå¤± +Comment[zh_TW]=您點é¸æŸäº›çŸ³é ­ä¸¦è®“它們消失。 + +[game over] +Name=Game Over +Name[af]=Speletjie Bo +Name[ar]=اللعبة انتهت +Name[az]=Oyun Qurtardı +Name[be]=Канец гульні +Name[bg]=Край на играта +Name[bn]=খেল খতম +Name[br]=Echu an abadenn +Name[bs]=Igra zavrÅ¡ena +Name[ca]=Fi de la partida +Name[cs]=Konec hry +Name[cy]=Gêm Drosodd +Name[da]=Spillet forbi +Name[de]=Spiel beendet +Name[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï +Name[eo]=Ludo finita +Name[es]=Fin de la partida +Name[et]=Mäng läbi +Name[eu]=Jokoa amaitu da +Name[fa]=بازی تمام شد +Name[fi]=Peli loppu +Name[fr]=Fin de la partie +Name[ga]=Cluiche Thart +Name[gl]=Fin do Xogo +Name[he]=×¡×™×•× ×ž×©×—×§ +Name[hi]=खेल ख़तà¥à¤® +Name[hr]=Igra je zavrÅ¡ena +Name[hu]=Vége a játéknak +Name[id]=permainan berakhir +Name[is]=Leik lokið +Name[it]=Gioco terminato +Name[ja]=ゲームオーãƒãƒ¼ +Name[km]=ល្បែង​ចប់ +Name[ko]=SameGame +Name[lt]=Žaidimas baigtas +Name[lv]=SpÄ“les beigas +Name[mk]=Играта заврши +Name[mt]=Il-Logħba SpiÄ‹Ä‹at +Name[nb]=Spillet er slutt +Name[nds]=Speel vörbi +Name[ne]=खेल समापà¥à¤¤ +Name[nl]=Spel is afgelopen +Name[nn]=Spelet er slutt +Name[nso]=Papadi e Fedile +Name[pa]=ਖੇਡ ਖਤਮ +Name[pl]=Koniec gry +Name[pt]=Fim do jogo +Name[pt_BR]=Fim do jogo +Name[ro]=Joc terminat +Name[ru]=Конец игры +Name[se]=Speallu nogai +Name[sk]=Koniec hry +Name[sl]=Konec igre +Name[sr]=Крај игре +Name[sr@Latn]=Kraj igre +Name[sv]=Spelet är slut +Name[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Name[tg]=Бозӣ ба итмом раÑид +Name[th]=จบเà¸à¸¡ +Name[tr]=Oyun Bitti +Name[uk]=Гру завершено +Name[uz]=OÊ»yin tugadi +Name[uz@cyrillic]=Ўйин тугади +Name[ven]=Muthambo wo Fhela +Name[vi]=Game kết thúc +Name[wa]=Li djeu est houte +Name[xh]=Uphelile Umdlalo +Name[zh_CN]=游æˆç»“æŸ +Name[zh_TW]=éŠæˆ²çµæŸ +Name[zu]=Umdlalo uphelile +Comment=Game over because there are no more removeable stones +Comment[be]=Канец гульні - больш нÑма каменьчыкаў, ÑÐºÑ–Ñ Ð¼Ð¾Ð¶Ð½Ð° выдалÑць +Comment[bg]=Край на играта, защото нÑма повече топки за премахване +Comment[bn]=সরিয়ে নেওয়ার মত আর কোন পাথর অবশিষà§à¦Ÿ নেই বিধায় খেল খতম। +Comment[bs]=Igra je zavrÅ¡ena jer viÅ¡e nema uklonjivih stijena +Comment[ca]=Final de la partida perque no hi ha més pedres a eliminar +Comment[cs]=Konec hry; už nelze odstranit žádné kameny +Comment[cy]=Gêm drosodd gan nad oes rhagor o gerrig i'w gwaredu +Comment[da]=Spillet er forbi da der ikke er flere sten som kan fjernes +Comment[de]=Das Spiel ist vorüber. Es gibt keine Steine mehr, die sich entfernen lassen +Comment[el]=Τέλος Ï€Î±Î¹Ï‡Î½Î¹Î´Î¹Î¿Ï Î´Î¹ÏŒÏ„Î¹ δεν υπάÏχουν άλλες μετακινήσιμες πέτÏες +Comment[eo]=Ludo finiÄis ĉar ne plu estas forigeblaj Åtonoj. +Comment[es]=Fin de la partida porque no hay más piedras que eliminar +Comment[et]=Mäng sai läbi, sest pole enam ühtegi eemaldatavat kivi +Comment[eu]=Jokoa amaitu da mugi daitezkeen harri gehiagorik ez dagoelakko +Comment[fa]=بازی تمام شد، زیرا هیچ سنگ قابل حذ٠دیگری نیست +Comment[fi]=Peli on loppu, koska ei ole enää poistettavia kiviä +Comment[fr]=La partie est terminée car aucune pierre ne peut plus être enlevée. +Comment[he]=המשחק ×”×¡×ª×™×™× ×›×™ ×œ× × ×©×רו עוד ××‘× ×™× ×©× ×™×ª×Ÿ להסיר +Comment[hr]=Igra je zavrÅ¡ena jer viÅ¡e nema kamenÄića koje je moguće pomaknuti +Comment[hu]=Vége a játéknak, mert nincs több eltávolítható bábu +Comment[is]=Leik lokið því það eru engir fleiri steinar til að fjarlægja +Comment[it]=Il gioco è terminato perché non puoi rimuovere altre bilie +Comment[ja]=ゲーム終了。å–り除ã‘る石ãŒã‚ã‚Šã¾ã›ã‚“。 +Comment[km]=ល្បែង​ចាញ់ ព្រោះ​មិន​មាន​ážáŸ’ម​ដែល​យក​ចáŸáž‰â€‹áž‡áž¶â€‹áž…្រើន +Comment[lt]=Žaidimas baigtas, nes nebÄ—ra panaikinamų akmenų +Comment[lv]=SpÄ“le beigusies, jo aizvÄcamu akmeņu vairs nav +Comment[mk]=Играта заврши бидејќи повеќе нема камчиња што можат да Ñе отÑтранат +Comment[nb]=Spillet er slutt fordi ingen flere stener kan fjernes. +Comment[nds]=Dat Speel is vörbi, wiel sik keen Steen mehr wegdoon lett +Comment[ne]=तà¥à¤¯à¤¹à¤¾à¤ हटाउनका लागि ढà¥à¤™à¥à¤—ाहरू नभà¤à¤•à¤¾à¤²à¥‡ खेल सकियो +Comment[nl]=Het spel is afgelopen omdat er geen verwijderbare stenen meer aanwezig zijn +Comment[nn]=Spelet er slutt fordi ingen fleire steinar kan fjernast. +Comment[pa]=ਖੇਡ ਖਤਮ ਹੋ ਗਈ ਹੈ, ਕਿਉਕਿ ਹੋਰ ਹਟਾਉਣਯੋਗ ਪੱਧਰ ਨਹੀਂ ਹਨ +Comment[pl]=Koniec gdy, bo nie ma już usuwalnych kamieni +Comment[pt]=Fim do jogo, dado que já não há mais peças para remover +Comment[pt_BR]=Fim do jogo porque não há mais pedras removíveis +Comment[ru]=Конец игры - больше нет камешков, которые можно удалÑÑ‚ÑŒ +Comment[se]=Speallu nogai dannego ii leat vejolaÅ¡ váldit eanet geÄ‘ggiid eret. +Comment[sk]=Koniec hry, pretože už nie sú žiadne odstrániteľné kamene +Comment[sl]=Igre je konec, saj ni veÄ kamnov, ki bi jih bilo moÄ odstraniti +Comment[sr]=Игра је завршена јер више нема каменчића које можете уклонити +Comment[sr@Latn]=Igra je zavrÅ¡ena jer viÅ¡e nema kamenÄića koje možete ukloniti +Comment[sv]=Spelet är slut för det finns inga flera stenar som kan tas bort +Comment[ta]=நீகà¯à®•à¯à®µà®¤à®±à¯à®•à¯ கறà¯à®•à®³à¯ இலà¯à®²à¯ˆà®¯à¯†à®©à¯à®ªà®¤à®¾à®²à¯ ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯ +Comment[uk]=Кінець гри - більше немає камінців, Ñкі можна видалÑти +Comment[zh_CN]=没有å¯ç§»åŠ¨çš„石头,游æˆç»“æŸ +Comment[zh_TW]=沒有å¯ç§»å‹•çš„石頭,éŠæˆ²çµæŸã€‚ + +[game won] +Name=Excellent finish +Name[af]=Uitstekende einde +Name[ar]=نهاية ممتازة +Name[az]=GözÉ™l bir final +Name[be]=Добры фінал +Name[bg]=Край на играта +Name[bn]=চমতà§â€à¦•à¦¾à¦° সমাপà§à¦¤à¦¿ +Name[bs]=Sjajan zavrÅ¡etak +Name[ca]=Final excel·lent +Name[cs]=SkvÄ›lý závÄ›r +Name[cy]=Gorffenniad gwych +Name[da]=Glimrende afslutning +Name[de]=Hervorragender Schluss-Spurt +Name[el]=Τέλειος τεÏματισμός +Name[eo]=Grandioza fino +Name[es]=Excelente final +Name[et]=Suurepärane lõpetamine +Name[eu]=Amaiera ederra +Name[fa]=پایانی عالی +Name[fi]=Erinomainen loppu +Name[fr]=Fin de partie victorieuse +Name[gl]=Excelente final +Name[he]=×¡×™×•× ×ž×•×©×œ× +Name[hi]=शà¥à¤°à¥‡à¤·à¥à¤  समापन +Name[hr]=OdliÄan zavrÅ¡etak +Name[hu]=KitűnÅ‘ befejezés +Name[id]=penyelesaian hebat +Name[is]=Frábær endir +Name[it]=Ottima conclusione +Name[ja]=エクセレントフィニッシュ +Name[km]=បញ្ចប់​អស្ចារ្យ +Name[lt]=Puikus finiÅ¡as +Name[lv]=Lielisks finiÅ¡s +Name[mk]=Одлична завршница +Name[mt]=Tmiem eÄ‹Ä‹ellenti +Name[nb]=Fantastisk avslutning +Name[nds]=Bannig goot beendt +Name[ne]=धेरै रामà¥à¤°à¥‹ समापà¥à¤¤à¤¿ +Name[nl]=Uitstekend einde +Name[nn]=Fantastisk avslutning +Name[pa]=ਸ਼ਾਨਦਾਰ ਸਮਾਪਤੀ +Name[pl]=DoskonaÅ‚e zakoÅ„czenie +Name[pt]=Fim de jogo excelente +Name[pt_BR]=Final excelente +Name[ro]=AÅ£i terminat excelent +Name[ru]=Великолепный финал +Name[sk]=Fantastický koniec +Name[sl]=Izvrsten konec +Name[sr]=Одличан завршетак +Name[sr@Latn]=OdliÄan zavrÅ¡etak +Name[sv]=Utmärkt avslutning +Name[ta]= à®…à®°à¯à®®à¯ˆà®¯à®¾à®© à®®à¯à®Ÿà®¿à®µà¯ +Name[tg]=Интиҳои олиҷаноб +Name[th]=จบได้ยอดเยี่ยมมาภ+Name[tr]=Harika bir bitiriÅŸ +Name[uk]=Відмінний фініш +Name[ven]=Kufhedzele kwa vhudisa +Name[vi]=Hoàn thành xuất sắc! +Name[xh]=Ugqibo Oluphume emagqabini +Name[zh_CN]=精彩的结局 +Name[zh_TW]=å®Œç¾Žå®Œæˆ +Name[zu]=Isiphetho esihle +Comment=Game over, you removed even the last stone +Comment[be]=Канец гульні, не заÑталоÑÑ Ð½Ñ–Ð²Ð¾Ð´Ð½Ð°Ð³Ð° каменьчыка +Comment[bg]=Край на играта, защото вÑички топки Ñа премахнати +Comment[bn]=খেল খতম, আপনি শেষ পাথরটিও সরিয়ে ফেলেছেন। +Comment[bs]=Igra je zavrÅ¡ena, uklonili ste i posljednju stijenu +Comment[ca]=Final de la partida, heu eliminat fins a l'última pedra +Comment[cs]=Konec hry; odstranili jste vÅ¡echny kameny +Comment[cy]=Gêm drosodd, rydych wedi gwaredu'r garreg olaf! +Comment[da]=Spillet forbi, du fjernede endog den sidste sten +Comment[de]=Das Spiel ist vorüber. Sie haben den letzten Stein entfernt +Comment[el]=Τέλος παιχνιδιοÏ, αφαιÏέσατε μέχÏι και την τελευταία πέτÏα +Comment[eo]=Ludo finita, vi forigis eĉ la lastan Åtonon. +Comment[es]=Fin de la partida, eliminó hasta la última piedra +Comment[et]=Mäng sai läbi, sest sa eemaldasid viimase kivi +Comment[eu]=Jokoa amaitu da, azken harria ere kendu duzu +Comment[fa]=بازی تمام شد، شما حتی آخرین سنگ را هم حذ٠کردید +Comment[fi]=Peli loppu,poistit viimeisenkin kiven +Comment[fr]=La partie est terminée, vous avez enlevé la dernière pierre. +Comment[he]=המשחק הסתיי×, הסרת ×פילו ×ת ×”×בן ×”×חרונה +Comment[hr]=Igra je zavrÅ¡ena, uklonili ste i posljednji kamenÄić +Comment[hu]=Vége a játéknak, mert az utolsó bábut is eltávolította +Comment[is]=Leik lokið því þú fjarlægðir meira að segja síðasta steininn +Comment[it]=Gioco terminato, hai rimosso fino all'ultima bilia +Comment[ja]=ゲーム終了。最後ã®çŸ³ã‚’å–り除ãã¾ã—ãŸã€‚ +Comment[km]=ល្បែង​ចប់ អ្នក​បាន​យក​ចáŸáž‰â€‹ážŸáž¼áž˜áŸ’បី​ážáŸ‚​ážáŸ’ម​ចុង​ក្រោយ +Comment[lt]=Žaidmas baigtas, JÅ«s panaikinote net paskutinį akmenį +Comment[lv]=SpÄ“le beigusies, jÅ«s aizvÄcÄt arÄ« pÄ“dÄ“jo akmeni +Comment[mk]=Играта заврши, го отÑтранивте и поÑледното камче +Comment[nb]=Spillet er slutt, du klarte til og med Ã¥ fjerne den siste stenen. +Comment[nds]=Dat Speel is vörbi. Du hest ok den lesten Steen wegdaan +Comment[ne]=खेल समापà¥à¤¤, तपाईà¤à¤²à¥‡ अनà¥à¤¤à¤¿à¤® ढà¥à¤™à¥à¤—ा पनि हटाउनॠभयो +Comment[nl]=Het spel is afgelopen, u hebt zelfs de laatste verwijderd +Comment[nn]=Spelet er slutt, du klarte til og med Ã¥ fjerna den siste steinen. +Comment[pa]=ਖੇਡ ਖਤਮ ਹੋਈ, ਤà©à¨¸à©€à¨‚ ਆਖਰੀ ਪੱਥਰ ਵੀ ਹਟਾ ਦਿੱਤਾ ਹੈ +Comment[pl]=Koniec gry, usunÄ…Å‚eÅ› nawet ostatni kamieÅ„ +Comment[pt]=Fim do jogo, dado que removeu todas as peças +Comment[pt_BR]=Fim do jogo, você removeu até a última pedra +Comment[ru]=Конец игры, не оÑталоÑÑŒ ни единого камешка +Comment[se]=Speallu nogai, don nágadit vela maÅ‹emuÅ¡ geaÄ‘ggi váldit eret. +Comment[sk]=Koniec hry, pretože ste odstránili aj posledný kameň +Comment[sl]=Igre je konec, odstranili ste celo zadnji kamen +Comment[sr]=Крај игре, уклонили Ñте Ñве каменчиће +Comment[sr@Latn]=Kraj igre, uklonili ste sve kamenÄiće +Comment[sv]=Spelet är slut, du tog bort till och med den sista stenen +Comment[ta]=ஆடà¯à®Ÿà®®à¯ à®®à¯à®Ÿà®¿à®¨à¯à®¤à®¤à¯, நீஙà¯à®•à®³à¯ இறà¯à®¤à®¿à®•à¯à®•à®²à¯à®²à¯ˆà®•à¯à®•à¯‚ட நீகà¯à®•à®¿à®µà®¿à®Ÿà¯à®Ÿà¯€à®°à¯à®•à®³à¯ +Comment[tr]=Oyun bitti, son topu da kaldırdınız. +Comment[uk]=Кінець гри - ви видалили навіть оÑтанній камінь +Comment[zh_CN]=游æˆç»“æŸï¼Œæ‚¨ç”šè‡³æ¶ˆåŽ»äº†æœ€åŽä¸€ä¸ªçŸ³å¤´ +Comment[zh_TW]=éŠæˆ²çµæŸï¼Œæ‚¨ç§»èµ°äº†æ‰€æœ‰çš„石頭。 diff --git a/ksame/hi128-app-ksame.png b/ksame/hi128-app-ksame.png new file mode 100644 index 0000000000000000000000000000000000000000..5b15fda9074974a34461248aa4c24f516a801053 GIT binary patch literal 16637 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 M07*qoM6N<$g8vg|LI3~& literal 0 HcmV?d00001 diff --git a/ksame/hi16-app-ksame.png b/ksame/hi16-app-ksame.png new file mode 100644 index 0000000000000000000000000000000000000000..cfd17984cc0f171963fce16fe2128237ede3942b GIT binary patch literal 800 zcmV+*1K<3KP)1uyb*j3$tGj2?-P7YtGMcC$Mo>Xe!7;7`<5CE^ z6NGF85nQ>A1o96I33x#;I}sNO-WEm|B1}RUNW8!#lj+QKr>CZ?`cl_Z=eU?LsQKXC zz2CEVcm@AEbBZcQ+b=V}xQbhO6x*&6?p>k3{Vf;Mp9PqI_r-sn->$vF!jZE)_~bJj zc>?AhfXNiX9oV|2*;xCIS@^#AglqQ!%xUK>8r}0e_L9r$tFW*F#)fDDMGDgZu6_@{ ze$_AX^mX?Vm+t}0o4lY)>j&}3vz~bPX=t^eIS010U#T}hI+*o!#QKk<*RO7BpH=^J zdX@nziTR)?rDyyGs1Wi#WchoA!bx~AEl|@#PO2_4oV5CV!gIckNth68;!_76Q14xJ+ zh&-5hlx~AWSWMzkywK!NS#9!5vX$>dNoC1D)Wz-vh)Dq*_5lW%0z)WA9*J|5(JUn{ z`0f1H+_L5`F{(Gk$GzT|AX^|!=Fvj|cYw`<^gs=5W?7rzos!M18HA`w7gwDSz=IOGNdL zY|A^@KHR3+X;SeVjDcj9F&>2MY==6r)y2&+diTtA@_(i*01v1fud{s2_1<~Pubf=2 zx`#c-lE%O^)7eI>Hm=0!m%k^;XF*N`%z#(`)`6}L&^my%x>7FrQg$@D40AP%v>qJj eR6qdi0^#5G8A;L05dfb60000* literal 0 HcmV?d00001 diff --git a/ksame/hi22-app-ksame.png b/ksame/hi22-app-ksame.png new file mode 100644 index 0000000000000000000000000000000000000000..26a1d2ca991cab761e4687af7bf45aa9e43705fa GIT binary patch literal 1328 zcmV-01<(44P)z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v(rb$FWRCwBal}(5oMHI(>RbAaZJs&&S-PuW&Nj7d2 zl>{|LVKsiBAO>;}MDXBE5cJ?pJPDe!Coe%gc@z=Bn}U)YB7&F%MDP={G1=Xj{m9Jp zO!xFxb(M#iML}c1gQ^Gb_a4;$y(cXHb7~TfHd?g2gRF>U3R7^?Y;kk4MK_Jnw+~hS z>8(3(X6boOF22ls?KGahfYT6QA?<4h!!3G)ulO!}pDWoF%)b%hUlpGSRyo^#k9PAp z=8i$3>U>-&zNJ1EOVe=|%{FrhrJkQ1SZT9Ts_7y+oJ@{(2$HF9zqsQ zEeRpq0ZK?>?9FRfuZ|paDU;D%VT0ZJpL`D>0rI=m*8pV#NLG2pCg8b%$q{A(q6F7~SAg$=rvL%c zLX^Rz3GF$=&L-9x4B!e{7gl)w6Ne9F*`Suenx1J=WJ9nM5HY9-{4oS02*RmVLhwcq z-GGJ%GJ|*zVu_KhN(EMeQw6^PA^^7n%7wfJlRdC~&?N>3 zEgZDYo1Vp2BBjg z8-r3YMvnL*^0QM>BJd9!#?6PQU#}w?78ZaQ@BynK&0rkC0Lqah7xQG*V?={%^fQ4P z@u@!qMG1#D_~7cQe0}%EPScsY8>wA_Bt~hAEN5VM*82JMU51dS97Zb=Rdx1mS)S8LidyU-8%u~1$O=ddOu$B9#uA+dWX>{Ca3}rHvibWB zx{06MH^^RG9+q7!IFA5#Sh%AwSJw~uM|Q&UR2Ggf!91qf!TN2Cvxt_9M-$F z7?ulcZw%S^=iLAQ+$VU8kN?_Wp0IxvNxX;)X2Bb5wT3olm~Ol(en|ON_7ne= z(=YcI*?r({hNBOXL^t8tg9swL1Mk6T&`p7|OEmQ{w#JY0myKiM_n7|+fd3SIt~~U4 zhNCYb#1#ZFWb-gsfc_k$0}vAG9EvsAJPn(#LsfyQpQ0)r5Z^0)`#%BjU+mq)?gI}Z z{CkiIEF6Y?M_|uEn45>e3}_1*V=xB11y#V-61@6nICUKAN~6?+ESKL9zr1v5E(ySY zp?8$g{6pwwk-;Kddn;UXJuEChZvasWVFK%0ur+B*Q036nU=3IWD=)&|o`CThDE&B% z`;7PrYyS~|zc09xZ0-?sJ!JL(TzeagX5oe#+fjrdB9K8lcMh}x?^?%I4s`{qt8nTC zcMz-6oCIMR}u7%VeI9EDctx0$c7MxFr0z8-H>J=g7yRF!PA9a@C%0^2*Eah zg(ipfGjQ%SY%N214)hk-DcDJS2$xPkQ^3Y5s1o1g_?dk$S|k);4EIex%DtBaU}BCM z<3FNmi-{s!e;3}?;0>TbYKROZ8e#=e15pKG*;=fiSb^y|IIsxPAYFrV7B|`pq9sg~ z^UaNWn7IhRvD*o?;wy~-cz$`(R6i zZLo2Q6(9vz$6?oS*F^x{dw}ay!)?Z1D5-E7eA5Du4M+t+2O_N7=%c#JK-B^Z8z|0# zn!xUvHgm24XR-dmp({%6z6d~5bBl3=#&yoE0#}3YK!^@R(Ch$Ew~nJ7pu!q_CrX11 zVu5fCr~>c7(U!p&Zk~Ra#R~uw@7AVORRrJ^)LRIzfEq-X`t8V*65_HQlLF$RElX6j zWvJF6bTG_76^L^EBGOpLo+{ubfbiJugj#c*^&k*<;St`$^eoKu!3TipxUV4yAPgWD z5Ogdyfb}3FvjO=UNbp{?t;2eXg2TYC!ox91rI?d}AP^xQjh8?@!RO0} z!Jdu@rscm3K!`R|LbUG!3891t7!4uqK{J8KLw=@JM*$9rLt>rAIrajCX^&|098nxY zBqKbQwpt*H6(}6^cYzJsNL>IjgLnk-07MEthBStrfJQ>4TRo`OAhhSx??iwV0oH1) zqX!VG7y}q%z={};!FzQ<>GD-b3rOc6T7don^oK1~Y4?z*io(S)yNErCyq#OpiJ*sq&h_#)Of4{30p7AKb28{GhS{MXs2x zZcxb)b+`|u6Kn~-e1p@iOW3qkVpp{1R3*Eq(*xvZHpy33*;EnA2d^Eus`>dgfN*=_ z=6OVn{e_+qI3Zg&32fdW+Q20!8T= zpLw0lEzLRA=dyYI!2TUqvx{y-|2WFqj=!88ivl;_O9OUad61dCyXmK%#7~LT27#L( zsX#aoU5oPq=OUcwW5ZdRXpYMDC?<;WYfFq*)>&zKNGX2$(%&~fcK^20y%7Mwb9cn| z{Pk4&@F2EvFNN6ydzsz8NN*64hK@)y1inFfg>VKhBwj{15)28A3#l3?#s%Y3rf&?5+ec$-eZkJmlaK3P)(*(m7#VK+9!y>K77T#m9O6UBECNFyKnCLc$Ig4K>*SV*#)q8M_Qc7m`qqofTR(cHRSrQb z-2VbV(9$shdYuhAO9DtP&%*0(==W|pGDzOOD+u>zfk;Cs&hKcwpmN?u0^=ou;Lt|0hA_Os_lXKlcCt002ovPDHLkV1h8R@vHy< literal 0 HcmV?d00001 diff --git a/ksame/hi48-app-ksame.png b/ksame/hi48-app-ksame.png new file mode 100644 index 0000000000000000000000000000000000000000..c522c0313dcc630bd939ac284d3195d98a3693e3 GIT binary patch literal 3972 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 UP|U5L`2YX_07*qoM6N<$f+LPIv;Y7A literal 0 HcmV?d00001 diff --git a/ksame/ksame.desktop b/ksame/ksame.desktop new file mode 100644 index 00000000..18fdb24c --- /dev/null +++ b/ksame/ksame.desktop @@ -0,0 +1,119 @@ +[Desktop Entry] +Name=SameGame +Name[af]=Samegame +Name[az]=EyniOyun +Name[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Same +Name[bn]=সেইমগেম +Name[br]=Heñvelded +Name[ca]=Joc iguals +Name[cs]=Same +Name[cy]=YrUnGêm +Name[da]=Samspil +Name[de]=Probiere +Name[eo]=Samludo +Name[es]=Juego Iguales +Name[et]=Sama mäng +Name[fa]=همین بازی +Name[fi]=KSame +Name[fr]=Jeu Same +Name[gl]=Iguais +Name[hi]=सेमगेम +Name[hr]=Ista Igra +Name[hu]=KSame +Name[is]=Sama spil +Name[ja]=ã•ã‚ãŒã‚ +Name[mt]=LogħbaStess (SameGame) +Name[ne]=उसà¥à¤¤à¥ˆ खेल +Name[nso]=Papadi yago Swana +Name[pa]=ਸੇਮ ਖੇਡ +Name[pl]=ToSamo +Name[pt]=Iguais +Name[pt_BR]=Mesmo Jogo +Name[ro]=Identice +Name[ru]=Игра Same +Name[sk]=Same +Name[sv]=Samegame +Name[ta]=அதே விளையாடà¯à®Ÿà¯ +Name[tg]=Бозии Same +Name[th]=บอลที่คล้ายà¸à¸±à¸™ - K +Name[tr]=AynıOyun +Name[ven]=Wono ula Muthambo +Name[wa]=Djeu Same Game +Name[xh]=Ikwa ngumdlalo ofanayo +Name[zu]=Umdlalo ofanayo +Exec=ksame %i %m -caption "%c" +Type=Application +DocPath=ksame/index.html +GenericName=Board Game +GenericName[af]=Bord Speletjie +GenericName[ar]=لعبة لوح +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ +GenericName[bg]=Табла +GenericName[bn]=ছককেনà§à¦¦à§à¦°à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn +GenericName[bs]=Igra s ploÄom +GenericName[ca]=Joc de taula +GenericName[cs]=Desková Same +GenericName[cy]=Gêm Fwrdd +GenericName[da]=Brætspil +GenericName[de]=Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι +GenericName[eo]=Tabuloludo +GenericName[es]=Juego de tablero +GenericName[et]=Lauamäng +GenericName[eu]=Mahai-jokoa +GenericName[fa]=بازی تخته +GenericName[fi]=Lautapeli +GenericName[fo]=Borðspæl +GenericName[fr]=Jeu de plateau +GenericName[ga]=Cluiche Chláir +GenericName[gl]=Xogo de Taboleiro +GenericName[he]=משחק לוח +GenericName[hi]=बिसात का खेल +GenericName[hr]=Igra na ploÄi +GenericName[hu]=Táblajáték +GenericName[is]=Borðleikur +GenericName[it]=Gioco da tavolo +GenericName[ja]=ボードゲーム +GenericName[km]=ល្បែង​ក្ដារ +GenericName[ko]=리버시 ë³´ë“œ 게임 +GenericName[lt]=Stalo žaidimas +GenericName[lv]=Galda spÄ“le +GenericName[mk]=Игра на табла +GenericName[mt]=Logħba tal-bord +GenericName[nb]=Brettspill +GenericName[nds]=Brettspeel +GenericName[ne]=बोरà¥à¤¡ खेल +GenericName[nl]=Bordspel +GenericName[nn]=Brettspel +GenericName[pa]=ਬੋਰਡ ਖੇਡ +GenericName[pl]=Gra planszowa +GenericName[pt]=Jogo de Tabuleiro +GenericName[pt_BR]=Jogo de Tabuleiro +GenericName[ro]=Un joc de table +GenericName[ru]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð¸Ð³Ñ€Ð° +GenericName[rw]=Umukino w'Ikibaho +GenericName[se]=Duolbbášspeallu +GenericName[sk]=Stolová hra +GenericName[sl]=Namizna igra +GenericName[sr]=Игра на табли +GenericName[sr@Latn]=Igra na tabli +GenericName[sv]=Brädspel +GenericName[ta]=பலகை விளையாடà¯à®Ÿà¯ +GenericName[tg]=Бозии Рӯи Мизӣ +GenericName[th]=เà¸à¸¡à¸à¸£à¸°à¸”าน +GenericName[tr]=Tahta Oyunu +GenericName[uk]=Гра на дошці +GenericName[uz]=Stol oÊ»yini +GenericName[uz@cyrillic]=Стол ўйини +GenericName[ven]=Mutambo wa Bodo +GenericName[vi]=Game bàn +GenericName[wa]=Djeu d' platea +GenericName[xh]=Umdlalo Webhodi +GenericName[zh_CN]=æ£‹ç±»æ¸¸æˆ +GenericName[zh_TW]=棋盤éŠæˆ² +GenericName[zu]=Umdlalo webhodi +Icon=ksame +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/ksame/ksameui.rc b/ksame/ksameui.rc new file mode 100644 index 00000000..b9697064 --- /dev/null +++ b/ksame/ksameui.rc @@ -0,0 +1,17 @@ + + + + &Settings + + + + + + + + +Main Toolbar + + + + diff --git a/ksame/main.cpp b/ksame/main.cpp new file mode 100644 index 00000000..08c1e076 --- /dev/null +++ b/ksame/main.cpp @@ -0,0 +1,55 @@ +/* + * ksame 0.4 - simple Game + * Copyright (C) 1997,1998 Marcus Kreutzberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#include + +#include + +#include +#include +#include + +#include "version.h" +#include "KSameWidget.h" +#include +#include + +static const char description[] = I18N_NOOP("Same Game - a little game about balls and how to get rid of them"); +static const char copyright[] = "(c) 1997-1998 Marcus Kreutzberger"; + +int main( int argc, char **argv ) { + KAboutData aboutData("ksame", I18N_NOOP("SameGame"), KSAME_VERSION, + description, KAboutData::License_GPL, copyright); + aboutData.addAuthor("Marcus Kreutzberger", 0, "kreutzbe@informatik.mu-luebeck.de"); + KCmdLineArgs::init(argc, argv, &aboutData); + + KApplication::setColorSpec(QApplication::ManyColor+QApplication::CustomColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + KSameWidget *w = new KSameWidget; + if (kapp->isRestored()) { + if (KSameWidget::canBeRestored(1)) + w->restore(1); + } else + w->show(); + a.setMainWidget(w); + + return a.exec(); +} diff --git a/ksame/stones.png b/ksame/stones.png new file mode 100644 index 0000000000000000000000000000000000000000..db3caa47e194ad39d711307a7fd456ac3ee720d7 GIT binary patch literal 63981 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 uufTr+G (v.1.4.90) + + * GUI cleanup + * Double-buffer painting + * Replaced paused pixmap with i18n-friendly text + * Code cleanup + * Added 'Prefer Unscaled Tiles' feature + * Removed 'Is Game Solvable?' feature (it didn't work). It + will be re-instated when a suitable algorithm is present + * Fix bug 53893: Gravity should be applied before calculating hints + +2002-11-27 Dave Corrie (v.1.4.1) + + In conjunction with Jason Lane: + + * Move tile handling (loading/scaling/highlighting etc) into + separate class (class TileSet) + * Permit resize of playing window + * TODO: game startup optimization, double buffering, remove + paused icon (not i18n friendly) + +2002-08-01 Dave Corrie (v.1.4) + + Recent changes of note. (Does not list all the changes that + happened in the last three years!) + + * [Andreas Beckermann] make keybindings configurable + * [Jason Lane] statusbar shows number of tiles remaining + * [Jason Lane] smoothScale tiles when shrinking + * [Jason Lane] remove need for separate tile mask pixmap + * [Jason Lane] reset "cheat mode" flag when changing board + size or difficulty level + * unicode names are now shown correctly in highscore table + * improved speed of layout calculation + * reduced flicker when highlighting/unhighlighting tiles + * cache current tile scale value + * FINALLY fix undo/redo in gravity mode + +1999-06-19 Mario Weilguni (v.1.3) + + * tiles now have a mask, better drawing + * pausing games is now possible, but the pixmap should + be i18n'ed by some means + * fixed undo/redo in combination with gravity + * fixed a mysterious undo bug in combination with gravity + * prepared for 2.0 (hopefully) + +1999-04-09 Mario Weilguni (v.1.2.2) + + * fixed another bug in writeHighscore + +1999-04-04 Mario Weilguni (v.1.2.1) + + * replace all locale->translate with i18n + * fixed a bug in the writeHighscore method + +1999-03-31 Mario Weilguni (v.1.2) + + * added gravity. + * TODO: gravity and the check for unsolvable games do not work + together + * fixed menu accelerator + +1999-01-03 Mario Weilguni (v.1.1) + + * some board sizes did not match the entry shown in the + menu. Fixed. + * fixed a few warnings with egcs + * removed my initial highscore + +1998-07-17 Mario Weilguni + + * moved to version 1.0 (it´s stable enough now) + +1998-04-10 Mario Weilguni + + * better highscore management. highscores are now score-based, not + time based + * board.cpp: fixed some bugs + * the game doesn´t ask anymore for a name if the score + is not good enough for the hall of fame + +0.2.1: + * [Robert Williams] Added getHelpMenu() + * [Robert Williams] Added version.h + +0.2: added this changelog + + fixed a bug in the pathfinder (reported and fixed + by Stephane Alnet ) + + fixed a bug in the Board::lighten() function on 8-bit + displays. Reported by Marc Diefenbruch + + + Added a "Hall of Fame", mostly taken from kreversi. + + Added "About Qt" to keep the Trolls happy + + Fixed that "player-has-won-and-must-go-to-the-bathroom"-bug + (the elapsed time was taken after getPlayerName() instead + of taking it before) + + Fixed that "game-starts-before-midnight-and-ends-after-midnight" + bug. This will allow games with a duration up to 68 years, + hopefully enough for the common player. The first player who + breaks this limit should contact me in the year 2065; I will send + him 20 bottles of Austrian beer ("Murauer") :-) + +0.1: initial release diff --git a/kshisen/Makefile.am b/kshisen/Makefile.am new file mode 100644 index 00000000..ae805477 --- /dev/null +++ b/kshisen/Makefile.am @@ -0,0 +1,28 @@ + + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +PICDIR = $(kde_datadir)/kshisen/pics + +bin_PROGRAMS = kshisen + +METASOURCES = board.moc app.moc + +noinst_HEADERS = app.h board.h tileset.h debug.h version.h + +kshisen_SOURCES = main.cpp board.cpp app.cpp tileset.cpp settings.ui prefs.kcfgc +kshisen_LDADD = $(LIB_KDEGAMES) $(LIB_KDEUI) +kshisen_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +kshisen_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +xdg_apps_DATA = kshisen.desktop +kde_kcfg_DATA = kshisen.kcfg +KDE_ICON = kshisen + +SUBDIRS = pics + +rcdir = $(kde_datadir)/kshisen +rc_DATA = kshisenui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp $(kshisen_SOURCES) -o $(podir)/kshisen.pot diff --git a/kshisen/app.cpp b/kshisen/app.cpp new file mode 100644 index 00000000..27f8cbf2 --- /dev/null +++ b/kshisen/app.cpp @@ -0,0 +1,760 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "app.h" +#include "prefs.h" +#include "settings.h" + +App::App(QWidget *parent, const char *name) : KMainWindow(parent, name), + cheat(false) +{ + highscoreTable = new KHighscore(this); + + // TODO? + // Would it make sense long term to have a kconfig update rather then + // havin both formats supported in the code? + if(highscoreTable->hasTable()) + readHighscore(); + else + readOldHighscore(); + + statusBar()->insertItem("", SBI_TIME); + statusBar()->insertItem("", SBI_TILES); + statusBar()->insertFixedItem(i18n(" Cheat mode "), SBI_CHEAT); + statusBar()->changeItem("", SBI_CHEAT); + + initKAction(); + + board = new Board(this, "board"); + loadSettings(); + + setCentralWidget(board); + + setupGUI(); + + connect(board, SIGNAL(changed()), this, SLOT(enableItems())); + + QTimer *t = new QTimer(this); + t->start(1000); + connect(t, SIGNAL(timeout()), this, SLOT(updateScore())); + connect(board, SIGNAL(endOfGame()), this, SLOT(slotEndOfGame())); + connect(board, SIGNAL(resized()), this, SLOT(boardResized())); + + kapp->processEvents(); + + updateScore(); + enableItems(); +} + +void App::initKAction() +{ + // Game + KStdGameAction::gameNew(this, SLOT(newGame()), actionCollection()); + KStdGameAction::restart(this, SLOT(restartGame()), actionCollection()); + KStdGameAction::pause(this, SLOT(pause()), actionCollection()); + KStdGameAction::highscores(this, SLOT(hallOfFame()), actionCollection()); + KStdGameAction::quit(this, SLOT(quitGame()), actionCollection()); + + // Move + KStdGameAction::undo(this, SLOT(undo()), actionCollection()); + KStdGameAction::redo(this, SLOT(redo()), actionCollection()); + KStdGameAction::hint(this, SLOT(hint()), actionCollection()); + //new KAction(i18n("Is Game Solvable?"), 0, this, + // SLOT(isSolvable()), actionCollection(), "move_solvable"); + +#ifdef DEBUGGING + (void)new KAction(i18n("&Finish"), 0, board, SLOT(finish()), actionCollection(), "move_finish"); +#endif + + // Settings + KStdAction::preferences(this, SLOT(showSettings()), actionCollection()); +} + +void App::hallOfFame() +{ + showHighscore(); +} + +void App::newGame() +{ + board->newGame(); + resetCheatMode(); + enableItems(); +} + +void App::quitGame() +{ + kapp->quit(); +} + +void App::restartGame() +{ + board->setUpdatesEnabled(false); + while(board->canUndo()) + board->undo(); + board->setUpdatesEnabled(true); + board->update(); + enableItems(); +} + +void App::isSolvable() +{ + if(board->solvable()) + KMessageBox::information(this, i18n("This game is solvable.")); + else + KMessageBox::information(this, i18n("This game is NOT solvable.")); +} + +void App::pause() +{ + bool paused = board->pause(); + lockMenus(paused); +} + +void App::undo() +{ + if(board->canUndo()) + { + board->undo(); + setCheatMode(); + enableItems(); + } +} + +void App::redo() +{ + if(board->canRedo()) + board->redo(); + enableItems(); +} + +void App::hint() +{ +#ifdef DEBUGGING + board->makeHintMove(); +#else + board->showHint(); + setCheatMode(); +#endif + enableItems(); +} + +void App::loadSettings() +{ + // Setting 'Prefer Unscaled Tiles' to on is known to fail in the following + // situation: The Keramik window decoration is in use AND caption bubbles + // stick out above the title bar (i.e. Keramik's 'Draw small caption + // bubbles on active windows' configuration entry is set to off) AND the + // kshisen window is maximized. + // + // The user can work-around this situation by un-maximizing the window first. + if(Prefs::unscaled()) + { + QSize s = board->unscaledSize(); + + // We would have liked to have used KMainWindow::sizeForCentralWidgetSize(), + // but this function does not seem to work when the toolbar is docked on the + // left. sizeForCentralWidgetSize() even reports a value 1 pixel too small + // when the toolbar is docked at the top... + // These bugs present in KDE: 3.1.90 (CVS >= 20030225) + //resize(sizeForCentralWidgetSize(s)); + + s += size() - board->size(); // compensate for chrome (toolbars, statusbars etc.) + resize(s); + //kdDebug() << "App::preferUnscaled() set size to: " << s.width() << " x " << s.height() << endl; + } +} + +void App::lockMenus(bool lock) +{ + // Disable all actions apart from (un)pause, quit and those that are help-related. + // (Only undo/redo and hint actually *need* to be disabled, but disabling everything + // provides a good visual hint to the user, that they need to unpause to continue. + KPopupMenu* help = dynamic_cast(child("help", "KPopupMenu", false)); + KActionPtrList actions = actionCollection()->actions(); + KActionPtrList::iterator actionIter = actions.begin(); + KActionPtrList::iterator actionIterEnd = actions.end(); + + while(actionIter != actionIterEnd) + { + KAction* a = *actionIter; + if(!a->isPlugged(help)) + a->setEnabled(!lock); + ++actionIter; + } + + actionCollection()->action(KStdGameAction::name(KStdGameAction::Pause))->setEnabled(true); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Quit))->setEnabled(true); + + enableItems(); +} + +void App::enableItems() +{ + if(!board->isPaused()) + { + actionCollection()->action(KStdGameAction::name(KStdGameAction::Undo))->setEnabled(board->canUndo()); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Redo))->setEnabled(board->canRedo()); + actionCollection()->action(KStdGameAction::name(KStdGameAction::Restart))->setEnabled(board->canUndo()); + } +} + +void App::boardResized() +{ + // If the board has been resized to a size that requires scaled tiles, then the + // 'Prefer Unscaled Tiles' option should be set to off. + + //kdDebug() << "App::boardResized " << b->width() << " x " << b->height() << endl; + bool unscaled = Prefs::unscaled(); + if(unscaled && board->size() != board->unscaledSize()) + Prefs::setUnscaled(false); +} + +void App::slotEndOfGame() +{ + if(board->tilesLeft() > 0) + { + KMessageBox::information(this, i18n("No more moves possible!"), i18n("End of Game")); + } + else + { + // create highscore entry + HighScore hs; + hs.seconds = board->getTimeForGame(); + hs.x = board->x_tiles(); + hs.y = board->y_tiles(); + hs.gravity = (int)board->gravityFlag(); + + // check if we made it into Top10 + bool isHighscore = false; + if(highscore.size() < HIGHSCORE_MAX) + isHighscore = true; + else if(isBetter(hs, highscore[HIGHSCORE_MAX-1])) + isHighscore = true; + + if(isHighscore && !cheat) + { + hs.name = getPlayerName(); + hs.date = time((time_t*)0); + int rank = insertHighscore(hs); + showHighscore(rank); + } + else + { + QString s = i18n("Congratulations! You made it in %1:%2:%3") + .arg(QString().sprintf("%02d", board->getTimeForGame()/3600)) + .arg(QString().sprintf("%02d", (board->getTimeForGame() / 60) % 60)) + .arg(QString().sprintf("%02d", board->getTimeForGame() % 60)); + + KMessageBox::information(this, s, i18n("End of Game")); + } + } + + resetCheatMode(); + board->newGame(); +} + +void App::updateScore() +{ + int t = board->getTimeForGame(); + QString s = i18n(" Your time: %1:%2:%3 %4") + .arg(QString().sprintf("%02d", t / 3600 )) + .arg(QString().sprintf("%02d", (t / 60) % 60 )) + .arg(QString().sprintf("%02d", t % 60 )) + .arg(board->isPaused()?i18n("(Paused) "):QString::null); + + statusBar()->changeItem(s, SBI_TIME); + + // Number of tiles + int tl = (board->x_tiles() * board->y_tiles()); + s = i18n(" Removed: %1/%2 ") + .arg(QString().sprintf("%d", tl - board->tilesLeft())) + .arg(QString().sprintf("%d", tl )); + + statusBar()->changeItem(s, SBI_TILES); +} + +void App::setCheatMode() +{ + // set the cheat mode if not set + if(!cheat) + { + cheat = true; + statusBar()->changeItem(i18n(" Cheat mode "), SBI_CHEAT); + } +} + +void App::resetCheatMode() +{ + // reset cheat mode if set + if(cheat) + { + cheat = false; + statusBar()->changeItem("", SBI_CHEAT); + } +} + +QString App::getPlayerName() +{ + QDialog *dlg = new QDialog(this, "Hall of Fame", true); + + QLabel *l1 = new QLabel(i18n("You've made it into the \"Hall Of Fame\". Type in\nyour name so mankind will always remember\nyour cool rating."), dlg); + l1->setFixedSize(l1->sizeHint()); + + QLabel *l2 = new QLabel(i18n("Your name:"), dlg); + l2->setFixedSize(l2->sizeHint()); + + QLineEdit *e = new QLineEdit(dlg); + e->setText("XXXXXXXXXXXXXXXX"); + e->setMinimumWidth(e->sizeHint().width()); + e->setFixedHeight(e->sizeHint().height()); + e->setText( lastPlayerName ); + e->setFocus(); + + QPushButton *b = new KPushButton(KStdGuiItem::ok(), dlg); + b->setDefault(true); + b->setFixedSize(b->sizeHint()); + + connect(b, SIGNAL(released()), dlg, SLOT(accept())); + connect(e, SIGNAL(returnPressed()), dlg, SLOT(accept())); + + // create layout + QVBoxLayout *tl = new QVBoxLayout(dlg, 10); + QHBoxLayout *tl1 = new QHBoxLayout(); + tl->addWidget(l1); + tl->addSpacing(5); + tl->addLayout(tl1); + tl1->addWidget(l2); + tl1->addWidget(e); + tl->addSpacing(5); + tl->addWidget(b); + tl->activate(); + tl->freeze(); + + dlg->exec(); + + lastPlayerName = e->text(); + delete dlg; + + if(lastPlayerName.isEmpty()) + return " "; + return lastPlayerName; +} + +int App::getScore(const HighScore &hs) +{ + double ntiles = hs.x*hs.y; + double tilespersec = ntiles/(double)hs.seconds; + + double sizebonus = std::sqrt(ntiles/(double)(14.0 * 6.0)); + double points = tilespersec / 0.14 * 100.0; + + if(hs.gravity) + return (int)(2.0 * points * sizebonus); + else + return (int)(points * sizebonus); +} + +bool App::isBetter(const HighScore &hs, const HighScore &than) +{ + if(getScore(hs) > getScore(than)) + return true; + else + return false; +} + +int App::insertHighscore(const HighScore &hs) +{ + int i; + + if(highscore.size() == 0) + { + highscore.resize(1); + highscore[0] = hs; + writeHighscore(); + return 0; + } + else + { + HighScore last = highscore[highscore.size() - 1]; + if(isBetter(hs, last) || (highscore.size() < HIGHSCORE_MAX)) + { + if(highscore.size() == HIGHSCORE_MAX) + { + highscore[HIGHSCORE_MAX - 1] = hs; + } + else + { + highscore.resize(highscore.size()+1); + highscore[highscore.size() - 1] = hs; + } + + // sort in new entry + int bestsofar = highscore.size() - 1; + for(i = highscore.size() - 1; i > 0; i--) + { + if(isBetter(highscore[i], highscore[i-1])) + { + // swap entries + HighScore temp = highscore[i-1]; + highscore[i-1] = highscore[i]; + highscore[i] = temp; + bestsofar = i - 1; + } + } + + writeHighscore(); + return bestsofar; + } + } + return -1; +} + +void App::readHighscore() +{ + QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name; + hi_x = highscoreTable->readList("x", HIGHSCORE_MAX); + hi_y = highscoreTable->readList("y", HIGHSCORE_MAX); + hi_sec = highscoreTable->readList("seconds", HIGHSCORE_MAX); + hi_date = highscoreTable->readList("date", HIGHSCORE_MAX); + hi_grav = highscoreTable->readList("gravity", HIGHSCORE_MAX); + hi_name = highscoreTable->readList("name", HIGHSCORE_MAX); + + highscore.resize(0); + + for (unsigned int i = 0; i < hi_x.count(); i++) + { + highscore.resize(i+1); + + HighScore hs; + + hs.x = hi_x[i].toInt(); + hs.y = hi_y[i].toInt(); + hs.seconds = hi_sec[i].toInt(); + hs.date = hi_date[i].toInt(); + hs.date = hi_date[i].toInt(); + hs.gravity = hi_grav[i].toInt(); + hs.name = hi_name[i]; + + highscore[i] = hs; + } +} + +void App::readOldHighscore() +{ + // this is for before-KHighscore-highscores + int i; + QString s, e, grp; + KConfig *conf = kapp->config(); + + highscore.resize(0); + i = 0; + bool eol = false; + grp = conf->group(); + conf->setGroup("Hall of Fame"); + while ((i < (int)HIGHSCORE_MAX) && !eol) + { + s.sprintf("Highscore_%d", i); + if(conf->hasKey(s)) + { + e = conf->readEntry(s); + highscore.resize(i+1); + + HighScore hs; + + QStringList e = conf->readListEntry(s, ' '); + int nelem = e.count(); + hs.x = (*e.at(0)).toInt(); + hs.y = (*e.at(1)).toInt(); + hs.seconds = (*e.at(2)).toInt(); + hs.date = (*e.at(3)).toInt(); + + if(nelem == 4) // old version <= 1.1 + { + hs.gravity = 0; + hs.name = *e.at(4); + } + else + { + hs.gravity = (*e.at(4)).toInt(); + hs.name = *e.at(5); + } + + highscore[i] = hs; + } + else + { + eol = true; + } + i++; + } + +// // freshly installed, add my own highscore +// if(highscore.size() == 0) +// { +// HighScore hs; +// hs.x = 28; +// hs.y = 16; +// hs.seconds = 367; +// hs.name = "Mario"; +// highscore.resize(1); +// highscore[0] = hs; +// } + + // restore old group + conf->setGroup(grp); + + // write in new KHighscore format + writeHighscore(); + // read form KHighscore format + readHighscore(); +} + +void App::writeHighscore() +{ + int i; + QStringList hi_x, hi_y, hi_sec, hi_date, hi_grav, hi_name; + for(i = 0; i < (int)highscore.size(); i++) + { + HighScore hs = highscore[i]; + hi_x.append(QString::number(hs.x)); + hi_y.append(QString::number(hs.y)); + hi_sec.append(QString::number(hs.seconds)); + hi_date.append(QString::number(hs.date)); + hi_grav.append(QString::number(hs.gravity)); + hi_name.append(hs.name); + } + highscoreTable->writeList("x", hi_x); + highscoreTable->writeList("y", hi_y); + highscoreTable->writeList("seconds", hi_sec); + highscoreTable->writeList("date", hi_date); + highscoreTable->writeList("gravity", hi_grav); + highscoreTable->writeList("name", hi_name); + highscoreTable->sync(); +} + +void App::showHighscore(int focusitem) +{ + // this may look a little bit confusing... + QDialog *dlg = new QDialog(0, "hall_Of_fame", true); + dlg->setCaption(i18n("Hall of Fame")); + + QVBoxLayout *tl = new QVBoxLayout(dlg, 10); + + QLabel *l = new QLabel(i18n("Hall of Fame"), dlg); + QFont f = font(); + f.setPointSize(24); + f.setBold(true); + l->setFont(f); + l->setFixedSize(l->sizeHint()); + l->setFixedWidth(l->width() + 32); + l->setAlignment(AlignCenter); + tl->addWidget(l); + + // insert highscores in a gridlayout + QGridLayout *table = new QGridLayout(12, 5, 5); + tl->addLayout(table, 1); + + // add a separator line + KSeparator *sep = new KSeparator(dlg); + table->addMultiCellWidget(sep, 1, 1, 0, 4); + + // add titles + f = font(); + f.setBold(true); + l = new QLabel(i18n("Rank"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 0); + l = new QLabel(i18n("Name"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 1); + l = new QLabel(i18n("Time"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 2); + l = new QLabel(i18n("Size"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint()); + table->addWidget(l, 0, 3); + l = new QLabel(i18n("Score"), dlg); + l->setFont(f); + l->setMinimumSize(l->sizeHint().width()*3, l->sizeHint().height()); + table->addWidget(l, 0, 4); + + QString s; + QLabel *e[10][5]; + unsigned i, j; + + for(i = 0; i < 10; i++) + { + HighScore hs; + if(i < highscore.size()) + hs = highscore[i]; + + // insert rank + s.sprintf("%d", i+1); + e[i][0] = new QLabel(s, dlg); + + // insert name + if(i < highscore.size()) + e[i][1] = new QLabel(hs.name, dlg); + else + e[i][1] = new QLabel("", dlg); + + // insert time + QTime ti(0,0,0); + if(i < highscore.size()) + { + ti = ti.addSecs(hs.seconds); + s.sprintf("%02d:%02d:%02d", ti.hour(), ti.minute(), ti.second()); + e[i][2] = new QLabel(s, dlg); + } + else + { + e[i][2] = new QLabel("", dlg); + } + + // insert size + if(i < highscore.size()) + s.sprintf("%d x %d", hs.x, hs.y); + else + s = ""; + + e[i][3] = new QLabel(s, dlg); + + // insert score + if(i < highscore.size()) + { + s = QString("%1 %2") + .arg(getScore(hs)) + .arg(hs.gravity ? i18n("(gravity)") : QString("")); + } + else + { + s = ""; + } + + e[i][4] = new QLabel(s, dlg); + e[i][4]->setAlignment(AlignRight); + } + + f = font(); + f.setBold(true); + f.setItalic(true); + for(i = 0; i < 10; i++) + { + for(j = 0; j < 5; j++) + { + e[i][j]->setMinimumHeight(e[i][j]->sizeHint().height()); + + if(j == 1) + e[i][j]->setMinimumWidth(std::max(e[i][j]->sizeHint().width(), 100)); + else + e[i][j]->setMinimumWidth(std::max(e[i][j]->sizeHint().width(), 60)); + + if((int)i == focusitem) + e[i][j]->setFont(f); + + table->addWidget(e[i][j], i+2, j, AlignCenter); + } + } + + QPushButton *b = new KPushButton(KStdGuiItem::close(), dlg); + + b->setFixedSize(b->sizeHint()); + + // connect the "Close"-button to done + connect(b, SIGNAL(clicked()), dlg, SLOT(accept())); + b->setDefault(true); + b->setFocus(); + + // make layout + tl->addSpacing(10); + tl->addWidget(b); + tl->activate(); + tl->freeze(); + + dlg->exec(); + delete dlg; +} + +void App::keyBindings() +{ + KKeyDialog::configure(actionCollection(), this); +} + +/** + * Show Settings dialog. + */ +void App::showSettings(){ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Prefs::self(), KDialogBase::Swallow); + Settings *general = new Settings(0, "General"); + dialog->addPage(general, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + connect(dialog, SIGNAL(settingsChanged()), board, SLOT(loadSettings())); + dialog->show(); +} + +#include "app.moc" diff --git a/kshisen/app.h b/kshisen/app.h new file mode 100644 index 00000000..47808f9e --- /dev/null +++ b/kshisen/app.h @@ -0,0 +1,122 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + + +#ifndef __APP__H__ +#define __APP__H__ + +// Should this get the whole HAVE_SYS_TIME_H TIME_WITH_SYS_TIME treatment? +#include + +#include +#include "board.h" + +class KHighscore; + +struct HighScore +{ + QString name; + int seconds; + int x, y; + time_t date; + int gravity; +}; + +const unsigned HIGHSCORE_MAX = 10; + +class App : public KMainWindow +{ + Q_OBJECT + +public: + App(QWidget *parent = 0, const char *name=0); + +private slots: + void loadSettings(); + + void slotEndOfGame(); + void enableItems(); + void updateScore(); + void showSettings(); + + void newGame(); + void quitGame(); + void restartGame(); + void isSolvable(); + void pause(); + void undo(); + void redo(); + void hint(); + void hallOfFame(); + void keyBindings(); + void boardResized(); + +private: + void lockMenus(bool); + QString getPlayerName(); + + /** + * Read the old (pre- @ref KHighscore) highscore table. + * + * This reads the config file first, then saves it in the new format and + * re-reads it again as a KHighscore table. + **/ + void readOldHighscore(); + void readHighscore(); + void writeHighscore(); + int insertHighscore(const HighScore &); + int getScore(const HighScore &); + bool isBetter(const HighScore &, const HighScore &); + void showHighscore(int focusitem = -1); + + void initKAction(); + void setCheatMode(); + void resetCheatMode(); + +private: + QString lastPlayerName; + Board *board; + QValueVector highscore; + KHighscore* highscoreTable; + bool cheat; + + enum statusBarItems { SBI_TIME, SBI_TILES, SBI_CHEAT }; + +}; + +#endif diff --git a/kshisen/board.cpp b/kshisen/board.cpp new file mode 100644 index 00000000..ab61a912 --- /dev/null +++ b/kshisen/board.cpp @@ -0,0 +1,1082 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "board.h" +#include "prefs.h" + +#define EMPTY 0 +#define DEFAULTDELAY 500 +#define DEFAULTSHUFFLE 4 + +static int size_x[5] = {14, 18, 24, 26, 30}; +static int size_y[5] = { 6, 8, 12, 14, 16}; +static int DELAY[5] = {1000, 750, 500, 250, 125}; + +Board::Board(QWidget *parent, const char *name) : + QWidget(parent, name, WResizeNoErase), field(0), + _x_tiles(0), _y_tiles(0), + _delay(125), paused(false), + gravity_flag(true), _solvable_flag(true), + grav_col_1(-1), grav_col_2(-1), highlighted_tile(-1) +{ + // Randomize + setShuffle(DEFAULTSHUFFLE); + + random.setSeed(0); + starttime = time((time_t *)0); + + setDelay(DEFAULTDELAY); + _redo.setAutoDelete(true); + _undo.setAutoDelete(true); + + QPixmap bg(KGlobal::dirs()->findResource("appdata", "kshisen_bgnd.png")); + setBackgroundPixmap(bg); + + loadSettings(); +} + +Board::~Board() +{ + delete [] field; +} + +void Board::loadSettings(){ + int index = Prefs::size(); + setSize(size_x[index], size_y[index]); + + setShuffle(Prefs::level() * 4 + 1); + setGravityFlag(Prefs::gravity()); + setSolvableFlag(Prefs::solvable()); + setDelay(DELAY[Prefs::speed()]); +} + +int Board::x_tiles() const +{ + return _x_tiles; +} + +int Board::y_tiles() const +{ + return _y_tiles; +} + +void Board::setField(int x, int y, int value) +{ + if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) + { + kdFatal() << "Attempted write to invalid field position " + "(" << x << ", " << y << ")" << endl; + } + + field[y * x_tiles() + x] = value; +} + +int Board::getField(int x, int y) const +{ +#ifdef DEBUGGING + if(x < -1 || y < -1 || x > x_tiles() || y > y_tiles()) + { + kdFatal() << "Attempted read from invalid field position " + "(" << x << ", " << y << ")" << endl; + } +#endif + + if(x < 0 || y < 0 || x >= x_tiles() || y >= y_tiles()) + return EMPTY; + + return field[y * x_tiles() + x]; +} + +void Board::gravity(int col, bool update) +{ + if(gravity_flag) + { + int rptr = y_tiles()-1, wptr = y_tiles()-1; + while(rptr >= 0) + { + if(getField(col, wptr) != EMPTY) + { + rptr--; + wptr--; + } + else + { + if(getField(col, rptr) != EMPTY) + { + setField(col, wptr, getField(col, rptr)); + setField(col, rptr, EMPTY); + if(update) + { + updateField(col, rptr); + updateField(col, wptr); + } + wptr--; + rptr--; + } + else + rptr--; + } + } + } +} + +void Board::mousePressEvent(QMouseEvent *e) +{ + // Calculate field position + int pos_x = (e->pos().x() - xOffset()) / tiles.tileWidth(); + int pos_y = (e->pos().y() - yOffset()) / tiles.tileHeight(); + + if(e->pos().x() < xOffset() || e->pos().y() < yOffset() || + pos_x >= x_tiles() || pos_y >= y_tiles()) + { + pos_x = -1; + pos_y = -1; + } + + // Mark tile + if(e->button() == LeftButton) + { + clearHighlight(); + + if(pos_x != -1) + marked(pos_x, pos_y); + } + + // Assist by highlighting all tiles of same type + if(e->button() == RightButton) + { + int clicked_tile = getField(pos_x, pos_y); + + // Clear marked tile + if(mark_x != -1 && getField(mark_x, mark_y) != clicked_tile) + { + // We need to set mark_x and mark_y to -1 before calling + // updateField() to ensure the tile is redrawn as unmarked. + int oldmarkx = mark_x; + int oldmarky = mark_y; + mark_x = -1; + mark_y = -1; + updateField(oldmarkx, oldmarky, false); + } + else + { + mark_x = -1; + mark_y = -1; + } + + // Perform highlighting + if(clicked_tile != highlighted_tile) + { + int old_highlighted = highlighted_tile; + highlighted_tile = clicked_tile; + for(int i = 0; i < x_tiles(); i++) + { + for(int j = 0; j < y_tiles(); j++) + { + const int field_tile = getField(i, j); + if(field_tile != EMPTY) + { + if(field_tile == old_highlighted) + updateField(i, j, false); + else if(field_tile == clicked_tile) + updateField(i, j, false); + } + } + } + } + } +} + +// The board is centred inside the main playing area. xOffset/yOffset provide +// the coordinates of the top-left corner of the board. +int Board::xOffset() const +{ + return (width() - (tiles.tileWidth() * x_tiles())) / 2; +} + +int Board::yOffset() const +{ + return (height() - (tiles.tileHeight() * y_tiles())) / 2; +} + +void Board::setSize(int x, int y) +{ + if(x == x_tiles() && y == y_tiles()) + return; + + if(field != 0) + delete [] field; + + field = new int[ x * y ]; + _x_tiles = x; + _y_tiles = y; + for(int i = 0; i < x; i++) + for(int j = 0; j < y; j++) + setField(i, j, EMPTY); + + // set the minimum size of the scalable window + const double MINIMUM_SCALE = 0.2; + int w = qRound(tiles.unscaledTileWidth() * MINIMUM_SCALE) * x_tiles(); + int h = qRound(tiles.unscaledTileHeight() * MINIMUM_SCALE) * y_tiles(); + w += tiles.unscaledTileWidth(); + h += tiles.unscaledTileWidth(); + + setMinimumSize(w, h); + + resizeBoard(); + newGame(); + emit changed(); +} + +void Board::resizeEvent(QResizeEvent*) +{ + resizeBoard(); + emit resized(); +} + +void Board::resizeBoard() +{ + // calculate tile size required to fit all tiles in the window + int w = static_cast( static_cast(width() - tiles.unscaledTileWidth()) / x_tiles() ); + int h = static_cast( static_cast(height() - tiles.unscaledTileWidth()) / y_tiles() ); + + const double MAXIMUM_SCALE = 2.0; + w = std::min(w, static_cast((tiles.unscaledTileWidth() * MAXIMUM_SCALE) + 0.5)); + h = std::min(h, static_cast((tiles.unscaledTileHeight() * MAXIMUM_SCALE) + 0.5)); + + tiles.resizeTiles(w, h); +} + +QSize Board::unscaledSize() const +{ + int w = tiles.unscaledTileWidth() * x_tiles() + tiles.unscaledTileWidth(); + int h = tiles.unscaledTileHeight() * y_tiles() + tiles.unscaledTileWidth(); + return QSize(w, h); +} + +void Board::newGame() +{ + //kdDebug() << "NewGame" << endl; + int i, x, y, k; + + mark_x = -1; + mark_y = -1; + highlighted_tile = -1; // will clear previous highlight + + _undo.clear(); + _redo.clear(); + connection.clear(); + + // distribute all tiles on board + int cur_tile = 1; + for(y = 0; y < y_tiles(); y += 4) + { + for(x = 0; x < x_tiles(); ++x) + { + for(k = 0; k < 4 && y + k < y_tiles(); k++) + setField(x, y + k, cur_tile); + + cur_tile++; + if(cur_tile > TileSet::nTiles) + cur_tile = 1; + } + } + + if(getShuffle() == 0) + { + update(); + starttime = time((time_t *)0); + emit changed(); + return; + } + + // shuffle the field + int tx = x_tiles(); + int ty = y_tiles(); + for(i = 0; i < x_tiles() * y_tiles() * getShuffle(); i++) + { + int x1 = random.getLong(tx); + int y1 = random.getLong(ty); + int x2 = random.getLong(tx); + int y2 = random.getLong(ty); + int t = getField(x1, y1); + setField(x1, y1, getField(x2, y2)); + setField(x2, y2, t); + } + + // do not make solvable if _solvable_flag is false + if(!_solvable_flag) + { + update(); + starttime = time((time_t *)0); + emit changed(); + return; + } + + + int fsize = x_tiles() * y_tiles() * sizeof(int); + int *oldfield = new int[x_tiles() * y_tiles()]; + memcpy(oldfield, field, fsize); // save field + int *tiles = new int[x_tiles() * y_tiles()]; + int *pos = new int[x_tiles() * y_tiles()]; + + while(!solvable(true)) + { + //kdDebug() << "Not solvable" << endl; + //dumpBoard(); + + // generate a list of free tiles and positions + int num_tiles = 0; + for(i = 0; i < x_tiles() * y_tiles(); i++) + if(field[i] != EMPTY) + { + pos[num_tiles] = i; + tiles[num_tiles] = field[i]; + num_tiles++; + } + + // restore field + memcpy(field, oldfield, fsize); + + // redistribute unsolved tiles + while(num_tiles > 0) + { + // get a random tile + int r1 = random.getLong(num_tiles); + int r2 = random.getLong(num_tiles); + int tile = tiles[r1]; + int apos = pos[r2]; + + // truncate list + tiles[r1] = tiles[num_tiles-1]; + pos[r2] = pos[num_tiles-1]; + num_tiles--; + + // put this tile on the new position + field[apos] = tile; + } + + // remember field + memcpy(oldfield, field, fsize); + } + + + // restore field + memcpy(field, oldfield, fsize); + delete tiles; + delete pos; + delete oldfield; + + update(); + starttime = time((time_t *)0); + emit changed(); +} + +bool Board::isTileHighlighted(int x, int y) const +{ + if(x == mark_x && y == mark_y) + return true; + + if(getField(x, y) == highlighted_tile) + return true; + + if(!connection.empty()) + { + if(x == connection.front().x && y == connection.front().y) + return true; + + if(x == connection.back().x && y == connection.back().y) + return true; + } + + return false; +} + +void Board::updateField(int x, int y, bool erase) +{ + QRect r(xOffset() + x * tiles.tileWidth(), + yOffset() + y * tiles.tileHeight(), + tiles.tileWidth(), + tiles.tileHeight()); + + repaint(r, erase); +} + +void Board::paintEvent(QPaintEvent *e) +{ + + QRect ur = e->rect(); // rectangle to update + QPixmap pm(ur.size()); // Pixmap for double-buffering + pm.fill(this, ur.topLeft()); // fill with widget background + QPainter p(&pm); + p.translate(-ur.x(), -ur.y()); // use widget coordinate system + + if(paused) + { + p.setFont(KGlobalSettings::largeFont()); + p.drawText(rect(), Qt::AlignCenter, i18n("Game Paused")); + } + else + { + int w = tiles.tileWidth(); + int h = tiles.tileHeight(); + for(int i = 0; i < x_tiles(); i++) + { + for(int j = 0; j < y_tiles(); j++) + { + int tile = getField(i, j); + if(tile == EMPTY) + continue; + + int xpos = xOffset() + i * w; + int ypos = yOffset() + j * h; + QRect r(xpos, ypos, w, h); + if(e->rect().intersects(r)) + { + if(isTileHighlighted(i, j)) + p.drawPixmap(xpos, ypos, tiles.highlightedTile(tile-1)); + else + p.drawPixmap(xpos, ypos, tiles.tile(tile-1)); + } + } + } + } + p.end(); + bitBlt( this, ur.topLeft(), &pm ); +} + +void Board::marked(int x, int y) +{ + // make sure that the previous connection is correctly undrawn + undrawConnection(); + + if(getField(x, y) == EMPTY) + return; + + if(x == mark_x && y == mark_y) + { + // unmark the piece + mark_x = -1; + mark_y = -1; + updateField(x, y, false); + return; + } + + if(mark_x == -1) + { + mark_x = x; + mark_y = y; + updateField(x, y, false); + return; + } + + int fld1 = getField(mark_x, mark_y); + int fld2 = getField(x, y); + + // both field same? + if(fld1 != fld2) + return; + + // trace + if(findPath(mark_x, mark_y, x, y, connection)) + { + madeMove(mark_x, mark_y, x, y); + drawConnection(getDelay()); + setField(mark_x, mark_y, EMPTY); + setField(x, y, EMPTY); + grav_col_1 = x; + grav_col_2 = mark_x; + mark_x = -1; + mark_y = -1; + + // game is over? + // Must delay until after tiles fall to make this test + // See undrawConnection GP. + } + else + { + connection.clear(); + } +} + + +void Board::clearHighlight() +{ + if(highlighted_tile != -1) + { + int old_highlight = highlighted_tile; + highlighted_tile = -1; + + for(int i = 0; i < x_tiles(); i++) + for(int j = 0; j < y_tiles(); j++) + if(old_highlight == getField(i, j)) + updateField(i, j, false); + } +} + +// Can we make a path between two tiles with a single line? +bool Board::canMakePath(int x1, int y1, int x2, int y2) const +{ + if(x1 == x2) + { + for(int i = std::min(y1, y2) + 1; i < std::max(y1, y2); i++) + if(getField(x1, i) != EMPTY) + return false; + + return true; + } + + if(y1 == y2) + { + for(int i = std::min(x1, x2) + 1; i < std::max(x1, x2); i++) + if(getField(i, y1) != EMPTY) + return false; + + return true; + } + + return false; +} + +bool Board::findPath(int x1, int y1, int x2, int y2, Path& p) const +{ + p.clear(); + + if(findSimplePath(x1, y1, x2, y2, p)) + return true; + + // Find a path of 3 segments + const int dx[4] = { 1, 0, -1, 0 }; + const int dy[4] = { 0, 1, 0, -1 }; + + for(int i = 0; i < 4; i++) + { + int newx = x1 + dx[i]; + int newy = y1 + dy[i]; + while(newx >= -1 && newx <= x_tiles() && + newy >= -1 && newy <= y_tiles() && + getField(newx, newy) == EMPTY) + { + if(findSimplePath(newx, newy, x2, y2, p)) + { + p.push_front(Position(x1, y1)); + return true; + } + newx += dx[i]; + newy += dy[i]; + } + } + + return false; +} + +// Find a path of 1 or 2 segments between tiles. Returns whether +// a path was found, and if so, the path is returned via 'p'. +bool Board::findSimplePath(int x1, int y1, int x2, int y2, Path& p) const +{ + // Find direct line (path of 1 segment) + if(canMakePath(x1, y1, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x2, y2)); + return true; + } + + // If the tiles are in the same row or column, then a + // a 'simple path' cannot be found between them + if(x1 == x2 || y1 == y2) + return false; + + // Find path of 2 segments (route A) + if(getField(x2, y1) == EMPTY && canMakePath(x1, y1, x2, y1) && + canMakePath(x2, y1, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x2, y1)); + p.push_back(Position(x2, y2)); + return true; + } + + // Find path of 2 segments (route B) + if(getField(x1, y2) == EMPTY && canMakePath(x1, y1, x1, y2) && + canMakePath(x1, y2, x2, y2)) + { + p.push_back(Position(x1, y1)); + p.push_back(Position(x1, y2)); + p.push_back(Position(x2, y2)); + return true; + } + + return false; +} + +void Board::drawConnection(int timeout) +{ + if(connection.empty()) + return; + + // lighten the fields + updateField(connection.front().x, connection.front().y); + updateField(connection.back().x, connection.back().y); + + QPainter p; + p.begin(this); + p.setPen(QPen(QColor("red"), tiles.lineWidth())); + + // Path.size() will always be >= 2 + Path::const_iterator pathEnd = connection.end(); + Path::const_iterator pt1 = connection.begin(); + Path::const_iterator pt2 = pt1; + ++pt2; + while(pt2 != pathEnd) + { + p.drawLine( midCoord(pt1->x, pt1->y), midCoord(pt2->x, pt2->y) ); + ++pt1; + ++pt2; + } + + p.flush(); + p.end(); + + QTimer::singleShot(timeout, this, SLOT(undrawConnection())); +} + +void Board::undrawConnection() +{ + if(grav_col_1 != -1 || grav_col_2 != -1) + { + gravity(grav_col_1, true); + gravity(grav_col_2, true); + grav_col_1 = -1; + grav_col_2 = -1; + } + + // is already undrawn? + if(connection.empty()) + return; + + // Redraw all affected fields + + Path oldConnection = connection; + connection.clear(); + + // Path.size() will always be >= 2 + Path::const_iterator pathEnd = oldConnection.end(); + Path::const_iterator pt1 = oldConnection.begin(); + Path::const_iterator pt2 = pt1; + ++pt2; + while(pt2 != pathEnd) + { + if(pt1->y == pt2->y) + { + for(int i = std::min(pt1->x, pt2->x); i <= std::max(pt1->x, pt2->x); i++) + updateField(i, pt1->y); + } + else + { + for(int i = std::min(pt1->y, pt2->y); i <= std::max(pt1->y, pt2->y); i++) + updateField(pt1->x, i); + } + ++pt1; + ++pt2; + } + + Path dummyPath; + // game is over? + if(!getHint_I(dummyPath)) + { + time_for_game = (int)difftime( time((time_t)0), starttime); + emit endOfGame(); + } +} + +QPoint Board::midCoord(int x, int y) const +{ + QPoint p; + int w = tiles.tileWidth(); + int h = tiles.tileHeight(); + + if(x == -1) + p.setX(xOffset() - (w / 4)); + else if(x == x_tiles()) + p.setX(xOffset() + (w * x_tiles()) + (w / 4)); + else + p.setX(xOffset() + (w * x) + (w / 2)); + + if(y == -1) + p.setY(yOffset() - (w / 4)); + else if(y == y_tiles()) + p.setY(yOffset() + (h * y_tiles()) + (w / 4)); + else + p.setY(yOffset() + (h * y) + (h / 2)); + + return p; +} + +void Board::setDelay(int newvalue) +{ + _delay = newvalue; +} + +int Board::getDelay() const +{ + return _delay; +} + +void Board::madeMove(int x1, int y1, int x2, int y2) +{ + Move *m = new Move(x1, y1, x2, y2, getField(x1, y1)); + _undo.append(m); + while(_redo.count()) + _redo.removeFirst(); + emit changed(); +} + +bool Board::canUndo() const +{ + return !_undo.isEmpty(); +} + +bool Board::canRedo() const +{ + return !_redo.isEmpty(); +} + +void Board::undo() +{ + if(canUndo()) + { + clearHighlight(); + undrawConnection(); + Move* m = _undo.last(); + _undo.take(); + if(gravityFlag()) + { + int y; + + // When both tiles reside in the same column, the order of undo is + // significant (we must undo the lower tile first). + if(m->x1 == m->x2 && m->y1 < m->y2) + { + std::swap(m->x1, m->x2); + std::swap(m->y1, m->y2); + } + + for(y = 0; y < m->y1; y++) + { + setField(m->x1, y, getField(m->x1, y+1)); + updateField(m->x1, y); + } + + for(y = 0; y < m->y2; y++) + { + setField(m->x2, y, getField(m->x2, y+1)); + updateField(m->x2, y); + } + } + + setField(m->x1, m->y1, m->tile); + setField(m->x2, m->y2, m->tile); + updateField(m->x1, m->y1); + updateField(m->x2, m->y2); + _redo.prepend(m); + emit changed(); + } +} + +void Board::redo() +{ + if(canRedo()) + { + clearHighlight(); + undrawConnection(); + Move* m = _redo.take(0); + setField(m->x1, m->y1, EMPTY); + setField(m->x2, m->y2, EMPTY); + updateField(m->x1, m->y1); + updateField(m->x2, m->y2); + gravity(m->x1, true); + gravity(m->x2, true); + _undo.append(m); + emit changed(); + } +} + +void Board::showHint() +{ + undrawConnection(); + + if(getHint_I(connection)) + drawConnection(1000); +} + + +#ifdef DEBUGGING +void Board::makeHintMove() +{ + Path p; + + if(getHint_I(p)) + { + mark_x = -1; + mark_y = -1; + marked(p.front().x, p.front().y); + marked(p.back().x, p.back().y); + } +} + +void Board::finish() +{ + Path p; + bool ready=false; + + while(!ready && getHint_I(p)) + { + mark_x = -1; + mark_y = -1; + if(tilesLeft() == 2) + ready = true; + marked(p.front().x, p.front().y); + marked(p.back().x, p.back().y); + kapp->processEvents(); + usleep(250*1000); + } +} + +void Board::dumpBoard() const +{ + kdDebug() << "Board contents:" << endl; + for(int y = 0; y < y_tiles(); ++y) + { + QString row; + for(int x = 0; x < x_tiles(); ++x) + { + int tile = getField(x, y); + if(tile == EMPTY) + row += " --"; + else + row += QString("%1").arg(getField(x, y), 3); + } + kdDebug() << row << endl; + } +} +#endif + +bool Board::getHint_I(Path& p) const +{ + //dumpBoard(); + short done[TileSet::nTiles]; + for( short index = 0; index < TileSet::nTiles; index++ ) + done[index] = 0; + + for(int x = 0; x < x_tiles(); x++) + { + for(int y = 0; y < y_tiles(); y++) + { + int tile = getField(x, y); + if(tile != EMPTY && done[tile - 1] != 4) + { + // for all these types of tile search path's + for(int xx = 0; xx < x_tiles(); xx++) + { + for(int yy = 0; yy < y_tiles(); yy++) + { + if(xx != x || yy != y) + { + if(getField(xx, yy) == tile) + if(findPath(x, y, xx, yy, p)) + { + //kdDebug() << "path.size() == " << p.size() << endl; + //for(Path::const_iterator i = p.begin(); i != p.end(); ++i) + // kdDebug() << "pathEntry: (" << i->x << ", " << i->y + // << ") => " << getField(i->x, i->y) << endl; + return true; + } + } + } + } + done[tile - 1]++; + } + } + } + + return false; +} + +void Board::setShuffle(int newvalue) +{ + if(newvalue != _shuffle){ + _shuffle = newvalue; + newGame(); + } +} + +int Board::getShuffle() const +{ + return _shuffle; +} + +int Board::tilesLeft() const +{ + int left = 0; + + for(int i = 0; i < x_tiles(); i++) + for(int j = 0; j < y_tiles(); j++) + if(getField(i, j) != EMPTY) + left++; + + return left; +} + +int Board::getCurrentTime() const +{ + return (int)difftime(time((time_t *)0),starttime); +} + +int Board::getTimeForGame() const +{ + if(tilesLeft() == 0) + { + return time_for_game; + } + else + { + if(paused) + return (int)difftime(pause_start, starttime); + else + return (int)difftime(time((time_t *)0), starttime); + } +} + +bool Board::solvable(bool norestore) +{ + int *oldfield = 0; + + if(!norestore) + { + oldfield = new int [x_tiles() * y_tiles()]; + memcpy(oldfield, field, x_tiles() * y_tiles() * sizeof(int)); + } + + Path p; + while(getHint_I(p)) + { + kdFatal(getField(p.front().x, p.front().y) != getField(p.back().x, p.back().y)) + << "Removing unmateched tiles: (" << p.front().x << ", " << p.front().y << ") => " + << getField(p.front().x, p.front().y) << " (" << p.back().x << ", " << p.back().y << ") => " + << getField(p.back().x, p.back().y) << endl; + setField(p.front().x, p.front().y, EMPTY); + setField(p.back().x, p.back().y, EMPTY); + //if(gravityFlag()) + //{ + // gravity(p.front().x, false); + // gravity(p.back().x, false); + //} + } + + int left = tilesLeft(); + + if(!norestore) + { + memcpy(field, oldfield, x_tiles() * y_tiles() * sizeof(int)); + delete [] oldfield; + } + + return (bool)(left == 0); +} + +bool Board::getSolvableFlag() const +{ + return _solvable_flag; +} + +void Board::setSolvableFlag(bool value) +{ + if(value && !_solvable_flag && !solvable()){ + _solvable_flag = value; + newGame(); + } + else + _solvable_flag = value; +} + +bool Board::gravityFlag() const +{ + return gravity_flag; +} + +void Board::setGravityFlag(bool b) +{ + if( gravity_flag != b ){ + if(canUndo() || canRedo()) + newGame(); + gravity_flag = b; + } +} + +bool Board::pause() +{ + paused = !paused; + if(paused) + pause_start = time((time_t *)0); + else + starttime += (time_t) difftime( time((time_t *)0), pause_start); + update(); + + return paused; +} + +QSize Board::sizeHint() const +{ + int dpi = QPaintDeviceMetrics(this).logicalDpiX(); + if (dpi < 75) + dpi = 75; + return QSize(9*dpi,7*dpi); +} + +#include "board.moc" diff --git a/kshisen/board.h b/kshisen/board.h new file mode 100644 index 00000000..c38fba57 --- /dev/null +++ b/kshisen/board.h @@ -0,0 +1,189 @@ +/* Yo Emacs, this is -*- C++ -*- + ******************************************************************* + ******************************************************************* + * + * + * KSHISEN + * + * + ******************************************************************* + * + * A japanese game similar to mahjongg + * + ******************************************************************* + * + * created 1997 by Mario Weilguni + * + ******************************************************************* + * + * This file is part of the KDE project "KSHISEN" + * + * KSHISEN is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * KSHISEN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with KSHISEN; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ******************************************************************* + */ + +#ifndef __BOARD__H__ +#define __BOARD__H__ + +// Should this get the whole HAVE_SYS_TIME_H TIME_WITH_SYS_TIME treatment? +#include + +#include +#include +#include "tileset.h" +#include "debug.h" + +struct Position +{ + Position() : x(0), y(0) { } + Position(int _x, int _y) : x(_x), y(_y) { } + int x; + int y; +}; + +typedef std::list Path; + +class Move +{ +public: + Move(int _x1, int _y1, int _x2, int _y2, int _tile) : + x1(_x1), y1(_y1), x2(_x2), y2(_y2), tile(_tile) { } + + int x1, y1, x2, y2; + int tile; +}; + +class Board : public QWidget +{ + Q_OBJECT + +public: + Board(QWidget *parent = 0, const char *name=0); + ~Board(); + + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void resizeEvent(QResizeEvent*); + + void setDelay(int); + int getDelay() const; + + bool canUndo() const; + bool canRedo() const; + void redo(); + void undo(); + + void setSize(int x, int y); + void resizeBoard(); + QSize unscaledSize() const; + void newGame(); + void setShuffle(int); + int getShuffle() const; + + void showHint(); + bool getHint_I(Path& p) const; + +#ifdef DEBUGGING + void makeHintMove(); + void finish(); + void dumpBoard() const; +#endif + + int tilesLeft() const; + int getCurrentTime() const; + int getTimeForGame() const; + + bool solvable(bool norestore = FALSE); + + bool getSolvableFlag() const; + void setSolvableFlag(bool); + bool gravityFlag() const; + void setGravityFlag(bool); + + int x_tiles() const; + int y_tiles() const; + + bool isPaused() const { return paused; } + +signals: + void markMatched(); + void changed(); + void endOfGame(); + void resized(); + +public slots: + bool pause(); + void loadSettings(); + +private slots: + void undrawConnection(); + void gravity(int, bool); + +protected: + virtual QSize sizeHint() const; + +private: // functions + void initBoard(); + + int xOffset() const; + int yOffset() const; + + void setField(int x, int y, int value); + int getField(int x, int y) const; + void updateField(int, int, bool erase = true); + void clearHighlight(); + bool canMakePath(int x1, int y1, int x2, int y2) const; + bool findPath(int x1, int y1, int x2, int y2, Path& p) const; + bool findSimplePath(int x1, int y1, int x2, int y2, Path& p) const; + bool isTileHighlighted(int x, int y) const; + void drawConnection(int timeout); + QPoint midCoord(int x, int y) const; + void marked(int x, int y); + void madeMove(int x1, int y1, int x2, int y2); + +private: + time_t starttime; + time_t time_for_game; + + TileSet tiles; + + KRandomSequence random; + + QPtrList _undo; + QPtrList _redo; + + int undraw_timer_id; + int mark_x; + int mark_y; + Path connection; + int *field; + int _x_tiles; + int _y_tiles; + int _delay; + int _shuffle; + + bool paused; + time_t pause_start; + + bool gravity_flag; + bool _solvable_flag; + int grav_col_1, grav_col_2; + + int highlighted_tile; +}; + +#endif diff --git a/kshisen/debug.h b/kshisen/debug.h new file mode 100644 index 00000000..aa79a7e5 --- /dev/null +++ b/kshisen/debug.h @@ -0,0 +1 @@ +//#define DEBUGGING diff --git a/kshisen/hi128-app-kshisen.png b/kshisen/hi128-app-kshisen.png new file mode 100644 index 0000000000000000000000000000000000000000..8badc8f9786d106d724a37b526da1e098e7facd6 GIT binary patch literal 12289 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
    ~~x4XEG{ncymut`!c@Q9<`2moL9(JNylh_ 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 literal 0 HcmV?d00001 diff --git a/kshisen/hi32-app-kshisen.png b/kshisen/hi32-app-kshisen.png new file mode 100644 index 0000000000000000000000000000000000000000..7f7ac9d9ecafc89b1ddd36da63614bf06c59b057 GIT binary patch literal 1967 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 literal 0 HcmV?d00001 diff --git a/kshisen/hi64-app-kshisen.png b/kshisen/hi64-app-kshisen.png new file mode 100644 index 0000000000000000000000000000000000000000..7ec56314e92383ec5eedb5636e7c52876e99b55f GIT binary patch literal 5039 zcmV;g6Hx4lP)Xr`DL!QJ2dCn)D{+O86mrTHvjj}p@g}p_7!U@r7+D}df+y+i`!-wm zJ^65#zCH8oFA1NlR*h!f?R)RJ=bZmJ=YMZET)Zw`7q5%g#p~Di3cT;|_!mEUpfe1w zY$!49Ny`A`{>!wuuGtYuwoO44gpJj>cKm&Jefu;Z=1%y+7eDpULLoXRqGcYJax_lZS71win3zx)2)kL@4+ukLl7M>;y%37lQ@B_cFm zf~3pGvx%tIc1izIiLki5#L~*@J=gx#!+(0mjeBx`^RWXD^^}W8ik)2$1pWZBqNU|a zGWP^gExmQS==D~r6{e@>Mox`RfAG^^ed6^T(DuQ-{dW!Y^&A--?9=_j2%%5|5L%nHSt z#IuapJ4pknEmNYKwnE#MO1<%q!0kDpn2Ums4)pix5j~q>U7ZTv={I|mdKC2c_%KwA+8cWT_N6@#LhAlZbsCc)y3(|?47L%WGro+gY! z8nN`|6A7YRPNQyMhnC$Ea(aB)+~Q*6_-46HbFCXf7RHd(Iie`)vvx zZdKR{ZSs&ntMP0fV+0DFNM)5qlH}`gqGcC4AqZ3Dyss8WxhYdQYs$49eOnc}V0qT+ z*`5PJWew2ed1)4`vaqsaT9c&8cm`B9R*hmxNth_5We1UyEp`rXI=z}qTqz-Jn-{J=*)L=^pjt1 zl`GaL>7bmxVWtyBI6V!$y{QUoz)*gP!ZsnUl`{|s78a0VF^ypX5^V$_n}F@OYNN(% zeTJ@{h>4YP8md7=lraz{-p|aA&{+;qagD{=BJ0|UEj-1hQX4H{x^@k_@aWeGe|U`W z?t9?%Hw+>a%D_|n5D*V$&Z|wK5+~%p{Lh3h|D5P6{{j=^dJ#U6u?bjZ$BHPv^y9Bk z?^>iZ81dNnqdfNdlUi{bT1v0&^_jDL^VRz(4d&<`4Ef9N-<4UXC=;r-(i!nND_@LT zlh58s_}6zK8#ki1UYhYrP^}{MdWr@c$7V89OObpI3T?I%@im9Y{rkN%K6M9bZ~(?< z5rjmEku4|@BCo7jezB#znbGAll*;YMV#07&zl&=h{@sNl<$Re^Sy8Pd46DmbrAc)| zL>r7WbaNI%4uaqPK62))nYiB_YlDqFA8h6}=+Ik7eH3yMgXk>}W z&VbUTDRK)oY0l;jAd2#tr0VYxY`Z8S3L-)xTKS4{{cHkG5g__@C<^j9Jy||a<4^w3 zY>E`g)$b)5@29HIbXfzdi@{V?jP!-!JUKld3V2tg%CF6S-4 zqEZ=OnppKw0X%;8Y36HlNTrSEMqlL6;FWCb9%`vBtBnKGvJaF>6+_w9J zETa#4j$w!;|odK`5JO`nL^ zn|doPp&Eke)

    WUpc*9$!W4pl*wBu*!{ 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 literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/pyramid.layout b/kmahjongg/pics/pyramid.layout new file mode 100644 index 00000000..edd634dc --- /dev/null +++ b/kmahjongg/pics/pyramid.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +....121212121212121212121212.... +....434343434343434343434343.... +# Level 1 ------------------------- +................................ +................................ +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +......12121212121212121212...... +......43434343434343434343...... +................................ +................................ +# Level 2 ------------------------- +................................ +................................ +................................ +................................ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +........1212121212121212........ +........4343434343434343........ +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +..........121212121212.......... +..........434343434343.......... +..........121212121212.......... +..........434343434343.......... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +................................ +................................ +................................ +................................ +................................ +................................ +................................ +............12121212............ +............43434343............ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/runes.tileset b/kmahjongg/pics/runes.tileset new file mode 100644 index 0000000000000000000000000000000000000000..30106ae245a9038c1ad9751c768a9d3069efb683 GIT binary patch literal 302454 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%0DjY18^=FM60)?)W*j7$veDt7}FYEuO>t#^g z@UQ?!T{|BJ*fn@kZ8OC@?8z!*)+u_P5gQd)^G?M-Z06SR)$i0XxJpx&{d}kBadR!i z7L&{#MZafC<(tt2=1R!fuY+^5q$MbEAtuoJ2Alfr6@1lgJpH|U)xUcGM%qX!kzm8*t&PmC z2i*Q@;XrNAAVdt7aa1xKDPu|AuA!+M2tfnNfD#utEaUX1g4O+Y>F-O<-#^+%!}V<) zAC%PGXlMhJI)Mht)6i>4QC-gMBe8y#xbcheoK;6GnO0!)*NO*$2N=QPf6noS(&}O> z&9{l{Dhjib!n|EesOapk6A$8g2I?4{u!w>v=qqog-oiECsUt=+2Ao-CMp?g*E3;A6 zv$`WQXooaeh^bTbxVhP97ZQwaxx_gx8;Qz2Q@8O~|9)X6u|}2Y&%2M*RMDxb;z-}S zdH;j^wV8!U>wsDbqvxZthRmy3>C5jNUt-7)-CzlT{9a+}td; z(A9SMCG?r0Em71%eWNqIBjeXe+=_OQ=S`DWU=1ZMtQUn5MJia`C9)x6ykFg1$(B`e z`*H9b4f*ZMHp5f4_drKCJFPUvwFn@5`Gbl4>F;pR(kV05?fcQBCg5DF?f0A zz=zcry~xPQjuLlnd4s7iK$ih$NlN>*W{$!;QAQWss&4wUilBG6z-*>Y1zU2v_M&2- z#LX&g>hv9M2eV(bP`5ZkVnerm7u`ff4EO7)xy{X_qqk21lpR4l+^V0*BSS zK42JlhTB||P=ga{AXqiaAy_%v20g8sAt1lDL(0&*Qvt1-C#U>8^P*{<(_xdSutKRS z=zKzZSL&@aVg-Q^rN4;TIej{bGteY8z`dTMHaGvA&nZv<`GC2c#3&=tMTVi?kqFN; z$}{=k;2M2O?_`@)SH+TX)z+o_AD3bBH{Gb9U6Hswlc<-7AaYeO&m?}|^%??QH$qi8 zpd=mEz(+sBcq6{L3DiN~0f>5m>a7Qjr7W2)7z4@8s>W;OjZR-EQwPWGyIDg*^fVf)T zaV>cX(df962%+7XGD*iZdA~mTDqiD^NX3~$C^fu zn`QZG2y}!A3XQL)1S~0)_S6r=%QtM zl{@HW_9*f6j=SYGx65k}PEHXEnI%MtySTms0U;HvI*dNPEhKhLibp1!L<1!>VNEON zZbM5co<!N4xB&j!c2?Q#I=Urt)+P5zn)I~nf~b6rM>o0}0Nmth_gF3d2)JTuHT_qrw|#u2JyfT8t_ zF73J(cg2n{xud6%#)hM$vhMKVBgCSng(z_on|rOjhMu7a@GKsV!d;KjYpG<(h+J_= z6BUleu70ZA!Zmt9A$3rs1N6?kD|S%grpnns=TSR*DV!drM8i|7##0*G#Y(YrETwbpXM3M=8Rk*?o2oB(qYdx1;q5lO^6?{ z&h2udP}Q!si)_I@_arDNM`t0LNrxkCeFkP%&n0nF+1n+WZk@fy8tAcypspcis{Ao1 z3Ba)=Dz*sWOPMRd%`zezbl4(siE|7*?X!7X*~se*vvQ?QQSzLX*T1kGO6(6s7nw>Y ztemt&6UOiYd>=YGy(n|f8KbKz?`(^BFe_K)DSF)8MwMv?YZ^Un+VaP>WJ0G57XLuR zpbxlGFa4{!wQaHiSQlN&5RpW0cqju?C~;SI4wcTJCNK}3h@)}d`RN*RrqnTazwoZUQClXk^+I7stLb1( z9jvT_^>r!v!semEGt)CPJ-PW10!WW#l*aFcfk$;ib2+YQ*oUyw3rgJ8okO5k8;k>o zOyIM%`mY7ziR)S(mTWKx3uTkd;_b${lnw3;faaO zQ}6O|WaHsrc)>oqnHe@y?xXod=`W%wxoKp0Dyi^HpF8LX(DKjv=4QVPE?8^iNL{gw zk=b>}WU{MQ6Hl(@LLf@e<03YyL8ph()l28~v~*$EqzFoa9ye8NS7hcB9OZmc{d^&` zm8Tzqb-55jV72qRrMMQ^fW$NPK@)3!Z3ofKv=Alk+U}8VWXu#vKqqSNGu^5orY)or zQpdO@l8A0T8lKx42+k2ZK-!wL=KMGs{t(5_lFEJhco#r*2q29Dy!un_7h-v?I33HwAi!CvDTKhOtF` zaK1xrXLn!OcWu$+j=&b_wnw>!K!@JvoZZL`o0%l!OQZA`F}up<@y1r66&c4CwIiXn zj=Zbda)bHd;0-v*-Tknx9==v+>NS5RsGx6YJi+Rgnpll}3YWj;!=s zJu-W!TW#*O`XPGgEe^1g#XLMd+^T3Gvbt$qGU&Vlouj(Bt+XEbi!d~}>7v3Hg7BRn zd@Tsy48oU#@ckfMa)&2bB1H>V0uQGuhhI4yu|^W^xvjC)llb0~nU&qe&4aXsbOI&r z#=%38$?Ke11D+vRnZ_5*kp#pgpmZd@Egg!oG$Vo8q`?z`tG3ByxZ0_6a)f!mWJHOZ zE@x&Ui|4GcnW>qZz6`F$F&c<(m?KLNwDsg_U`V5_jw1)Z(A^ z%x^&_;%RJI&nanx`rx8tW_$JYb7OF>jBwGiRW!K1`CSMd!Ng%8aR70{6r6+5QB7gv zIlbxb%o6nz*&=GEbZ~UWGQQ-Q-!_Hg3VRUxb-GlU2{gEAeb|uz_9cMb31E)`xcj*M zVTcv*NJ8awGlfc>d(s`-2qzDxx6W2iJ{yhiYWz#~@uhV3%mhl@t;0t$ThJTd0iI#B z>|59!Upt9zoehSUcskGg+J4HW!98Z1NCFAY>D6w%i<^Ju0VQr`QPb(n7M>29>8lqf zE`zHd35_RqhNsuSL6dKSiS&JOKJ?4rg3TtTD7t(wv-vQxd>~dC5~~~efU@N1anp@z zXmX{EC#okiSq>oEsoF#M=&q};D=B+%G*E9!jVyj>VQZnK~tHr155 zqOh$8j4=cz_s7-EjUD3ieTuX$*k=Z|P3Vv+ZqyK36jo(m_LCO=<@ZD0IXbh4V=#9F zok*Y^$`?0)%!!pF)5O;D(epZb-j{=*hSFaM9iD~5=-ScZ(PtCuN5RCdP;DW!cMnY@ zp(MaOSF8}L&3fOAYi13KH?jV35e7Wd%WN6)EEY=KRI8|^b-_MJup5#mwvMN<`3fV2 z#cyKr2fUMG%Lh|irwfPAH_l#Vx&x=#N9bQQw)uTlLz44*vjG9%8_Gw zqt`iU^3SI1q<$^F=vAX$2PqE^r8x?G{ z6^L(o<~IC`JChqHb9+w~51u&VhX-JMDcOnSgZf=n**!x~C|;q5AH zfe35qX+}Tn^#pqq+-u?8s3Uiq&-bze3GC+v+cbeMzrXnawv1?ZM0!RdcR31pt%k?| zO5Bm~v^_W-O6)-=l3KjCYMK$PrG&tFB)R9Fyn=oN2AB5E9CHVet!Eo&pF`Y$Ni&$o za7-l5H#dVV&Jw%OwS$R0n4*MO?Tu{=gp+XOD;`RJAv;g1f*Ez#cM*191Qc6f3gU({ zx-_+Z3_xpVueYDP_ATsLCpQ+hj`P8#(U!AGAX`QTm(qC^ONiG|z4vg_3*YD~FEo3@iUz z=FD8F^kr~0cK_Vw$?V2S;@}zl>X5!`&PBw8D^wbUDpRUaO>dmQp1|-M-+!`v{M;Se z>K~p+9KG};PPd+Zv-|vyiwDobi9N&U3}2xaNRa1CQTjVFKFwBJ=;jdYPY91B@VW6@ zHLb9*7;K{o68MEpDPdbk@TH`wl{4sKSi>D7)BR(MZ7Lh0%P4UfLX~f7DYCo|ov6<; z`f2`+LlIGOg(z`T86j>g>^~b_IXn6MPta!rb8DH*&u*PA9X;PV``jK)=sZ)oSeX*^ zxP5~|(2Alf2Xi}Ta03F|h4cD-x&PviyHCF!TRpW-tS@Y%T{4Oi7v8#RheKn@J>XfS za}e7RZnfjFC9Z7HHWr^)JBqFz%I$u>q(9Tg%rr{e%q*!iR|4AsiPB*cHn4?lY;)D} zNeaewk4>{>x=8YTvs~y?Bja=IuDq*La?uOM1yl2T&o&;thU4MuKTRfg1xib<{d^LX zxN`I8_|ne#|0~%!gI&ZSGOV3`4oz1vJZTR9xRmbkt8W5pPuCuQbNc09HXnbPIC?R? zvgZsSH*A!+rXhE2yP!s>J>LRRJ94Axe38bvL^MFu48wk^us_nrKfLU?RaH;!)x*cg zurCD17W}0CJecJEp~UU%8=PD_ifuod-+kO?_C6pma!p~9pu|nvgwO$TBe?ctV*TXg zi@$6?{R#>L%qI??uReUWbnr|!Iv+{wruFkB(Bn?5K0JN>{ppu~-F*B-^616Z<1e8e z_nv+|GItP4KHPcuJRbz291ZUF;i+Y8#W%MJ{bgbQsnI`6=|l(!F!BjbFN79%zCiuJ^wxk@ed6yVn#AsIJvwRUfKl|=)wxq2%=g(VZpf3Si|7bLPv0J-5Eyp9}rTeQ2Go1pyc?~pN>EO9$Z^F{%rf~_4N8f zje9n}agD^a}N8i2p?mtgn|B2me8eiDTQow`@|0txYU38bwM(okF$#fr9;UPQB zg5D#v;X8z6iS9OmAyl95w^>GPyF+ZhRZk|0wWUM`{AZKP;Kuh}p1l74=(9gTCk@Re zse(6b!;6TODMZP0W)?^X*PlnWUM-z|d+_qRz2|>~!fZbJ+#lc5deK%fdffQ_%h|&} z?tlKb&%XcfXJ7yO(&4Mn>Qi5QZ|nGJR;kdOqQO0V_IW6Abnx=qqgQ`|rVFc%STbX- z@z}=0*v9ev-s7z&U%_M!x8LNSzJlknETE~4v!j>aLJNnIM3#0~;=KPJC@W*?6pg=# z84sU+vHu)ywSR?i9o!D2TAO#`4Qtvn?G%igG6K%-efGD#m;XMq{)Pp>nV^tHi950J zY;x!I{)=y4{^`F?UVZDH-ms75T`!tA`Ev2(kK@Thy&HM3fgTq$@AK3jlRMw4X@P%e zl`i}(y67Do4gMuu316EQ!p_?t6IdS+J1+Q-S1A4U#Sa3@kDq??w`g*&N1ylZmWF;~ z0wr#)9f%te|P%%_h(=HS?h^8qX_FIaxlDy7yp0`qelk^ zM>=2&L!ii2xL8s!E|_J>4DHgvhPEz+byV&6h2qu4 z?!58pW&C}k@e34+xEGLvc&UJ(0d|T!Ba(UL!XnRCPAQ_nBF~7lT)D8w^OaMIXt2mL zA}v=gEb@Hilp-1|@{CB!l?#hJUpb|S28%o+(sJd(BF|S&DWbt5&xo{KxvS1v5_eC3oP8Z7dRNXwNAi#%UBrHBTLJR{O_<-#J*S57IS!6MIyv|PEc z$n%v`ifFLNGa@ZlE-dnV<&+{C{QnQnxg{w|iVPGPC^ArFpvXXxfg%G%28s+687MMP zWT41Ek%1xuMFxrt6d5QoP-LLUK#_qW14RZh89;p9bLGNJ#8mpqArK7~aS>^`a$%9@ zE2k9EV3B7;TCQAJ@L?6wzRjXGB`ATv+7!$|*%OSmYUzmMa$)dA@Q=5e*i3Mx^D+g+-pPoKi%C zMV=99xpHBV=PRca(O{8hL|U#~SmgQ2DMd6`-fLjoW=hHgIz;pmzVMuHW$hTeInmT>KUxy>8=#c->soDw2~B-Y_t+u^uVi_e7~ zk1f(NKJgtt#DJRex+IV<6+cfOSwTVW82^S0sRD-4D z_J(d1o+~b+w0~I9X!Bh3|KIbK0X^>2!t!+-xs=EtinaGhoSO~Ql16f_+4svD;A(xh znj%t^Hjv6GOvHkr#|4OMILdkbN(S!N5wDlmA=WRbHMR@x;F{}Mf*Td}cWUsLuU~Iu zrjptLE|VnMvS#W{9O+tR?Rx!JQncseN>sRmH$aNXLzeJXRBJ2lK zA6MbuuOuLnoGQRS-zX;u6`=WmK)YE>q;LdT1%p%cxT(C?npwA~{I@Fb?^cuED{sg( zh*E+HS2AF&rmzVD#k~g7t?H)IM(Wf;5+zWU8hYHB#40o@&fo}Tz*8?Z(v+rKHBDL8 z;MC>|i|l^NfSasyRSR`AiTYM$eXbgMT*!M3M<#a5YehO-XI~?ef32(*1|GRK(MT}i zrl4tkdhms)8Nu}oKoE5J$It*}>7d8;MrUr;ky=$YlFn7zqpzTM-mI+4vi9DCy0`9C z(R9O9<0w%(Qp%DLyQHQegj+v)T-VrC6@yQf4Yue#^%4VFszI#ZwcP_egV!-Q(j>Q3 zbo7!sWrAL1R`K8zJuWEEEUkWmBeTq-tp;D6%!K1BDk<&z4f80?T$fSeLSt^|>}@gl&P{bG>N|UHS2t$DrPH8U)h?vyJ)nt`7>GS;600kH zHJw0-o6`U1PbwW_%eKnmx zj|*Aes;2NqCQ=6Bm!&ehA~R`Im=c(9!See!Dj-sg{uaHrQEGq)=9!qogqTuBj|&Zz zE;m4!f|$Utj&Y5iauOpG@x2GQx9(Qav`(_t)jH^dUy9D%D$$ONPiLxO#hqMSsqYXs zE36mSFBA_(lF$rj=0LkMDlmJLc7(AXdVisunJ&{UA&xcpaV_ca(bKd$(@QJAyIom9 z(Mz-5wlq|Fpyt1Y=Mqr_d<+~*FunD%in1-~4vy@At{hRaNVoT)HFbwC4v_S_;? zcPI>*3aKHD+BRpv37%37KB(MUw&)I-lMBIBnR>mGaD#^WW!T1`a%K#mwHuND`olC5>L40z8& zv?%~QH}Rw?i0Q?Zcgq^@Hc=`4W}2Cl=VBeve~Y& zL9>U0r5~vwGjwi*dnI~YD08OEG^4+UCAv*yW%}~3H_PueGjL)()i5e>P4@d|m1B#4 zsG{V$j1m{(#!b3Z8%ea=$Ej-jTlaCK4pAmzD(&=-e^5!_nISljPz`>`NaWXdar0~2 zxtb_(QyDWFmG5S2*E=u)i7w&`Y{l=R$M3gAGYUpvHa@)0b2$JLh zcay>n?K#&PNFW+ZJy95MfM&PV2SLLfjpb^h$A!EwB?Y6z^Z0D?6S?9#YDca?loCw1 zVDoq7&F3y_T`hy&W`3_kgK)1w1C1VcZuPulQgki=trP37*V9t=rx%xhf48Qljfd-2 zH!B_RBnSx5i$x|^r^bR<3@o@mso!5;FF`bzZU#V0yUJ9GH1`4-(c^-0rprxo z>;LoqtxV{5N@`&;r(R|RPPq1PS-ZGitZ(fbgix8OmP(_??Xh@n&_t48yrx_8ZfPAn z`sG@CyR@F8vEUW9mO&5CIexvq6>f{UY9K+63(Dz6)vR*7U)L%(IuUK65n{uwAVM>S z{tFLi&4ON-)3P_(I7`L2w|C^bij~tu=SiZm0-@340?$m91%?v< zTESO+f@^_dOf4?MNTY(*Stm6@NuUa#>bXWYL{u06fI_}MOt|U#y~7Z_f4>^hrfYnL zp@PxQ5Ckrc&e_c5BC4Ush3uIwm$Zxj>HSaBHY+IYZ{M$N?lm^{L+ACi$qly%?dhwz z2~4=RiG2x~g6-;rwQ%!=(&2HY45##*KT*Q3WP>`)$-*$=nfw`y9U$6bt$2{&E8cPQL^ln zTWi0XxRL2HPx0%I@1&ff@}+OxtD$Rb^)eH9O;;G1JWd_dsv4DP0W`_1~6 z4=V8rgEb{rS%%+v&_I$7)Je~wMUKG#xmkGuG$!11qpFh{-mW4R{89soiV63(rA;Kw z2-MxheKWtUjI48Eoh^HT7E?J9_eUH1xPo{xr|f zm0G&xWlb%&Yw$X2A)`A?xB&f|d-Vi}7;+nopU>Ty{n_KjY;v96Z-l@Lpd}2^ho!YJ zpnrF9~XH(-XglHP?AX`u~3+~8Ah8j{N9SytL?V?agxD?UULIS6rDwwtb<~p#|7nI z->ZZBY=hhiE|>B8YgwY27S<;vl{FNWdDxrl)CCeuxM1g3cX5q!8;mFGW#$ILuH-r8S9fb0<<|OM6Zp$Fx+KG42qSg8-YPQV zllv9#-7VwGE?V!GSx)r+rgHtut(tnN87DTtQP1j*%*?|p@85iU2g2sPdg32$z%)Oq zNm}%{=RC{wDa6~})fXJ-qQiIoAlkcBzRIvLl zy4|Nqz=WI5)!eNtY3pX0{ADzLC7Jnd39hn@SIJjH)pr>^|L?U@h-_(P!~`bXbfW@@ z-coTh$n1Rc8!V|> zVDZ%mwXik}2KKO|=aRUP{ajlC9Jhpd*$sU|aP zDDCu4AtF|$5WT;tT)(->NQ(y$5mQ18xEb|(4bQB=w`4w)!7@$~ zzpqxHhL@qRYN&H?2o@1t2`(7PbossV#`o@5U#n;=W66N|cD?7*#fRdtu@_oWR}ztngGsthI& zp#Q=_?vj1-02hePWbuIV6urN5%Ny+qBT-=^$}MG#?ssmNAws}&ifkxE=rh#{RPdaF zr~^XuxS;&6cX7=yTOu_!$xKabA;f2cFLI+r(AYDe4#uc@&jShvM?GJK{{G!^UJt@& z3+ZBD!i8M_cE6siauDQZe6OjriPA*pwhCkoLM@yU`>n9Dks(&ztZKMb*&yoIoF5A0 zIa~C&z%x@}0OJ69e)O3s>Y>g7#~;cGohoD65tIo`xas<>>gxk`2OQS?jQ9FgUz;Ey2bVH*TlmR_17|C?`0Zg5h)irf1fnko(d$W;tpMtPVC?7N? z+)N`2>$^87U8=wg6W;X$`9M1?OzlzLCj4a4U1pZdG$!11qpB0>-g?j=(-|{OVM=4e zEvcud9MFGB3R_K=qMF=Z+SuwFo5X~eQbvy(U)}1|*dPMaHN#bH{0p8_#6x3D?MQ8h z9G+A16$w2qSa|C}6HPlpfkg{SM{`HdsV z^p3{%E)t7>rwU;u0D9c`>LyohrE7-)G=+Gp$Cq%IjnAkRd}O{2$!yqPXH%M2X7t5s=dn?^ZGV=4J3 z3&3)LvLu*rv+@p%_}?ifz+4K{HB@@vQ0Uh;9>kGWd1YmUQ%tz&Mg`8^YHF*Zu!&sh z(9it+0u=Jy@@AIa1N|3{AF5c1Yhjl(v{*;5O%h`AcX543I5^a03Ght5+IC^S=M-MQ zUSJFJ%>L%CKA7vsWjH0l#PbJLWUgrxS}be8P3e?O%`aw}HMoKwR+C#rD(TQTwclFL z?rCClU4KwxbNMpW3c!UdA68R%<^adw?J)Xy=27_VF!=bkkUlU|&g!if>H$;}iqkdD zHt7IfsDUPqY$V^UtgFRSY*=a;3+_Ma zSLhr!j>TDOXIUe;9N+Swk=&-VbB#W}H3)N|*Kn;_-sPU6$A!F^F5~$Mrp5`J3kICm z8yQO*ySc_MkYK{ivV&{o@RUZn$^lgjM+K37AL$FMtX!#6Ot|Sr#UAufdW|@a^mapQ z6`6&j@hom^pQ&QP{kXOTY7qKwr`fL<3iW%YX#JK70`-19#UDZVNCDAPto|l8_oQZz zWH>5vgb4y=zVSwV8@J2s6WT)%H{h8iA2cR^Va=q-IVP|M`IZ1x&^sOf@nf8-W*WSc zt!QrJ^ywICN0-?Tv`Af(%F!8x%{v;M%|*wA3)z2!wgm;Yb99k&TpOBKdnfNvI<5XF zC@@uyPIJIMx2XusciTcdqZfL8Bb}E8jUE?zN{`Vca*P2Ycr7yfyo=ka&;na+Z{hZ| za)m7%0fo&+89mVBX4bEGC{oMqcg$^e47y7jDeQg&&)|`{CJn*ZwHkzvbF-XC%z&{wp3D@3W6e>_J^5 zNmtq`3DldFO|w|OjYW^UvVEX%_|>Dc3hxwEGJrVVm|tG6ZDk8Bqy3&q@u1_X#v8Zs z?Q-{&bT}$;j?=s4({qcZb zwRRiNlzXORBT?yaL^(P;JhRNv41xQ=M!H%%;F&@-99%iDOs`uel6bC+BOgp(O(oFt zoZQ;)v-;%jNdOg?eekh77^iF9qf5J;nsrfzMA(XSa> z>T^X&UGiIX6aq(FNu+zi6X_D>OJKsiPvWS4=)b)qQNGGp&+B(gCT%n8+<_4SlMkZ~ z#h^JKP?j7$?%M8=b~tPpTN((?x66$m-mA(o1*Zdc7sL&XZ^j%=47kQPBIG@1KodPK zD3?-r+Q7WZH`_NHZD?iHwy>*-G@77~r*+C*6WZXsF`5jm9O_1A85(zoW@vIT?^`oW zxBy*3=4t|SD&LGcFlQf+-)f`}xW_@B^=ApswGa40%ZF^WgDOVYa|S)`%FdzE8PuHn zJJZ%{{Iucx`;@u0J&7aIVG3C$SEshmtTP+TJ|k1q-$-RuHBm}x8Xa!rSxWS{nf1$3 zSZ+4ZB=)evGZWc3bamCJ;OR?i+oLl{rqQT)V&Ti)6Q^$7h}<((EN45dx2?HZRizU^Mz6Bs-= z3iBS0G@n?>=VPWEJ?_T-$)G3dm|nF+6I`|ZgPipnp~*Q28Ux-5OEl@6S`iO8B)T70 z?PemT(&%wPzltU>M-qnc!a!iI&p3i-@_U_QaB7=caWCx7?3|g$m)Kgj!h!H%W)?Ii zT*zBNev+BU8&v)ewKc?TxLfJX7s5J-je&W^n5XEx8|+W5Ug>U!Hd8ql)u& zUrNukKC~1+{w$o>!L^Ii&b~2$-d`}4=`u%Y{RHU~vo|896fohYvb1#c(1c2fH2@t7 zYEWhvZWXG4XQ^X+eB+^Fc>HD^<>R~M7Z(5%Zn{y)hodFUjHSPS%pRIw>oSLXJ#&t^ z9VkhwNYls?wRFkr+dA{jKVZVGWr%E(E6{(TH}%;AwJjaDst8@CPt(o1CN0Ch20D+|r!xfRT(NcY*aE~2o;2@r zEcCd5Qr9ka&a646R)%KQhGy5D)2kz~jo`{rZ0C_KFfX!4wC*>L=u)M{gbP{zPIb~a zw!oFDV8~iaq~aOe4w;78qiEp@%Ly&f~p!Z3_Z z>4S6Di6qeCS=b#*?%SbiF83XMX8rO9hHw6Th2&spF_1Wv*+bzu44+@2_ZL{obXjEb z-Tg(r?@rYY-0bS89GyF&^-V+5^3JZ7Q@9W`;HttsZ5dzkF79>oo8G!z!Qx*OHZl2| zZd7XT6fAE`tZijB6`j*xMdQPeL+zXC)D5)=H7x=Ki7QR4BixcO;Wl!luGj|jU+AF& zzG;ccT}$EIsUh^aW~MeCPVGE~Alcm30S_#mU?j6hxoPycJI7~^u~;~{?_b#NRGCui z%xC5o*xgcQkFt>gL1WMoj{D}f##axA#$pZaJ-Kjc33^<}g6H;m=eON+o1VGNQ1Wno z|7r60g=s7)wnd_gZ}_k)jhISc!i6l2Y?&*zIXts&pIC0?ic*JoaM&72sNGRUkE)tL zZJ@Hfp)2Eg>)_PlpZ3ge0ciK2>srI7M z&2EUS<9aXhXcoP{ne{6+d#_cVk2ebbLyrp(nJx=-&U-jiQ?H0+!-NYK8rygZi`x*K z_snlY+a`0R7T-VaL9*fL>5YfZsa2*_2Lpro6Ad&n}r=v&x@S~iB_0^RWX?olo}CR}1ye_&}B`fn(?AKy9k&TonbVFwj9 z%n56QiTS;!3r8T}n?zo}e_>}dzCAR#!WAnzdK7x!jB92M3Nx|(aBlC}&_t3z=eF@Bu!9Y} zfrZT{7m)}@kGpepW)H`}UjS7Ojn@!bVSv%v(M@je>ekq75%%jeUBvf&1@9EU`V@q^RJ~E?lBFw4*514RUg__99A@tvgwd26t`qJTx`2MqQ zwH4RWQQk;3Pi!QQKU+P0Z5W-cBemVDB}HfQ?izs}_u$bp-&7*L`($SGA%qCH)e2OW zp=ct!v^%+aG`6?{m)0M>-Z^{SJ>U+;@$dvv~;Bls;nX z!h{PJYH3}g3tQo3=*3&%#4eYKr`C^#7moK{d>vfc?N%CCJqo&5Q{O^w zYU|LN>{;mOaRXBej?hdfu?L{)kqA>DV~gZE?-T^9vE_s5tw*b;uaZa4wSh%LFn&SA zNEHA*u6=Z3WGpthb`15)Ra!r||C8s?EO*{`iXInCjmMIX@a*`?0l3_2_5WgVgUk_m zrj}eYt5BhnD|`43X|7+Bs|SWaY~}P-r^?Fi)!!&ZcpU(_F!@XDl*1h#oV7=nKCEkn zNvb+JpCK9OcZ7WNo3p!*lMi3b>^&cfZ7V#pwlMOSp{b%`!fj%TqRR(RzmeqwlY7iI zvGM=4cjmus8(AFx6Z=Dp{bjLj(I2`+5A34ZP18Hww2qx-o7k?eM7ER2i5$zfWlOSk z(zHcWq9{tFBvPV8-KS$EzS2IGCWAZqKg01W|XLjoi3> z-^<3md>ShE%D}`M@1Fl%TYK;hC5XBw5kBkcv)DWiHV!HG-(UROdmnd*+<6na?gtmX z8Z=vHydgKA@(bzXHe^+&dkqTl?{1EIf|A9#dGUvvA71+3t+q}9b_regYQO2@FT3X{ z9+I`X*$dqR-}T=ewmKmFEv3Y34X>D+^a^9k%rgDzYjAYk`-^UPFRzV0?%!STsOb0K zEDTP$*<=9<2PPIiz5M?V{&U&liG`FZpQ(?|y54%{{kK2*@1W`Y!Hku!oGmU>p`cP` zpt;CYQc_0-Zv#GixQgX+ezG#k#shz9!bSrwkUtjB6ro&D)4kwN`k{-A-8IhFJ zIJhh5x)Ax(Z+e`J=wuWAXvQaGpv7QQ<)B;~n)c3k{uoe&)0HKzAeDEO`d&n9P_gW7 z%bbHc|1b+(cfuK3SdaPy6+~fyfQ(#<<{BVm@x~sy!(1YRo;9NGD#Z+$XFOpg5YIvV zLhJYD+2=UuiYJzcx?s#p^4C8Ot}r5`49N~!Y=8gfnMX)i(ulf2rFwnno-34aaWY)) z-J5g3MYpbyn`Up0T9yMMoNneN|2I9o1GmQ8EfICW@xr$~6L#9pNKm<;?&6Mn1TItK zbK6v+Jhv%Bi?JToxs}l_3MJ^e-2-?1yy88s;yfj&Bj#yvU!}PXAqB%%8!b%Q=}#^m z&+5N5We=-WI2a~u{;uBJeS@zxuRyD8uM6p9xh3YeL&^LClg4T&&Sj0B?;;%oMd;H* zsj`nxeg3`9VDen>4qwrAAqbNwsg3<~CPpH`Q~anQlV99{R({n(6&>d zEB*Qt=Q_)sZbj4ud$mw!;`vyv37Nu}+2&>5{p8YV9NZN|U1&F8N!Rz}>Mo~Ntq#iO z*8fKUF;j_Z4KS06J5VDJmpyi$&T}P1T}a2yD)I6Y^|;S%MY0XCumfMIczMz*&QfAT z&S9mDu1kse{Psb<^=qp3gpsl^x&R@#tB`H%7g|r#jXgIfU;O>0?psPtdvpj#0wliV z3C!vJcL2I>skzH$TjkxO;?7~Oc?o@Jq00eXcP(1s3j3A4BRDG=QSG_)P&XiAA-XOE zv~Q_?xg}M9Depd$OFI@PXLY~y63HW0gyO-BC*lj~T&AJxQi-NpYOV&Ah+3n?-0XUM z$|o@~j4{TGDA097`TbDo*-8E0nfLm+m)>iAG5*We(RCpLC1@MG5=!J*DeK%2zPN@u zyR2OuUDqeo7nsb{>Xv1N{%L&1NuPNbdKy_NKAH=pEiT@%DR@H3vFTNp-mF4&-8G@O z$f#?)7A>@-^5eO6ae+~1yg~%OQ-GaB*X6VpTX>qAy3)Lz<_Xu<{+V1+ETHUg;Y* z&pJ*cw69z<&2$Z0E?w=ve8cqf-Pv{nD8bV0y*GcQZ>)RxLC?s8(K(N9K*T~UU1&PQ z@-JfP(8Ha%pOCB^WF9>aCd-mqI?+8j61dFH+LnIzfu)O>3yK-hMWOWiHh&e)Yk}CJ zFSHhjt|Q!3?||j6l5d9;ur`r=v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU z!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2? zv2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnS zCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*V2?v2YnSCY%kU!*XorOiBm=LVyr> HC4t`n%~c@< literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/slate.bgnd b/kmahjongg/pics/slate.bgnd new file mode 100644 index 0000000000000000000000000000000000000000..07d3f73e0fa00aafbd8092abd7a7de4b1f22caa9 GIT binary patch literal 18270 zcmYM54|rYWec#V}B(e~zQe`p+u9=%h!4_7Gk|@YAGSzM`l3T8cB;V1S%5zOHh!L>t z+96KHR7@$Q2@k~{ql|}G2pKht-NsMi*(JsD+z=sGmSRtGrVz^{Rpe%g5@_Y^$=ca; z`+VMGT0PIXI`^FS&-eZP|NY+g7CyQ8*OU0Sx-v;_*P49cu_u#T3$AB1^KEAC zmSpkbB}wIbs*_DyHYe|2vLad4xElJDlC3*;B@aFF7`Thc`i^pPLqj9)m$9~nPmxw&c!IH*{Xjx|St3EW07O>%N`I zt*d`6xqk5nczY>9))%)I?3?15^>^pQYdG>|p;MHoLArsF$mprudk!0&5 zPbN3itxg_&;z`!9Ia#%Ob@J3RPbFKoJ;3)4{_je5KKvN(pQc}Ra!1Gd3NjulFWew2fk>uvpEv&7?7%j;s`G5VgM(DX3UN2`o%aZHi_0|V=!n1a+^+_MJ z?|bAAk~=#-m0Z8%SCXfo?<#1!Y2&@n37R1jHy{VA;Q!wB_D27H#(q20hwEo z+zvhS>s(IxsNtQY}nro(V3hhmuF&B0K|^Qo|pHa$Ge z^Sm6!`Mx_G3X8(&kPGKRM_3#}Xybn_Tx7%@;g!%Awuh#0Pbh`j@Xas~Y8a=M@7qH^ z_m_F@=SUc3q=8V+$aA5Yr%OXFZR2i?*}Fn99RtlAbB=-V?=fQkBm6IBS|7d@-V@e_ zd&61g-^6T_;dq!}#_7;Y`>}|t0M3~!xU140W-If40_3I85DtXr!vZKu%r=qMvihaG zYvxT~=mzCIne)7cqshu@Wb#Nd}t28VD=_PuHk+zEyIt4V4s4LQ}#-WJkRrO z6|Htd=a<gMS< zh|Y&uuBo&NyruM1Tul)w48Zq1^R4H79xA?RPbggCQ<!Hcp>1JuWh5AyR9eURAUoWb6&BW0oH6e4k() z8`3xVR4&?S-wBSppg=o_PBMQ-*qY{;V;D?*X%q5V8xDb6eJj#leiXRh5HWSZ$;q&S z*~gKA7SODrWj<=<5IslWYJV6_*D-U6b(KL};wsakDzvAzd>ie3KFp@);`{M%H6xW7 zYctpi$m=F}aFkh_pr91G2;N)Z_FAZTiq#JAUQL<;!?m=VqtymDc!>K6ke{PfduV0M zyP!iIBaeZRHt8BhEHZ07J@fFWg)s_Xu8rDRORMAP#e+!P7!puMibhyrJ9mfax4`#n znZ2Gqr}->Hk#Xi4;9U)PT9Bp#^xe%0Pw;dTZSp*y2hnNxF$?AM%y^8RHQ^1x{3UV65HemPV=!sn52H@J3%o!s|Azn8Hx zV@S?}P~shC{{`IE?ruZchSF|E)Dl2vaEoVwbsd5h2a)j_W~@Q_zl-cHFyGbab0=*} zk-lkHb`S?E2@TkISo=2)A4 z`!Idx_#9wuxm2H3KzGK$v_Q*OXf+n;CcohUV-7RwI8R5orlDRY7f0azPLS6yRtwbK z0nYz|uV2R~CurM`Ug$reFaDt?ErM6OR)o5Gnc?tWS}kF`X3&rFIgc+Xz?omC&p1~O z4lLsN#YoXAu=LV%ecu+le?gmsL$l#z^0 zj)3(rD4U?@C9L6}Vkw1pM-AnlGKee;Yv$Y^6BO$R~L9Bon&{M{hE1l?NV zT8xnzk*MX&lhbd}V}Vh$15@E(+7fA8;9lR`4hO}oemZM8&BF^Nznf?MjBz|&j*VXq z%2kYY4(UCeei)={!}qao2N~74>@6-<4l;FQ4!qvA;V-pi<7o0!)U;FF_t5ebcr#le zh03&A4(7L6zmb~pfRdF%Zr+Xf>KQ>>E?y;D#JM=@6u9)HJ>kn-Z-8YlTwKa{+K#*V zUt;e4Aiu~A_v6#X!L^m%!_enCMmob2_4o}&p8uqAkZcT}p&hc9Dg~LY%ahJD z51pzQ{W!CIkv7I)Iqds5-l%|lOhoRBR1e}R)#*G~>*+hsbG`H#czI*^Y51$Knz0rk zXM4iOh%LQ7!;E^sR{m$6riSlzL>E5^p9nV)MgI(K$kQSh+Q5xXN|kb>HTAj*Zs)z2 zy9!(_M7GLd7j5#O(oRcFrLBN9pP^?7`S<&3KHp-5*LarM%qsLMT4<;@!f0>6?{72G z5b}>rPS=K0tZRl#FHsDOQa$U*@QL(ahCc}ZA^c(Z9;2QQw}#iiYSgCgH4_cYKo_;i zcvC)9GqyA~Dir%P9zqMCpU_t4z;0$R4vuN$tA}@)zPxp%ovY1s5yV&)?fD3^Ur);( zXwV;@;MbycH%3IW;II+lP{j6dxGMZ#X#9ua-$Cbn^rj!U=OX>apus5ZhC?qF#U4Yv zFG7KOR@jDg&8M@>JI}KL)}vLEmR4>9IbBz;|OTK&8T;R z^=xQEvIZmlFCkmF^R*jpvOQwu8Bp>J!zbxr$=s z%ouV|X6%Q-`?cr=ZP9`K6wiFLD9v~r^CkUUzQUQbZ{Vz);z=*_OWRRUw{iD{+G|YWNb_KI z&1QuYalInGCY4Ldk$RE?cMVqyy!g$q2(&dwUYY3bhupypE)aVMbuSXB>qg{2_R56SF`Y|Z9oaV;8aDNV2 z%Az#T!iyPZP#??ak8`}0{v~}+g<;SRVy%meH7!o(uh8C0-(ILX364qLsawtQNz@1^ za~@Pie%ijOFp?VWYlVpLl<;&s(pT$Fw8|yDI>Uz&#YSLe^0!zg7Ho=f_7#OyEzvckco(wk;_==A|q{N zEM>|pNIEFfTEm}#T5Qf~Rw-Y##%8EWR|&a%6EAB1GEb~{0-N4&!dRmu|TSX9Erk!z|{BZ5p(S8&1SO*=IpdL`DUC21J z%`CSzX(|;Z`6{h^dJ2r%3tO6H5&JVs2cwswekyau?s|b9<}-R+!&@y#4e@b5@4Jbj zXNb$MiKxvsK$_OZG2%VnwbirmbDDK(QOr?#i2XajD|g^OpHdq-Vn5A7s$vv69yRn} z#90NFBI}ujBM120$0c?-Rf5JP`l*XqW{-CT*8+2HU^Z(VRF4?-HE3C61T(iDz8PPr z3CJ=2MvG$}p}o-pIC@QmRTHDo6XfezB`3x_AXP_*2N`z;38;#zcXVx{^$#m}MLMc|IIt=^NfRS@Un zkzKcOni?%1wSrpvF2*sF?2>+wS5*~QBlI?lBcxtGy*6Q+rR_YLHUshkD7S*O1i`4yqqM_I%*@^BTdxd5mqJAF?uS= z?^+ae1CQK{9WmPY95Y)5&^C^s@veUmuWmdjuX128H_+Ote+P*yH$&6e7$Md$f)?N< zZ8)^=;C%<}4$x2iGP}+Z;r$N&XCKzi+J_XAb|W!vE~jS2nUs!4nho-P7;cNlsL8g< zOP;YNpv)dD`z-(UVnl58b|yWU8oUFfc~-9u9j3SSgsD_HUXOI#2=Y^Cg`9I#?T=4Z zqT7Yob~aW(23X}hqY*nWgS7+st3~b(Rmg@G*SH|F^6GJ!QLOh=GtWGuiCXkZ{Zedq ztZ;DOf=&!DrgSr!yNPjo`8E#Pj6=rEQ+K*ev%(yhr^CM{^6w1i;L>R_ zlTN5{fQ(uxTws)9IF+xH{cw-cB7At&_`-72MnawLAuQMRiBVl8a zq_>fYX3$Apbx~@#hUodRs77|}%qFF?`lNP6u9;D(QCU_yAMagF9Z{azRu9itQmr#% zZRhh!lzRCJ$Z=@ z6_IvXJ2y{zF&NbuN%>qWF3qiPY59r7qkmL04l`D!#m0VTqvbGb_y!1u$Xy$mBSSwQ z)>7xR7J8X}Z=jPc$k(G#s zwkMHF?RqV%$#G{b$(*YI^$tdj-W}GY*2~|tS4A`Ncr$pj_#w-T=a|vD!5U=Z6|BdD z;nnmbtj#*9w#~|$wRP9EJ=B8Q8b%Xq*T0QJy)MQ)$EOsR7lU9Jidh#rH#PrgSCko*d(;f+BSj8n&vd^^_&aLUPMa9cz%%4 z`l!}?6YNKsO-y4@RW07jbsgWRo`U{CMm)sYrfI(b)jP@Tj2$vRX?&W+O-7;ZAkugJ zCK-oRaQBIQ6E34n_cJni7(up2ps+Q+6|BL%son5uS6CV;ZkB08th5+e=#|a9deNO0 z>ZKRr-5mDVSj}jn8XmUu?Q^_$7wS03`&i`|dcT~k-~e+J&{L^wb2uNLn05ZCZD59RL$=_*D#2|{J+TCQ(EXY2j;zYHSdPJLf@g_YT6JZ%%} zxP|?@VoD95vWDF=Gz`2G;Yh{n)R-s1`eCSQEmT}1jBUmwGA+u>^mpZ%zTd@Mqs*rL zpQ=#f_J@6F-i_=Qoea$&oMhxN_&y6)tqA@ZQfC#TI@Qv;LgQ+8B&{yySs)G_n8o8_cCHt|+6g{0H6)q(@w2u8rPZ|ssAZH#z6H`O>KsNY?jXiF2hMuNh<=&; zS)L)~wCji7waDBN^k5RiyTE#y8767(?(`w7nNfdT_*sl&j-kUZVrd2$c@O^Q-FUW| z*7fYy`D6#99|+$9?H*P)2z94HJQrJ7Ein%n1j{^>8>uj|UPYU6)@JlxV4PEUmOB1t zd{ml^+K5EqgHYB2m-cq062`g`FqEan}=4y~bZ)j8gq zFXWNY!}#p;ApIgp%ltQn?Sfm_Zf5IYgdDUz6D68@I?v2Xv;+I2>0K zDLwGg7&h~7_=t2mU6b}h(G4KwlZZiEW{hDL_+0pH`mQDF*DjrcUhdAFfw#ukj%%Dc zPv!GGyw>idpgm1bz0(+DXfgHm`hclOF{@kiqKNsan6-SYide+_-snKPtbPoFS+Cv` zJ@Pd2pjD8@%CUB{DXdGI!WTe10Y3L7j)C_S7_OrZ(Ms*`1orww_)dBneG1r@(;zD_ zkM>LXI1%=O`9U<_y&@&FJLaib-BSyDKGo-2!_^-dn>+JGY+Wt1o{w0~aATdT( zFI|h)i7G(lG}geX@X_!U^zIPSA|H$myU_rv9WzjL7981bA=QiuYY)j9h_K>ilsf%|k{ zLw=wnh-Zl;ni$*c+%qoPFta!Y}-B9JA z1`zA?QvWKwhW_3EoLPu=VY0Cewy23dy^LB!BGv41FxT_Oc+bkdRGLGAhv=DcSG~#V zMyxjN1mh)IXBArN>(ScD7b&gvUd;$o3T=(P_QQsB)vWM&U>_;;`?K&)^7R<`5mN)ae8J3~6 z2mfmwj=g1OklS#$Vl?%?^)n;daiWD3gk~*TS8%c}>BuQ5u6Gez1Jbjc0k;;+%9L95 zt@JY>+#CKomsZ8PKn?b>0gRpmTOfAIDBwJeL_GT#!JQ_(R~|0mL76ejnX-La^|>2w zHk#hfCFebhWUR3{Mh0D2{s7wDQ2jFfM_9KpZ7F;m&Sdj~j8->u4WQMXplwA? zk3pyFnEfTzyap}3hjCqLH#oPY%F|KM=n2l!Y7SY{suZv{W=oleFjMM@RiYQ%2oly>dPLy z;2=G}$5VHoloo5Uo-%U(xHcS39pAj8gZ0~2{V#%f(-oN*Q|Gy?{OAp(w7LL7=Jt5li7<4um_91gi`pHoh>^_3W5ArLns)O)VZlyGv|@E+D&=QX<-g ztF#904E%ytoy195+-E&$48ChEALQOB#(3FE<_J&KT=SiSkH`1MA~(yHR!Gtawv=^Q-YKVJ;&{vzH<#oRrydX73uEBE1mmbz<9%^M)`zS4+PZtq^S>4bo zDydooy|f&(GUR?xE%C;i#sn$Uy+YYi9;h5- zHlxJ;?kYHzorN{yFw)8ND`r99ld9S?=~)XRbEq5P;T!1Y?Y!9la&5Bp4o}9WU?*ed z|J!_1+74m|zZbrWU68)k6b@6bonbBRQD|Y5$V)MXso}r+)dzBEtbLRtYMb>ct;HZ* z=wg;d$jp*hUw9eW=m(G3wH%&UoZzh|Rg4oxiOGH%E7D{a$h;bAMRAzbcv{Fa%w$m@ z8Dd^@;u&ncCy=d99S=vqeFzHZ%gR`ZJHcUuv%uKy@f?NM>-b!NKJCOrR*RH=y|9{k z8H=r-SBg9*NOc6=QZ_x?+mH9HWfrk21=((45AB=dH#v}5JT*|szJTZICl1j!$NU{s z1FS{Of0fVW;B6ubwZ3)_b6%~Vh6ZJ<^PRL?M?J*~kF||MwE1RSm*+(~S)J92G4eqD zY4mP!PN{PnRE!xv)V?V-*VGQ*4!V)pUg;rzCl=J|>$Bap`zz)h1qa?MG=TTrN`4RH z86RB2BOK@5WT*u95!SGiky&AC-g62}Q?yxt*Z&6_kKazke3X%`>9~JnzA?r3XyI6C z6*fWNWe#Obp(QsWgLa_Sx^;(t5&kIFV}?L{0ghn%$i6b_dZK5B9(tA$W-dXCEaE?we-vr2P8tP^k`-0dMko|Gz4c~)k{at&?X_1i?Z@Fk1w#N9+JVQcH61GHU$XXYKAOmdgevv`v!r#?Y^ z9Nnnovl&}%ouwHIpuMdDfjY0>*YB)E#yhdlnMYF=Je%cdgq7ev$oTF<>6@i(W>K^) z#`j9bTXDz2J2~Om75s4Ylio#IW7|GPHh&QWo>V>%{t~Kqmdl*fcy=Cn<@_uhZvwfe zX#FL_Zsx8dsxrfLbgk4VJh!gA2&rfVg%xu(Evr_V$B!`E3t=%f{T$zn%RK#I9aFiM zD@{C~f_gph`#kN<=sdA)tToDi&YND*#}86fxtDnIi=l$g4?q#)4YOw}szwO)+*?0X z3zZZ7)H!T|Ij6twm`FVZ=;>g8FYz+Gt>x`}i~%R;56#FDMxdhh-(QZLg6c0or|jee zG)GeO{i8g&lYZ@J`p7>;9XIh@$0EDy00?vI=IQoQ)NiK&9pl^N2K+#X0n~N@GB5{n@OFFwlVX1MnjoIIEPWq z7^9fcMtca-er8(F)y>+K?XPp;*JI?5oU?+PpzK(zbUu%4tOr92GtM*HK$Hzz&bZ1wC@Y7a zH>PSD~F#{aP>+_PE*mUYnE?9N*8Sf%!92rpn1 zZxy&mW$G-rC$Znxf?4aO<&yU`l}hP3>d=e9IT1Dy9jd7*h^4$ymZ!k=nAV0Ch^|kQ zF?^qzt#wOxni!E?1?@)zpIMFO=jgx!0cvsWwJ8G}yg)zH!i-s}y^vi=d|!a95n z(sfJN4P|deQk$`HX5rN6Q+3D4`z`u>lz6g|OvO0MKHC3bXlT@~{CZNt-Nsg=jd47iY`GV+OX7o{O-LnRZOGZgMQ}PVmN@#jMobo4??RJC=1+=!bcqKH9% zS)KKJe{awk*IQ)eQfU}X&=+M_cWNcBAB*9MH?zG$9QP>WDrrCFyvPDpT1!0;?OYYQ zXbxpv+8$;i{)WW3avM^7l$`7l^1>cg`;+h=!-t^W87SJEPBQBy#%O1aX5q^EN#5k) zRe|+8QWfa3%9eU=E2CyJX0)Q2+z&lI68)mPOpVy*Q;hW>{=(?a zz4L?UpS9Y|reZ}wyoBCl0$hF2?fvxIOcd~2;XlFmm+*lFY~q*WY-XJE=(V}*U2#t1 zMe$pMuwrHIr$^NunAxbwS$5D&3<`bu>}Gy-r4NhN9Azc@wNW!1`V1>zep)Y6o)DsGxQn4;)<;oSY6Ay=nsAXyK#^C&Ba`(UC*R6lebN4LFvyF<3?WY*eY+@hM zV8vG{v#MiW7FUzHYGYV*9ZAvKWS%cO`ABq*)$!LjJD|<6@M$>tHZs>lThCpoUCLRG zQLQO^ve6S5>O~Kz&8Kppbk+J5DvEm+)I(^$ z#qJ4aEA(}6z`a(ZJ2e%|)D}w>dOY_Sq_TFeFXnAKiRq6(w?p(XbF;du_UT8>46UKK zd!EJWc`*Bn7ropz=HG)RS+(-KlGQjR(z6ww;kTZY=^Z_T-gdK+?no@$4(K-37@ zO;G8|x_NTNdt!<4`^bcub{5TTU>#%h>jUpckdEb8(Hy+>R6#5A$TKmohR#Z#92R>S z#8P392y!n{?`besXUveD8FRmNfHnJjK~K+WJ^`p0 zU6dKTfRP8DjVhS`MXZ#VL0XS`6C3p>aCkYke>rw^2bf#PYK|$_AU8Y8L6@wK=Z-9O zBXGL{PhK@Ly87vF_>3*Hv)_4YcUkO>%s{&~=Ie_;Ybq_O zdv~7Ht>*G9NONk9$lv%PDcFoG>wcAAZXG2a`Ye(?i8py1+`Ge;@RM{UK5GuI#F;u?DrBb(vrI+(by=r_~YodK$mBlAdd!gTGNx7pWY9 zLui3NQ9;o^-?itXhn{V#b4@u`arv_Sy>VupBT>v1;d8Ja;Ke@Ngc={ApN$ z=h+Dk>orE=_cLY_mSUQCxf@Iwm5n3Z+1U`+q78B9@hI}~C(wF-lu38|{bl6>(ZD`X zh-WS>RCH2vc30;{Wb_1>%dE%x!bv>QT0T3N!{0M$)x@0H=4{u`h`t+{UBI@M@i$ZO z>Uc=dD=Tn)NP;qU7z7>sZ$Of>d`Hw*PJOdBpLuqvmitU=7ohTfWN(%=o`N>i*HW{E z^XZ+)=2muz3oF%M zW{gfO!0w7k?5)x10r;#A&(p$6hh7Qmfd@AZB`3y`)F2s`K-mEucQES`aJ+_J@l=Dm zbZ4Umah|+_GxFecck(5uMf5?<+xY(k_+0Nfy&08xvuUa9$$ukbDm_TU56Rd(0sVI| zBDIokO|2vSL3khgMjsKT)g9?-c5Oc3ub15QBPwO={YbIrHI3cOUrr#! z9num?c}mi2 literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/splash.png b/kmahjongg/pics/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..dba4cf67eb622895b93be83da44bac52a1e8acc0 GIT binary patch literal 19311 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 literal 0 HcmV?d00001 diff --git a/kmahjongg/pics/triangle.layout b/kmahjongg/pics/triangle.layout new file mode 100644 index 00000000..e1dd4891 --- /dev/null +++ b/kmahjongg/pics/triangle.layout @@ -0,0 +1,86 @@ +kmahjongg-layout-v1.0 +# Level 0 ------------------------- +.121212121212121212121212121212. +.434343434343434343434343434343. +...12121212121212121212121212... +...43434343434343434343434343... +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +# Level 1 ------------------------- +...12121212121212121212121212... +...43434343434343434343434343... +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +# Level 2 ------------------------- +.....1212121212121212121212..... +.....4343434343434343434343..... +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +# Level 3 ------------------------- +.......121212121212121212....... +.......434343434343434343....... +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +# Level 4 ------------------------- +.........12121212121212......... +.........43434343434343......... +...........1212121212........... +...........4343434343........... +.............121212............. +.............434343............. +...............12............... +...............43............... +................................ +................................ +................................ +................................ +................................ +................................ +................................ +................................ diff --git a/kmahjongg/pics/wood.bgnd b/kmahjongg/pics/wood.bgnd new file mode 100644 index 0000000000000000000000000000000000000000..1433b16cf86aa2614b822f04beb34722b65d147b GIT binary patch literal 18534 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 literal 0 HcmV?d00001 diff --git a/kmahjongg/prefs.kcfgc b/kmahjongg/prefs.kcfgc new file mode 100644 index 00000000..1498936f --- /dev/null +++ b/kmahjongg/prefs.kcfgc @@ -0,0 +1,7 @@ +# Code generation options for kconfig_compiler +File=kmahjongg.kcfg +#IncludeFiles=defines.h +ClassName=Prefs +Singleton=true +Mutators=true +#CustomAdditions=true diff --git a/kmahjongg/settings.ui b/kmahjongg/settings.ui new file mode 100644 index 00000000..e53c6e37 --- /dev/null +++ b/kmahjongg/settings.ui @@ -0,0 +1,171 @@ + +Settings + + + Settings + + + + 0 + 0 + 274 + 200 + + + + + unnamed + + + 0 + + + 0 + + + + frame3 + + + StyledPanel + + + Raised + + + 0 + + + + unnamed + + + + groupBox1 + + + General + + + + unnamed + + + + kcfg_ShowRemoved + + + Show removed tiles + + + + + kcfg_SolvableGames + + + Generate solvable games + + + true + + + + + kcfg_PlayAnimation + + + Play winning animation + + + true + + + + + + + spacer1 + + + Vertical + + + Expanding + + + + 20 + 16 + + + + + + kcfg_TiledBackground + + + Background + + + + unnamed + + + + Background_scale + + + Scale + + + + + Background_tiled + + + Tiled + + + true + + + + + + + groupBox2 + + + Tiles + + + + unnamed + + + + kcfg_ShowShadows + + + Draw shadows + + + false + + + + + kcfg_MiniTiles + + + Use mini-tiles + + + + + + + + + + diff --git a/kmahjongg/version.h b/kmahjongg/version.h new file mode 100644 index 00000000..fb1c1583 --- /dev/null +++ b/kmahjongg/version.h @@ -0,0 +1 @@ +#define KMAHJONGG_VERSION "0.7.9" diff --git a/kmines/CHANGELOG b/kmines/CHANGELOG new file mode 100644 index 00000000..a6d3aac0 --- /dev/null +++ b/kmines/CHANGELOG @@ -0,0 +1,325 @@ +2.1.10 (25 Aug 2005) [KDE 3.5 devel] + +2.1.9a (25 Aug 2005) [KDE 3.4.3 stable] + * fix constness in solver [reported by Garrett Kajmowicz] + * fix bug: cannot load log [reported by Tobias Meyer] + +2.1.9 (27 Jun 2004) [KDE 3.3 stable] + * fix compilation of solver debugging code. + * add missing events + * replace case size option by zoom in/zoom out actions + +2.1.8c (31 May 2004) [KDE 3.2.3 stable] + * fix bug: hint not displayed [reported by Daniel Schepler] + * fix bug: log file restarted when game paused [reported by Astharoth] + +2.1.8b (26 February 2004) [KDE 3.2.1 stable] + * fix solver crash when "magic reveal" on. + * fix longstanding crash in solver dialog. + +2.1.8 (17 January 2003) [KDE 3.2 stable] + * configure button in highscores dialog + * notifications + +2.1.7e (11 May 2003) [KDE 3.1.3 stable] + * fix score trends display + +2.1.7d (30 April 2003) [KDE 3.1.2 stable] + * fix assert in custom dialog [reported by Albert Astals Cid] + +2.1.7c (17 January 2003) [KDE 3.1.1 stable] + * fix score lcd colors for custom games + +2.1.7b (3 December 2002) [KDE 3.1 stable] + * fix bug in highscores dialog in statistics and histogram tabs + * fix icons in adviser menu and in configuration dialog + * fix bug in XML configuration that was leading to a crash at program + end [report by connyosis and "Quel Qun"] + * fix bug in mean score computation + * fix bug that prevents logging solver actions + +2.1.7 (27 July 2002) + * XML configuration is working !! + * separate keyboard and general shortcuts in configuration dialog + * add "advanced" tab to highscores configuration + possibility to remove + registration + * export highscores to text file + * add (optionnal) statistics and histogram to highscores dialog + * track lost games and black marks for kmines + * histogram for kmines + * some cosmetic fixes in configuration dialog + * view/save/replay/load game log + * fix a bug in KMultiConfigItem [reported by ...] + * at game end and in case of victory, do not show mines but add flags [bug + reported by ...] + +2.1.6 (24 April 2002) + * mark/unmark actions on mouse release [patch by Thomas Capricelli] + * "magic reveal" : a new option that leave only the non-trivial cases to + solve [patch by Thomas Capricelli] + * fix obscure flicker condition : keyboard play and moving pressed mouse + outside field :) + * add home/end/pageup/pagedown keyboard actions + * fix pause menu entry state in some situation + * fix resize when case size changed + * first step for game logs + +2.1.5 (14 February 2002) + * now include a solver/adviser ! [contributed by Mikhail Kourinny] + * revamped game state management + * slightly changed settings API + +2.1.4c (1 May 2002) [KDE 3.0.1 stable] + * fixed case drawing for all styles [bug reported by kanthoney and fixed by + Maksim Orlovich] + +2.1.4 (23 January 2002) + * completely revamped API for highscores + dynamic library + * revamped settings (now includes Custom minefield) + * fixed repaint of LCDs in inactive mode + +2.1.3 (29 November 2001) + * use KConfigGroupSaver everywhere + * config entry for wwhs server url (just in case ...) + +2.1.2 (19 November 2001) + * date for each highscores and for best highscore [proposed by Jenne] + * some more reorganisation + * better custom dialog + * slightly modify the init/repaint at game start : more clean & repaint could + leak some infos about mines position (?) + * forward port the bug fix from 2.1.0b (in 2.2.x branch) + * do not bother people who want to stay anonymous with message boxes + [suggestion from Bernhard Berger] + * autoreveal does not take "?" flag into account anymore + * changing nickname will change nickname in highscores list too [suggestion + from Juliette] + +2.1.1 (16 October 2001) + * port to Qt 3.0 completed (hopefully) + * some changes in highscores (nicer dialog) + * i18n error messages for wwhs + * new smiley pixmap for pause + * clicking smiley resumes when game paused + * changes in keyboard accelators since Ctrl and Shift seems to be + forbidden as keys now ... + * count nb of clicks (reveal, autoreveal, mark) and display it in highscores. + +2.1.0b (19 November 2001) [SF 2.2 stable] + * fix a nasty bug : uncorrect flag was not shown at game over in certain + cases [thanx for the bug reports from Viira and from an anonymous admin :)] + +2.1.0 (17 July 2001) [KDE 2.2 stable] + * fixed checked entry in "show highscores" submenu + +2.0.13 (10 June 2001) [SF 2.2 devel] + * enable world-wide highscores !! + +2.0.12 (09 June 2001) [SF 2.2 devel] + * option to put the game in pause mode if the window loses focus [proposed by + Bernhard Berger] + * better default keys + fixed autoreveal display for keyboard game + * use KStdGameAction + * big revamping of highscores + use of KHighscores + * removed status bar (ugly and not very useful) + +2.0.11 (14 March 2001) + * changed highscores accelerator to CTRL + H (more standard) + * better use of session-management + +2.0.10 (23 October 2000) [KDE 2.1 stable] + * fixed mouse action in settings (autoreveal and toggle mark were inversed) + +2.0.9 (21 September 2000) [KDE 2.0 stable] + * fix obscure bug reported by Tobias Oed (was crashing version 1.0.1a !) + : mouse buttons were messing things when pressed simultaneously. + * in the same move : simplified some code and correct a small buglet due to + rounding error (with left mouse button pressed, the first line and first + column case was pressed when the mouse was moved just outside the top and + left side of the field). + +2.0.8 (7 September 2000) + * fix bug that was pausing a stopped game when calling highscores + +2.0.7 (4 September 2000) + * fixed bug in mine field drawing with some styles [found by Gerard Delafond + and Tobias Kretschmar] that induces a complete rewrite of the drawing code. + now it honors the global style (beautiful in marble style) and it even + leads to code simplification ! + * the keyboard cursor uses now the focus drawing method. + * fixed bug when field.width != field.height when exploding + * fixed font in the pause button (with non standard case size) + * fixed behaviour of button in highscores dialog when entering the winner name + [proposed by Lotta Inkovaara for ksirtet] + * keyboard is disabled by default + +2.0.6 (23 August 2000) + * use of KMainWindow (replace KTMainWindow) but this does not solve the + resizing problems :( + * pause game when high scores requested + * custom game settings are saved [feature suggested by Toan Nguyen, + Williaw Barnes and François-Xavier Duranceau] + * solve the resizing problems by intercepting the Layout Hint event ... + (due to limitations in K/QMainWindow IMHO) + +2.0.5 (14 June 2000) + * default for mouse binding changed + * added color configuration for numbers, flag and explosion (for B&W monitors + and few-colors themes) [feature suggested by Hume Smith and François-Xavier + Duranceau] + * moved around some things about configuration (cleaned up "defines.h") + * case state is now more clean (but some bugs might have crept in) + * properly mark all the flagged cases with no mine to error on game end + * versioning the XMLGUI file + +2.0.4 (11 April 2000) + * hack to resize correctly when menu is hidden (due to bugs + in KTMainWindow) + * nicer (and simpler) custom level dialog with KNumIntInput + * kstatusbar in place of the label + * remove the title in dialogs (more consistent with other apps) + +2.0.3 (6 April 2000) + * use kkeydialog for actions + * rationalize settings (much better now) + * animate autoreveal with keyboard + * enable/disable and change text for pause in menu + * configuration of mouse bindings [idea of FX Duranceau] + +2.0.2 (28 February 2000) + * XMLify the GUI + +2.0.1 (19 February 2000) + * KAction/KAccel integrated + * keyboard play + * can choose case size (font is scaled) + * less flicker in repainting (+ fixed a strange divide negative int by + uint thing) + * fix the pixmaps drawing so that they are not too bad in custom case sizes + +2.0.0 (14 December 1999) + * use of KDialogBase and KAboutData/KAboutDialog + * unflagged mines are shown at game's end + * hack for focus handling in highscores dialog ... + * slightly better LCDs, message label and smiley button + * fixed a bug when clicking on the frame outside the mine field + * small fixes for custom games + * "What's This" added. + +1.0.6 (21 July 1999) + * slightly better highscores dialog + * fixed a bug in flagged mines display (cannot be negative) + * the LCDs gets red when there are more flagged cases than mines + and when you have used more time than the better player. + * use of a status bar. + * the smiley moods are now XPMs [made by Andreas Zehender] + +1.0.5 (6 July 1999) + * fixed layout handling (all this "updateGeometry" and "LayoutHint" stuff) + * space bar hit cannot restart game anymore + * clean code (config) + level is saved + +1.0.4 (12 March 1999) + * porting to QT 2.0 + +1.0.3 (10 March 1999) +Mario Weilguni + * fixed the bug in the level selection (showed up with Qt 2.0) + * fixed layout for Qt 2.0 + * fixed signal/slot handling for Qt 2.0 + * fixed layout with floating menubar + * level menu items are now checked + +1.0.2 (23 February 1999) + * bug fix : argh! a "brown paper bag" one [bug report by Szokovacs Robert] + +1.0.1 (2 Junuary 1999) [KDE 1.1 stable] + * bug fix : when paused a game can be continued by changing desktops or + iconifying/deiconifying without time consumption ... [bug report by + François-Xavier Duranceau, Frank Pieczynski & Oliver Eiden] + * add printing facility : well it seems hacky to me and certainly not perfect + but it works ... [feature suggested by Tomislav Marsic] + +1.0.0 (18 June 1998) [KDE 1.0 stable] + * final cleanup before 1.0 ! + +0.6.12 (9 June 1998) + * use QLayout for "custom level dialog" and "highscore dialog" + * some cleanup + * and a fix ! (yes there was a bug still lurking : when only two + uncovered cases remain, it was possible to win on clicking on the mine) + +0.6.11 + * Added kapp->getHelpMenu() & setCaption() [Robert Williams] + +0.6.10 + * included in the CVS tree + +0.6.9 + * bugfix from Anders Widell (doesn't allow anymore to middle click on a flag) + +0.6.8 + * some polishing (game over message do not overwrite mines number & marked + mines stay marked even when the game is lost : so you can completely analyse + why you have lost :) [thanx to Christoph Rummel for pointing these to me] + +0.6.7 + * internationalization + +0.6.6 + * no more NULLs (for the sake of 64bits) + * nicer dialog boxes + * no mine on first click + +0.6.5 + * minor changes + * "kexample 0.31" compliant + +0.6.4 + * adapted to libkdecore 0.7 + * hide/show menubar and popup + * more compliant to the Style Guide + +0.6.3 + * use kdehelp + * updating of Makefile (use KDEDIR) + * use kmsgbox + +0.62 + * use KKeyCode to manage some keyboard shortcuts + +0.6 + * change the class name "Status" to "KStatus" as an include file present on my + system defines something called "Status"... + +0.5 + * use KApp for configuration and highscoring save (via KConfig) + * better look of option & highscores windows + +0.4 + * minor bug fix + * 4 spaces tabs indentation + * slight change in the "pause code" + +0.3 + * the random configuration seems to be a bit too repetitive ... + (fixed : silly me !) + * a red cross must show where the marked cases have not contained mines + * uncover case on button release, not on button push and also allow moving + the mouse with the left button pressed + * clear function with the mid*button : it should be kool :) + * a pause entry in the menu + * QLCD for timer and mines left + * cleaning of the highscore stuff (the highscore file is now in the home dir) + * option : "'?' mark" on/off (the default behaviour is on : you can change + it by editing the defines.h file) + +0.2 + * some code reorganisation + * timer starts at first click now + * it is no more possible to click on a marked or uncertain case now + * a small help + * colored pixmaps + * a better look (?) + * highscoring + * custom level diff --git a/kmines/LICENSE b/kmines/LICENSE new file mode 100644 index 00000000..6b772e1c --- /dev/null +++ b/kmines/LICENSE @@ -0,0 +1,19 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) + + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/kmines/Makefile.am b/kmines/Makefile.am new file mode 100644 index 00000000..e49f6501 --- /dev/null +++ b/kmines/Makefile.am @@ -0,0 +1,66 @@ +SUBDIRS = data bitmaps solver +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) + +KDE_CXXFLAGS = $(KDE_USE_FPIE) + +bin_PROGRAMS = kmines +kmines_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(KDE_USE_PIE) +kmines_LDADD = ./solver/libsolver.la $(LIB_KDEGAMES) +kmines_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +kmines_SOURCES = kzoommainwindow.cpp defines.cpp highscores.cpp settings.kcfgc \ + dialogs.cpp frame.cpp field.cpp status.cpp main.cpp +kmines_METASOURCES = AUTO + +rcdir = $(kde_datadir)/kmines +rc_DATA = kminesui.rc + +messages: rc.cpp + $(XGETTEXT) rc.cpp *.cpp solver/*.cpp -o $(podir)/kmines.pot + +# for system-wide highscore file +DESTBIN = $(DESTDIR)$(bindir)/$(bin_PROGRAMS) +DESTHIGHSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY) +DESTSCORES = $(DESTDIR)$(HIGHSCORE_DIRECTORY)/$(bin_PROGRAMS).scores + +install-data-local: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && echo "********************************************************" \ + && echo "" \ + && echo "This game is installed sgid \"games\" to use the" \ + && echo "system-wide highscore file (in "$(HIGHSCORE_DIRECTORY)")." \ + && echo "" \ + && echo "If the system-wide highscore file does not exist, it is" \ + && echo "created with the correct ownership and permissions. See the" \ + && echo "INSTALL file in \"kdegames/libkdegames/highscore\" for details." \ + && echo "" \ + && echo "********************************************************" \ + ) || true + +install-exec-hook: + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((mkdir -p $(DESTHIGHSCORES) && chown $(highscore_user):$(highscore_group) $(DESTHIGHSCORES) \ + && chmod 750 $(DESTHIGHSCORES)) \ + || echo "Error: Could not create the highscore directory with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((chown $(highscore_user):$(highscore_group) $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test ${setgid} = true \ + && ((chmod 2755 $(DESTBIN)) \ + || echo "Error: Could not install the game with correct permissions !!" \ + )) || true + + @(test x$(HIGHSCORE_DIRECTORY) != x \ + && ((touch $(DESTSCORES) && chown $(highscore_user):$(highscore_group) $(DESTSCORES) \ + && chmod 0660 $(DESTSCORES)) \ + || echo "Error: Could not create system-wide highscore file with correct permissions !!" \ + )) || true + diff --git a/kmines/README b/kmines/README new file mode 100644 index 00000000..3371a474 --- /dev/null +++ b/kmines/README @@ -0,0 +1,16 @@ +KMINES : the KDE minesweeper +---------------------------- +Copyright (c) 1996-2004 Nicolas HADACEK (hadacek@kde.org) +Copyright (c) 2001 Mikhail Kourinny (mkourinny@yahoo.com) +Distributed under the GNU General Public License + + +This is a very classical minesweeper written from scratch +with three predefined levels and custom levels. + + Easy : 8x8 with 10 mines + Normal : 16x16 with 40 mines + Expert : 30x16 with 99 mines + + +Requirements : up to date KDE and QT libraries. diff --git a/kmines/TODO b/kmines/TODO new file mode 100644 index 00000000..655b7b07 --- /dev/null +++ b/kmines/TODO @@ -0,0 +1,14 @@ +TODO: + + * messages from/to a named pipe for external AI [yawn...] + * icons for easy/normal/expert + * new levels ... + * flower / star shaped levels + * option for only solvable games + + * do you have any idea ? + + +KNOWN BUGS: + + * please find one ! diff --git a/kmines/bitmaps/Makefile.am b/kmines/bitmaps/Makefile.am new file mode 100644 index 00000000..3266d0c7 --- /dev/null +++ b/kmines/bitmaps/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = smile smile_happy smile_ohno smile_stress + diff --git a/kmines/bitmaps/README b/kmines/bitmaps/README new file mode 100644 index 00000000..368803c1 --- /dev/null +++ b/kmines/bitmaps/README @@ -0,0 +1 @@ +These XPMs are a contribution from Andreas Zehender. Many thanks! diff --git a/kmines/bitmaps/smile b/kmines/bitmaps/smile new file mode 100644 index 00000000..9977db89 --- /dev/null +++ b/kmines/bitmaps/smile @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+..+...........+..+$..", +"..#+.+$...........$+.+#..", +"..@@.@#+.........+#@.@@..", +"..+$...#*.......*#...$+..", +"...&@...@#@+++@#@...@&...", +"...+$+...+@$#$@+...+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_happy b/kmines/bitmaps/smile_happy new file mode 100644 index 00000000..475ac3eb --- /dev/null +++ b/kmines/bitmaps/smile_happy @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_happy_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+..+++...+++..+$+...", +"...&@#+@#%%+.+%%#@+#@&...", +"..+$.+#%%%%%#%%%%%#+.$+..", +"..@@..+%%%%%+%%%%%+..@@..", +"..#+...@#%%@.@%%#@...+#..", +"..$+....+++...+++....+$..", +"..%...................%..", +"..$+..+...........+..+$..", +"..#+.+$...........$+.+#..", +"..@@.@#+.........+#@.@@..", +"..+$...#*.......*#...$+..", +"...&@...@#@+++@#@...@&...", +"...+$+...+@$#$@+...+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_ohno b/kmines/bitmaps/smile_ohno new file mode 100644 index 00000000..fb8d0784 --- /dev/null +++ b/kmines/bitmaps/smile_ohno @@ -0,0 +1,37 @@ +/* XPM */ +const char * smile_ohno_xpm[] = { +"25 25 9 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +"* c #A0A0A0", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.................+$..", +"..#+.................+#..", +"..@@.....+@$#$@+.....@@..", +"..+$....@#@+++@#@....$+..", +"...&@..#*.......*#..@&...", +"...+$+.+.........+.+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_sleep b/kmines/bitmaps/smile_sleep new file mode 100644 index 00000000..695cf9bd --- /dev/null +++ b/kmines/bitmaps/smile_sleep @@ -0,0 +1,36 @@ +/* XPM */ +const char * smile_sleep_xpm[] = { +"25 25 8 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@...............@&...", +"..+$.................$+..", +"..@@...@%%.....%%@...@@..", +"..#+.................+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.................+$..", +"..#+.................+#..", +"..@@.................@@..", +"..+$.....%%%%%%#.....$+..", +"...&@...............@&...", +"...+$+.............+$+...", +"....+$+...........+$+....", +".....+$@.........@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/bitmaps/smile_stress b/kmines/bitmaps/smile_stress new file mode 100644 index 00000000..57b973da --- /dev/null +++ b/kmines/bitmaps/smile_stress @@ -0,0 +1,36 @@ +/* XPM */ +const char * smile_stress_xpm[] = { +"25 25 8 1", +" c None", +". c #FFFF00", +"+ c #C0C000", +"@ c #808000", +"# c #404000", +"$ c #303030", +"% c #000000", +"& c #585858", +".........................", +".........................", +"........+@#$%$#@+........", +"......+&$@++.++@$&+......", +".....+$@.........@$+.....", +"....+$+...........+$+....", +"...+$+.............+$+...", +"...&@..+@@+...+@@+..@&...", +"..+$...@%%@...@%%@...$+..", +"..@@...@%%@...@%%@...@@..", +"..#+...+@@+...+@@+...+#..", +"..$+.................+$..", +"..%...................%..", +"..$+.......+++.......+$..", +"..#+......@#$#@......+#..", +"..@@.....@#...#@.....@@..", +"..+$.....#+...+#.....$+..", +"...&@....#+...+#....@&...", +"...+$+...@&...&@...+$+...", +"....+$+...&#&#&...+$+....", +".....+$@...+@+...@$+.....", +"......+&$@++.++@$&+......", +"........+@#$%$#@+........", +".........................", +"........................."}; diff --git a/kmines/data/Makefile.am b/kmines/data/Makefile.am new file mode 100644 index 00000000..b3c82d50 --- /dev/null +++ b/kmines/data/Makefile.am @@ -0,0 +1,7 @@ +KDE_ICON = kmines + +xdg_apps_DATA = kmines.desktop + +appdatadir = $(kde_datadir)/kmines +appdata_DATA = eventsrc + diff --git a/kmines/data/eventsrc b/kmines/data/eventsrc new file mode 100644 index 00000000..867865db --- /dev/null +++ b/kmines/data/eventsrc @@ -0,0 +1,988 @@ +[!Global!] +IconName=kmines +Comment=KMines +Comment[ar]=لعبة الألغام (KMines) +Comment[be]=Сапёр +Comment[bn]=কে-মাইনà§à¦¸ +Comment[hi]=के-माइनà¥à¤¸ +Comment[hr]=KMine +Comment[ne]=केडीई माइन +Comment[pa]=ਕੇ-ਸਰà©à©°à¨— +Comment[pt_BR]=KMinas +Comment[sv]=Minröjning +Comment[ta]=கேகனà¯à®©à®¿à®µà¯†à®Ÿà®¿à®•à®³à¯ +Comment[tg]=KСапёр +Comment[tr]=Mayın tarlası +Comment[wa]=KMenes +Comment[zh_TW]=KMine 踩地雷 + +[reveal] +Name=Reveal case +Name[ar]=أظهر القضية +Name[be]=Ðдкрыццё Ð¿Ð¾Ð»Ñ +Name[bg]=Разкриване +Name[bn]=মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Name[bs]=Otkrij polja +Name[ca]=Descobreix casella +Name[cs]=Odkrýt pole +Name[cy]=Dangos cas +Name[da]=Vis felt +Name[de]=Aufdecken +Name[el]=Αποκάλυψη +Name[eo]=MalkaÅi kazon +Name[es]=Revelar el caso +Name[et]=Avab välja +Name[eu]=Erakutsi kasua +Name[fa]=آشکار شدن موقعیت +Name[fi]=Paljasta peli +Name[fr]=Révéler la case +Name[gl]=Amosar cadrado +Name[he]=גלה ריבוע +Name[hi]=केस पà¥à¤°à¤•à¤Ÿ करें +Name[hr]=Otkrij sluÄaj +Name[hu]=MezÅ‘ felfedése +Name[is]=Sýna tösku +Name[it]=Rivela casella +Name[ja]=é–‹ã„ãŸã¨ã +Name[lt]=Atverti langelį +Name[lv]=AtrisinÄt +Name[mk]=Отворено е поле +Name[nb]=Avslør +Name[nds]=Opmaken +Name[ne]=केस पà¥à¤°à¤•à¤Ÿ +Name[nl]=Vak openen +Name[nn]=Avslør +Name[pa]=ਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsÅ‚oniÄ™te +Name[pt]=Quadrado revelado +Name[pt_BR]=Revelar quadrado +Name[ru]=Открытие Ð¿Ð¾Ð»Ñ +Name[se]=Čájet +Name[sk]=OdkryÅ¥ pole +Name[sl]=Odkrij ploÅ¡Äico +Name[sr]=Откриј Ñлучај +Name[sr@Latn]=Otkrij sluÄaj +Name[sv]=Avslöja ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Кушодани ҳолат +Name[tr]=Kutu aç +Name[uk]=Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐºÐ¾Ð¼Ñ–Ñ€ÐºÐ¸ +Name[zh_CN]=æ­å¼€ç›’ç›– +Name[zh_TW]=開挖方格 +Comment=Reveal case +Comment[be]=Ðдкрыццё Ð¿Ð¾Ð»Ñ +Comment[bg]=Разкриване +Comment[bn]=মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Comment[bs]=Otkrij polja +Comment[ca]=Descobreix casella +Comment[cs]=Odkrýt pole +Comment[cy]=Dangos cas +Comment[da]=Vis felt +Comment[de]=Aufdecken +Comment[el]=Αποκάλυψη +Comment[eo]=MalkaÅi kazon +Comment[es]=Revelar el caso +Comment[et]=Avab välja +Comment[eu]=Erakutsi kasua +Comment[fa]=آشکار شدن موقعیت +Comment[fi]=Paljasta peli +Comment[fr]=Révéler la case +Comment[gl]=Amosar cadrado +Comment[he]=גלה ריבוע +Comment[hi]=केस पà¥à¤°à¤•à¤Ÿ करें +Comment[hr]=Otkrij sluÄaj +Comment[hu]=MezÅ‘ felfedése +Comment[is]=Sýna tösku +Comment[it]=Rivela casella +Comment[ja]=é–‹ã„ãŸã¨ã +Comment[km]=ករណី​បក +Comment[lt]=Atverti langelį +Comment[lv]=AtrisinÄt +Comment[mk]=Отворено е поле +Comment[nb]=Avslør +Comment[nds]=Opmaken +Comment[ne]=केस पà¥à¤°à¤•à¤Ÿ +Comment[nl]=Vak openen +Comment[nn]=Avslør +Comment[pl]=Pole zaznaczone +Comment[pt]=Quadrado revelado +Comment[pt_BR]=Revelar quadrado +Comment[ru]=Открытие Ð¿Ð¾Ð»Ñ +Comment[se]=Čájet +Comment[sk]=OdkryÅ¥ pole +Comment[sl]=Odkrij ploÅ¡Äico +Comment[sr]=Откриј Ñлучај +Comment[sr@Latn]=Otkrij sluÄaj +Comment[sv]=Avslöja ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Comment[tg]=Кушодани ҳолат +Comment[tr]=Kutuyu aç +Comment[uk]=Відкрити комірку +Comment[zh_CN]=æ­å¼€ç›’ç›– +Comment[zh_TW]=開挖方格 +default_presentation=0 + +[autoreveal] +Name=Autoreveal case +Name[be]=Ðдкрыццё пуÑÑ‚Ñ‹Ñ… палёў +Name[bg]=Ðвтоматично разкриване +Name[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿà¦­à¦¾à¦¬à§‡ মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Name[bs]=Automatski otkrij polja +Name[ca]=Descobreix automàticament casella +Name[cs]=Automaticky odkrýt pole +Name[cy]=Dangos cas yn ymysgogol +Name[da]=Vis felt automatisk +Name[de]=Automatisch aufdecken +Name[el]=Αυτόματη αποκάλυψη +Name[eo]=AÅ­tomalkaÅi kazon +Name[es]=Autorevelar el caso +Name[et]=Välja automaatne avamine +Name[eu]=Auto-erakutsi kasua +Name[fa]=آشکار شدن موقعیت به طور خودکار +Name[fi]=Automaattisesti paljasta peli +Name[fr]=Révéler automatiquement la case +Name[gl]=Auto-amosar cadrado +Name[he]=גלה ריבוע ×וטומטית +Name[hi]=केस सà¥à¤µà¤¯à¤‚ पà¥à¤°à¤•à¤Ÿ करें +Name[hr]=Automatski otkrij sluÄaj +Name[hu]=MezÅ‘ automatikus felfedése +Name[is]=Sýna tösku sjálfkrafa +Name[it]=Rivela automaticamente caselle +Name[ja]=自動ã§é–‹ã„ãŸã¨ã +Name[km]=ករណី​បក​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž· +Name[lt]=Automatinis atvÄ—rimas +Name[lv]=AutomÄtiski atrisinÄt +Name[mk]=ÐвтоматÑки е отворено поле +Name[nb]=Automatisk avsløring +Name[nds]=Automaatsch opmaken +Name[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤•à¤Ÿ केस +Name[nl]=Vak automatisch openen +Name[nn]=Automatisk avsløring +Name[pa]=ਆਟੋਰੀਵਲ ਕੇਸ +Name[pl]=Pole odsÅ‚oniÄ™te automatycznie +Name[pt]=Auto-revelar quadrado +Name[pt_BR]=Auto-revelar quadrado +Name[ru]=Открытие пуÑÑ‚Ñ‹Ñ… полей +Name[se]=AutomáhtalaÄÄat Äájet +Name[sk]=Automaticky odkryÅ¥ pole +Name[sl]=Samodejno odkrij ploÅ¡Äico +Name[sr]=ÐутоматÑки откриј Ñлучај +Name[sr@Latn]=Automatski otkrij sluÄaj +Name[sv]=Avslöja ruta automatiskt +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ தானே வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Name[tg]=Худкушодашудани ҳолат +Name[tr]=Otomatik kutu aç +Name[uk]=Ðвтоматичне відкрити комірки +Name[zh_CN]=自动æ­å¼€ç›’ç›– +Name[zh_TW]=周åœè‡ªå‹•é–‹æŒ–方格 +Comment=Autoreveal case +Comment[be]=Ðдкрыццё пуÑÑ‚Ñ‹Ñ… палёў +Comment[bg]=Ðвтоматично разкриване +Comment[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à¦¿à§Ÿà¦­à¦¾à¦¬à§‡ মাইন পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করো +Comment[bs]=Automatski otkrij polja +Comment[ca]=Descobreix automàticament casella +Comment[cs]=Automaticky odkrýt pole +Comment[cy]=Dangos cas yn ymysgogol +Comment[da]=Vis felt automatisk +Comment[de]=Automatisch aufdecken +Comment[el]=Αυτόματη αποκάλυψη +Comment[eo]=AÅ­tomalkaÅi kazon +Comment[es]=Autorevelar el caso +Comment[et]=Välja automaatne avamine +Comment[eu]=Auto-erakutsi kasua +Comment[fa]=آشکار شدن موقعیت به طور خودکار +Comment[fi]=Automaattisesti paljasta peli +Comment[fr]=Révéler automatiquement la case +Comment[gl]=Auto-amosar cadrado +Comment[he]=גלה ריבוע ×וטומטית +Comment[hi]=केस सà¥à¤µà¤¯à¤‚ पà¥à¤°à¤•à¤Ÿ करें +Comment[hr]=Automatski otkrij sluÄaj +Comment[hu]=MezÅ‘ automatikus felfedése +Comment[is]=Sýna tösku skjakrafa +Comment[it]=Rivela automaticamente caselle +Comment[ja]=自動ã§é–‹ã„ãŸã¨ã +Comment[km]=ករណី​បក​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž· +Comment[lt]=Automatinis atvÄ—rimas +Comment[lv]=AutomÄtiski atrisinÄt +Comment[mk]=ÐвтоматÑки е отворено поле +Comment[nb]=Automatisk avsløring +Comment[nds]=Automaatsch opmaken +Comment[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤•à¤Ÿ केस +Comment[nl]=Vak automatisch openen +Comment[nn]=Automatisk avsløring +Comment[pl]=Pole odsÅ‚oniÄ™te automatycznie +Comment[pt]=Quadrado auto-revelado +Comment[pt_BR]=Auto-revelar quadrado +Comment[ru]=Открытие пуÑÑ‚Ñ‹Ñ… полей +Comment[se]=AutomáhtalaÄÄat Äájet +Comment[sk]=Automaticky odkryÅ¥ pole +Comment[sl]=Samodejno odkrij ploÅ¡Äico +Comment[sr]=ÐутоматÑки откриј Ñлучај +Comment[sr@Latn]=Automatski otkrij sluÄaj +Comment[sv]=Avslöja ruta automatiskt +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ தானே வெளிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ +Comment[tg]=Худкушодани ҳолат +Comment[tr]=Kutuyu otomatik aç +Comment[uk]=Ðвтоматично відкрити комірку +Comment[zh_CN]=自动æ­å¼€ç›’ç›– +Comment[zh_TW]=周åœè‡ªå‹•é–‹æŒ–方格 +default_presentation=0 + +[mark] +Name=Mark case +Name[be]=Пазнака міны +Name[bg]=ПоÑтавÑне на флаг +Name[bn]=মাইন-ঠচিহà§à¦¨ দাও +Name[bs]=OznaÄi polje +Name[ca]=Marca casella +Name[cs]=OznaÄit pole +Name[cy]=Marcio cas +Name[da]=Markér felt +Name[de]=Markieren +Name[el]=Σημείωση ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Name[eo]=Marki kazon +Name[es]=Marcar el caso +Name[et]=Märgib välja +Name[eu]=Markatu kasua +Name[fa]=مشخص کردن موقعیت +Name[fi]=Merkkaa peli +Name[fr]=Marquer la case +Name[gl]=Marcar cadrado +Name[he]=סמן ריבוע +Name[hi]=केस चिहà¥à¤¨à¤¿à¤¤ करें +Name[hr]=Obilježi sluÄaj +Name[hu]=MezÅ‘ megjelölése +Name[is]=Merkja tösku +Name[it]=Segna casella +Name[ja]=マークã—ãŸã¨ã +Name[km]=ករណី​សម្គាល់ +Name[lt]=PažymÄ—ti langelį +Name[lv]=MarÄ·Ä“t +Name[mk]=Обележано е поле +Name[nb]=Merk +Name[nds]=Markeren +Name[ne]=चिनà¥à¤¹ केस +Name[nl]=Vak markeren +Name[nn]=Merk +Name[pa]=ਮਾਰਕ ਕੇਸ +Name[pl]=Pole zaznaczone +Name[pt]=Marcar quadrado +Name[pt_BR]=Marcar quadrado +Name[ru]=Отметка мины +Name[se]=Merke +Name[sk]=OznaÄiÅ¥ pole +Name[sl]=OznaÄi ploÅ¡Äico +Name[sr]=Обележи Ñлучај +Name[sr@Latn]=Obeleži sluÄaj +Name[sv]=Markera ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿ +Name[tg]=Ðишонакунии ҳолат +Name[tr]=Kutuyu iÅŸaretle +Name[uk]=ÐŸÐ¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ€ÐºÐ¸ +Name[zh_CN]=æ ‡è®°ç›’å­ +Name[zh_TW]=標記方格 +Comment=Mark case +Comment[be]=Пазнака міны +Comment[bg]=ПоÑтавÑне на флаг +Comment[bn]=সমà§à¦­à¦¾à¦¬à§à¦¯ মাইন-ঠচিহà§à¦¨ দাও +Comment[bs]=OznaÄi polje +Comment[ca]=Marca casella +Comment[cs]=OznaÄit pole +Comment[cy]=Marcio cas +Comment[da]=Markér felt +Comment[de]=Markieren +Comment[el]=Σημείωση ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Comment[eo]=Marki kazon +Comment[es]=Marcar el caso +Comment[et]=Märgib välja +Comment[eu]=Markatu kasua +Comment[fa]=مشخص کردن موقعیت +Comment[fi]=Merkkaa peli +Comment[fr]=Marquer la case +Comment[gl]=Marcar cadrado +Comment[he]=סמן ריבוע +Comment[hi]=केस चिहà¥à¤¨à¤¿à¤¤ करें +Comment[hr]=Obilježi sluÄaj +Comment[hu]=MezÅ‘ megjelölése +Comment[is]=Merkja tösku +Comment[it]=Segna casella +Comment[ja]=マークã—ãŸã¨ã +Comment[km]=ករណី​សម្គាល់ +Comment[lt]=PažymÄ—ti langelį +Comment[lv]=MarÄ·Ä“t +Comment[mk]=Обележано е поле +Comment[nb]=Merk +Comment[nds]=Markeren +Comment[ne]=केसमा चिनà¥à¤¹ लगाउनà¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vak markeren +Comment[nn]=Merk +Comment[pl]=Zaznaczenie pola +Comment[pt]=Quadrado marcado +Comment[pt_BR]=Marcar quadrado +Comment[ru]=Отметка мины +Comment[se]=Merke +Comment[sk]=OznaÄiÅ¥ pole +Comment[sl]=OznaÄi ploÅ¡Äico +Comment[sr]=Обележи Ñлучај +Comment[sr@Latn]=Obeleži sluÄaj +Comment[sv]=Markera ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿ +Comment[tg]=Ðишонакунии ҳолат +Comment[tr]=Kutuyu iÅŸaretle +Comment[uk]=Помітити комірку +Comment[zh_CN]=æ ‡è®°ç›’å­ +Comment[zh_TW]=標記方格 +default_presentation=0 + +[unmark] +Name=Unmark case +Name[be]=Здыманне пазнакі +Name[bg]=Премахване на флаг +Name[bn]=মাইন-ঠদেওয়া চিহà§à¦¨ মà§à¦›à§‡ ফেল +Name[bs]=OdznaÄi polje +Name[ca]=Desmarca casella +Name[cs]=ZruÅ¡it oznaÄení pole +Name[cy]=Dadmarcio cas +Name[da]=Afmarkér felt +Name[de]=Markierung entfernen +Name[el]=ΑναίÏεση σημείωσης ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Name[eo]=Malmarki kazon +Name[es]=Desmarcar el caso +Name[et]=Eemaldab väljalt märgi +Name[eu]=Desmarkatu kasua +Name[fa]=نامشخص کردن موقعیت +Name[fi]=Poista merkki pelistä +Name[fr]=Ne plus marquer la case +Name[gl]=Desmarcar cadrado +Name[he]=הורד סימון מריבוע +Name[hi]=केस अचिहà¥à¤¨à¤¿à¤¤ करें +Name[hr]=Skini obilježje sa sluÄaja +Name[hu]=MezÅ‘ kijelölésének megszüntetése +Name[is]=Afmerkja tösku +Name[it]=Togli segno su casella +Name[ja]=マークを外ã—ãŸã¨ã +Name[km]=ករណី​មិន​សម្គាល់ +Name[lt]=AtžymÄ—ti langelį +Name[lv]=Noņemt marÄ·Ä“jumu +Name[mk]=Одобележано е поле +Name[nb]=Fjern merket +Name[nds]=Markeren wegdoon +Name[ne]=केसको चिनà¥à¤¹ हटाउनà¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vakmarkering verwijderen +Name[nn]=Fjern merke +Name[pa]=ਅਣ-ਮਾਰਕ ਕੇਸ +Name[pl]=Pole odznaczone +Name[pt]=Desmarcar quadrado +Name[pt_BR]=Desmarcar quadrado +Name[ru]=СнÑтие отметки +Name[se]=Váldde mearkka eret +Name[sk]=OdstrániÅ¥ znaÄku poľa +Name[sl]=Odstrani oznako +Name[sr]=Скини обележје Ñа Ñлучаја +Name[sr@Latn]=Skini obeležje sa sluÄaja +Name[sv]=Avmarkera ruta +Name[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿à®•à¯à®•à®¾à®¤à¯‡ +Name[tg]=Гирифтани нишонаи ҳолат +Name[tr]=Kutudaki iÅŸareti kaldır +Name[uk]=ЗнÑÑ‚Ñ‚Ñ Ð¿Ð¾Ð¼Ñ–Ñ‚ÐºÐ¸ з комірки +Name[zh_CN]=ä¸æ ‡è®°ç›’å­ +Name[zh_TW]=去標記方格 +Comment=Unmark case +Comment[be]=Здыманне пазнакі +Comment[bg]=Премахване на флаг +Comment[bn]=মাইন-ঠদেওয়া চিহà§à¦¨ মà§à¦›à§‡ ফেল +Comment[bs]=OdznaÄi polje +Comment[ca]=Desmarca casella +Comment[cs]=ZruÅ¡it oznaÄení pole +Comment[cy]=Dadmarcio cas +Comment[da]=Afmarkér felt +Comment[de]=Markierung entfernen +Comment[el]=ΑναίÏεση σημείωσης ÎºÎ¿Ï…Ï„Î¹Î¿Ï +Comment[eo]=Malmarki kazon +Comment[es]=Desmarcar el caso +Comment[et]=Eemaldab väljalt märgi +Comment[eu]=Desmarkatu kasua +Comment[fa]=نامشخص کردن موقعیت +Comment[fi]=Poista merkki pelistä +Comment[fr]=Ne plus marquer la case +Comment[gl]=Desmarcar cadrado +Comment[he]=הורד סימון מריבוע +Comment[hi]=केस अचिहà¥à¤¨à¤¿à¤¤ करें +Comment[hr]=Skini obilježje sa sluÄaja +Comment[hu]=MezÅ‘ megjelölésének megszüntetése +Comment[is]=Afmerkja tösku +Comment[it]=Togli segno a casella +Comment[ja]=マークを外ã—ãŸã¨ã +Comment[km]=ករណី​មិន​សម្គាល់ +Comment[lt]=AtžymÄ—ti langelį +Comment[lv]=Noņemt marÄ·Ä“jumu +Comment[mk]=Одобележано е поле +Comment[nb]=Fjern merket +Comment[nds]=Markeren wegdoon +Comment[ne]=केसको चिनà¥à¤¹ हटाउनà¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vakmarkering verwijderen +Comment[nn]=Fjern merke +Comment[pl]=Odznaczenie pola +Comment[pt]=Quadrado desmarcado +Comment[pt_BR]=Desmarcar quadrado +Comment[ru]=СнÑтие отметки +Comment[se]=Váldde eret mearkka +Comment[sk]=OdstrániÅ¥ znaÄku poľa +Comment[sl]=Odstrani oznako +Comment[sr]=Скини обележје Ñа Ñлучаја +Comment[sr@Latn]=Skini obeležje sa sluÄaja +Comment[sv]=Avmarkera ruta +Comment[ta]=நிகழà¯à®šà¯à®šà®¿à®¯à¯ˆ கà¯à®±à®¿à®•à¯à®•à®¾à®¤à¯‡ +Comment[tg]=Гирифтани нишонаи ҳолат +Comment[tr]=Kutudaki iÅŸareti kaldır +Comment[uk]=ЗнÑти помітку з комірки +Comment[zh_CN]=ä¸æ ‡è®°ç›’å­ +Comment[zh_TW]=去標記方格 +default_presentation=0 + +[explosion] +Name=Explosion +Name[ar]=انÙجار +Name[be]=Выбух +Name[bg]=ЕкÑÐ¿Ð»Ð¾Ð·Ð¸Ñ +Name[bn]=বিসà§à¦«à§‹à¦°à¦£ +Name[br]=Tarzhad +Name[bs]=Eksplozija +Name[ca]=Explosió +Name[cs]=Exploze +Name[cy]=Ffrwydriad +Name[da]=Eksplosion +Name[el]=ΈκÏηξη +Name[eo]=Eksplodo +Name[es]=Explosión +Name[et]=Plahvatus +Name[eu]=Eztanda +Name[fa]=انÙجار +Name[fi]=Räjähdys +Name[gl]=Estoupido +Name[he]=פיצוץ +Name[hi]=धमाका +Name[hr]=Eksplozija +Name[hu]=Robbanás +Name[is]=Sprenging +Name[it]=Esplosione +Name[ja]=爆発 +Name[km]=ការ​ផ្ទុះ +Name[ko]=í­ë°œ +Name[lt]=Sprogimas +Name[lv]=Eksplozija +Name[mk]=ЕкÑплозија +Name[nb]=Eksplosjon +Name[nds]=Exploschoon +Name[ne]=विसà¥à¤«à¥‹à¤Ÿ +Name[nl]=Explosie +Name[nn]=Eksplosjon +Name[pa]=ਧਮਾਕੇ +Name[pl]=Wybuch +Name[pt]=Explosão +Name[pt_BR]=Explosão +Name[ro]=Explozie +Name[ru]=Взрыв +Name[se]=EksploÅ¡uvdna +Name[sk]=Výbuch +Name[sl]=Eksplozija +Name[sr]=ЕкÑплозија +Name[sr@Latn]=Eksplozija +Name[ta]=அதிரà¯à®µà¯†à®Ÿà®¿ +Name[tg]=Таркиш +Name[tr]=Patlama +Name[uk]=Вибух +Name[uz]=Portlash +Name[uz@cyrillic]=Портлаш +Name[wa]=Esplôzion +Name[zh_CN]=爆炸 +Name[zh_TW]=爆炸 +Comment=Explosion +Comment[be]=Выбух +Comment[bg]=ЕкÑÐ¿Ð»Ð¾Ð·Ð¸Ñ +Comment[bn]=বিসà§à¦«à§‹à¦°à¦£ +Comment[br]=Tarzhad +Comment[bs]=Eksplozija +Comment[ca]=Explosió +Comment[cs]=Exploze +Comment[cy]=Ffrwydriad +Comment[da]=Eksplosion +Comment[el]=ΈκÏηξη +Comment[eo]=Eksplodo +Comment[es]=Explosión +Comment[et]=Plahvatus +Comment[eu]=Eztanda +Comment[fa]=انÙجار +Comment[fi]=Räjähdys +Comment[gl]=Estoupido +Comment[he]=פיצוץ +Comment[hi]=धमाका +Comment[hr]=Eksplozija +Comment[hu]=Robbanás +Comment[is]=Sprenging +Comment[it]=Esplosione +Comment[ja]=爆発 +Comment[km]=ការ​ផ្ទុះ +Comment[ko]=í­ë°œ +Comment[lt]=Sprogimas +Comment[lv]=Eksplozija +Comment[mk]=ЕкÑплозија +Comment[nb]=Eksplosjon +Comment[nds]=Exploschoon +Comment[ne]=विसà¥à¤«à¥‹à¤Ÿ +Comment[nl]=Explosie +Comment[nn]=Eksplosjon +Comment[pa]=ਧਮਾਕਾਖੇਜ਼ +Comment[pl]=Wybuch +Comment[pt]=Explosão +Comment[pt_BR]=Explosão +Comment[ro]=Explozie +Comment[ru]=Взрыв +Comment[se]=EksploÅ¡uvdna +Comment[sk]=Výbuch +Comment[sl]=Eksplozija +Comment[sr]=ЕкÑплозија +Comment[sr@Latn]=Eksplozija +Comment[ta]=அதிரà¯à®µà¯†à®Ÿà®¿ +Comment[tg]=Таркиш +Comment[tr]=Patlama +Comment[uk]=Вибух +Comment[uz]=Portlash +Comment[uz@cyrillic]=Портлаш +Comment[wa]=Esplôzion +Comment[zh_CN]=爆炸 +Comment[zh_TW]=爆炸 +default_presentation=0 + +[won] +Name=Game won +Name[ar]=ربحت اللعبة +Name[be]=Перамога +Name[bg]=Спечелихте +Name[bn]=খেলা জিতেছেন +Name[br]=Gounezet eo ar c'hoari +Name[bs]=Pobjeda +Name[ca]=Partida guanyada +Name[cs]=Vyhraná hra +Name[cy]=Gêm wedi ei ennill +Name[da]=Spillet vundet +Name[de]=Spiel gewonnen +Name[el]=Παιχνίδι κεÏδήθηκε +Name[eo]=Ludo venkita +Name[es]=Partida ganada +Name[et]=Mäng läbi, sina võitsid +Name[eu]=Jokoa irabazi da +Name[fa]=برد بازی +Name[fi]=Peli voitettu +Name[fr]=Partie gagnée +Name[gl]=Xogo gañado +Name[he]=ניצחת! +Name[hi]=खेल में जीत हà¥à¤ˆ +Name[hr]=Igra je dobivena +Name[hu]=GyÅ‘zelem +Name[is]=Leikur unninn +Name[it]=Partita vinta +Name[ja]=ゲームã«å‹ã¡ +Name[km]=ល្បែង​បាន​ឈ្នះ +Name[ko]=게임ì—ì„œ ì´ê¹€ +Name[lt]=Žaidimas laimÄ—tas +Name[lv]=SpÄ“le uzvarÄ“ta +Name[mk]=Играта е добиена +Name[nb]=Du vant +Name[nds]=Speel wunnen +Name[ne]=खेल जितà¥à¤¨à¥ भयो +Name[nl]=Spel gewonnen +Name[nn]=Du vann +Name[pa]=ਖੇਡ ਜਿੱਤੀ +Name[pl]=Gra wygrana +Name[pt]=Jogo ganho +Name[pt_BR]=Jogo ganho +Name[ro]=Joc cîştigat +Name[ru]=Победа +Name[se]=Don vuitet +Name[sk]=Vyhraná hra +Name[sl]=Igra je dobljena +Name[sr]=Игра је добијена +Name[sr@Latn]=Igra je dobijena +Name[sv]=Du vann spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ ғолиб омадед +Name[tr]=Oyun kazanıldı +Name[uk]=Гру виграно +Name[wa]=Djeu wangnî +Name[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²ç²å‹ +Comment=Game won +Comment[ar]=ربحت اللعبة +Comment[be]=Перамога +Comment[bg]=Спечелихте +Comment[bn]=খেল খতম +Comment[br]=Gounezet eo ar c'hoari +Comment[bs]=Pobjeda +Comment[ca]=Partida guanyada +Comment[cs]=Vyhraná hra +Comment[cy]=Gêm wedi ei ennill +Comment[da]=Spil vundet +Comment[de]=Spiel gewonnen +Comment[el]=Παιχνίδι κεÏδήθηκε +Comment[eo]=Ludo venkita +Comment[es]=Partida ganada +Comment[et]=Mäng läbi, sina võitsid +Comment[eu]=Jokoa irabazi da +Comment[fa]=برد بازی +Comment[fi]=Peli voitettu +Comment[fr]=Partie gagnée +Comment[gl]=Xogo gañado +Comment[he]=ניצחת! +Comment[hi]=खेल में जीत हà¥à¤ˆ +Comment[hr]=Igra je dobivena +Comment[hu]=GyÅ‘zelem +Comment[is]=Leikur unninn +Comment[it]=Partita vinta +Comment[ja]=ゲームã«å‹ã¡ +Comment[km]=ល្បែង​បាន​ឈ្នះ +Comment[ko]=게임ì—ì„œ ì´ê¹€ +Comment[lt]=Žaidimas laimÄ—tas +Comment[lv]=SpÄ“le ir uzvarÄ“ta +Comment[mk]=Играта е добиена +Comment[nb]=Du vant! +Comment[nds]=Speel wunnen +Comment[ne]=खेल जितà¥à¤¨à¥ भयो +Comment[nl]=Spel gewonnen +Comment[nn]=Du vann +Comment[pa]=ਖੇਡ ਜਿੱਤੀ +Comment[pl]=Gra wygrana +Comment[pt]=Jogo ganho +Comment[pt_BR]=Jogo ganho +Comment[ro]=Joc cîştigat +Comment[ru]=Победа +Comment[se]=Don vuitet +Comment[sk]=Vyhraná hra +Comment[sl]=Igra je dobljena +Comment[sr]=Игра је добијена +Comment[sr@Latn]=Igra je dobijena +Comment[sv]=Du vann spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ ஜெயிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ ғолиб омадед +Comment[tr]=Oyun kazanıldı +Comment[uk]=Гру виграно +Comment[wa]=Djeu wangnî +Comment[zh_CN]=æ‚¨èµ¢äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²ç²å‹ +default_presentation=0 + +[lost] +Name=Game lost +Name[ar]=خسرت اللعبة +Name[be]=Параза +Name[bg]=Загубихте +Name[bn]=খেলায় হেরে গিয়েছেন +Name[br]=Kollet eo ar c'hoari +Name[bs]=Poraz +Name[ca]=Partida perduda +Name[cs]=Prohraná hra +Name[cy]=Gêm wedi ei golli +Name[da]=Spil tabt +Name[de]=Spiel verloren +Name[el]=Παιχνίδι χάθηκε +Name[eo]=Ludo malvenkita +Name[es]=Partida perdida +Name[et]=Mäng läbi, sina kaotasid +Name[eu]=Jokoa galdu da +Name[fa]=باخت بازی +Name[fi]=Peli hävitty +Name[fr]=Partie perdue +Name[gl]=Xogo perdido +Name[he]=המשחק הסתיי×, הפסדת +Name[hi]=खेल में हार हà¥à¤ˆ +Name[hr]=Igra je izgubljena +Name[hu]=Vereség +Name[is]=Leik tapað +Name[it]=Partita persa +Name[ja]=ゲームã«è² ã‘ +Name[km]=ល្បែង​បាន​ចាញ់ +Name[ko]=게임ì—ì„œ ì§ +Name[lt]=Žaidimas pralaimÄ—tas +Name[lv]=SpÄ“le zaudÄ“ta +Name[mk]=Играта е изгубена +Name[nb]=Du tapte +Name[nds]=Speel verloren +Name[ne]=खेल हारà¥à¤¨à¥ भयो +Name[nl]=Spel verloren +Name[nn]=Du tapte +Name[pa]=ਖੇਡ ਹਾਰੀ +Name[pl]=Koniec gry, przegraÅ‚eÅ› +Name[pt]=Jogo perdido +Name[pt_BR]=Jogo perdido +Name[ro]=Joc pierdut +Name[ru]=Поражение +Name[se]=Don vuoittohallet +Name[sk]=Prehraná hra +Name[sl]=Igra je izgubljena +Name[sr]=Игра је изгубљена +Name[sr@Latn]=Igra je izgubljena +Name[sv]=Du förlorade spelet +Name[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Name[tg]=Дар бозӣ мағлуб шудед +Name[tr]=Oyun kaybedildi +Name[uk]=Гра програна +Name[wa]=Djeu pierdou +Name[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Name[zh_TW]=éŠæˆ²å¤±æ•— +Comment=Game lost +Comment[ar]=خسرت اللعبة +Comment[be]=Параза +Comment[bg]=Загубихте +Comment[bn]=খেলায় হেরে গিয়েছেন +Comment[br]=Koll eo ar c'hoari +Comment[bs]=Poraz +Comment[ca]=Partida perduda +Comment[cs]=Prohraná hra +Comment[cy]=Gêm wedi ei golli +Comment[da]=Spil tabt +Comment[de]=Spiel verloren +Comment[el]=Παιχνίδι χάθηκε +Comment[eo]=Ludo malvenkita +Comment[es]=Partida perdida +Comment[et]=Mäng läbi, sina kaotasid +Comment[eu]=Jokoa galdu da +Comment[fa]=باخت بازی +Comment[fi]=Peli hävitty +Comment[fr]=Partie perdue +Comment[gl]=Xogo perdido +Comment[he]=המשחק הסתיי×, הפסדת +Comment[hi]=खेल में हार हà¥à¤ˆ +Comment[hr]=Igra je izgubljena +Comment[hu]=Vereség +Comment[is]=Leik tapað +Comment[it]=Partita persa +Comment[ja]=ゲームã«è² ã‘ +Comment[km]=ល្បែង​បាន​ចាញ់ +Comment[ko]=게임ì—ì„œ ì§ +Comment[lt]=Žaidimas pralaimÄ—tas +Comment[lv]=SpÄ“le ir zaudÄ“ta +Comment[mk]=Играта е изубена +Comment[nb]=Du tapte +Comment[nds]=Speel verloren +Comment[ne]=खेल हारà¥à¤¨à¥ भयो +Comment[nl]=Spel verloren +Comment[nn]=Du tapte +Comment[pa]=ਖੇਡ ਹਾਰੀ +Comment[pl]=Koniec gry, przegraÅ‚eÅ› +Comment[pt]=Jogo perdido +Comment[pt_BR]=Jogo perdido +Comment[ro]=Joc pierdut +Comment[ru]=Поражение +Comment[se]=Don vuoittohallet +Comment[sk]=Prehraná hra +Comment[sl]=Igra je izgubljena +Comment[sr]=Игра је изгубљена +Comment[sr@Latn]=Igra je izgubljena +Comment[sv]=Du förlorade spelet +Comment[ta]=ஆடà¯à®Ÿà®®à¯ இழகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯ +Comment[tg]=Дар бозӣ мағлуб шудед +Comment[tr]=Oyun kaybedildi +Comment[uk]=Гра програна +Comment[wa]=Djeu pierdou +Comment[zh_CN]=æ‚¨è¾“äº†æ¸¸æˆ +Comment[zh_TW]=éŠæˆ²å¤±æ•— +default_presentation=0 + +[set_uncertain] +Name=Set question mark +Name[be]=Пазначыць пытальнікам +Name[bg]=ПоÑтавÑне на въпроÑителна +Name[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ দিন +Name[bs]=Postavi upitnik +Name[ca]=Marca amb un interrogant +Name[cs]=Nastavit otazník +Name[cy]=Gosod gofynnod +Name[da]=Sæt spørgsmÃ¥lstegn +Name[de]=Markierung setzen +Name[el]=ΠÏοσθήκη εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Name[eo]=Meti demandsignon +Name[es]=Establecer signo de interrogación +Name[et]=Pane küsimärk +Name[eu]=Ezarri galdera-marka +Name[fa]=گذاردن علامت سؤال +Name[fi]=Aseta kysymysmerkki +Name[fr]=Ajout d'un point d'interrogation +Name[he]=הצב סימן ש×לה +Name[hr]=Postavi oznaku pitanja +Name[hu]=KérdÅ‘jel beállítása +Name[is]=Setja spurningamerkið +Name[it]=Metti punto interrogativo +Name[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’セットã™ã‚‹ +Name[km]=កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Name[lt]=UždÄ—ti klaustukÄ… +Name[lv]=Novietot jautÄjuma zÄ«mi +Name[mk]=ПоÑтавен е прашалник +Name[nb]=Angi spørsmÃ¥ltegn +Name[nds]=Fraagteken setten +Name[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vraagteken instellen +Name[nn]=Set spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਬਣਾਓ +Name[pl]=Wstaw znak zapytania +Name[pt]=Colocar um ponto de interrogação +Name[pt_BR]=Marcar como ponto de interrogação +Name[ru]=Отметка вопроÑом +Name[se]=Bija jearaldatmearkka +Name[sk]=NastaviÅ¥ otáznik +Name[sl]=Postavi vpraÅ¡aj +Name[sr]=ПоÑтави знак питања +Name[sr@Latn]=Postavi znak pitanja +Name[sv]=Placerade frÃ¥getecken +Name[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿à®¯à¯ˆ அமை +Name[uk]=ПоÑтавити знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Name[zh_CN]=è®¾ç½®äº†é—®å· +Name[zh_TW]=設定å•è™Ÿ +Comment=Set question mark +Comment[be]=Пазначыць пытальнікам +Comment[bg]=ПоÑтавÑне на въпроÑителна +Comment[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ দিন +Comment[bs]=Postavi upitnik +Comment[ca]=Marca amb interrogant +Comment[cs]=Nastavit otazník +Comment[cy]=Gosod gofynnod +Comment[da]=Sæt spørgsmÃ¥lstegn +Comment[de]=Markierung setzen +Comment[el]=ΠÏοσθήκη εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Comment[eo]=Meti demandsignon +Comment[es]=Establecer signo de interrogación +Comment[et]=Pane küsimärk +Comment[eu]=Ezarri galdera-marka +Comment[fa]=گذاردن علامت سؤال +Comment[fi]=Aseta kysymysmerkki +Comment[fr]=Ajout d'un point d'interrogation +Comment[he]=הצב סימן ש×לה +Comment[hr]=Postavi oznaku pitanja +Comment[hu]=KérdÅ‘jel beállítása +Comment[is]=Setja spurningamerkið +Comment[it]=Metti punto interrogativo +Comment[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’セットã™ã‚‹ +Comment[km]=កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Comment[lt]=UždÄ—ti klaustukÄ… +Comment[lv]=Novieto jautÄjuma zÄ«mi +Comment[mk]=ПоÑтавен е прашалник +Comment[nb]=Angi spørsmÃ¥ltegn +Comment[nds]=Fraagteken setten +Comment[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ सेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vraagteken instellen +Comment[nn]=Set spørjeteikn +Comment[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਦਿਓ +Comment[pl]=Wstawia znak zapytania +Comment[pt]=Colocar um ponto de interrogação +Comment[pt_BR]=Marcar como ponto de interrogação +Comment[ru]=Отметка вопроÑом +Comment[se]=Bija jearaldatmearkka +Comment[sk]=NastaviÅ¥ otáznik +Comment[sl]=Postavi vpraÅ¡aj +Comment[sr]=ПоÑтави знак питања +Comment[sr@Latn]=Postavi znak pitanja +Comment[sv]=Placerade frÃ¥getecken +Comment[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿à®¯à¯ˆ அமை +Comment[uk]=ПоÑтавити знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Comment[zh_CN]=è®¾ç½®äº†é—®å· +Comment[zh_TW]=設定å•è™Ÿ +default_presentation=0 + +[unset_uncertain] +Name=Unset question mark +Name[be]=ЗнÑць пытальнік +Name[bg]=Премахване на въпроÑителна +Name[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ সরিয়ে ফেলà§à¦¨ +Name[bs]=IskljuÄi upitnik +Name[ca]=Desmarca l'interrogant +Name[cs]=ZruÅ¡it nastavení otazníku +Name[cy]=Dadosod gofynnod +Name[da]=Fjern spørgsmÃ¥lstegn +Name[de]=Markierung entfernen +Name[el]=ΑφαίÏεση του εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Name[eo]=Malmeti demandsignon +Name[es]=Quitar signo de interrogación +Name[et]=Eemalda küsimärk +Name[eu]=Kendu galdera-marka +Name[fa]=برداشتن علامت سؤال +Name[fi]=Poista kysymysmerkki +Name[fr]=Retrait d'un point d'interrogation +Name[he]=הורד סימן ש×לה +Name[hr]=Ukloni oznaku pitanja +Name[hu]=KérdÅ‘jel megszüntetése +Name[is]=Afsetja spurningamerkið +Name[it]=Togli punto interrogativo +Name[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’外㙠+Name[km]=ដោះ​កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Name[lt]=Nuimti klaustukÄ… +Name[lv]=Noņemt jautajuma zÄ«mi +Name[mk]=ОтÑтранет е прашалник +Name[nb]=Fjern spørsmÃ¥ltegn +Name[nds]=Fraagteken wegmaken +Name[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ अनसेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Name[nl]=Vraagteken weghalen +Name[nn]=Fjern spørjeteikn +Name[pa]=ਸਵਾਲੀਆ ਨਿਸ਼ਾਨ ਹਟਾਓ +Name[pl]=UsuÅ„ znak zapytania +Name[pt]=Retirar um ponto de interrogação +Name[pt_BR]=Desmarcar ponto de interrogação +Name[ru]=СнÑтие знака вопроÑа +Name[se]=Váldde jearaldatmearkka eret +Name[sk]=OdstrániÅ¥ otáznik +Name[sl]=Odstrani vpraÅ¡aj +Name[sr]=Скини знак питања +Name[sr@Latn]=Skini znak pitanja +Name[sv]=Tog bort frÃ¥getecken +Name[ta]=கேளà¯à®µà®¿à®•à¯ கà¯à®±à®¿ அமைகà¯à®•à®µà®¿à®²à¯à®²à¯ˆ +Name[tr]=Soru iÅŸaretini kaldır +Name[uk]=ЗнÑти знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Name[zh_CN]=å–æ¶ˆäº†é—®å· +Name[zh_TW]=å–消å•è™Ÿ +Comment=Unset question mark +Comment[be]=ЗнÑць пытальнік +Comment[bg]=Премахване на въпроÑителна +Comment[bn]=পà§à¦°à¦¶à§à¦¨à¦¬à§‹à¦§à¦• চিহà§à¦¨ সরিয়ে ফেলà§à¦¨ +Comment[bs]=IskljuÄi upitnik +Comment[ca]=Desmarca l'interrogant +Comment[cs]=ZruÅ¡it nastavení otazníku +Comment[cy]=Dadosod gofynnod +Comment[da]=Fjern spørgsmÃ¥lstegn +Comment[de]=Markierung entfernen +Comment[el]=ΑφαίÏεση του εÏÏ‰Ï„Î·Î¼Î±Ï„Î¹ÎºÎ¿Ï +Comment[eo]=Malmeti demandsignon +Comment[es]=Quitar signo de interrogación +Comment[et]=Eemalda küsimärk +Comment[eu]=Kendu galdera-marka +Comment[fa]=برداشتن علامت سؤال +Comment[fi]=Poista kysymysmerkki +Comment[fr]=Retrait d'un point d'interrogation +Comment[he]=הורד סימן ש×לה +Comment[hr]=Ukloni oznaku pitanja +Comment[hu]=KérdÅ‘jel megszüntetése +Comment[is]=Afsetja spurningamerkið +Comment[it]=Togli punto interrogativo +Comment[ja]=クエスãƒãƒ§ãƒ³ãƒžãƒ¼ã‚¯ã‚’外㙠+Comment[km]=ដោះ​កំណážáŸ‹â€‹ážŸáž‰áŸ’ញា​សួរ +Comment[lt]=Nuimti klaustukÄ… +Comment[lv]=Noņem jautÄjuma zÄ«mi +Comment[mk]=ОтÑтранет е прашалник +Comment[nb]=Fjern spørsmÃ¥ltegn +Comment[nds]=Fraagteken wegmaken +Comment[ne]=पà¥à¤°à¤¶à¥à¤¨ चिनà¥à¤¹ अनसेट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥ +Comment[nl]=Vraagteken weghalen +Comment[nn]=Fjern spørjeteikn +Comment[pl]=Usuwa znak zapytania +Comment[pt]=Retirar um ponto de interrogação +Comment[pt_BR]=Desmarcar ponto de interrogação +Comment[ru]=СнÑтие знака вопроÑа +Comment[se]=Váldde jearaldatmearkka eret +Comment[sk]=OdstrániÅ¥ otáznik +Comment[sl]=Odstrani vpraÅ¡aj +Comment[sr]=Скини знак питања +Comment[sr@Latn]=Skini znak pitanja +Comment[sv]=Tog bort frÃ¥getecken +Comment[ta]=கேளà¯à®µà®¿à®•à¯à®•à¯à®±à®¿ நீகà¯à®•à¯ +Comment[tr]=Soru iÅŸaretini kaldır +Comment[uk]=ЗнÑти знак Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ +Comment[zh_CN]=å–æ¶ˆäº†é—®å· +Comment[zh_TW]=å–消å•è™Ÿ +default_presentation=0 diff --git a/kmines/data/hi128-app-kmines.png b/kmines/data/hi128-app-kmines.png new file mode 100644 index 0000000000000000000000000000000000000000..faaab0c2d0fea29135cfea508a88c95dc8aa0c6f GIT binary patch literal 12464 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 literal 0 HcmV?d00001 diff --git a/kmines/data/hi22-app-kmines.png b/kmines/data/hi22-app-kmines.png new file mode 100644 index 0000000000000000000000000000000000000000..fc1ae801101bd6c7fd1c156a52ff732baedb338c GIT binary patch literal 3926 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 literal 0 HcmV?d00001 diff --git a/kmines/data/hi32-app-kmines.png b/kmines/data/hi32-app-kmines.png new file mode 100644 index 0000000000000000000000000000000000000000..1074028009038cac66d59d831a9ed59450902bae GIT binary patch literal 2061 zcmV+o2=e!dP)1hd8OMKfFnb?n*SogyfsKvV2piXNTA;>&ND%O$r4pfP zq@V_Ws3<}aAoWUAp;9$SQIsm6fE2}2`H(iDG%1Z$;-HEoibRQ7dlOiP#xAkF_IiD+ z?e%&O&(2IgI7TrA8$$AzMjGjTj{m&RITGIjHa}AP#E$0XpHEMYMaHLRVvEVj-+(`7 zgUq^0JN4i3Qdzy6m4y|vn^JNnPbo5u{b!I*z7+zs(D0bVfpVFhHXAD{@}V>ziv0qy zSPz5Y9z=(1TguL21kAtQ=-$&4pPd*_sZlM<>}-Z*9zic|GT2#{)b?p2wh;+5AgM!A zgLFM)%es&>t=JJhXFE&|LWYHvvf}AyA#sK{eVd3^O|Mu_%w5crr-u=eVFJAc| zekGMNu*o8JQNSXAnYqUTSOri7SSEr+)WNuu-o3F@5+}%|5Fe%7jK{Tqil9Gp*(r%Z-xWF{7{EyWChWXC*}Q(nRj33X(v_&B@&7Hz#v% z0?Pphpdm>Df}oPgMaW!o`2>VLfT)?24f zojUbfU{cOyR8iLvbY1&K3HW@ziv9cdzqobl*4=);AD7DoKzVt&+|tr=sJ6ED@slS{ z9@TXn!!VE}X~}Z9UjoAR?c1Mz^2sNkudS{1+3j`!(&@SQ3W9*uYGvcbjRC*ke@;=9 zOfHwhFpSxUCgAh=D(dR$Ufi~A+wLt}wg50SHHBdq|0ioUoAG!&)~Tr}t0;<;mX_8G z3=I5zG5gXYXF_vx^Zp}8j{Nzlr=EJmX0vhm@?~^g$Ki00%jM8C4NaR1vnYy$!(q;! zKabDnW7VovxZUn;`T6+;!C>$T_b7+E5^%X(6)i0-tX z^wUo<352m*GyouQ#2Y&IJO1qGh^`ud+Z9FD@SuC4{Qv4Ft#?c0BK{P^*g_w3nYx7+Px zvsrq3dx=J)WHK3Yxg3%tk(ZZ;AP6K92}Vap*|B2>Hk*yX!9g@l!)!KVx7!Ja!PKcB85)mo8nRyu2JmQOIO6#N%;BM@NZ7BE;iy{C+=vzaNLg!J0K|>VQI^ z?@j`W0hYG5HY&=?F$96nZr()8RaKEBiGhIu6h$Er2;g?R@pwG+_V#k&!UcRj zA5~RV=({c(9l3A6e1FdpePEut}`|^mYI+L zoi|`iPfr*5{Qk!rPNyUQ!^6XLcXtz?nBavMUcm47Gd4EH^5x5Mxm?(6HY^qkilQKj zBATXAUtiC`g9i}=fws0bVzC&aDB}0~NhA`qwY9z5-{1e&`AS$&1&KsrFc=JuxLmH) zg#`s}li5rZi2xeSFc`0lBC-S z9gD?g&YnH{r%)*L5-`A*OVyoyAgQYA&Fj~%U!RzqbeEQvuF7+}g}(lNWLc)KzaOX5 zNnv5(0wr0Nv0AN+jEwO9`|lHtMzL5dn9XM5@i^`6?Vp`Hckaaa`1or;50JZC2yIVbb z_UzliVDR_4uKyDl=8kppUb7$vjHObk>s?)4H$_P*s;;gs@%elxio*E#I3uwbl~q*$ zeE8vq3;M;Ev5vI1w!SnxJbV)90%q>!Xt7~rFt?8VcXV{Lv%9-nb2uD%ZFD+kdC3>fe})_dOSr#R$wp1%{K!C znOs?1QsOHvE~b3tN;26j$z+mvJkC4syc2)*)mP8lx^?SC;0iExANC&zE?`?(S=pP% zjvdSDx^7HPP8w&;6MS0anYh{F|dkk48?MIFa`Ge67Iu0sDg* rd$9kgg?e7#QNRscWp22r4{GRt`L;1s8vOHW00000NkvXXu0mjfOMC{i literal 0 HcmV?d00001 diff --git a/kmines/data/hi48-app-kmines.png b/kmines/data/hi48-app-kmines.png new file mode 100644 index 0000000000000000000000000000000000000000..a1905746c029b24d4ab16afdf08f3d423da89157 GIT binary patch literal 3877 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 literal 0 HcmV?d00001 diff --git a/kmines/data/hi64-app-kmines.png b/kmines/data/hi64-app-kmines.png new file mode 100644 index 0000000000000000000000000000000000000000..b4f5d27f5051c598155b42aae01665a054e299df GIT binary patch literal 5325 zcmWld1yqx57={OebV`FVVB(|%1Vp+^LYmPrKpN==DcSIs&JhAqqohkh$&o4o5(5cI zVN9g!|9sEczH_#7_QrER*L}Z3Jl9pHB4;B9fk0H68Y%|BQ{mr>^fvI0c6qr3JcyhX zbreCMIikgec|s6~Ay!jG(I_Zy8s*|{IU5{b*g3X1_j)&ZwK%TneS*2iK>ho{M9R^Z z?9X(uQg83Hiqn#iKL0L0&!W2W{>4uz?`cl7F>`YjL$ialmZ9@kgGXQKF~tn(+%8Cz zB;+-{CSuoHW1@SpR6gEU)!_SQRnK!mlE+}2>`54|wE(wBcZz)tI-p_aAEBQpl{g#A z>BcGF7H0DJvusBi^^3Fcrm-*Btz~a(e_t(0_4Pd~N38#iZ|DwzM-fdjbN8U?E8$;; zlKxK$M}0qh-i626J!ci`2=G%gH6RygF4dCB@4?;{yM>68JH0_h<@K(P>dr_C$|QR^ z`xeBETR#x@WW@-^Jc~B6gky(c=Qo>Tj9rA{fx+#Tp8mHaz!*ZSl}26#Q2!Hn%Lum~ znmehA&@mOnL(5AeA6Oi2 zEB8y8gQWr0nwFBIMuHmxPTz!pd`Yl^9L4@Rlx)c@huZmqEjASBIl})+^L@EUST>R+}Cawi5X5S+oJX}vJ zr2OHfSF59Vqa6O&yw{Rccv!0B{P)GD4jyd14Yo`?yezwl`28y~+pQH&nr_2|EXhFj znZG&qwM1JAlv^Yn`{7kLM}a7wo^O8_Sjrw#Ai+-t!h9;p-`tYzjVq_2>^Yo1xov%z z0&OPj)C(K67EX)^yW zLq>z@X|YuK71`X4MgzV{pM3_3H4}{v$B<_x(T18D1a`LTS+bZ&@OWHG*gz5nAaa!kkuBmAa7#KPNYXqf#lEMWaxrLM`s)1qliyHSI2 zIV04aKlAke_?OF%LMO5qqq~BSw)A1GJ#f(+X(GkbE+i6}bb?#HnbniQ$BH^o=T|$b zE_wzIuHoN7$pbp`F+Gov#EabrA&i9JuCoo)GPTT z9o3UNTOSHN{ynELWA7sjvSKI~^hHw$vb3gw2xtRwOIFR1H`jgCuh))Vi0$A*y4XmYt@Y;0VCK0o{O=a1e}_zAXWuE8-e|DlDKA}v!XffTzZGXWuZZ=n@u zF`6mp_C;*(Sx0T7(>R-i=VHQuz2dCDZ+d2CL-3&!2leZJi&yG?}} z;O+f>zTnjp{^OTVSAlN1?6XV(yTgs~(=a(P9hASB0Mks3#SoH_|BHXPp`D%GUU&FK zH_lJ$ZoJ)Mi{OgAr~v32yVpm&PWe>3#jJnQfW6(-`B59z)5utL%Co!fk&qB@V6>z} zaGfdZADw|XQx*M>9_-NrUBiD#el~26YI$2<} z+PW)uWyLNeB*cUd>2rwMK*Z65LEj2wL%X+6E&{XC(}VsT9?k)0B$SnCCZNWbp)|Y6 z{DFdk;$yzR?(fEofjS+;+)k`trh;dkAF6wWeGJ4+Fnf&CZa-qMn3)fs@_^NK`0Sqh5z{8`CTh&e~Q%+cO{ zb&lCOsOT-R7*#3C#$E~3&ukM`zq4y|bKV)yc@+%H%%EbIIQXJWqYr~2AexqK-mhk6 zOmcH`hbJe8_5vJO2J_THL#4gC&v;{EV#3ZwZ8i!*_lw@Yk3ByR0R&=JZ%>SYe{-%k zD+oSad7beb>bLRd*QnHC1*KO;R9>CDg^|(Uf?}u}1vz=pR?+JUG+Jra-@cwD8vNYc zTuYb*?&p_&eI4;_TP!Xvjx^fa@KYKFGL*!*`MuImz^WtND{PJZ`}gn3TBQePVc%zG z*L>Rl&1qf7Y;3HmGqsQXVAtoc(Be@gAMpFfPfN`#rxf^6hbBXg_stEo-0SY!Ef6DXt?a`Y%o(I9eJF$RK8iw%r3dSuy8Np&I1;9 z_O<$jEbj4qx+kR|)!rtwcMkXf3E^L)X z$RHW&sBtG0ZxC-bq7Nkv*H6xzENP~yVYQCouMYsee##s}pA8l*$0wB1}^1h1#4lM)f~ zA`CV81O-dGyYH1xY3l2L6`y?BZV5$3m$}GQO`neeD$nqi3^R1ghLMm)op#~Ey-<^z z7rmaX(pb+YG%^Puxx&eh(DDr6=*8IXGmv9H{CFwJ@nbIl&_OCBMXMCR1|&^W<{pi7 z;Qsrzw(PPp-c-!G_zC;q-y^qN3Agx{Qu#mJC89sQ6VUv&9I@wSb8%H)Uw?5K5&35K z_dRF+Sa*PB(h{igcGqdX7Ei3JE2YA$|5Pi`Aj-@ zmAOab+s)gJ6Q@XCo4QW^On?hc7EXR@2?Gvg}HGKLmhv6dq8AJFlkViHG}F(KjIFD zlpm>PDpW|Qz@Iwm>sNgheU&M(RlMZgyLT-+{19-_r+f3-yfI)>0OCw_v(NZ4(2#`! z6ysyEWYB9gI@3rL)7#tI6j?UqqNb+y!rlFSYbzjsvN$E87)5$Se-pXTCMp&3uWCVf z?i39oU@|E5T~kv!5=jn+m9@9$nA(IhzV1_Wae3HLYxCGNJX{v&pk9as1Nqnad9BDu z`5f)rRnuD5*7u^p*^qLBhZhinMia-!WV9g7DKSL`RW>#@$-sRM44l4C7|=xCQ{Q+z z9`BaG%FfQiLP?)EAWV5npW&;2K7fzQ2>ETl+;86uMn*^V8Hiz-n9)&(zIdwij0_Pe zsS$vtz#jTH4|RAx2~(0&P*he_0Ag`qiLZRAGsVKep+ZXpeEM&Dgaiha3}|#J1`eq` zzF+sLTJd@6`^zA1MbVU>f48S7X{hjLoj8f9x9Ev(>1luLo1Y~mB>=<-RQ21|-bz56xDQ4v=v26A?G z#=y*60wos|6r`l0vTHUcNKh-;>X2*Z3Z-%?Q)C@p00h7(WYbbDnXe@xCo%Z*C%CV# z&&b%+(9rM}Jlu!PH`+EJ1eihD&5fU!nAm-}JAY(E3l5XuX$6z6pNF;U0i^(_@byLd z#)gw_p`4D)y$6pTCHYBtm`B^OK#@wCnq=bQ;*$Bg(VxJ8N}PC8A($rg{{H^_;pXH` z=I@*h@$G{UA{l*i^PMUGd^`rsj8NHX4@$X=Cfx7X6$0qN!p6p(uZ#LjLP$V;SE~Q? zR5D5K9I7XK#L!Xe&}6sGaevM`s>g&H+#+6rA zA_fQ50L%k&8qN`8WMo_nTn-07_?A;%*TRBb0`;HeGFZKLAdz`{cel#J)wRQs5CX>S zcj6-8bKgoc&f)74es>9mEBLIetbi7Gb#(=50z`z6(9oyeKSAYsg@c=i_!0iP4;~8= z=jZ3o83rKWlN=r%^;n4JaKjrYGND)lP$lY#7jKG&_w{9P#35=~SW znQAT$YTtvOikA^Ru;2~!7zaUDSJ&Uae>vP-Tq=9+5j=K?B6v}6<}OT+_~|X#F!8u# z>&1%~@9OH3)C2d|YdiN>2bgX8aVL!qL(~xmF`UoM%rty`A4{M-atD1s=hC3w5Vr^9 zKs0&DqQM92X?hKIeYcPIvCn{B0^koWEiLs64D`J`eaW4Yu&rb$NcOI#=D!zWBL@cu zv`=0XO+pk%L;>%tk(HHAlFpylJ;nn~eEQYG3*UQ-=DD%)z~f1ozFMo!WXHL&Ig=_z z)Of&uExz?&)5A^DVKVt64%@x?W)As?ywp^BBjaDQJ=fx|6u@q^hD0sqH5=jacES=8 zYBKkdA_2Q#ZvVtJqRmVTa#L4VUtIBqdL}t%4A`eQacE}?Q^q7RKl+2mr2SrOPs`vp z<<_LnElbgWXj1@e+&Edxb~*1A|5dZFCqa>GD?EIG>zO<__>i+{Y%Jco(*9X%N(=}f z?LNOozL0Q+9ZUkJ{GUSdu2FQON4$!{&hOup$45s;nd#|9FvbB{aim_Y1p7GgtB&~F z^avtAI_c@@+3IGwWjUW7J_wrlsH!g}p52J7gcX1Iko@P54@9#V;MDxQxskDW-5nwV zohiT-cwsr(@jxJ(pP!ctzSwI$p`xS|BMgV@)jE%38|iEd=5@8T``Ovq7d~3o^=A&so`5&fqr`k(0>E< zBpQkYRE_miXHXTg=^g`S9q;OMS2i}jO+v!T%4%n8`}6ao2z+8V7K?R$m3PpW=41JU z)VhwVSTB{fkON^W%;M_e(w>}^Wop|vmkF4#eKYqndgsZ4ar);5(NI#B=w6_-ENmPA zU+o&_Jhc>bJApF<(poMGHGb9qeJuH+00ZB`;mSKZrLl{PVFz1ZOXM{n^vqGz@0dwb zWYIl#de>@qxnipM-w$tmG2>0sY-(;MPOUWhv4>ye8f_TxY&~3Pbgrzebpkk54B;Xp zjE2x94Q%c25cWQq8|{SY!daKrHM^z_VfoC6%W_D_h6Jq!kG$)+QM=znB;06}O=jRQpYY-P_H zb=1w%Dy^uhdaM`eL|(`P=AgbqO0>1J!*;b*lC*@bs=COqV~Kc|_B%Auu3@I5!fGW4 zvSsOykWBu)z0ImoYLEpuV3m=MZ#&kLLA9MvLFkvx%r#-M&GRUM(bzsapd>&hEbE!% z07}GfTDyLHdy|?&@_>_vho>-g`uu)_zTBV;jrxX?0(Cpc#avmE<82c#=Jo>0(P&1K zL{;y0>~L{{a1PEotkWvC1TGb2|5H^7^Idy&Rll-gA!yu7@?l0V~4)b;YEj+TMK z^cWd8=U2~MM2R%)KgBUaHy!W(ZE2AJ`t$NN4d?aA($bRNa9OLTZG!tdi-Y>J zFtTATXR3Nk3gzvXl2;1v`kWdW@6#jbQParT-EsPN&>)$6GAOh;!!4K(c)hP17Ac zBPMRk(_Iu|CXR~(ECa}y_mY5M3*?ZcC2QcEUA(+{e1JaYJioe5v()jr8sqB1@-f9k zarod!H|&*t08ePEtE(GzZ5H}Zg*!Od`cdh#%xosw!~qQmeXJ6fxw*MyYr8vP5v06hf02n>~39m(Jy7f5z%l8}&in=j?RqrN#= zc;hYFlxU`;7FNpv#pSy&Grf80KGGp6o_vB8G?ue3s vaFb+^bh3ITDGf|f8D7;MoY6ora6@RO|K{RBzH15a|0GCLRad1}$u|0b%O5%o literal 0 HcmV?d00001 diff --git a/kmines/data/kmines.desktop b/kmines/data/kmines.desktop new file mode 100644 index 00000000..a74b3333 --- /dev/null +++ b/kmines/data/kmines.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Name=KMines +Name[af]=Kmyne +Name[ar]=لعبة الألغام (KMines) +Name[be]=Сапёр +Name[bn]=কে-মাইনà§à¦¸ +Name[hi]=के-माइनà¥à¤¸ +Name[hr]=KMine +Name[ne]=केडीई बारूद +Name[pa]=ਕੇ-ਸਰà©à©°à¨— +Name[pl]=Miny +Name[pt_BR]=KMinas +Name[sv]=Kmines +Name[ta]=கேகனà¯à®©à®¿à®µà¯†à®Ÿà®¿à®•à®³à¯ +Name[tg]=KСапёр +Name[th]=à¸à¸¹à¹‰à¸£à¸°à¹€à¸šà¸´à¸” - K +Name[wa]=KMenes +Name[zh_TW]=KMines 踩地雷 +Icon=kmines +Exec=kmines -caption "%c" %i %m +Type=Application +DocPath=kmines/index.html +GenericName=Minesweeper-like Game +GenericName[be]=Ð“ÑƒÐ»ÑŒÐ½Ñ Ñž Ñапёра +GenericName[bg]=Мини +GenericName[bn]=মাইনসà§à¦‡à¦ªà¦¾à¦°-জাতীয় খেলা +GenericName[br]=C'hoari doare Minesweeper +GenericName[bs]=Igra minskog polja +GenericName[ca]=Joc a l'estil del buscamines +GenericName[cs]=Hra s minovým polem +GenericName[cy]=Gêm tebyg i Minesweeper +GenericName[da]=Minesøger-lignende spil +GenericName[de]=Minesweeper-ähnliches Spiel +GenericName[el]=Παιχνίδι παÏόμοιο με το ναÏκαλιευτή +GenericName[eo]="Minesweeper"-simila ludo +GenericName[es]=Juego similar al buscaminas +GenericName[et]=Miiniväljamäng +GenericName[eu]=Mina bilatzailearen antzeko jokoa +GenericName[fa]=بازی Minesweeper-like +GenericName[fi]=Kaivostyylinen peli +GenericName[fr]=Jeu dans le style du démineur +GenericName[he]=חיקוי שולה ×ž×•×§×©×™× +GenericName[hr]=Igra s poput Minesweepera +GenericName[hu]=AknakeresÅ‘ +GenericName[is]=Leikur sem líkist Minesweeper +GenericName[it]=Gioco simile a Mine +GenericName[ja]=地雷ゲーム +GenericName[km]=ល្បែង​ដូច Minesweeper +GenericName[ko]=지뢰찾기 게임 +GenericName[lt]=Minesweeper primenantis žaidimas +GenericName[lv]=Minesweeper lÄ«dzÄ«ga spÄ“le +GenericName[mk]=Игра Ñлична на Minesweeper +GenericName[nb]=Minesveiper-lignende spill +GenericName[nds]=Minesweeper-liek Speel +GenericName[ne]=बारूद हटाउने जसà¥à¤¤à¥ˆ खेल +GenericName[nl]=Mijnenveger-achtig spel +GenericName[nn]=Minesveipar-liknande spel +GenericName[pa]=ਸà©à¨°à©°à¨— ਹਟਾਓਣ ਵਰਗੀ ਖੇਡ +GenericName[pl]=Gra typu saper +GenericName[pt]=Jogo tipo Minas +GenericName[pt_BR]=Jogo parecido com Campo Minado +GenericName[ru]=Сапёр +GenericName[sk]=Hra typu Minesweeper +GenericName[sl]=Igra, podobna Minesweeperju +GenericName[sr]=Игра налик на миноловац +GenericName[sr@Latn]=Igra nalik na minolovac +GenericName[sv]=Minröjarliknande spel +GenericName[ta]=சà¯à®°à®™à¯à®•à®®à¯à®µà¯†à®Ÿà¯à®Ÿà¯à®®à¯ விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра в Ñапера +GenericName[wa]=On djeu di tchamp d' menes +GenericName[zh_CN]=扫雷 +GenericName[zh_TW]=類似踩地雷的éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/kmines/defines.cpp b/kmines/defines.cpp new file mode 100644 index 00000000..b008de00 --- /dev/null +++ b/kmines/defines.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "defines.h" + +#include + +const char *Level::LABELS[NB_TYPES+1] = { + I18N_NOOP("Easy"), I18N_NOOP("Normal"), I18N_NOOP("Expert"), + I18N_NOOP("Custom") +}; + +const Level::Data Level::DATA[NB_TYPES] = { + { 8, 8, 10, "easy", "8x8x10", }, + {16, 16, 40, "normal", "16x16x40", }, + {30, 16, 99, "expert", "30x16x99", } +}; + +Level::Level(Type type) +{ + Q_ASSERT( type!=Custom ); + _width = DATA[type].width; + _height = DATA[type].height; + _nbMines = DATA[type].nbMines; +} + +Level::Level(uint width, uint height, uint nbMines) + : _width(width), _height(height), _nbMines(nbMines) +{ + Q_ASSERT( width>=2 && height>=2 ); + if (_nbMines > maxNbMines(width, height) ) + _nbMines = maxNbMines(width, height); +} + +Level::Type Level::type() const +{ + for (uint i=0; i + + +class Level +{ + public: + enum Type { Easy = 0, Normal, Expert, NB_TYPES, Custom = NB_TYPES }; + static const char *LABELS[NB_TYPES+1]; + struct Data { + uint width, height, nbMines; + const char *label, *wwLabel; + }; + static const Data DATA[NB_TYPES]; + + Level(Type); + Level(uint width, uint height, uint nbMines); + + uint width() const { return _width; } + uint height() const { return _height; } + uint nbMines() const { return _nbMines; } + Type type() const; + static uint maxNbMines(uint width, uint height) { return width*height - 2;} + + bool operator ==(const Level &level) const { + return ( _width==level._width && _height==level._height && + _nbMines==level._nbMines ); + } + + private: + uint _width, _height, _nbMines; +}; + +class KMines +{ + public: + enum GameState { Playing = 0, Paused, GameOver, Stopped, Replaying, + Init, NB_STATES }; + static const char *STATES[NB_STATES]; + enum SolvingState { Regular, Advised, Solved }; + + enum CaseState { Covered, Uncovered, Uncertain, Marked, Exploded, Error }; + struct Case { + bool mine; + CaseState state; + }; + + enum NumberColor { NB_N_COLORS = 8 }; + enum Mood { Normal = 0, Stressed, Happy, Sad, Sleeping, NbMoods }; +}; + +#endif diff --git a/kmines/dialogs.cpp b/kmines/dialogs.cpp new file mode 100644 index 00000000..d02b2eea --- /dev/null +++ b/kmines/dialogs.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "dialogs.h" +#include "dialogs.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" + +#include "bitmaps/smile" +#include "bitmaps/smile_happy" +#include "bitmaps/smile_ohno" +#include "bitmaps/smile_stress" +#include "bitmaps/smile_sleep" + + +//----------------------------------------------------------------------------- +const char **Smiley::XPM_NAMES[NbMoods] = { + smile_xpm, smile_stress_xpm, smile_happy_xpm, smile_ohno_xpm, + smile_sleep_xpm +}; + +void Smiley::setMood(Mood mood) +{ + QPixmap p(XPM_NAMES[mood]); + setPixmap(p); +} + +//----------------------------------------------------------------------------- +DigitalClock::DigitalClock(QWidget *parent) +: KGameLCDClock(parent, "digital_clock") +{ + setFrameStyle(Panel | Sunken); + setDefaultBackgroundColor(black); + setDefaultColor(white); +} + +KExtHighscore::Score DigitalClock::score() const +{ + KExtHighscore::Score score(KExtHighscore::Won); + score.setScore(3600 - seconds()); + score.setData("nb_actions", _nbActions); + return score; +} + +void DigitalClock::timeoutClock() +{ + KGameLCDClock::timeoutClock(); + + if ( _cheating || _customGame ) setColor(white); + else if ( _firstsetLabel(i18n("Width:")); + _width->setRange(minWidth, maxWidth); + connect(_width, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_width); + + _height = new KIntNumInput(this, "kcfg_CustomHeight"); + _height->setLabel(i18n("Height:")); + _height->setRange(minWidth, maxWidth); + connect(_height, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_height); + + _mines = new KIntNumInput(this, "kcfg_CustomMines"); + _mines->setLabel(i18n("No. of mines:")); + _mines->setRange(1, Level::maxNbMines(maxWidth, maxHeight)); + connect(_mines, SIGNAL(valueChanged(int)), SLOT(updateNbMines())); + top->addWidget(_mines); + + top->addSpacing(2 * KDialog::spacingHint()); + + // combo to choose level + QHBoxLayout *hbox = new QHBoxLayout(top); + QLabel *label = new QLabel(i18n("Choose level:"), this); + hbox->addWidget(label); + _gameType = new KComboBox(false, this); + connect(_gameType, SIGNAL(activated(int)), SLOT(typeChosen(int))); + for (uint i=0; i<=Level::NB_TYPES; i++) + _gameType->insertItem(i18n(Level::LABELS[i])); + hbox->addWidget(_gameType); + hbox->addWidget(new QWidget(this), 1); + + top->addStretch(1); +} + +void CustomConfig::updateNbMines() +{ + if (_block) return; + _block = true; + Level l(_width->value(), _height->value(), _mines->value()); + _mines->setRange(1, Level::maxNbMines(l.width(), l.height())); + _mines->setLabel(i18n("Mines (%1%):") + .arg( (100*l.nbMines()) / (l.width() * l.height()) )); + _gameType->setCurrentItem(l.type()); + _block = false; +} + +void CustomConfig::typeChosen(int i) +{ + if (_block) return; + _block = true; + Level::Type type = (Level::Type)i; + if ( type==Level::Custom ) { + Level level = Settings::customLevel(); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } else { + Level level(type); + _width->setValue(level.width()); + _height->setValue(level.height()); + _mines->setRange(1, Level::maxNbMines(level.width(), level.height())); + _mines->setValue(level.nbMines()); + } + _block = false; + updateNbMines(); +} + +//----------------------------------------------------------------------------- +static const char *MOUSE_BUTTON_LABELS[Settings::EnumButton::COUNT] = { + I18N_NOOP("Left button:"), I18N_NOOP("Middle button:"), + I18N_NOOP("Right button:") +}; + +static const char *MOUSE_CONFIG_NAMES[Settings::EnumButton::COUNT] = { + "kcfg_leftMouseAction", "kcfg_midMouseAction", + "kcfg_rightMouseAction" +}; + +static const char *MOUSE_ACTION_LABELS[Settings::EnumMouseAction::COUNT-1] = { + I18N_NOOP("Reveal"), I18N_NOOP("Autoreveal"), + I18N_NOOP("Toggle Flag"), I18N_NOOP("Toggle ? Flag") +}; + +GameConfig::GameConfig() + : QWidget(0, "game_config_widget"), _magicDialogEnabled(false) +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QCheckBox *cb = new QCheckBox(i18n("Enable ? mark"), this, "kcfg_UncertainMark"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Enable keyboard"), this, "kcfg_KeyboardGame"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("Pause if window loses focus"), this, "kcfg_PauseFocus"); + top->addWidget(cb); + + cb = new QCheckBox(i18n("\"Magic\" reveal"), this, "kcfg_MagicReveal"); + QWhatsThis::add(cb, i18n("Set flags and reveal squares where they are trivial.")); + connect(cb, SIGNAL(toggled(bool)), SLOT(magicModified(bool))); + top->addWidget(cb); + + top->addSpacing(2 * KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QVGroupBox *gb = new QVGroupBox(i18n("Mouse Bindings"), this); + hbox->addWidget(gb); + QGrid *grid = new QGrid(2, gb); + grid->setSpacing(KDialog::spacingHint()); + for (uint i=0; i< Settings::EnumButton::COUNT; i++) { + (void)new QLabel(i18n(MOUSE_BUTTON_LABELS[i]), grid); + QComboBox *cb = new QComboBox(false, grid, MOUSE_CONFIG_NAMES[i]); + for (uint k=0; k< (Settings::EnumMouseAction::COUNT-1); k++) + cb->insertItem(i18n(MOUSE_ACTION_LABELS[k])); + cb->setCurrentItem(i); + } + hbox->addStretch(1); + + top->addStretch(1); +} + +void GameConfig::magicModified(bool on) +{ + if ( !_magicDialogEnabled || !on ) return; + KMessageBox::information(this, i18n("When the \"magic\" reveal is on, you lose the ability to enter the highscores."), QString::null, "magic_reveal_warning"); +} + +//----------------------------------------------------------------------------- +static const char *COLOR_LABELS[Settings::EnumType::COUNT] = { + I18N_NOOP("Flag color:"), I18N_NOOP("Explosion color:"), + I18N_NOOP("Error color:") +}; + +static const char *COLOR_CONFIG_NAMES[Settings::EnumType::COUNT] = { + "kcfg_flagColor", "kcfg_explosionColor", "kcfg_errorColor" +}; + +static const char *N_COLOR_CONFIG_NAMES[KMines::NB_N_COLORS] = { + "kcfg_MineColor0", "kcfg_MineColor1", "kcfg_MineColor2", + "kcfg_MineColor3", "kcfg_MineColor4", "kcfg_MineColor5", + "kcfg_MineColor6", "kcfg_MineColor7" +}; + +AppearanceConfig::AppearanceConfig() + : QWidget(0, "appearance_config_widget") +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialog::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QGrid *grid = new QGrid(2, this); + grid->setSpacing(KDialog::spacingHint()); + hbox->addWidget(grid); + for (uint i=0; isetFixedWidth(100); + } + for (uint i=0; isetFixedWidth(100); + } + hbox->addStretch(1); + + top->addStretch(1); +} + diff --git a/kmines/dialogs.h b/kmines/dialogs.h new file mode 100644 index 00000000..12dde5ac --- /dev/null +++ b/kmines/dialogs.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 1996-2003 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef DIALOGS_H +#define DIALOGS_H + +#include + +#include +#include + +#include "defines.h" +#include "settings.h" + +class KComboBox; +class KIntNumInput; + +//----------------------------------------------------------------------------- +class Smiley : public QPushButton, public KMines +{ + Q_OBJECT + public: + Smiley(QWidget *parent, const char *name = 0) + : QPushButton(QString::null, parent, name) {} + + public slots: + void setMood(Mood); + + private: + static const char **XPM_NAMES[NbMoods]; +}; + +//----------------------------------------------------------------------------- +class DigitalClock : public KGameLCDClock +{ + Q_OBJECT + public: + DigitalClock(QWidget *parent); + + void reset(bool customGame); + + bool cheating() const { return _cheating; } + uint nbActions() const { return _nbActions; } + KExtHighscore::Score score() const; + + public slots: + void start(); + void setCheating(); + void addAction() { _nbActions++; } + + private slots: + void timeoutClock(); + + private: + KExtHighscore::Score _first, _last; + uint _nbActions; + bool _cheating, _customGame; +}; + +//----------------------------------------------------------------------------- +class CustomConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + CustomConfig(); + + static const uint maxWidth; + static const uint minWidth; + static const uint maxHeight; + static const uint minHeight; + + void init() { updateNbMines(); } + + private slots: + void typeChosen(int); + void updateNbMines(); + + private: + bool _block; + KIntNumInput *_width, *_height, *_mines; + KComboBox *_gameType; +}; + +//----------------------------------------------------------------------------- +class GameConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + GameConfig(); + + static void saveLevel(Level::Type); + + void init() { _magicDialogEnabled = true; } + + private slots: + void magicModified(bool); + + private: + bool _magicDialogEnabled; + +}; + +class AppearanceConfig : public QWidget, public KMines +{ + Q_OBJECT + public: + AppearanceConfig(); +}; + +#endif diff --git a/kmines/field.cpp b/kmines/field.cpp new file mode 100644 index 00000000..1f3b3dfd --- /dev/null +++ b/kmines/field.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "field.h" +#include "field.moc" + +#include + +#include +#include +#include + +#include +#include + +#include "settings.h" +#include "solver/solver.h" +#include "dialogs.h" + + +using namespace KGrid2D; + +const Field::ActionData Field::ACTION_DATA[Nb_Actions] = { + { "Reveal", "reveal", I18N_NOOP("Case revealed") }, + { "AutoReveal", "autoreveal", I18N_NOOP("Case autorevealed") }, + { "SetFlag", "mark", I18N_NOOP("Flag set") }, + { "UnsetFlag", "unmark", I18N_NOOP("Flag unset") }, + { "SetUncertain", "set_uncertain", I18N_NOOP("Question mark set") }, + { "UnsetUncertain", "unset_uncertain", I18N_NOOP("Question mark unset") } +}; + +Field::Field(QWidget *parent) + : FieldFrame(parent), _state(Init), _solvingState(Regular), _level(Level::Easy) +{} + +void Field::readSettings() +{ + if ( inside(_cursor) ) { + QPainter p(this); + drawCase(p, _cursor); + } + if ( Settings::magicReveal() ) emit setCheating(); +} + +QSize Field::sizeHint() const +{ + return QSize(2*frameWidth() + _level.width()*Settings::caseSize(), + 2*frameWidth() + _level.height()*Settings::caseSize()); +} + +void Field::setLevel(const Level &level) +{ + _level = level; + reset(false); + adjustSize(); +} + +void Field::setReplayField(const QString &field) +{ + setState(Replaying); + initReplay(field); +} + +void Field::setState(GameState state) +{ + Q_ASSERT( state!=GameOver ); + emit gameStateChanged(state); + _state = state; +} + +void Field::reset(bool init) +{ + BaseField::reset(_level.width(), _level.height(), _level.nbMines()); + if ( init || _state==Init ) setState(Init); + else setState(Stopped); + if (Settings::magicReveal()) emit setCheating(); + _currentAction = Settings::EnumMouseAction::None; + _reveal = false; + _cursor.first = _level.width()/2; + _cursor.second = _level.height()/2; + _advisedCoord = Coord(-1, -1); + update(); +} + +void Field::paintEvent(QPaintEvent *e) +{ + QPainter painter(this); + drawFrame(&painter); + if ( _state==Paused ) return; + + Coord min = fromPoint(e->rect().topLeft()); + bound(min); + Coord max = fromPoint(e->rect().bottomRight()); + bound(max); + for (short i=min.first; i<=max.first; i++) + for (short j=min.second; j<=max.second; j++) + drawCase(painter, Coord(i,j)); +} + +void Field::changeCase(const Coord &p, CaseState newState) +{ + BaseField::changeCase(p, newState); + QPainter painter(this); + drawCase(painter, p); + if ( isActive() ) emit updateStatus( hasMine(p) ); +} + +QPoint Field::toPoint(const Coord &p) const +{ + QPoint qp; + qp.setX( p.first*Settings::caseSize() + frameWidth() ); + qp.setY( p.second*Settings::caseSize() + frameWidth() ); + return qp; +} + +Coord Field::fromPoint(const QPoint &qp) const +{ + double i = (double)(qp.x() - frameWidth()) / Settings::caseSize(); + double j = (double)(qp.y() - frameWidth()) / Settings::caseSize(); + return Coord((int)floor(i), (int)floor(j)); +} + +int Field::mapMouseButton(QMouseEvent *e) const +{ + switch (e->button()) { + case Qt::LeftButton: return Settings::mouseAction(Settings::EnumButton::left); + case Qt::MidButton: return Settings::mouseAction(Settings::EnumButton::mid); + case Qt::RightButton: return Settings::mouseAction(Settings::EnumButton::right); + default: return Settings::EnumMouseAction::ToggleFlag; + } +} + +void Field::revealActions(bool press) +{ + if ( _reveal==press ) return; // avoid flicker + _reveal = press; + + switch (_currentAction) { + case Reveal: + pressCase(_cursor, press); + break; + case AutoReveal: + pressClearFunction(_cursor, press); + break; + default: + break; + } +} + +void Field::mousePressEvent(QMouseEvent *e) +{ + if ( !isActive() || (_currentAction!=Settings::EnumMouseAction::None) ) return; + + emit setMood(Stressed); + _currentAction = mapMouseButton(e); + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::mouseReleaseEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + int tmp = _currentAction; + emit setMood(Normal); + revealActions(false); + int ma = mapMouseButton(e); + _currentAction = Settings::EnumMouseAction::None; + if ( ma!=tmp ) return; + + Coord p = fromPoint(e->pos()); + if ( !inside(p) ) return; + placeCursor(p); + + switch (ma) { + case Settings::EnumMouseAction::ToggleFlag: doMark(p); break; + case Settings::EnumMouseAction::ToggleUncertainFlag: doUmark(p); break; + case Settings::EnumMouseAction::Reveal: doReveal(p); break; + case Settings::EnumMouseAction::AutoReveal: doAutoReveal(p); break; + default: break; + } +} + +void Field::mouseMoveEvent(QMouseEvent *e) +{ + if ( !isActive() ) return; + + Coord p = fromPoint(e->pos()); + if ( p==_cursor ) return; // avoid flicker + + revealActions(false); + if ( !inside(p) ) return; + placeCursor(p); + revealActions(true); +} + +void Field::pressCase(const Coord &c, bool pressed) +{ + if ( state(c)==Covered ) { + QPainter painter(this); + drawCase(painter, c, pressed); + } +} + +void Field::pressClearFunction(const Coord &p, bool pressed) +{ + pressCase(p, pressed); + CoordList n = coveredNeighbours(p); + QPainter painter(this); + for (CoordList::const_iterator it=n.begin(); it!=n.end(); ++it) + drawCase(painter, *it, pressed); +} + +void Field::keyboardAutoReveal() +{ + _cursor_back = _cursor; + pressClearFunction(_cursor_back, true); + QTimer::singleShot(50, this, SLOT(keyboardAutoRevealSlot())); +} + +void Field::keyboardAutoRevealSlot() +{ + pressClearFunction(_cursor_back, false); + doAutoReveal(_cursor_back); +} + +void Field::doAutoReveal(const Coord &c) +{ + if ( !isActive() ) return; + if ( state(c)!=Uncovered ) return; + emit addAction(c, AutoReveal); + resetAdvised(); + doAction(AutoReveal, c, Settings::magicReveal()); +} + +void Field::pause() +{ + switch (_state) { + case Paused: setState(Playing); break; + case Playing: setState(Paused); break; + default: return; + } + update(); +} + +void Field::moveCursor(Neighbour n) +{ + Coord c = neighbour(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +void Field::moveToEdge(Neighbour n) +{ + Coord c = toEdge(_cursor, n); + if ( inside(c) ) placeCursor(c); +} + +bool Field::doReveal(const Coord &c, CoordList *autorevealed, + bool *caseUncovered) +{ + if ( !isActive() ) return true; + if ( state(c)!=Covered ) return true; + if ( firstReveal() ) setState(Playing); + CaseState state = + doAction(Reveal, c, Settings::magicReveal(), autorevealed, caseUncovered); + emit addAction(c, Reveal); + return ( state!=Error ); +} + +void Field::doMark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: action = SetFlag; break; + case Marked: action = (Settings::uncertainMark() ? SetUncertain : UnsetFlag); break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + +void Field::doUmark(const Coord &c) +{ + if ( !isActive() ) return; + ActionType action; + CaseState oldState = state(c); + switch (oldState) { + case Covered: + case Marked: action = SetUncertain; break; + case Uncertain: action = UnsetUncertain; break; + default: return; + } + CaseState newState = doAction(action, c, Settings::magicReveal()); + addMarkAction(c, newState, oldState); +} + + +KMines::CaseState Field::doAction(ActionType type, const Coord &c, + bool complete, CoordList *autorevealed, + bool *caseUncovered) +{ + resetAdvised(); + CaseState state = Error; + if ( _solvingState==Solved ) complete = false; + + KNotifyClient::event(winId(), ACTION_DATA[type].event, + i18n(ACTION_DATA[type].eventMessage)); + switch (type) { + case Reveal: + if ( !reveal(c, autorevealed, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case AutoReveal: + if ( !autoReveal(c, caseUncovered) ) + emit gameStateChanged(GameOver); + else { + state = Uncovered; + if (complete) completeReveal(); + } + break; + case SetFlag: + state = Marked; + if (complete) completeReveal(); + break; + case UnsetFlag: + case UnsetUncertain: + state = Covered; + break; + case SetUncertain: + state = Uncertain; + break; + case Nb_Actions: + Q_ASSERT(false); + break; + } + + if ( state!=Error ) changeCase(c, state); + return state; +} + +void Field::addMarkAction(const Coord &c, CaseState newS, CaseState oldS) +{ + switch (newS) { + case Marked: emit addAction(c, SetFlag); return; + case Uncertain: emit addAction(c, SetUncertain); return; + default: break; + } + switch (oldS) { + case Marked: emit addAction(c, UnsetFlag); return; + case Uncertain: emit addAction(c, UnsetUncertain); return; + default: break; + } +} + +void Field::placeCursor(const Coord &p) +{ + if ( !isActive() ) return; + + Q_ASSERT( inside(p) ); + Coord old = _cursor; + _cursor = p; + if ( Settings::keyboardGame() ) { + QPainter painter(this); + drawCase(painter, old); + drawCase(painter, _cursor); + } +} + +void Field::resetAdvised() +{ + if ( !inside(_advisedCoord) ) return; + QPainter p(this); + Coord tmp = _advisedCoord; + _advisedCoord = Coord(-1, -1); + drawCase(p, tmp); +} + +void Field::setAdvised(const Coord &c, double proba) +{ + resetAdvised(); + _solvingState = Advised; + _advisedCoord = c; + _advisedProba = proba; + if ( inside(c) ) { + QPainter p(this); + drawCase(p, c); + } +} + +void Field::drawCase(QPainter &painter, const Coord &c, bool pressed) const +{ + Q_ASSERT( inside(c) ); + + QString text; + uint nbMines = 0; + PixmapType type = NoPixmap; + + switch ( state(c) ) { + case Covered: + break; + case Marked: + type = FlagPixmap; + pressed = false; + break; + case Error: + type = ErrorPixmap; + pressed = true; + break; + case Uncertain: + text = '?'; + pressed = false; + break; + case Exploded: + type = ExplodedPixmap; + pressed = true; + break; + case Uncovered: + pressed = true; + if ( hasMine(c) ) type = MinePixmap; + else { + nbMines = nbMinesAround(c); + if (nbMines) text.setNum(nbMines); + } + } + + int i = -1; + if ( c==_advisedCoord ) { + if ( _advisedProba==1 ) i = 0; + else if ( _advisedProba>0.75 ) i = 1; + else if ( _advisedProba>0.5 ) i = 2; + else if ( _advisedProba>0.25 ) i = 3; + else i = 4; + } + + bool hasFocus = ( Settings::keyboardGame() && (c==_cursor) ); + drawBox(painter, toPoint(c), pressed, type, text, nbMines, i, hasFocus); +} diff --git a/kmines/field.h b/kmines/field.h new file mode 100644 index 00000000..32b959fe --- /dev/null +++ b/kmines/field.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1996-2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef FIELD_H +#define FIELD_H + +#include "solver/bfield.h" +#include "frame.h" + + +//----------------------------------------------------------------------------- +class Field : public FieldFrame, public BaseField +{ + Q_OBJECT + public: + enum ActionType { Reveal = 0, AutoReveal, SetFlag, UnsetFlag, SetUncertain, + UnsetUncertain, Nb_Actions }; + struct ActionData { + const char *name, *event, *eventMessage; + }; + static const ActionData ACTION_DATA[Nb_Actions]; + + public: + Field(QWidget *parent); + + virtual QSize sizeHint() const; + + void setLevel(const Level &level); + void setReplayField(const QString &field); + const Level &level() const { return _level; } + void reset(bool init); + + GameState gameState() const { return _state; } + bool isActive() const { return _state!=Paused && _state!=GameOver; } + void pause(); + void setGameOver() { _state = GameOver; } + + void moveCursor(Neighbour); + void moveToEdge(Neighbour); + void doReveal() { doReveal(_cursor); } + void doMark() { doMark(_cursor); } + void doUmark() { doUmark(_cursor); } + void keyboardAutoReveal(); + CaseState doAction(ActionType type, const KGrid2D::Coord &c, + bool completeReveal, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + + void readSettings(); + + void setAdvised(const KGrid2D::Coord &c, double proba); + void setSolvingState(SolvingState state) { _solvingState = state; } + SolvingState solvingState() const { return _solvingState; } + + signals: + void updateStatus(bool); + void gameStateChanged(GameState); + void setMood(Mood); + void setCheating(); + void addAction(const KGrid2D::Coord &, Field::ActionType); + + protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + + private slots: + void keyboardAutoRevealSlot(); + + private: + GameState _state; + bool _reveal; + SolvingState _solvingState; + KGrid2D::Coord _cursor, _cursor_back, _advisedCoord; + double _advisedProba; + int _currentAction; + Level _level; + + void pressCase(const KGrid2D::Coord &, bool); + void pressClearFunction(const KGrid2D::Coord &, bool); + void placeCursor(const KGrid2D::Coord &); + void revealActions(bool press); + + void doAutoReveal(const KGrid2D::Coord &); + bool doReveal(const KGrid2D::Coord &, KGrid2D::CoordList *autorevealed = 0, + bool *caseUncovered = 0); + void doMark(const KGrid2D::Coord &); + void doUmark(const KGrid2D::Coord &); + void changeCase(const KGrid2D::Coord &, CaseState newState); + void addMarkAction(const KGrid2D::Coord &, CaseState newS, CaseState oldS); + + QPoint toPoint(const KGrid2D::Coord &) const; + KGrid2D::Coord fromPoint(const QPoint &) const; + + void drawCase(QPainter &, const KGrid2D::Coord &, + bool forcePressed = false) const; + + int mapMouseButton(QMouseEvent *) const; + void resetAdvised(); + void setState(GameState); +}; + +#endif // FIELD_H diff --git a/kmines/frame.cpp b/kmines/frame.cpp new file mode 100644 index 00000000..a502b87d --- /dev/null +++ b/kmines/frame.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "frame.h" + +#include +#include +#include +#include + +#include "settings.h" + + +FieldFrame::FieldFrame(QWidget *parent) + : QFrame(parent, "field"), _button(0) +{ + setFrameStyle( QFrame::Box | QFrame::Raised ); + setLineWidth(2); + setMidLineWidth(2); +} + +void FieldFrame::adjustSize() +{ + setFixedSize(sizeHint()); + _button.resize(Settings::caseSize(), Settings::caseSize()); + + QBitmap mask; + for (uint i=0; i +#include +#include + +#include "defines.h" + +class QPainter; + +class FieldFrame : public QFrame, public KMines +{ + public: + FieldFrame(QWidget *parent); + + protected: + enum PixmapType { FlagPixmap = 0, MinePixmap, ExplodedPixmap, + ErrorPixmap, Nb_Pixmap_Types, + NoPixmap = Nb_Pixmap_Types }; + enum { Nb_Advised = 5 }; + + void drawBox(QPainter &, const QPoint &, bool pressed, + PixmapType, const QString &text, + uint nbMines, int advised, bool hasFocus) const; + virtual void adjustSize(); + + private: + QPushButton _button; + QPixmap _pixmaps[Nb_Pixmap_Types]; + QPixmap _advised[Nb_Advised]; + + void drawPixmap(QPixmap &, PixmapType, bool mask) const; + void drawAdvised(QPixmap &, uint i, bool mask) const; + void initPixmap(QPixmap &, bool mask) const; +}; + +#endif diff --git a/kmines/highscores.cpp b/kmines/highscores.cpp new file mode 100644 index 00000000..c0e40c10 --- /dev/null +++ b/kmines/highscores.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002 Nicolas HADACEK (hadacek@kde.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "highscores.h" + +#include +#include +#include +#include + +#include "version.h" +#include "defines.h" + + +namespace KExtHighscore +{ + +ExtManager::ExtManager() + : Manager(Level::NB_TYPES) +{ + setScoreType(MinuteTime); + setWWHighscores(KURL( HOMEPAGE ), SHORT_VERSION); + setShowStatistics(true); + const uint RANGE[16] = { 1, 3120, 3180, 3240, 3300, 3360, 3420, 3480, + 3510, 3540, 3550, 3560, 3570, 3580, 3590, 3600 }; + QMemArray s; + s.duplicate(RANGE, 16); + setScoreHistogram(s, ScoreBound); + + Item *item = new Item((uint)0, i18n("Clicks"), Qt::AlignRight); + addScoreItem("nb_actions", item); +} + +QString ExtManager::gameTypeLabel(uint gameType, LabelType type) const +{ + const Level::Data &data = Level::DATA[gameType]; + switch (type) { + case Icon: + case Standard: return data.label; + case I18N: return i18n(Level::LABELS[gameType]); + case WW: return data.wwLabel; + } + return QString::null; +} + +void ExtManager::convertLegacy(uint gameType) +{ + QString group; + switch ((Level::Type)gameType) { + case Level::Easy: group = "Easy level"; break; + case Level::Normal: group = "Normal level"; break; + case Level::Expert: group = "Expert level"; break; + case Level::NB_TYPES: Q_ASSERT(false); + } + + KConfigGroupSaver cg(kapp->config(), group); + QString name = cg.config()->readEntry("Name", QString::null); + if ( name.isNull() ) return; + if ( name.isEmpty() ) name = i18n("anonymous"); + uint minutes = cg.config()->readUnsignedNumEntry("Min", 0); + uint seconds = cg.config()->readUnsignedNumEntry("Sec", 0); + int score = 3600 - (minutes*60 + seconds); + if ( score<=0 ) return; + Score s(Won); + s.setScore(score); + s.setData("name", name); + submitLegacyScore(s); +} + +bool ExtManager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + if ( s1.score()==s2.score() ) + // when time is same, favour more clicks (it means auto-reveal + // didn't help so much): + return s1.data("nb_actions").toUInt() +#include + +namespace KExtHighscore +{ + +class KDE_EXPORT ExtManager : public Manager +{ + public: + ExtManager(); + + private: + QString gameTypeLabel(uint gameTye, LabelType) const; + void convertLegacy(uint gameType); + bool isStrictlyLess(const Score &s1, const Score &s2) const; +}; + +} + +#endif diff --git a/kmines/kmines.kcfg b/kmines/kmines.kcfg new file mode 100644 index 00000000..382b6ccd --- /dev/null +++ b/kmines/kmines.kcfg @@ -0,0 +1,105 @@ + + + + + + + 4 + 100 + 20 + + + + 5 + 50 + 10 + + + + 5 + 50 + 10 + + + + 20 + + + + true + + + + false + + + + true + + + + false + + + + Easy + + + + + + + + + + + + + + + + + + + left + mid + right + + + + Reveal + AutoReveal + ToggleFlag + + + + + flag + explosion + error + + + + #ff0000 + + + + + #0000ff + #008800 + #888800 + #880088 + #ff0000 + #880000 + #000000 + #000000 + + + + true + + + diff --git a/kmines/kminesui.rc b/kmines/kminesui.rc new file mode 100644 index 00000000..cd1ef166 --- /dev/null +++ b/kmines/kminesui.rc @@ -0,0 +1,102 @@ + + + + + +

  • VI9tYZ)7rK*?~c5 zGzg?5B}^ivsRtod*J0ml;ROLj)A}m9hRe@%d3fTF>!re4l|z{fLI{-Bxa~F#&tuXbwZ>%*fF}&)%EuA%&klRPam+bxOl3j)m;s}Q5<)t;CElvcr@n`0p4_9}}WYtS(CFpI1)V}mk2My{V zDRd0iI%p6?(ZNgk2YBg_D4iCQs1Q1Ol09@Nf+7h_f&wuicir9Dnc11yd1t4SJ?H24 zQ<5YBV5?AQuYCHrst)+#-fdQDjh3Po>v83dAlmj0(3<$yyMM^X!llff{;!>)+ z3oc)zUa4ScDh(M^uKN^gHb;(&Q?Jvbi0@iwc5q>DFgtbNIHs?voH+c8rfJAHrWOQL z9ET&jN*Dxmwpq*K-NuIfEASdW_y3Ih*RJBon5ySdc0Ecpo2p~uI3C$_`t{>n?wN8d z{e1bw(zcWj149?83e7m7AtTa?2>V-GUoFhuz4u@GxR?2S_9%|$BZae}lKtu300000NkvXXu0mjfgE$|T literal 0 HcmV?d00001 diff --git a/kbounce/pics/hi128-app-kbounce.png b/kbounce/pics/hi128-app-kbounce.png new file mode 100644 index 0000000000000000000000000000000000000000..d9c49bbe593fcecf81bf645c1dbfa2f30d978e9a GIT binary patch literal 12438 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 literal 0 HcmV?d00001 diff --git a/kbounce/pics/hi16-app-kbounce.png b/kbounce/pics/hi16-app-kbounce.png new file mode 100644 index 0000000000000000000000000000000000000000..916fe18d855af8758f0143f96fe518d6094474a8 GIT binary patch literal 822 zcmV-61Ihe}P)LRZQNE*x+r zv$9Kf8r->b;Xk@@VL=duA|lMdh%z!#n`kN2796KSr&66xY%)!ov`yROz2x2dj*I?T z6#T*2ozLQM&QY98KA#^RA0NLmHa7O+(9qCGp-{L~tyWj(=H`B0TwJ_Wuh-Yko73bb zCnrBFm&?sot0l%5vDQiyMPiJR!^1;aT3&9KN~P&cCQ~?%2dAc{D$Qn7thHi{IV)?e z#BnUA3I6$0W?p$_4j4GaPN`J-s?lgj5CjqgfkaVs2IDxESVUqG3I3?a{%7w<_v1I^ zgJ;v91E|r_(U)gtW^Rv+jNmv9p68*p#&H|~lv03TfBBl;!VQcCl3Bw0e{uE8g_qok ziHX;W#Uj>PwALu4PS6RC4a`pCEZ)FZK8|SmM}6`a2e>k%ue!s-!%vC`B60>#G<5f2 z_ET6bqY@sjiO^AqG9cX!Pv!K}uJ8MaFbvUJ<9Qyg>w@E;S{pDs1zYzZodOeq2~nM0 zyxnc2-Nuit@Ai7V*cgMgmN*uy=b>wN;fMF2yA6pnLfE&jwKevZ8RF& z{eBNCX^i8OT)Bl)`3MLgJ8(RT3#bId8L;-aq62C{yyLE{tSq$Khi~|)G`ZC;@Rz5L zVZI2-BsdECedxFl113U(kf^UHx0rY4=jU(UuWfDUdu1}$-v{4=Ocv5<$mJlHhfD@8 zT!2&(5d}LPerwr{pBmiM#+ZY(^}jb?f9}y&3!UG6krd#fijP1xdkl?19z2Bl4m5YE z9UUEBtJt?{eHJyq&Q^W(*M+5jpHGD^JU;qZp20!X6PID|BKQf24JzrN5{f(3{fF1S z-z(i|^9_JLU3;S&-JMyBZm&Ps3vu;8dLWh7*{s)hJ(|rr Ak^lez literal 0 HcmV?d00001 diff --git a/kbounce/pics/hi22-app-kbounce.png b/kbounce/pics/hi22-app-kbounce.png new file mode 100644 index 0000000000000000000000000000000000000000..51b7b1c89ae235b64a8482288eb5cb138078bcaf GIT binary patch literal 1298 zcmV+t1?~EYP)z^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v(h)G02RCwBql}&6_MHI(>ckbN#`o4tsY(cG-Rsy0J zgBCR|j2mLqg$Y|0Xw(>WA#vqLf`Nq+jV?@#VW9?1jK=7~1uD=~LTD5b3MwB}z|z*X zuPu*9-{<|9IW8^~s|7vDKX-B`=Qnfaf9_n(Rl;>$r%)(#c6N4l<#M?$5%G;NB&u#10t$S+a%9XucU0uua`Fw}tI64f&^4Qqe=XrCh6F(%VHnQA{;60*!gCW++xfnfUsxwwZ{(dh ze*k)Wdmr4qdGiM=R;+bGGYmK#bLD*%xld(Ori}=ge(e-JxijmT3yj#|=Y0cs-MMb+_ zwQALUYuBz_dkLzwUJyDDzUMuhAHGEEGzu3d4#9w_#jL!!>-q-iYb=fd$2i0$#Ls8xy_UOmKA-PUN@0w_^Spn-=f*RCz^=Do z{3K*DU<`;w#Sxwu5C>_6IF%C1GW2MrlxM9a2m-X$c%FxdAR@_mIu48+hTU&Ka~AS> zum)laR)7enS;4E!V9RCP$lxfnw$@^d;nLF>gX=1k_7KOx`RP*_*bdqQ-viTtD1=rW z8da#*pjO3c*721=c_0EhilRoN(IAydVXeg)gK;t_YtXyi#5w#iWUm1wAR%CZ7+eFM z3#n96fe1<(NYhXbIj>8l(%D+AMkbRX<@?0SN7v8c?b(VN*$2g?NwW5mW3e{baS(uV z6BDe3IEFYPoHZP)b4m{m4(_YfY8B-;i?fAx82^>r?w3)msRWwIKwAntACv>eUWSS! zCdM(uKFt}2dW`Hl$6i&fR)6pB@Bb=aY-jTDUfRES234Pcd;wB^0!?QimxXK&vKdIH zlPY|FPR-enw1j*;&Hj^B_A9_{-@g6L$*Jj)u3`q67zQ-BZc@JILt9${&F3MLO&0V0 zWG)PeCZjGnC)ZfLU|*W`SUMOo-5 zLU9qKQ{V`dbF=2V?}tx4{o%3qUf+NIm0HaH|MhDEmZUv(=N(=7ySh6I-NlYYi`}+1 zRc!?Iv9a>l(V^1Onc3hFFnqDO`M==5hksEdUk3poS4;S707DLAnu>W`cmMzZ07*qo IM6N<$g1V_`#sB~S literal 0 HcmV?d00001 diff --git a/kbounce/pics/hi32-app-kbounce.png b/kbounce/pics/hi32-app-kbounce.png new file mode 100644 index 0000000000000000000000000000000000000000..652824f7f103605849953a56783a76ac5be726e9 GIT binary patch literal 2175 zcmV-_2!QvAP)H)oFEe`}e+!NGxb>(&jnw6x@aHoyW_!Z4hjo}NB8 zH8nL>EEWq_I-$6NG3h(*xZ^vw+;YqAp`oGMv)SyrY&Pq*w6tJZ7D5QL)>Nxi=I7^C zp-{+=kB^U@JbCiq=;-JR`Fwu%{}tfw-o5*tyY9N{zQMu48~ghD=jTG+y!Ogh z2OGHUw%fk{%rnpAj~_p7W@l$j7>1@=t(r=uVye}u34*|cVQ8W#GEo$nIF8p?y-z7+ zj4`Ghgl25?4fE!nTZ=o}x$|$XSM^XJVT1I^>D`k(IZ?!JBd_8k})80g=!WedLVqm)8Q z*+{lJxNdde<$Xd*gp{aHKcV#Z-w;iWCM>jVU75c1r8!1Is5A=@_uqg26FYY7 z*tv1zM$+jtT5BxJS_7|pcR8@R2`Q065RHveeerQjF^`EuOu2|Xcr(4r=dbB3FLfPW z;>E@iY~Q|p*YNQ053<=TnM|hPd2?bly!#Sx?Ir;6+s_ld@GHdXBDMuKF_a6ia1ra9 zcXIc7`$xC8v$HP1+O=!fPqNvp=;-Jmj^i~Revz$ZHr(`t41dHr>$C(b49*F2gTudsSC-JlMMSxZB|!*myV*OfHA*RzQdpMN*Z~{fTUN4|$M?er&w=HDpQ=ru z6R-f{7)%I38CI8JrGO|Ev4R*&fpNfSfz}3FYfTu2SeDfQ)x~J7ky0knuxtbdapW=h z;BVl!f|Q_DB1$WWBM1YiloAU92&?I!V8P#!FXeY#m`FR1_#fXfG`-&6Ue#Y};;Z z!CGr{Z8tPd3b8neb?8CF)Z5U~362An1)^!+&}+|zS4XUEUca^f#^zWURomi=^-2X+E*-308{;B_I6+vIs|nQFq-V!%oe z(3v(&u2On>iV+~N0Lta^T%}U!-E+@9+h!LFq>lc9uF2Dg-kwX|`nB+T&ED65wOs{F zZOt_(o#ciw#1nZ|^UGv10*9Cf-Zg)GbAcy;O8r0*oH}*tLSJ9s*4^K`d!0A&2I<0S z%u-5v3C5`~_4{A=Tw!#bq8pt=@WgjE8^=&~zjN z7LOi1I3%$&IgxJuJSq>pqh^yKm?Olq3(-a9~Y9$y$6hpa;IddMSKSQ*q zvD!0;n=-qoVmY&M%!k zS4Q5H2{v{*frsxSa(#&2K15FrQ>h@X>V;GqY&XerDKWG25Gus_K15pz!c`V7E`&dS zeg2hw?^GWJMuAc@@NxkFRp8T8MNU3FX~tK}vG)xx>h5%79~ZDBqP0CKwR9$V6FNz$ ziH;UTM+&MHW+vxW_aB~k``+iKe)r;d%I+JGqACzg>QZLhR)${Z|%8h z%TP~WZbN^o-`(Xnt?h!8Poy!G!b)`RlbOQ%Cnu-hJaOUlt0yo1b8b040gMAhlEwO5 zyONJnwnC=k8gf?5I@@tJ#)z)a^D^2?+Y!>NRDw!=Zn>~riRYB!BJe4&ND}U^JZ4>y zfcn@Y!&omB@WzWc(+k*VN>Hk$ArLj$m7VQ?{{oI_4y{ZysXzb#002ovPDHLkV1iHk BC}98q literal 0 HcmV?d00001 diff --git a/kbounce/pics/hi48-app-kbounce.png b/kbounce/pics/hi48-app-kbounce.png new file mode 100644 index 0000000000000000000000000000000000000000..4b91913c96b2908de72e3352c28b354b2f841b66 GIT binary patch literal 3697 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 T%$mo700000NkvXXu0mjfLM7Sf literal 0 HcmV?d00001 diff --git a/kbounce/pics/tiles.png b/kbounce/pics/tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..13461e3d1203f43367bd9578faad30f59d23d1e9 GIT binary patch literal 753 zcmVKqoBxG54C+Xzm?gJr%^lH%azTchoX_jRL5UBP+wXoJ1 zj&Sh(cVYdXe*=8|vY%V`^Jp7pM+X3C>ti83034tA==MD7{lUD+e4dbg?E-`l7=}UD z2Xw@mWm!S1)hZ0(K*eX8;lW zxCtgx7ma!icOfIde-*esMksbS(g4a z)6diM(oYCkO$TW0?WZ4~KT+Ycl3%TCF2PTB0NIT28tG^0v!vfJ40)CS7X0Me&x0?6 z5mZRORC@p(eA?PCqY+d|zcdC2A+z8o*Zw>IQCcIYl749nkOrLwe=>D(=Raag-w@uQ z@jlQL(W{hxsX9Pf;%OVdEqx>E0NVPPn+V=V7!ke7>0gNf!rgw_k!2Z(L#J*0lI_3S z^MpW(>Qznu$~u5E98r%f6^EV%pVt20+6bzpU#forw8V#NejGk;1Nat3P%-^LNwa`( zupvFipSm ji62MLoA?2sP7GfFU%6CP#!i@*00000NkvXXu0mjfKk!#M literal 0 HcmV?d00001 diff --git a/kbounce/sounds/Makefile.am b/kbounce/sounds/Makefile.am new file mode 100644 index 00000000..6b779bda --- /dev/null +++ b/kbounce/sounds/Makefile.am @@ -0,0 +1,7 @@ +AUTOMAKE_OPTIONS = foreign + +sound_DATA = death.au reflect.au wallstart.au wallend.au seconds.au timeout.au +sounddir = $(kde_datadir)/kbounce/sounds + +EXTRA_DIST = $(sound_DATA) + diff --git a/kbounce/sounds/death.au b/kbounce/sounds/death.au new file mode 100644 index 0000000000000000000000000000000000000000..7d62863297f102275a220cc1b17d19ee8b3ef37a GIT binary patch literal 1407 zcmX9;%THQq6n8S6v{Myot=7jNwL0}tk+xduC}OSNTEq(>Uko8%2n!~;3zK0n3;%#g z`oc%8uUbW&3A7_mu}A^0AXPw$S3+(KAvcDFi*MoLE?wTaot~5LoRcpnzw`SgC#U_- zKYlMND!Nrv^pla#KNJ;-AAiq$FrTk}s)Hd~e zdQVh(xtv>^n07Z_FD;X-PfjePMAs@CgNw7)Sbh18hUDto`HctFLSaq!$Au*aXcXO% z7hG0XM{V7>^Is&c{Z*cDtXOrTvf^tGZ8MfRN>nDS=w6$)Oxjwmmz0S7bCx%o^+G{K zt9x#8#?yXNSk>iOn3}S+RSAl#pW0_FmgT7Un($`zE!Vt#Wlh>dwALwCz0PH?c+ zY%X;Ot1cJbY;n%7IEQc7R#ai@Z(hHeaf5Oqdv}enSWvgU zFgZCFzEdd>HgC*LSmqDKyMrlt|wSXx~2c8Y83WsJwRk?3u`eJ`_VU-v1R#J{vM&eeBo+4~Rf z;eMz6Z{K54&7D5iN2iAzX_NHRF59YG)gx0F_Z*H57r56hX~mPCt?hKDR8E9k>npac z3u#M3vrHXycpT0IF(~U%W#TAp=xo0$>B_imHdpu@zepd2eIcgnzN8zWOvrnn!#X?J zJ@S+1Gp<#Qq;`TH&sKb-^FjX@ri73VY;HPjc8A*^4EVnygh?fmc6BUFZu*Yk340RQ zj`%lrSr#aWB*(=V+-w|D8ja_X&q1FnLNWMgAw%V9qpYJ%N-+Dr;1^ES-Jv|DK6y!s z1p_#fB!emNxL-?$Hoe|3G0-7XnB%*F5Y2%nx@0W0<%>}?Lr49g;125RQh?(vw||>F zMJN^ayIr3Gd-O4rW4sb0Q5{{&DK-sHMK`OH24@4-aRcW~N zJ{dbKa0Ju=Xk-z!yF-$PtVY=*>%ol$nm)-DxLiCEJ7A1T6{bSb9Ws6ja9oQH$v`kl zvrrFNCPQcG6DqwQi;&TH>Nv+B!U&-nj?;-Q_N91i%bB=o(??&9Gi_FF=r5y%VoWq3zW}|0|V64>0~N(${FxyLmHsgnINKB zZj4Z3!<#69l#NyA_qxC;2J=Djqb(LjF}iinRqIl zB~%!u)S2^CB9%%}RE{x$VMR}GZ@-p9=!{{Baf0B?yw<@1OsR$}Ldc8{Ddh5=ehi1a zj$wQlSL+~9^(hojcwRs^s{GbF3P+)de|nvXGa1JTjyrNU$7^QuY_6hm`O{#5bH5xB%F(QI2UO?m~g0i=n*%#^)w2h{X{t*JDsYn6w z26EfQS_F1?c4j-K-;Z;?eCOnR=S3>M>2NrnI~-3xF@EB3JbV408M3hepafAQLs^DM z;6-7drxYPdZAqZUXk?hgBGg9oals4_1c+cl3<_mP5tJkmKw`^)2~43_9}X2n6rd0j zBWjsL3?WqjkbopXCmUCi!9v& zvIK03Bwmn!g4m6Tral;1EP_w$BO3x4VG|n$CYFH}M1drv_C!dq#Ud<>sZF88^D^sC zM+4J_SJ`*dUoV0)uMWSw96QLo_if*1{qv$|~mASkeYKH^e>vdPP~WH0Y=SU#k}8`dyfc?zRSzUN6>d2IYo6 zBBA!h`bKk@OosG&Wc}_TYM48QnSOw=`&N9b*R+y}`)i3aZ&RuCzNQC}78BbkzB90u W*yvK+RO{(j@=s&Or!<8yGRZ&l^36d2 literal 0 HcmV?d00001 diff --git a/kbounce/sounds/seconds.au b/kbounce/sounds/seconds.au new file mode 100644 index 0000000000000000000000000000000000000000..c6d4f6c113cfbe5ba336ad776b54298c8055ab0c GIT binary patch literal 362 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 literal 0 HcmV?d00001 diff --git a/kbounce/sounds/wallstart.au b/kbounce/sounds/wallstart.au new file mode 100644 index 0000000000000000000000000000000000000000..d4377742b18f3eb505d7d60e73e957cdfe54b883 GIT binary patch literal 1271 zcmW+$!HOG45LF=mp;I7WvW~N{V{bN_7$X)T1|8-whdwmHh9V@S4k6#8B&T373BhZ_ z?m9wmO%RvPxA6TVr(VY4*6|fq<*5CZ#KVe=1({CId;P*E}K7p ze~A4&Y_|I3%lB_?-_T8x?bi=)o;`kje(~AT7k}NJ9s0{hAM!WDdcW)PbC$Ba3cM{% zx?itXjg5z^i%iAR#j=<2o>$|lyS?x3n+3OTYYKZ#{knhuMZQam{kF>P@@##cTlzcE zg?zQV>z{V%^ZkP-@?o|6x2fUo`tC-&U*zp_M@LWRdhA8x)#W~P%$}BcrxZhWmlsjG zw!|t6P3-l4EPV8#8xCC`?I>KtiyZCxu3#w zwq+m~-k7A&3ynGKmmObwQV}saWRgIzpeUn1Yc5`zZq#<5+=a~J(GHr-_LYVVe$&j^ z%cvyQBXm6ymWO zd5pO*Sb|1lk$M6#aZH0~ZnG9P#@ob<8EPbiv0`A1a_-zPP-ey0g5A*rHgD8Y>*Y(S90Pk02gz{|_k_D|%(B%_ig=W}A1x7|#YOoG9Of=9rm7*{LnIJ+zsA5tMdkCeR zh!F;9CyBVDS`>Tw!3msCr2q}VI_XCQylZ^8ww}FM?9_QE5uXF##72X^d@>sS1~)N= Z<@DDBSzV10S*Jcv +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AbTop.h" +#include "Board.h" +#include "BoardWidget.h" +#include "EvalDlgImpl.h" +#include "EvalScheme.h" +#include "Network.h" +#include "Spy.h" +#include "version.h" + +#include +#include + +// #define MYTRACE 1 + +const AbTop::Data AbTop::LEVEL[Nb_Levels] = { + { "Easy", I18N_NOOP("&Easy") }, + { "Normal", I18N_NOOP("&Normal") }, + { "Hard", I18N_NOOP("&Hard") }, + { "Challange", I18N_NOOP("&Challenge") } +}; + +const AbTop::Data AbTop::IPLAY[Nb_IPlays] = { + { "Red", I18N_NOOP("&Red") }, + { "Yellow", I18N_NOOP("&Yellow") }, + { "Both", I18N_NOOP("&Both") }, + { "None", I18N_NOOP("&None") } +}; + +AbTop::AbTop() + :KMainWindow(0) +{ + timerState = noGame; + + myPort = Network::defaultPort; + currentEvalScheme = 0; + net = 0; + + actValue = 0; + stop = false; + editMode = false; + spyLevel = 0; + pastePossible = true; + + + timer = new QTimer; + connect( timer, SIGNAL(timeout()), this, SLOT(timerDone()) ); + + board = new Board(); + setMoveNo(0); + + connect( board, SIGNAL(searchBreak()), this, SLOT(searchBreak()) ); + Q_CHECK_PTR(board); + boardWidget = new BoardWidget(*board,this); + +#ifdef SPION + spy = new Spy(*board); +#endif + + connect( boardWidget, SIGNAL(updateSpy(QString)), + this, SLOT(updateSpy(QString)) ); + + setCentralWidget(boardWidget); + boardWidget->show(); + + // this creates the GUI + setupActions(); + setupStatusBar(); + setMinimumSize(200,300); + + // RMB context menu + connect( boardWidget, SIGNAL(rightButtonPressed(int,const QPoint&)), + this, SLOT(rightButtonPressed(int,const QPoint&)) ); + + connect( boardWidget, SIGNAL(edited(int)), + this, SLOT(edited(int)) ); + + connect( board, SIGNAL(updateBestMove(Move&,int)), + this, SLOT(updateBestMove(Move&,int)) ); + + connect( boardWidget, SIGNAL(moveChoosen(Move&)), + this, SLOT(moveChoosen(Move&)) ); + + /* default */ + setLevel(Easy); + setIPlay(Red); + showMoveLong = true; + showSpy = false; + renderBalls = true; + + updateStatus(); + updateActions(); + setupGUI(); +} + +AbTop::~AbTop() +{ + /* Unregister from other abalone processes */ + delete net; + delete timer; +#ifdef SPION + delete spy; +#endif +} + + +/** + * Create all the actions... + * + * The GUI will be built in createGUI using a XML file + * + */ + +void AbTop::setupActions() +{ + newAction = KStdGameAction::gameNew( this, SLOT(newGame()), actionCollection() ); + KStdGameAction::quit( this, SLOT(close()), actionCollection() ); + + stopAction = new KAction( i18n("&Stop Search"), "stop", Key_S, this, + SLOT(stopSearch()), actionCollection(), "move_stop"); + + backAction = new KAction( i18n("Take &Back"), "back", + KStdAccel::shortcut(KStdAccel::Prior), this, + SLOT(back()), actionCollection(), "move_back"); + + forwardAction = new KAction( i18n("&Forward"), "forward", + KStdAccel::shortcut(KStdAccel::Next), this, + SLOT(forward()), actionCollection(), "move_forward"); + + hintAction = KStdGameAction::hint(this, SLOT(suggestion()), actionCollection()); + + KStdAction::copy( this, SLOT(copy()), actionCollection()); + pasteAction = KStdAction::paste( this, SLOT(paste()), actionCollection()); + + (void) new KAction( i18n("&Restore Position"), + KStdAccel::shortcut(KStdAccel::Open), + this, SLOT(restorePosition()), + actionCollection(), "edit_restore" ); + + (void) new KAction( i18n("&Save Position"), + KStdAccel::shortcut(KStdAccel::Save), + this, SLOT(savePosition()), + actionCollection(), "edit_save" ); + + KToggleAction *ta; + + ta = new KToggleAction( i18n("&Network Play"), "network", Key_N, + actionCollection(), "game_net"); + connect(ta, SIGNAL(toggled(bool)), this, SLOT(gameNetwork(bool))); + + editAction = new KToggleAction( i18n("&Modify"), "edit", + CTRL+Key_Insert, actionCollection(), "edit_modify"); + connect(editAction, SIGNAL(toggled(bool)), this, SLOT( editModify(bool))); + + showMenubar = KStdAction::showMenubar(this, SLOT(toggleMenubar()), actionCollection()); + KStdAction::saveOptions( this, SLOT(writeConfig()), actionCollection()); + + KStdAction::preferences( this, SLOT(configure()), actionCollection()); + + moveSlowAction = new KToggleAction( i18n("&Move Slow"), 0, + actionCollection(), "options_moveSlow"); + connect(moveSlowAction, SIGNAL(toggled(bool)), this, SLOT(optionMoveSlow(bool))); + + renderBallsAction = new KToggleAction( i18n("&Render Balls"), 0, + actionCollection(), "options_renderBalls"); + connect(renderBallsAction, SIGNAL(toggled(bool)), this, SLOT(optionRenderBalls(bool))); + + showSpyAction = new KToggleAction( i18n("&Spy"), 0, + actionCollection(), "options_showSpy"); + connect(showSpyAction, SIGNAL(toggled(bool)), this, SLOT(optionShowSpy(bool))); + + + levelAction = KStdGameAction::chooseGameType(0, 0, actionCollection()); + QStringList list; + for (uint i=0; isetItems(list); + connect(levelAction, SIGNAL(activated(int)), SLOT(setLevel(int))); + + iplayAction = new KSelectAction(i18n("&Computer Play"), 0, actionCollection(), "options_iplay"); + list.clear(); + for (uint i=0; isetItems(list); + connect(iplayAction, SIGNAL(activated(int)), SLOT(setIPlay(int))); +} + +void AbTop::toggleMenubar() +{ + if (menuBar()->isVisible()) + menuBar()->hide(); + else + menuBar()->show(); +} + +void AbTop::configure() +{ + KDialogBase *dlg = new KDialogBase( 0, "ConfigureEvaluation", true, + i18n("Configure Evaluation"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, true); + + EvalDlgImpl *edlg = new EvalDlgImpl(dlg,board); + dlg->setMainWidget(edlg); + if (dlg->exec()) { + *currentEvalScheme = *(edlg->evalScheme()); + board->setEvalScheme(currentEvalScheme); + } + delete edlg; +} + +/* Right Mouse button pressed in BoardWidget area */ +void AbTop::rightButtonPressed(int /* field */, const QPoint& pos) +{ + QPopupMenu* rmbMenu = static_cast (factory()->container("rmbPopup",this)); + if (rmbMenu) + rmbMenu->popup( pos ); +} + +/* Read config options + * + * menu must already be created! + */ +void AbTop::readConfig() +{ + kdDebug(12011) << "Reading config..." << endl; + + KConfig* config = kapp->config(); + config->setGroup("Options"); + + readOptions(config); + + applyMainWindowSettings( config, "Appearance" ); + + showMenubar->setChecked( !menuBar()->isHidden() ); + + currentEvalScheme = new EvalScheme("Current"); + currentEvalScheme->read(config); + board->setEvalScheme( currentEvalScheme ); +} + +void AbTop::readOptions(KConfig* config) +{ + QString entry = config->readEntry("Level"); + for (uint i=0; ireadEntry("Computer"); + for (uint i=0; ireadBoolEntry("MoveSlow", false); + moveSlowAction->setChecked( showMoveLong ); + + renderBalls = config->readBoolEntry("RenderBalls", true); + boardWidget->renderBalls(renderBalls); + renderBallsAction->setChecked( renderBalls ); + + showSpy = config->readBoolEntry("ShowSpy", true); + board->updateSpy(showSpy); + showSpyAction->setChecked( showSpy ); +} + +void AbTop::readProperties(KConfig *config) +{ + QString entry; + + readOptions(config); + + currentEvalScheme = new EvalScheme("Current"); + currentEvalScheme->read(config); + board->setEvalScheme( currentEvalScheme ); + + + if (!(entry = config->readEntry("TimerState")).isNull()) + timerState = entry.toInt(); + if (timerState == noGame) return; + + stop = config->readBoolEntry("GameStopped", false); + + int mNo = 0; + if (!(entry = config->readEntry("Position")).isNull()) { + mNo = board->setState(entry); + boardWidget->updatePosition(true); + } + setMoveNo(mNo, true); + + show(); + playGame(); +} + +void AbTop::writeConfig() +{ + kdDebug(12011) << "Writing config..." << endl; + + KConfig* config = kapp->config(); + config->setGroup("Options"); + + writeOptions(config); + + saveMainWindowSettings( config, "Appearance" ); + + if (currentEvalScheme) + currentEvalScheme->save(config); + config->sync(); +} + + +void AbTop::writeOptions(KConfig *config) +{ + config->writeEntry("Level", LEVEL[levelAction->currentItem()].key); + config->writeEntry("Computer", IPLAY[iplayAction->currentItem()].key); + + config->writeEntry("MoveSlow", showMoveLong); + config->writeEntry("RenderBalls", renderBalls); + config->writeEntry("ShowSpy", showSpy); +} + +void AbTop::saveProperties(KConfig *config) +{ + writeOptions(config); + if (currentEvalScheme) + currentEvalScheme->save(config); + + config->writeEntry("TimerState", timerState); + + if (timerState == noGame) return; + + config->writeEntry("GameStopped", stop); + config->writeEntry("Position", board->getState(moveNo)); + config->sync(); +} + +void AbTop::savePosition() +{ + KConfig* config = kapp->config(); + config->setGroup("SavedPosition"); + config->writeEntry("Position", board->getState(moveNo)); +} + +void AbTop::restorePosition() +{ + KConfig* config = kapp->config(); + config->setGroup("SavedPosition"); + QString entry = config->readEntry("Position"); + + timerState = notStarted; + timer->stop(); + board->begin(Board::color1); + stop = false; + setMoveNo( board->setState(entry), true ); + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + + boardWidget->updatePosition(true); + + playGame(); +} + +void AbTop::setupStatusBar() +{ + QString tmp; + + QString t = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); + statusLabel = new QLabel( t, statusBar(), "statusLabel" ); + statusBar()->addWidget(statusLabel,1,false); + + // PERMANENT: Moving side + move No. + + // validPixmap, only visible in Modify mode: is position valid ? + warningPix = BarIcon( "warning" ); + okPix = BarIcon( "ok" ); + validLabel = new QLabel( "", statusBar(), "validLabel" ); + validLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); + validLabel->setAlignment( AlignCenter ); + validLabel->hide(); + validShown = false; + + redBall = BarIcon( "redball" ); + yellowBall = BarIcon( "yellowball" ); + noBall = BarIcon( "noball" ); + ballLabel = new QLabel( "", statusBar(), "ballLabel" ); + ballLabel->setPixmap(noBall); + ballLabel->setFixedSize( 18, statusLabel->sizeHint().height() ); + ballLabel->setAlignment( AlignCenter ); + statusBar()->addWidget(ballLabel, 0, true); + + moveLabel = new QLabel( i18n("Move %1").arg("--"), statusBar(), "moveLabel" ); + statusBar()->addWidget(moveLabel, 0, true); + +#ifdef MYTRACE + /* Create a toolbar menu for debugging output level */ + KToolBar *tb = toolBar("mainToolBar"); + if (tb) { + QPopupMenu* spyPopup = new QPopupMenu; + spy0 = BarIcon( "spy0" ); + spy1 = BarIcon( "spy1" ); + spy2 = BarIcon( "spy2" ); + spy3 = BarIcon( "spy3" ); + spyPopup->insertItem(spy0, 0); + spyPopup->insertItem(spy1, 1); + spyPopup->insertItem(spy2, 2); + spyPopup->insertItem(spy3, 3); + connect( spyPopup, SIGNAL(activated(int)), + this, SLOT(setSpy(int)) ); + tb->insertButton(spy0, 30, spyPopup, + TRUE, i18n("Spy")); + } +#endif + +} + + + +void AbTop::updateSpy(QString s) +{ + if (showSpy) { + if (s.isEmpty()) { + updateStatus(); + // statusBar()->clear(); + } + else + statusLabel->setText(s); + } +} + +void AbTop::updateBestMove(Move& m, int value) +{ + if (showSpy) { + boardWidget->showMove(m,3); + boardWidget->showMove(m,0,false); + + QString tmp; + tmp.sprintf("%s : %+d", (const char*) m.name().utf8(), value-actValue); + updateSpy(tmp); + kapp->processEvents(); + } +} + + +void AbTop::updateStatus() +{ + QString tmp; + bool showValid = false; + + if (!editMode && timerState == noGame) { + tmp = i18n("Move %1").arg("--"); + ballLabel->setPixmap(noBall); + } + else { + tmp = i18n("Move %1").arg(moveNo/2 + 1); + ballLabel->setPixmap( (board->actColor() == Board::color1) + ? redBall : yellowBall); + } + moveLabel->setText(tmp); + + if (editMode) { + tmp = QString("%1: %2 %3 - %4 %5") + .arg( i18n("Edit") ) + .arg( i18n("Red") ).arg(boardWidget->getColor1Count()) + .arg( i18n("Yellow") ).arg(boardWidget->getColor2Count()); + validLabel->setPixmap( (board->validState() == Board::invalid) + ? warningPix:okPix ); + showValid = true; + } + else if (timerState == noGame) { + tmp = i18n("Press %1 for a new game").arg( newAction->shortcut().toString()); + } + else { + if (timerState == gameOver) { + tmp = (board->actColor() == Board::color2) ? + i18n("Red won"):i18n("Yellow won"); + validLabel->setPixmap( warningPix ); + showValid = true; + } + else { + tmp = QString("%1 - %2") + .arg( (board->actColor() == Board::color1) ? + i18n("Red"):i18n("Yellow") ) + .arg( iPlayNow() ? + i18n("I am thinking...") : i18n("It is your turn!") ); + } + } + statusLabel->setText(tmp); + if (validShown != showValid) { + if (showValid) { + statusBar()->addWidget(validLabel); + validLabel->show(); + } + else { + statusBar()->removeWidget(validLabel); + validLabel->hide(); + } + validShown = showValid; + } + statusBar()->clear(); + statusBar()->repaint(); +} + +void AbTop::edited(int vState) +{ + if (vState == Board::empty) + timerState = noGame; + + updateStatus(); +} + +/* only , , have to be updated */ +void AbTop::updateActions() +{ + bool iPlay = iPlayNow(); + + /* New && Copy always on */ + + /* Paste */ + pastePossible = !iPlay; + pasteAction->setEnabled(!iPlay); + + /* Edit */ + editAction->setEnabled(!iPlay); + + /* Stop search */ + stopAction->setEnabled(iPlay); + + /* Back */ + bool bBack = (editMode && moveNo>0) || + (board->movesStored() >=1 && !iPlay); + backAction->setEnabled(bBack); + + /* Forward */ + bool bForward = editMode && moveNo<999; + forwardAction->setEnabled(bForward); + + /* Hint */ + bool bHint = !editMode && !iPlay && (haveHint().type != Move::none); + hintAction->setEnabled(bHint); +} + +/* let the program be responsive even in a long search... */ +void AbTop::searchBreak() +{ + kapp->processEvents(); +} + + +void AbTop::setSpy(int id ) +{ + toolBar("mainToolBar")->setButtonPixmap(30, (id==0)?spy0:(id==1)?spy1:(id==2)?spy2:spy3 ); + spyLevel = id; + board->setSpyLevel(spyLevel); +} + +void AbTop::timerDone() +{ + int interval = 400; + + switch(timerState) { + case noGame: + case notStarted: + return; + case showMove: + case showMove+2: + case showSugg: + case showSugg+2: + case showSugg+4: + boardWidget->showMove(actMove, 2); + interval = 200; + break; + case showMove+1: + case showMove+3: + case showSugg+1: + case showSugg+3: + boardWidget->showMove(actMove, 3); + break; + case showSugg+5: + interval = 800; + case showMove+4: + boardWidget->showMove(actMove, 4); + break; + case showMove+5: + boardWidget->showMove(actMove, 0); + timerState = moveShown; + playGame(); + return; + case showSugg+6: + boardWidget->showMove(actMove, 0); + timerState = notStarted; + return; + } + timerState++; + timer->start(interval,TRUE); +} + +void AbTop::userMove() +{ + /* User has to move */ + static MoveList list; + + list.clear(); + board->generateMoves(list); + + if (list.getLength() == 0) { + stop = true; + timerState = gameOver; + playGame(); + } + else + boardWidget->choseMove(&list); +} + +bool AbTop::iPlayNow() +{ + if (editMode || + (board->validState() != Board::valid) || + timerState == gameOver) + return false; + + int c = board->actColor(); + + /* color1 is red */ + return ((iplay == Both) || + ((c == Board::color1) && (iplay == Red) ) || + ((c == Board::color2) && (iplay == Yellow) )); +} + +void AbTop::playGame() +{ + if (timerState == moveShown) { + if (actMove.type != Move::none) { + board->playMove(actMove); + moveNo++; // actColor in board is changed in playMove + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + } + actValue = - board->calcEvaluation(); + boardWidget->updatePosition(true); + timerState = notStarted; + } + if (!board->isValid()) { + stop = true; + timerState = gameOver; + } + + updateStatus(); + updateActions(); + boardWidget->setCursor(crossCursor); + if (stop) return; + + + if (!iPlayNow()) { + userMove(); + return; + } + boardWidget->setCursor(waitCursor); + kapp->processEvents(); + + if (moveNo <4) { + /* Chose a random move making the position better for actual color */ + + /* If comparing ratings among color1/2 on move, we have to negate one */ + int v = -board->calcEvaluation(), vv; + do { + actMove = board->randomMove(); + board->playMove(actMove); + vv = board->calcEvaluation(); + board->takeBack(); + } while( (board->actColor() == Board::color1) ? (vvv) ); + } + else { + actMove = (board->bestMove()); + + if (actMove.type == Move::none) { + stop = true; + timerState = gameOver; + playGame(); + return; + } + } + + timerState = showMoveLong ? showMove : showMove+3; + timerDone(); +} + +void AbTop::moveChoosen(Move& m) +{ + actMove = m; + timerState = moveShown; + playGame(); +} + +void AbTop::newGame() +{ + /* stop a running animation */ + timerState = notStarted; + timer->stop(); + + /* reset board */ + board->begin(Board::color1); + boardWidget->updatePosition(true); + setMoveNo(0, true); + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); + + /* if not in EditMode, start Game immediately */ + if (!editMode) { + stop = false; + playGame(); + } +} + +/* Copy ASCII representation into Clipboard */ +void AbTop::copy() +{ + QClipboard *cb = QApplication::clipboard(); + cb->setText( board->getASCIIState( moveNo ).ascii() ); +} + +void AbTop::paste() +{ + if (!pastePossible) return; + + QClipboard *cb = QApplication::clipboard(); + pastePosition( cb->text().ascii() ); + /* don't do this in pastePosition: RECURSION !! */ + + if (net) + net->broadcast( board->getASCIIState( moveNo ).ascii() ); +} + +void AbTop::pastePosition(const char * text) +{ + if (!pastePossible) return; + if ( text ) { + timerState = notStarted; + timer->stop(); + board->begin(Board::color1); + stop = false; + + int mNo = board->setASCIIState(text); + if (mNo<0) mNo=0; + setMoveNo( mNo, true); + + boardWidget->updatePosition(true); + + if ( (board->validState()==Board::invalid) && !editMode) { + editAction->setChecked(true); + return; + } + + playGame(); + } +} + + +void AbTop::gameNetwork(bool on) +{ + if (!on) { + if (net != 0) { + delete net; + net = 0; + } + return; + } + + if (myPort == 0) myPort = Network::defaultPort; + net = new Network(myPort); + char *h, h2[100]; + int p, i; + for(h = hosts.first(); h!=0; h=hosts.next()) { + for(i=0;h[i]!=0 && h[i]!=':';i++); + if (h[i]==':') + p = atoi(h+i+1); + else + p = 0; + + if (p == 0) p = Network::defaultPort; + strncpy(h2,h,i); + h2[i]=0; + net->addListener(h2, p); + } + QObject::connect(net, SIGNAL(gotPosition(const char *)), + this, SLOT(pastePosition(const char *)) ); +} + + +void AbTop::editModify(bool on) +{ + int vState = board->validState(); + + editMode = boardWidget->setEditMode( on ); + if (vState != Board::valid) + timerState = noGame; + + updateActions(); + updateStatus(); + if (!editMode && vState == Board::valid) { + actMove.type = Move::none; + timerState = moveShown; + playGame(); + } +} + +void AbTop::stopGame() +{ + stop = true; + board->stopSearch(); +} + +void AbTop::stopSearch() +{ + // When computer plays both, switch back to human for next color + if (iplay == Both) { + if (board->actColor() == Board::color1) setIPlay(Red); + else setIPlay(Yellow); + } + board->stopSearch(); +} + +bool AbTop::queryClose() +{ + board->stopSearch(); + return true; +} + +void AbTop::continueGame() +{ + if (timerState != noGame && timerState != gameOver) { + stop = false; + if (timerState == notStarted) + playGame(); + } +} + +/** + * Reset the Move number of the actual game to + * If is true, update GUI actions and redraw statusbar + */ +void AbTop::setMoveNo(int m, bool updateGUI) +{ + moveNo = m; + + board->setActColor( ((moveNo%2)==0) ? Board::color1 : Board::color2 ); + + if (updateGUI) { + updateStatus(); + updateActions(); + } +} + + +/* "Back" action activated + * + * If in edit mode, simple go 1 back + * If in a game, go back 2 if possible + */ +void AbTop::back() +{ + if (editMode) { + if (moveNo > 0) + setMoveNo(moveNo-1, true); + return; + } + + if (moveNo < 1) return; + + if (timerState == gameOver) + timerState = notStarted; + if (timerState != notStarted) return; + + /* If possible, go 2 steps back */ + if (moveNo>0 && board->takeBack()) moveNo--; + if (moveNo>0 && board->takeBack()) moveNo--; + setMoveNo( moveNo, true ); + + boardWidget->updatePosition(true); + + userMove(); +} + +/* Only for edit Mode */ +void AbTop::forward() +{ + if (editMode) { + if (moveNo < 999) + setMoveNo(moveNo+1, true); + return; + } +} + +Move AbTop::haveHint() +{ + static Move m; + static int oldMoveNo = 0; + + if (timerState != notStarted) { + m.type = Move::none; + } + else if (moveNo != oldMoveNo) { + MoveList list; + + oldMoveNo = moveNo; + m = board->nextMove(); + board->generateMoves(list); + if (!list.isElement(m,0)) + m.type = Move::none; + } + return m; +} + + +void AbTop::suggestion() +{ + if (timerState != notStarted) return; + Move m = haveHint(); + if (m.type == Move::none) return; + + actMove = m; + + timerState = showSugg; + timerDone(); +} + +void AbTop::setLevel(int l) +{ + levelAction->setCurrentItem(l); + depth = l+2; + board->setDepth(depth); + // kdDebug(12011) << "Level set to " << d << endl; +} + +void AbTop::setIPlay(int i) +{ + iplayAction->setCurrentItem(i); + iplay = (IPlay)i; + continueGame(); +} + +void AbTop::optionMoveSlow(bool on) +{ + showMoveLong = on; +} + +void AbTop::optionRenderBalls(bool on) +{ + renderBalls = on; + boardWidget->renderBalls(renderBalls); +} + +void AbTop::optionShowSpy(bool on) +{ + showSpy = on; + board->updateSpy(showSpy); + +#ifdef SPION + if (showSpy) + spy->show(); + else { + spy->nextStep(); + spy->hide(); + } +#endif + +} + + +#include "AbTop.moc" diff --git a/kenolaba/AbTop.h b/kenolaba/AbTop.h new file mode 100644 index 00000000..35357452 --- /dev/null +++ b/kenolaba/AbTop.h @@ -0,0 +1,152 @@ +/* Class AbTop: the toplevel widget of Kenolaba + * + * Josef Weidendorfer, 9/97 +*/ + +#ifndef _ABTOP_H_ +#define _ABTOP_H_ + +#include + +#include "Move.h" + + +class QTimer; +class QPopupMenu; +class QLabel; + +class KAction; +class KToggleAction; +class KSelectAction; + +class Network; +class Board; +class BoardWidget; +class Move; +class EvalScheme; + +#ifdef SPION +class Spy; +#endif + + + +class AbTop: public KMainWindow +{ + Q_OBJECT + +public: + AbTop(); + ~AbTop(); + + /* timer states */ + enum { noGame, gameOver, notStarted, moveShown, + showMove = 100, showSugg=200 + }; + + void netPort(int p) { myPort = p; } + void netHost(char* h) { hosts.append(h); } + +protected: + virtual void saveProperties( KConfig * ); + virtual void readProperties( KConfig * ); + + +public slots: + void timerDone(); + void newGame(); + void copy(); + void paste(); + void pastePosition(const char *); + void stopGame(); + void continueGame(); + bool queryClose(); + void back(); + void forward(); + void suggestion(); + void stopSearch(); + void searchBreak(); + void moveChoosen(Move&); + void savePosition(); + void restorePosition(); + void setSpy(int); + void updateSpy(QString); + void edited(int); + void updateBestMove(Move&,int); + void readConfig(); + void writeConfig(); + void rightButtonPressed(int,const QPoint&); + + void gameNetwork(bool); + void editModify(bool); + void optionMoveSlow(bool); + void optionRenderBalls(bool); + void optionShowSpy(bool); + void toggleMenubar(); + void configure(); + void setLevel(int); + void setIPlay(int); + +private: + void setupActions(); + void updateStatus(); + void userMove(); + void playGame(); + void loadPixmaps(); + void setupStatusBar(); + void updateActions(); + void setMoveNo(int, bool updateGUI = false); + bool iPlayNow(); + Move haveHint(); + void readOptions(KConfig *); + void writeOptions(KConfig *); + + Move actMove; + Board* board; + int actValue; + BoardWidget *boardWidget; + EvalScheme* currentEvalScheme; + QTimer *timer; + int timerState; + int depth, moveNo; + bool showMoveLong, stop, showSpy; + bool editMode, renderBalls; + int spyLevel; + bool pastePossible, validShown; + + enum IPlay { Red = 0, Yellow, Both, None, Nb_IPlays }; + IPlay iplay; + + int stop_id, back_id, hint_id; + int easy_id, normal_id, hard_id, challange_id, slow_id, level_id; + int render_id; + int yellow_id, red_id, both_id, none_id, iplay_id; + int spy_id, paste_id, edit_id, forward_id, net_id; + + QLabel *validLabel, *ballLabel, *moveLabel, *statusLabel; + QPixmap warningPix, okPix, redBall, yellowBall, noBall, netPix; + QPixmap spy0, spy1, spy2, spy3; + + Network *net; + int myPort; + QStrList hosts; + + KAction *newAction, *stopAction, *backAction, *forwardAction, *hintAction, *pasteAction; + KToggleAction *showMenubar, *renderBallsAction, *moveSlowAction, + *showSpyAction, *editAction; + KSelectAction *levelAction, *iplayAction; + + struct Data { + const char *key, *label; + }; + enum Level { Easy = 0, Normal, Hard, Challenge, Nb_Levels }; + static const Data LEVEL[Nb_Levels]; + static const Data IPLAY[AbTop::Nb_IPlays]; + +#ifdef SPION + Spy* spy; +#endif + +}; + +#endif /* _ABTOP_H_ */ diff --git a/kenolaba/Ball.cpp b/kenolaba/Ball.cpp new file mode 100644 index 00000000..565ef296 --- /dev/null +++ b/kenolaba/Ball.cpp @@ -0,0 +1,492 @@ +/* Ball animation classes */ + +#include "Ball.h" +#include +#include +#include +#include +#include +#include + +Ball* Ball::first = 0; +//QImage Ball::back; +int Ball::sizeX, Ball::sizeY; +double Ball::lightX, Ball::lightY, Ball::lightZ; +QColor Ball::lightColor; +double Ball::rippleCount, Ball::rippleDepth; + +/* set global Ball parameter */ +void Ball::setSize(int x, int y) +{ + sizeX = x; + sizeY = y; + + invalidate(); +} + +void Ball::invalidate() +{ + Ball *b; + + /* invalidate all Balls... */ + for(b=first;b!=0;b=b->next) + b->pm.resize(0,0); +} + +void Ball::setLight(int x, int y, int z, const QColor& c) +{ + double len = sqrt(double(x*x + y*y + z*z)); + + lightX = x/len; + lightY = y/len; + lightZ = z/len; + + lightColor = c; + + invalidate(); +} + + +void Ball::setTexture(double c, double d) +{ + rippleCount = c; + rippleDepth = d; + + invalidate(); +} + + + +Ball::Ball(const QColor& c, double a, int t) +{ + if (first ==0) { + sizeX = sizeY = -1; + setLight(); + setTexture(7,.3); + } + + bColor = c; + an = a; + sina = sin(a), cosa = cos(a); + + zoom= 1.05, flip = 2.0, limit = 0; + tex = t; + + next = first; + first = this; +} + +Ball::~Ball() +{ + Ball* b; + + if (first == this) + first = next; + else { + for(b = first; b!=0; b=b->next) + if (b->next == this) break; + if (b!=0) + b->next = next; + } +} + +QPixmap* Ball::pixmap() +{ + if (pm.isNull() && sizeX>0 && sizeY>0) + render(); + return ± +} + +void Ball::render() +{ + int x,y; + double xx,yy,zz, ll,lll, red,green,blue; + + if (sizeX==0 || sizeY==0) + return; + + QImage image(sizeX,sizeY,32); + image.fill(0); + + double vv=2./(sizeX+sizeY); + + /* Go through all pixels, mapping x/y to (-1..1,-1..1) */ + for(y=0;yflip) zz=2*flip-zz; + else { + zz -= limit; + } + + if (zz>-vv) { + zz = (zz<0) ? 0 : sqrt(zz); + + /* ll: light intensity at this point */ + ll = xx*lightX + yy*lightY + zz*lightZ; + + /* some face modification */ + double mapx = xx*(2-zz); + double mapy = yy*(2-zz); + double rmapx = cosa*mapx + sina*mapy; /* rotate */ + double rmapy = -sina*mapx + cosa*mapy; + + if (tex>0) + ll += rippleDepth* cos(rippleCount*rmapx)*cos(rippleCount*rmapy); + + ll = (ll<0.01) ? 0.0 : (ll>.99) ? 1.0 : ll; + lll = ll*ll; + + // printf("x %f, y %f, z %f : ll %f lll %f\n", xx,yy,zz,ll,lll); + + + /* mix ball+light */ + red = lll * lightColor.red() + (1-lll) * bColor.red(); + green = lll * lightColor.green() + (1-lll) * bColor.green(); + blue = lll * lightColor.blue() + (1-lll) * bColor.blue(); + + /* lightness */ + red = .2 * bColor.red() + .8 * ll * red; + green = .2 * bColor.green() + .8 * ll * green; + blue = .2 * bColor.blue() + .8 * ll * blue; + + image.setPixel(x,y, qRgb( (int)red, (int)green, (int)blue )); + } + } + } + const QImage iMask = image.createHeuristicMask(); + QBitmap bMask; + bMask = iMask; + pm.convertFromImage( image, 0 ); + pm.setMask(bMask); +} + + +/* Class BallAnimation */ + +BallAnimation::BallAnimation(int s, Ball* ball1, Ball* ball2) +{ + QColor c1 = ball1->ballColor(); + double a1 = ball1->angle(); + int r1 = c1.red(), g1 = c1.green(), b1 = c1.blue(); + + QColor c2 = ball2->ballColor(); + double a2 = ball2->angle(); + int r2 = c2.red(), g2 = c2.green(), b2 = c2.blue(); + + QColor c; + double a; + int i; + + steps = s; + s--; + + balls.append( new Ball( c1,a1 ) ); + + for(i=1; i< s; i++) { + c.setRgb( r1+(r2-r1)*i/s, g1+(g2-g1)*i/s, b1+(b2-b1)*i/s ); + a = a1+(a2-a1)*i/s; + + balls.append( new Ball( c,a ) ); + } + + balls.append( new Ball( c2,a2 ) ); +} + + +/* Class BallPosition */ +BallPosition::BallPosition(int xp,int yp, Ball* d) +{ + x=xp; + y=yp; + def=d; + actStep = -1; + actType = ANIMATION_STOPPED; + actAnimation=0; +} + + +/* Class BallWidget */ + +BallWidget::BallWidget( int _freq, int bFr, QWidget *parent, const char *name ) + : QWidget(parent,name), positions(MAX_POSITION), animations(MAX_ANIMATION) +{ + int i; + + for(i=0;i= MAX_ANIMATION) return; + + if (animations[no] !=0) + delete animations[no]; + + animations[no] = new BallAnimation(s,b1,b2); +} + + +/* X, Y are coordinates in a virtual 1000x1000 area */ +void BallWidget::createBallPosition(int no, int x, int y, Ball* def) +{ + if (no<0 || no>= MAX_POSITION) return; + + if (positions[no] !=0) + delete positions[no]; + + positions[no] = new BallPosition(x,y, def); +} + +void BallWidget::startAnimation(int pos, int anim, int type) +{ + BallPosition *p; + + if (pos<0 || pos>=MAX_POSITION || positions[pos]==0) return; + if (anim<0 || anim>=MAX_ANIMATION || animations[anim]==0) return; + + p = positions.at(pos); + p->actAnimation = animations.at(anim); + + /* One step *BEFORE* start */ + p->actStep = -1; + p->actDir = 1; + p->actType = type; + + if (!isRunning) { + isRunning = true; + timer->start( 0, true ); + } +} + +/* If LOOP: Set to ONESHOT, otherwise set to last frame */ +void BallWidget::stopAnimation(int pos) +{ + BallPosition *p; + + if (pos<0 || pos>=MAX_POSITION || positions[pos]==0) return; + + p = positions.at(pos); + if (p->actType == ANIMATION_STOPPED || + p->actAnimation == 0) return; + + if (p->actType == ANIMATION_LOOP || + p->actType == ANIMATION_CYCLE) { + p->actType = ANIMATION_FORWARD; + // return; + } + /* Set last step: animate() does the rest */ + p->actDir = 1; + p->actStep = p->actAnimation->steps; +} + +void BallWidget::resizeEvent(QResizeEvent *) +{ + int w = width() *10/12, h = height(); + + realSize = (w>h) ? h:w; + + Ball::setSize( realSize/ballFraction, realSize/ballFraction ); + repaint(); +} + +void BallWidget::paintEvent(QPaintEvent *) +{ + paint(this); +} + + +void BallWidget::paint(QPaintDevice *pd) +{ + int i; + BallPosition *p; + int xReal, yReal; + + int w = width(), h = height(); + + if (realSize<0) return; + + for(i=0;ix * realSize / 500 - Ball::w() )/2; + yReal = (h + p->y * realSize / 500 - Ball::h() )/2; + + if (p->actAnimation==0 || p->actStep==-1) { + if (p->def !=0 ) + bitBlt( pd, xReal, yReal, p->def->pixmap() ); + } + else { + int s = p->actStep; + if (s>= p->actAnimation->steps) + s = p->actAnimation->steps-1; + Ball* b = p->actAnimation->balls.at(s); + bitBlt( pd, xReal, yReal, b->pixmap() ); + } + } +} + +void BallWidget::animate() +{ + bool doAnimation = false; + + int i; + BallPosition *p; + int xReal, yReal; + int w = width(), h = height(); + + for(i=0;iactType == ANIMATION_STOPPED || + p->actAnimation ==0) continue; + + p->actStep += p->actDir; + if (p->actStep <= -1) { + p->actDir = 1; + p->actStep = 1; + doAnimation = true; + } + else if (p->actStep >= p->actAnimation->steps) { + if (p->actType == ANIMATION_CYCLE) { + p->actDir = -1; + p->actStep = p->actAnimation->steps -2; + doAnimation = true; + } + else if (p->actType == ANIMATION_LOOP) { + p->actStep = 1; /*skip first frame for smooth animation */ + doAnimation = true; + } + else { + p->actType = ANIMATION_STOPPED; + p->actAnimation = 0; + emit animationFinished(i); + } + } + else { + doAnimation = true; + } + + /* Update Pixmap */ + xReal = (w + p->x * realSize / 500 - Ball::w() )/2; + yReal = (h + p->y * realSize / 500 - Ball::h() )/2; + if (p->actAnimation==0 || p->actStep==-1) { + if (p->def !=0 ) + bitBlt( this, xReal, yReal, p->def->pixmap() ); + } + else { + int s = p->actStep; + if (s>= p->actAnimation->steps) + s = p->actAnimation->steps-1; + Ball* b = p->actAnimation->balls.at(s); + bitBlt( this, xReal, yReal, b->pixmap() ); + } + } + if (!doAnimation) { + isRunning = false; + emit animationsFinished(); + } + else { + timer->start(1000/freq,true); + } + + // repaint( false ); +} + + +/* Ball Test */ + + +BallTest::BallTest( QWidget *parent, const char *name ) + : BallWidget(10,2,parent,name) +{ + int w,h; + + w = h = 150; + resize(w,h); + // Ball::setSize( w/2, h/2, this ); + + Ball *b1 = new Ball( green ); + Ball *b2 = new Ball( yellow ); + Ball *b3 = new Ball( red ); + Ball *b4 = new Ball( red, 3.14/2 ); + + createBlending(0,5,b1,b2); + createBallPosition( 0,250, 250, b1); + + createBlending(1,10,b1,b3); + createBallPosition(1, 250, 750, b1); + + createBlending(2,15,b3,b2); + createBallPosition( 2, 750, 250, b3); + + createBlending(3,20,b3,b4); + createBallPosition(3, 750, 750, b3); +} + +/* +void BallTest::paintEvent( QPaintEvent * ) +{ + bitBlt(this,0,0, b.pixmap()); +} +*/ + +void BallTest::mousePressEvent( QMouseEvent * ) +{ + startAnimation(0,0, ANIMATION_CYCLE); + startAnimation(1,1); + startAnimation(2,2); + startAnimation(3,3, ANIMATION_LOOP); +} + +void BallTest::mouseReleaseEvent( QMouseEvent * ) +{ + stopAnimation(0); + stopAnimation(1); + stopAnimation(3); +} + +/* Test... + +#include + +int main(int argc, char *argv[]) +{ + zoom=.52; + flip=.85; + limit=.75; + + KApplication app(argc, argv, "BallTest"); + BallTest top; + + app.setMainWidget( &top ); + top.show(); + return app.exec(); +} + +*/ +#include "Ball.moc" diff --git a/kenolaba/Ball.h b/kenolaba/Ball.h new file mode 100644 index 00000000..1a3e0d28 --- /dev/null +++ b/kenolaba/Ball.h @@ -0,0 +1,155 @@ +/* Class Ball, BallWidget + * + * Online rendered balls with caching + animation widget + * + * Supported static effects + * - ball color + * - ripple texture + * + * Supported animation sequences for now: + * - Color Blending + * - Texture rotate + * + * April 1999, Josef Weidendorfer + */ + +#ifndef _BALL_H_ +#define _BALL_H_ + +#include +#include +#include +#include +#include + +/* textures for balls */ +#define TEX_FLAT 0 +#define TEX_RIPPLE 1 + +class Ball { + + public: + Ball(const QColor& c, double a = 0.0, int t=TEX_RIPPLE ); + ~Ball(); + + QPixmap* pixmap(); + + double angle() { return an; } + QColor ballColor() { return bColor; } + void setSpecials(double z, double f, double l) + { zoom = z, flip=f, limit=l; } + + static int w() { return sizeX; } + static int h() { return sizeY; } + static void setSize(int x,int y); + static void setLight(int x=5, int y=3, int z=10, + const QColor& c = QColor(200,230,255) ); + static void setTexture(double c=13., double d=.2); + + private: + + void render(); + static void invalidate(); + + //static QImage back; + static int sizeX, sizeY; + static double lightX, lightY, lightZ; + static QColor lightColor; + static double rippleCount, rippleDepth; + + QPixmap pm; + QColor bColor; + double an, sina, cosa; + double zoom, flip, limit; + int tex; + + Ball *next; + static Ball* first; +}; + + +class BallAnimation { + public: + BallAnimation(int s, Ball*, Ball*); + + int steps; + QPtrList balls; +}; + +#define ANIMATION_STOPPED 0 +#define ANIMATION_FORWARD 1 +#define ANIMATION_BACK 2 +#define ANIMATION_LOOP 3 +#define ANIMATION_CYCLE 4 + +class BallPosition { + public: + BallPosition(int xp,int yp, Ball* d); + + int x, y, actStep, actDir, actType; + Ball* def; + BallAnimation* actAnimation; +}; + +#define MAX_POSITION 130 +#define MAX_ANIMATION 20 + +class BallWidget : public QWidget +{ + Q_OBJECT + + public: + BallWidget(int _freq, int bFr, QWidget *parent = 0, const char *name = 0); + ~BallWidget(); + + void createBlending(int, int, Ball* , Ball* ); + void createBallPosition(int, int x, int y, Ball*); + + void startAnimation(int pos, int anim, int type=ANIMATION_FORWARD); + void stopAnimation(int pos); + + void paint(QPaintDevice *); + + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *); + + signals: + void animationFinished(int); + void animationsFinished(void); + + protected: + void drawBackground(); + + private slots: + void animate(); + + protected: + QMemArray positions; + QMemArray animations; + + private: + int freq; + int xStart, yStart, realSize, ballFraction; + bool isRunning; + QTimer *timer; +}; + + +/* Ball Test */ + +class BallTest: public BallWidget +{ + Q_OBJECT +public: + BallTest(QWidget *parent=0, const char *name=0 ); +protected: + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + + +}; + + + + +#endif // _BALL_H_ diff --git a/kenolaba/Board.cpp b/kenolaba/Board.cpp new file mode 100644 index 00000000..b8546fbc --- /dev/null +++ b/kenolaba/Board.cpp @@ -0,0 +1,1493 @@ +/* Class Board + * + * with methods for + * - play/take back moves + * - generate allowed moves + * - calculate rating for position + * - search for best move + * + * Josef Weidendorfer, 28.8.97 +*/ + +#include + +#include +#include + +#include +#include + +#include "Board.h" +#include "EvalScheme.h" + +// #define MYTRACE 1 + +#if 0 +#define CHECK(b) Q_ASSERT(b) +#else +#define CHECK(b) +#endif + + +static int ratedPositions, wonPositions, searchCalled, moveCount; +static int normalCount, pushCount, outCount, cutoffCount; + +/*********************** Class PrincipalVariation *************************/ + +void PrincipalVariation::clear(int d) +{ + int i,j; + + for(i=0;iactMaxDepth) return; + for(i=d+1;i<=actMaxDepth;i++) { + move[d][i]=move[d+1][i]; + move[d+1][i].type = Move::none; + } + move[d][d]=m; +} + + + +/****************************** Class Board ****************************/ + + +/* Board for start of a game */ +int Board::startBoard[]={ + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 10, 10, 10, 10, 10, + 10, 1, 1, 1, 1, 1, 1, 10, 10, 10, 10, + 10, 0, 0, 1, 1, 1, 0, 0, 10, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 10, 10, 10, 0, 0, 2, 2, 2, 0, 0, 10, + 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; + + +/* first centrum of board, then rings around (numbers are indexes) */ +int Board::order[]={ + 60, + 61,72,71,59,48,49, + 62,73,84,83,82,70,58,47,36,37,38,50, + 63,74,85,96,95,94,93,81,69,57,46,35,24,25,26,27,39,51, + 64,75,86,97,108,107,106,105,104,92,80,68,56,45,34,23,12, + 13,14,15,16,28,40,52 }; + +/* See EvalScheme.{h|cpp} + * + // Ratings for fields are calculated out of these values + // (see setFieldValues) + int Board::ringValue[] = { 45, 35, 25, 10, 0 }; + int Board::ringDiff[] = { 0, 10, 10, 8, 5 }; + + // Value added to board rating according to the difference of + // stones in game of player1 and player2 + int Board::stoneValue[]= { 0,-800,-1800,-3000,-4400,-6000 }; + + // Default Values for moves values (see Move.h) + int Board::moveValue[]= { 40,30,30, 15,14,13, 5,5,5, 2,2,2, 1 }; + + // Default Values for inARow values (see Move.h) + int Board::inARowValue[]= { 2, 5, 4, 3 }; +*/ + +int Board::fieldValue[61]; +int Board::direction[]= { -11,1,12,11,-1,-12,-11,1 }; + + +Board::Board() +{ + clear(); + breakOut = bUpdateSpy = false; + spyLevel = 1; + spyDepth = 0; + debug = 0; + realMaxDepth = 1; + _evalScheme = 0; +} + +void Board::setEvalScheme(EvalScheme* scheme) +{ + if (!scheme) + scheme = new EvalScheme( QString("Default") ); + + _evalScheme = scheme; + setFieldValues(); +} + +void Board::setFieldValues() +{ + if (!_evalScheme) return; + + int i, j = 0, k = 59; + int ringValue[5], ringDiff[5]; + + for(i=0;i<5;i++) { + ringDiff[i] = _evalScheme->ringDiff(i); + ringValue[i] = _evalScheme->ringValue(i); + if (ringDiff[i]<1) ringDiff[i]=1; + } + + fieldValue[0] = ringValue[0]; + for(i=1;i<7;i++) + fieldValue[i] = ringValue[1] + ((j+=k) % ringDiff[1]); + for(i=7;i<19;i++) + fieldValue[i] = ringValue[2] + ((j+=k) % ringDiff[2]); + for(i=19;i<37;i++) + fieldValue[i] = ringValue[3] + ((j+=k) % ringDiff[3]); + for(i=37;i<61;i++) + fieldValue[i] = ringValue[4] + ((j+=k) % ringDiff[4]); +} + + + +void Board::begin(int startColor) +{ + int i; + + for(i=0;i */ +void Board::generateFieldMoves(int startField, MoveList& list) +{ + int d, dir, c, actField; + bool left, right; + int opponent = (color == color1) ? color2 : color1; + + Q_ASSERT( field[startField] == color ); + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + /* (c .) */ + list.insert(startField, d, Move::move1); + continue; + } + if (c != color) + continue; + + /* 2nd == color */ + + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + /* 2 left */ + list.insert(startField, d, Move::left2); + } + + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + /* 2 right */ + list.insert(startField, d, Move::right2); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + list.insert(startField, d, Move::move2); + continue; + } + else if (c == opponent) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + list.insert(startField, d, Move::push1with2); + } + else if (c == out) { + /* (c c o |) */ + list.insert(startField, d, Move::out1with2); + } + continue; + } + if (c != color) + continue; + + /* 3nd == color */ + + if (left) { + if (field[actField+direction[d-1]] == free) + /* 3 left */ + list.insert(startField, d, Move::left3); + } + + if (right) { + if (field[actField+direction[d+1]] == free) + /* 3 right */ + list.insert(startField, d, Move::right3); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + list.insert(startField, d, Move::move3); + continue; + } + if (c != opponent) + continue; + + /* 4nd == opponent */ + + /* 5. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o .) */ + list.insert(startField, d, Move::push1with3); + continue; + } + else if (c == out) { + /* (c c c o |) */ + list.insert(startField, d, Move::out1with3); + continue; + } + if (c != opponent) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c o o .) */ + list.insert(startField, d, Move::push2); + } + else if (c == out) { + /* (c c c o o |) */ + list.insert(startField, d, Move::out2); + } + } +} + + +void Board::generateMoves(MoveList& list) +{ + int actField, f; + + for(f=0;f delete oldest entry */ + if (storedLast == storedFirst) + if (++storedFirst == MvsStored) storedFirst = 0; + + storedMove[storedLast] = m; + + f = m.field; + CHECK( (m.type >= 0) && (m.type < Move::none)); + CHECK( field[f] == color ); + field[f] = free; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::out1with3: /* (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = color; + break; + case Move::move3: /* (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == free ); + field[f + 3*dir] = color; + break; + case Move::out1with2: /* (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = color; + break; + case Move::move2: /* (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == free ); + field[f + 2*dir] = color; + break; + case Move::push2: /* (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == free ); + field[f + 3*dir] = color; + field[f + 5*dir] = opponent; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + CHECK( field[f + 2*dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::push1with3: /* (c c c o .) => (. c c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + CHECK( field[f + 4*dir] == free ); + field[f + 3*dir] = color; + field[f + 4*dir] = opponent; + break; + case Move::push1with2: /* (c c o .) => (. c c o) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == opponent ); + CHECK( field[f + 3*dir] == free ); + field[f + 2*dir] = color; + field[f + 3*dir] = opponent; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == color ); + CHECK( field[f + dir2] == free ); + CHECK( field[f + dir+dir2] == free ); + field[f+dir2] = color; + field[f+=dir] = free; + field[f+dir2] = color; + break; + case Move::move1: /* (c .) => (. c) */ + CHECK( field[f + dir] == free ); + field[f + dir] = color; + break; + default: + break; + } + + if (m.isOutMove()) { + if (color == color1) + color2Count--; + else + color1Count--; + } + + /* change actual color */ + color = opponent; + + CHECK( isConsistent() ); + +} + +bool Board::takeBack() +{ + int f, dir, dir2; + int opponent = color; + Move& m = storedMove[storedLast]; + + CHECK( isConsistent() ); + + if (storedFirst == storedLast) return false; + + /* change actual color */ + color = (color == color1) ? color2:color1; + + if (m.isOutMove()) { + if (color == color1) + color2Count++; + else + color1Count++; + } + + f = m.field; + CHECK( field[f] == free ); + field[f] = color; + dir = direction[m.direction]; + + switch(m.type) { + case Move::out2: /* (. c c c o |) => (c c c o o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::out1with3: /* (. c c c |) => (c c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == out ); + field[f + 3*dir] = opponent; + break; + case Move::move3: /* (. c c c) => (c c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + field[f + 3*dir] = free; + break; + case Move::out1with2: /* (. c c | ) => (c c o |) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == out ); + field[f + 2*dir] = opponent; + break; + case Move::move2: /* (. c c) => (c c .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + field[f + 2*dir] = free; + break; + case Move::push2: /* (. c c c o o) => (c c c o o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + CHECK( field[f + 5*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 5*dir] = free; + break; + case Move::left3: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right3: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + 2*dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + CHECK( field[f + 2*dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::push1with3: /* (. c c c o) => (c c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == color ); + CHECK( field[f + 4*dir] == opponent ); + field[f + 3*dir] = opponent; + field[f + 4*dir] = free; + break; + case Move::push1with2: /* (. c c o) => (c c o .) */ + CHECK( field[f + dir] == color ); + CHECK( field[f + 2*dir] == color ); + CHECK( field[f + 3*dir] == opponent ); + field[f + 2*dir] = opponent; + field[f + 3*dir] = free; + break; + case Move::left2: + dir2 = direction[m.direction-1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::right2: + dir2 = direction[m.direction+1]; + CHECK( field[f + dir] == free ); + CHECK( field[f + dir2] == color ); + CHECK( field[f + dir+dir2] == color ); + field[f+dir2] = free; + field[f+=dir] = color; + field[f+dir2] = free; + break; + case Move::move1: /* (. c) => (c .) */ + CHECK( field[f + dir] == color ); + field[f + dir] = free; + break; + default: + break; + } + + if (--storedLast < 0) storedLast = MvsStored-1; + + CHECK( isConsistent() ); + + return true; +} + +int Board::movesStored() +{ + int c = storedLast - storedFirst; + if (c<0) c+= MvsStored; + return c; +} + + +/** countFrom + * + * Used for board evaluation to count allowed move types and + * connectiveness. VERY similar to move generation. + * + * Returns number of found moves + */ +void Board::countFrom(int startField, int color, + MoveTypeCounter& TCounter, + InARowCounter& CCounter) +{ + int d, dir, c, actField, c2; + bool left, right; + + /* 6 directions */ + for(d=1;d<7;d++) { + dir = direction[d]; + + /* 2nd field */ + c = field[actField = startField+dir]; + if (c == free) { + TCounter.incr( Move::move1 ); + continue; + } + + if (c != color) + continue; + + /* 2nd == color */ + + CCounter.incr( InARowCounter::inARow2 ); + + /* left side move 2 */ + left = (field[startField+direction[d-1]] == free); + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left2 ); + } + + /* right side move 2 */ + right = (field[startField+direction[d+1]] == free); + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right2 ); + } + + /* 3rd field */ + c = field[actField += dir]; + if (c == free) { + /* (c c .) */ + TCounter.incr( Move::move2 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c o .) */ + TCounter.incr( Move::push1with2 ); + } + else if (c == out) { + /* (c c o |) */ + TCounter.incr( Move::out1with2 ); + } + continue; + } + + /* 3nd == color */ + + CCounter.incr( InARowCounter::inARow3 ); + + /* left side move 3 */ + if (left) { + left = (field[actField+direction[d-1]] == free); + if (left) + TCounter.incr( Move::left3 ); + } + + /* right side move 3 */ + if (right) { + right = (field[actField+direction[d+1]] == free); + if (right) + TCounter.incr( Move::right3 ); + } + + /* 4th field */ + c = field[actField += dir]; + if (c == free) { + /* (c c c .) */ + TCounter.incr( Move::move3 ); + continue; + } + else if (c == out) { + continue; + } + else if (c != color) { + + /* 4nd == opponent */ + + /* 5. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o .) */ + TCounter.incr( Move::push1with3 ); + continue; + } + else if (c2 == out) { + /* (c c c o |) */ + TCounter.incr( Move::out1with3 ); + continue; + } + if (c2 != c) + continue; + + /* 5nd == opponent */ + + /* 6. field */ + c2 = field[actField += dir]; + if (c2 == free) { + /* (c c c o o .) */ + TCounter.incr( Move::push2 ); + } + else if (c2 == out) { + /* (c c c o o |) */ + TCounter.incr( Move::out2 ); + } + + continue; + } + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow4 ); + + /* 5th field */ + c = field[actField += dir]; + if (c != color) + continue; + + /* 4nd == color */ + + CCounter.incr( InARowCounter::inARow5 ); + } +} + +/** indent + * + * Internal: for debugging output only + */ +void indent(int d) +{ + char tmp[]=" "; + tmp[d*3] = 0; + printf("> %s",tmp); +} + + + +/** validState + * + * Check for a valid board position to play from: + * (1) Number of balls for each color has to be between 9 and 14 + * (2) There must be a move possible for actual color + */ +int Board::validState() +{ + MoveTypeCounter tc; + InARowCounter cc; + + int c1 = 0, c2 = 0; + int i,j, moveCount, res; + + for(i=0;i8 && c1<15 && c2>8 && c2<15 && moveCount>0 ) + res = valid; + else + res = invalid; + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Valid: %s (Color1 %d, Color2 %d, moveCount of %d: %d)\n", + (res == empty) ? "empty" : (res==valid) ? "valid":"invalid", + c1,c2,color,moveCount); + } +#endif + + return res; +} + + + +/* Calculate a evaluation for actual position + * + * A higher value means a better position for opponent + * NB: This means a higher value for better position of + * 'color before last move' + */ +int Board::calcEvaluation() +{ + MoveTypeCounter tcColor, tcOpponent; + InARowCounter ccColor, ccOpponent; + + int f,i,j; + + /* different evaluation types */ + int fieldValueSum=0, stoneValueSum=0; + int moveValueSum=0, inARowValueSum=0; + int valueSum; + + /* First check simple winner condition */ + if (color1Count <9) + valueSum = (color==color1) ? 16000 : -16000; + else if (color2Count <9) + valueSum = (color==color2) ? 16000 : -16000; + else { + + /* Calculate fieldValueSum and count move types and connectivity */ + for(i=0;imoveValue(t) * + (tcOpponent.get(t) - tcColor.get(t)); + + for(int i=0;i < InARowCounter::inARowCount;i++) + inARowValueSum += _evalScheme->inARowValue(i) * + (ccOpponent.get(i) - ccColor.get(i)); + + if (color == color2) + stoneValueSum = _evalScheme->stoneValue(14 - color1Count) - + _evalScheme->stoneValue(14 - color2Count); + else + stoneValueSum = _evalScheme->stoneValue(14 - color2Count) - + _evalScheme->stoneValue(14 - color1Count); + + valueSum = fieldValueSum + moveValueSum + + inARowValueSum + stoneValueSum; + } + } + +#ifdef MYTRACE + if (spyLevel>2) { + indent(spyDepth); + printf("Eval %d (field %d, move %d, inARow %d, stone %d)\n", + valueSum, fieldValueSum, moveValueSum, + inARowValueSum, stoneValueSum ); + } +#endif + + return valueSum; +} + +bool Board::isConsistent() +{ + int c1 = 0, c2 = 0; + int i,j; + + for(i=0;i1) { + indent(depth); + printf("%s (%6d .. %6d) MaxType %s\n", depth, + (color==color1)?"O":"X", alpha,beta, + (depth < maxDepth-1) ? "Moving" : + (depth < maxDepth)? "Pushing" : "PushOUT" ); + } + */ +#endif + + /* check for a old best move in main combination */ + if (inPrincipalVariation) { + m = pv[depth]; + + if ((m.type != Move::none) && + (!list.isElement(m, 0, true))) + m.type = Move::none; + + if (m.type == Move::none) + inPrincipalVariation = false; + +#ifdef MYTRACE + else { + if (spyLevel>1) { + indent(spyDepth); + printf("Got from pv !\n" ); + } + } +#endif + } + + // first, play all moves with depth search + depthPhase = true; + + while (1) { + + // get next move + if (m.type == Move::none) { + if (depthPhase) + depthPhase = list.getNext(m, maxType); + if (!depthPhase) + if (!list.getNext(m, Move::none)) break; + } + // we could start with a non-depth move from principal variation + doDepthSearch = depthPhase && (m.type <= maxType); + +#ifdef MYTRACE + + if (m.isOutMove()) outCount++; + else if (m.isPushMove()) pushCount++; + else normalCount++; + + if (doDepthSearch) { + oldRatedPositions = ratedPositions; + oldWonPositions = wonPositions; + oldSearchCalled = searchCalled; + oldMoveCount = moveCount; + oldNormalCount = normalCount; + oldPushCount = pushCount; + oldOutCount = outCount; + oldCutoffCount = cutoffCount; + + if (spyLevel>1) { + indent(spyDepth); + printf("%s [%6d .. %6d] ", + (color==color1)?"O":"X", alpha,beta); + m.print(); + printf("\n"); + } + +#ifdef SPION + if (bUpdateSpy) emit update(depth, 0, m, false); +#endif + } +#endif + + playMove(m); + if (!isValid()) { + /* Possibility (1) to win: Ball Count <9 */ + value = 14999-depth; + // value = ((depth < maxDepth) ? 15999:14999) - depth; +#ifdef MYTRACE + wonPositions++; +#endif + } + else { + + if (doDepthSearch) { + /* opponent searches for his maximum; but we want the + * minimum: so change sign (for alpha/beta window too!) + */ + value = - search(depth+1,-beta,-alpha); + } + else { + ratedPositions++; + + value = calcEvaluation(); + } + } + takeBack(); + + /* For GUI response */ + if (doDepthSearch && (maxDepth - depth >2)) + emit searchBreak(); + +#ifdef MYTRACE + + if (doDepthSearch) { + spyDepth = depth; + + if (spyLevel>1) { + + indent(spyDepth); + if (oldSearchCalled < searchCalled) { + printf(" %d Calls", searchCalled-oldSearchCalled); + if (cutoffCount>oldCutoffCount) + printf(" (%d Cutoffs)", cutoffCount-oldCutoffCount); + printf(", GenMoves %d (%d/%d/%d played)", + moveCount - oldMoveCount, + normalCount - oldNormalCount, + pushCount-oldPushCount, + outCount-oldOutCount); + printf(", Rate# %d", + ratedPositions+wonPositions + - oldRatedPositions - oldWonPositions); + if (wonPositions > oldWonPositions) + printf(" (%d Won)", wonPositions- oldWonPositions); + printf("\n"); + indent(spyDepth); + } + + printf(" => Rated %d%s\n", + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + else { + if (spyLevel>2) { + indent(spyDepth); + printf("%s (%6d .. %6d) %-25s => Rating %6d%s\n", + (color==color1)?"O":"X", alpha,beta, + m.name().latin1(), + value, + (value>14900) ? ", WON !": + (value>=beta) ? ", CUTOFF !": + (value>actValue) ? ", Best !": ""); + } + } + + if (value>=beta) cutoffCount++; +#endif + +#ifdef SPION + if (bUpdateSpy) { + if (value > actValue) + emit updateBest(depth, value, m, value >= beta); + emit update(depth, value, m, true); + } +#endif + if (value > actValue) { + actValue = value; + pv.update(depth, m); + + // Only update best move if not stopping search + if (!breakOut && (depth == 0)) { + _bestMove = m; + + if (bUpdateSpy) { + emit updateBestMove(m, actValue); +#ifdef MYTRACE + if (spyLevel>0) { + int i; + printf("> New pv (Rating %d):", actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: %s", + i, pv[i].name().latin1() ); + } + printf("\n>\n"); + } +#endif + } + } + + if (actValue>14900 || actValue >= beta) + return actValue; + + /* maximize alpha */ + if (actValue > alpha) alpha = actValue; + } + + if (breakOut) depthPhase=false; + m.type = Move::none; + } + + return actValue; +} + + +Move& Board::bestMove() +{ + int alpha=-15000,beta=15000; + int nalpha,nbeta, actValue; + + if (!_evalScheme) { + // Use default values if not set + setEvalScheme(); + } + + pv.clear(realMaxDepth); + _bestMove.type = Move::none; + + maxDepth=1; + show = false; + breakOut = false; + spyDepth = 0; + + if (spyLevel>0) + printf("\n> New Search\n>\n"); + + /* iterative deepening loop */ + do { + if (spyLevel>0) + printf("> MaxDepth: %d\n>\n", maxDepth); + + /* searches on same level with different alpha/beta windows */ + while(1) { + if (spyLevel>0) + printf("> AB-Window: (%d ... %d)\n>\n", alpha, beta); + + nalpha=alpha, nbeta=beta; + inPrincipalVariation = (pv[0].type != Move::none); + + /* Statistics */ + searchCalled = 0; + moveCount = 0; + ratedPositions = 0; + wonPositions = 0; + normalCount = 0; + pushCount = 0; + outCount = 0; + cutoffCount = 0; + + actValue = search(0,alpha,beta); + + if (spyLevel>0) + { + int i; + if (spyLevel>1) + printf(">\n"); + printf("> Got PV with Rating %d:",actValue); + for(i=0;i<=maxDepth;i++) { + printf("\n> D %d: ", i); + pv[i].print(); + } + printf("\n>\n"); + + printf("> Search called : %6d / %d Cutoffs\n", + searchCalled, cutoffCount); + printf("> Moves generated : %6d / %d Played\n", + moveCount, normalCount+pushCount+outCount); + printf("> Nrml/Push/Out : %6d / %d / %d\n", + normalCount,pushCount,outCount); + printf("> Positions rated : %6d / %d Won\n>\n", + ratedPositions+wonPositions, wonPositions); + + } + + if (actValue > 14900 || actValue < -14900) + breakOut=true; + + /* Don't break out if we haven't found a move */ + if (_bestMove.type == Move::none) + breakOut=false; + + if (breakOut) break; + + // widen alpha-beta window if needed + if (actValue <= nalpha) { + alpha = -15000; + if (beta<15000) beta=actValue+1; + continue; + } + if (actValue >= nbeta) { + if (alpha > -15000) alpha = actValue-1; + beta=15000; + continue; + } + break; + } + + /* Window in both directions cause of deepening */ + alpha=actValue-200, beta=actValue+200; + + if (breakOut) break; + + maxDepth++; + } + while(maxDepth <= realMaxDepth); + + /* If Spy is On, we want replayable search: don't change rating! */ + if (spyLevel==0) + changeEvaluation(); + else { + printf(">>> Got Move : "); + pv[0].print(); + printf("\n\n"); + } + + spyDepth = 0; + + return _bestMove; +} + +Move Board::randomMove() +{ + Move m; + MoveList list; + + generateMoves(list); + int l = list.getLength(); + + int j = random.getLong(l) +1; + + while(j != 0) { + list.getNext(m, Move::none); + j--; + } + + return m; +} + + +void Board::print(int ) +{ + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + printf("\n -----------\n"); + for(row=0;row<4;row++) { + printf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) printf("%s",z[field[row*11+12+i]]); + printf("\\\n"); + } + printf(" | "); + for(i=0;i<9;i++) printf("%s",z[field[56+i]]); + printf("|\n"); + for(row=0;row<4;row++) { + printf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) printf("%s",z[field[68+row*12+i]]); + printf("/\n"); + } + printf(" ----------- O: %d X: %d\n", + color1Count, color2Count); +} + +QString Board::getASCIIState(int moveNo) +{ + QString state, tmp; + + int row,i; + char spaces[]=" "; + const char *z[]={". ","O ","X ", "o ", "x "}; + + state += tmp.sprintf("\n #%-3d ----------- O: %d X: %d\n", + moveNo, color1Count, color2Count); + for(row=0;row<4;row++) { + state += tmp.sprintf("%s/ ",spaces+row); + for(i=0;i<5+row;i++) + state += tmp.sprintf("%s",z[field[row*11+12+i]]); + state += "\\\n"; + } + state += " | "; + for(i=0;i<9;i++) + state += tmp.sprintf("%s",z[field[56+i]]); + state += "|\n"; + for(row=0;row<4;row++) { + state += tmp.sprintf("%s\\ ",spaces+3-row); + for(i=0;i<8-row;i++) + state += tmp.sprintf("%s",z[field[68+row*12+i]]); + state += "/\n"; + } + state += " -----------\n\n"; + + return state; +} + +int Board::setASCIIState(const QString& state) +{ + int moveNo=-1, index; + int len = state.length(); + int color1Count = 0; + int color2Count = 0; + + /* get moveNo if supplied */ + if ((index = state.find("#"))>=0) + moveNo = state.mid(index+1,3).toInt(); + + int f=12, row=0, rowEnd = 17; + char c = ' '; + + index=state.find("/"); + + while(index>=0) { + + while(++index= len) index=-1; + continue; + } + + if (f == rowEnd) { + row++; + if (row <4) { + index = state.find("/",index); + f = 12 + row*11; + rowEnd = row*12+17; + } + else if (row==4) { + index = state.find("|",index); + f = 56; + rowEnd = 65; + } + else if (row <9) { + index = state.find("\\",index); + f = 8 + row*12; + rowEnd = 21 + row*11; + } + else + break; + // printf("Row %d: %d - %d, Idx %d\n", row, f, rowEnd, index); + } + } + return moveNo; +} + + +QString Board::getState(int moveNo) +{ + QString state; + QString entry, tmp; + int i; + + /* Color + Counts */ + state += (char) ('A' + moveNo /25 ); + state += (char) ('A' + moveNo %25 ); + state += (char) ('A' + color1Count); + state += (char) ('A' + color2Count); + state += (char) ('A' + 4*color + field[order[0]]); + + /* Board (field values can be 0-3; 2 fields coded in one char */ + for(i=1;i<61;i+=2) + state+= (char) ('A' + 4*field[order[i]] + field[order[i+1]] ); + + /* -> 35 chars */ + return state; +} + +int Board::setState(QString& _state) +{ + int moveNo; + const char *state = _state.ascii(); + + if (_state.length() != 35) return 0; + + moveNo = 25*(state[0] - 'A') + (state[1] - 'A'); + color1Count = state[2] - 'A'; + color2Count = state[3] - 'A'; + color = (state[4] - 'A') / 4; + field[order[0]] = (state[4] - 'A') %4; + + int i = 1; + for(int j = 5; j<35; j++) { + int w = state[j] - 'A'; + field[order[i++]] = w/4; + field[order[i++]] = w % 4; + } + return moveNo; +} + +void Board::setSpyLevel(int level) +{ + spyLevel = level; +} +#include "Board.moc" diff --git a/kenolaba/Board.h b/kenolaba/Board.h new file mode 100644 index 00000000..61871d1f --- /dev/null +++ b/kenolaba/Board.h @@ -0,0 +1,198 @@ +/* Class Board - represents a game state + * + * Josef Weidendorfer, 28.8.97 +*/ + +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include +#include +#include "Move.h" + +class KConfig; +class EvalScheme; + +/* Class for best moves so far */ +class PrincipalVariation +{ +public: + PrincipalVariation() + { clear(1); } + + enum { maxDepth = 10 }; + + bool hasMove(int d) + { return (d>actMaxDepth) ? + false : (move[0][d].type != Move::none); } + + Move& operator[](int i) + { return (i<0 || i>=maxDepth) ? move[0][0] : move[0][i]; } + + void update(int d, Move& m); + void clear(int d); + void setMaxDepth(int d) + { actMaxDepth = (d>maxDepth) ? maxDepth-1 : d; } + +private: + Move move[maxDepth][maxDepth]; + int actMaxDepth; + +}; + + +class Board : public QObject +{ + Q_OBJECT + + public: + Board(); + ~Board() {} + + /* different states of one field */ + enum { + out = 10, free = 0, + color1, color2, color1bright, color2bright + }; + enum { AllFields = 121, /* visible + ring of unvisible around */ + RealFields = 61, /* number of visible fields */ + MvsStored = 100 }; + + int debug; + + /* fill Board with defined values */ + void begin(int startColor); /* start of a game */ + void clear(); /* empty board */ + + /* fields can't be changed ! */ + int operator[](int no) const; + + int actColor() const + { return color; } + + /* Generate list of allowed moves for player with + * Returns a calculated value for actual position */ + void generateMoves(MoveList& list); + + /* Functions handling moves + * played moves can be taken back ( moves are remembered) */ + void playMove(const Move& m); + bool takeBack(); /* if not remembered, do nothing */ + int movesStored(); /* return how many moves are remembered */ + + Move& lastMove() + { return storedMove[storedLast]; } + + void showHist(); + + /* Evaluation Scheme to use */ + void setEvalScheme( EvalScheme* scheme = 0); + EvalScheme* evalScheme() { return _evalScheme; } + + /* Calculate a value for actual position + * (greater if better for color1) */ + int calcEvaluation(); + + /* Evalution is based on values which can be changed + * a little (so computer's moves aren't always the same) */ + void changeEvaluation(); + + void setActColor(int c) { color=c; } + void setColor1Count(int c) { color1Count = c; } + void setColor2Count(int c) { color2Count = c; } + void setField(int i, int v) { field[i] = v; } + + void setSpyLevel(int); + + int getColor1Count() { return color1Count; } + int getColor2Count() { return color2Count; } + + enum { empty=0, valid, invalid }; + int validState(); + bool isValid() { return (color1Count>8 && color2Count>8); } + + /* Check that color1Count & color2Count is consisten with board */ + bool isConsistent(); + + /* Searching best move: alpha/beta search */ + void setDepth(int d) + { realMaxDepth = d+1; } + Move& bestMove(); + + /* next move in main combination */ + Move& nextMove() { return pv[1]; } + + Move randomMove(); + void stopSearch() { breakOut = true; } + + /* Compressed ASCII representation */ + QString getState(int); + int setState(QString&); + + /* Readable ASCII representation */ + QString getASCIIState(int); + int setASCIIState(const QString&); + + void updateSpy(bool b) { bUpdateSpy = b; } + + /* simple terminal view of position */ + void print(int); + + static int fieldDiffOfDir(int d) { return direction[d]; } + + signals: + void searchBreak(); + void updateBestMove(Move&,int); + + void update(int,int,Move&,bool); + void updateBest(int,int,Move&,bool); + + private: + void setFieldValues(); + + /* helper function for generateMoves */ + void generateFieldMoves(int, MoveList&); + /* helper function for calcValue */ + void countFrom(int,int, MoveTypeCounter&, InARowCounter&); + /* helper functions for bestMove (recursive search!) */ + int search(int, int, int); + int search2(int, int, int); + + KRandomSequence random; /* random generator */ + + int field[AllFields]; /* actual board */ + int color1Count, color2Count; + int color; /* actual color */ + Move storedMove[MvsStored]; /* stored moves */ + int storedFirst, storedLast; /* stored in ring puffer manner */ + + /* for search */ + PrincipalVariation pv; + Move _bestMove; + bool breakOut, inPrincipalVariation, show, bUpdateSpy; + int maxDepth, realMaxDepth; + + int spyLevel, spyDepth; + EvalScheme* _evalScheme; + + /* ratings; semi constant - are rotated by changeRating() */ + static int fieldValue[RealFields]; + + /* constant arrays */ + static int startBoard[AllFields]; + static int order[RealFields]; + static int direction[8]; + + // static int stoneValue[6]; + // static int moveValue[Move::typeCount]; + // static int connectValue[ConnectCounter::connectCount]; + // static int ringValue[5], ringDiff[5]; +}; + + +inline int Board::operator[](int no) const +{ + return (no<12 || no>120) ? out : field[no]; +} + +#endif diff --git a/kenolaba/BoardWidget.cpp b/kenolaba/BoardWidget.cpp new file mode 100644 index 00000000..9d420cc3 --- /dev/null +++ b/kenolaba/BoardWidget.cpp @@ -0,0 +1,1027 @@ +/* Implementation of class BoardWidget + * + * This class handles rendering of a Board to a KDE/QT widget, + * shows moves (with a timer) and manages input of moves + * + * Josef Weidendorfer, 9/97 + */ + +#include +#include +#include + +#include +#include + +#ifdef HAVE_KIR +#include +#endif + +#include "Board.h" +#include "BoardWidget.h" + +/* Cursors */ +#include "bitmaps/Arrow1" +#include "bitmaps/Arrow1Mask" +#include "bitmaps/Arrow2" +#include "bitmaps/Arrow2Mask" +#include "bitmaps/Arrow3" +#include "bitmaps/Arrow3Mask" +#include "bitmaps/Arrow4" +#include "bitmaps/Arrow4Mask" +#include "bitmaps/Arrow5" +#include "bitmaps/Arrow5Mask" +#include "bitmaps/Arrow6" +#include "bitmaps/Arrow6Mask" + +BoardWidget::BoardWidget(Board& b, QWidget *parent, const char *name) + : BallWidget(10,9,parent, name), board(b) +{ + pList =0; + gettingMove = false; + editMode = false; + renderMode = false; + +#ifdef HAVE_KIR + m_backRenderer = KIRManager::attach( this, "Background" ); + connect( m_backRenderer, SIGNAL(rendered()), + this, SLOT(drawBoard()) ); +#endif + + /* setup cursors */ + +#define createCursor(bitmap,name) \ + static QBitmap bitmap(bitmap##_width, bitmap##_height, \ + (unsigned char *) bitmap##_bits, TRUE); \ + static QBitmap bitmap##Mask(bitmap##Mask_width, bitmap##Mask_height, \ + (unsigned char *) bitmap##Mask_bits, TRUE); \ + name = new QCursor(bitmap, bitmap##Mask, bitmap##_x_hot, bitmap##_y_hot); + + createCursor(Arrow1, arrow[1]); + createCursor(Arrow2, arrow[2]); + createCursor(Arrow3, arrow[3]); + createCursor(Arrow4, arrow[4]); + createCursor(Arrow5, arrow[5]); + createCursor(Arrow6, arrow[6]); + + setCursor(crossCursor); + + // boardColor = new QColor("lightblue"); + boardColor = new QColor(backgroundColor()); + redColor = new QColor("red2"); + yellowColor = new QColor("yellow2"); + redHColor = new QColor("orange"); + yellowHColor = new QColor("green"); + + initBalls(); + + updatePosition(); +} + +BoardWidget::~BoardWidget() +{ + for(int i=1; i<7; i++) + if (arrow[i] != 0) + delete arrow[i]; + +#ifdef HAVE_KIR + if (m_backRenderer != 0) + delete m_backRenderer; +#endif + delete boardColor; + delete redColor; + delete yellowColor; + delete redHColor; + delete yellowHColor; + +} + +void BoardWidget::configure() +{ +#ifdef HAVE_KIR + if (m_backRenderer != 0) { + m_backRenderer->setup(); + m_backRenderer->manager()->saveModules(); + } +#endif +} + + +void BoardWidget::createPos(int pos, int i, int j, Ball* b) +{ + int x=(465*(2*(i)-(j))/9); + int y=(500*19*(j)/100); + createBallPosition(pos, x,y, b); +} + +void BoardWidget::initBalls() +{ + n2 = new Ball( *yellowColor ); + h2 = new Ball( *yellowHColor ); + d2 = new Ball( *yellowHColor, 3.14/2 ); + + n1 = new Ball( *redColor ); + h1 = new Ball( *redHColor ); + d1 = new Ball( *redHColor, 3.14/2 ); + + // e = new Ball( white,0,0 ); + // e->setSpecials(.6,.85,.75); + + createBlending(1,10,h1,n1); + createBlending(2,10,h1,d1); + createBlending(3,10,h2,n2); + createBlending(4,10,h2,d2); + + int i,j,pos; + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + pos=60+j*11+i; + createPos(pos, i,j, 0); + } + + pos = 0; + /* the outer marks of color1 */ + for(i=0;i<3;i++) createPos(pos++, -6, i-4, 0 ); + for(i=0;i<3;i++) createPos(pos++, 2+i, i-4, 0 ); + + /* the outer marks of color2 */ + for(i=0;i<3;i++) createPos(pos++, 6, 4-i, 0 ); + for(i=0;i<3;i++) createPos(pos++, -2-i, 4-i, 0 ); +} + +void BoardWidget::resizeEvent(QResizeEvent *e) +{ + drawBoard(); + BallWidget::resizeEvent(e); +} + +void BoardWidget::moveEvent(QMoveEvent*) +{ + drawBoard(); +} + +void BoardWidget::paintEvent(QPaintEvent *) +{ + if (renderMode) { + pm = boardPM; + BallWidget::paint(&pm); + } + else + draw(); + bitBlt(this, 0, 0, &pm); +} + + +void drawShadedHexagon(QPainter *p, int x, int y, int r, int lineWidth, + const QColorGroup& g, bool sunken) +{ + int dx=r/2, dy=(r*87)/100; + int y1=y-dy, y2=y+dy; + int i; + + QPen oldPen = p->pen(); + + p->setPen(sunken ? g.midlight() : g.dark()); + + for(i=0; idrawLine( x-i+dx, y-dy, x+2*dx-i, y); + p->drawLine( x+2*dx-i, y, x-i+dx, y+dy); + p->drawLine( x-i+dx, y1+i, x+i-dx, y1+i); + } + + p->setPen(sunken ? g.dark() : g.midlight()); + + for(i=0; idrawLine( x+i-dx, y-dy, x+i-2*dx, y); + p->drawLine( x+i-2*dx, y, x+i-dx, y+dy); + p->drawLine( x-i+dx, y2-i, x+i-dx, y2-i); + } + + p->setPen(oldPen); +} + + +void drawColor(QPainter *p, int x, int y, int r, QColor* c) +{ + QColor w("white"); + QPalette pal(*c); + + QPen oldPen = p->pen(); + QBrush oldBrush = p->brush(); + + p->setBrush(pal.active().dark()); + p->setPen(pal.active().dark()); + p->drawEllipse( x-r - 10,y-r +5, 2*r,2*r); + + p->setBrush(pal.active().mid()); + p->setPen(pal.active().mid()); + p->drawEllipse( x-r,y-r, 2*r,2*r); + + p->setBrush(pal.active().light()); + p->setPen(pal.active().light()); + p->drawEllipse( x-r/3, y-r/3, 4*r/3,4*r/3); + + p->setBrush(w); + p->setPen(w); + p->drawEllipse( x+r/3, y+r/3, r/3,r/3); + + p->setPen(oldPen); + p->setBrush(oldBrush); +} + + +void BoardWidget::drawBoard() +{ + boardPM.resize(width(), height()); + boardPM.fill(this, 0,0); + +#ifndef HAVE_KIR + QColorGroup g = QPalette( *boardColor ).active(); + QColorGroup g2 = QWidget::colorGroup(); + + int boardSize = width() *10/12; + if (boardSize > height()) boardSize = height(); + + QPainter p; + p.begin(&boardPM); + p.setBrush(g2.brush(QColorGroup::Mid)); + + QWMatrix m; + QPoint cp = rect().center(); + m.translate(cp.x(), cp.y()); + m.scale(boardSize/1100.0, boardSize/1000.0); + + m.rotate(0); + + p.setWorldMatrix(m); + + /* draw field */ + + int i,j; + + QPointArray a; + int dx=520 /2, dy=(520 *87)/100; + a.setPoints(6, -dx,-dy, dx,-dy, 2*dx,0, dx,dy, -dx,dy, -2*dx,0 ); + p.drawPolygon(a); + + drawShadedHexagon(&p, 0,0, 505, 1, g, false); + drawShadedHexagon(&p, 0,0, 512, 3, g, true); + drawShadedHexagon(&p, 0,0, 525, 5, g2, true); + +#define xpos(i,j) (495*(2*(i)-(j))/9) +#define ypos(j) (500*19*(j)/100) + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int x=xpos(i,j); + int y=ypos(j); + + drawShadedHexagon(&p, x,y, 50, 2, g, true); + drawShadedHexagon(&p, x,y, 30, 1, g, false); + } + p.end(); +#endif + draw(); +} + +void BoardWidget::renderBalls(bool r) +{ + renderMode=r; + draw(); +} + +void BoardWidget::updateBalls() +{ + int i,j; + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int pos = 60+j*11+i; + int w=field[60+j*11+i]; + + if (w==Board::color1) { + if (positions[pos]->def != n1) { + positions[pos]->def= n1; + startAnimation(pos,1, ANIMATION_FORWARD); + } + else { + stopAnimation(pos); + } + } + else if (w==Board::color1bright) + startAnimation(pos,2,ANIMATION_LOOP); + else if (w==Board::color2) { + if (positions[pos]->def != n2) { + positions[pos]->def= n2; + startAnimation(pos,3,ANIMATION_FORWARD); + } + else { + stopAnimation(pos); + } + } + else if (w==Board::color2bright) + startAnimation(pos,4,ANIMATION_LOOP); + else if (w==Board::free) { + positions[pos]->def= 0; + positions[pos]->actAnimation = 0; + // stopAnimation(pos); + } + } + + for(i=0;i<6;i++) + positions[i]->def= ((14-color1Count)>i && color1Count>0) ? n1:0; + + for(i=6;i<12;i++) + positions[i]->def= ((14-color2Count)>i-6 && color2Count>0) ? n2:0; + +} + +void BoardWidget::draw() +{ + if (boardPM.isNull()) + return; + + pm = boardPM; + + if (renderMode) { + updateBalls(); + repaint(false); + return; + } + + int boardSize = width() *10/12; + if (boardSize > height()) boardSize = height(); + + QPainter p; + p.begin(&pm); + p.setBrush(foregroundColor()); + + QWMatrix m; + QPoint cp = rect().center(); + m.translate(cp.x(), cp.y()); + m.scale(boardSize/1100.0, boardSize/1000.0); + + m.rotate(0); + + p.setWorldMatrix(m); + + /* draw fields */ + + int i,j; + + for(j=-4;j<5;j++) + for(i= ((j>0)?j-4:-4) ; i< ((j<0)?5+j:5) ;i++) { + int x=xpos(i,j); + int y=ypos(j); + int w=field[60+j*11+i]; + + if (w==Board::color1) + drawColor(&p, x,y, 35, redColor ); + else if (w==Board::color1bright) + drawColor(&p, x,y, 35, redHColor ); + else if (w==Board::color2) + drawColor(&p, x,y, 35, yellowColor ); + else if (w==Board::color2bright) + drawColor(&p, x,y, 35, yellowHColor ); + } + + if (color1Count >0) { + /* the outer marks of color1 */ + if (color1Count <12) { + for(i=11; i>8 && i>color1Count ;i--) + drawColor(&p, xpos(12-i,7-i)+55, ypos(7-i), 35, redColor ); + } + for(i=14; i>11 && i>color1Count ;i--) + drawColor(&p, xpos(-6,10-i)+55, ypos(10-i), 35, redColor ); + + /* the outer marks of color2 */ + if (color2Count <12) { + for(i=11; i>8 && i>color2Count ;i--) + drawColor(&p, xpos(i-12,i-7)-55, ypos(i-7), 35, yellowColor); + } + for(i=14; i>11 && i>color2Count ;i--) + drawColor(&p, xpos(6,i-10)-55, ypos(i-10), 35, yellowColor); + } + p.end(); + bitBlt(this, 0, 0, &pm); +} + +/** updatePosition + * + * Update my position with that of the member. + * If is true, draw widget + */ +void BoardWidget::updatePosition(bool updateGUI) +{ + for(int i=0; i2) ? Board::color1bright:Board::free; + opponentNew = (step<2) ? Board::color2 : Board::color2bright; + } + else { + colorNew = (step<2) ? Board::color2 : + (step>2) ? Board::color2bright:Board::free; + opponentNew = (step<2) ? Board::color1 : Board::color1bright; + } + + afterMove = (step == 1) || (step == 4); + + f = m.field; + dir = Board::fieldDiffOfDir(m.direction); + + /* first field */ + field[f] = afterMove ? Board::free : colorNew; + + switch(m.type) { + case Move::out2: /* (c c c o o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = opponentNew; + break; + case Move::out1with3: /* (c c c o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + break; + case Move::move3: /* (c c c .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : Board::free; + break; + case Move::out1with2: /* (c c o |) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : opponentNew; + break; + case Move::move2: /* (c c .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : Board::free; + break; + case Move::push2: /* (c c c o o .) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = opponentNew; + field[f + 5*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::left3: + dir2 = Board::fieldDiffOfDir(m.direction-1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::right3: + dir2 = Board::fieldDiffOfDir(m.direction+1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::push1with3: /* (c c c o .) => (. c c c o) */ + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + field[f + 3*dir] = afterMove ? colorNew : opponentNew; + field[f + 4*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::push1with2: /* (c c o .) => (. c c o) */ + field[f + dir] = colorNew; + field[f + 2*dir] = afterMove ? colorNew : opponentNew; + field[f + 3*dir] = afterMove ? opponentNew : Board::free; + break; + case Move::left2: + dir2 = Board::fieldDiffOfDir(m.direction-1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::right2: + dir2 = Board::fieldDiffOfDir(m.direction+1); + field[f+dir2] = afterMove ? colorNew : Board::free; + field[f+=dir] = afterMove ? Board::free : colorNew; + field[f+dir2] = afterMove ? colorNew : Board::free; + break; + case Move::move1: /* (c .) => (. c) */ + field[f + dir] = afterMove ? colorNew : Board::free; + break; + default: + break; + } + + if (updateGUI) + draw(); +} + +void BoardWidget::showStart(const Move& m, int step, bool updateGUI) +{ + int f, dir; + int colorNew; + + if (boardOK) { + /* board ok means: board has the normal state before move */ + if (step == 0) + return; /* nothing to be done */ + } + boardOK = (step == 0) ? true:false; + + if (color == Board::color1) + colorNew = (step==0) ? Board::color1 : Board::color1bright; + else + colorNew = (step==0) ? Board::color2 : Board::color2bright; + + f = m.field; + + /* first field */ + field[f] = colorNew; + + switch(m.type) { + case Move::left3: + case Move::right3: + dir = Board::fieldDiffOfDir(m.direction); + field[f + dir] = colorNew; + field[f + 2*dir] = colorNew; + break; + case Move::left2: + case Move::right2: + dir = Board::fieldDiffOfDir(m.direction); + field[f + dir] = colorNew; + default: + break; + } + + if (updateGUI) + draw(); +} + + +void BoardWidget::choseMove(MoveList *pl) +{ + if (!gettingMove && pl != 0) { + pList = pl; + gettingMove = true; + mbDown = false; + actValue = - board.calcEvaluation(); + kdDebug(12011) << "Chose Move..." << endl; + } +} + + +/* returns position of point (xx,yy) + */ +int BoardWidget::positionOf(int xx, int yy) +{ + int boardSize = QMIN( width()*10/12, height() ); + int x = (1000 * (xx- (width()-boardSize)/2)) / boardSize; + int y = (1000 * (yy- (height()-boardSize)/2)) / boardSize; + + /* + kdDebug(12011) << "(" << xx << "," << yy << ") -> (" + << x << "," << y << ")" << endl; + */ + + y = (y-2)/47; + if (y < 2 || y > 18) return 0; + x= ((x+25)/25+ (y-10) )/2; + if (y < 10 && ((x < 2) || (x > 8+y) )) return 0; + if (y >= 10 && ((x < y-8) || (x > 18) )) return 0; + + return 22*y + x; +} + + +bool isBetweenFields(int pos) +{ + bool res = ( ((pos%2) == 1) || ( ((pos/22)%2) == 1) ); + // kdDebug(12011) << "Pos " << pos << " is between fields: " << res << endl; + return res; +} + +int fieldOf(int pos) +{ + int f = 11*(pos/44) + (pos%22)/2; + // kdDebug(12011) << "Field1 of pos " << pos << " is " << f << endl; + return f; +} + +int field2Of(int pos) +{ + int y = pos/22, x = pos%22; + int f2 = 0, f1 = 11*(y/2) + (x/2); + + if ( (y%2) == 0) { + /* exact on row */ + if ( (x%2) != 0) { + /* horizontial between fields */ + f2 = f1+1; + } + } + else { + /* vertical between fields */ + f2 = f1 + ( ((x%2)==0) ? 11:12 ); + } + + // kdDebug(12011) << "Field2 of pos " << pos << " is " << f2 << endl; + return f2; +} + + +/* get direction depending on difference of positions */ +int directionOfPosDiff(int d) +{ + if (d>0) { + return ((d<21) ? Move::Right : + ((d%22) == 0) ? Move::LeftDown : + ((d%23) == 0) ? Move::RightDown : 0); + } + else if (d<0) { + return ((d>-21) ? Move::Left : + ((d%23) == 0) ? Move::LeftUp : + ((d%22) == 0) ? Move::RightUp : 0); + } + return 0; +} + +int directionOfFieldDiff(int d) +{ + if (d>0) { + return ((d<10) ? Move::Right : + ((d%11) == 0) ? Move::LeftDown : + ((d%12) == 0) ? Move::RightDown : 0); + } + else if (d<0) { + return ((d>-10) ? Move::Left : + ((d%12) == 0) ? Move::LeftUp : + ((d%11) == 0) ? Move::RightUp : 0); + } + return 0; +} + +/* Check if is a valid start position for a allowed move + * If yes, set + * , and + */ +bool BoardWidget::isValidStart(int pos, bool midPressed) +{ + bool res = false; + int f1 = fieldOf(pos); + + startField = f1; + + if (isBetweenFields(pos)) { + int f2 = field2Of(pos); + + actMove = Move(f1, directionOfFieldDiff( f2-f1 ), Move::none); + res = pList->isElement(actMove, MoveList::start2); + if (!res) { + startField = f2; + actMove = Move(f2, directionOfFieldDiff( f1-f2 ), Move::none); + res = pList->isElement(actMove, MoveList::start2); + } + startType = MoveList::start2; + return res; + } + + if (midPressed) { + startType = MoveList::start3; + + /* Check all 6 directions */ + for(int dir=1;dir<7;dir++) { + actMove = Move(f1 - Board::fieldDiffOfDir(dir), dir, Move::none ); + if (pList->isElement(actMove, startType)) + return true; + } + /* if we don't find a side move3 fall trough to normal moves... */ + } + + startType = MoveList::start1; + actMove = Move(f1, 0, Move::none); + res = pList->isElement(actMove, startType); + + return res; +} + + +/* Check if is a valid end position for a move + * regarding + * If yes, set + */ +bool BoardWidget::isValidEnd(int pos) +{ + int dir = directionOfPosDiff(pos - startPos); + Move m; + + if (dir == 0) return false; + + switch(startType) { + case MoveList::start1: + m = Move(startField, dir, Move::none); + if (!pList->isElement(m, startType)) + return false; + break; + + case MoveList::start2: + { + int f1 = fieldOf(startPos); + int f2 = field2Of(startPos); + int dir2 = directionOfFieldDiff( f2-f1 ); + int dir3 = directionOfFieldDiff( f1-f2 ); + + switch((dir2-dir+6)%6) { + case 1: + m = Move(f1, dir2, Move::left2); + break; + case 2: + m = Move(f2, dir3, Move::right2); + break; + case 4: + m = Move(f2, dir3, Move::left2); + break; + case 5: + m = Move(f1, dir2, Move::right2); + break; + default: + return false; + } + if (!pList->isElement(m, startType)) + return false; + + break; + } + case MoveList::start3: + { + int rightDir = (dir%6)+1; + m = Move( startField - Board::fieldDiffOfDir(rightDir), rightDir, Move::left3 ); + if (!pList->isElement(m, startType)) { + int leftDir = ((dir-2)%6)+1; + m = Move( startField - Board::fieldDiffOfDir(leftDir), leftDir, Move::right3 ); + if (!pList->isElement(m, startType)) + return false; + } + } + break; + } + + actMove = m; + shownDirection = dir; + return true; +} + + + +void BoardWidget::mousePressEvent( QMouseEvent* pEvent ) +{ + int pos = positionOf( pEvent->x(), pEvent->y() ); + int f = fieldOf(pos); + + if (pEvent->button() == RightButton) { + emit rightButtonPressed(f, pEvent->globalPos()); + return; + } + + if (!gettingMove && !editMode) { + return; + } + mbDown = true; + + + if (editMode) { + editColor = (pEvent->button() == MidButton) ? + Board::color2 : Board::color1; + int newColor = (pEvent->button() == MidButton) ? + Board::color2bright : Board::color1bright; + + if (field[f] == Board::free) { + field[f] = newColor; + } + else if (field[f] == Board::color1) { + if (editColor == Board::color1) { + editColor = Board::free; + newColor = Board::color1bright; + } + field[f] = newColor; + } + else if (field[f] == Board::color2) { + if (editColor == Board::color2) { + editColor = Board::free; + newColor = Board::color2bright; + } + field[f] = newColor; + } + else { + editColor = Board::out; + } + + oldPos = pos; + draw(); + return; + } + + startValid = isValidStart(pos, (pEvent->button() == MidButton)); + kdDebug(12011) << "Start pos " << pos << " is valid: " << startValid << endl; + // actMove.print(); + + if (!startValid) return; + startPos = oldPos = pos; + + showStart(actMove,1); + startShown = true; + + QString tmp; + actValue = - board.calcEvaluation(); + tmp = i18n("Board value: %1").arg(actValue); + emit updateSpy(tmp); +} + + +void BoardWidget::mouseMoveEvent( QMouseEvent* pEvent ) +{ + if ((!gettingMove && !editMode) || !mbDown) return; + + int pos = positionOf( pEvent->x(), pEvent->y() ); + if (pos == oldPos) return; + oldPos = pos; + + if (editMode) { + int f = fieldOf(pos); + if (field[f] != Board::out && field[f] != editColor) { + int newColor = (editColor == Board::color1) ? Board::color1bright : + (editColor == Board::color2) ? Board::color2bright : + (field[f] == Board::color1) ? Board::color1bright : + (field[f] == Board::color2) ? Board::color2bright : field[f]; + field[f] = newColor; + draw(); + } + return; + } + + if (!startValid) { + /* We haven't a valid move yet. Check if we are over a valid start */ + + startValid = isValidStart(pos, (pEvent->button() == MidButton)); + kdDebug(12011) << "Start pos " << pos << " is valid: " << startValid << endl; + // actMove.print(); + + if (!startValid) return; + startPos = oldPos = pos; + + showStart(actMove,1); + startShown = true; + + QString tmp; + actValue = - board.calcEvaluation(); + tmp = i18n("Board value: %1").arg(actValue); + emit updateSpy(tmp); + return; + } + + /* restore board */ + updatePosition(); + startShown = false; + + if (isValidEnd(pos)) { + // actMove.print(); + + board.playMove(actMove); + int v = board.calcEvaluation(); + board.takeBack(); + + QString tmp; + tmp.sprintf("%+d", v-actValue); + QString str = QString("%1 : %2").arg(actMove.name()).arg(tmp); + emit updateSpy(str); + + showMove(actMove,3); + setCursor(*arrow[shownDirection]); + } + else { + QString tmp; + + setCursor(crossCursor); + if (pos == startPos) { + showStart(actMove,1); + startShown = true; + tmp = i18n("Board value: %1").arg(actValue); + } + else + draw(); + emit updateSpy(tmp); + } +} + + +void BoardWidget::mouseReleaseEvent( QMouseEvent* pEvent ) +{ + if (!gettingMove && !editMode) return; + mbDown = false; + + if (editMode) { + int i; + + // printf("Releasing..."); + for(i=0; ix(), pEvent->y() ); + if (isValidEnd(pos)) { + // actMove.print(); + startValid = false; + setCursor(crossCursor); + gettingMove = false; + emit moveChoosen(actMove); + return; + } + + updatePosition(true); + startValid = false; + setCursor(crossCursor); + + QString tmp; + emit updateSpy(tmp); +} + +QSize BoardWidget::sizeHint() const +{ + return QSize(400, 350); +} + +#include "BoardWidget.moc" diff --git a/kenolaba/BoardWidget.h b/kenolaba/BoardWidget.h new file mode 100644 index 00000000..8fc0a317 --- /dev/null +++ b/kenolaba/BoardWidget.h @@ -0,0 +1,116 @@ +#ifndef _BOARDWIDGET_H_ +#define _BOARDWIDGET_H_ + +#include +#include + +#include "Ball.h" +#include "Move.h" +#include "Board.h" + +#ifdef HAVE_KIR +class KIRenderer; +#endif + +class BoardWidget : public BallWidget +{ + Q_OBJECT + + public: + BoardWidget(Board&, QWidget *parent = 0, const char *name = 0); + ~BoardWidget(); + + void createPos(int , int , int , Ball*); + void initBalls(); + void updateBalls(); + + virtual void resizeEvent(QResizeEvent *); + virtual void moveEvent(QMoveEvent *); + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent( QMouseEvent* pEvent ); + virtual void mouseReleaseEvent( QMouseEvent* pEvent ); + virtual void mouseMoveEvent( QMouseEvent* pEvent ); + + void renderBalls(bool r); + + void draw(); + + void choseMove(MoveList*); + + /* Show a move with highlighting + * step 0: state before move + * 1: state after move + * 2: remove all marks (for blinking) + * 3: highlight marks (before move) + * 4: highlight marks (after move) + * Always call with step 0 before actual playing the move !! */ + void showMove(const Move& m, int step, bool updateGUI = true); + + /* Only highlight start + * Step 0: original state + * 1: highlight start + * Always call with step 0 before actual playing the move !! */ + void showStart(const Move& m, int step, bool updateGUI = true); + + + /* enable/disable Edit Mode: + * On disabling & valid position it's actually copied to Board + */ + bool setEditMode(bool); + // int validState() { return board->validState(); } + // bool isValid() { return validState()==Board::valid; } + + /* copy actual board position */ + void updatePosition(bool updateGUI = false); + void clearPosition(); + + int getColor1Count() { return color1Count; } + int getColor2Count() { return color2Count; } + + public slots: + void configure(); + void drawBoard(); + + signals: + void moveChoosen(Move&); + void rightButtonPressed(int,const QPoint&); + void updateSpy(QString); + void edited(int); + protected: + virtual QSize sizeHint() const; + + private: + int positionOf(int x, int y); + bool isValidStart(int pos, bool); + bool isValidEnd(int pos); + + QPixmap pm, boardPM; + Board& board; + int actValue; + + bool editMode, renderMode; + int editColor; + + /* copied position */ + int field[Board::AllFields]; + int color1Count, color2Count, color; + bool boardOK; + + /* for getting user Move */ + MoveList *pList; + Move actMove; + bool gettingMove, mbDown, startValid, startShown; + int startPos, actPos, oldPos, shownDirection; + int startField, startField2, actField, oldField, startType; + QColor *boardColor, *redColor, *yellowColor, *redHColor, *yellowHColor; + QCursor *arrowAll, *arrow[7]; + + Ball *n1, *n2, *h1, *h2, *d1, *d2; //, *e; + +#ifdef HAVE_KIR + KIRenderer *m_backRenderer; +#endif +}; + +#endif /* _BOARDWIDGET_H_ */ + diff --git a/kenolaba/ChangeLog b/kenolaba/ChangeLog new file mode 100644 index 00000000..dc3947a5 --- /dev/null +++ b/kenolaba/ChangeLog @@ -0,0 +1,35 @@ +Version 1.01 (24.9.97) + - Internationalisation supported (locale DE supplied) + - Updated HTML documentation (german added !) + +Version 1.02 (16.10.97) + - Improved computers playing (a little slower but much stronger !) + - Toolbar + - Options saved in config file + Ratings too: Change the way computer rates a position + (see Board::calcRating / readRating) + - New option "Spy": See some of the internals of the computers thoughts ! + - Session management + - KStdAccel used for standard key bindings + - Icon/MiniIcon supplied + + +Version 1.03: + * [Robert Williams] Added version.h and ChangeLog + * [Robert Williams] Added -caption "%c" to kenolaba.kdelnk + * [Robert Williams] Added getHelpMenu() + +Version 1.04 (10.6.99) + - Fixed *BIG* bug in a/b search + - Edit mode + - Copy/Paste Positions in nice ASCII over clipboard + - Ball rendering + - Network Mode + +Version 1.05 (16.3.2000) + - Statusbar correction + - Action'ized (Menu + Toolbar), using XML GUI description + +Version 1.05b (24.7.2000) + - Bugfix release, Cleanup for KDE 2.0 + \ No newline at end of file diff --git a/kenolaba/EvalDlg.ui b/kenolaba/EvalDlg.ui new file mode 100644 index 00000000..06931458 --- /dev/null +++ b/kenolaba/EvalDlg.ui @@ -0,0 +1,1760 @@ + +EvalDlg + + + EvalDlg + + + + 0 + 0 + 350 + 388 + + + + Configure Evaluation + + + + unnamed + + + 11 + + + 6 + + + + TabWidget2 + + + + + + moves + + + Moves + + + + unnamed + + + 11 + + + 6 + + + + Layout17 + + + + unnamed + + + 0 + + + 6 + + + + Layout35 + + + + + + unnamed + + + 0 + + + 4 + + + + moveEval1 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel3_2 + + + image0 + + + false + + + + + moveEval2 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_2_5 + + + image1 + + + false + + + + + moveEval3 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel3_3 + + + image0 + + + false + + + + + unnamed + + + image1 + + + false + + + + + PixmapLabel1_2_6 + + + image1 + + + false + + + + + PixmapLabel1_2_4 + + + image1 + + + false + + + + + PixmapLabel3 + + + image0 + + + false + + + + + PixmapLabel1_2 + + + image1 + + + false + + + + + PixmapLabel1_3 + + + image1 + + + false + + + + + Spacer2 + + + Horizontal + + + Expanding + + + + + + + TextLabel4_2_2 + + + Push Out + + + AlignTop|AlignLeft + + + + + + + Spacer9_2 + + + Horizontal + + + Expanding + + + + + TextLabel4_2 + + + Push + + + AlignTop|AlignLeft + + + + + + + Layout37 + + + + + + unnamed + + + 0 + + + 4 + + + + PixmapLabel1_2_5_2_3 + + + image1 + + + false + + + + + PixmapLabel3_3_2_3 + + + image0 + + + false + + + + + PixmapLabel3_3_2_2 + + + image0 + + + false + + + + + moveEval4 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + moveEval5 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel2_3 + + + image2 + + + false + + + + + PixmapLabel1_2_5_2 + + + image1 + + + false + + + + + PixmapLabel3_3_2 + + + image0 + + + false + + + + + PixmapLabel2_2_2 + + + image2 + + + false + + + + + PixmapLabel1_3_2 + + + image1 + + + false + + + + + Spacer2_2 + + + Horizontal + + + Expanding + + + + + moveEval6 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4 + + + image1 + + + false + + + + + PixmapLabel1_2_2 + + + image1 + + + false + + + + + PixmapLabel2_2 + + + image2 + + + false + + + + + PixmapLabel1_4_3 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_2 + + + image1 + + + + + PixmapLabel2 + + + image2 + + + false + + + + + PixmapLabel1_4_2 + + + image1 + + + false + + + + + + + Spacer9 + + + Horizontal + + + Expanding + + + + + Layout38 + + + + + + unnamed + + + 0 + + + 4 + + + + PixmapLabel2_4 + + + image2 + + + false + + + + + PixmapLabel1_3_2_2 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_2_2 + + + image1 + + + + + Spacer2_2_2 + + + Horizontal + + + Expanding + + + + + moveEval7 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_2_2_2 + + + image1 + + + false + + + + + PixmapLabel2_2_2_3 + + + image2 + + + false + + + + + moveEval9 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4_2_2 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_3_3 + + + image1 + + + false + + + + + moveEval8 + + + + 0 + 0 + 0 + 0 + + + + + 30 + 32767 + + + + AlignLeft + + + + + + + PixmapLabel1_4_3_2 + + + image1 + + + false + + + + + PixmapLabel1_4_4 + + + image1 + + + false + + + + + PixmapLabel1_2_5_2_4 + + + image1 + + + false + + + + + PixmapLabel2_3_3 + + + image2 + + + false + + + + + PixmapLabel2_2_3 + + + image2 + + + false + + + + + + + TextLabel4 + + + Normal + + + AlignTop|AlignLeft + + + + + + + + + TextLabel5 + + + + 0 + 20 + + + + For every move possible the given points are added to the Evaluation. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer5 + + + Vertical + + + Expanding + + + + + + + position + + + Position + + + + unnamed + + + 11 + + + 6 + + + + Layout63 + + + + unnamed + + + 0 + + + 0 + + + + Spacer12 + + + Horizontal + + + Expanding + + + + + Layout62 + + + + unnamed + + + 0 + + + 6 + + + + TextLabel1_2_3_2 + + + Inner ring 3: + + + + + posEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2 + + + Outermost ring: + + + + + diffEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + diffEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2 + + + Middle position: + + + + + diffEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + posEval1 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_3 + + + Inner ring 2: + + + + + TextLabel3_4 + + + +/- + + + + + TextLabel3_3 + + + +/- + + + + + diffEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel3_2 + + + +/- + + + + + TextLabel1_2_2_2 + + + Innermost ring: + + + + + TextLabel3 + + + +/- + + + + + + + Spacer11 + + + Horizontal + + + Expanding + + + + + + + TextLabel6 + + + For every ball, the given points are added to the evaluation depending on the balls position. The bonus for a given position is changed randomly in the +/- range. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer4 + + + Vertical + + + Expanding + + + + + + + inarow + + + In-A-Row + + + + unnamed + + + 11 + + + 6 + + + + Layout64 + + + + unnamed + + + 0 + + + 6 + + + + Spacer14 + + + Horizontal + + + Expanding + + + + + Layout42 + + + + unnamed + + + 0 + + + 6 + + + + rowEval3 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval2 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval5 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + rowEval4 + + + + 30 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_2 + + + Three in-a-row: + + + + + TextLabel1_2_2_3 + + + Two in-a-row: + + + + + TextLabel1_2_2_3_3 + + + Four in-a-row: + + + + + TextLabel1_2_2_3_4 + + + Five in-a-row: + + + + + + + Spacer13 + + + Horizontal + + + Expanding + + + + + + + TextLabel6_2 + + + For a number of balls In-a-Row, the given points are added to the evaluation + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer6 + + + Vertical + + + Expanding + + + + + + + count + + + Count + + + + unnamed + + + 11 + + + 6 + + + + Layout66 + + + + unnamed + + + 0 + + + 6 + + + + Spacer16 + + + Horizontal + + + Expanding + + + + + Layout65 + + + + unnamed + + + 0 + + + 6 + + + + countEval2 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_4_2 + + + 4 Balls more: + + + + + countEval5 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + countEval4 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_3_2 + + + 3 Balls more: + + + + + TextLabel1_2_2_3_4_2_2 + + + 5 Balls more: + + + + + countEval1 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_2_2 + + + 2 Balls more: + + + + + countEval3 + + + + 40 + 32767 + + + + AlignLeft + + + + + + + TextLabel1_2_2_3_5 + + + 1 Ball more: + + + + + + + Spacer15 + + + Horizontal + + + Expanding + + + + + + + TextLabel6_2_2 + + + For a difference in the number of balls, the given points are added to the evaluation. A difference of 6 only can be a lost/won game. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + Spacer6_2 + + + Vertical + + + Expanding + + + + + + + tab + + + Evaluation Schemes + + + + unnamed + + + 11 + + + 6 + + + + evalList + + + + + Layout13 + + + + unnamed + + + 0 + + + 6 + + + + evalSaveAs + + + Save As... + + + + + evalDelete + + + Delete + + + + + + + TextLabel1 + + + Your evaluation scheme, defined in all other tabs of this dialog, can be stored here. + + + WordBreak|AlignTop|AlignLeft + + + + + + + + + + + + Layout13 + + + + unnamed + + + 0 + + + 6 + + + + Spacer2_3 + + + Horizontal + + + Expanding + + + + + TextLabel9 + + + Evaluation of actual position: + + + + + actualEval + + + Flat + + + + + Spacer1 + + + Horizontal + + + Expanding + + + + + + + + + 789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade6523234530022630543251d2e253d856405bffcbc54105b19c856b630b630b43006711341dc343000ab0401651000b3f4a062ca30801003721241002208160309817525825950318810d8108818921058901662d8ecd583fb4a0fe13eb820c4f1e8fe4d448401d8ab204f23850b483431116a9352ad351700a1cd4f38 + + + 789c6dd0410ac2301005d07d4e3124bb20ad4511413c82e2521017e9c4a28b56d0ba10f1ee66664cda6a3fa1cc7f9436496e61bfdb80cdd5bd75ed0501cfee06d63feafa7938ae5f4a170b082b3cf444e90c10b6d7e644b309b39972a83aaaf322d692ea72162b52adaa58bd540a7f9662283c655f33c65144c5884a0a235b2241b14401c5fa44d83744fc3544c6bea1e4cf7c32f92f91f76806fbe3f7d2fe3aecce317aded17b1944bf57ea03b18f6cb9 + + + 789c6dd04d0ac2301005e07d4e3124bb20ad452c05f1088a4b415c24138b2e5a41eb42c4bb9bc9246a208f52e67d94fcb4d6b0df6d40d7e23e99e98280677303ed1ec3f03c1cd72f219b16fce35f7226640508dbeb78a259f959cd43a81aaadda26bb85aaab8c4962b52ed7db8ba54fb3e2c4b51943055d194321456b648118331596b19d9220564fb1163329b921bfa940cffcd047398d68bfb7a72cea8fc7cf4ddf77ca57b14ef5bfc2f59e47b253ec60c6d67 + + + + TabWidget2 + moveEval1 + moveEval2 + moveEval3 + moveEval4 + moveEval5 + moveEval6 + moveEval7 + moveEval8 + moveEval9 + posEval1 + posEval2 + diffEval2 + posEval3 + diffEval3 + posEval4 + diffEval4 + posEval5 + diffEval5 + rowEval2 + rowEval3 + rowEval4 + rowEval5 + countEval1 + countEval2 + countEval3 + countEval4 + countEval5 + evalList + evalDelete + evalSaveAs + + + kseparator.h + + + diff --git a/kenolaba/EvalDlgImpl.cpp b/kenolaba/EvalDlgImpl.cpp new file mode 100644 index 00000000..a5903429 --- /dev/null +++ b/kenolaba/EvalDlgImpl.cpp @@ -0,0 +1,299 @@ +/** + * Some Implementation details for configEval Dialog + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EvalDlgImpl.h" +#include "Board.h" +#include "EvalScheme.h" + +EvalDlgImpl::EvalDlgImpl(QWidget* parent, Board* board) + :EvalDlg(parent) +{ + _board = board; + _origScheme = board->evalScheme(); + _scheme = new EvalScheme(*_origScheme); + + connect( evalDelete, SIGNAL( clicked() ), this, SLOT( deleteEntry() ) ); + connect( evalSaveAs, SIGNAL( clicked() ), this, SLOT( saveas() ) ); + connect( evalList, SIGNAL( highlighted(int) ), this, SLOT( select(int) ) ); + + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + evalList->insertItem( i18n("Current") ); + evalList->insertItem( i18n("Default") ); + for(int i=0;iinsertItem(list[i]); + + evalList->setSelected(0, TRUE); + + updateWidgets(); + connectEditLines(); +} + +EvalDlgImpl::~EvalDlgImpl() +{ + delete _scheme; +} + + +void EvalDlgImpl::connectEditLines() +{ + connect( moveEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval6, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval7, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval8, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( moveEval9, SIGNAL(textChanged(const QString&)), this, SLOT(updateMove()) ); + connect( posEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( posEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( diffEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateFields()) ); + connect( rowEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( rowEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateInARow()) ); + connect( countEval1, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval2, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval3, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval4, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); + connect( countEval5, SIGNAL(textChanged(const QString&)), this, SLOT(updateCount()) ); +} + +void EvalDlgImpl::disconnectEditLines() +{ + moveEval1->disconnect(); + moveEval2->disconnect(); + moveEval3->disconnect(); + moveEval4->disconnect(); + moveEval5->disconnect(); + moveEval6->disconnect(); + moveEval7->disconnect(); + moveEval8->disconnect(); + moveEval9->disconnect(); + posEval1->disconnect(); + posEval2->disconnect(); + posEval3->disconnect(); + posEval4->disconnect(); + posEval5->disconnect(); + diffEval2->disconnect(); + diffEval3->disconnect(); + diffEval4->disconnect(); + diffEval5->disconnect(); + rowEval2->disconnect(); + rowEval3->disconnect(); + rowEval4->disconnect(); + rowEval5->disconnect(); + countEval1->disconnect(); + countEval2->disconnect(); + countEval3->disconnect(); + countEval4->disconnect(); + countEval5->disconnect(); +} + +void EvalDlgImpl::updateWidgets() +{ + // Moves + moveEval1->setText( QString::number(_scheme->moveValue(Move::move1)) ); + moveEval2->setText( QString::number(_scheme->moveValue(Move::move2)) ); + moveEval3->setText( QString::number(_scheme->moveValue(Move::move3)) ); + moveEval4->setText( QString::number(_scheme->moveValue(Move::push1with2)) ); + moveEval5->setText( QString::number(_scheme->moveValue(Move::push1with3)) ); + moveEval6->setText( QString::number(_scheme->moveValue(Move::push2)) ); + moveEval7->setText( QString::number(_scheme->moveValue(Move::out1with2)) ); + moveEval8->setText( QString::number(_scheme->moveValue(Move::out1with3)) ); + moveEval9->setText( QString::number(_scheme->moveValue(Move::out2)) ); + + // Position + posEval1->setText( QString::number(_scheme->ringValue(0)) ); + posEval2->setText( QString::number(_scheme->ringValue(1)) ); + posEval3->setText( QString::number(_scheme->ringValue(2)) ); + posEval4->setText( QString::number(_scheme->ringValue(3)) ); + posEval5->setText( QString::number(_scheme->ringValue(4)) ); + + diffEval2->setText( QString::number(_scheme->ringDiff(1)) ); + diffEval3->setText( QString::number(_scheme->ringDiff(2)) ); + diffEval4->setText( QString::number(_scheme->ringDiff(3)) ); + diffEval5->setText( QString::number(_scheme->ringDiff(4)) ); + + // InARow + rowEval2->setText( QString::number(_scheme->inARowValue(0)) ); + rowEval3->setText( QString::number(_scheme->inARowValue(1)) ); + rowEval4->setText( QString::number(_scheme->inARowValue(2)) ); + rowEval5->setText( QString::number(_scheme->inARowValue(3)) ); + + // Count + countEval1->setText( QString::number(_scheme->stoneValue(1)) ); + countEval2->setText( QString::number(_scheme->stoneValue(2)) ); + countEval3->setText( QString::number(_scheme->stoneValue(3)) ); + countEval4->setText( QString::number(_scheme->stoneValue(4)) ); + countEval5->setText( QString::number(_scheme->stoneValue(5)) ); + + updateEval(); +} + +void EvalDlgImpl::updateEval() +{ + int value; + + // set temporary the new scheme + _board->setEvalScheme(_scheme); + value = - _board->calcEvaluation(); + _board->setEvalScheme(_origScheme); + + kdDebug(12011) << "Updated Eval: " << value << endl; + + if (value<-15999 || value>15999) value=0; + actualEval->display(value); +} + +void EvalDlgImpl::updateMove() +{ + _scheme->setMoveValue(Move::move1, moveEval1->text().toInt()); + _scheme->setMoveValue(Move::move2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::left2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::right2, moveEval2->text().toInt()); + _scheme->setMoveValue(Move::move3, moveEval3->text().toInt()); + _scheme->setMoveValue(Move::left3, moveEval3->text().toInt()); + _scheme->setMoveValue(Move::right3, moveEval3->text().toInt()); + + _scheme->setMoveValue(Move::push1with2, moveEval4->text().toInt()); + _scheme->setMoveValue(Move::push1with3, moveEval5->text().toInt()); + _scheme->setMoveValue(Move::push2, moveEval6->text().toInt()); + + _scheme->setMoveValue(Move::out1with2, moveEval7->text().toInt()); + _scheme->setMoveValue(Move::out1with3, moveEval8->text().toInt()); + _scheme->setMoveValue(Move::out2, moveEval9->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateCount() +{ + _scheme->setStoneValue(1, countEval1->text().toInt()); + _scheme->setStoneValue(2, countEval2->text().toInt()); + _scheme->setStoneValue(3, countEval3->text().toInt()); + _scheme->setStoneValue(4, countEval4->text().toInt()); + _scheme->setStoneValue(5, countEval5->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateFields() +{ + _scheme->setRingValue(0, posEval1->text().toInt()); + _scheme->setRingValue(1, posEval2->text().toInt()); + _scheme->setRingValue(2, posEval3->text().toInt()); + _scheme->setRingValue(3, posEval4->text().toInt()); + _scheme->setRingValue(4, posEval5->text().toInt()); + _scheme->setRingDiff(1, diffEval2->text().toInt()); + _scheme->setRingDiff(2, diffEval3->text().toInt()); + _scheme->setRingDiff(3, diffEval4->text().toInt()); + _scheme->setRingDiff(4, diffEval5->text().toInt()); + + updateEval(); +} + +void EvalDlgImpl::updateInARow() +{ + _scheme->setInARowValue(0, rowEval2->text().toInt()); + _scheme->setInARowValue(1, rowEval3->text().toInt()); + _scheme->setInARowValue(2, rowEval4->text().toInt()); + _scheme->setInARowValue(3, rowEval5->text().toInt()); + + updateEval(); +} + + +void EvalDlgImpl::deleteEntry() +{ + int i = evalList->currentItem(); + // You cannot delete Pseudo Items 0 (Current) and 1 (Default) + + if (i>1) { + QString name = evalList->text(i); + evalList->removeItem(i); + + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + list.remove(name); + config->writeEntry("EvalSchemes", list); + config->sync(); + } +} + +void EvalDlgImpl::saveas() +{ + KLineEditDlg dlg(i18n("Name for scheme:"), QString::null, this); + dlg.setCaption(i18n("Save Scheme")); + + if (dlg.exec()) { + QString name=dlg.text(); + KConfig* config = kapp->config(); + config->setGroup("General"); + QStringList list = config->readListEntry("EvalSchemes"); + QListBoxItem *it = evalList->findItem(name); + if (!it) { + evalList->insertItem(name); + it = evalList->findItem(name); + list << name; + config->writeEntry("EvalSchemes", list); + } + evalList->setSelected(it, TRUE); + + EvalScheme savedScheme(*_scheme); + savedScheme.setName(name); + savedScheme.save(config); + config->sync(); + } +} + +void EvalDlgImpl::select(int i) +{ + QString name = evalList->text(i); + + delete _scheme; + _scheme = 0; + + // 2 fixed entries: 0 is Current (origScheme), 1 is Default + + if (i == 0) _scheme = new EvalScheme(*_origScheme); + else if (i==1) { + _scheme = new EvalScheme(name); + } + else { + // read in the Scheme from the config file + _scheme = new EvalScheme(name); + KConfig* config = kapp->config(); + _scheme->read(config); + } + + kdDebug(12011) << "Selected " << name << ", Index " << i << endl; + + disconnectEditLines(); + updateWidgets(); + connectEditLines(); +} +#include "EvalDlgImpl.moc" diff --git a/kenolaba/EvalDlgImpl.h b/kenolaba/EvalDlgImpl.h new file mode 100644 index 00000000..70c903fb --- /dev/null +++ b/kenolaba/EvalDlgImpl.h @@ -0,0 +1,44 @@ +/** + * Some Implementation details for configEval Dialog + * + */ + +#ifndef _EVALDLGIMPL_H_ +#define _EVALDLGIMPL_H_ + +#include "EvalDlg.h" + +class EvalScheme; +class Board; + +class EvalDlgImpl: public EvalDlg +{ + Q_OBJECT + + public: + EvalDlgImpl(QWidget* parent, Board* board); + ~EvalDlgImpl(); + + EvalScheme* evalScheme() { return _scheme; } + + public slots: + void deleteEntry(); + void saveas(); + void select(int i); + void updateCount(); + void updateMove(); + void updateFields(); + void updateInARow(); + + private: + void updateEval(); + void updateWidgets(); + void connectEditLines(); + void disconnectEditLines(); + + EvalScheme *_origScheme, *_scheme; + Board* _board; +}; + +#endif // _EVALDLGIMPL_H_ + diff --git a/kenolaba/EvalScheme.cpp b/kenolaba/EvalScheme.cpp new file mode 100644 index 00000000..18ba5b43 --- /dev/null +++ b/kenolaba/EvalScheme.cpp @@ -0,0 +1,231 @@ +/** + * EvalScheme + * + * Configuration options for a Evaluation Scheme. + * Evaluation Schemes are used for evalution of a Abalone board position + * + * (C) JW, 2000 + */ + +#include +#include + +#include "EvalScheme.h" + +// Default Values +static int defaultRingValue[] = { 45, 35, 25, 10, 0 }; +static int defaultRingDiff[] = { 0, 10, 10, 8, 5 }; +static int defaultStoneValue[]= { 0,-800,-1800,-3000,-4400,-6000 }; +static int defaultMoveValue[Move::typeCount] = { 40,30,30, 15,14,13, + 5,5,5, 2,2,2, 1 }; +static int defaultInARowValue[InARowCounter::inARowCount]= { 2, 5, 4, 3 }; + + +/** + * Constructor: Set Default values + */ +EvalScheme::EvalScheme(QString n) +{ + _name = n; + setDefaults(); +} + + +/** + * Copy Constructor + */ +EvalScheme::EvalScheme(EvalScheme& s) +{ + _name = s._name; + + for(int i=0;i<6;i++) + _stoneValue[i] = s._stoneValue[i]; + + for(int i=0;isetGroup(confSection); + + QStringList list; + QString tmp; + + list = config->readListEntry("StoneValues"); + if (list.count()>0) { + _stoneValue[0] = 0; + for(int i=1;i<6;i++) + _stoneValue[i] = list[i-1].toInt(); + } + + list = config->readListEntry("MoveValues"); + if (list.count()>0) { + for(int i=0;ireadListEntry("InARowValues"); + if (list.count()>0) { + for(int i=0;ireadListEntry("RingValues"); + if (list.count()>0) { + for(int i=0;i<5;i++) + _ringValue[i] = list[i].toInt(); + } + + list = config->readListEntry("RingDiffs"); + if (list.count()>0) { + for(int i=0;i<5;i++) + _ringDiff[i] = list[i].toInt(); + } +} + + +void EvalScheme::save(KConfig* config) +{ + QString confSection = QString("%1 Evaluation Scheme").arg(_name); + config->setGroup(confSection); + + QString entry; + + entry.sprintf("%d,%d,%d,%d,%d", _stoneValue[1], _stoneValue[2], + _stoneValue[3], _stoneValue[4], _stoneValue[5]); + config->writeEntry("StoneValues", entry); + + entry.sprintf("%d", _moveValue[0]); + for(int i=1;iwriteEntry("MoveValues", entry); + + entry.sprintf("%d", _inARowValue[0]); + for(int i=1;iwriteEntry("InARowValues", entry); + + entry.sprintf("%d,%d,%d,%d,%d", _ringValue[0], _ringValue[1], + _ringValue[2], _ringValue[3], _ringValue[4]); + config->writeEntry("RingValues", entry); + + entry.sprintf("%d,%d,%d,%d,%d", _ringDiff[0], _ringDiff[1], + _ringDiff[2], _ringDiff[3], _ringDiff[4]); + config->writeEntry("RingDiffs", entry); +} + +void EvalScheme::setRingValue(int ring, int value) +{ + if (ring >=0 && ring <5) + _ringValue[ring] = value; +} + +void EvalScheme::setRingDiff(int ring, int value) +{ + if (ring >=1 && ring <5) + _ringDiff[ring] = value; +} + +void EvalScheme::setStoneValue(int stoneDiff, int value) +{ + if (stoneDiff>0 && stoneDiff<6) + _stoneValue[stoneDiff] = value; +} + +void EvalScheme::setMoveValue(int type, int value) +{ + if (type>=0 && type=0 && stones=,,,,... (34 values) + * + */ + +EvalScheme* EvalScheme::create(QString scheme) +{ + int pos = scheme.find('='); + if (pos<0) return 0; + + EvalScheme* evalScheme = new EvalScheme( scheme.left(pos) ); + evalScheme->setDefaults(); + + QStringList list = QStringList::split( QString(","), scheme.right(pos+1) ); + + int i=0; + while(i_stoneValue[i+1] = list[i].toInt(); + else if (i<10) + evalScheme->_ringValue[i-5] = list[i].toInt(); + else if (i<15) + evalScheme->_ringDiff[i-10] = list[i].toInt(); + else if (i<15+Move::typeCount) + evalScheme->_moveValue[i-15] = list[i].toInt(); + else if (i<15+Move::typeCount+InARowCounter::inARowCount) + evalScheme->_inARowValue[i-15-Move::typeCount] = list[i].toInt(); + i++; + } + + return evalScheme; +} + +QString EvalScheme::ascii() +{ + QString res; + int i; + + res.sprintf("%s=%d", _name.ascii(), _stoneValue[1]); + for(i=1;i<6;i++) + res += QString(",%1").arg( _stoneValue[i] ); + for(i=0;i + +#include "Move.h" + +class KConfig; + +/* + * The constructor gets a name, and tries to read the scheme + * for the Kenolaba configuration file, if nothing found, use + * default values + */ + +class EvalScheme +{ + public: + EvalScheme(QString); + EvalScheme(EvalScheme&); + ~EvalScheme() {} + + void setDefaults(); + void read(KConfig*); + void save(KConfig*); + + static EvalScheme* create(QString); + QString ascii(); + + void setName(QString n) { _name = n; } + void setRingValue(int ring, int value); + void setRingDiff(int ring, int value); + void setStoneValue(int stoneDiff, int value); + void setMoveValue(int type, int value); + void setInARowValue(int stones, int value); + + QString name() { return _name; } + int ringValue(int r) { return (r>=0 && r<5) ? _ringValue[r] : 0; } + int ringDiff(int r) { return (r>0 && r<5) ? _ringDiff[r] : 0; } + int stoneValue(int s) { return (s>0 && s<6) ? _stoneValue[s] : 0; } + int moveValue(int t) { return (t>=0 && t=0 && s +#include + +#include + +#include + +#include "Move.h" +#include "Board.h" + +const QString nameOfDir(int dir) +{ + dir = dir % 6; + return + (dir == 1) ? i18n("Right") : + (dir == 2) ? i18n("RightDown") : + (dir == 3) ? i18n("LeftDown") : + (dir == 4) ? i18n("Left") : + (dir == 5) ? i18n("LeftUp") : + (dir == 0) ? i18n("RightUp") : QString("??"); +} + +QString nameOfPos(int p) +{ + static char tmp[3]; + tmp[0] = 'A' + (p-12)/11; + tmp[1] = '1' + (p-12)%11; + tmp[2] = 0; + + return QString( tmp ); +} + +QString Move::name() const +{ + QString s,tmp; + + /* sideway moves... */ + if (type == left3 || type == right3) { + int f1, f2, df; + + f1 = f2 = field; + df = 2* Board::fieldDiffOfDir(direction); + if (df > 0) + f2 += df; + else + f1 += df; + + s = nameOfPos( f1 ); + s += '-'; + s += nameOfPos( f2 ); + s+= '/'; + s+= (type == left3) ? nameOfDir(direction-1): nameOfDir(direction+1); + } + else if ( type == left2 || type == right2) { + int f1, f2, df; + + f1 = f2 = field; + df = Board::fieldDiffOfDir(direction); + if (df > 0) + f2 += df; + else + f1 += df; + + s = nameOfPos( f1 ); + s += '-'; + s += nameOfPos( f2 ); + s+= '/'; + s+= (type == left2) ? nameOfDir(direction-1): nameOfDir(direction+1); + } + else if (type == none) { + s = QString("??"); + } + else { + s = nameOfPos( field ); + s += '/'; + s += nameOfDir(direction); + + tmp = (type <3 ) ? i18n("Out") : + (type <6 ) ? i18n("Push") : QString(""); + if (!tmp.isEmpty()) { + s += '/'; + s += tmp; + } + } + return s; +} + +void Move::print() const +{ + printf("%s", name().ascii() ); +} + +MoveTypeCounter::MoveTypeCounter() +{ + init(); +} + +void MoveTypeCounter::init() +{ + for(int i=0;i < Move::typeCount;i++) + count[i] = 0; +} + +int MoveTypeCounter::sum() +{ + int sum = count[0]; + + for(int i=1;i < Move::typeCount;i++) + sum += count[i]; + + return sum; +} + + +InARowCounter::InARowCounter() +{ + init(); +} + +void InARowCounter::init() +{ + for(int i=0;i < inARowCount;i++) + count[i] = 0; +} + +MoveList::MoveList() +{ + clear(); +} + +void MoveList::clear() +{ + int i; + + for(i=0;i= Move::typeCount) return; + if (nextUnused == MaxMoves) return; + + assert( nextUnused < MaxMoves ); + + /* adjust queue */ + if (first[t] == -1) { + first[t] = last[t] = nextUnused; + } + else { + assert( last[t] < nextUnused ); + next[last[t]] = nextUnused; + last[t] = nextUnused; + } + + next[nextUnused] = -1; + move[nextUnused] = m; + nextUnused++; +} + +bool MoveList::isElement(int f) +{ + int i; + + for(i=0; i 0) && (mm.direction != m.direction)) + continue; + + /* if type is supplied it has to match */ + if ((m.type != Move::none) && (m.type != mm.type)) + continue; + + if (m.type == mm.type) { + /* exact match; eventually supply direction */ + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + + switch(mm.type) { + case Move::left3: + case Move::right3: + if (startType == start3 || startType == all) { + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + break; + case Move::left2: + case Move::right2: + if (startType == start2 || startType == all) { + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + break; + default: + if (startType == start1 || startType == all) { + /* unexact match: supply type */ + m.type = mm.type; + m.direction = mm.direction; + if (del) mm.type = Move::none; + return true; + } + } + } + return false; +} + + +bool MoveList::getNext(Move& m, int maxType) +{ + if (actualType == Move::typeCount) return false; + + while(1) { + while(actualType < 0 || actual[actualType] == -1) { + actualType++; + if (actualType == Move::typeCount) return false; + actual[actualType] = first[actualType]; + if (actualType > maxType) return false; + } + m = move[actual[actualType]]; + actual[actualType] = next[actual[actualType]]; + if (m.type != Move::none) break; + } + + return true; +} + + diff --git a/kenolaba/Move.h b/kenolaba/Move.h new file mode 100644 index 00000000..1226d969 --- /dev/null +++ b/kenolaba/Move.h @@ -0,0 +1,132 @@ +/* Classes Move, MoveList + * - represents a move in the game of Abalone + * + * Josef Weidendorfer, 28.8.97 +*/ + +#ifndef _MOVE_H_ +#define _MOVE_H_ + +#include + +class Move +{ + public: + + /* Type of move: Moves are searched in this order */ + enum MoveType { out2 = 0, out1with3, out1with2, push2, + push1with3, push1with2, move3, left3, right3, + left2, right2, move2, move1, none }; + enum { typeCount = none }; + + Move() { type = none; } + Move(short f, char d, MoveType t) + { field = f; direction = d, type = t; } + + + bool isOutMove() const + { return type <= out1with2; } + bool isPushMove() const + { return type <= push1with2; } + static int maxOutType() + { return out1with2; } + static int maxPushType() + { return push1with2; } + static int maxMoveType() + { return move1; } + + QString name() const; + + void print() const; + + /* Directions */ + enum { Right=1, RightDown, LeftDown, Left, LeftUp, RightUp }; + + short field; + unsigned char direction; + MoveType type; +}; + +/** + * Class MoveTypeCounter + * + * Used in Board evaluation to count types of board allowed + * in a position + */ +class MoveTypeCounter +{ + public: + MoveTypeCounter(); + ~MoveTypeCounter() {} + + void init(); + void incr(int t) { count[t]++; } + int get(int t) { return count[t]; } + int sum(); + + private: + int count[Move::typeCount]; +}; + +/** + * Class InARowCounter + * + * Used in Board evaluation to count connectiveness + * of some degrees in a position + */ +class InARowCounter +{ + public: + enum InARowType { inARow2 = 0, inARow3, inARow4, inARow5, inARowCount }; + + InARowCounter(); + ~InARowCounter() {} + + void init(); + void incr(int c) { count[c]++; } + int get(int c) { return count[c]; } + + private: + int count[inARowCount]; +}; + + +/* MoveList stores a fixed number of moves (for efficince) + * returns reference of next move ordered according to type + * does nothing if there isn't enough free space + * + * Recommend usage (* means 0 or more times): + * [ clear() ; insert() * ; isElement() * ; getNext() * ] * + */ +class MoveList +{ + public: + MoveList(); + + enum { MaxMoves = 150 }; + + /* for isElement: search for moves starting with 1/2/3 fields */ + enum { all , start1, start2, start3 }; + + void clear(); + void insert(Move); + bool isElement(int f); + bool isElement(Move&, int startType, bool del=false); + void insert(short f, char d, Move::MoveType t) + { insert( Move(f,d,t) ); } + int getLength() + { return nextUnused; } + + bool getNext(Move&,int maxType); /* returns false if no more moves */ + + private: + Move move[MaxMoves]; + int next[MaxMoves]; + int first[Move::typeCount]; + int last[Move::typeCount]; + int actual[Move::typeCount]; + int nextUnused, actualType; +}; + +#endif /* _MOVE_H_ */ + diff --git a/kenolaba/Network.cpp b/kenolaba/Network.cpp new file mode 100644 index 00000000..e12f6b5e --- /dev/null +++ b/kenolaba/Network.cpp @@ -0,0 +1,193 @@ +#include + +#include "Network.h" + +#include +#include +#include +#include +#include +#include +#include + +Listener::Listener(const char* h, int p, struct sockaddr_in s,bool r) +{ + if (h==0) + host[0]=0; + else { + int l = strlen(h); + if (l>99) l=99; + strncpy(host, h, l); + host[l] = 0; + } + port = p; + sin = s; + reachable = r; +} + +Network::Network(int port) +{ + struct sockaddr_in name; + int i,j; + + listeners.setAutoDelete(TRUE); + + fd = ::socket (PF_INET, SOCK_STREAM, 0); + if (fd<0) return; + + for(i = 0; i<5;i++) { + name.sin_family = AF_INET; + name.sin_port = htons (port+i); + name.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (fd, (struct sockaddr *) &name, sizeof (name)) >= 0) + break; + // printf("...Port %d in use\n", port+i); + } + mySin = name; + // printf("I'm using Port %d\n", port+i); + if (i==5) { + printf("Error in bind to port %d\n", port); + close(fd); + fd = -1; + return; + } + for(j = 0; jreachable) + sendString( l->sin, tmp, len); + } + listeners.clear(); + + delete sn; +} + +void Network::gotConnection() +{ + static char tmp[1024]; + int len=0; + struct sockaddr_in sin; + kde_socklen_t sz = sizeof (sin); + + // printf("GotConnection: "); + int s = accept(fd,(struct sockaddr *)&sin, &sz); + if (s<0) { + printf("Error in accept\n"); + return; + } + while(read(s, tmp+len, 1)==1) len++; + close(s); + tmp[len]=0; len++; + // printf("Got: '%s'\n",tmp); + if (strncmp(tmp,"reg ",4)==0) { + int port = atoi(tmp+4); + sin.sin_port = htons( port ); + Listener *l = new Listener(0,0,sin); + // printf("Reg of 0x%x:%d\n", + // ntohl(sin.sin_addr.s_addr ), ntohs(sin.sin_port)); + listeners.append(l); + return; + } + + if (strncmp(tmp,"unreg ",6)==0) { + int port = atoi(tmp+6); + sin.sin_port = htons( port ); + Listener* l; + for(l=listeners.first(); l!=0; l=listeners.next()) + if (l->sin.sin_addr.s_addr == sin.sin_addr.s_addr && + l->sin.sin_port == sin.sin_port) break; + if (l==0) { + printf("Error: UnReg of 0x%x:%d. Not Found\n", + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + return; + } + listeners.remove(l); + // printf("UnReg of 0x%x:%d\n", + // ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port)); + return; + } + + if (strncmp(tmp,"pos ",4)==0) { + emit gotPosition(tmp+4); + } +} + +void Network::addListener(const char* host, int port) +{ + struct hostent *hostinfo; + struct sockaddr_in name; + + memset(&name, 0, sizeof(struct sockaddr_in)); + name.sin_family = AF_INET; + name.sin_port = htons (port); + hostinfo = gethostbyname (host); + if (hostinfo == NULL) { + printf ("Error in addListener: Unknown host %s.\n", host); + return; + } + name.sin_addr = *(struct in_addr *) hostinfo->h_addr; + + Listener *l = new Listener(host,port,name); +// printf("Added Listener %s, 0x%x:%d\n", +// host, ntohl(name.sin_addr.s_addr), ntohs(name.sin_port)); + listeners.append(l); + + char tmp[50]; + int len = sprintf(tmp, "reg %d", ntohs(mySin.sin_port)); + + if (!sendString( name, tmp, len)) + listeners.remove(l); +} + +void Network::broadcast(const char* pos) +{ + char tmp[1024]; + int len = sprintf(tmp,"pos %s", pos); + + for(Listener* l=listeners.first(); l!=0; l=listeners.next()) + if (l->reachable) + l->reachable = sendString(l->sin, tmp, len); +} + +bool Network::sendString(struct sockaddr_in sin, char* str, int len) +{ + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s<0) { + printf("Error in sendString/socket ??\n"); + return false; + } + if (::connect (s, (struct sockaddr *)&sin, sizeof (sin)) <0) { + printf("Error in sendString/connect to socket 0x%x:%d\n", + ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port) ); + return false; + } + write(s, str, len); + close(s); + // printf("Send '%s' to 0x%x:%d\n", str, + // ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port) ); + return true; +} + +#include "Network.moc" diff --git a/kenolaba/Network.h b/kenolaba/Network.h new file mode 100644 index 00000000..d5251cc6 --- /dev/null +++ b/kenolaba/Network.h @@ -0,0 +1,60 @@ +/* + * Simple Network Support + * Install a listening socket; receive positions on incoming + * connections (incoming positions are treated as if pasted in) + */ + +#ifndef _NETWORK_H_ +#define _NETWORK_H_ + +#include +#include + +#include +#include +#include + +class Listener { + public: + Listener(const char*,int,struct sockaddr_in,bool=true); + + char host[100]; + int port; + struct sockaddr_in sin; + bool reachable; +}; + + +class Network: public QObject +{ + Q_OBJECT + + public: + enum { defaultPort = 23412 }; + + /* install listening TCP socket on port */ + Network(int port = defaultPort); + ~Network(); + + bool isOK() { return (fd>=0); } + void addListener(const char* host, int port); + void broadcast(const char* pos); + + signals: + void gotPosition(const char* pos); + + private slots: + void gotConnection(); + + private: + bool sendString(struct sockaddr_in sin, char* str, int len); + + QPtrList listeners; + struct sockaddr_in mySin; + int fd, myPort; + QSocketNotifier *sn; +}; + +#endif + + diff --git a/kenolaba/README b/kenolaba/README new file mode 100644 index 00000000..6d69911b --- /dev/null +++ b/kenolaba/README @@ -0,0 +1,23 @@ +Another kool game for KDE :) + +Kenolaba is a game like Abalone. You play against the computer on a +board. For rules look at the HTML manual. + +INSTALLATION: +If you find this directory (and README) in a kde package bundled with +other applications, there is nothing to do. By installing the package +Kenolaba should be installed too. + +Otherwise get the kdeapps package and unpack it. +Make the kenolaba/ directory (with this README) a subdirectory of +kdeapps/ and add "kenolaba" to the SUBDIRS variable in the toplevel +Makefile.in of the kdeapps package. By installing the package kdeapps now +Kenolaba should be installed too. + +Have fun... +...and remember: Your computer already is a real thinking machine ! + +Greetings, + +Josef Weidendorfer + diff --git a/kenolaba/Spy.cpp b/kenolaba/Spy.cpp new file mode 100644 index 00000000..07f17702 --- /dev/null +++ b/kenolaba/Spy.cpp @@ -0,0 +1,155 @@ +/* Class Spion + * + * Josef Weidendorfer, 10/97 + */ + + +#include +#include +#include + +#include +#include + +#include "BoardWidget.h" +#include "Spy.h" + +Spy::Spy(Board& b) + :board(b) +{ + int i; + + top = new QVBoxLayout(this, 5); + + QLabel *l = new QLabel(this); + l->setText( i18n("Actual examined position:") ); + l->setFixedHeight( l->sizeHint().height() ); + l->setAlignment( AlignVCenter | AlignLeft ); + top->addWidget( l ); + + QHBoxLayout* b1 = new QHBoxLayout(); + top->addLayout( b1, 10 ); + + for(i=0;iaddLayout( b2 ); + + actBoard[i] = new BoardWidget(board,this); + actLabel[i] = new QLabel(this); + actLabel[i]->setText("---"); + // actLabel[i]->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + actLabel[i]->setAlignment( AlignHCenter | AlignVCenter); + actLabel[i]->setFixedHeight( actLabel[i]->sizeHint().height() ); + + b2->addWidget( actBoard[i] ); + b2->addWidget( actLabel[i] ); + connect( actBoard[i], SIGNAL(mousePressed()), this, SLOT(nextStep()) ); + } + + l = new QLabel(this); + l->setText( i18n("Best move so far:") ); + l->setFixedHeight( l->sizeHint().height() ); + l->setAlignment( AlignVCenter | AlignLeft ); + top->addWidget( l ); + + b1 = new QHBoxLayout(); + top->addLayout( b1, 10 ); + + for(i=0;iaddLayout( b2 ); + + bestBoard[i] = new BoardWidget(board,this); + bestLabel[i] = new QLabel(this); + bestLabel[i]->setText("---"); + // bestLabel[i]->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + bestLabel[i]->setAlignment( AlignHCenter | AlignVCenter); + bestLabel[i]->setFixedHeight( bestLabel[i]->sizeHint().height() ); + + b2->addWidget( bestBoard[i] ); + b2->addWidget( bestLabel[i] ); + connect( bestBoard[i], SIGNAL(mousePressed()), this, SLOT(nextStep()) ); + } + + connect( &board, SIGNAL(update(int,int,Move&,bool)), + this, SLOT(update(int,int,Move&,bool)) ); + connect( &board, SIGNAL(updateBest(int,int,Move&,bool)), + this, SLOT(updateBest(int,int,Move&,bool)) ); + top->activate(); + setCaption(i18n("Spy")); + // KWM::setDecoration(winId(), 2); + resize(500,300); +} + +void Spy::keyPressEvent(QKeyEvent *) +{ + nextStep(); +} + +void Spy::nextStep() +{ + next = true; +} + +void Spy::clearActBoards() +{ + for(int i=0;iclearPosition(); + actBoard[i]->draw(); + actLabel[i]->setText("---"); + } +} + +void Spy::update(int depth, int value, Move& m, bool wait) +{ + next = false; + + if (depth>BoardCount-1) return; + actBoard[depth]->showMove(m,3); + // actBoard[depth]->showMove(m,0,false); + + if (!wait) { + actLabel[depth]->setText("---"); + return; + } + + if (depthupdatePosition(true); + actLabel[depth+1]->setNum(value); + board.takeBack(); + + if (depthclearPosition(); + actBoard[depth+2]->draw(); + } + } + + while(!next) + kapp->processEvents(); +} + +void Spy::updateBest(int depth, int value, Move& m, bool cutoff) +{ + if (depth>BoardCount-1) return; + bestBoard[depth]->showMove(m,3); + // board.showMove(m,0); + + if (depthupdatePosition(true); + + QString tmp; + tmp.setNum(value); + if (cutoff) tmp += " (CutOff)"; + bestLabel[depth+1]->setText(tmp); + board.takeBack(); + } +} + + + + + + +#include "Spy.moc" diff --git a/kenolaba/Spy.h b/kenolaba/Spy.h new file mode 100644 index 00000000..f69d99d3 --- /dev/null +++ b/kenolaba/Spy.h @@ -0,0 +1,46 @@ +/* Class Spion + * + * Josef Weidendorfer, 10/97 + */ + +#ifndef _SPY_H_ +#define _SPY_H_ + + +#include +#include "Board.h" + + +class BoardWidget; +class QLabel; + +class Spy: public QWidget +{ + Q_OBJECT + +public: + Spy(Board&); + ~Spy() {} + + enum { BoardCount = 5 }; + + void clearActBoards(); + + void keyPressEvent(QKeyEvent *e); + +public slots: + void update(int,int,Move&,bool); + void updateBest(int,int,Move&,bool); + void nextStep(); + +private: + bool next; + Board &board; + QBoxLayout *top; + BoardWidget *actBoard[BoardCount], *bestBoard[BoardCount]; + QLabel *actLabel[BoardCount], *bestLabel[BoardCount]; +}; + + + +#endif /* _SPION_H_ */ diff --git a/kenolaba/TODO b/kenolaba/TODO new file mode 100644 index 00000000..baad7913 --- /dev/null +++ b/kenolaba/TODO @@ -0,0 +1,8 @@ +TODO for Abalone > 1.05b + +* "Back" in Network mode +* Computer Character Editor (=Rating values) +* Search Inspector +* Computer playing in own process/thread +* 3 player mode +* OpenGL 3D board diff --git a/kenolaba/bitmaps/Arrow1 b/kenolaba/bitmaps/Arrow1 new file mode 100644 index 00000000..45c2ce41 --- /dev/null +++ b/kenolaba/bitmaps/Arrow1 @@ -0,0 +1,8 @@ +#define Arrow1_width 16 +#define Arrow1_height 16 +#define Arrow1_x_hot 7 +#define Arrow1_y_hot 7 +static unsigned char Arrow1_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x03, 0x00, 0x0f, + 0xfc, 0x3f, 0xfe, 0x7f, 0xfc, 0x3f, 0x00, 0x0f, 0x80, 0x03, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow1Mask b/kenolaba/bitmaps/Arrow1Mask new file mode 100644 index 00000000..2e50b48d --- /dev/null +++ b/kenolaba/bitmaps/Arrow1Mask @@ -0,0 +1,6 @@ +#define Arrow1Mask_width 16 +#define Arrow1Mask_height 16 +static unsigned char Arrow1Mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xc0, 0x03, 0xc0, 0x0f, 0xfc, 0x3f, + 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xc0, 0x0f, 0xc0, 0x03, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow2 b/kenolaba/bitmaps/Arrow2 new file mode 100644 index 00000000..47487a89 --- /dev/null +++ b/kenolaba/bitmaps/Arrow2 @@ -0,0 +1,8 @@ +#define Arrow2_width 16 +#define Arrow2_height 16 +#define Arrow2_x_hot 7 +#define Arrow2_y_hot 7 +static unsigned char Arrow2_bits[] = { + 0x00, 0x00, 0x10, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf0, 0x00, 0xf0, 0x08, + 0xe0, 0x0d, 0xc0, 0x0f, 0xc0, 0x0f, 0xf0, 0x0f, 0xe0, 0x0f, 0x80, 0x0f, + 0x00, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow2Mask b/kenolaba/bitmaps/Arrow2Mask new file mode 100644 index 00000000..121295e6 --- /dev/null +++ b/kenolaba/bitmaps/Arrow2Mask @@ -0,0 +1,6 @@ +#define Arrow2Mask_width 16 +#define Arrow2Mask_height 16 +static unsigned char Arrow2Mask_bits[] = { + 0x10, 0x00, 0x38, 0x00, 0x7c, 0x00, 0xfc, 0x00, 0xf8, 0x09, 0xf8, 0x1d, + 0xf0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1f, 0xf0, 0x1f, 0xe0, 0x1f, + 0x80, 0x1f, 0x00, 0x1e, 0x00, 0x18, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow3 b/kenolaba/bitmaps/Arrow3 new file mode 100644 index 00000000..b511a017 --- /dev/null +++ b/kenolaba/bitmaps/Arrow3 @@ -0,0 +1,8 @@ +#define Arrow3_width 16 +#define Arrow3_height 16 +#define Arrow3_x_hot 7 +#define Arrow3_y_hot 7 +static unsigned char Arrow3_bits[] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x0f, 0x80, 0x07, 0x88, 0x07, + 0xd8, 0x03, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x07, 0xf8, 0x03, 0xf8, 0x00, + 0x38, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow3Mask b/kenolaba/bitmaps/Arrow3Mask new file mode 100644 index 00000000..f4af9ccb --- /dev/null +++ b/kenolaba/bitmaps/Arrow3Mask @@ -0,0 +1,6 @@ +#define Arrow3Mask_width 16 +#define Arrow3Mask_height 16 +static unsigned char Arrow3Mask_bits[] = { + 0x00, 0x04, 0x00, 0x0e, 0x00, 0x1f, 0x80, 0x1f, 0xc8, 0x0f, 0xdc, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, 0xfc, 0x07, 0xfc, 0x03, + 0xfc, 0x00, 0x3c, 0x00, 0x0c, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow4 b/kenolaba/bitmaps/Arrow4 new file mode 100644 index 00000000..dfcdbd82 --- /dev/null +++ b/kenolaba/bitmaps/Arrow4 @@ -0,0 +1,8 @@ +#define Arrow4_width 16 +#define Arrow4_height 16 +#define Arrow4_x_hot 8 +#define Arrow4_y_hot 7 +static unsigned char Arrow4_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x01, 0xf0, 0x00, + 0xfc, 0x3f, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x00, 0xc0, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow4Mask b/kenolaba/bitmaps/Arrow4Mask new file mode 100644 index 00000000..6c498fb5 --- /dev/null +++ b/kenolaba/bitmaps/Arrow4Mask @@ -0,0 +1,6 @@ +#define Arrow4Mask_width 16 +#define Arrow4Mask_height 16 +static unsigned char Arrow4Mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x03, 0xf0, 0x03, 0xfc, 0x3f, + 0xfe, 0x7f, 0xff, 0xff, 0xfe, 0x7f, 0xfc, 0x3f, 0xf0, 0x03, 0xc0, 0x03, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow5 b/kenolaba/bitmaps/Arrow5 new file mode 100644 index 00000000..72b93adc --- /dev/null +++ b/kenolaba/bitmaps/Arrow5 @@ -0,0 +1,8 @@ +#define Arrow5_width 16 +#define Arrow5_height 16 +#define Arrow5_x_hot 7 +#define Arrow5_y_hot 7 +static unsigned char Arrow5_bits[] = { + 0x00, 0x00, 0x08, 0x00, 0x38, 0x00, 0xf8, 0x00, 0xf8, 0x03, 0xf8, 0x07, + 0xf8, 0x01, 0xf8, 0x01, 0xd8, 0x03, 0x88, 0x07, 0x80, 0x07, 0x00, 0x0f, + 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow5Mask b/kenolaba/bitmaps/Arrow5Mask new file mode 100644 index 00000000..da77ea00 --- /dev/null +++ b/kenolaba/bitmaps/Arrow5Mask @@ -0,0 +1,6 @@ +#define Arrow5Mask_width 16 +#define Arrow5Mask_height 16 +static unsigned char Arrow5Mask_bits[] = { + 0x0c, 0x00, 0x3c, 0x00, 0xfc, 0x00, 0xfc, 0x03, 0xfc, 0x07, 0xfc, 0x0f, + 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x07, 0xdc, 0x0f, 0xc8, 0x0f, 0x80, 0x1f, + 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow6 b/kenolaba/bitmaps/Arrow6 new file mode 100644 index 00000000..159770b4 --- /dev/null +++ b/kenolaba/bitmaps/Arrow6 @@ -0,0 +1,8 @@ +#define Arrow6_width 16 +#define Arrow6_height 16 +#define Arrow6_x_hot 7 +#define Arrow6_y_hot 7 +static unsigned char Arrow6_bits[] = { + 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x80, 0x0f, 0xe0, 0x0f, 0xf0, 0x0f, + 0xc0, 0x0f, 0xc0, 0x0f, 0xe0, 0x0d, 0xf0, 0x08, 0xf0, 0x00, 0x78, 0x00, + 0x38, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Arrow6Mask b/kenolaba/bitmaps/Arrow6Mask new file mode 100644 index 00000000..453ffde2 --- /dev/null +++ b/kenolaba/bitmaps/Arrow6Mask @@ -0,0 +1,6 @@ +#define Arrow6Mask_width 16 +#define Arrow6Mask_height 16 +static unsigned char Arrow6Mask_bits[] = { + 0x00, 0x18, 0x00, 0x1e, 0x80, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1f, + 0xf0, 0x1f, 0xe0, 0x1f, 0xf0, 0x1f, 0xf8, 0x1d, 0xf8, 0x09, 0xfc, 0x00, + 0x7c, 0x00, 0x38, 0x00, 0x10, 0x00, 0x00, 0x00}; diff --git a/kenolaba/bitmaps/Makefile.am b/kenolaba/bitmaps/Makefile.am new file mode 100644 index 00000000..d25cc3b2 --- /dev/null +++ b/kenolaba/bitmaps/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = Arrow1 Arrow1Mask Arrow2 Arrow2Mask Arrow3 Arrow3Mask Arrow4 Arrow4Mask Arrow5 Arrow5Mask Arrow6 Arrow6Mask diff --git a/kenolaba/hi128-app-kenolaba.png b/kenolaba/hi128-app-kenolaba.png new file mode 100644 index 0000000000000000000000000000000000000000..d645ff30b9ef9b5b5b93e6b0befe1aa7824f7216 GIT binary patch literal 15572 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Ý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 literal 0 HcmV?d00001 diff --git a/kenolaba/hi16-app-kenolaba.png b/kenolaba/hi16-app-kenolaba.png new file mode 100644 index 0000000000000000000000000000000000000000..44974a0f47887b784e682b589d3cdec23f41f146 GIT binary patch literal 879 zcmV-#1CacQP)uYE1$K^t1vvQbxLhT;Z}xj06c!7VJ)uxKO$ zj3&l|7Y|-kA~Eq|b~KoHAt9U?CM2G`c+#Mv93%l2h84HLHVa+%UfaiOOV`);{vNPN zL*h62CBOXg{e1H~{C+O#)YQ}znwXgQT9TwD$8m(?ID|r>pyN15lH@W?Gc!3k`2k?m z7`Z-Re0={*Y%_fG>wgo&0W26`PeCUmIK*K zrsxppIY+rQ!4rQQ^3Z!^OAZHJmk1=z;&sbpv)LC`S66SzgM)*gjf}rD_Hxk1^Xn}< z+5^;JGoeBXccqAN;RaR5!8-mto@k8ytcI$p`2GIK-rinW>gecr`^51mu9g`0*8k?w z&SMNE%+}&>WDh*#b2_)zR*|eSnSUO!v2h2l*Gn`SB@&6eDK|DYzU%k-+Rj|OggEmG z%_=+-R4{vo;icDU@F|2tA;RG>ilWfg)`qI8EG#Urw6wG)YnoQAR;zEEJQ;VL?mUI~ z{u{*oJ2=58Dc>>5rhyQGVzG!}7{p>RR#sM+ot>?0Zf<@mTb7l~WHLPnCvm=ifO4sd zrWf!H-r(Wx9=fK{-rmmk_BNJf5eNjBnwny9adFPJ?XPNgbiSpfMW3FY7Dn+vWO9bc z@0%i>PK#73C33l(*xlU~nx=`FnHiy~Y6|G&h`#La?-%Rq>!MI73SHMlE|(Med|v3f zE=K1AJ-sPT|m_1@%%J8I@;CR+KO%4hX&W7MV4hWO=Et3ex*{WTm^DR zHvkv~o&w^3sss$z1Fjz^Q>-8KIssI20AY({UO#lFGm;eBCjsO7iaR2~=Yybd* zy8r;x$N&H_>;M26yq<`~KL7v(&q+i4pePDHpYKj25=ktLl`IE*=+L3>+S=OQ3sDd0Sg=~HSZy{ewiQ?{ndu zTeogKa`oD^pXTT1$CqV%;J|@nPUnlScfGNls(Y6(g>IvaUqeeKF#9iIO3hK29HJNv zQa=70hIv0Lt84Jbi>9i|6)l5@P*zq}v17;fFD(`e8#Znt zrwc;PE+#KtU}|id*yjBN!da$%K1-;wjo7*#?q9pX{edCEHEUVd=BB>Bp3ctB&uVIF znis#@?%lgjY}>Z&yc1xg=6S zV4k>6!m|sLK_Qb;5vDSXwhD~0L_CoopU>lTIthhBE>%^hN<1FVM-GR>(BauYdSRCM z*fgfwUy-YBMCp2shd&-gF6NLn?j)T~Gctactft|1yU`w{8JU?udp!4eJlwc(gQljY z_m$e(+IPyz%AD19I}NRCiPyFgvpFeWyN;^*Y8GnOlWyvyw0l40RvXz&hRVuHoK7bu zlL<}Ju-omlx3@DkHpYbu7nqos2r8iKWbW@mV`5GCr`!h_j4QmGWNSPXz*Fo>$EEG#V0+}uome?RBWox|tz z{SlAHKU9EhDwUd4Rduhf>n4}e$qO5{5}XT@NN6a#4q|C)eZ`hcei3x zBqX6D$uefM8H>fTsB&hr8Jo?9&*$U#@#Bn+j$Q#i0JNpRSztU8iR_hSxwNsdkwT$B zB9S1GNRUh>7fn^w#rr;U<_u@go_(b2`a$5%v-J3Su~)5C@tigQta`521gr--|0Vw9{_DWd^8!mi>2m<{__6e* b&OZkL>ZwCB%LA$y00000NkvXXu0mjf?9qu| literal 0 HcmV?d00001 diff --git a/kenolaba/hi32-app-kenolaba.png b/kenolaba/hi32-app-kenolaba.png new file mode 100644 index 0000000000000000000000000000000000000000..9620d54be1cc4b215b3d333a44de3850d640f867 GIT binary patch literal 2388 zcmV-a39I&rP)H3qbzOX50c~w(4Bpp`l^p?%lgzx_b30n>KBtb?rkGav3OTm{u1;sQ{vcDS)8m zFsx1ly$G0?k`+NqQy*B)ooIso%RM~%?6WUlxpL)XI2;~a$R#bpZ`-$T-y05xqe+%! zzWmhFlx9XyMmn)ZhcTm*6ssB`Ig4%R9jvkI2;mX5@?{vNfHHIf<<3>asSj8h{2T*g z)0l>CudS_Z=<4eF3t)VrfE_z_ytsAi*1gly(>$~5E7(OH+h2c%QeKT@uf*E(HkziP zTz(rnMWnJiEQ4n##qS{98N)q%3aPFYDL#$d`!*F@zRsndK9&ap&6!MQW_*0S`(p+8 ze7+TX_wIc?5{cN?u3gKTwd>GsUcor=d#GH7k;{=A8$_F(z?>KXdj&?ZL}6--LU;o6 z&LpOq0L4Wy9w&3@Bo$9QPfoIPePo#C=H^WoFJ3%SDwR_6oZ`I#wr$(CUy`H>i^anF z_3P0J1xogEVo8nUr86YE|4Cwah_tJogszb6IY+Yl42i3KsFq4pdku+eS4ec9CDDDB zcupiQC~V%m37gG^#bWVwbacG9XarWST-mW}*RF%HSWN7A@=0oz1}JHH47ZnXEKRoO zJegRAc=nS@M%1Qfij`f@D|*58!nfN zzP>&JfxyF)lauFSvDi3#bPtptIB?*c%jH@V3I9IH~saP-#1E0^w(W6J{@9#%d z)yszuA8rRy62Q)#J6~>TX?f0Swes|HyRnR%$9?e)tplL>NHE?_##5xV{kP9J(E zOX22KjC6`g?<%xR8c7n#=ktu;x`oeMNoaBsS(Y&j165U-nVF%nv60HkO8Wcz@%elk zW@cv2%NsXt{6VQyvaMddnqV-9Rw$CGXdpH8N9-9BLo1L=XHfjBNT>q#f4z(7@{pgr ziRGEc(Df1%x5kLYqGYpKGMNmrER)SjZ;AT3TA@@9(!aH#ff^ zCX>msLZQIz+qY3w6|q!6+_-~S+f#(6;)LTxQro_dHMoJy&V7V)GU4$Ek;W~UTlP>_ zRfzzELLp+Y7|WI|!{hN#TU(2&s)RxzG)*HA2w<^Tn4O(vY;25dHcLDnx67)kl1wJi zG>zWgUfSE+ad^sbe(R^C!yzQWpsqAQW&a!Ct0eTpUsEa+a8%cV;y^6qS-Em0>2w-J zQK+e@K~+^e9uI5QtRb7t;&!{KtE)p01m1uDeP(86h{xlkQmLY(X* zNkv5knx^sATW>KqILM6~Hy9lq{Zl5B`K=6$jgOE25?CV$LZzZ8^!E0$`H@G+TWlmu zH`ez~BKek3oW6yz`cV*NB6n_6EaVYIk#IOnAP_*)G^VGg$>;NwN+o7zXIZ**DHe-` z3l}ahFfhRI@Guh-6NAZQ@-PsQ0Mj(hnQS&|%Vx8WIvfu2`8+L6O}M?)q#RYmFJB@v z9U)t>1mznq;jXP`b~;2T6e5{SQYw{bZEeNvb~83MMkEp;7K`C@I%#WbBN~lz?AS57 zy1Ez`7|>Kz{W)*~(C4pWP1p5_d_KQ6l}au5csvNA$f}lRthE7*bz9+~tvH{29#8!W zEEtrPmm^6MZnv9YFo@sp$6~SI@puqL5wF)vYilbmmy6Se`^-Jnd98R1*KMtn@(bO?a6Gc&|uCAu0riQYzGMEb` zE|&|R&xhad$K&xZJUmQiXD264oFI`%i~v6Yt}j-OO&|BJ z?FGNz&&bFKrBdl`c;{gl#(gi(djp5VK|CI3cz77#55Os)bf4Ho1k7U-@VX>PEl#KN zt|z$H8{C`wg`PnW1k&j=#bWUy@Eu_26Sx+I0(=k1GMAbM88P5%e1iY@*ahZNag@0a zwT=bt^k;wwi~^@wB=qz6G;=Bf4(6iqAjktb{(JHNJNyTLku<37d(4Ue0000WP!Ah)hhE%C($;Cxw38;2&SWNE!no;VlBS)hGqvkDZlUAYm;yEsOe`S_5J;faWwq>T z@7MR<4~w>3jp(@3e#n`5X5aUHp6A{Fd7u9|=XuV7U&;Ti2)8?-wYAmv;DZm|scD)Z z2!iuT0g+`{7#ka#`RJpMF5S+&+f^X2ckkYRT)uqycH6db90$j_T8Fv&E1%!&`pSDj z5EvO5IsD>_FFpZO=L<->9rW9`Z~vVgJ9d0291cr>B+K$uO_HR>uc9c8Cd=}5*K_0M z#sZSt?d}eT!}iIOC*PefCV9RCSFT)n&z?Pdz7~l@c;k&Xc>C?QQC0P(<0VN#6vgYF zU-`uy$H6pB48x$mzn}j8el~90_-DtDAAe_HVBkMLNd?5Go_gx*9UUDV6B84p(`k0? z+I1Zr=41}swV>zvt{p3iA_D^h3=a>pbm>y%>8GE5X<%Sr5XgNZ3Ow@2BcEEcX3ciP zFnIOVS9#%u7wGTrN7J}ovuDpsue|cgmv8m% zTiQTdTidcPeBlePc64<31_lObYir~2#~-I!tzp;>rY&IFbt;&KUjH>sOv}Qw!L-4! z?3?x*HfV-PM|(TEu5s+xF_VD7 zhil?}an-~R!oKZ@0I7lI(*IrKez=l0iBluqbI^rE$z`q27U5K5(hWE9vA;!<3oOl(_ z@BxHnkAd0@i|<8|{P_Sx^Ms>&|O6Z4P!-$PwBVh{yMlnxqbdYLn46`YUnViDPrm-Cx%K;;upf);0HIsoA{aEKd zz)Xx|BVySOM&=T=u~XDCY3%ho@wasmZVK|&TW{g_`&qDHL8qc9*>mU49h=Vzgdck7 zp+5kK#bWq;KGv;Uhpy}3I0&LdHQb4lD`QSiAk0kHw-mNTt)xTSN^IT1b@qMq3nMT- z3but^t5GfLcv`yA0*g^b29QTjBS<4SwvAn_P&Hlnn_AFnRl2*oS-*Zg3D^iPCJI zsxDEfI%G%3$Su7Gg3VxC2!g<_UAvHFnOrW1+wESkWy_X7`B(~db#>kSnJ;Zn?)TR$gN#ug6&ib z1IhG`;663xxcsH)2AufNWRAAU$U944R7UwHZDmp4`_m6@w3 zbpQSL?`dmm+gzj@Cc*-f{1$Ut|ci|pALUjLM)7iF&B}+Q! zJ@q4&3ORhyKEwrg;mJQ1{MM5lD-iItJctwZiaF{?KfB+=W!f|&6_tPNfO0kkw75O{oHfUeP@n6;$`aGyKURHuhnWb zE?v4rUtb>`9UT~gg!th;>`WF%3FFKraMB5!NFUbN5Yp6Xf+v20>+EqHcPq~949@H& zY=0ZpNAKf0_yc6Ok5aLK-|t7$G-|aPZnqoDvPdP9Gz9~sl9#Y73sqIA)oK`qK`3R zvg#IY>F9TEppg6)eld#_jsT!fO;QO&SlG1&J&JLy#Z{*tSin zRH9HQ_@!_-yfqq)ZjxmgUDt8BTy%GLV_6o0V`GI{$-BZ-$41a{d8)+Psx_ z>4l~U`Os1-r+Rk!r0*CX+$aG<01@*L5P12)(_%xLht2 zMIo2Vk;~;U45PmKg20LuD~Lv;2!ep3DD26TmeV0fi!m3rP&@>INEE3wboBZm2ta1?~71`&ZZT1(04t)z-P@&~s;xGLZRwfQO zNWLJEw#6uxPO+-9c=2KafdHja3Cpr@yWNDtVZ2^1rBVq&5LmWs8PRBza=DBw%XmB< zT3cI@BnerTiO1s%4i2Iy3aM0zVzG#6nv$H!WJYtjT&-5Cxjh~a07s7=rKhK-VNeVF zUKD2$x)lM-qV?e4(iC(-?=~o0qV3c_BeXw6Y56t;$3~GPYNaA2(*!`%G)&W^wY9ar zf01QmStgZAHTYerR8Uowa5#+PIJjIcd_Et7AfW3y`}gmsSS+Gx8p&i5!!RoGc>JhT zE|+VfD6aJReBGr|iEubfCX+!`)drOS+#WaHrUitK{sX~_zks#RfK~;`gW$+`e)dh& z`=7OT9>0h&o4`m;Q%k2PX%hD2S<0G5EEYo)MXJ>*rBVq^(~xDE z)2B~247gUS;W!TEa+zYWh$xDb%Vj1eCXgfv$8k^;g{i42-hH>8Wr@XN%*@P?PN%05 ziNwDEvoer34C8|f7cTsyr>Ezu7cXAK<#OTicsO$82rVrwsH%#6`CMMHRkTY|E5{6+=E|*EC)0n2&pg<;*Y4~$ z!NEbI(I{KDZbj2IP`y;{-i~wX7kFl~kV=CnVwMV2Dh|@(UKD`_PqW!4IiSiO2R9*>7yF2|d1zDX*T z;_TV8jEsz+>-xD;sq`IS5GVl7oB|GD099Sry}=Ki#bS}3o*oogMv`T$ z6%U}dc46Sg?pcTWsXrpPels3fL=eE|^HZzUD3{BP{OQuAOIfyT8L})RNfJJvk7BXN zToCnoy{ufhl1L_U&VQe4Ksz_RU4ALKjs;YQAo`xHByWLmyOOn)Z7CxU3 zuh)yJs+gw9p+kolA0OxV@#Bn*jo~=X6z~n;6p+8nm>ViEr$m{$UEG(?=jGPcRvf2( zO1kT=yAVaOaeuS?ydVgTD0-tLFMueDNRo6_B6xUsn4zH|&YnHX;lqcqEK3J|47^F5 zI_6a$;s)=vE)U88D-6Tv%w#fjc6Op^8lg~#Xf#?sBhOQAl^X>b`nkY{{id;E^>pq!Ob#r*5#%F8!DBG-|O`f4u`QUi=|7KHlpX8&;Gaud~ClciddG# znKNfNbLI?3j~-=sco?7rd=K~l$Z`dBZl!=jJw6lw6?h<%$q4~y!sO&6gM)(%4-fOsJMWFLhYug-R)16B9Vv;4GmGN)fxwnE5Tk{*sv_?mi(WK6CE8LOioS`i^ZUBe?J07xg4-= zHh(@vJ{PbQcoF!0fTpIVM#yix=mwRSkL$m7tZmyQlgYW=?_TQMzW~(cf$?KS0D?e2 z@N?>4h;2=4iKpWAKJYhvF9-Urfr40Yz@cIw0~pc{A+ zSbcf!CspX4d)Haur_}9kUbW)eQ9u9!z+zw_pxhqDTgqJUoTk2Am{+8{-AcVk9k|M^ o>ua|wHg%fn%oF^tG7e)PlD`#CjVE_OC07*qoM6N<$f>V)CTmS$7 literal 0 HcmV?d00001 diff --git a/kenolaba/hi64-app-kenolaba.png b/kenolaba/hi64-app-kenolaba.png new file mode 100644 index 0000000000000000000000000000000000000000..9e5aa438d33bdb75929707aabf6d39aba1ef7775 GIT binary patch literal 6254 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 literal 0 HcmV?d00001 diff --git a/kenolaba/kenolaba.cpp b/kenolaba/kenolaba.cpp new file mode 100644 index 00000000..e59aa9d7 --- /dev/null +++ b/kenolaba/kenolaba.cpp @@ -0,0 +1,71 @@ +/* Start point */ + +#include + +#include +#include +#include +#include + +#include + +#include "version.h" +#include "AbTop.h" + + +static const char description[] = + I18N_NOOP("Board game inspired by Abalone"); + +static KCmdLineOptions options[] = +{ + { "h", 0, 0}, + { "host ", I18N_NOOP("Use 'host' for network game"), 0 }, + { "p", 0, 0}, + { "port ", I18N_NOOP("Use 'port' for network game"), 0 }, + KCmdLineLastOption +}; + +AbTop *create(KCmdLineArgs *args) +{ + AbTop* top = new AbTop; + if (args->isSet("port")) + top->netPort( args->getOption("port").toInt() ); + if (args->isSet("host")) + top->netHost( args->getOption("host").data() ); + top->readConfig(); + return top; +} + +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kenolaba", I18N_NOOP("Kenolaba"), + KENOLABA_VERSION, description, KAboutData::License_GPL, + "(c) 1997-2000, Josef Weidendorfer"); + aboutData.addAuthor("Josef Weidendorfer",0, "Josef.Weidendorfer@gmx.de"); + aboutData.addAuthor("Robert Williams"); + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions( options ); // Add our own options. + + KApplication app; + KGlobal::locale()->insertCatalogue("libkdegames"); + + /* command line handling */ + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + /* session management */ + if ( app.isRestored() ) { + uint n = 1; + while ( KMainWindow::canBeRestored(n) ) { + AbTop *top = create(args); + top->restore(n); + n++; + } + } else { + AbTop *top = create(args); + app.setMainWidget(top); + top->show(); + } + args->clear(); + return app.exec(); +} + diff --git a/kenolaba/kenolaba.desktop b/kenolaba/kenolaba.desktop new file mode 100644 index 00000000..add3fdda --- /dev/null +++ b/kenolaba/kenolaba.desktop @@ -0,0 +1,64 @@ +[Desktop Entry] +Name=Kenolaba +Name[ar]=لعبة Kenolaba +Name[be]=Ðбалон +Name[bn]=কেনোলাবা +Name[hi]=के-नोलाबा +Name[ne]=केनोलाबा +Name[pa]=ਕੀਨੋਲਾਬਾ +Name[ta]=கெனோபà¯à®³à®¾ +Name[tg]=Кенолаба +Name[zh_TW]=Kenolaba 互推 +Name[zu]=I-Kenolaba +Icon=kenolaba +Type=Application +Exec=kenolaba %i %m -caption "%c" +DocPath=kenolaba/index.html +GenericName=Abalone-like Board Game +GenericName[be]=ÐаÑÑ‚Ð¾Ð»ÑŒÐ½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ Ðбалон +GenericName[bg]=ЛогичеÑка игра +GenericName[bn]=অà§à¦¯à¦¾à¦¬à¦¾à¦²à¦¨-জাতীয় ছকভিতà§à¦¤à¦¿à¦• খেলা +GenericName[br]=Ur c'hoari taolenn a seurt gant Abalone +GenericName[bs]=Igra nalik na Abalone +GenericName[ca]=Jocs de taula similar a l'Abalone +GenericName[cs]=Deskové hry podobné Abalone +GenericName[cy]=Gêm Fwrdd sy'n debyg i Abalone +GenericName[da]=Abalone-lignende brætspil +GenericName[de]=Abalone-ähnliches Brettspiel +GenericName[el]=ΕπιτÏαπέζιο παιχνίδι παÏόμοιο με το Abalone +GenericName[eo]=Abalone-simila bretludo +GenericName[es]=Juegos de tablero similar a Abalone +GenericName[et]=Abalone moodi lauamäng +GenericName[eu]=Abalone bezalako mahai-jokoa +GenericName[fa]=بازی Abalone-like Board +GenericName[fi]=Abalone-tyylinen lautapeli +GenericName[fr]=Jeu de plateau dans le style d'Abalone +GenericName[he]=חיקוי Abalon, משחק לוח +GenericName[hr]=Igra na ploÄi poput Abalonea +GenericName[hu]=Abalone-szerű táblás +GenericName[is]=Leikur sem líkist Abalone +GenericName[it]=Gioco da tavolo simile ad Abalone +GenericName[ja]=Abalone ã®ã‚ˆã†ãªãƒœãƒ¼ãƒ‰ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ក្ážáž¶ážšâ€‹ážŠáž¼áž… Abalone +GenericName[lv]=Abalone lÄ«dzÄ«ga galda spÄ“le +GenericName[mk]=Игра на табла Ñлична на Abalone +GenericName[nb]=Abalone-lignende brettspill +GenericName[nds]=Abalone-liek Brettspeel +GenericName[ne]=à¤à¤¬à¤¾à¤²à¥‹à¤¨ जसà¥à¤¤à¥ˆ बोरà¥à¤¡ खेल +GenericName[nl]=Abalone-achtig bordspel +GenericName[nn]=Abalone-liknande brettspel +GenericName[pl]=Gra planszowa typu Abalone +GenericName[pt]=Jogo de Tabuleiro tipo Abalone +GenericName[pt_BR]=Jogo parecido com Abalone +GenericName[ru]=Ðбалоне +GenericName[se]=Abalone-lágan duolbbášspeallu +GenericName[sk]=Stolová hra typu Abalone +GenericName[sl]=PloÅ¡Äadna igra, podobna Abalone +GenericName[sr]=Игра на табли налик на Abalone +GenericName[sr@Latn]=Igra na tabli nalik na Abalone +GenericName[sv]=Abalone-liknande brädspel +GenericName[uk]=Гра на дошці подібна до гри Abalone +GenericName[zh_TW]=é¡žä¼¼ Abalone 的棋盤éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;BoardGame; diff --git a/kenolaba/kenolabaui.rc b/kenolaba/kenolabaui.rc new file mode 100644 index 00000000..cf8c1626 --- /dev/null +++ b/kenolaba/kenolabaui.rc @@ -0,0 +1,53 @@ + + + + +

    9{UK>!qtDPT;2rm4_04S|3knxr}Sr#}~5s!xvi-l2NUx$H#VQ}up_U&D;?K${-9Go*KO4&8fBM`!aWtA{9 zGYct!_3IlEjm9uLJ3IR5qq|nss+KuAHa7O&ci(;Yr}_E0{zM|0<(!97sWqUKfpd;0 zp4g3Kat-|c5QK2yx;7YNuq;4T6;P@`*L4H}RnRn!Ty7G!?V!22F?8+PK>xLC1N}c2 zbj3cZs!G=Lgz5KJp;U5USvFKv!?|<47#JuQj-~}+Sr#0}0U>~*5a_xB&N%{s03wkn zOtTIP3k&%2%aeHFg?+2qRjt^b48wRg5-~!tShV~$bSWVOQ&UrzoSeY?d>)?Xfe@fr zw4kag2q`b4j45y&4}O0Y3?l$t*Dy0PYmSVJ_K%NG{47^j1TB@UzGyUZfH4J9N)Q4_ zNx&F^QLduR>mJLcNd_E3B2s}@~^E}wL16|i3B|-U80#&7ul0sD( z9ETtfs6sMX9qR1-ZMT%3aqis3#hH|Jd00PmaMoK@B66dimvppEWkF zgX=oDa^>F;0+6ztSxNyy2vn7UQhLW-Av{p(ExrgqDZsf3LI^0Oa2yw!#?a7EpZ(&C ze}1&EP?}v8=)Qfwf48ZMhxOwvA>7TT`t_YgRwD#=SvF(pSNHE6WI0Cxv zLsL^DEXziJ|5Zpy;CV}hw)}n}C_K-DWm!;^a+al(C=`mYta-fi&igoX<^uNa?QR_y zxZXcDcJqM|SP}H~*I&8px&oZ5SSpP2)Df&%Q;Uro(-<2Y$K>QKNGTUVD}4SKmW%^rIK~u2;2qw+;jVm)zlIc+luupPkWR0IZ9BMeV+_S&aWM`*Km%BGQoyn-6pJOCI&}tT&tAOqtd5R0 zY~Gv+ajuzteP7=<0?RBewZ5O7w2JP)F zIDh_Mn3>5h7L|k$60Yk(2m#v`n4T`+)~#uL{PFR{8ITBtf_V1X9w>@}bb4Lu?b}m@ z`}Nm5poS4jz4+pDpZ&LsWs${|sr=Cf073{TiUJ`d6ony^X@RP$7#zHgQptkrdhk3K zQhJ!1n}g%H`0A^($meI_c^;&c*t_>hXj-}V`u%=%bhLG!KK=EH>FJpjH`&W}PxbX? z769`3+0nIYQ=P-ZBYokpVGa+E^tQLR^$NF;0$0(|q$ z<-T}4dT8CcwTEJ{DExju0O01$9Ijm(0Hx)uZ{D0iYwITHx{gGm8g+Fw2nPKa8oDuh z;J_;@?y~OMI9Ec9u~agdc&n;PPer4VPF>fquuwuiUx2ErXm4+Y-yZ-WKr9wTA`wST zO#;DS0B6pe>wEj{qxgu=|kH>m$-kf;#^5v^X9(?CR jJ=8-z)I&YguciM0^rbq3DZtR`00000NkvXXu0mjffT9$c literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0007.png b/kasteroids/sprites/ship/ship0007.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec6ce4ec963aab27e8aebe1d994a4e20152236b GIT binary patch literal 2226 zcmV;j2u=5iP)*M<0H8^V8e6KeNj;O=2;N zwQJWDo3-{efp&IwUiE=%)~qf*^2m?3S(b$m5~UQ|w*CC&>ql(ps-TYJbUpasg9l90 z{MK+h_uR9)3WZ!D3?srY!Z0L-LbmXK0nKK!U9Y_I%Gpw>M4?dFW|~IVvSmF7Z@cXW z(E|@`+O_z*?c1N(wRCBwV44ac1fJ(1r6QZn7BZP7O-aAH(v5UF-L-Y=)?H&`V+GIi z@O{54l}hwwGMz*B-g_5)eYf@}5-t7f*WXs$y!nAA5(%3yl-RaGy&hl~7KWk7=d*>~ zyZ`#;^@8flT}-7?U0b$nd8xOzcVifajE;_y&vzDtSTdZ;Wf&M(L#0yZ{`>D4zHniV z$;l}krxnAHl*=x*ZKJg$iX^#QwkeKY7Iejm6~nn)ZdeGxkt0WtQeYUGd_GIDxE?7L zrfCuc7O|Mc`Sa(9$1Qq#x@l{(0RyEJVF&{Q0~?zGddb!=UAlC5!-fsNtyC&}`sq<> zwJM#RZP>QWx^;tiUO+r^3@po_r6tDH)HE|Qv*hzx!cb7JhnS{;ZEMQqa_Q*N ze;&LZ&~Jpvah&47z`(9jsl=HxU-9+V({yyCh{t2BUAqbhF-?u{M+5-~q0u@<7@GF>G@x0%dR6}$Z~Xq%`T6o(Q$QDC zE0xMfDwP~+YfJTac6N|XJAlMAZSK2oh(scd=edAO6lqMe0jpG_4O&~{%+Hso*8@^1 zhaiYJcI?E#v9Z%5-xu_<=cs9##aPT97D8YcCa&w@d3AER3?F{@H-aD_o9zJ%!my6- zN0iHT3_~NONn2Z*Y_^l>=_!sMKZR0~&6|gYzb_>L;%y`p40UmqoC(rlvboY;poI)vuQV@m$r4%5D7T!3u)kf~YXf#wZm+)6!y*PPZr5ty??%_S=8j zdEKDNWU_1P)+Y~0DKQK|OG|>@-X1>r*C&MS|JcGFnSAna=)q-g)<$UA+YK*=K)o zvc26YgrP)ANf?GGC6Q7fC9GVzf*|mjoSY;G0;F8r+D3m0A&4S@@B2iNk5Yml04ZU9 zzKjs?;)}nbSlp0Ir&Ilh5C7wuUID)Wx_R@^%d1zf9P)j!046T>qf!FTi;z-MC}dDd zF*zH~K2k^V`OBMnVC5rdT6Nsz4!LMTC2HN z4c0}_yYJrk^rlVs{xXULhS7MClnr;JY`Eq7K8B$Q0&tuZx7@Of!-tRJx*n#f5uyR9 zHH2Ys@js2Axtu(CioJXH128c$NujWm*48-3kB{uWW>CjT7G`JX#?PGj*P)r2(zxro zB>>yDlZ{;!Xsz&lpL*RV2m+GH7FMoY#`ySorl)6!B8Va`hAN7{_aiP`C~@}eID7WI zkL!8>q*4xd-gyUi+_Ap+(ML!2T@|a4i?r5VYu2nfkjrHYsg#3dnFK+|=;&$Aotr=ife?aCn|{dP-~fq4 z3zwmlG#WT{c)Z1awjLx<9f2y>nt>a*ykJN!P7A zLEtBC+XNyil`8dm9oshP>swtsa^%>anVH#3f^|bd|5v^rjL*%L-kg~D_s-L&zuYl1 zGwXz5SOlomT*~DNwVF${TALdiJAH|(O%2m`l~St9agrNvy>->Dd_GGo7Q^@bL$AO7 zR?|MNHC?uC6+1fGhHcww>IdMPx~ZGGsheu(zjVn+K!hqhQ~&?~07*qoM6N<$f)+MS AwEzGB literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0008.png b/kasteroids/sprites/ship/ship0008.png new file mode 100644 index 0000000000000000000000000000000000000000..f26327456f3d941f9447f21f718f235cb608f5ee GIT binary patch literal 2289 zcmVNkle82pJm6FjLPAtjDndo7#2X6y17V7Ma5##R6HI_ zt;Jk)Ehf30dU}o=KYH}Y@wJ$Xuf-()uXsF`dg-O3$M@{not~PS8C{DxmmN{VFjDb& zylXjQj~#pA_};ybq*YaeVdyKTyGhFieeuN?zie)9PTy(V8*lu%@6kv1r4>birZKEr z*Oct+>{!+Ym(m?U6NyCX%$YM;DP=Mk45k6VFalkhHf_wdwzm8a`0KC#sc--OeQ8DE z00;<2ZQcnI{2oEG$?U9=<;M_~ZMNkw_4PPyiIRT|yubKrpCbW@dJD@7@Ee zvZ-6X^}4R79((MuKG$`3jg5`s+_`g5Re?~*Kz+T5-Mb%#VHj{-ABLeoQCM<#_!=17 zfM_&~Vo^d>Rj8^0*99nDy$k*%qTz73Yv<0LeWqz9RaM3G^b}N8foTR23^DiYPI#MIO{W@o3NsyZT(CP-O@ls7;abX`M3LlljT5ey7mz{0`;9Eae* zfhX=*ELH?++xAJu81i`wmSv+-DMLyLDIHw8bOF=TlZy~O9H)vxp$yIe#u!vpLo^!2 z#*MA``s)GA&*$N~-fB2+Z}?I=nR2;e>bl+~1P7%IoKtYlAfX1pwPF_9v5xo=T+}QWOr(DI5n- zRTV;T5JDiO1J45-$ARyYMcZPK5@1ZAt}X!I2S!K7;kxeV*|T4)s6s92#0L*P_4fY# zzezfdgNcbL&ePjQ^x(lK-`=dQ z&N(>0xjUkeQleBUL(>Elr3S0(3RIOt%KtjT)~)SbpMCb_>6w|?pVd(R1oZjmpX+OH z-`p*w3r$nt`vim#Fhfl>y~BjB8XF}4_e z2F5t((u4*98=X!*ObsLI?$=1iG#vl}ciIdIt0J^I(jF zv06kmO#x#Rj1fpF7n>1++G|kTDCZQGwE!WMP$qLZGdA|avK`<@(8Gs+*SBq3NBTze zYKssE!QlHo7^Cn!Kv5JV5^)R-4Wn2rfpZSVC@8I6gw)`ql(hwz4?=49^Z7h1s{&Q! z&@?r5=FGX?p9N}~q12{L8@nearZc*(nYylpY8`s5>`_YKx;}j02O$KSrlGz40bIH? zh*GHx#yEglGzqC~h*FAt-UjCco(EK`60Ymw(xpMX`|c?`{d9No^Uu$p&gTou%GFyI zVABkxwr$&zUcbI&SABgrwP8ccu3AG3l*^UHoMi|E6r4Em9_s7EShubbkw_T9U>yuY z2j?6@2vAC47&;V1!S(B-c>nzmK`F(NBfm#3H~-a}ZysOP_tdgGh1S-Vu5j2)CX>xw zZEYL62mv;2YTHE!L2vKh5Q|08*cgXth7k+~5C~{sOhLI^h7b(X(=+(^sfsep5iNTeQz4n2wO+aJKGQ~x-5;>3H;-D#aAOXzYmF)^9RWG?qlP0jQc z3YMv9Y8OyT5!dyQ&5q;3g@2(~EW-C|dEVXqYjkux2ue8ufq?n#x0$aB1#8J%T{Y;< zDi%xqxm@1zJiohAslxL-T)A=;xm@ALG2Pw!kVqupI4%MK713xkG%}LSeEYjCDxqhH|+A zENdY%JbZmszkcqT7-OlXriMT6+_~d?FlemF-|2h0r+d1mUr_%6NCHoEQ~Pz*00000 LNkvXXu0mjfd=FDN literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0009.png b/kasteroids/sprites/ship/ship0009.png new file mode 100644 index 0000000000000000000000000000000000000000..e7188fd1e864f9ff5439023f3e5b3094f92013be GIT binary patch literal 2297 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 TEr~7600000NkvXXu0mjfT5eTN literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0010.png b/kasteroids/sprites/ship/ship0010.png new file mode 100644 index 0000000000000000000000000000000000000000..20c5aa7244fb85593f8270a3c5a7f5954a466e01 GIT binary patch literal 2311 zcmV+i3HbJjP)9C)9>+h=^Ss-6&sqwnMOrq4 zSQyKQqAj2e7ZWoiMx9BCi4&vo#>9(>OvZcv0L5gIxzK?duPk^sF#{7tT%aJ8t*Jn< z3q38Tr`uV2)_0qWwnUwgE`S#kzjr6+{LcHy_qRRI3;*v*CX?;UjoG&C;bq6%m3Hpj z+4<_LuZ}DyrnB>fgC|dZ608(fTh5Og8ylP7fB*e1-}l>&9Xqzkr>^(j`}={bSFay!Y1!KT;){QLbN~L|bq0Yz*EOV62q9@~Y-mcSGs`L) z0ClG?H*MN<@ad#aZkjuSBEV{UAySgpkb6mM{zh&tpFQ_ZW0RmSwfsw%IJDARdphdGmV0VT<$U&*L~1g22c3 z1)8P^g-q70iBqdp7#*EN*Cm>!sH+R3ltL-V*x2}rnrTtc#>QkcoDjorFGsZXaf2S-P5rE<9(rBWH!b@6=<%QCrqxrb7zNVQtM4LdVa0Ti|!V)Nz= z3=9mQlmG-l0K0bWY+DKFq65F9W6!~OJjTpSjz}boWf@ef`n-by{r#7T#UfaiK@b?! z)kV;C07YG03@JVO`v=*+eG85QK@hBj^XEb1@mTY7&+UI_$Bsujg8)vRJjL{MmQYB? zbpuwfuE#VrYBi5+wuoVv=(->rHZe^D!w_hiPCUMf$;lxmCUPVaaaPFrzf&KN$C_V% z{k7BEwzYK5)t02CKYE-KZwOWmGxlATAjpI0!%QYOw!S{VU4=l@M z!-i%C2L~`s$qJkP$BxG1vF4XwKG3yk)B3hj$wk*Ss#On7Q#|lMBYXGm=HkW6SeAut z8z?om!=)5Bj)UvgaGVO2ih)u(L7)IZBod*aA<3;QmmB3b=}*JDy1+C4I$>T5(E-WlTD{jpF6r@&;?bi)%>+}KYaD-wG-uXC0|#UXtpdX7K_=Kra^!I0MV$8VdyBOA%sK- ziR1Wycl#VF1myFx6pKY%*Nc7n>8T^tYIVu!Ulepfxm-SV{rZjLUwm=;P$o0cw{z!? z&ef~y>Fc|O<2YEBiBj6Us~`lpu7gq%-w&9Yn!)p2PMtbUQ`1_8heuL3Z>G*H_4&)X zg)ZpQr7K6@c;l~P}TJ;y#ztPx8Ghm zQ7FtVX;a-ZP=Io|d}d}Q+b0C=(P$)AE>|cPO8|t!HqSr*dm0-X@qN%Vjlsd;zLAm9 zB{kGNN&OF1ES8Rb_ucjGLZQ&LapU?nr6j9Y*Rx~Cc0f=l%n}Y;Se8j+V{*xLy7EiR z9pri5$nbEgy{G5W0n;>6(P)I3nF6!3MGA!i)v7}f1ax%lesih+c~4XSQ`Krt-^9e^ zu}Y=d9tv4a;cy7kH1T~O)6~&*or@Q*oXF=3i`MCV1qJYZKR+{*Jv=ovT?mD&?Y3>j zgqV8|5Q*6I_Fg@nPG=T<#dF_57gR1+&Ww*|x^3Hj&9W?lARrVn2?Drusdv!>$}a{A z;5cq-boAB{-}l>Nu~^g8)C}Qpc>B3?J#Q}-`-_6YTq&f}Af@dAi=fB%g`>2zkv hJO5Yxs$X@N`VR_RgQHWSU%>zX002ovPDHLkV1kn%d9nZi literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0011.png b/kasteroids/sprites/ship/ship0011.png new file mode 100644 index 0000000000000000000000000000000000000000..1d21e59c22923dc02d45d8d2b11deb69302bc6e6 GIT binary patch literal 2288 zcmV+iD-h1x3_s+kT(oS1u zS}6ib1r$Tl5!XdR)Mz$lO~%BRePLfVnkaqa(HE07f{EEzmlxwh`(&a~Ad9YSXa!2K z#X_aH+Lo4Di__`Mbmrc<_wK_`mTX+?U^UsS-Fj&&y*F_1;K9CzhK8Pc+rRwspN`bqrY_Kp8#iX&dh4x$Lx&Fa z#pCgw6DLkEKR*vZA`#ChWv1`c{)s2{W#50lZ))GZ-)8FtHCFSHZQJQRd-fdOxpQYG zkw~PjT)D!>AAiiu%na+-H)S4uw5PARd42Z#?{k9_6JPaCPtOe8-u{Ibvc3EF@6A5+ z(2gG4R($ZmiT*VQ?da&}efZ&r4@)U2l}cQ_dX=G}A6Y`|R4av*)?zes^TwzP*{h12{cDT7YhHYCU5^*+d>LQtJ# z$8m7ofTKs>1vEU*$Mb64DWzy^j1h)aDis$Y1jS;_&p`mXUISipP~Z2b9ml4jA%PGA z->=?^;%l${nUwAkK~Tl>JX|**3_;U$lv1>{CGos~O2xzT z0+iB`Qe$Rjc52N*J3G^v*I$2ivbnj5VzEf2Ql?UIsa9(qoS3-GH{X1T>$-%YN2L-{ zD*5QTPAnEB46BsOMI1+BnlLmpG*~Yr+#x+Z@W8h0-o20Zc6D|3gduF--pwbUoFW#B zB7_cTBocAL(5F}|Vw#dLEYZ?p5sjKut6u6#plYZBiV_QVDsiKKKke+juRmskKnor zT{qBmpD+x_=kr7&7L`gBDFZw&z_KJwP3t&w<^qpD{u`#Juhs3(TcC+VJpJN}2TnIM zBvL{Mgb)~pK^WEmP168$Hf(5P)24e^T+Gwh7{hTCVW=<+6QxYTFdzs#T-T*i@c@Hr zH6$9f*t+#z^7$OY!x!$#qdGvPQpuk>HTY(^Tv=*uZRvI#71uNkAvBaS2m;VFfu;$X znwmIsW(dcLV443db=~O5%AwQc5&UU|A-HAu&yZ za@ohS6sD=+`vLE~)BpC=)XdmV0$tJg_{6c}$4|c6+M4WcYfE;Ap&$%v(3&Rb>gwd& zxeF*IQK}YI+qST66I};g2PsX2Fo?wzL7*Xoj+6$LWu`y;@NaLg3FwMG|NLVA;NZEx zY8q_Z*qQOXDy~~g-eDMW{rVh^W22ObVHg;Oj;87Oet@P)q^w0X3^iQ0iVy*@STuF? z=)143DJazZd-?K}W3#h!V;vpoOe7MC`@T^9wZ-{#(R>lzPt zcBW}*Sx-D3!*QIO3#*okrBnzJGCuxK;&F$@#ztOw%HLb(4EJ~#! zqoZT1zS>!H&=r+R%LDWC3uBIBXH3(K*QzWN+g3P^BA3fAojG%U)k%NNJo-Z_6bk*L zqhsj0am2P&%C=2%xg3sT(cIjWs;94?Eut%OU3V;(%S}fjPS&<<3`57VOrp_fd}!$N zeN8T7hjAIE-e*VSjbVSRIn_y>Zty#Q-8ZWuR3-1?D^*{ zOP%iMNTqVQeBCMP7nNzs^!DxB1|EBCPhT_|x#JD>uliO0OZpc}Dq;qq>k;Ds0000< KMNUMnLSTZSBY;5w literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0012.png b/kasteroids/sprites/ship/ship0012.png new file mode 100644 index 0000000000000000000000000000000000000000..1d5c51c3a593d75541f2d708c4709b4e03103a20 GIT binary patch literal 2283 zcmV9)x6~}+~-S;(Lp0Nje2JplL z1s59#BnqmHNJtbZY_hCTvxu_ouHs{tWx9$Qr;)OY#43vjSLz~&G^C5DR%(I*1Y@uv z1!BfC1K4;zJ@ekod+&BJCMAhTJ$xjp+W+$E-Ftp>&OPVcGw@#``7oO|Y}k<5yLa#Y zH{N*TQ^PPuuU@@6@uD9yxMkeCyV&{ey#p{YQ@;C7aEjdem_DN_6sZ zCX*Q(9UVQiZ{NO=WHQO}@-kn1@dbrKfeRNd98fA)=<14%=5mDz*LAb^JEyO&HB z_V3*J^yr5lel)%o+iUWuWm%aQUwrXKI2^{dZSwgX^YimuyLOGGr6p|JWcTi84yDt5 zBaY)xsno`fAOGv<56?-Z`ZBM-{@a7kJ-2Igcz9^!?YIAQcrEtV4C=aWc4}&B&#qlN zkJW0`{(L@lnRJO!y`jOPmxZi(KLPJ!i9^Y^Yhs$fE_!AM~@u&{rJ;Q zZy(WhNho9?rDSR8&V9Z9Lv<8-(=;ulS$l^r_xao?&pXLva%A1Qb!0LdNvHb|LUQ`_88j_G*EKXvA*IB&4U)-jlnRhiB7_7q zJkJ9Jo+mhe{`}NhZFdA33WZ1}doWD{Ap}YV07N1QeBZ?Ld|E94fv%fGqhWMiP%4$E z*PB$UE7-P0G#bIS4f6S03y%eKY3a_pjYb34ZQ}bLuItfi`Rv~P9KF41eBVQ<03kpK ziEW#tQoR(56}r1aBobXD5@9q=!!RVLPoI4}9<5euQ>9WF$8l&hn)rS|5GYEeI{?ID zX}|*%QYv)aBpeRo`|WTCLED^ZN|X|O`Q@388pMB0rh$RX=)Qe>5ANEvee9D@KErVw zT(^l*K1u}?i^~*?cd%^>+xGCh_La~yNhqX~O7)P<-e&#!G>+pDix~jqa)mV~(tSbW z@vh9yo!iHThlj>CZ{9Rw7?L1R#N%-)l`4rulUl8TQUcqyDHN^~i$zJLdhmUp#l;%Q zq(vlR5Q)T?nYm0RvjN*~L!Lc*w&QmDJ21wsiy%;#rj6q?SXrrI+aXNT z#x&cnBGc5+b%AAB2+>|^-)}__z(*hb<>d7ArB5FzD1hs_m9M|PFfliG<>cVtQ~P7F zSeNH{cwUpsm;XsD79$k0u`C^3*U_~0k(y>=+a{(d2?7nz6DZ}A%`PlVPM&(S9=%tk zQh8x|dgjluSgb#j*|060?&a#$>u8#WZChBDg<GC(}R}-hA^98ONzl6pJNlwH0c$2CY^T&vR+D zTGZ<`8V#35!^QJjEH78cW^?3nh1H7CnI3AcKYMn1!f~AHx^>C@w*BwWODWMbfl`Wk z-C=2I8P|1KTr47`U}k3SR4!NO=$ZFGbN#(4mn-873;BbQNTfd+4PjX(x~@|!Rtbd+ zbX{`m))LpQ-C%b18x9@%9p!TQ!F&C~D4We4N+y%Xq?9z99^tTq=lRUd-Q?1x%Q%j^ zI+6&A#S$IPl#dRyTCGjY%v?IG)X+NsUww6+N~MPDx&UaJhNiXCK`Hg!81u7(0u+m- z@oU$<9WhOPtlbP&z5@(`fZpD93=aGy)jfMgB%Yy>sa)kp*sbgEVJUKQnkfFDC9lCB(uh+3{3)9pQqVuZwQ03`< zs#a?gnx<{*?oMno3=^dS48tHEk71f7wOWnY*~=ZZ(MRslyINStA2tjlV_DWnDwRSh zMYGw&G~Y$QE0=?&R#j( z)zvj>S$2Q>aqZC6)s^Y=tjFfjyYhWMd;0X5J>PzNV`25bQ!15e$LBm&kKUE%dD+Ry zsRQ9~=pXgEL!;sR9A+O}Qp(JZ9mC_1NVwzB|6l*J`WIKC&i%kDm=6E|002ovPDHLk FV1jW}Ug7`% literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0013.png b/kasteroids/sprites/ship/ship0013.png new file mode 100644 index 0000000000000000000000000000000000000000..37e1a68d266a55ac9cee335cfd63bbfed35e8042 GIT binary patch literal 2209 zcmV;S2wwMzP)6j}+fD2O3c2?$EpsJ2i@Fo6&P`JAy6 z$8p9pp0E3LP8Z{#h(hCW9I@$78qMO~bKgh5^Pcy-=fD=KTrN*-<@dH!d-m*^dhx{< z4}bRAXVt@p59?yFcu!#EW(JkZ<>`ruiRps}4^9sa4I!mub#;}cr6ub1dTpyAVL;GQ zsdQ*!V&Wyw^C%PwEH5vkl;YH>Q_RoLlOzdY7*==eC{4$4%)-Le%2t7jL0)zor?h9! zp7#oc!lZ3m2w~7@G%!sA-}lI7Q*7IoBZ^|0%@!w5&Odwh?73frotW4=wQt|vskh($ z`{6AGy`@JUd1N-1%S~?IK0+KP*tUgIigI~7V`D{9DUZ?7agH7Pu=4urZ#{ea*oPn9 zJ9Xf|fgRSqu#gQ7jJQ`yQ#3kCYajPK4|FIF3m+D-j~cTW`H~Wyg*(nT(HRNwhYQ zQsTN6Qc6Dl_|pyLu`vi}I-M#N3d4+zjgrZvF-;St;P~->^5vJOaU2uV1k*IIZ4=*j zSzBx2`wmi?^m++OX*!)Qwk?rTGBfknJLV@0=xDdwn~kD)GKwO!Rzy*Pl$vLrImp=9 z7-6_Z9BY&kNGb7s7srv zXq1W&!bEEYXiQT8CXQn>K7Kz}uPy_EMx#wGm&J7*ve^_zkAAe_@^wc?%jNN@#~%CL z^Y`7CEA8A_o_yz>_X&c4FpLmFp;SV>zCx+=0IoX(1b{|Ljg%JYbefwt8`!o%KA**P zO|-U=(%}8~kKY|=A8!Bi*=(*fIyyFKSrW@K(Hc@IpCAZPDkcmQlnN0-P_HjznlZMW zUw2j@gv51Sg5WxF3_@tM7MP|%wYpfFn>#T(Fla89Ej|78ftMyG9-0O%%nLra_YQ^D>Im z`U2@MvV7jdGzC%$QYnv4r-f}BxUPW^nwgouY-qLr7bw8#(`PC(GjHx+U2WEc04WU& zqwlP3TS#eADvgoJqzQt6B#DWlm@tg!b^}_iHqB;h{h1<36iS(Ro=vN@_T%D2!)~*OsttIk1}m=Kp#)KmT>bb)DSK zojazq?&qx#0@H*OC%z<~zmH5Nh3iTT!va8Sf$#g6riJG@7)HO?Cy7RD#qe;Bg9rcg znmA5K5{2ib*tRWC5cCMckSJ0Jq1S;Dk|ZQaV&XVLsf2Df z#Igk4ZjWQfJ{j0exPzm&)MzwnXU~5BuaS}AhqBr1I0!t?p;}!eolfC-E|z8C`wos{ zVp#^B=V2HYrYZ4#3&VgYQXD`2N#*3p`A-K0-S8HA`SO+7nVC1IE?>SfXWJ%YV?`Q` zCf#mteLIcU-P8m@M7!Ou3WgEUZb!ti#?e?IIgMr@CtFOK>b?VgVN;;iEN=dKR zBZ>mzxDTq7qSp(FqKGgI>2wkxq1kNF?RMF{`?s4q{B3CJf2vcbzN)yclgsCGQ|)#K z*LAUN7ss(N42cj1t}6*cg;IJwe<2KG3`2~6@WDra9yI2rPN4utkA8TlSR9!xm&e~5 z8X6*wBYM3MDJ6y>sMlBVeQ;fyAh3wznAOz=%gZa9innP{fTgADm2^5a*K9T?v)L4m z;}Avt&M1y0Vc215DMZSf1VIcWEHB^ORIE)+{p|{ZpgBA|G@VMN@H`vCuxK>e#BoBm z+a`)43`6k67xTn%LbKVdeg63uFAo~$t{esEbh?#VZE?=?{7HaJCc^VP78dH%YBerh zs?zNS7>2=9PyK;vbz#G;-Ijs^RI3YzGMV(hUDxK^xeF{W-yjG=fc2fsL~BiwBA@)K@iNIKY#J% z2Or$^(#lF5Aq0iO5QV}p`}RG==xC87iDx>)N&D7jE3RIR}EZwKlz8zj4WAQrNbA=MVgXwj4A` zlIq2amkup1UaN&+0Q4^;wC*n~%i7!|ZaFAGuNTaoKK;$1dVLkG)sNl3)(MM?*EZGW z-4jrNwY65|yYIfQv|9ZOi!cmOO5r%p_-4L$&p-jrpTG3NxpU`h{bNgqBvFV>U36~o zs{g5yB&nV`bM{c7F!Y{j8n;fVn<`Itz1QDKK@gm|absm|czF0gt+r74;fD)vsJIR73E(qiJ*u00000NkvXXu0mjfx7#^F literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0014.png b/kasteroids/sprites/ship/ship0014.png new file mode 100644 index 0000000000000000000000000000000000000000..49e2d6da3a55c5f5a5c42e7efb9e3b206571ae60 GIT binary patch literal 2170 zcmV-=2!;2FP)%Nv@nefvru?A$pu5tYMPNHc#-ENmJzWAzq_D&=4p z2BvA?`vJLJ8W0>l{9Nhw?Zx{Rptf01-Mfe;#& zrISh}*s)`5>cD}$h0)Ozsg#AT8)%x&$Vdj;Hu?DDPv;&42}6Xw-yhVZl!Rf3Qi>o@ zgki+1uf9qqGeQt_2*Zdd66m^)WtpVYE>ikLkxLK=eBVb(!N^DoAq>u+zi{7>KN4s* zn<6(Stf>| z6GdR#7E($yO#u!HKpKY*TiEtks{G)*8SK>YS$7-CrlN=2Bafs~SVyN_))!}GFIaapfs{VFuA^xJUDt4&1pR*ZZtnU%3jWSO&v{(!CS31D=Vv{(=O?B64SKMG#w#y zlF1}N0Ir)Nm9jBS6Cq%Ed6gYIc3@d1Ld5I6^wKLQ+wFF1C`SRR)%yJD)4wUMuCCQJ zO`sGg1)3(%G}yg+7yZ6R6vb5^MRA+*eV=BtO%z24p%6k51QEKf<2W{z%D*rSuq?%; zOP9(E3lGoD2ZCpB>Q;G>T=2l+^#0F_F0{_NRvCzh9gtcPKM z=LL+9kFHM`xgJu5ZJU5VsQ}-Pr3MNLUD=FioBBzyC-5>#x6gq?R-kD8S8| zwfXnn`{SufrCQ#zXBWL*AEn|vjG}-jQiLH$8Pe|u1VM;lgm-(ZWm%j%_xst+LT(xq zVrSoZ=e<)>hV{|W5j@W)2;}kW4x_P6E@6w{S{<>n7-SeKaj12ts^+K)cq8jYogZj!eJ z)c5`Qg@s#l*=%;^=FJ-Q`fW5#yE|?*O+(W(4jkA=v$ zD;tK6Qt`&)UMUr_w6ro*e!>=khGAG+Sh#h@Fmw#VxC`qzNlK*x)6>s0HT4{gMq{Y_ zge?gDhPvI}+_h^rW}kfWiP!e-EiyJXil!MDhCwRj(C_zHTwEF&bW0rFP@}Ol+ibS# zQik+;9zhV$>-C5tg|6$gTJ2vp^cxDpuvV#5XIrf{K@gv>GMO}qM1mj)>2`bn8=wHq z=GvK?H>+jeAE4{;WkVDxR#sN({eFMwJJT%(1*lZ2XBv%VJkLX^_?GW+wJo6Sc4zMT z^&4~Tc4z(CC*ant#i0ucwh0uVQmMXCtuEB1lxn1!pDC3JGru&)|8=ka4e0_u+J#_9+W-In07*qoM6N<$f~s67c>n+a literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0015.png b/kasteroids/sprites/ship/ship0015.png new file mode 100644 index 0000000000000000000000000000000000000000..e65bdf2b34a685d654494c4c9406828cf62fee99 GIT binary patch literal 2084 zcmV+<2;29GP)Y~=9mapNJIh^>6eWeSEZd|j z$2N?8=I0+QY;orM~@yYJ^Spl+U+)Pzx{UYl~-PQrrmB=dj*xfyxia4 z|McO*hbQ{``+5BF$MJn1DJ7qO{u#ztu3o)5yBm-_gI>RW{l$TSf$=DcN>LQCu&_X@ zwL!DFP8`R~&(F^R>H@Q$sQ(k(lV%h=dYUO04U>;=#B@cj%ykim5&gM$U~ z`7By%u3!IR?)>==d&=vM6)lwxEbiYw%Fxgd`FxHb@DaixNi?1(DHQS)i-Q!4LmWAB z_-Uoo=?fSBG4+Fj=5pDIJ$r^38QDXzSfp4SL`s+8;m7Ff%Tg>3GcqznKA**PC9PJ( z(WCz^eb=}L4(uy0E#3Lni0lY7iqu>tlOdnak;`TAeV=SL;P&l0$B!RFN{O`&j^pCG z9@(rVo9*L?Cw?(LI=Z)X>eMgCJQe2*iD$~Y2AU*E zRS1E#;5g~8wStwERjywBk|&@1Ij%d1ln%b{BBjTvQ@@&c?z!Jg?g z2r_t{$Jw)gs61%SZv`C~D3k#P2MeXa!GThtP$-wn<)@`|02m%Fj?d11jJ1X&FV;?|T?y@jNIL^7Qou z#Bod%DU!sXltOC_(1i!bvG~5lbzSoLEdBlc2q9=T+ayVX@B6gd9jetkwYj-}&29@6 z;GK6WlM4&AIS}~1>?&i?+Mtxc7~8enB1GDXJx`L&2CS@95yD`t033vH0T0*p$YcaT zV9Dij3=a?Dc^)?43tu;wv?%V8n zX)IE1i?x;{u~;h*B7J`I<|1L}5CjsfEkWQhJNxn6^z@n2I|gkw*Q;;7`RB=d_nI}w z5kygp>!yEq9KpVQBLE1IDjmm~B+<)r7_0xD3A{Y z1z1?9&d$tSm=Xef-|MQCP8!EaH@B@`LuHW{Dl_*w6;i@c7lTk51>@c=B6S^EK-VYD{_!hBBeu; zSmM|K2FHPRyM-|Z&jV{U@4YwkNRW>N3h>cK|2%#9@+TD`pw;Rigdj;0k|gQ2CWA2+ z0Ba4N=ioRNAv8j4!3yFyMr(sHns?uwsq8u^z#DJ;X>xx4)|}%wY;H!3j*i~9-WZGP z+WR1BUJY?<3By!%W1!J!5{5DDcEXu6uYKoA-w`OltFOK`zPMPM^SpGQ&^pbjF&1MW zmkaRy3__T$iW(^)3`2}D_$5)K^GURv#-DY)}*zdltKv8eWI}z zv=(R$N~QMuzDp*PsxPI>xpOm>J9qAG`?SApP=HI9u2wEyyfh7#IM!YJ!I*TnGDc&q z!P+z$jm9R{rV5`u`^Rmoxnod(bLajx)o83&(#2!CU|TyADI$(KAqe{JTfe@(K{lKH;X##BwJ;2c zqNv+fHO8bC$8oxy3K0=SmMBuJtu@g)+0k4*fo^QHDvd_t_d`R2tgo+QjHT7;kk9+w zYlm#sM=67~G1so$tj*8gdgLa*YoJ=|YPGsl85kIt2m&7|1N!Gl!a@z#b?={1g-B2S*=!#tPafy)-TJng z{D0SuSN&EgrDm6wZcnG58tKa4s;w^2*B5a2ZoO8k-QKaHJ%a+QtgKG2uC5`admnDA z4jnqk{{5pYE#29%ny?GjLz>lFw-%?59XtBMz(7C8jvc{qBti&+z$Xkly#M}(bG^*5 zD~bXvFW0BE)(i{`5=9A7q}bdHiDON(*`i+mx+g_<4HRH;aj6o9A#ofdgu`Qx4U)@c zi6T{NG@3sq(CXys>KfKsq;%0*qjkcK8#i~nh_-51K>@z_;;Tv&g;<*&Otem@*H?N{ z6TSz~<>kB6Yimv7IKdc=QZXwltF>Oo-F46?ie?uVt8-hQC9PJAd-obYK1BiM=f6C? zyj-VVU*+=UPkU-hTjlP?Am1#d)HNyPuN}v!UAlB-Dh$J(t^a@RcKrwbOcO~N#ehlx O0000Nao3L&v+)eLk~2^O#*n^<1(5F!$S;x>3%@d#>V zL_v=2xiOC7=lIU!KF&E^+;Lje1d^GN+DiSTBi)V9_w)bzzW@F1!2d{yK`-!q|HRD9 z%=t>CQms@fJpAy()g(#kciwqtRZ1BYi^b}N3m16hl~-oNFl_XjM-B=aMNy;GYBfC1 zt2&NT?Q}Zab=O^wRw|Y1%*@QAQ501-H#dVpAYllK0+dRn(+@rL(8o$C3WWj_6BBG~ zY;f}ANt(?j3kwTBisN`7P&LROd!BcCYHITA^z_lw<#Mq+F)==p%eiQskjwd$%LPCO z6BFYjCr%vynGj-RZ*PCOpIpu$prfPZrza;T&Kx^7O+KHeQ1B5#lBSwNb&nn$tDZV_ z^673jp-}j|dinC&d_TDcD*b~GK49;@`y`W-M;RF@Q7q>1JeP8Lgko`&si|>BM@#s= zM;u#%;0ljF{`0NtwMy=LILs=BhWhA|e$k!TIZz~0_2SFi5y$RkIX znsRYnf$u9^S8&H2lhqer{MGr9kr5n6;dvfPDTEMw_SqNp>FKE|SU&mW^QDD_H^0*& zH!Z_+&&@T)#>!P81lEEv7OgF=t58Z}Eu1@do>Qmpp;DSX#>S|E8R-lwXN{Q!r zSPQ=IlBT-%?BV+!xtx!+3GMbaN`clADM3oGRuadWFl=G1WpD2QYc0iM5im3wK`=Wz zd-`T9KlloL>#cX^4xg>HD3z%ch9RQY;Yvw_5WVt>o&$jEW`M>>tQBZ&7#%%=Qj$ug zOrcOfDG4ZQwU=t&$MJ)pYio_A#l?%YtY0O@(7RlqR5n;rW&@WbnmA4{23jpml0dg> zNK*^g9{draNMo(-S)1VO*_Udy+Dm5#0tI;U&3EVa_xA$;tu@9#xm-ePgS8+8C?yag zQ@zvaVvIou$mc<8gE1OoOs`!mNn!v?qp=>;YQOtlHE$Ud;I-GzJptJ?ODSlz4iG|M z3_VzZ>$-TJhwHlJaz1Hl(ORHXW^Z8F-oA$802qWYoId^2*&w*`gA2oYq{^fdwBKL*J_`B zdg+G7J~U8(g@w20wzhWaT5B9fAp{6v0Wd~jtt3q?X=;1D>R?Pt97lZc!5`}{yzuK= zF8M&90I$D(Zca)?nrhP2_5iuQzzAUxLIZ{vYmJm{H(_;ky)QLkKt;Eu)p8r(xtx*_fzCn2hb4V$~2Kv$Go%i-qfO zhheQHm&>uUvm5N~?e{%MFoepj0aL7Ho@BlI`s+E?oFHXle;QEx#>VDVbysmDirBPBY{6nt7H!2Q-IPg1P^zk3M2LjO6GUlD5*j5q zv6<@!cAa_Onfp4Yi#ra5@~}OFy6GofX*P4d-~FEddlVzI0|ySANRp%-$8l?v=K@%x zI%sTc?1@Joee|t-K9B3VUb$TMjvYHzURYR|i=xQucDr1-aAEGojT@E!DQK(Jn%mge zsOIze$FtdNxm+%nOQjM=jvP60;J^W{UAxAeJ9o;v1y!R01&E@kRVWlrwA*bOjRrSw z-sJY}+w}W=u3Wjo+i$=9(R4H}%;K2tj@87?-Tr3v6si{5Kwu4fdZnsIZ zd5_OOU*YcE#o5WpJ>_nw;<_#%h~tE@u_BX` zdngogB#B{h@iXs@H-2|`r+$WxXeQ&9r>06wO-(T|F;20V$8{a@`3z%YlQ@n|sWgsl zTZCc6-n~=h!-vaH)N0<`NI=KO#}2>r(y7m+6f~P{uhnXK^?I|mx!LqOownC(wrY(= z(-T4*mQtdW#Ih7pNN8n8z4Fv6w>d)V}c_&1S21=~8v}si&Si zH8C+>9vd4kA39WiJV`W`WnozoW8lph-7G6Bb(D&E+ptT{BanKqpOVDg~aa|kBQfLjioQqPDC<-}x z^ccCELI?{XtUbMs#9!w>&aU0z}hb6++;7%l8}a zTS1%6R_)B0KTJRO+|S;!EP<2)V?YQ2NIY*9DJ4=$Y}-Ogg)xxL<^gb=v>b$xgA8bb zpwH2xKPn$QxUcLu4pQ2WKl|*-6GEgB7eb=7&&iX|PoF*e1*rV4kbnH~jmqL;t(rzr z4!{IS0!m42+s3vn;y5MFff5W+^v3_>`( z{`zlb&z?Q^rN!`_pa2&y{_Qm>!I<=sQm_@_OePCRa=9$IT!vgO3xr77$F{c`3oJ`x zj3Edjl#;};K`BY6(;*B)wsudYQmLFi{fn8eGHeTUe*Su;*=%_rQ0j~H`+g6l1h%d2 zcf3*(5I9cyTS|!#f+SHykzrzD4??6bTd%hP$YhED)M_a|L}-t^KAHz)66pdYV{kJz_vjF&YkGVMvmI@Ar^0?P$wNQ*7H7j$@P0 z=LTtqDAH&h69j$w{XSp_!;m0|&^lpe=7rOh${)W~RqY6Le*Svp#*Kegw(2g0OlPE0 zknZ;w*LBEb6vd)N92xtxRJs6n&^zMs(T`h;PG?+5hz0lpuwu(0C2@WRVq+uDVz?R@!V*8XO0-PHVE=_IdEZACM%P=b!)i^vcTGwzcW5KpTx_ zZDHX~#dY2L<4K5gex@79LTgA8==EZx6og?w7>2B^t@6w>&p!FlNAo+9dAkAyIDh^x z72CEk<_k!z8LY%qd#A63QNj# zphuA*j$^b=2!ekWM0 z$F?oj*Edi~a_?S?)zvjFU;dza=g#7&+M#}h!e>sNf=r&ufise0csV+_U^ip2u+^Ves?Fx=I}!m=+Fb?^LeCHBuRqr`>e08 z^T{W-c0H2*(}qetKomu_cDqBr9}>r!I8NyGdStUMQ51VOZ{FIr@c3RqeZN3w{PF6gkdyPZEo4npa2^i|Eho?)u|G#HDMUAy1F)!w8Mx%>-9!8j+6VH z9>)>4ZY@+BjpnWg{8w!xpzU^huG{rVk_0KC-|utv>a|^ok8iS(fcAR*xmK&~Z3&bs zSN=X*uQx^(O&AdN3UoB#j- literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0018.png b/kasteroids/sprites/ship/ship0018.png new file mode 100644 index 0000000000000000000000000000000000000000..50e74c92cac5ef723e511f4a3d437e3c418ba18c GIT binary patch literal 2191 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 bUt#|OPbI7GH8#@|00000NkvXXu0mjfsS7g_ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0020.png b/kasteroids/sprites/ship/ship0020.png new file mode 100644 index 0000000000000000000000000000000000000000..da5d88390c09dc69e98bd7aff27324be49be0496 GIT binary patch literal 2233 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0022.png b/kasteroids/sprites/ship/ship0022.png new file mode 100644 index 0000000000000000000000000000000000000000..6828ccece45a9d48b4560d6ea7df6382735e684e GIT binary patch literal 2255 zcmV;=2r&1FP)j*p{XbPwb?W=&|9$8C&Vm02d0ss8--;!h zUD(r;&hFUpyMu+oT&_~77OU0j-1V&6ym@n^C`xg9divO6;wjBxF39t|^tNqV_dWL5 zmVMW|MhL;Wb?bI*+O%n(=Xuwik)}Zlg}I!R3PK2adwcqJ?fO%+diARQZ+<7G%w#g@ z(eCclZe7_C@pQVIcs!1#saTdtA`xd~ zB!{9%T-T+eqa(d))vErHk&#?JpFjIu!PI6c6A3RJMG!?1UzdPs>PWfpE|u!qSFZ-z@=2A(RH z%hyc(4S))S5GcyRGk~T^ip3&l&V0_&rMh%x+DFv#k@aCKEkV++KX>rjsjabY?*L5^a z#c|p=cWxNhjo~;BD_8c?+nXkxPNkoI`VU7_sbu!1gU-wpasaxnA*DhT3C^AS65FpH5c;<}wksgO!_BBh3ul18J!^z=LJa~w%t|W;>9LI^FX(onYpz9i{DuIyBP7lk{QB_G4 zh4_A%si_$bAO0wJl#(cF&}dXB7H7F|VU)SKc^-W5f%L9jf8O=_>o4!?>)Y_1 z8|sD*B_KF>@DRyl=hc*3mW8J27>0pi=xCaT?}r3|V17QJUJoc1ea6P}3=Lgm>(<|* zX%bac*}8R0{~K?-vhR^c9{iTOO@j&npL}wXAP8w|YsGb)E06|Z*gyzDdwVOkZ4-~j z&~*ifD3vNqP0cbmc#&+jk9gc62csOr94Cft8yJR$rb${` zJrW7$N()kmq6WUd;95SPXLR%u_uhLq<#Ir^8lq_`Q&ZFUz7GTl5fViWs?{3LJoCrh zU0umc)9tCJC*7Y&BygOsQ*I)qhOVpFwu-8%D2gD8BI@;kTCGZW+y|e%M=l`_WYmALeP%8P{e*4c+l!!(H%H-2e95|J?uXyZ^W*VAG(E6YF;zo0*vcU0q3hzd|Ar z0|fPYgYoexR8`?(6_uR9Ak3T*$@an67-~Imkhkg+5hCp>)&&1;n z$B&<4czA^S@88Va+&oeWTsMvo5%qe=`1k~hqEINzGCNyf?b=@qY~8wJ=VzaNzNl;R z*IM-D?$%qE9Cc9uk7rFUvzY|ao1hHBnTF!lBP*6To~n}j|R5n^V2s3xoA*b z*Ryxtx&G*i6+dHqe3HSzVajEnD2j-p2*c31?Y5ObL_R;m#fu|*E?pYCu6z3rX;C%m zx=yyG#Y0Msl`EH%OeQ&UAfi`m3t?$ufB z`(->Y&g^UP2*{KQi~HY6;<(az5HKG%)WXf&9eEig1RoI7>u z%+9&F;*XN*Z|b^1Ez8RIewAXe$m!E(_n$m@ddrUq*0h!{U$*ms2YwT!Qe8KtFaD&T d^ppNa`Zs|bKWlRc%+>$^002ovPDHLkV1n1{a3cT! literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0023.png b/kasteroids/sprites/ship/ship0023.png new file mode 100644 index 0000000000000000000000000000000000000000..34d81a057a42fc34c45eb79944ca759d72d57478 GIT binary patch literal 2266 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0028.png b/kasteroids/sprites/ship/ship0028.png new file mode 100644 index 0000000000000000000000000000000000000000..e0524641982e176534b7885c2fb9805744d10bb3 GIT binary patch literal 2174 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0030.png b/kasteroids/sprites/ship/ship0030.png new file mode 100644 index 0000000000000000000000000000000000000000..ee734fa60a361cfe0afb2c3897f2e03009367d90 GIT binary patch literal 2147 zcmV-p2%PtcP)8y?zG(xo9cwT zL=qlG6O_0Kpoj&t8Fo1TM>0#6BV{&BBB4PeAwVpUfOt33EP{}TG$_584H*%Wmx*yV zu|wj}Zo8}Nb#Kk0O@>F%I4A~bl;5Umb8mh1JLlYU?}dl@KhV_F6RrClqi+Ss|I+KP z|9N&W81%1QyFRzsG1^uF{a8~|PqaS#@SXO-gHKPt{POQ-?q{sARZu@xqfu+V`syD~ z9Xd2Mt?N3Pc7MSXTg3GfojdoB&i3t-zG3L3slfA0o_gx&_8T{rJDVB5<)N?FYt8rH zf2Tb;IpHfM039I&Qff?0O!%9fXUjo9_~4!M2Mt_O@mhJ@TMJo;GnfywfWXte?31wKISWxVOctcp<|i?T_3?p37Q6` zY52Q$@0{uNR-2W|Hh(Y}bZ^{PK6S5oA86Ir>(%CKul?!F)YKExQ6%v^7soLGgUgq% za`ECNzWnkl78g6LtlS}=cd6BCR4Usjl{`$-!t& zDl#@!A(wNwdGniAmWkHk!-tOhev@{)4Ll%6ZQE)+_uTXu+qV6cm2Ytzn_6w0eBQ;f z9EwGkd_IS+n>bDm%QEr20=Df?DitUe3wU0MVzI!jTerJ!yz#ETfB$|OjR{VjI`!<_ z++6z?J8D^0tI?<(Z!~H%)#^B&S0tBn*uDEvOw%NvcQH*9!!U5&9JXy^Sthz}B7{b{ zT)?s{48tOqbFeHOO;c1V6+fRZP_H-GyLT_9X#&46^b-^HGdp%{pQ+Wxaorr%YIOtD zLe~u($HFu%Y+FayEi_HXFidn^$1p~pjAMyqX=GVO94BO1KpaO51~C&8HFSN{820Vk z*Sc`w!u>f~DCB3F%|}i(o4#Kx7OB_Aa2%Ub$)i%)hG7_(rjDkW8&4e}G=$Iqfu?Dg zrp{mxVwxsWrYtY7@ad-Dyvtw^62~!`CRkbNp;U%#S6Ey71}PQ$_wOM|6e}w$+`fH>Ua!aM zYL9NWi3aU6?LCl&~i0TL+{ zLTH$#L7GNLDG7ohK@cE>LMcTYFOjB-G?f4hheMP~Szqtd@AvrXtHsk%6wj1OCBIxQ zPyYl|*Y)Njk4zjtaNx1y^?D7P%2?K1Su7o zmJvk~$jGvkIF^7SjwMQ|jh~UEsU%4xN~Ig`DRCUr>-AY&Ts%ELKYzmWyp|B+hoP@j z%B^Rf`Q4ex$%bDj;^%wF}WhkAPiS=90Ms8N@Zl3LP|xJN#b|} zmn4!nPJ#3;WSSKeCi-@!D^=_E-y{eHjwgB-2bt1~aWaO6y*QNyw< zG)+SYfnf*+g8&dzD%&>R;l_=d?Afy$$B9u2G%aIoEddl!lp;h%6s0INaxqC#luAjG zlq5+=(}XyVNs@#piizWd_4R$ zRI3#h78U_mUG1~JzD^hp$>-e-7e}F&G9?H?bX{zWO-PavOcccg!H_gf34(wi2-&ft z$-+XXJ3oK@Sdye&DdkUg{dYk-o$l<}vunr7#FeD5k#>OgCD%+^l zs(4<3TCGl&W#~Exk&&j^2ts5GhY2T7zP7#JU%TfSYEuh-7)I?djOh2*W`Pqh>h-2+ z_?Bfg9mk%|vaFjVDPb74UwrZT6VE?CJ)O(tky2rr`rWh`Ek_z*7%@7Hj@DgG6O_v( z|K~t|@dbpGvYRByTo{J!_4UE*`ugDPa5y}hB+1;BEAww(ySDI=Z9AUp=1Q(>dzzM^ z6qu%l5MUSv!(oV&ithruv~+W>)9Kzb{9ATY((A2uKl|+8v!8tO&(mQTuGzLzDwj*X zQW;8RciXE{YGdc0vAle9?&{U~v%f0n_jUdHV!Pe`*V{`=H|GFc*D2-mt|z6!b#1aN zBZ?CG{eJiJ&%cqbsE68| Z{sSJgz1$fa^mJtaap^aK|KMRC-C znh+~VA#p;zcH)@WYdg!%&dhtShgn;t)WvD*q*C*}%;;&}o6r0Goi}h#_jKp=;)_2z zbhjD4Qil(}GTCf?r1$OHTff^ZTk7c1Umu&Bo6(Dl7j&gERNKxRbEl#HUQa%`w|?Tp z@28)8?%9Ut*;tm%{{8#Lwlmk-X}AqN`|NichY$a3^8Wkps|sNehMHVXAcV24`F8~L zg%^H!=+#$$J|TnvOtcobt_cXV-X5sfQ9|IzkzXBq>7^f!3n7T2gdp%x%E0$s;#gu? z7AH>p;rLgrvtuz@E)UkGrrwx-=9z;HDK(~Pkj?r?2}zO=$C7L|!1J7Kt+ykf2M<2g zn3{Uyy}5$LROI_npS%xkVf&+`6^Sg$tiht=`AN!gV^G5Z86Mc=0b33I%N2CZEp}1Obj?v#@ZTiHYBQ ziR(WG&E@(I6$<&WT&}M+I$9m`JdZez$z)s{$Hlg7eBa0O96Zm%aU8N)56{bx$z<^T z0N?j16#8*pk5;S2^UuFHy1ssk{rmS%EG#S>pPijO{%^Tn2vI8*3*!$zylcF#FH1h( z&(P2ywr$~g4yI{gSpv`V00Y~$2?7__br8ZqYXcz!rBVUcb;;+mxUNqw*GFHUkLP8^ zo__k^(x~^L<6#Bz}i0*DhveW`J!b zlMtd-sSHn4DnoV4vZ_*QDwPp}z-3^dgk=c^2MY)xuq*?^ur}|dOktG*(*)DBkWvwb z9cfpL2aV*hVVOcgxDYRBdDT(7Q-LB;9 z*%<}~@>D8=EG#S$MP1tM4whw+&*y7fTGVlz`U4M)PCoR|17p6QAqWC|-{bV@vsjiz z9LK$l*to8ZVHiC?6EHAMO_C_I)-*|yLW%BF+5zZkBpS7uIsXq3`r6rgupP;LJ`L?*{p|Y zrhrN*KmrCzDU>p?EECg&B+;y|uM-3w?RH3~(?&{-lnN;ot}Do70_NuzxpwV3tE+2V zzy2BL&YfdzZS9LO`uO9IP4C+E;MmB>5RPMESr&#NNRrfT*=*k?q+u9zyB$E24r#(L zL@7{8W15;cR=t%KaV&{rNf>q!LZg&MYe|x%uyNe&Jum5YL*h8~!e^f?0mc_CS}qS# zDD>kv7N%*Sb@~$97PQ-4Ow*)VtuQw?4?vPA!Z5+}5)8v2Nj}FkHA>M-4vmx=*tn`q zFvCzNC3{wty_ikoHc4WZmzSHDE?qgYw6rufIy%~@R4R>4(CO(v9h#r7jz9R|Xv6b7 zGMP*-b1jt8#Bod<$2}Lr%1Vookusf*jn)R11?eZ8rrS-hZIdXHJ=aS>lOz(u&^<`m zv!dF(S4z@uw`sRK)n>Cf*=#nsbm>wJw&+Zr=hi*Xs}2p75B2xws@ZH%?duD;cI^f? zZ>|tU5w%*CM<3nIz(78AumUO7CSVlB7>3cSkFsa61f^2Ybkpg06varXND{^B>MFCd zS5GZ2F8x4CIk&;ItrqGyPCc6q>izw>u~Mm6&*idJ&-3s+mmmlb!o;!!TBlG^lmczI zQYp0wltLy*Dd~2j&9P1=JhirVYdVS|mX}w)bUMB5UN3~G*|t?J7K`J)pQ)A017oI{ zb|NWLOD0Kzl!{Eo!*$bIE2Sa`0!BuL0LAd|Fj{L|*CR<1jvoE(x8~;Nr?-0VZTEU9 z<(!mqZeiitG{BWBpVnO08Iw{rwT7pj`gY^Mfp3ggDkDUZ#J1Bb}Spbb` zrq^69SB3el4RpJp|0u2XS{x_mrIbx6W%Kgot8cyc-t_C`a(U0l$j}}k(l|{Lg_Ig0 z45CO8hT&$o0Gv59bNt4QrSm%mx|PnH`RMq?iyyx|JUq0gP{>scBdxJO4>pbCjf7iS zX*ECjAni{lDmMnA@V3IJOF> P00000NkvXXu0mjfUZ4Q{ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0032.png b/kasteroids/sprites/ship/ship0032.png new file mode 100644 index 0000000000000000000000000000000000000000..f438621cc7d5d243008f79d367356bd45628e273 GIT binary patch literal 2004 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0034.png b/kasteroids/sprites/ship/ship0034.png new file mode 100644 index 0000000000000000000000000000000000000000..ae436b667279228acfc8ccc1ba5c88ccd03a240e GIT binary patch literal 2110 zcmV-E2*LM>P)q==8 z8j?bsD3M}r6)DgnaY5#WdO*E2>cOtyKsiu>)B}ZZsVX5Aaq1ydNJXJ4y}$voJ`@p0 zA&?L!v3H5>jqTm6cfD`ldo$C+Tc@Rv;;12MrF^9)J?YJx`OI&AznSlQa94MAS9kS4 zDeF!HZ0PXegX5EvKcCvO=lgr7r~h8Q(>!0&$jG+Q#~*)e;=>Q$Eont1TRA5IJp50wCHTheZ?lgacUghVN@EC~pVS!a5>eCt77*BzHqlF6hewr?LU z#c^B>f}rZUF3&yp%gKWW50qTjMoK}aqbU}%02GT^i~%YC*;gC~SXPQk<%{Z##uJ-` z@;tZH*H@hIeQz|I%^bFEn`|}*7{2l&3n?%lf>9v)<9Xn_9y z0dlzlsg#Rl*(fDQrCh)VEQD}w$n&KKg+gv(+qU5e&-0N|j24T9!;WJkrG;(V7^Asx z;Sy3xgb*A!a6hS(&pr2i7a=6sY!2785yBA1nmE=-X;@zmh@zMv2*~Afd;7O2Ob@KUxbcOQ1E_c2YDT=yqd#-$6=H3NBxs9mWK3BtB*!=*!q4jf-yt@GMzZyY;u;;&Qx3VQtr zt=AjnM&sHVh`qLL<)xG)mxYuTmL)lV{sNs&fDnRv@7>AB$S{39IjmoCknU0hr``xQ`twY9bK z{CxFK*RD13eQ#WAgVsqz!!Y91sWS*6da1ILty>3hT^p@6xm*Du3_>VCV+^dV1q4Ba z5RxG1qP4jGzA=VuHic~qTCI>&${~&o%gf7zVaSOS?;cxStyhPJhQ{0NcJ(W!L~Grw z*BkH7&wuf^APDvXB`GDf+A>$JEM9MS=+GFB<06EEWl4-NL{Ur>DYTA=B25%2y4@~X zE8;l0?~OsL)$To4Xsvo}Vy<3YrdC_#v(IMp4?q0KD-S*N(B#U>%Gqz+o(hHh)b8E8 zUUprV%a9oaFK3T@%iT!#>U1N9314xkt5<8ZCwCvN&!t{p z#q%6o*TJ$JY}>~3l4zyV9>xefPZEX_#}P!4q1_IMV{lzTt5rj5@VpH3^YiT6cMnTT zH7b<_R#sLS7#JdoVk(tN1#Z$fM^SY4>eZFko6Qzd%5i`&>=MT@aU2qcAwdw*?Iu?g zDZ(&9skm2^Q7`%mr8IG@h@ybe(Gi->b(WTvsn_e&YAfv6v4dq??1$=&40 z6j&B$o$R$iuui8Fpp<4|VSy-$n3+wJu?7Z#SLvf0eUV~_p#`0VT@ zy4{dWCQT~k;rkw*=i$08ww*xRwj{B7HkKu^EZN&{1+7*KrD6aYjRx&@n@*<#Y|@l$ zM#W-b^qFUVaniD^QXCsT`sfsnV{_`%X|`_Nf>H|K_wjuf-}i7`rw5)y+;uHxXXm(d zY5rKV*(x7Ac<^|q)0uw%{rAUjDd-056l!xnjc%DlfE0&gOT)cSsflenV=kxjT;o;$l^XJb`-Ez-)qIkB(CsGs zu4M_NG$;jN2*a4UxoY*TxBh%A48xM+xTRLBHNCdhdgbf-z2%@AuungI^kkt>C>aCB zfDo{@*1>g?JQc#wZg+Y1*EwG#b^!gBp}lv|8)e!Oxue^v;8B=+#$$|3JOos9F}J(;h-> z7#K~n*}UO5(!c9l2W>R2Ro-~xk53q*(c0kq4jcAye!jZtUpL=6D8S{*mGb1|bD~~v zRE;r636^zpb$nYuH}v9*za71Jac(+}V?O@)^rrI@ZZjyr+i(A6s#aUB&dpV(Z}-?Y otz0fQn$PF%{Liqv`hUCv{r~^~ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0035.png b/kasteroids/sprites/ship/ship0035.png new file mode 100644 index 0000000000000000000000000000000000000000..ccae1f4af4b6874623233ad4b48e6e1b778fd735 GIT binary patch literal 2197 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0037.png b/kasteroids/sprites/ship/ship0037.png new file mode 100644 index 0000000000000000000000000000000000000000..66ab96ca0d4b77072d95b8ad41af94bcef898556 GIT binary patch literal 2264 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 Z`VXBMo5}fkq6Gi|002ovPDHLkV1gneS1$kn literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0043.png b/kasteroids/sprites/ship/ship0043.png new file mode 100644 index 0000000000000000000000000000000000000000..222c63a47cde82b7ce763da47dbd028603898de7 GIT binary patch literal 2281 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 m-V4}=W+NjbJzwDd8;?>Xnf|E*)?a_Qt_j~zL+ zQ$KPyU~Xo)oIi8VJwJHq;fH^6VrZyT*@-V>x1erhk34dCZhU+{`}U2}ZnxJz`e^RW zZhhUhi0#lugip63nlSz|GrEp!Fg9pDqy%V3i4w}iNXG*2PY1eg7%Eq!3AaNXLM}Bt|)G&NzMsYSd+@w8N-0K1 z_jaBBU6ZGDIyEykw*U7RE?na4uNSy*;WI8?{G5e_Yiw+6Fg`xY`g)yr-#zu`8+X?aiMU92}Ug*Eh&y z(nu+B(a_!nO^YdRL zghVNuk&)rb=b!)mz5hCBvDkm2udk2Z-aLguo+Q!uzR$sfRc+s_WGIezttzz`m2Il)DLKVwwua zu>egFXoN73613JtQA`*HL{Ze$d0j=+_p{{}Uij^-5MtW0ER<5%wuNO0Kp>@wWf?S^ zA2rADn*SYHGrMQcozq%FlE4>VT*kI-Ow+`+Eo@sN zr9cQt5G2%UZQ?i~iW0OAX*SzrGAVj`T&AWbw=Gdyf_5N(J?s0~lGe~}MMhh)YZpY}+7;BDB_k20~h`kXEZn=TF8w{q$2Ob`^Bbo^s{*@n6m6a@msB8m&Rf z&c#_?UPUQ;vo%T#Lr|{=wA%@mWntTjFieP|h#&}PG@5u`57lauAP9Nz!3VaL{+ohk zvtIewv7={vKU<0-jSwIN7>3~5wFO+)+0;%GAq>J0gb>7WjMkbU2+=yB)k;`gTxDft zjlRAdmSxaxw>f(Bscl`V8-flEl`79Y_sdz&^GaHG(%fnV)M`x(BcWce5Y%clT-POvV$x}syYHTwe(0gYw;a{~0QJ0{@)J)yddl~` z5yJVR9EEC(dNfP}p0^Y)PJ45m}GbacNh3}zt zr%aM0AxRP%jgUs8g=v~-9TUef>9j+VBpg0`|IFm%_$?{Gw^orL2-ervYxC>t)p^hJ z%9dq$T7zXtqy#A;2ogT|Ix$x z!+4%YlITs@iUCc%-dI0-_HSFhpop!0FflPcbLXAqN~v^5MF@#)tLd|6|H{b79{T$F z@H`LKbr~ESVsi3_eEH=is?}8-M^UZT5F+Brm3hX;Cny&Cv8+yPEK3jsnx&efBxg^3z_5Cm3!~~(X3&Z7>2|!I%;6siX=&{D+0tQWm2!VsMi|| z4D?~!oxU#wL{SH}uP!;y9wwXyUr5%`s1JmP4mjgP=1996I#lEicu!K^@1L4#RM>W(87qF5TGJ zJ`Nr{0Dx(B>blhmsMi}*tJV3%#l`tBjFD2%Znu$A69f@aq`Ck8%1oiq` z)oQh9G@4yK;rzRGwcHhp#hG+EO&Esrp68WDM@JbR9%gQCj<3G@iYSUo_4>y9K@fDc zcHGFiPW?AFF)=YSH8oXnUAMHlx>`=B)6>h#%e?j0TSM)3d)Gj3i>1@)9X*5pPuo8M XP&XLQzMH@)00000NkvXXu0mjfZxb*( literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0051.png b/kasteroids/sprites/ship/ship0051.png new file mode 100644 index 0000000000000000000000000000000000000000..a1039a088a8ef9512b90b221b0dff09b22eeb7e4 GIT binary patch literal 2172 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 X*J8k4AfA?700000NkvXXu0mjfcbA7I literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0056.png b/kasteroids/sprites/ship/ship0056.png new file mode 100644 index 0000000000000000000000000000000000000000..c26fae984894177a57051d16f144aca58e06c5d5 GIT binary patch literal 2379 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 literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0057.png b/kasteroids/sprites/ship/ship0057.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2733a4d9414c8986a93d3a690bb42224e88cd0 GIT binary patch literal 2354 zcmV-23C;G2P)Tm8EsHUkMfj~`HFc^p~S<=+r+$!|irOL{VhZru(U{57XEfAsTI< zzP^q?z(+cr<;55O*gZ0G;rLwxU9>3lQZyRra=8qgPJ_wGH0RI9kY$;8Ji+G8590Ux zak<RsbV;C-6E(4Fpjo0fY91hdp|22mWeS|2A2!cQ`Si`f= z{+92)JI6E6{I0QBoEg0y==W=M)28;FTekfC4~C&3Npq74g3xYRHUPFQAP550DoE1Y zQ!35y_S+v22-JLUrD3?xb&c9uKf3Nj({vQY!K06ErM|wNS6|(SZQD#tO!E2X$LQ}r z(Q_k6_`w=IcC7EEL}H@PvTQ8N#(#Ms6H%0CZC%H+kN@ksYxA^#Z4d;3p`l?02fwARE<`94!sYVd^?LAloanlS zsv79Jh9H2`>B8Xkg)-rs?hNBa_JzjfV02{dhcXoK6E()i4YdP1DeI6-knjWd|VO^SP;3 zEvnTj4u{0p*d)h~|1t5>h;bT}kLQ9xBia=B>^ zAO0N6vS?_iClK&s7*5=77fz>vqNu2ChX~kIt2X6wg-WGFxm=}KEOFsNjG>|7*vB7#cGX?df+Koa`FtU!D6+Rw zF+2MEzow?fODGh;@Au;IxKI>*KBHAY!0k4eoi!j_ znx>+vG9aNS0)jACdu`jKQYkV$T_TsulS*ZX$1h=;Cbn&Nm&@e^CC`<*YOj|hDH;j| zyXxu|;q&=$yFECa2AZbga45LlURJJbMGzeGwG}MOq*5tUEEdV<3#3wME?pXDc=$ZB ztkBi9gYof+o?C&0YeZC4)pl8yn3*Zk*w}!s>u8#aqNp@9G*VmJKqS&cE|(>hN&pr! zGqXUILZL`1l_8VKaPHg%mM(2#$BykBIPhNg#Khz+Me~}V#bU7sgg#Le+soxy{C+PW zkApJ#T3cJ&xqkincNImZTrN{66w!5^SS*fVC_M4R zV`MVpRI77k2uw<)Su&X{#o{!vSe&V;X`XuO*DPJSm`cUMG%W@P|J|2Nrf%7GxFVSn ziA2xv@bE6T+e2ex6MlaU>2wauvbl6=0?V?fRx9&=Vv2M+PcD}wm&-6Ra*m0KNuGOd z7fY5jV%s*BWua*bXU`7ZmhEtrI+{+Wj|YQ6Z%0Q*$MWUN85uc8sZ>Ii9h6EX48x#Q zDp4wx$Y%4TQYnUq&l3*U)77;DMNz-6roonT{Skx;GpSKU^pjBC;$qF_9#dn(QkUrz5koWzy*k-+p_xueEh`&(^KK=ulOO zYSm_TwhYXj0ssyke82mvuTCs@^KsLl0HdR$XTss|_F}Qf#fukvPM$pVux;B9PfR2t z@%UKJ*x2}!Cr+I1X=;kB35Ofjn5K=QNb}ZP*fzZO+P+=MWa_qThXow&8X6ip*REar z?vW!$8n4y<0)d+73oktXrQ7X}Xqtv33COaDZNs*0kKd7NqZ=NVn78!Xmm7wM%Ohj*utlu_Acn)_LHi)dD(SuKK^g@ YUpi#BcxupQ7ytkO07*qoM6N<$f~KFKC;$Ke literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0058.png b/kasteroids/sprites/ship/ship0058.png new file mode 100644 index 0000000000000000000000000000000000000000..86e233eae30ad5c1dcf3f3b922f58cd5f073d5d1 GIT binary patch literal 2367 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 U!Ze|PzW@LL07*qoM6N<$f+uf*UjP6A literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0060.png b/kasteroids/sprites/ship/ship0060.png new file mode 100644 index 0000000000000000000000000000000000000000..b227019cd42d9be548b0746d85475c9ffebffef8 GIT binary patch literal 2277 zcmVzP; z30PP>AWKP5BO#Cy#0yd-gLo)N5c&cyfxiJHB$}v^iW(GD0YVM2Ku{1!4Yv@`I+UP< zIN0^2G2YDu$KIP~cP?jUrw=9w5+@~>mOkY3cphdx`u=|Bcg}&E+~g)VxyeobpHMDd z*tYH7r?+jpcW13uE0xQY(yt-464J#BLqm5CKlRk_52R8_R8^r|u9T|PO6l{@kBmr? z#JAs07RJZ_S@_jOEvv2XrI-FVrzkS2svyfUAfc)XiXsCtvMe(-H8r}huvq-~<)RL3rV($g+&4DX6M~s>-OUOccp1 zEj96dmm^31G5XO*hYJ%EQ>C7s{IF$Nn^&#Mlh1b`NiylQLAhMTb$vE(z7xYRNTt$b zvlfJknVFekcD9J;d1Nvs*=!pf9XSjmMNiKv%H=Ary*7fPC@6};lTSX* zrcFbn(-nrXVbJL&9fC!5XD)6-2ZXOYR6Y}jxM z0CRI^P*s(=xe^aO^ji{%B%UXLh-$UU`1mCI_Ptu#v**u)OH2023r_0gns;Ggv1Ho~ zgz#}3k9yrEnbeUaiDSpUp;Dre3bh?dty@uzx%*@QNfB)N~@4x@|2g5M@Snf(1K+_alw@ECP zAP7Rn#>Q#2TGVPwq*6&HC#QMqt@lQb9Qo?LD2nPUxvVIvC`w;4nb;mh(T;k(+N)^_ z`FsZ*9XWEjEZM9@DrJ((nV4qkhqA{})fl?26N_nF7-Np(qw5-B7~=a8i9~{T-BVT-RWcTcB z@#!l`t|)4n>7CiEwYj4si)C4)Qp;(@V#|WDn2x4tWHK5-uq>h|GKwN0M1&9lng(Im zA_xLpSKxWeJ!#uEu~?j2Z(UDsZ=S=43nO!LrT-`imzkqmw{D46ukK;>>K2_zJSDAOyIsho-4`o=+4-#N#SL2z=kibv=X-v|163MuSGf;mn!u`SQ!7 zyU(6I_lqQdmgwN%-~rclOSM|9VB5_R-}i^(@i?lwoTjQOSXMi0*Y@N4KBrDiAVdq# z^8g9g^#EwK0%|n}-*@qSf$RF%wu9?BR4O&9)hbg{Gb2|c`7_b(?(QwgWU|+`?VUjo zAW14g5MdZP-+w>PJ@?#4U*8%eNupM(;CbSQ9t;S>5NP51f-v-O-2l(?a2yxMY0_vo zR4P?Ur8A>v&YT;m)#|&hMiPFq>xB@djT<)(r&1~E^*WYiQmxkM?96iZ>^YW}91;nQ zZ@xLo`1nbhO@}CI;kqtDcr+Rgj?=`p8`Nq`RI4=>7t5SGH_z;Bu{1q>>gBn)(u1Do zUDM6$$3*Am=0?`7TSqdPL{StL7tfQ;X7D_h{{CCgG=;ITgp_v}{{h;27nUJGy>AJ6j-#4wUSme_F|YPCA`dYwcf z!Pc#J(bKbvC|drG=Lu9*<>0|jcb_^ncSBwSmmHyuMnkeJYrE&UeD>Ml)taVm@qNEH ziXy7j8uRn#snzQA_pjyl+XvXRX^2cF4M3{}Q3S143n4`5jW^zWqSVB2;*2!dh9aXEErc4T_`Ur#*t*zX=+y}A#}G7%!=r{{(&E38|$ zhF4zs>yGj9$s6{zTJdCx#bSM)=UsBuy1)Yu+`sGYyMMDYik1_UWr-+?$Ye~qx;lCH z-4Ax}-TRm8+T|A`ue5k2Nq}R=PK+!M`WBKD0TSKac~-6JVsdhN^hQg<6>j)f;=AvP zg&+utqLB9XEctvVAQ6Two_p?v8?wtUX2nE*2?q~;{LI?5c}&wpk|Ja|;_bKJ+kN4A zd!xB#(N8`(ysKC|I~tEGK*;?3`O?cTzjnh*et~Ni1(=waDnwC)5E0KmzvsVF@*6@f zXZ7vf`-kGAk3M|Wx1s+zTeb`h|Jn{WxfcEn&o&j241i)J00000NkvXXu0mjf68u-$ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0061.png b/kasteroids/sprites/ship/ship0061.png new file mode 100644 index 0000000000000000000000000000000000000000..c0302e20d1d1c6fb1e3cdde7a99d0de977db04e6 GIT binary patch literal 2189 zcmV;82y*v{P)ZpgZ; zRoBaE+{}(+vO9a3**WLTnRDyIY^0iviHX{W`h`CS&dd4m{aybD?&+TH>7MTC|3dO| zFL&(NzVq2(UVeG^RBvx@A(2p!Wr;WzD2j|M%LpMja%8al@yCPZGiS~Y zPfypU{%4?1J@wnYk36!iq$mpQb{koi(KL-jLPt?#6h%T+WrUFU=%ZuhQ>Vs<>-E{G zg9i_n?<#0dPrmrdD=$?vO(lwAf*`=Mk|YueAQ6UO7%H+X5d<-cqF~!iW@hTuXP)^( z*>&6W^yCV;T&}ccO%Yw!xq9`BvSlT=&dtpa#c|BS!oqMEMtELlDvo0k37ylY&z3Wp zbg|KBUKgcr1O<5Y)jdtA$B$-T_zP?`0otppz?RE!A5fmnWhTFD2ha*F~|ITi_y`4Q?J*VnyM0pA=zw(zP=tT%fzyh z7=}r2Zx5+d62s8x>FMY4ytZ~r;(zkl!=sBOEbsz#&HB9)4Pi7#KdI|9QarD(4iv( zuIpZFgUjZqWhG1be6Ezw=eG9sdV70#^UZ&-fB)fYuzU9Wk?H9GI87usj5nZ zp&$&Q*<2u#N#S`ut(MECO&fXdy@SMYOc;iYjEpfeGtb!ANsb*G-sU*Yvcu$SpsK2p z&1T5u()9H7ECH&hs)nwssA|_W%j&{`gr;>{7J#A%AViV`Q50j_5wh$NMFFi=8_Tjt zBy<)Q8c34FyYC)ge!fvXeR_P`jV>+=+TY(_a$UFTy6xe7K8L1hAlMOzx93aYB0 zsuHr?4Qmw1xNaLEAdyg6Ty#)X4NZ#)!k7UfAViGo z21HR8)OCG=z{hc1S}mLT`399rwR-X5XFDCo`Of3^zjJhG?b@}aX0yq;b7xT$1;=qn zrwxuDA7N~46w}nWdbLKQv4Ey&#BtX_0N?kBV;|p-@O>Z8^YMKT&+~EJ_7da*)oN{c za@TNW~=r3?9j~2Z24APTo!a>WTafL*Q-K^f@N6<0j*Yxxw(0g z$t1oX5=AjWbYYv#CbBFMh5?<9hi!M^I-L%Si;FayEwb4RYu6ST8#}f0%P*&Y&{lO_ zdpdgb=+MfQD@#!naplT1{ry=M78Z#jmcs1Q>G(uZa4o!^*LAMdvWepuNs`#GVI7+` zZNRcp96EGl_YZ~q8tCZgXnD()EhSyonVg&~8-`NqbbKJD)A5&%hwHZSyf%%-B5@4s z*RN&s=AV#GCoxTnAPCVkh2zIhmiO&@=Q~yPErHI=%naMMO{G#PkB^TJL{Tu5N?BXF zxf$TPZ59_7NvAC`nJk+(|CB;uB|-?IZl@v$BEm4DR-3Nw+xO31w*wMxxZDLn(DXcS zabjX(CxGKP%}&QJwpuoqE?xdxr82d9VBlA!2Or$bx^+bi!$b%{9Cw?$5D-Zb(Z}@k?C=XO>=~Gyox9^dA- literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0062.png b/kasteroids/sprites/ship/ship0062.png new file mode 100644 index 0000000000000000000000000000000000000000..41d98263c39a5a1edc067e60e35916925ae7588a GIT binary patch literal 2174 zcmV-^2!Z#BP)-BoQjxlj| z^HNix$|@A4DmEwxQ6xexTyQE7H~s>WBRz!!2h>=VxFF?{f{KKRP$(@bmncPG2UJ38 z93{?+9q-z)V(*LX-FbWFndxDj@RGz3nnXS1D?OT(W;FWj@Av(^4BXQ_-P1k&U&_4M zje&t3!!Nura7T{HF2>RZ8hZC)1Iy%zWwu#nY z+Y-|hjE#Lbdi?ljW2aA_8NVB#H%&djmM#5*0|Pq-!JyUhiQ^cp6Qr~l9Q@VrbI(2d z@?($vc6TQ`))nH_sS`AV~lca=8wqG%-y97;M?nR~#Px z-7`-;_2kIYPd~Z$#ECCIxf8$}f))zh#a+9GM;u3DniA7A2}6aH7D{QPlo*CUN(0+Y z=Sro`#TQ?E{uL?3?$f8IS9^N$yEkv%JhE+D>E&Xve^{w#HJx@!p6B0k7unFGg+h1f zt+)O>m&>IAK@e!Vx-7sVjzI{)S6_WiwOXfEt8x7J=VUTza=9$oYzI9(1qy|39LJ%* zzYncrqDbSqEfy9QC#I&(j(MKP(o(fNGjnO|XM+M9IPk~uhaVmsvTaGNR%deZG>t}s z$;mSWK|ns=Nmo}6$FWJLGo;fgve^ulWsypyx$nLnq?8m21#H{qop;7a5`#^fy4kbm z_jGh*h@ylzjtRqn+1a^?YPHU}a~H->ocQ;6xm=lOwcN728K7yJrA#J0^7YrJiXVUc zPvSTxolcR&Lq_J%a%d)X73lL+8cb1z{L* z;leCJ2p)fYh=(3}2q6TiR0_)yJn_V1Ls~0b*B_dlt-SL3>j(EAJ^GJ>*MXX*St=Cr ze|q47UyS7QUBq$3z`!dgdqfh&)Qm(Mq`!n@h>@f@;_yO>p)W}XQ;35 zzL8?FpPrt=HNbD7B$g#flJ;-gHUWv_m?NkX%^LO$P#)(O7vlOzyD5dfDj zFET%0pjckh4~QhuC?zNsODLs? z%dH!|NP69Ls#dF*ro{I{q-+;Q z5Crt}^pejP=-C%N5E8)iyt3msmA<~dV*B4FXdUCaJ_`%;NZG-jJb+t+NiHf;3Qfu1^b zYHVm|Xr#Nlo3m#lgb;*bNEB&y?HXcXVGe*WR0ttZN|7WnNzzU+#Kf^ishBv9P)eh9 zL=Z%TVTkK`c%H}N;uS7jm>FGOuI~FOfY*UmDwWFg^fZk|1FbbmDOOh2NTpJMvFf_aFZLHODe^L~02wJb# zE9G)|J&oY|9=`9B$z(7L)<>L1h?uoC57RV>qBfl8g(wxFREX=g{n%`_NT*XYo2|;l zi?jP&*Slr78-li4t+MO7G@DJPrl$7h^PR(`Qt_3Ql~t6A&|0sjHbUzN&+`bw_SN%z z3WYpMMLhDzuj%i8PIx5+=jg;+2 zf*{2AJ(N$m?ItdSL9Mo0dHd~m_ufuO_>m3&hsx)3rBbOh9EK4|0+wa2 zFVF^t0kgAn6Ne5R-o3K2dRx-{U5Q(HcxGm9JP1Nev;EDKB+0c?uQw`3kN$n%?egRg zwPEUSB+HU$t&!3sirT54nVFk7eE8kHD=T*f5^gb~lao_prkMckRGTI=8jZ@~!+*V_ zknl4iTC1&A3WctsVZ@~ literal 0 HcmV?d00001 diff --git a/kasteroids/sprites/ship/ship0063.png b/kasteroids/sprites/ship/ship0063.png new file mode 100644 index 0000000000000000000000000000000000000000..3f2ac1021adae4606d58854d763332c431d3073c GIT binary patch literal 2148 zcmV-q2%GnbP)w3@w~nfd5VR?R?_KIc46W2Otaan-VCT~X+zM12OoZ6eEfmO4jkAwaplTtkk1D*Hv{ZH zwIS%!Pd|Cs_j~+IChb4;(7uU1dw%+OPfsd4Gjsmr%>cZr4MCrH;?cvdoA4dS#&IOM zT>sFXJr6v#XV3kQHyX|A++6;qM0i8c(a}5ij*Q$s)^10HVT3UPV_;xl`_RFI2PSsy zx@}BKi`m)v8w$K3=-qdZ?!EWkd-l4n0|<=KWHKp)5NHke+;hj+FMjdx#K_35v0`yK zC>EE49|bg-O!x;5?3-{LiR(H7&+|wm5_q1A<2bmkgX7qEo`>spzO!u$&-1Wt8{f~6OeRUC zlK6gxC{jdG$ozZ|T)w;*eDJ}?M=oBRo32!<-wXXMP}g<$Zrir?*y!lU(BR-sI<;xz zx(=3Qb&q!Uy^C$z*tSGU8)G_`OC%DcQVwBg*s)_9o@Zm*Hpyg$H{bjtrBWHovKSw~ zpGO~kh+3`9*I&;DL9h_K`|kTkip64Z{`|#n%<1*id!9GkZnq0s>tN^3fuX*>EJH&B zNZFa6wk-jP=UGT;cfl+i$3_SX$B`H#NF*GhNRvu=NNJJHrZA>+{+2Bnd_T>pQ(q#4 zAdyJ$+;bCb*(kelUGHikB(`l~+ZLA9ftIrKSqO>a2#o0*Qz;7}ETj}vDs^(X zUMx#cuSX0G?Zg-Z!1D4HTCI>qqeV8`gOr_XXEHswuER6W{AyzR_N~J&zx>Kx8=z9D zOlPw`sZ?^@_x)q(bQ-NSp64Ni-R*1(DJ>jFAcVxSER^cZQpb^4mcST`I0nZ7r3}V^ z5C)|pv=&&FqNgW?QVM{@#U+-PS7^5*78Wk!c@C{slXAJrrArIs^OyMW!;cH=sc$x$ zgD}PH5|vqwq1{Teogyaq$x8&YhvvYO%Dmgb)VH5=2pqQW_~O0HP>D2n}@7 zuC>M(L!;3o3?m%Jq1}#XwOYh+Ogf$7^y#xyt82{7x~3~z2VGfN;lhOr z)a!K`jV75)l4i5b)~z`}aPHj9I!wn&*+h{-2muJRHb`k`G#Y4a0EO0Ij3x|Y;y9+= z4yo1Z3=VE5olengHkq0E=hVW&;!8@Y@13LTpqDRSMoLM&UI(DnYE!G#F$TI#ie(An zSQE!FVJN!MgnHdzSsJa)zr8xC4MT-e5m6LTt2HQ<%EYna?Ad=zE-n@ie;?d6L4zQe z&StX{?RJ|WC`^uy?mASh)`(-h4%pdFLxd3Bba%S9(Fn0^i7}mejAI2D;#i@S?xwy? zrLxBSe139iX?fFt-vX^xtA&dfFHX(Q&Q2<&=?uTPcB)u}KHgN;)BUApF8FQptF9Nh7@-rnp`E|=@x zTz6qQ4U)_CqqV~KGYkxDBa=xJ$C|;x0enA$*6`6sf1P~iofH3c^fnE8Rj%vqwQYL{ z3@^U;n`2tT;NSqpfNftL@*RgI1T>pb*OUm}c;gSxe)id!sp|m=8~9PBR1ihcOccd4 zcigdSY;<&Fth4KguERGN(-~E@S`%XorBXR~`|WpLICbia>jnv%@S|7N+nXI$$`Ho} zr8KVVAf<&65Qd6G!lPJR30{5mcl$Q7Y(GTSW*4rklnPN4b&ZHbNT0!M47jcZXl7@F=@Td3J3K#MxG7IeHVvB3 z7p6NiRB-0Z%+!e!@4a-R>hW6IG-x*K4;f=PefsRwTW?K0dn2L#XYJoVeqDB%Te_wH aNBs*W%@yGCZPd{K0000 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "settings.h" +#include "toplevel.h" +#include "ledmeter.h" + +#include "toplevel.moc" + +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include +#endif +#include + +#define SB_SCORE 1 +#define SB_LEVEL 2 +#define SB_SHIPS 3 + +struct SLevel +{ + int nrocks; + double rockSpeed; +}; + +#define MAX_LEVELS 16 + +SLevel levels[MAX_LEVELS] = +{ + { 1, 0.4 }, + { 1, 0.6 }, + { 2, 0.5 }, + { 2, 0.7 }, + { 2, 0.8 }, + { 3, 0.6 }, + { 3, 0.7 }, + { 3, 0.8 }, + { 4, 0.6 }, + { 4, 0.7 }, + { 4, 0.8 }, + { 5, 0.7 }, + { 5, 0.8 }, + { 5, 0.9 }, + { 5, 1.0 } +}; + +#ifdef KA_ENABLE_SOUND +#include +#include + +Arts::SimpleSoundServer *soundServer = 0; +#endif + + +KAstTopLevel::KAstTopLevel() +{ + QWidget *mainWin = new QWidget( this ); + mainWin->setFixedSize(640, 480); + + view = new KAsteroidsView( mainWin ); + connect( view, SIGNAL( shipKilled() ), SLOT( slotShipKilled() ) ); + connect( view, SIGNAL( rockHit(int) ), SLOT( slotRockHit(int) ) ); + connect( view, SIGNAL( rocksRemoved() ), SLOT( slotRocksRemoved() ) ); + connect( view, SIGNAL( updateVitals() ), SLOT( slotUpdateVitals() ) ); + + QVBoxLayout *vb = new QVBoxLayout( mainWin ); + QHBoxLayout *hb = new QHBoxLayout; + QHBoxLayout *hbd = new QHBoxLayout; + vb->addLayout( hb ); + + QFont labelFont( KGlobalSettings::generalFont().family(), 24 ); + QColorGroup grp( darkGreen, black, QColor( 128, 128, 128 ), + QColor( 64, 64, 64 ), black, darkGreen, black ); + QPalette pal( grp, grp, grp ); + + mainWin->setPalette( pal ); + + hb->addSpacing( 10 ); + + QLabel *label; + label = new QLabel( i18n("Score"), mainWin ); + label->setFont( labelFont ); + label->setPalette( pal ); + label->setFixedWidth( label->sizeHint().width() ); + hb->addWidget( label ); + + scoreLCD = new QLCDNumber( 6, mainWin ); + scoreLCD->setFrameStyle( QFrame::NoFrame ); + scoreLCD->setSegmentStyle( QLCDNumber::Flat ); + scoreLCD->setFixedWidth( 150 ); + scoreLCD->setPalette( pal ); + hb->addWidget( scoreLCD ); + hb->addStretch( 10 ); + + label = new QLabel( i18n("Level"), mainWin ); + label->setFont( labelFont ); + label->setPalette( pal ); + label->setFixedWidth( label->sizeHint().width() ); + hb->addWidget( label ); + + levelLCD = new QLCDNumber( 2, mainWin ); + levelLCD->setFrameStyle( QFrame::NoFrame ); + levelLCD->setSegmentStyle( QLCDNumber::Flat ); + levelLCD->setFixedWidth( 70 ); + levelLCD->setPalette( pal ); + hb->addWidget( levelLCD ); + hb->addStretch( 10 ); + + label = new QLabel( i18n("Ships"), mainWin ); + label->setFont( labelFont ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hb->addWidget( label ); + + shipsLCD = new QLCDNumber( 1, mainWin ); + shipsLCD->setFrameStyle( QFrame::NoFrame ); + shipsLCD->setSegmentStyle( QLCDNumber::Flat ); + shipsLCD->setFixedWidth( 40 ); + shipsLCD->setPalette( pal ); + hb->addWidget( shipsLCD ); + + hb->addStrut( 30 ); + + QFrame *sep = new QFrame( mainWin ); + sep->setMaximumHeight( 5 ); + sep->setFrameStyle( QFrame::HLine | QFrame::Raised ); + sep->setPalette( pal ); + + vb->addWidget( sep ); + + vb->addWidget( view, 10 ); + +// -- bottom layout: + QFrame *sep2 = new QFrame( mainWin ); + sep2->setMaximumHeight( 1 ); + sep2->setFrameStyle( QFrame::HLine | QFrame::Raised ); + sep2->setPalette( pal ); + vb->addWidget( sep2, 1 ); + + vb->addLayout( hbd ); + + QFont smallFont( KGlobalSettings::generalFont().family(), 14 ); + hbd->addSpacing( 10 ); + + QString sprites_prefix = + KGlobal::dirs()->findResourceDir("sprite", "rock1/rock10000.png"); +/* + label = new QLabel( i18n( "T" ), mainWin ); + label->setFont( smallFont ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + teleportsLCD = new QLCDNumber( 1, mainWin ); + teleportsLCD->setFrameStyle( QFrame::NoFrame ); + teleportsLCD->setSegmentStyle( QLCDNumber::Flat ); + teleportsLCD->setPalette( pal ); + teleportsLCD->setFixedHeight( 20 ); + hbd->addWidget( teleportsLCD ); + + hbd->addSpacing( 10 ); +*/ + QPixmap pm( sprites_prefix + "powerups/brake.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + brakesLCD = new QLCDNumber( 1, mainWin ); + brakesLCD->setFrameStyle( QFrame::NoFrame ); + brakesLCD->setSegmentStyle( QLCDNumber::Flat ); + brakesLCD->setPalette( pal ); + brakesLCD->setFixedHeight( 20 ); + hbd->addWidget( brakesLCD ); + + hbd->addSpacing( 10 ); + + pm.load( sprites_prefix + "powerups/shield.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + shieldLCD = new QLCDNumber( 1, mainWin ); + shieldLCD->setFrameStyle( QFrame::NoFrame ); + shieldLCD->setSegmentStyle( QLCDNumber::Flat ); + shieldLCD->setPalette( pal ); + shieldLCD->setFixedHeight( 20 ); + hbd->addWidget( shieldLCD ); + + hbd->addSpacing( 10 ); + + pm.load( sprites_prefix + "powerups/shoot.png" ); + label = new QLabel( mainWin ); + label->setPixmap( pm ); + label->setFixedWidth( label->sizeHint().width() ); + label->setPalette( pal ); + hbd->addWidget( label ); + + shootLCD = new QLCDNumber( 1, mainWin ); + shootLCD->setFrameStyle( QFrame::NoFrame ); + shootLCD->setSegmentStyle( QLCDNumber::Flat ); + shootLCD->setPalette( pal ); + shootLCD->setFixedHeight( 20 ); + hbd->addWidget( shootLCD ); + + hbd->addStretch( 1 ); + + label = new QLabel( i18n( "Fuel" ), mainWin ); + label->setFont( smallFont ); + label->setFixedWidth( label->sizeHint().width() + 10 ); + label->setPalette( pal ); + hbd->addWidget( label ); + + powerMeter = new KALedMeter( mainWin ); + powerMeter->setFrameStyle( QFrame::Box | QFrame::Plain ); + powerMeter->setRange( MAX_POWER_LEVEL ); + powerMeter->addColorRange( 10, darkRed ); + powerMeter->addColorRange( 20, QColor(160, 96, 0) ); + powerMeter->addColorRange( 70, darkGreen ); + powerMeter->setCount( 40 ); + powerMeter->setPalette( pal ); + powerMeter->setFixedSize( 200, 12 ); + hbd->addWidget( powerMeter ); + + initKAction(); + + setCentralWidget( mainWin ); + + setupGUI( KMainWindow::Save | Create ); + + setFocusPolicy( StrongFocus ); + setFocus(); + +#ifdef KA_ENABLE_SOUND + soundServer = new Arts::SimpleSoundServer( + Arts::Reference("global:Arts_SimpleSoundServer") ); +#endif + + loadSettings(); + slotNewGame(); + installEventFilter(this); +} + +KAstTopLevel::~KAstTopLevel() +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + soundDict.setAutoDelete(true); + soundDict.clear(); +#ifdef KA_ENABLE_SOUND + delete soundServer; +#endif +} + +void KAstTopLevel::initKAction() +{ +// game + KStdGameAction::gameNew( this, SLOT( slotNewGame() ), actionCollection() ); + KStdGameAction::highscores( this, SLOT( slotShowHighscores() ), actionCollection() ); + KStdGameAction::pause( this, SLOT( slotPause() ), actionCollection() ); + KStdGameAction::quit(this, SLOT( close() ), actionCollection()); + +// settings + KStdAction::keyBindings(this, SLOT( slotKeyConfig() ), actionCollection()); + KStdAction::preferences(this, SLOT( slotPref() ), actionCollection()); + +// keyboard-only actions + keycodes.insert(Thrust, new KAction(i18n("Thrust"), Qt::Key_Up, 0, 0, actionCollection(), "Thrust")); + keycodes.insert(RotateLeft, new KAction(i18n("Rotate Left"), Qt::Key_Left, 0, 0, actionCollection(), "RotateLeft")); + keycodes.insert(RotateRight, new KAction(i18n("Rotate Right"), Qt::Key_Right, 0, 0, actionCollection(), "RotateRight")); + keycodes.insert(Shoot, new KAction(i18n("Shoot"), Qt::Key_Space, 0, 0, actionCollection(), "Shoot")); +// keycodes.insert(Teleport, new KAction(i18n("Teleport"), Qt::Key_Z, 0, 0, actionCollection(), "Teleport")); + keycodes.insert(Brake, new KAction(i18n("Brake"), Qt::Key_X, 0, 0, actionCollection(), "Brake")); + keycodes.insert(Shield, new KAction(i18n("Shield"), Qt::Key_S, 0, 0, actionCollection(), "Shield")); + launchAction = new KAction(i18n("Launch"), Qt::Key_L, this, SLOT(slotLaunch()), actionCollection(), "Launch"); +} + + +void KAstTopLevel::loadSettings() +{ + soundDict.insert("ShipDestroyed", + new QString( locate("sounds", Settings::soundShipDestroyed())) ); + soundDict.insert("RockDestroyed", + new QString( locate("sounds", Settings::soundRockDestroyed())) ); + + shipsRemain = Settings::numShips(); +} + +void KAstTopLevel::playSound( const char *snd ) +{ +// KAudioPlayer sometimes seem to be a little bit...slow +// it takes a moment until the sound is played - maybe an arts problem +// but it's a good temporary solution + if (!Settings::playSounds()) + return; + + QString *filename = soundDict[ snd ]; + if (filename) { + KAudioPlayer::play(*filename); + } + +return; // remove this and the above when the sound below is working correctly +#ifdef KA_ENABLE_SOUND + QString *filename = soundDict[ snd ]; + if (filename) { + kdDebug(12012)<< "playing " << *filename << endl; + if(!soundServer->isNull()) soundServer->play(filename->latin1()); + } +#endif +} + +bool KAstTopLevel::eventFilter( QObject* /* object */, QEvent *event ) +{ + QKeyEvent *e = static_cast(event); + if (event->type() == QEvent::AccelOverride) + { + if (processKeyPress(e)) return true; + else return false; + } + else if (event->type() == QEvent::KeyRelease) + { + if (processKeyRelease(e)) return true; + else return false; + } + return false; +} + +bool KAstTopLevel::processKeyPress( QKeyEvent *event ) +{ + KKey key(event); + Action a = Invalid; + QMap::Iterator it = keycodes.begin(); + for (; it != keycodes.end(); ++it) + { + if ( (*it)->shortcut().contains(key) ) + { + a = it.key(); + break; + } + } + switch ( a ) + { + case RotateLeft: + view->rotateLeft( true ); + break; + + case RotateRight: + view->rotateRight( true ); + break; + + case Thrust: + view->thrust( true ); + break; + + case Shoot: + if (gameOver) + slotNewGame(); + else + view->shoot( true ); + break; + + case Shield: + view->setShield( true ); + break; + +/* case Teleport: + view->teleport( true ); + break;*/ + + case Brake: + view->brake( true ); + break; + + case Invalid: + default: + return false; + } + return true; +} + +bool KAstTopLevel::processKeyRelease( QKeyEvent *event ) +{ + KKey key(event); + Action a = Invalid; + QMap::Iterator it = keycodes.begin(); + for (; it != keycodes.end(); ++it) + { + if ( (*it)->shortcut().contains(key) ) + { + a = it.key(); + break; + } + } + switch ( a ) + { + case RotateLeft: + view->rotateLeft( false ); + break; + + case RotateRight: + view->rotateRight( false ); + break; + + case Thrust: + view->thrust( false ); + break; + + case Shoot: + view->shoot( false ); + break; + + case Brake: + view->brake( false ); + break; + + case Shield: + view->setShield( false ); + break; + +/* case Teleport: + view->teleport( false ); + break;*/ + + case Invalid: + default: + return false; + } + + return true; +} + +void KAstTopLevel::focusInEvent( QFocusEvent *e ) +{ + view->pause( false ); +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOff( qt_xdisplay() ); +#endif + KMainWindow::focusInEvent(e); +} + +void KAstTopLevel::focusOutEvent( QFocusEvent *e ) +{ + view->pause( true ); +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + KMainWindow::focusOutEvent(e); +} + +void KAstTopLevel::slotNewGame() +{ + loadSettings(); + score = 0; + scoreLCD->display( 0 ); + level = 0; + levelLCD->display( level ); + shipsLCD->display( shipsRemain-1 ); + view->newGame(); + view->setRockSpeed( levels[0].rockSpeed ); + view->addRocks( levels[0].nrocks ); + view->showText( i18n( "Press %1 to launch." ) + .arg(launchAction->shortcut().seq(0).toString()), + yellow ); + waitShip = true; + gameOver = false; +} + +bool KAstTopLevel::queryExit() +{ +#if defined Q_WS_X11 && ! defined K_WS_QTONLY + XAutoRepeatOn( qt_xdisplay() ); +#endif + return true; +} + +void KAstTopLevel::slotShipKilled() +{ + shipsRemain--; + shipsLCD->display( shipsRemain-1 ); + + playSound( "ShipDestroyed" ); + + if ( shipsRemain ) + { + waitShip = true; + view->showText( i18n( "Ship Destroyed. Press %1 to launch.") + .arg(launchAction->shortcut().seq(0).toString()), + yellow ); + } + else + { + view->showText( i18n("Game Over!"), red ); + view->endGame(); + doStats(); + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + KScoreDialog::FieldInfo scoreInfo; + scoreInfo[KScoreDialog::Level].setNum(level); + + if (d.addScore(score, scoreInfo) || Settings::showHiscores()) + { + // Show highscore and ask for name. + d.exec(); + slotGameOver(); + } + else + { + QTimer::singleShot(1000, this, SLOT(slotGameOver())); + } + } +} + +void KAstTopLevel::slotGameOver() +{ + gameOver = true; +} + +void KAstTopLevel::slotRockHit( int size ) +{ + switch ( size ) + { + case 0: + score += 10; + break; + + case 1: + score += 20; + break; + + default: + score += 40; + } + + playSound( "RockDestroyed" ); + + scoreLCD->display( score ); +} + +void KAstTopLevel::slotRocksRemoved() +{ + level++; + + if ( level >= MAX_LEVELS ) + level = MAX_LEVELS - 1; + + view->setRockSpeed( levels[level-1].rockSpeed ); + view->addRocks( levels[level-1].nrocks ); + + levelLCD->display( level ); +} + +void KAstTopLevel::slotKeyConfig() +{ + KKeyDialog::configure( actionCollection(), this ); + if ( waitShip ) view->showText( i18n( "Press %1 to launch." ) + .arg(launchAction->shortcut().seq(0).toString()), + yellow, false ); +} + +void KAstTopLevel::slotPref() +{ + if(KConfigDialog::showDialog("settings")) + return; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self(), KDialogBase::Swallow); + + /* Make widget */ + QWidget *w = new QWidget(0, "Settings"); + QVBoxLayout *page = new QVBoxLayout( w, 11 ); + + QHBoxLayout *hb = new QHBoxLayout( page, 11 ); + QLabel *label = new QLabel( i18n("Start new game with"), w ); + QSpinBox* sb = new QSpinBox( 1, 5, 1, w, "kcfg_numShips" ); + sb->setValue(3); + QLabel *lb = new QLabel( i18n(" ships."), w ); + QSpacerItem* hspacer = new QSpacerItem( 16, 20, QSizePolicy::Minimum, QSizePolicy::Expanding ); + hb->addWidget(label); + hb->addWidget(sb); + hb->addWidget(lb); + hb->addItem(hspacer); + + QCheckBox *f1 = new QCheckBox(i18n("Show highscores on Game Over"), w, "kcfg_showHiscores"); + QCheckBox *f2 = new QCheckBox(i18n("Player can destroy Powerups"), w, "kcfg_canDestroyPowerups"); + f2->setChecked(true); + page->addWidget(f1); + page->addWidget(f2); + QSpacerItem* spacer = new QSpacerItem( 20, 16, QSizePolicy::Minimum, QSizePolicy::Expanding ); + page->addItem( spacer ); + /* Done */ + + dialog->addPage(w, i18n("General"), "package_settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + +void KAstTopLevel::slotShowHighscores() +{ + KScoreDialog d(KScoreDialog::Name | KScoreDialog::Level | KScoreDialog::Score, this); + d.exec(); +} + +void KAstTopLevel::doStats() +{ + QString r; + if ( view->shots() ) + r = KGlobal::locale()->formatNumber(( (float)view->hits() / + (float)view->shots() ) * 100, 2 ); + else + r = KGlobal::locale()->formatNumber( 0.0, 2 ); + + QString s = i18n( "Game Over\n\n" + "Shots fired:\t%1\n" + " Hit:\t%2\n" + " Missed:\t%3\n" + "Hit ratio:\t%4 %\t\t") + .arg(view->shots()).arg(view->hits()) + .arg(view->shots() - view->hits()) + .arg(r); + + view->showText( s, green, FALSE ); +} + +void KAstTopLevel::slotUpdateVitals() +{ + brakesLCD->display( view->brakeCount() ); + shieldLCD->display( view->shieldCount() ); + shootLCD->display( view->shootCount() ); +// teleportsLCD->display( view->teleportCount() ); + powerMeter->setValue( view->power() ); +} + +void KAstTopLevel::slotPause() +{ + KMessageBox::information( this, + i18n("KAsteroids is paused."), + i18n("Paused") ); + //Hack (montel@kde.org) + //To create "pause" action we display a messagebox + //and we "play" when we close this messagebox + //so before this action was 'checked/uncheched' + //so I force to unchecked it when we close messagebox. + KToggleAction * act = (KToggleAction *)(sender()); + act->setChecked(false); +} + +void KAstTopLevel::slotLaunch() +{ + if ( waitShip ) + { + view->newShip(); + waitShip = false; + view->hideText(); + } +} diff --git a/kasteroids/toplevel.h b/kasteroids/toplevel.h new file mode 100644 index 00000000..70545f71 --- /dev/null +++ b/kasteroids/toplevel.h @@ -0,0 +1,94 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __KAST_TOPLEVEL_H__ +#define __KAST_TOPLEVEL_H__ + +#include +#include +#include +#include + +#include "view.h" + +//#define KA_ENABLE_SOUND + +class KALedMeter; +class QLCDNumber; +class KDialogBase; + +class KAstTopLevel : public KMainWindow +{ + Q_OBJECT +public: + KAstTopLevel(); + virtual ~KAstTopLevel(); + +private: + void initKAction(); + void playSound( const char *snd ); + void readSoundMapping(); + void doStats(); + bool queryExit(); + bool processKeyPress( QKeyEvent *event ); + bool processKeyRelease( QKeyEvent *event ); + +protected: + virtual bool eventFilter( QObject *object, QEvent *event ); + virtual void focusInEvent( QFocusEvent *event ); + virtual void focusOutEvent( QFocusEvent *event ); + +private slots: + void loadSettings(); + void slotNewGame(); + void slotGameOver(); + + void slotShipKilled(); + void slotRockHit( int size ); + void slotRocksRemoved(); + + void slotUpdateVitals(); + + void slotKeyConfig(); + void slotPref(); + void slotShowHighscores(); + + void slotPause(); + void slotLaunch(); + +private: + KAsteroidsView *view; + QLCDNumber *scoreLCD; + QLCDNumber *levelLCD; + QLCDNumber *shipsLCD; + + QLCDNumber *teleportsLCD; +// QLCDNumber *bombsLCD; + QLCDNumber *brakesLCD; + QLCDNumber *shieldLCD; + QLCDNumber *shootLCD; + KALedMeter *powerMeter; + + QDict soundDict; + + // waiting for user to press Enter to launch a ship + bool waitShip; + bool gameOver; + + int shipsRemain; + int score; + int level; + + enum Action { Invalid, Launch, Thrust, RotateLeft, RotateRight, Shoot, + Teleport, Brake, Shield }; + + QMap keycodes; + + KAction *launchAction; +}; + +#endif + diff --git a/kasteroids/version.h b/kasteroids/version.h new file mode 100644 index 00000000..5ab917a4 --- /dev/null +++ b/kasteroids/version.h @@ -0,0 +1 @@ +#define KASTEROIDS_VERSION "2.3" diff --git a/kasteroids/view.cpp b/kasteroids/view.cpp new file mode 100644 index 00000000..8ca358fd --- /dev/null +++ b/kasteroids/view.cpp @@ -0,0 +1,857 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#include +#include +#include +#include +#include + +#include "settings.h" +#include "view.h" +#include "view.moc" + +#define IMG_BACKGROUND "bg.png" +#define SPRITES_PREFIX kapp->kde_datadir() + "/kasteroids/" + +#define REFRESH_DELAY 33 +#define SHIP_SPEED 0.3 +#define MISSILE_SPEED 10.0 +#define SHIP_STEPS 64 +#define ROTATE_RATE 2 +#define SHIELD_ON_COST 1 +#define SHIELD_HIT_COST 30 +#define BRAKE_ON_COST 4 + +#define MAX_ROCK_SPEED 2.5 +#define MAX_POWERUP_SPEED 1.5 +#define MAX_SHIP_SPEED 12 +#define MAX_BRAKES 5 +#define MAX_SHIELDS 5 +#define MAX_FIREPOWER 5 + +#define TEXT_SPEED 4 + +#define PI_X_2 6.283185307 + +static struct +{ + int id; + const char *path; + int frames; +} +kas_animations [] = +{ + { ID_ROCK_LARGE, "rock1/rock1%1.png", 32 }, + { ID_ROCK_MEDIUM, "rock2/rock2%1.png", 32 }, + { ID_ROCK_SMALL, "rock3/rock3%1.png", 32 }, + { ID_SHIP, "ship/ship%1.png", 64 }, + { ID_MISSILE, "missile/missile.png", 1 }, + { ID_BIT, "bits/bits%1.png", 16 }, + { ID_EXHAUST, "exhaust/exhaust.png", 1 }, + { ID_ENERGY_POWERUP, "powerups/energy.png", 1 }, +// { ID_TELEPORT_POWERUP, "powerups/teleport%1.png", 12 }, + { ID_BRAKE_POWERUP, "powerups/brake.png", 1 }, + { ID_SHIELD_POWERUP, "powerups/shield.png", 1 }, + { ID_SHOOT_POWERUP, "powerups/shoot.png", 1 }, + { ID_SHIELD, "shield/shield%1.png", 6 }, + { 0, 0, 0 } +}; + + + +KAsteroidsView::KAsteroidsView( QWidget *parent, const char *name ) + : QWidget( parent, name ), + field(640, 440), + view(&field,this) +{ + view.setVScrollBarMode( QScrollView::AlwaysOff ); + view.setHScrollBarMode( QScrollView::AlwaysOff ); + rocks.setAutoDelete( true ); + missiles.setAutoDelete( true ); + bits.setAutoDelete( true ); + powerups.setAutoDelete( true ); + exhaust.setAutoDelete( true ); + + field.setBackgroundColor(black); + QPixmap pm( locate("sprite", IMG_BACKGROUND) ); + field.setBackgroundPixmap( pm ); + + textSprite = new QCanvasText( &field ); + QFont font( KGlobalSettings::generalFont().family(), 18 ); + textSprite->setFont( font ); + + shield = 0; + shieldOn = false; + refreshRate = REFRESH_DELAY; + + readSprites(); + + shieldTimer = new QTimer( this ); + connect( shieldTimer, SIGNAL(timeout()), this, SLOT(hideShield()) ); + mTimerId = -1; + + shipPower = MAX_POWER_LEVEL; + vitalsChanged = true; + mBrakeCount = 0; + mShootCount = 0; + mShieldCount = 0; +} + +// - - - + +KAsteroidsView::~KAsteroidsView() +{ +} + +// - - - + +void KAsteroidsView::reset() +{ + rocks.clear(); + missiles.clear(); + bits.clear(); + powerups.clear(); + exhaust.clear(); + + shotsFired = 0; + shotsHit = 0; + + rockSpeed = 1.0; + powerupSpeed = 1.0; + mFrameNum = 0; + mPaused = false; + + ship->hide(); + shield->hide(); + + if ( mTimerId >= 0 ) { + killTimer( mTimerId ); + mTimerId = -1; + } +} + +// - -- + +void KAsteroidsView::newGame() +{ + if ( shieldOn ) + { + shield->hide(); + shieldOn = false; + } + reset(); + if ( mTimerId < 0 ) + mTimerId = startTimer( REFRESH_DELAY ); + emit updateVitals(); +} + +// - - - + +void KAsteroidsView::endGame() +{ +} + +void KAsteroidsView::pause( bool p ) +{ + if ( !mPaused && p ) { + if ( mTimerId >= 0 ) { + killTimer( mTimerId ); + mTimerId = -1; + } + } else if ( mPaused && !p ) + mTimerId = startTimer( REFRESH_DELAY ); + mPaused = p; +} + +// - - - + +void KAsteroidsView::newShip() +{ + ship->move( field.width()/2, field.height()/2, 0 ); + shield->move( field.width()/2, field.height()/2, 0 ); + ship->setVelocity( 0.0, 0.0 ); + shipDx = 0; + shipDy = 0; + shipAngle = 0; + rotateL = false; + rotateR = false; + thrustShip = false; + shootShip = false; + brakeShip = false; + teleportShip = false; + shieldOn = true; + shootDelay = 0; + shipPower = MAX_POWER_LEVEL; + rotateRate = ROTATE_RATE; + rotateSlow = 0; + + mBrakeCount = 0; + mTeleportCount = 0; + mShootCount = 0; + + ship->show(); + shield->show(); + mShieldCount = 1; // just in case the ship appears on a rock. + shieldTimer->start( 1000, true ); +} + +void KAsteroidsView::setShield( bool s ) +{ + if ( shieldTimer->isActive() && !s ) { + shieldTimer->stop(); + hideShield(); + } else { + shieldOn = s && mShieldCount; + } +} + +void KAsteroidsView::brake( bool b ) +{ + if ( mBrakeCount ) + { + if ( brakeShip && !b ) + { + rotateL = false; + rotateR = false; + thrustShip = false; + rotateRate = ROTATE_RATE; + } + + brakeShip = b; + } +} + +// - - - + +void KAsteroidsView::readSprites() +{ + QString sprites_prefix = + KGlobal::dirs()->findResourceDir("sprite", "rock1/rock10000.png"); + + int i = 0; + while ( kas_animations[i].id ) + { + animation.insert( kas_animations[i].id, + new QCanvasPixmapArray( sprites_prefix + kas_animations[i].path, + kas_animations[i].frames ) ); + i++; + } + + ship = new QCanvasSprite( animation[ID_SHIP], &field ); + ship->hide(); + + shield = new KShield( animation[ID_SHIELD], &field ); + shield->hide(); +} + +// - - - + +void KAsteroidsView::addRocks( int num ) +{ + for ( int i = 0; i < num; i++ ) + { + KRock *rock = new KRock( animation[ID_ROCK_LARGE], &field, + ID_ROCK_LARGE, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + double dx = (2.0 - krandom.getDouble()*4.0) * rockSpeed; + double dy = (2.0 - krandom.getDouble()*4.0) * rockSpeed; + rock->setVelocity( dx, dy ); + rock->setFrame( krandom.getLong( rock->frameCount() ) ); + if ( dx > 0 ) + { + if ( dy > 0 ) + rock->move( 5, 5, 0 ); + else + rock->move( 5, field.height() - 25, 0 ); + } + else + { + if ( dy > 0 ) + rock->move( field.width() - 25, 5, 0 ); + else + rock->move( field.width() - 25, field.height() - 25, 0 ); + } + rock->show( ); + rocks.append( rock ); + } +} + +// - - - + +void KAsteroidsView::showText( const QString &text, const QColor &color, bool scroll ) +{ +// textSprite->setTextFlags( AlignHCenter | AlignVCenter ); + textSprite->setText( text ); + textSprite->setColor( color ); + + if ( scroll ) { + textSprite->move( (field.width()-textSprite->boundingRect().width()) / 2, + -textSprite->boundingRect().height() ); + textDy = TEXT_SPEED; + } else { + textSprite->move( (field.width()-textSprite->boundingRect().width()) / 2, + (field.height()-textSprite->boundingRect().height()) / 2 ); + textDy = 0; + } + textSprite->show(); +} + +// - - - + +void KAsteroidsView::hideText() +{ + textDy = -TEXT_SPEED; +} + +// - - - + +void KAsteroidsView::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + field.resize(width()-4, height()-4); + view.resize(width(),height()); +} + +// - - - + +void KAsteroidsView::timerEvent( QTimerEvent * ) +{ + field.advance(); + + QCanvasSprite *rock; + + // move rocks forward + for ( rock = rocks.first(); rock; rock = rocks.next() ) { + ((KRock *)rock)->nextFrame(); + wrapSprite( rock ); + } + + wrapSprite( ship ); + + // check for missile collision with rocks. + processMissiles(); + + // these are generated when a ship explodes + for ( KBit *bit = bits.first(); bit; bit = bits.next() ) + { + if ( bit->expired() ) + { + bits.removeRef( bit ); + } + else + { + bit->growOlder(); + bit->setFrame( ( bit->frame()+1 ) % bit->frameCount() ); + } + } + + for ( KExhaust *e = exhaust.first(); e; e = exhaust.next() ) + exhaust.removeRef( e ); + + // move / rotate ship. + // check for collision with a rock. + processShip(); + + // move powerups and check for collision with player and missiles + processPowerups(); + + if ( textSprite->isVisible() ) + { + if ( textDy < 0 && + textSprite->boundingRect().y() <= -textSprite->boundingRect().height() ) + textSprite->hide(); + else + { + textSprite->moveBy( 0, textDy ); + } + if ( textSprite->boundingRect().y() > (field.height()-textSprite->boundingRect().height())/2 ) + textDy = 0; + } + + if ( vitalsChanged && !(mFrameNum % 10) ) { + emit updateVitals(); + vitalsChanged = false; + } + + mFrameNum++; +} + +void KAsteroidsView::wrapSprite( QCanvasItem *s ) +{ + int x = int(s->x() + s->boundingRect().width() / 2); + int y = int(s->y() + s->boundingRect().height() / 2); + + if ( x > field.width() ) + s->move( s->x() - field.width(), s->y() ); + else if ( x < 0 ) + s->move( field.width() + s->x(), s->y() ); + + if ( y > field.height() ) + s->move( s->x(), s->y() - field.height() ); + else if ( y < 0 ) + s->move( s->x(), field.height() + s->y() ); +} + +// - - - + +void KAsteroidsView::rockHit( QCanvasItem *hit ) +{ + KPowerup *nPup = 0; + int rnd = static_cast(krandom.getDouble()*30.0) % 30; + switch( rnd ) + { + case 4: + case 5: + nPup = new KPowerup( animation[ID_ENERGY_POWERUP], &field, + ID_ENERGY_POWERUP ); + break; + case 10: +// nPup = new KPowerup( animation[ID_TELEPORT_POWERUP], &field, +// ID_TELEPORT_POWERUP ); + break; + case 15: + nPup = new KPowerup( animation[ID_BRAKE_POWERUP], &field, + ID_BRAKE_POWERUP ); + break; + case 20: + nPup = new KPowerup( animation[ID_SHIELD_POWERUP], &field, + ID_SHIELD_POWERUP ); + break; + case 24: + case 25: + nPup = new KPowerup( animation[ID_SHOOT_POWERUP], &field, + ID_SHOOT_POWERUP ); + break; + } + if ( nPup ) + { + double r = 0.5 - krandom.getDouble(); + nPup->move( hit->x(), hit->y(), 0 ); + nPup->setVelocity( hit->xVelocity() + r, hit->yVelocity() + r ); + nPup->show( ); + powerups.append( nPup ); + } + + if ( hit->rtti() == ID_ROCK_LARGE || hit->rtti() == ID_ROCK_MEDIUM ) + { + // break into smaller rocks + double addx[4] = { 1.0, 1.0, -1.0, -1.0 }; + double addy[4] = { -1.0, 1.0, -1.0, 1.0 }; + + double dx = hit->xVelocity(); + double dy = hit->yVelocity(); + + double maxRockSpeed = MAX_ROCK_SPEED * rockSpeed; + if ( dx > maxRockSpeed ) + dx = maxRockSpeed; + else if ( dx < -maxRockSpeed ) + dx = -maxRockSpeed; + if ( dy > maxRockSpeed ) + dy = maxRockSpeed; + else if ( dy < -maxRockSpeed ) + dy = -maxRockSpeed; + + QCanvasSprite *nrock; + + for ( int i = 0; i < 4; i++ ) + { + double r = rockSpeed/2 - krandom.getDouble()*rockSpeed; + if ( hit->rtti() == ID_ROCK_LARGE ) + { + nrock = new KRock( animation[ID_ROCK_MEDIUM], &field, + ID_ROCK_MEDIUM, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + emit rockHit( 0 ); + } + else + { + nrock = new KRock( animation[ID_ROCK_SMALL], &field, + ID_ROCK_SMALL, krandom.getLong(2), + krandom.getLong(2) ? -1 : 1 ); + emit rockHit( 1 ); + } + + nrock->move( hit->x(), hit->y(), 0 ); + nrock->setVelocity( dx+addx[i]*rockSpeed+r, dy+addy[i]*rockSpeed+r ); + nrock->setFrame( krandom.getLong( nrock->frameCount() ) ); + nrock->show( ); + rocks.append( nrock ); + } + } + else if ( hit->rtti() == ID_ROCK_SMALL ) + emit rockHit( 2 ); + rocks.removeRef( (QCanvasSprite *)hit ); + if ( rocks.count() == 0 ) + emit rocksRemoved(); +} + +void KAsteroidsView::reducePower( int val ) +{ + shipPower -= val; + if ( shipPower <= 0 ) + { + shipPower = 0; + thrustShip = false; + if ( shieldOn ) + { + shieldOn = false; + shield->hide(); + } + } + vitalsChanged = true; +} + +void KAsteroidsView::addExhaust( double x, double y, double dx, + double dy, int count ) +{ + for ( int i = 0; i < count; i++ ) + { + KExhaust *e = new KExhaust( animation[ID_EXHAUST], &field ); + e->move( x + 2 - krandom.getDouble()*4, y + 2 - krandom.getDouble()*4 ); + e->setVelocity( dx, dy ); + e->show( ); + exhaust.append( e ); + } +} + +void KAsteroidsView::processMissiles() +{ + KMissile *missile; + + // if a missile has hit a rock, remove missile and break rock into smaller + // rocks or remove completely. + QPtrListIterator it(missiles); + + for ( ; it.current(); ++it ) + { + missile = it.current(); + missile->growOlder(); + + if ( missile->expired() ) + { + missiles.removeRef( missile ); + continue; + } + + wrapSprite( missile ); + + QCanvasItemList hits = missile->collisions( true ); + QCanvasItemList::Iterator hit; + for ( hit = hits.begin(); hit != hits.end(); ++hit ) + { + if ( (*hit)->rtti() >= ID_ROCK_LARGE && + (*hit)->rtti() <= ID_ROCK_SMALL ) + { + shotsHit++; + rockHit( *hit ); + missiles.removeRef( missile ); + break; + } + } + } +} + +// - - - + +void KAsteroidsView::processShip() +{ + if ( ship->isVisible() ) + { + if ( shieldOn ) + { + shield->show(); + reducePower( SHIELD_ON_COST ); + static int sf = 0; + sf++; + + if ( sf % 2 ) + shield->setFrame( (shield->frame()+1) % shield->frameCount() ); + shield->move( ship->x() - 9, ship->y() - 9 ); + + QCanvasItemList hits = shield->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it)->rtti() >= ID_ROCK_LARGE && + (*it)->rtti() <= ID_ROCK_SMALL ) + { + int factor; + switch ( (*it)->rtti() ) + { + case ID_ROCK_LARGE: + factor = 3; + break; + + case ID_ROCK_MEDIUM: + factor = 2; + break; + + default: + factor = 1; + } + + if ( factor > mShieldCount ) + { + // shield not strong enough + shieldOn = false; + break; + } + rockHit( *it ); + // the more shields we have the less costly + reducePower( factor * (SHIELD_HIT_COST - mShieldCount*2) ); + } + } + } + + if ( !shieldOn ) + { + shield->hide(); + QCanvasItemList hits = ship->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it)->rtti() >= ID_ROCK_LARGE && + (*it)->rtti() <= ID_ROCK_SMALL ) + { + KBit *bit; + for ( int i = 0; i < 12; i++ ) + { + bit = new KBit( animation[ID_BIT], &field ); + bit->move( ship->x() + 5 - krandom.getDouble() * 10, + ship->y() + 5 - krandom.getDouble() * 10, + krandom.getLong(bit->frameCount()) ); + bit->setVelocity( 1-krandom.getDouble()*2, + 1-krandom.getDouble()*2 ); + bit->setDeath( 60 + krandom.getLong(60) ); + bit->show( ); + bits.append( bit ); + } + ship->hide(); + shield->hide(); + emit shipKilled(); + break; + } + } + } + + + if ( rotateSlow ) + rotateSlow--; + + if ( rotateL ) + { + shipAngle -= rotateSlow ? 1 : rotateRate; + if ( shipAngle < 0 ) + shipAngle += SHIP_STEPS; + } + + if ( rotateR ) + { + shipAngle += rotateSlow ? 1 : rotateRate; + if ( shipAngle >= SHIP_STEPS ) + shipAngle -= SHIP_STEPS; + } + + double angle = shipAngle * PI_X_2 / SHIP_STEPS; + double cosangle = cos( angle ); + double sinangle = sin( angle ); + + if ( brakeShip ) + { + thrustShip = false; + rotateL = false; + rotateR = false; + rotateRate = ROTATE_RATE; + if ( fabs(shipDx) < 2.5 && fabs(shipDy) < 2.5 ) + { + shipDx = 0.0; + shipDy = 0.0; + ship->setVelocity( shipDx, shipDy ); + brakeShip = false; + } + else + { + double motionAngle = atan2( -shipDy, -shipDx ); + if ( angle > M_PI ) + angle -= PI_X_2; + double angleDiff = angle - motionAngle; + if ( angleDiff > M_PI ) + angleDiff = PI_X_2 - angleDiff; + else if ( angleDiff < -M_PI ) + angleDiff = PI_X_2 + angleDiff; + double fdiff = fabs( angleDiff ); + if ( fdiff > 0.08 ) + { + if ( angleDiff > 0 ) + rotateL = true; + else if ( angleDiff < 0 ) + rotateR = true; + if ( fdiff > 0.6 ) + rotateRate = mBrakeCount + 1; + else if ( fdiff > 0.4 ) + rotateRate = 2; + else + rotateRate = 1; + + if ( rotateRate > 5 ) + rotateRate = 5; + } + else if ( fabs(shipDx) > 1 || fabs(shipDy) > 1 ) + { + thrustShip = true; + // we'll make braking a bit faster + shipDx += cosangle/6 * (mBrakeCount - 1); + shipDy += sinangle/6 * (mBrakeCount - 1); + reducePower( BRAKE_ON_COST ); + addExhaust( ship->x() + 20 - cosangle*22, + ship->y() + 20 - sinangle*22, + shipDx-cosangle, shipDy-sinangle, + mBrakeCount+1 ); + } + } + } + + if ( thrustShip ) + { + // The ship has a terminal velocity, but trying to go faster + // still uses fuel (can go faster diagonally - don't care). + double thrustx = cosangle/4; + double thrusty = sinangle/4; + if ( fabs(shipDx + thrustx) < MAX_SHIP_SPEED ) + shipDx += thrustx; + if ( fabs(shipDy + thrusty) < MAX_SHIP_SPEED ) + shipDy += thrusty; + ship->setVelocity( shipDx, shipDy ); + reducePower( 1 ); + addExhaust( ship->x() + 20 - cosangle*20, + ship->y() + 20 - sinangle*20, + shipDx-cosangle, shipDy-sinangle, 3 ); + } + + ship->setFrame( shipAngle ); + + if ( shootShip ) + { + if ( !shootDelay && (int)missiles.count() < mShootCount + 2 ) + { + KMissile *missile = new KMissile( animation[ID_MISSILE], &field ); + missile->move( 21+ship->x()+cosangle*10, + 21+ship->y()+sinangle*10, 0 ); + missile->setVelocity( shipDx + cosangle*MISSILE_SPEED, + shipDy + sinangle*MISSILE_SPEED ); + missile->show( ); + missiles.append( missile ); + shotsFired++; + reducePower( 1 ); + + shootDelay = 5; + } + + if ( shootDelay ) + shootDelay--; + } + + if ( teleportShip ) + { + int ra = rand() % 10; + if( ra == 0 ) + ra += rand() % 20; + int xra = ra * 60 + ( (rand() % 20) * (rand() % 20) ); + int yra = ra * 50 - ( (rand() % 20) * (rand() % 20) ); + ship->move( xra, yra ); + } + + vitalsChanged = true; + } +} + +// - - - + +void KAsteroidsView::processPowerups() +{ + if ( !powerups.isEmpty() ) + { + // if player gets the powerup remove it from the screen, if option + // "Can destroy powerups" is enabled and a missile hits the powerup + // destroy it + + KPowerup *pup; + QPtrListIterator it( powerups ); + + for( ; (pup = it.current()); ) + { + ++it; // We have to increase here, because pup may get deleted. + pup->growOlder(); + + if( pup->expired() ) + { + powerups.removeRef( pup ); + continue; + } + + wrapSprite( pup ); + + QCanvasItemList hits = pup->collisions( true ); + QCanvasItemList::Iterator it; + for ( it = hits.begin(); it != hits.end(); ++it ) + { + if ( (*it) == ship || (*it) == shield ) + { + switch( pup->rtti() ) + { + case ID_ENERGY_POWERUP: + shipPower += 150; + if ( shipPower > MAX_POWER_LEVEL ) + shipPower = MAX_POWER_LEVEL; + break; + case ID_TELEPORT_POWERUP: + mTeleportCount++; + break; + case ID_BRAKE_POWERUP: + if ( mBrakeCount < MAX_BRAKES ) + mBrakeCount++; + break; + case ID_SHIELD_POWERUP: + if ( mShieldCount < MAX_SHIELDS ) + mShieldCount++; + break; + case ID_SHOOT_POWERUP: + if ( mShootCount < MAX_FIREPOWER ) + mShootCount++; + break; + } + + powerups.removeRef( pup ); + vitalsChanged = true; + break; + } + + if ( (*it)->rtti() == ID_MISSILE ) + { + if ( Settings::canDestroyPowerups() ) + { + powerups.removeRef( pup ); + break; + } + } + } + } + } // -- if( powerups.isEmpty() ) +} + +// - - - + +void KAsteroidsView::hideShield() +{ + shield->hide(); + mShieldCount = 0; + shieldOn = false; +} + + +// - - - + diff --git a/kasteroids/view.h b/kasteroids/view.h new file mode 100644 index 00000000..6807037a --- /dev/null +++ b/kasteroids/view.h @@ -0,0 +1,133 @@ +/* + * KAsteroids - Copyright (c) Martin R. Jones 1997 + * + * Part of the KDE project + */ + +#ifndef __AST_VIEW_H__ +#define __AST_VIEW_H__ + +#include +#include +#include +#include +#include +#include "sprites.h" +#include + +#define MAX_POWER_LEVEL 1000 + +class KAsteroidsView : public QWidget +{ + Q_OBJECT +public: + KAsteroidsView( QWidget *parent = 0, const char *name = 0 ); + virtual ~KAsteroidsView(); + + int refreshRate; + + void reset(); + void setRockSpeed( double rs ) { rockSpeed = rs; } + void addRocks( int num ); + void newGame(); + void endGame(); + void newShip(); + + void rotateLeft( bool r ) { rotateL = r; rotateSlow = 5; } + void rotateRight( bool r ) { rotateR = r; rotateSlow = 5; } + void thrust( bool t ) { thrustShip = t && shipPower > 0; } + void shoot( bool s ) { shootShip = s; shootDelay = 0; } + void setShield( bool s ); + void teleport( bool te) { teleportShip = te && mTeleportCount; } + void brake( bool b ); + void pause( bool p); + + void showText( const QString &text, const QColor &color, bool scroll=TRUE ); + void hideText(); + + int shots() const { return shotsFired; } + int hits() const { return shotsHit; } + int power() const { return shipPower; } + + int teleportCount() const { return mTeleportCount; } + int brakeCount() const { return mBrakeCount; } + int shieldCount() const { return mShieldCount; } + int shootCount() const { return mShootCount; } + +signals: + void shipKilled(); + void rockHit( int size ); + void rocksRemoved(); + void updateVitals(); + +private slots: + void hideShield(); + +protected: + void readSprites(); + void wrapSprite( QCanvasItem * ); + void rockHit( QCanvasItem * ); + void reducePower( int val ); + void addExhaust( double x, double y, double dx, double dy, int count ); + void processMissiles(); + void processShip(); + void processPowerups(); + void processShield(); + + virtual void resizeEvent( QResizeEvent *event ); + virtual void timerEvent( QTimerEvent * ); + +private: + QCanvas field; + QCanvasView view; + QIntDict animation; + QPtrList rocks; + QPtrList missiles; + QPtrList bits; + QPtrList exhaust; + QPtrList powerups; + KShield *shield; + QCanvasSprite *ship; + QCanvasText *textSprite; + + bool rotateL; + bool rotateR; + bool thrustShip; + bool shootShip; + bool teleportShip; + bool brakeShip; + bool pauseShip; + bool shieldOn; + + bool vitalsChanged; + + int shipAngle; + int rotateSlow; + int rotateRate; + int shipPower; + + int shotsFired; + int shotsHit; + int shootDelay; + + int mBrakeCount; + int mShieldCount; + int mTeleportCount; + int mShootCount; + + double shipDx; + double shipDy; + + int textDy; + int mFrameNum; + bool mPaused; + int mTimerId; + + double rockSpeed; + double powerupSpeed; + + KRandomSequence krandom; + QTimer *shieldTimer; +}; + +#endif diff --git a/katomic/Makefile.am b/katomic/Makefile.am new file mode 100644 index 00000000..4d554b33 --- /dev/null +++ b/katomic/Makefile.am @@ -0,0 +1,35 @@ + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/highscore $(all_includes) +katomic_LDFLAGS = $(all_libraries) $(KDE_RPATH) +katomic_LDADD = $(LIB_KDEGAMES) +katomic_DEPENDENCIES = $(LIB_KDEGAMES_DEP) +METASOURCES = AUTO + +bin_PROGRAMS = katomic +katomic_SOURCES = feld.cpp molek.cpp main.cpp toplevel.cpp configbox.cpp\ + gamewidget.cpp + +pics_DATA = abilder.png molek.png +picsdir = $(appdir)/pics + +xdg_apps_DATA = katomic.desktop + +EXTRA_DIST = $(pics_DATA) +KDE_ICON = katomic + +appdir = $(kde_datadir)/katomic +app_DATA = katomicui.rc + +messages: rc.cpp + : > levelnames.cpp ;\ + for i in levels/level_*; do \ + egrep '^Name=' $$i | sed -e 's#^Name=\(.*\)$$#i18n(\"\1\")#' >> levelnames.cpp ;\ + done + $(XGETTEXT) *.cpp -o $(podir)/katomic.pot + rm -f levelnames.cpp + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(kde_datadir)/katomic + -rm -rf $(DESTDIR)$(kde_datadir)/katomic/levels + mkdir $(DESTDIR)$(kde_datadir)/katomic/levels + cp $(srcdir)/levels/level_* $(DESTDIR)$(kde_datadir)/katomic/levels/ diff --git a/katomic/abilder.png b/katomic/abilder.png new file mode 100644 index 0000000000000000000000000000000000000000..99c60a0cf075190f9e94e35ff51e1fb04cd5888e GIT binary patch literal 18617 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 Dx1 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 U{`S}Z{7?S-KdeJR_YGqK0Kbb&Y5)KL literal 0 HcmV?d00001 diff --git a/katomic/atom.h b/katomic/atom.h new file mode 100644 index 00000000..19d9b087 --- /dev/null +++ b/katomic/atom.h @@ -0,0 +1,35 @@ +#ifndef ATOM_H +#define ATOM_H + +#define MAX_CONNS_PER_ATOM 8 + +class atom { + public: + char obj; + char conn[MAX_CONNS_PER_ATOM + 1]; + + bool operator==(const atom& rhs) const { return (rhs.obj == obj && !strcmp(rhs.conn,conn)); } + bool isEmpty() const { return (obj == 0 || obj == '.'); } +}; + +inline char int2atom(int i) { + if (!i) + return '.'; + if (i == 254) + return '#'; + if (i <= 9) + return i + '0'; + return i + 'a' - 10; +} + +inline int atom2int(char ch) { + if (ch == '.' || ch == 0) + return 0; + if (ch == '#') + return 254; + if (ch >= '0' && ch <= '9') + return ch - '0'; + return ch - 'a' + 10; +} + +#endif diff --git a/katomic/configbox.cpp b/katomic/configbox.cpp new file mode 100644 index 00000000..ea546d89 --- /dev/null +++ b/katomic/configbox.cpp @@ -0,0 +1,59 @@ +/* configbox.cpp + * + * 1999 (c) Cristian Tibirna (tibirna@kde.org) + * + */ + +#include "configbox.h" +#include +#include +#include +#include +#include +#include + +#include "settings.h" + +extern Options settings; + +ConfigBox::ConfigBox ( QWidget *parent, const char *name) + : KDialogBase ( parent, name, true, i18n("Configure"), Ok | Cancel, Ok, true ) +{ + QWidget *page = makeMainWidget(); + + QGridLayout *glay = new QGridLayout (page, 4, 5, 0, spacingHint()); + glay->setRowStretch(0, 1); + glay->setRowStretch(3, 1); + glay->setColStretch(0, 1); + glay->setColStretch(4, 1); + + glay->addWidget(new QLabel(i18n("Animation speed:"),page), 2, 1); + + disp = new QLCDNumber(page); + glay->addWidget(disp, 1, 2); + disp->display(1); + + speed = new QSlider(1, 10, 1, 1, QSlider::Horizontal, page); + glay->addMultiCellWidget(speed, 2, 2, 2, 3); + + connect(speed, SIGNAL(valueChanged(int)), disp, SLOT(display(int))); + + speed->setValue(settings.anim_speed); + incInitialSize(QSize(20,20), true); +} + +void ConfigBox::slotOk() +{ + settings.anim_speed = speed->value(); + settings.changed = true; + + emit speedChanged(); + + accept(); +} + +ConfigBox::~ConfigBox() +{ +} + +#include "configbox.moc" diff --git a/katomic/configbox.h b/katomic/configbox.h new file mode 100644 index 00000000..0acdfd0d --- /dev/null +++ b/katomic/configbox.h @@ -0,0 +1,35 @@ +/* configbox.h + * + * 1999 (c) Cristian Tibirna (tibirna@kde.org) + * + */ + +#ifndef CONFIGBOX_H +#define CONFIGBOX_H + +#include +#include +#include + +#include + +class ConfigBox : public KDialogBase +{ + Q_OBJECT + +public: + ConfigBox ( QWidget *, const char* name ); + ~ConfigBox(); + +protected slots: + void slotOk(); + +signals: + void speedChanged(); + +private: + QSlider *speed; + QLCDNumber *disp; +}; + +#endif diff --git a/katomic/feld.cpp b/katomic/feld.cpp new file mode 100644 index 00000000..47af46d5 --- /dev/null +++ b/katomic/feld.cpp @@ -0,0 +1,644 @@ +/**************************************************************** +** +** Implementation Feld class, derieved from Qt tutorial 8 +** +****************************************************************/ + +// bemerkungen : wenn paintEvent aufgerufen wird, wird das komplette +// widget gelöscht und nur die sachen gezeichnet, die in +// paintEvent stehen ! sollen dinge z.b nur bei maustasten- +// druck gezeichnet werden, so muß dies in mousePressEvent +// stehen ! +// paintEvent wird aufgerufen, falls fenster überdeckt wird, +// oder auch einfach bewegt wird + +#include +#include +#include +#include +#include "molek.h" +#include "feld.h" +#include "settings.h" + +#if FIELD_SIZE < MOLEK_SIZE +#error Molecule size (MOLEK_SIZE) must be <= field size (FIELD_SIZE) +#endif + +extern Options settings; + +Feld::Feld( QWidget *parent, const char *name ) : + QWidget( parent, name ), + data(locate("appdata", "pics/abilder.png")), + undoBegin (0), undoSize (0), redoSize (0) +{ + anim = false; + dir = None; + sprite = QPixmap (30, 30); + + cx = -1; + cy = -1; + + point = new QPoint [1]; + + moving = false; + chosen = false; + + setMouseTracking(true); + + setFocusPolicy(QWidget::StrongFocus); + setBackgroundColor( QColor( 0, 0, 0) ); + + setFixedSize(15 * 30, 15 * 30); +} + +Feld::~Feld () +{ + delete [] point; +} + +void Feld::resetValidDirs() +{ + for (int j = 0; j < FIELD_SIZE; j++) + for (int i = 0; i < FIELD_SIZE; i++) + if (feld[i][j] >= 150 && feld[i][j] <= 153) + { + feld[i][j] = 0; + putNonAtom(i,j, Feld::None); + } +} + +void Feld::load (const KSimpleConfig& config) +{ + if(moving) + killTimers(); + + mol->load(config); + + QString key; + + for (int j = 0; j < FIELD_SIZE; j++) { + + key.sprintf("feld_%02d", j); + QString line = config.readEntry(key); + + for (int i = 0; i < FIELD_SIZE; i++) + feld[i][j] = atom2int(line[i].latin1()); + + } + + moves = 0; + chosen = false; + moving = false; + + undoSize = redoSize = undoBegin = 0; + emit enableUndo(false); + emit enableRedo(false); + + xpos = ypos = 0; + nextAtom(); +} + +void Feld::mousePressEvent (QMouseEvent *e) +{ + if (moving) + return; + + int x = e->pos ().x () / 30; + int y = e->pos ().y () / 30; + + if ( feld [x] [y] == 150) + startAnimation (Feld::MoveUp); + else if ( feld [x] [y] == 151) + startAnimation (Feld::MoveLeft); + else if ( feld [x] [y] == 152) + startAnimation (Feld::MoveDown); + else if ( feld [x] [y] == 153) + startAnimation (Feld::MoveRight); + else if (feld [x] [y] != 254 && feld [x] [y] != 0) { + chosen = true; + xpos = x; + ypos = y; + dir = None; + resetValidDirs(); + } else { + resetValidDirs(); + chosen = false; + } + emitStatus(); +} + +const atom& Feld::getAtom(uint index) const +{ + return mol->getAtom(index); +} + + +void Feld::nextAtom() +{ + int x = xpos, y; + + // make sure we don't check the current atom :-) + if (ypos++ >= 15) ypos = 0; + + while(1) + { + for (y = ypos; y < FIELD_SIZE; y++) + { + if ( feld [x] [y] != 0 && + feld [x] [y] != 254 && + feld [x] [y] != 150 && + feld [x] [y] != 151 && + feld [x] [y] != 152 && + feld [x] [y] != 153 ) + { + xpos = x; ypos = y; + chosen = true; + resetValidDirs(); + emitStatus(); + return; + } + } + ypos = 0; + x++; + if (x >= FIELD_SIZE) x = 0; + } + +} + + +void Feld::previousAtom() +{ + int x = xpos, y; + + // make sure we don't check the current atom :-) + if (ypos-- <= 0) ypos = FIELD_SIZE-1; + + while(1) + { + for (y = ypos; y >= 0; y--) + { + if ( feld [x] [y] != 0 && + feld [x] [y] != 254 && + feld [x] [y] != 150 && + feld [x] [y] != 151 && + feld [x] [y] != 152 && + feld [x] [y] != 153 ) + { + xpos = x; ypos = y; + chosen = true; + resetValidDirs(); + emitStatus(); + return; + } + } + ypos = FIELD_SIZE-1; + x--; + if (x <= 0) x = FIELD_SIZE-1; + } +} + + +void Feld::emitStatus() +{ + if (!chosen || moving) {} + else { + + if (ypos > 0 && feld[xpos][ypos-1] == 0) { + feld [xpos][ypos-1] = 150; + putNonAtom(xpos, ypos-1, Feld::MoveUp); + } + + if (ypos < FIELD_SIZE-1 && feld[xpos][ypos+1] == 0) { + feld [xpos][ypos+1] = 152; + putNonAtom(xpos, ypos+1, Feld::MoveDown); + } + + if (xpos > 0 && feld[xpos-1][ypos] == 0) { + feld [xpos-1][ypos] = 151; + putNonAtom(xpos-1, ypos, Feld::MoveLeft); + } + + if (xpos < FIELD_SIZE-1 && feld[xpos+1][ypos] == 0) { + feld [xpos+1][ypos] = 153; + putNonAtom(xpos+1, ypos, Feld::MoveRight); + } + + } +} + +void Feld::done () +{ + if (moving) + return; + + emitStatus(); + + if (checkDone()) + emit gameOver(moves); + +} + +void Feld::startAnimation (Direction d) +{ + // if animation is already started, return + if (moving || !chosen) + return; + + switch (d) { + case MoveUp: + if (ypos == 0 || feld [xpos] [ypos-1] != 150) + return; + break; + case MoveDown: + if (ypos == FIELD_SIZE-1 || feld [xpos] [ypos+1] != 152) + return; + break; + case MoveLeft: + if (xpos == 0 || feld [xpos-1] [ypos] != 151) + return; + break; + case MoveRight: + if (xpos == FIELD_SIZE-1 || feld [xpos+1] [ypos] != 153) + return; + break; + default: + break; + } + + // reset validDirs now so that arrows don't get drawn + resetValidDirs(); + + int x = 0, y = 0; + + moves++; + emit sendMoves(moves); + dir = d; + + switch (dir) { + case MoveUp : + for (x = xpos, y = ypos-1, anz = 0; y >= 0 && feld [x] [y] == 0; anz++, y--); + if (anz != 0) + { + feld [x] [++y] = feld [xpos] [ypos]; + } + break; + case MoveDown : + for (x = xpos, y = ypos+1, anz = 0; y <= FIELD_SIZE-1 && feld [x] [y] == 0; anz++, y++); + if (anz != 0) + { + feld [x] [--y] = feld [xpos] [ypos]; + } + break; + case MoveRight : + for (x = xpos+1, y = ypos, anz = 0; x <= FIELD_SIZE-1 && feld [x] [y] == 0; anz++, x++); + if (anz != 0) + { + feld [--x] [y] = feld [xpos] [ypos]; + } + break; + case MoveLeft : + for (x = xpos-1, y = ypos, anz = 0; x >= 0 && feld [x] [y] == 0; anz++, x--); + if (anz != 0) + { + feld [++x] [y] = feld [xpos] [ypos]; + } + break; + default: + return; + } + + if (anz != 0) { + moving = true; + + // BEGIN: Insert undo informations + uint undoChunk = (undoBegin + undoSize) % MAX_UNDO; + undo[undoChunk].atom = feld[xpos][ypos]; + undo[undoChunk].oldxpos = xpos; + undo[undoChunk].oldypos = ypos; + undo[undoChunk].xpos = x; + undo[undoChunk].ypos = y; + undo[undoChunk].dir = dir; + if (undoSize == MAX_UNDO) + undoBegin = (undoBegin + 1) % MAX_UNDO; + else + ++undoSize; + redoSize = undoSize; + emit enableUndo(true); + emit enableRedo(false); + // END: Insert undo informations + + feld [xpos] [ypos] = 0; + + // absolutkoordinaten des zu verschiebenden bildes + cx = xpos * 30; + cy = ypos * 30; + xpos = x; + ypos = y; + // 30 animationsstufen + framesbak = frames = anz * 30; + + // 10 mal pro sek + startTimer (10); + + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); + } + +} + +void Feld::doUndo () +{ + if (moving || !chosen || undoSize == 0) + return; + + UndoInfo &undo_info = undo[(undoBegin + --undoSize) % MAX_UNDO]; + emit enableUndo(undoSize != 0); + emit enableRedo(true); + + --moves; + emit sendMoves(moves); + + moving = true; + resetValidDirs (); + + cx = undo_info.xpos; + cy = undo_info.ypos; + xpos = undo_info.oldxpos; + ypos = undo_info.oldypos; + feld[cx][cy] = 0; + feld[xpos][ypos] = undo_info.atom; + cx *= 30; cy *= 30; + framesbak = frames = + 30 * (abs (undo_info.xpos - undo_info.oldxpos) + + abs (undo_info.ypos - undo_info.oldypos) ); + startTimer (10); + dir = (Direction) -((int) undo_info.dir); + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); +} + +void Feld::doRedo () +{ + if (moving || !chosen || undoSize == redoSize) + return; + + UndoInfo &undo_info = undo[(undoBegin + undoSize++) % MAX_UNDO]; + + emit enableUndo(true); + emit enableRedo(undoSize != redoSize); + + ++moves; + emit sendMoves(moves); + + moving = true; + resetValidDirs (); + + cx = undo_info.oldxpos; + cy = undo_info.oldypos; + xpos = undo_info.xpos; + ypos = undo_info.ypos; + feld[cx][cy] = 0; + feld[xpos][ypos] = undo_info.atom; + cx *= 30; cy *= 30; + framesbak = frames = + 30 * (abs (undo_info.xpos - undo_info.oldxpos) + + abs (undo_info.ypos - undo_info.oldypos) ); + startTimer (10); + dir = undo_info.dir; + bitBlt (&sprite, 0, 0, this, cx, cy, 30, 30, CopyROP); +} + +void Feld::mouseMoveEvent (QMouseEvent *e) +{ + // warning: mouseMoveEvents can report positions upto 1 pixel outside + // of the field widget, so we must be sure handle this case + + if( e->pos().x() < 0 || e->pos().x() >= 450 || + e->pos().y() < 0 || e->pos().y() >= 450 ) + { + setCursor(arrowCursor); + } + else + { + int x = e->pos ().x () / 30; + int y = e->pos ().y () / 30; + + // verschiedene cursor je nach pos + if (feld[x][y] != 254 && feld [x] [y] != 0) + setCursor (crossCursor); + else + setCursor (arrowCursor); + } +} + + +bool Feld::checkDone () +{ + int molecWidth = mol->molecSize().width(); + int molecHeight = mol->molecSize().height(); + int i = 0; + int j = 0; + + // find first atom in molecule + uint firstAtom = 0; + for(j = 0; j < molecHeight && !firstAtom; ++j) + firstAtom = mol->getAtom(0, j); + + // wot no atom? + if(!firstAtom) + return true; // true skips to next level + + // position of first atom (in molecule coordinates) + int mx = 0; + int my = j - 1; + + QRect extent(0, 0, FIELD_SIZE - molecWidth + 1, FIELD_SIZE - molecHeight + 1); + extent.moveBy(0, my); + + // find first atom in playing field + for(i = extent.left(); i <= extent.right(); ++i) + { + for(j = extent.top(); j <= extent.bottom(); ++j) + { + if(feld[i][j] == firstAtom) + { + // attempt to match playing field to molecule + int ox = i - mx; // molecule origin (in field coordinates) + int oy = j - my; // molecule origin (in field coordinates) + ++my; // no need to test first atom again + while(mx < molecWidth) + { + while(my < molecHeight) + { + uint nextAtom = mol->getAtom(mx, my); + if(nextAtom != 0 && feld[ox + mx][oy + my] != nextAtom) + return false; + ++my; + } + my = 0; + ++mx; + } + return true; + } + } + } + // if we got here, then the first atom is too low or too far right + // for the molecule to be assembled correctly + return false; +} + + +void Feld::timerEvent (QTimerEvent *) +{ + // animation beenden + if (frames <= 0) + { + moving = false; + killTimers (); + done(); + dir = None; + } + else + { + frames -= settings.anim_speed; + if (frames < 0) + frames = 0; + + paintMovingAtom(); + } +} + +void Feld::paintMovingAtom() +{ + int a = settings.anim_speed; + + QPainter paint(this); + + switch(dir) + { + case MoveUp: + bitBlt(this, cx, cy - framesbak + frames, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx, cy - framesbak + frames + 30, 30, a); + break; + case MoveDown: + bitBlt(this, cx, cy + framesbak - frames, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx, cy + framesbak - frames - a, 30, a); + break; + case MoveRight: + bitBlt(this, cx + framesbak - frames, cy, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx + framesbak - frames - a, cy, a, 30); + break; + case MoveLeft: + bitBlt(this, cx - framesbak + frames, cy, &sprite, CopyROP); + if(framesbak - frames > 0) + paint.eraseRect(cx - framesbak + frames + 30, cy, a, 30); + break; + case None: + break; + } +} + +void Feld::putNonAtom (int x, int y, Direction which, bool brick) +{ + int xarr=0, yarr=0; + switch (which) + { + case None : xarr = 279, yarr = 31 * (brick?1:2); break; + case MoveUp : xarr = 248; yarr = 62; break; + case MoveLeft : xarr = 217; yarr = 93; break; + case MoveDown : xarr = 248; yarr = 93; break; + case MoveRight : xarr = 279; yarr = 93; break; + } + + bitBlt(this, x * 30, y * 30, &data, xarr, yarr, 30, 30, CopyROP); +} + +void Feld::paintEvent( QPaintEvent * ) +{ + int i, j, x, y; + + QPainter paint ( this ); + + paint.setPen (black); + + // spielfeld gleich zeichnen + + for (i = 0; i < FIELD_SIZE; i++) + { + for (j = 0; j < FIELD_SIZE; j++) + { + if(moving && i == xpos && j == ypos) + continue; + + x = i * 30; + y = j * 30; + + // zeichnet Randstücke + if (feld [i] [j] == 254) { + putNonAtom(i, j, Feld::None, true); continue; + } + + if (feld[i][j] == 150) { + putNonAtom(i, j, Feld::MoveUp); continue; + } + + if (feld[i][j] == 151) { + putNonAtom(i, j, Feld::MoveLeft); continue; + } + if (feld[i][j] == 152) { + putNonAtom(i, j, Feld::MoveDown); continue; + } + + if (feld[i][j] == 153) { + putNonAtom(i, j, Feld::MoveRight); continue; + } + + // zeichnet Atome + if (getAtom(feld [i] [j]).obj <= '9' && getAtom(feld [i] [j]).obj > '0') + { + bitBlt (this, x, y, &data, (getAtom(feld [i] [j]).obj - '1') * 31, 0, 30, + 30, CopyROP); + } + + // zeichnet Kristalle + if (getAtom(feld [i] [j]).obj == 'o') + { + bitBlt (this, x, y, &data, 31, 93, 30, 30, CopyROP); + } + + + + // verbindungen zeichnen + if (getAtom(feld [i] [j]).obj <= '9' || + getAtom(feld [i] [j]).obj == 'o') + for (int c = 0; c < MAX_CONNS_PER_ATOM; c++) { + char conn = getAtom(feld [i] [j]).conn[c]; + if (!conn) + break; + + if (conn >= 'a' && conn <= 'a' + 8) + bitBlt (this, x, y, + &data, (conn - 'a') * 31, 31, 30, 30, + XorROP); + else + bitBlt (this, x, y, + &data, (conn - 'A') * 31, 62, 30, 30, + XorROP); + + } + + // zeichnet Verbindungsstäbe + if (getAtom(feld [i] [j]).obj >= 'A' && + getAtom(feld [i] [j]).obj <= 'F') + bitBlt (this, x, y, + &data, + (getAtom(feld [i] [j]).obj - 'A' + 2) * 31 , + 93, 30, 30, + CopyROP); + } + } +} + + +#include "feld.moc" diff --git a/katomic/feld.h b/katomic/feld.h new file mode 100644 index 00000000..6a1cf761 --- /dev/null +++ b/katomic/feld.h @@ -0,0 +1,116 @@ +/**************************************************************** +** +** Definition of Feld class, +** +****************************************************************/ + +#ifndef FELD_H +#define FELD_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "atom.h" + +#define MAX_UNDO (100) + +class KSimpleConfig; +class Molek; + +#define FIELD_SIZE 15 + +class Feld : public QWidget +{ + Q_OBJECT + +public: + Feld (QWidget *parent=0, const char *name=0); + ~Feld (); + + enum Direction { None = 0, + MoveUp = 1, + MoveDown = (-1), + MoveLeft = 2, + MoveRight = (-2) }; + + void startAnimation (Direction dir); + void done (); + + void load (const KSimpleConfig& config); + + void setMolek(Molek *_mol) { mol = _mol; } + + void doUndo (); + void doRedo (); + +signals: + void gameOver(int moves); + void sendMoves(int moves); + void enableRedo(bool enable); + void enableUndo(bool enable); + +protected: + bool checkDone(); + void timerEvent (QTimerEvent *); + void paintEvent( QPaintEvent * ); + void paintMovingAtom(); + void mousePressEvent (QMouseEvent *); + void mouseMoveEvent (QMouseEvent *); + void emitStatus(); + +protected: + struct UndoInfo { + uint atom; + int oldxpos, oldypos; + int xpos, ypos; + Direction dir; + }; + +public slots: + void nextAtom(); + void previousAtom(); + +private: + + const atom& getAtom(uint index) const; + + void putNonAtom(int, int, Direction, bool brick = false); + + QPoint *point; + QPixmap data; + QPixmap sprite; + + Molek *mol; + + uint feld[FIELD_SIZE][FIELD_SIZE]; + + // number of movements + int moves; + + Direction dir; + int cx, cy; + int xpos, ypos; + int anz; + int frames, framesbak; + + bool anim; + bool chosen, moving; + + uint undoBegin; + uint undoSize; + uint redoSize; + UndoInfo undo[MAX_UNDO]; + + void resetValidDirs(); + +}; + +#endif // FELD_H diff --git a/katomic/gamewidget.cpp b/katomic/gamewidget.cpp new file mode 100644 index 00000000..fc6838a8 --- /dev/null +++ b/katomic/gamewidget.cpp @@ -0,0 +1,230 @@ +/* toplevel.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include "settings.h" +#include "gamewidget.h" +#include "feld.h" +#include "molek.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +Options settings; + +#define MPOSX 480 +#define MPOSY 90 + +// ########################## +// # class GameWidget # +// ########################## + +int level; + +void GameWidget::moveUp() +{ + feld->startAnimation (Feld::MoveUp); +} + +void GameWidget::moveDown() +{ + feld->startAnimation (Feld::MoveDown); +} + +void GameWidget::moveLeft() +{ + feld->startAnimation (Feld::MoveLeft); +} + +void GameWidget::moveRight() +{ + feld->startAnimation (Feld::MoveRight); +} + +void GameWidget::nextAtom() +{ + feld->nextAtom(); +} + +void GameWidget::previousAtom() +{ + feld->previousAtom(); +} + +void GameWidget::getButton (int button) +{ + feld->startAnimation ((Feld::Direction)button); +} + +void GameWidget::doUndo () +{ + feld->doUndo (); +} + +void GameWidget::doRedo () +{ + feld->doRedo (); +} + +void GameWidget::gameOver(int moves) { + KMessageBox::information(this, i18n("You solved level %1 with %2 moves!").arg(level).arg(moves), i18n("Congratulations")); + + KScoreDialog high(KScoreDialog::Name | KScoreDialog::Score, this); + high.setCaption(i18n("Level %1 Highscores").arg(level)); + high.setConfigGroup(QString("Highscores Level %1").arg(level)); + + KScoreDialog::FieldInfo scoreInfo; + + if (high.addScore(moves, scoreInfo, true, true)) + { + high.exec(); + } + updateLevel(level+1); +} + +void GameWidget::getMoves(int moves) +{ + current.setNum(moves); + ys->setText(current); +} + +void GameWidget::updateLevel (int l) +{ + level=l; + QString levelFile = locate("appdata", QString("levels/level_%1").arg(l)); + if (levelFile.isNull()) { + return updateLevel(1); + } + + KSimpleConfig cfg(levelFile, true); + cfg.setGroup("Level"); + feld->load(cfg); + + highScore->setConfigGroup(QString("High Scores Level %1").arg(level)); + highest.setNum(highScore->highScore()); + + if (highest != "0" ) hs->setText(highest); + else hs->setText("-"); + ys->setText("0"); + scrl->setValue(level); + + feld->repaint(); +} + +void GameWidget::restartLevel() +{ + updateLevel(level); +} + +GameWidget::GameWidget ( QWidget *parent, const char* name ) + : QWidget( parent, name ) +{ + level = 1; + nlevels = KGlobal::dirs()->findAllResources("appdata", "levels/level_*", + false, true).count(); + + QHBoxLayout *top = new QHBoxLayout(this, 10); + + // spielfeld + feld = new Feld (this, "feld"); + feld->setFocus(); + + top->addWidget(feld); + + QVBox *vb = new QVBox(this); + vb->setSpacing(20); + top->addWidget(vb); + + // scrollbar + scrl = new QScrollBar(1, nlevels, 1, + 5, 1, QScrollBar::Horizontal, vb, "scrl" ); + connect (scrl, SIGNAL (valueChanged (int)), SLOT (updateLevel (int))); + + // molekül + molek = new Molek (vb, "molek"); + feld->setMolek(molek); + + connect (feld, SIGNAL (gameOver(int)), SLOT(gameOver(int))); + connect (feld, SIGNAL (sendMoves(int)), SLOT(getMoves(int))); + connect (feld, SIGNAL (enableRedo(bool)), SIGNAL(enableRedo(bool))); + connect (feld, SIGNAL (enableUndo(bool)), SIGNAL(enableUndo(bool))); + + highScore = new KScoreDialog(KScoreDialog::Name | KScoreDialog::Score, this); + + // the score group + QGroupBox *bg = new QGroupBox (i18n("Score"), vb, "bg"); + QBoxLayout *slay = new QVBoxLayout (bg, 10); + + slay->addSpacing(10); + + slay->addWidget(new QLabel(i18n("Highscore:"), bg)); + + QFont headerFont = KGlobalSettings::generalFont(); + headerFont.setBold(true); + + hs = new QLabel (highest, bg); + hs->setAlignment(Qt::AlignRight); + hs->setFont(headerFont); + slay->addWidget(hs); + + slay->addSpacing(10); + + slay->addWidget(new QLabel(i18n("Your score so far:"), bg)); + + ys = new QLabel (current, bg); + ys->setAlignment(Qt::AlignRight); + ys->setFont(headerFont); + slay->addWidget(ys); + + updateLevel(1); + + KConfig *config = KGlobal::config(); + config->setGroup("Options"); + settings.anim_speed = config->readNumEntry("Animation Speed", 1); + if (settings.anim_speed < 1 || settings.anim_speed > MAX_SPEED) + settings.anim_speed = 1; + + settings.changed = false; +} + +GameWidget::~GameWidget() +{ +} + +void GameWidget::showHighscores () +{ + KScoreDialog high(KScoreDialog::Name | KScoreDialog::Score, this); + high.setCaption(i18n("Level %1 Highscores").arg(level)); + high.setConfigGroup(QString("Highscores Level %1").arg(level)); + high.exec(); +} + +#include "gamewidget.moc" diff --git a/katomic/gamewidget.h b/katomic/gamewidget.h new file mode 100644 index 00000000..6259e6b7 --- /dev/null +++ b/katomic/gamewidget.h @@ -0,0 +1,74 @@ + +#ifndef GAMEWIDGET_H +#define GAMEWIDGET_H + +class Feld; +class Molek; +class QScrollBar; +class QLabel; +class KScoreDialog; + +#include + +class GameWidget : public QWidget +{ + Q_OBJECT + + public: + + GameWidget ( QWidget *parent, const char *name=0 ); + + ~GameWidget(); + + signals: + void enableRedo(bool enable); + void enableUndo(bool enable); + + public slots: + // bringt level auf neuesten stand + void updateLevel (int); + + // restart current level + void restartLevel(); + + // getbutton erhält button der gedrückt wurde + void getButton (int); + + void gameOver(int moves); + + // use this slot to update the moves continually + void getMoves(int moves); + + // Menupunkt Highscores im Pop-up Menu, der Highscore anzeigt + void showHighscores (); + + void moveUp(); + void moveDown(); + void moveLeft(); + void moveRight(); + void nextAtom(); + void previousAtom(); + void doUndo (); + void doRedo (); + + protected: + + // stellt das spielfeld dar ! + Feld *feld; + + // stellt molekül dar + Molek *molek; + + // scorllbar zur levelwahl + QScrollBar *scrl; + + // important labels : highest and current scores + QLabel *hs, *ys; + QString highest, current; + + int nlevels; + + KScoreDialog *highScore; +}; + +#endif diff --git a/katomic/hi128-app-katomic.png b/katomic/hi128-app-katomic.png new file mode 100644 index 0000000000000000000000000000000000000000..94f1cbd000262f221603059d2d1e0308e96eed23 GIT binary patch literal 18820 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 literal 0 HcmV?d00001 diff --git a/katomic/hi22-app-katomic.png b/katomic/hi22-app-katomic.png new file mode 100644 index 0000000000000000000000000000000000000000..d4a26a6a8dc444d85de50aeb841ad21da6007a78 GIT binary patch literal 4145 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 literal 0 HcmV?d00001 diff --git a/katomic/hi32-app-katomic.png b/katomic/hi32-app-katomic.png new file mode 100644 index 0000000000000000000000000000000000000000..afbe144d73e610d3822778e86d45fbb8c8382ee6 GIT binary patch literal 2425 zcmV-<35NEGP)7AmdFILgBrtqUTE5d=hiAVQ!RAql}iHs1c;z4zSr-svAG z5Ee&m`~CNv-}!!jzw=wpfnyO2Y7@d?*K?*>zeLK(k7hEb)7)(ELlFw;zlq07|0d;( zWl~OiEEX&I`aJQ?7VVhrqUPqC-80WR>xvFtUtJ_v7d4DGtBTIQkB#OSzY_E34Hb2@ z8uU1v6KPY!2x~iucukhsFEcr|f)!6^ShPq1>IC97kwW$rT*@ECr3YP{W8EP}53c|R zc(Al%Gt=z$(Bs#fd?L@ zv9X)Eb8}yd*Jx}MjJe`mR`38xin*8%s737glF_f&kk3O~2Si4RDgprmum=hNVtPI% z{o1C@8@Qaz;nXQ&OpF3!lM7^QC|%)Kmk{hb@;%p(-)1Ypu67xAhad5621q}Ea` zUgdO^!ADp7r%ab$h$^MQ!GP+pit;;2YbU)%K_XF(W6j|ixm8~*_v2vSa9wS1Y^5ro4H#a zqDMZ6X^tQibbmvt50Ef!T$Z6DGdQ9^MJZPoi>I)xfAh0`&1EA#8M6=rjsUS7<#p`($yW1>ScnIwmyDGD_W7jD&0 zCKF>~waKxS@nBdZ>Y7M~kxUV96SYm&&B~tI`@;}KB1NR^alB-&<1_gH?d@KI!NX!( zTHFP;-RRz6e^TBhcNMnFHt*Z^$D~q226!I|=_HdCJTKqiefu%`(?bcixKF`(9F~M# zB$I>bwtvFQavO)ti!jYvdhNN_ec{9l_Q|m46Pd3*C^KbU()O&8($^{9qDwwRMoyux zFTlZr1%%~7mdp3C?Quk-#SlCp;-~6rf}WlN3JV9*ru~||Vzw(sR=99AR8!g&7ryfw z;Sx94d3eErj-4uppN>d3Zt_I4qtzSi=EuxbkK@XI#7nymamPgGc%qZ_c|kBJ@cRqc zq1{4MOd3~1Wk*C9#?<^{W2Ly#b{M_D4-*W?16~`RI2$qA!>)J6s((3c;!?I~3n)=r z5%~=1bczFS=1%6Regb(Yq;cKt<9WmQGl*)=RWEi|vD{MlU0cQFMm4?$mnfL$J{rOX z17385$m^=R*CwU5h|BZS)eGn%$p_jERC>A?WVG^pEwJv`_B|j1g63wOs`Hw7jwhI= z)-gf6L@pmCk47z#j89wm=~gk-=LOC^YUojr>44URn4V2BCOeHkP$)j2lq`Ox^Op7~ zKbqUKHS{?u&@21gt7H_* zx{_FI(Baz&YP9wjVw&}AmJ69L-#^@nL;8`jCLDI%D&>^hO}XW{w4?5EWY4Iyyl+r{ ze<8fUvk9La+a%8bZ+3_JRF%0k7N$rj&XNa0d zO0}7#j(Dao@Be8{ZLMJ>Jf48FhpLUe&^H*OY1rRJ11gFfq#LVI-?iY6op%G6As)+u4h0 zrg6Dkcs*Y_v)RTD{eHTs<#X<)M+28Hj*XQuRmE36{I5shG%p-D2#GrS`-IR9Ofwy= zcE6QrQl7{cj5o{ZSL65)@_W(sDEWL6rEG*Su*3uosVOK@baX@*Ge!c?*=bT_OL)w5!pK@qTt>t)W3DqgTg$ve_t$Km3*l=YJ~z6sBqG#h&fmaedc@HKIWm(Cbru`Z;WJ94CE3^8Swn!0z3) zJ$dq%`3uzJneAMnEZo_%Y0mo9Cv^5^zVC5AEF;_7y;M{T!Z6AJK`s}gHQvwIAsGh# r7ypk41~po6(}J5&==hh_cgBAJetUgbEAtfq00000NkvXXu0mjfaj&R& literal 0 HcmV?d00001 diff --git a/katomic/hi48-app-katomic.png b/katomic/hi48-app-katomic.png new file mode 100644 index 0000000000000000000000000000000000000000..8f18153efb2575380bf169fdf1924db032a3babd GIT binary patch literal 4944 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 literal 0 HcmV?d00001 diff --git a/katomic/katomic.desktop b/katomic/katomic.desktop new file mode 100644 index 00000000..a614a171 --- /dev/null +++ b/katomic/katomic.desktop @@ -0,0 +1,73 @@ +[Desktop Entry] +Name=KAtomic +Name[af]=Katomic +Name[ar]=لعبة التركيب الجزيئي الكيميائي (KAtomic) +Name[be]=Ðтамы +Name[bn]=কে-অà§à¦¯à¦¾à¦Ÿà¦®à¦¿à¦• +Name[eo]=Atomoj +Name[hi]=के-à¤à¤Ÿà¥‰à¤®à¤¿à¤• +Name[ne]=केडीई à¤à¤Ÿà¥‹à¤®à¤¿à¤• +Name[pa]=ਕੇ-ਪਰਮਾਣੂ +Name[pl]=Atomy +Name[pt_BR]=KAtômico +Name[sv]=Katomic +Name[ta]=கேஅடாமிக௠+Name[tg]=KÐтомҳо +Name[th]=อะตอมมิภ- K +Name[tr]=Atomlar +Name[zh_TW]=KAtomic åŽŸå­ +Icon=katomic +Type=Application +Exec=katomic %i %m -caption "%c" +DocPath=katomic/index.html +GenericName=Sokoban-like Logic Game +GenericName[be]=Ð›Ð°Ð³Ñ–Ñ‡Ð½Ð°Ñ Ð³ÑƒÐ»ÑŒÐ½Ñ Ñ‚Ñ‹Ð¿Ñƒ Sokoban +GenericName[bg]=ЛогичеÑка игра Ñ Ð°Ñ‚Ð¾Ð¼Ð¸ +GenericName[bn]=সোকোবান-à¦à¦° মত যà§à¦•à§à¦¤à¦¿à¦° খেলা +GenericName[bs]=LogiÄka igra nalik na Sokoban +GenericName[ca]=Joc de lògica similar al Sokoban +GenericName[cs]=Logická hra jako Sokoban +GenericName[cy]=Gêm Resymeg sy'n debyg i Sokoban +GenericName[da]=Sokoban-lignende logisk spil +GenericName[de]=Logikspiel (ähnlich Sokoban) +GenericName[el]=Παιχνίδι λογικής παÏόμοιο με το Sokoban +GenericName[eo]=Logikludo simila al Sokoban +GenericName[es]=Juego de lógica similar al Sokoban +GenericName[et]=Sokobani moodi loogikamäng +GenericName[eu]=Sokoban-en antzeko joko logikoa +GenericName[fi]=Sokoban-tyylinen Logiikkapeli +GenericName[fr]=Jeu de logique dans le style de Sokoban +GenericName[ga]=Cluiche Loighce Mar Sokoban +GenericName[he]=חיקוי Sokoban, משחק לוגי +GenericName[hr]=LogiÄka igra poput Sokobana +GenericName[hu]=Sokoban-szerű logikai +GenericName[is]=Leikur sem líkist Sokoban +GenericName[it]=Gioco di logica simile a Sokoban +GenericName[ja]=倉庫番ã®ã‚ˆã†ãªè«–ç†ã‚²ãƒ¼ãƒ  +GenericName[km]=ល្បែង​ážáž€áŸ’ក​ដូច Sokoban +GenericName[ko]=소코반 ê°™ì€ ì „ëžµ 게임 +GenericName[lt]=Sokoban tipo loginis žaidimas +GenericName[lv]=Sokoban lÄ«dzÄ«ga loÄ£iskÄ spÄ“le +GenericName[mk]=Логичка игра Ñлична на Сокобан +GenericName[nb]=Sokoban-lignende logikkspill +GenericName[nds]=Sokoban-liek Logikspeel +GenericName[ne]=सोकोबान जसà¥à¤¤à¥ˆ यà¥à¤•à¥à¤¤à¤¿à¤¸à¤‚गत खेल +GenericName[nl]=Sokoban-achtig logisch spel +GenericName[nn]=Sokoban-liknande logikkspel +GenericName[pl]=Gra logiczna typu Sokoban +GenericName[pt]=Jogo de Lógica tipo Sokoban +GenericName[pt_BR]=Jogo Lógico parecido com Sokoban +GenericName[ru]=Забавные атомы +GenericName[se]=Sokoban-lágan logihkkaspeallu +GenericName[sk]=Logická hra ako Sokoban +GenericName[sl]=LogiÄna igra podobna Sokobanu +GenericName[sr]=Логичка игра налик на Sokoban +GenericName[sr@Latn]=LogiÄka igra nalik na Sokoban +GenericName[sv]=Sokoban-liknande logiskt spel +GenericName[ta]=சோகோபான௠போனà¯à®± லாஜிக௠விளையாடà¯à®Ÿà¯ +GenericName[uk]=Гра на логіку подібна до Сокобану +GenericName[wa]=Djeu di lodjike rishonnant a sokoban +GenericName[zh_TW]=倉庫番(sokoban)å¼çš„智力éŠæˆ² +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Game;StrategyGame; diff --git a/katomic/katomicui.rc b/katomic/katomicui.rc new file mode 100644 index 00000000..da25f243 --- /dev/null +++ b/katomic/katomicui.rc @@ -0,0 +1,6 @@ + + + + + + diff --git a/katomic/levels/level_1 b/katomic/levels/level_1 new file mode 100644 index 00000000..dc63e883 --- /dev/null +++ b/katomic/levels/level_1 @@ -0,0 +1,21 @@ +[Level] +Name=Water +atom_1=1-c +atom_2=3-cg +atom_3=1-g +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=..###########.. +feld_04=..#..#......#.. +feld_05=..#.3#......#.. +feld_06=..#.##......#.. +feld_07=..#.#..#.####.. +feld_08=..#....#.2..#.. +feld_09=..###.#.....#.. +feld_10=..#1....#...#.. +feld_11=..###########.. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=123 diff --git a/katomic/levels/level_10 b/katomic/levels/level_10 new file mode 100644 index 00000000..28bce761 --- /dev/null +++ b/katomic/levels/level_10 @@ -0,0 +1,24 @@ +[Level] +Name=Formic Acid +atom_1=1-c +atom_2=3-C +atom_3=2-cgA +atom_4=3-cg +atom_5=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.###..###..###. +feld_04=.#.....#.....#. +feld_05=.#.###...###.#. +feld_06=.#.#2..#...#.#. +feld_07=.#1435###....#. +feld_08=.#.#...#...#.#. +feld_09=.#.###...###.#. +feld_10=.#.....#.....#. +feld_11=.###..###..###. +feld_12=.#...........#. +feld_13=.#############. +feld_14=............... +mole_0=.2.. +mole_1=1345 diff --git a/katomic/levels/level_11 b/katomic/levels/level_11 new file mode 100644 index 00000000..4b423171 --- /dev/null +++ b/katomic/levels/level_11 @@ -0,0 +1,28 @@ +[Level] +Name=Acetic Acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-C +atom_6=2-cgA +atom_7=3-cg +atom_8=1-g +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#...#...#.#.. +feld_04=..#.7.......#.. +feld_05=..#.#..8#.1.#.. +feld_06=..#4.3......#.. +feld_07=..#...#...#.#.. +feld_08=..#.........#.. +feld_09=..#.#.6.#...#.. +feld_10=..#5.....2..#.. +feld_11=..#...#...#.#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=.25.. +mole_1=13678 +mole_2=.4... diff --git a/katomic/levels/level_12 b/katomic/levels/level_12 new file mode 100644 index 00000000..bb51be76 --- /dev/null +++ b/katomic/levels/level_12 @@ -0,0 +1,29 @@ +[Level] +Name=trans-Butene +atom_1=1-d +atom_2=1-b +atom_3=2-bdfh +atom_4=2-fhB +atom_5=1-h +atom_6=2-bdD +atom_7=1-f +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.#...#..6#...#. +feld_04=.#4.#.7...#..#. +feld_05=.#.#.##.##.#.#. +feld_06=.#..2........#. +feld_07=.###5##.##.###. +feld_08=.#...........#. +feld_09=.#.#.##1##.#.#. +feld_10=.#..#31...#1.#. +feld_11=.#...#..5#...#. +feld_12=.#3..5.......#. +feld_13=.#############. +feld_14=............... +mole_0=...1.7 +mole_1=.1..3. +mole_2=1.46.5 +mole_3=.3..5. +mole_4=2.5... diff --git a/katomic/levels/level_13 b/katomic/levels/level_13 new file mode 100644 index 00000000..29433c5e --- /dev/null +++ b/katomic/levels/level_13 @@ -0,0 +1,28 @@ +[Level] +Name=cis-Butene +atom_1=1-d +atom_2=1-b +atom_3=2-bdfh +atom_4=2-fhB +atom_5=1-h +atom_6=2-bdD +atom_7=1-f +feld_00=............... +feld_01=.#############. +feld_02=.#.#..##...#.#. +feld_03=.##..#.....#.#. +feld_04=.##....#..#..#. +feld_05=.#.5..1#.2..7#. +feld_06=.###....#.#..#. +feld_07=.#..#......###. +feld_08=.#.4..3.#6..3#. +feld_09=.#.##.##.....#. +feld_10=.##......#.#.#. +feld_11=.#.7##5.#2.#1#. +feld_12=.#.#....#.#..#. +feld_13=.#############. +feld_14=............... +mole_0=.1..7. +mole_1=1.46.7 +mole_2=.3..3. +mole_3=2.52.5 diff --git a/katomic/levels/level_14 b/katomic/levels/level_14 new file mode 100644 index 00000000..1dd41fa7 --- /dev/null +++ b/katomic/levels/level_14 @@ -0,0 +1,26 @@ +[Level] +Name=Dimethyl ether +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...3.#5...1#. +feld_03=.#.###.#.###.#. +feld_04=.#.#..4#...#.#. +feld_05=.#.#...#...#.#. +feld_06=.#.....#.....#. +feld_07=.####2....####. +feld_08=.#.....#.....#. +feld_09=.#.#...#...#.#. +feld_10=.#.#..6#..4#.#. +feld_11=.#.###.#.###2#. +feld_12=.#....3#.....#. +feld_13=.#############. +feld_14=............... +mole_0=.2.2. +mole_1=13536 +mole_2=.4.4. diff --git a/katomic/levels/level_15 b/katomic/levels/level_15 new file mode 100644 index 00000000..5de947f1 --- /dev/null +++ b/katomic/levels/level_15 @@ -0,0 +1,27 @@ +[Level] +Name=Butanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#..42.....3.#. +feld_03=.#.#..####.###. +feld_04=.###2...#....#. +feld_05=.#.6..#.....4#. +feld_06=.#.##.#.##.4.#. +feld_07=.#.#..#..#.###. +feld_08=.###.3#.5.3#.#. +feld_09=.#1..........#. +feld_10=.#4...#..2...#. +feld_11=.#..###2##.#.#. +feld_12=.#....#.3..#.#. +feld_13=.#############. +feld_14=............... +mole_0=.2222. +mole_1=133336 +mole_2=.4445. +mole_3=....4. diff --git a/katomic/levels/level_16 b/katomic/levels/level_16 new file mode 100644 index 00000000..0e466be9 --- /dev/null +++ b/katomic/levels/level_16 @@ -0,0 +1,31 @@ +[Level] +Name=2-Methyl-2-Propanol +atom_1=1-c +atom_2=1-d +atom_3=1-e +atom_4=2-aceg +atom_5=1-a +atom_6=2-abeh +atom_7=3-ae +atom_8=1-f +atom_9=1-g +feld_00=............... +feld_01=.#####...#####. +feld_02=.#9.6#...#5.2#. +feld_03=.#8#.#####.#.#. +feld_04=.#5....#.....#. +feld_05=.###.#..3#.###. +feld_06=...#.....54#... +feld_07=...##.....##... +feld_08=...#4.....3#... +feld_09=.###.#...#.###. +feld_10=.#.....#.3..1#. +feld_11=.#.#7#####.#.#. +feld_12=.#4..#...#...#. +feld_13=.#####...#####. +feld_14=............... +mole_0=.238. +mole_1=.363. +mole_2=14449 +mole_3=.575. +mole_4=..5.. diff --git a/katomic/levels/level_17 b/katomic/levels/level_17 new file mode 100644 index 00000000..dc9efe1b --- /dev/null +++ b/katomic/levels/level_17 @@ -0,0 +1,28 @@ +[Level] +Name=Glycerin +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=..###########.. +feld_02=..#.#.4#3.#.#.. +feld_03=..#.5#.#.#..#.. +feld_04=..##..3....##.. +feld_05=..#.#.....#.#.. +feld_06=..#.6....1..#.. +feld_07=..####5..####.. +feld_08=..#....15.1.#.. +feld_09=..#.#....3#.#.. +feld_10=..##.....6.##.. +feld_11=..#..#.#.#.2#.. +feld_12=..#.#.6#..#.#.. +feld_13=..###########.. +feld_14=............... +mole_0=.2.. +mole_1=1356 +mole_2=1356 +mole_3=1356 +mole_4=.4.. diff --git a/katomic/levels/level_18 b/katomic/levels/level_18 new file mode 100644 index 00000000..3fcd823d --- /dev/null +++ b/katomic/levels/level_18 @@ -0,0 +1,23 @@ +[Level] +Name=Poly-Tetra-Fluoro-Ethene +atom_1=6-e +atom_2=6-a +atom_3=2-aceg +feld_00=............... +feld_01=.#############. +feld_02=.#...........#. +feld_03=.#....#2#1...#. +feld_04=.#3...#.#....#. +feld_05=.#.####3####.#. +feld_06=.#2..........#. +feld_07=.####..#.2####. +feld_08=.#1.........1#. +feld_09=.#.####.####.#. +feld_10=.#..2.#.#....#. +feld_11=.#1...#.#...3#. +feld_12=.#..3........#. +feld_13=.#############. +feld_14=............... +mole_0=1111 +mole_1=3333 +mole_2=2222 diff --git a/katomic/levels/level_19 b/katomic/levels/level_19 new file mode 100644 index 00000000..70871b37 --- /dev/null +++ b/katomic/levels/level_19 @@ -0,0 +1,26 @@ +[Level] +Name=Oxalic Acid +atom_1=3-D +atom_2=2-ceD +atom_3=3-ae +atom_4=1-a +atom_5=2-egB +atom_6=3-B +feld_00=............... +feld_01=############### +feld_02=###3...#....### +feld_03=##.....#..6.5## +feld_04=#...#.....#...# +feld_05=#...##.2.##...# +feld_06=#....##.##....# +feld_07=###1...3....### +feld_08=#....##.##....# +feld_09=#...##.4.##...# +feld_10=#.4.#.....#...# +feld_11=##.....#.....## +feld_12=###....#....### +feld_13=############### +feld_14=............... +mole_0=6251 +mole_1=.33 +mole_2=.44 diff --git a/katomic/levels/level_2 b/katomic/levels/level_2 new file mode 100644 index 00000000..bc211909 --- /dev/null +++ b/katomic/levels/level_2 @@ -0,0 +1,25 @@ +[Level] +Name=Methane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +feld_00=............... +feld_01=............... +feld_02=.#############. +feld_03=.#...#..#....#. +feld_04=.#.#..#5#....#. +feld_05=.#..#...#3.#.#. +feld_06=.#......####.#. +feld_07=.#.......#1#.#. +feld_08=.#.2..#..#...#. +feld_09=.#.#####....##. +feld_10=.#.4.#..#...#.. +feld_11=.#####..#...#.. +feld_12=........#####.. +feld_13=............... +feld_14=............... +mole_0=.2. +mole_1=135 +mole_2=.4. diff --git a/katomic/levels/level_20 b/katomic/levels/level_20 new file mode 100644 index 00000000..8ae7aaf9 --- /dev/null +++ b/katomic/levels/level_20 @@ -0,0 +1,24 @@ +[Level] +Name=Formaldehyde +atom_1=1-e +atom_2=2-aeB +atom_3=1-a +atom_4=3-D +feld_00=............... +feld_01=.#############. +feld_02=.#4....#....2#. +feld_03=.#..##.#.##..#. +feld_04=.##.#.....#.##. +feld_05=.#..#.....#..#. +feld_06=.#.##..#..####. +feld_07=.#....###....#. +feld_08=.#.##..#..##.#. +feld_09=.#..#.....#..#. +feld_10=.##.#.....#.##. +feld_11=.#..##.#.##..#. +feld_12=.#3....#....1#. +feld_13=.#############. +feld_14=............... +mole_0=1 +mole_1=24 +mole_2=3 diff --git a/katomic/levels/level_21 b/katomic/levels/level_21 new file mode 100644 index 00000000..a15a81c6 --- /dev/null +++ b/katomic/levels/level_21 @@ -0,0 +1,30 @@ +[Level] +Name=Crystal 1 +atom_1=o-ce +atom_2=o-ace +atom_3=o-ac +atom_4=o-ceg +atom_5=o-aceg +atom_6=o-acg +atom_7=o-eg +atom_8=o-aeg +atom_9=o-ag +feld_00=............... +feld_01=......##....... +feld_02=.....####...... +feld_03=.....#55#...... +feld_04=....##56##..... +feld_05=..###89..###... +feld_06=.##2....72.##.. +feld_07=.##.41...5.##.. +feld_08=..###34.6###... +feld_09=....##.8##..... +feld_10=.....#..#...... +feld_11=.....####...... +feld_12=......##....... +feld_13=............... +feld_14=............... +mole_0=1447 +mole_1=2558 +mole_2=2558 +mole_3=3669 diff --git a/katomic/levels/level_22 b/katomic/levels/level_22 new file mode 100644 index 00000000..50568c5a --- /dev/null +++ b/katomic/levels/level_22 @@ -0,0 +1,28 @@ +[Level] +Name=Acetic acid ethyl ester +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-C +atom_6=2-cgA +atom_7=3-cg +atom_8=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#..##.#.##..#. +feld_03=.#...#.4.#...#. +feld_04=.#....3...3.3#. +feld_05=.##..#...#..##. +feld_06=.#7.##6..##..#. +feld_07=.#8....#.....#. +feld_08=.#..##...##..#. +feld_09=.##..#22.#4.##. +feld_10=.#...2.......#. +feld_11=.#..5#..1#...#. +feld_12=.#.4##.#.##..#. +feld_13=.#############. +feld_14=............... +mole_0=.25.22. +mole_1=1367338 +mole_2=.4..44. diff --git a/katomic/levels/level_23 b/katomic/levels/level_23 new file mode 100644 index 00000000..c44492ff --- /dev/null +++ b/katomic/levels/level_23 @@ -0,0 +1,24 @@ +[Level] +Name=Ammonia +atom_1=1-c +atom_2=4-bdg +atom_3=1-f +atom_4=1-h +feld_00=............... +feld_01=.#############. +feld_02=.##..3#..#..##. +feld_03=.#...#..#....#. +feld_04=.#........#..#. +feld_05=.#1#.#...#..##. +feld_06=.##...#....#.#. +feld_07=.#....2......#. +feld_08=.#.#....#..4##. +feld_09=.##..#...#.#.#. +feld_10=.#..#........#. +feld_11=.#....#..#...#. +feld_12=.##..#..#...##. +feld_13=.#############. +feld_14=............... +mole_0=..3 +mole_1=12. +mole_2=..4 diff --git a/katomic/levels/level_24 b/katomic/levels/level_24 new file mode 100644 index 00000000..0d47d71d --- /dev/null +++ b/katomic/levels/level_24 @@ -0,0 +1,29 @@ +[Level] +Name=3-Methyl-Pentane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-d +atom_6=2-abeh +atom_7=1-f +atom_8=1-g +feld_00=............... +feld_01=############### +feld_02=#.2.5..#..3..4# +feld_03=#2.#...#...#..# +feld_04=#.###.12..###.# +feld_05=#.3#....4..#..# +feld_06=#.......3.....# +feld_07=###.2..4....### +feld_08=#....3.......7# +feld_09=#8.#.......#.6# +feld_10=#.###....2###.# +feld_11=#..#4..#4..#..# +feld_12=#.....3#......# +feld_13=############### +feld_14=............... +mole_0=..527.. +mole_1=.22622. +mole_2=1333338 +mole_3=.44444. diff --git a/katomic/levels/level_25 b/katomic/levels/level_25 new file mode 100644 index 00000000..32b93dca --- /dev/null +++ b/katomic/levels/level_25 @@ -0,0 +1,27 @@ +[Level] +Name=Propanal +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-cgC +atom_6=3-A +atom_7=1-g +feld_00=............... +feld_01=..###########.. +feld_02=..#.2#.5....#.. +feld_03=..#.##4..##.#.. +feld_04=..#.....3.###.. +feld_05=..#..###....#.. +feld_06=..#..#....###.. +feld_07=..#....#....#.. +feld_08=..####..#...#.. +feld_09=..#72.....#.#.. +feld_10=..#..#.31.#.#.. +feld_11=..#6##..#.#4#.. +feld_12=..#.....#...#.. +feld_13=..###########.. +feld_14=............... +mole_0=.22.. +mole_1=13357 +mole_2=.446. diff --git a/katomic/levels/level_26 b/katomic/levels/level_26 new file mode 100644 index 00000000..1548b710 --- /dev/null +++ b/katomic/levels/level_26 @@ -0,0 +1,27 @@ +[Level] +Name=Propyne +atom_1=1-c +atom_2=2-gF +atom_3=1-e +atom_4=2-cH +atom_5=2-aceg +atom_6=1-a +atom_7=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#...#####...#. +feld_03=.#.#.........#. +feld_04=.#.#.2#...#..#. +feld_05=.####.###6#..#. +feld_06=.#......#.##.#. +feld_07=.#3#.......#.#. +feld_08=.####.####...#. +feld_09=.#.#....#....#. +feld_10=.#...#.....#.#. +feld_11=.#..###.1.##5#. +feld_12=.#.7#....##4.#. +feld_13=.#############. +feld_14=............... +mole_0=...3. +mole_1=12457 +mole_2=...6. diff --git a/katomic/levels/level_27 b/katomic/levels/level_27 new file mode 100644 index 00000000..06a349f7 --- /dev/null +++ b/katomic/levels/level_27 @@ -0,0 +1,33 @@ +[Level] +Name=Furanal +atom_1=1-d +atom_2=1-b +atom_3=2-chC +atom_4=2-dfA +atom_5=A- +atom_6=3-bh +atom_7=2-bgC +atom_8=1-f +atom_9=2-ehB +atom_a=1-a +atom_b=3-D +feld_00=............... +feld_01=...########.... +feld_02=...#..#...#.... +feld_03=.###..5...####. +feld_04=.#..8###..9..#. +feld_05=.#..a...3#...#. +feld_06=.####.#..#6..#. +feld_07=.#b...#.###..#. +feld_08=.#..........4#. +feld_09=.##.####....##. +feld_10=.#4.#.7...#..#. +feld_11=.#.....#..#..#. +feld_12=.####.1#.2#.##. +feld_13=....##########. +feld_14=............... +mole_0=1...8 +mole_1=.357. +mole_2=.4.4. +mole_3=2.6.9b +mole_4=....a diff --git a/katomic/levels/level_28 b/katomic/levels/level_28 new file mode 100644 index 00000000..a614ec0a --- /dev/null +++ b/katomic/levels/level_28 @@ -0,0 +1,32 @@ +[Level] +Name=Pyran +atom_1=3-bd +atom_2=1-e +atom_3=2-afB +atom_4=2-ehB +atom_5=1-a +atom_6=2-adD +atom_7=2-beD +atom_8=2-bdfh +atom_9=1-f +atom_a=1-h +feld_00=............... +feld_01=..#######...... +feld_02=..#.....#...... +feld_03=..#..6..######. +feld_04=..#.##..#9...#. +feld_05=..#.12.7####.#. +feld_06=..###...#....#. +feld_07=.##.##..#.4..#. +feld_08=.#.....#2.#..#. +feld_09=.#........#5.#. +feld_10=.#.3a#5.#.##.#. +feld_11=.#####..#....#. +feld_12=....#...#8####. +feld_13=....#######.... +feld_14=............... +mole_0=.22.. +mole_1=.36.9 +mole_2=1..8. +mole_3=.47.a +mole_4=.55.. diff --git a/katomic/levels/level_29 b/katomic/levels/level_29 new file mode 100644 index 00000000..e5417b46 --- /dev/null +++ b/katomic/levels/level_29 @@ -0,0 +1,31 @@ +[Level] +Name=Cyclo-Pentane +atom_1=1-d +atom_2=1-c +atom_3=2-begh +atom_4=2-aceg +atom_5=1-a +atom_6=2-bdfh +atom_7=2-bdeg +atom_8=1-f +atom_9=1-h +feld_00=............... +feld_01=..########..... +feld_02=..#5.....#####. +feld_03=..##..2#..9..#. +feld_04=..#..6#1###7.#. +feld_05=..#.......####. +feld_06=..#........#... +feld_07=..##2#.8#..#... +feld_08=...###.##..#... +feld_09=.###453....###. +feld_10=.#1..##..#9..#. +feld_11=.#....6..#####. +feld_12=.#.#....8#..... +feld_13=.#########..... +feld_14=............... +mole_0=.1.8. +mole_1=1.6.8 +mole_2=23.6. +mole_3=247.9 +mole_4=.559. diff --git a/katomic/levels/level_3 b/katomic/levels/level_3 new file mode 100644 index 00000000..0df76525 --- /dev/null +++ b/katomic/levels/level_3 @@ -0,0 +1,26 @@ +[Level] +Name=Methanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=............... +feld_02=..........####. +feld_03=.####.....#..#. +feld_04=.#..#.....#..#. +feld_05=.#..##...##.5#. +feld_06=.#..4#####...#. +feld_07=.#.#.#.3#....#. +feld_08=.#......#.6..#. +feld_09=.#..#.#...#..#. +feld_10=.#2..........#. +feld_11=.#...#.#...#.#. +feld_12=.######1...#.#. +feld_13=......########. +feld_14=............... +mole_0=.2.. +mole_1=1356 +mole_2=.4.. diff --git a/katomic/levels/level_30 b/katomic/levels/level_30 new file mode 100644 index 00000000..8b78c3c8 --- /dev/null +++ b/katomic/levels/level_30 @@ -0,0 +1,40 @@ +[Level] +Name=Nitro-Glycerin +atom_1=3-cd +atom_2=1-c +atom_3=4-beg +atom_4=3-ah +atom_5=1-e +atom_6=2-aceg +atom_7=3-af +atom_8=A- +atom_9=4-beh +atom_a=3-ae +atom_b=1-a +atom_c=3-fg +atom_d=3-ad +atom_e=1-g +atom_f=4-ceh +atom_g=3-ba +feld_00=............... +feld_01=...#########... +feld_02=...#.......#... +feld_03=...#.1#f..####. +feld_04=...#6#9#.c...#. +feld_05=...#1#...e..4#. +feld_06=.###.###...###. +feld_07=.#.....3..a.c#. +feld_08=.#.5#2.6.##..#. +feld_09=.#.###8#..####. +feld_10=.#g##..#b..d7#. +feld_11=.#6....##..###. +feld_12=.#######5..#... +feld_13=.......#####... +feld_14=............... +mole_0=..18c.. +mole_1=...9... +mole_2=..5a5.. +mole_3=.2666e. +mole_4=..7bd.. +mole_5=13...fc +mole_6=.4...g. diff --git a/katomic/levels/level_31 b/katomic/levels/level_31 new file mode 100644 index 00000000..c23f8a56 --- /dev/null +++ b/katomic/levels/level_31 @@ -0,0 +1,29 @@ +[Level] +Name=Ethane +atom_1=1-d +atom_2=1-b +atom_3=1-e +atom_4=2-abeh +atom_5=2-adef +atom_6=1-a +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=............... +feld_02=....#########.. +feld_03=....#....2..#.. +feld_04=....#..#.#..#.. +feld_05=....#3.5.####.. +feld_06=..#####4.#1#... +feld_07=..#...#....#... +feld_08=..#........#... +feld_09=..#.6.###8.##.. +feld_10=..###7...#..#.. +feld_11=..#.........#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=137 +mole_1=.4. +mole_2=.5. +mole_3=268 diff --git a/katomic/levels/level_32 b/katomic/levels/level_32 new file mode 100644 index 00000000..73d96b51 --- /dev/null +++ b/katomic/levels/level_32 @@ -0,0 +1,31 @@ +[Level] +Name=Crystal 2 +atom_1=o-bd +atom_2=o-bdf +atom_3=o-bdh +atom_4=o-df +atom_5=o-bdfh +atom_6=o-bh +atom_7=o-dfh +atom_8=o-bfh +atom_9=o-fh +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=......###...... +feld_04=.....##8##..... +feld_05=....##..2##.... +feld_06=...##7.5..##... +feld_07=...#...6..4#... +feld_08=...##3..9.##... +feld_09=....##..1##.... +feld_10=.....##.##..... +feld_11=......###...... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=..4.. +mole_1=.2.7. +mole_2=1.5.9 +mole_3=.3.8. +mole_4=..6.. diff --git a/katomic/levels/level_33 b/katomic/levels/level_33 new file mode 100644 index 00000000..929c5fea --- /dev/null +++ b/katomic/levels/level_33 @@ -0,0 +1,27 @@ +[Level] +Name=Ethylene-Glycol +atom_1=1-b +atom_2=1-d +atom_3=3-bf +atom_4=2-bdfh +atom_5=1-f +atom_6=1-h +feld_00=............... +feld_01=....##########. +feld_02=....#....3#..#. +feld_03=....#.#...#..#. +feld_04=....#..#.....#. +feld_05=.####...#...##. +feld_06=.#..4#..4#...#. +feld_07=.#...2#1..#.##. +feld_08=.#..#..#.5#.#.. +feld_09=.##1##..#.###.. +feld_10=..#....3...#... +feld_11=..#....#.6.#... +feld_12=..######.5.#... +feld_13=.......#####... +feld_14=............... +mole_0=.2.5.5 +mole_1=..4.3. +mole_2=.3.4.. +mole_3=1.1.6. diff --git a/katomic/levels/level_34 b/katomic/levels/level_34 new file mode 100644 index 00000000..5bee796c --- /dev/null +++ b/katomic/levels/level_34 @@ -0,0 +1,33 @@ +[Level] +Name=L-Alanine +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-cfgh +atom_5=1-e +atom_6=2-aceg +atom_7=4-adf +atom_8=3-C +atom_9=2-cgA +atom_a=1-h +atom_b=3-cg +atom_c=1-g +feld_00=............... +feld_01=.#############. +feld_02=.#.b.4...5...#. +feld_03=.#.####..##..#. +feld_04=.#..3......3.#. +feld_05=.##.....7..#.#. +feld_06=.#..#.#.#2.#.#. +feld_07=.#.1..#.#..#.#. +feld_08=.#c#......9..#. +feld_09=.#.#....8....#. +feld_10=.#....#..#a.##. +feld_11=.#..#.#..#...#. +feld_12=.#....#6.#...#. +feld_13=.#############. +feld_14=............... +mole_0=1.58.. +mole_1=2469bc +mole_2=3.7... +mole_3=.3.a.. diff --git a/katomic/levels/level_35 b/katomic/levels/level_35 new file mode 100644 index 00000000..948dcfa3 --- /dev/null +++ b/katomic/levels/level_35 @@ -0,0 +1,31 @@ +[Level] +Name=Cyanoguanidine +atom_1=1-d +atom_2=1-b +atom_3=4-cfh +atom_4=2-cgC +atom_5=4-eA +atom_6=1-a +atom_7=1-e +atom_8=4-acg +atom_9=2-gG +atom_a=4-E +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=..###########.. +feld_04=..#.#.......#.. +feld_05=..#9###53##.#.. +feld_06=..#..2.#..a.#.. +feld_07=..####....###.. +feld_08=..#.....8...#.. +feld_09=..#7..#6###1#.. +feld_10=..#......4..#.. +feld_11=..###########.. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=1..7. +mole_1=.3489 +mole_2=2.5.a +mole_3=..6.. diff --git a/katomic/levels/level_36 b/katomic/levels/level_36 new file mode 100644 index 00000000..b455234e --- /dev/null +++ b/katomic/levels/level_36 @@ -0,0 +1,23 @@ +[Level] +Name=Prussic Acid (Cyanic Acid) +atom_1=1-e +atom_2=2-aG +atom_3=4-E +feld_00=............... +feld_01=............... +feld_02=...####........ +feld_03=...#..####..... +feld_04=...##....####.. +feld_05=...#.3.#.#..#.. +feld_06=...###.#....#.. +feld_07=...#...1...##.. +feld_08=...##2......##. +feld_09=.###.....#...#. +feld_10=.#.###...#####. +feld_11=.#.........#... +feld_12=.###########... +feld_13=............... +feld_14=............... +mole_0=1 +mole_1=2 +mole_2=3 \ No newline at end of file diff --git a/katomic/levels/level_37 b/katomic/levels/level_37 new file mode 100644 index 00000000..4571d0ba --- /dev/null +++ b/katomic/levels/level_37 @@ -0,0 +1,41 @@ +[Level] +Name=Anthracene +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-ehB +atom_5=2-agC +atom_6=2-cfA +atom_7=2-beD +atom_8=2-acC +atom_9=2-egA +atom_a=2-faB +atom_b=1-f +atom_c=2-bgC +atom_d=2-ceA +atom_e=2-aeD +atom_f=2-adD +atom_g=1-g +atom_h=1-h +feld_00=############### +feld_01=#..4##.....#..# +feld_02=#..h##...7###.# +feld_03=#..d......1...# +feld_04=#.......b....## +feld_05=####..c.###d.## +feld_06=#....ce.g...3.# +feld_07=#..........##f# +feld_08=##....#....a#.# +feld_09=#9.#2.#b5.....# +feld_10=#.###.####....# +feld_11=#..#8.b..3....# +feld_12=#.....#....#..# +feld_13=#.a..#.....#63# +feld_14=############### +mole_0=1..b.. +mole_1=.47.b. +mole_2=258c.b +mole_3=.69dc. +mole_4=3.aedg +mole_5=.3.af. +mole_6=..3..h diff --git a/katomic/levels/level_38 b/katomic/levels/level_38 new file mode 100644 index 00000000..ffe619b3 --- /dev/null +++ b/katomic/levels/level_38 @@ -0,0 +1,30 @@ +[Level] +Name=Thiazole +atom_1=1-d +atom_2=2-ehB +atom_3=C- +atom_4=4-aB +atom_5=2-bdD +atom_6=1-f +atom_7=5-fh +atom_8=1-h +feld_00=............... +feld_01=..###########.. +feld_02=.##.........##. +feld_03=.#..#..###.1.#. +feld_04=.#.##..#8..#.#. +feld_05=.#3#...#..##.#. +feld_06=.#.........5.#. +feld_07=.#...###.5####. +feld_08=.#.#2#7..#4..#. +feld_09=.#6#.#.......#. +feld_10=.#.#.....#...#. +feld_11=.#......##...#. +feld_12=.##.....#...##. +feld_13=..###########.. +feld_14=............... +mole_0=1..6 +mole_1=.25. +mole_2=.3.7 +mole_3=.45. +mole_4=...8 diff --git a/katomic/levels/level_39 b/katomic/levels/level_39 new file mode 100644 index 00000000..cabcb547 --- /dev/null +++ b/katomic/levels/level_39 @@ -0,0 +1,39 @@ +[Level] +Name=Saccharin +atom_1=1-c +atom_2=4-bdg +atom_3=3-C +atom_4=2-dfA +atom_5=5-chCD +atom_6=2-ehB +atom_7=2-agB +atom_8=1-e +atom_9=2-acD +atom_a=2-ceD +atom_b=1-a +atom_c=2-bgC +atom_d=2-dgA +atom_e=1-f +atom_f=1-h +atom_g=3-A +atom_h=3-B +feld_00=............... +feld_01=.......#####... +feld_02=..######d#h###. +feld_03=..#4..3#.#.9e#. +feld_04=..#......b...#. +feld_05=..#c#5.......#. +feld_06=.##....#.#...#. +feld_07=.#.....#8#1..#. +feld_08=.#..f###.###.#. +feld_09=.#.....2.....#. +feld_10=.####...#g.###. +feld_11=....#a...7.#... +feld_12=....###..6.#... +feld_13=......######... +feld_14=............... +mole_0=..3.... +mole_1=..4.8.e +mole_2=12.69c. +mole_3=.h57ad. +mole_4=..g.b.f diff --git a/katomic/levels/level_4 b/katomic/levels/level_4 new file mode 100644 index 00000000..20558070 --- /dev/null +++ b/katomic/levels/level_4 @@ -0,0 +1,26 @@ +[Level] +Name=Ethylene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-f +atom_6=1-h +feld_00=............... +feld_01=............... +feld_02=..###.....###.. +feld_03=..#.#.....#.#.. +feld_04=..#2#######.#.. +feld_05=..#......#..#.. +feld_06=..##..4.#...#.. +feld_07=..#.#....5#3#.. +feld_08=..#...#....##.. +feld_09=..#..#....61#.. +feld_10=..#.#######.#.. +feld_11=..#.#.....#.#.. +feld_12=..###.....###.. +feld_13=............... +feld_14=............... +mole_0=1..5 +mole_1=.34. +mole_2=2..6 diff --git a/katomic/levels/level_40 b/katomic/levels/level_40 new file mode 100644 index 00000000..7a9bef2f --- /dev/null +++ b/katomic/levels/level_40 @@ -0,0 +1,37 @@ +[Level] +Name=Styrene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-c +atom_6=1-f +atom_7=2-chC +atom_8=2-egA +atom_9=2-afB +atom_a=2-bgC +atom_b=2-ceA +atom_c=2-adD +atom_d=1-g +atom_e=1-h +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#.......#.#.. +feld_04=..#..b###.8.#.. +feld_05=..#...16#.#.#.. +feld_06=..#...2e.6#5#.. +feld_07=..#..c.3.##.#.. +feld_08=..#.##2##.7##.. +feld_09=..#..#4#....#.. +feld_10=..##9#.#.d..#.. +feld_11=..#......#.a#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=1..6.. +mole_1=.34..6 +mole_2=2..7a. +mole_3=..58bd +mole_4=...9c. +mole_5=..2..e diff --git a/katomic/levels/level_41 b/katomic/levels/level_41 new file mode 100644 index 00000000..733a333d --- /dev/null +++ b/katomic/levels/level_41 @@ -0,0 +1,34 @@ +[Level] +Name=Melamine +atom_1=1-c +atom_2=1-e +atom_3=4-acg +atom_4=2-cgC +atom_5=4-cA +atom_6=1-b +atom_7=4-gec +atom_8=2-aceg +atom_9=4-adf +atom_a=4-gA +atom_b=1-h +atom_c=1-g +feld_00=............... +feld_01=.####.....####. +feld_02=.#..#######3.#. +feld_03=.#9....#....6#. +feld_04=.##.#.4#..#.##. +feld_05=..#.........#.. +feld_06=..#....2..2.#.. +feld_07=..###.13c.###.. +feld_08=..#7........#.. +feld_09=..#....4...a#.. +feld_10=.##.#..#5.#.##. +feld_11=.#.....#8....#. +feld_12=.#b.#######..#. +feld_13=.####.....####. +feld_14=............... +mole_0=.2...2. +mole_1=134743c +mole_2=..58a.. +mole_3=...9... +mole_4=..6.b.. diff --git a/katomic/levels/level_42 b/katomic/levels/level_42 new file mode 100644 index 00000000..50cc2ce5 --- /dev/null +++ b/katomic/levels/level_42 @@ -0,0 +1,26 @@ +[Level] +Name=Cyclobutane +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +feld_00=............... +feld_01=............... +feld_02=..########..... +feld_03=..#...1..##.... +feld_04=..##5.##.4###.. +feld_05=...##.3.....#.. +feld_06=....#.#.....#.. +feld_07=...##1##..#3#.. +feld_08=...#...5#2###.. +feld_09=...#......#.#.. +feld_10=...#3...3..2#.. +feld_11=...#4.#.###.#.. +feld_12=...######.###.. +feld_13=............... +feld_14=............... +mole_0=.22. +mole_1=1335 +mole_2=1335 +mole_3=.44. diff --git a/katomic/levels/level_43 b/katomic/levels/level_43 new file mode 100644 index 00000000..e2aabf80 --- /dev/null +++ b/katomic/levels/level_43 @@ -0,0 +1,42 @@ +[Level] +Name=Nicotine +atom_1=1-d +atom_2=1-c +atom_3=2-ehB +atom_4=2-agC +atom_5=4-cA +atom_6=1-e +atom_7=2-aeD +atom_8=2-acC +atom_9=2-egA +atom_a=1-a +atom_b=1-b +atom_c=2-abeg +atom_d=4-ace +atom_e=2-adef +atom_f=2-abdf +atom_g=2-bdeg +atom_h=1-h +atom_i=1-f +atom_j=2-bdfh +feld_00=............... +feld_01=...###########. +feld_02=..##.h..#a..i#. +feld_03=..#3...6#...##. +feld_04=.##..#..6...#.. +feld_05=.#...2..j#d.##. +feld_06=.#.a#....a#.e#. +feld_07=.#.#g..c...#.#. +feld_08=.##h..8.9..h##. +feld_09=.#......1....#. +feld_10=.##7f#5.#.6.##. +feld_11=..#..b#.#4###.. +feld_12=..##..i####.... +feld_13=...#####....... +feld_14=............... +mole_0=1.6.6i. +mole_1=.376f.i +mole_2=248c.j. +mole_3=.59dg.h +mole_4=..aeah. +mole_5=..bah.. diff --git a/katomic/levels/level_44 b/katomic/levels/level_44 new file mode 100644 index 00000000..3d432f09 --- /dev/null +++ b/katomic/levels/level_44 @@ -0,0 +1,43 @@ +[Level] +Name=Acetyle salicylic acid +atom_1=1-c +atom_2=3-B +atom_3=1-b +atom_4=1-e +atom_5=2-aceg +atom_6=2-aeD +atom_7=3-ae +atom_8=2-acC +atom_9=2-egA +atom_a=2-afB +atom_b=1-g +atom_c=2-bgC +atom_d=2-ceA +atom_e=2-adD +atom_f=2-cfA +atom_g=1-h +atom_h=3-cg +atom_i=3-C +feld_00=............... +feld_01=.........#####. +feld_02=.#########9..#. +feld_03=.#.#..f..#..##. +feld_04=.#..#2#.5.#1.#. +feld_05=.#7...8..b...#. +feld_06=.###6#.#.#bh.#. +feld_07=...#.e#.1.#d.#. +feld_08=...##c..g.b###. +feld_09=...#.#.#.4i#... +feld_10=..##...3#..#... +feld_11=..#..a#..###... +feld_12=..########..... +feld_13=............... +feld_14=............... +mole_0=.4.... +mole_1=15b... +mole_2=26.i.. +mole_3=.7.fhb +mole_4=.8c... +mole_5=19db.. +mole_6=.ae... +mole_7=3..g.. diff --git a/katomic/levels/level_45 b/katomic/levels/level_45 new file mode 100644 index 00000000..376fc944 --- /dev/null +++ b/katomic/levels/level_45 @@ -0,0 +1,36 @@ +[Level] +Name=Meta-Di-Nitro-Benzene +atom_1=3-B +atom_2=1-d +atom_3=4-bD +atom_4=3-A +atom_5=2-ehB +atom_6=2-afB +atom_7=1-e +atom_8=2-acD +atom_9=2-ceD +atom_a=1-a +atom_b=2-bgC +atom_c=2-dgA +atom_d=1-f +atom_e=4-hB +atom_f=3-D +feld_00=............... +feld_01=...####........ +feld_02=...#..####..... +feld_03=...#e2..f#..... +feld_04=...#.#..8#..... +feld_05=..##b...##..... +feld_06=..#.#....####.. +feld_07=..#....c....#.. +feld_08=..#7..##.6d.##. +feld_09=..#5#..9.#...#. +feld_10=..#.#1...#...#. +feld_11=..#....3.a.#.#. +feld_12=..####.#.#####. +feld_13=.....#####..... +feld_14=............... +mole_0=.2.7.d. +mole_1=..58b.. +mole_2=..69c.. +mole_3=13.a.ef diff --git a/katomic/levels/level_46 b/katomic/levels/level_46 new file mode 100644 index 00000000..9f2317f4 --- /dev/null +++ b/katomic/levels/level_46 @@ -0,0 +1,29 @@ +[Level] +Name=Propyne +atom_1=1-d +atom_2=1-e +atom_3=2-abeh +atom_4=2-aG +atom_5=2-eE +atom_6=1-a +atom_7=1-f +feld_00=............... +feld_01=...##########.. +feld_02=...#........#.. +feld_03=...#.1#.....#.. +feld_04=..##.##.6#.##.. +feld_05=..#.....##7#... +feld_06=..##.....####.. +feld_07=..#4.#......#.. +feld_08=..###5.#.3#.#.. +feld_09=....#..####.#.. +feld_10=....#..#..#.#.. +feld_11=....#..#..#.#.. +feld_12=....####..#2#.. +feld_13=..........###.. +feld_14=............... +mole_0=127 +mole_1=.3. +mole_2=.4. +mole_3=.5. +mole_4=.6. diff --git a/katomic/levels/level_47 b/katomic/levels/level_47 new file mode 100644 index 00000000..d410afa5 --- /dev/null +++ b/katomic/levels/level_47 @@ -0,0 +1,31 @@ +[Level] +Name=Malonic Acid +atom_1=1-c +atom_2=3-dg +atom_3=2-chC +atom_4=3-C +atom_5=1-e +atom_6=2-aceg +atom_7=1-a +atom_8=2-dgA +atom_9=3-ch +atom_a=1-g +atom_b=3-A +feld_00=............... +feld_01=............... +feld_02=..#########.... +feld_03=..#.#2....#.... +feld_04=..#.#...6.#.... +feld_05=..#..4.##5#.... +feld_06=..#..b..#7#.... +feld_07=..#..#.1a.###.. +feld_08=..#..##.....#.. +feld_09=..#.39.8....#.. +feld_10=..#.........#.. +feld_11=..###..#....#.. +feld_12=....#########.. +feld_13=............... +feld_14=............... +mole_0=12.54.. +mole_1=..368.. +mole_2=..b7.9a diff --git a/katomic/levels/level_48 b/katomic/levels/level_48 new file mode 100644 index 00000000..a78c8fc0 --- /dev/null +++ b/katomic/levels/level_48 @@ -0,0 +1,35 @@ +[Level] +Name=2,2-Dimethylpropane +atom_1=1-d +atom_2=1-c +atom_3=1-b +atom_4=2-cfgh +atom_5=1-e +atom_6=2-abeh +atom_7=2-aceg +atom_8=2-adfe +atom_9=1-a +atom_a=1-f +atom_b=2-bcdg +atom_c=1-h +atom_d=1-g +feld_00=............... +feld_01=......######... +feld_02=.####.#..1a#... +feld_03=.#5.#.##.3c#... +feld_04=.#9.###6#..#... +feld_05=.##...47b..###. +feld_06=.#.#..#8.....#. +feld_07=.#...#..#..#.#. +feld_08=.#.#...#.....#. +feld_09=.#..#....#.1a#. +feld_10=.#.2d......3c#. +feld_11=.#....#..#####. +feld_12=.##....#.#..... +feld_13=..########..... +feld_14=............... +mole_0=.15a. +mole_1=1.6.a +mole_2=247bd +mole_3=3.8.c +mole_4=.39c. diff --git a/katomic/levels/level_49 b/katomic/levels/level_49 new file mode 100644 index 00000000..446cba87 --- /dev/null +++ b/katomic/levels/level_49 @@ -0,0 +1,36 @@ +[Level] +Name=Ethyl-Benzene +atom_1=1-d +atom_2=1-b +atom_3=2-ehB +atom_4=2-afB +atom_5=1-e +atom_6=2-abeh +atom_7=2-acD +atom_8=2-ceD +atom_9=1-a +atom_a=1-f +atom_b=2-bgC +atom_c=2-dgA +atom_d=1-h +feld_00=............... +feld_01=..#####........ +feld_02=..#15a#######.. +feld_03=..#.6......8#.. +feld_04=..##9.c#...##.. +feld_05=..#.#.#7...3#.. +feld_06=..#........4#.. +feld_07=..#..1##...##.. +feld_08=..#..2.#.a..#.. +feld_09=..###..b#d..#.. +feld_10=....#...##..#.. +feld_11=....#.#.....#.. +feld_12=....#.#.16a.#.. +feld_13=....#########.. +feld_14=............... +mole_0=.15a. +mole_1=.16a. +mole_2=1.6.a +mole_3=.37b. +mole_4=.48c. +mole_5=2.9.d diff --git a/katomic/levels/level_5 b/katomic/levels/level_5 new file mode 100644 index 00000000..30adf5cc --- /dev/null +++ b/katomic/levels/level_5 @@ -0,0 +1,28 @@ +[Level] +Name=Propene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=............... +feld_02=..#########.... +feld_03=..#7#.#.#.#.... +feld_04=..#.#.#.#.###.. +feld_05=.##4.6#2#.#.#.. +feld_06=.#2.1...#5#.#.. +feld_07=.#.#......#.#.. +feld_08=.#.#3#......#.. +feld_09=.#.#.#.#...##.. +feld_10=.###.#8#.#.#... +feld_11=...#.#.#.#.#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=.22.7 +mole_1=1356. +mole_2=.4..8 diff --git a/katomic/levels/level_50 b/katomic/levels/level_50 new file mode 100644 index 00000000..3c79af00 --- /dev/null +++ b/katomic/levels/level_50 @@ -0,0 +1,38 @@ +[Level] +Name=L-Asparagine +atom_1=3-B +atom_2=1-d +atom_3=1-b +atom_4=1-e +atom_5=3-ae +atom_6=2-acD +atom_7=1-c +atom_8=4-cfh +atom_9=2-aceg +atom_a=2-agC +atom_b=4-bdg +atom_c=1-g +atom_d=1-f +atom_e=1-h +atom_f=3-A +feld_00=............... +feld_01=............... +feld_02=...#########... +feld_03=...#44#.1f5#... +feld_04=..##c.#..####.. +feld_05=..#7......#b#.. +feld_06=..#2........#.. +feld_07=..#3........#.. +feld_08=..#d#.#.....#.. +feld_09=..#e..##..#8#.. +feld_10=..##..#...###.. +feld_11=...#...699a#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=.4... +mole_1=.54.d +mole_2=169b. +mole_3=279ce +mole_4=.8a.. +mole_5=3.f.. diff --git a/katomic/levels/level_51 b/katomic/levels/level_51 new file mode 100644 index 00000000..944cbd64 --- /dev/null +++ b/katomic/levels/level_51 @@ -0,0 +1,31 @@ +[Level] +Name=1,3,5,7-Cyclooctatetraene +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-dfA +atom_5=2-fhB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +feld_00=............... +feld_01=.########..###. +feld_02=.#1....1####5#. +feld_03=.#..#5.#4....#. +feld_04=.#..##.##...4#. +feld_05=.#..........##. +feld_06=.#.#3..#3.#7#.. +feld_07=.#.##..##.###.. +feld_08=.#7.........##. +feld_09=.##.#8.#8...6#. +feld_10=..#.##.##6#2.#. +feld_11=..#.......##.#. +feld_12=..#2.###.....#. +feld_13=..####.#######. +feld_14=............... +mole_0=.1..7. +mole_1=1.56.7 +mole_2=.3..3. +mole_3=.4..4. +mole_4=2.56.8 +mole_5=.2..8. diff --git a/katomic/levels/level_52 b/katomic/levels/level_52 new file mode 100644 index 00000000..f0480c84 --- /dev/null +++ b/katomic/levels/level_52 @@ -0,0 +1,41 @@ +[Level] +Name=Vanillin +atom_1=1-c +atom_2=3-C +atom_3=2-dgA +atom_4=1-b +atom_5=2-ehB +atom_6=2-afB +atom_7=1-e +atom_8=2-acD +atom_9=2-ceD +atom_a=1-a +atom_b=2-bgC +atom_c=1-d +atom_d=3-bf +atom_e=3-dh +atom_f=2-bdfh +atom_g=1-h +atom_h=1-f +feld_00=............... +feld_01=........######. +feld_02=.#########.c.#. +feld_03=.#d.....f##h.#. +feld_04=.#.....b.4..g#. +feld_05=.#.#.........#. +feld_06=.##...#..87.##. +feld_07=.#5...3#2#1.6#. +feld_08=.##.#...#9..3#. +feld_09=.#..#..#e#..#.. +feld_10=.#.a#.#g..#.#.. +feld_11=.####...#...#.. +feld_12=....#...#...#.. +feld_13=....#########.. +feld_14=............... +mole_0=.....c.h +mole_1=.2....f. +mole_2=13.7.d.g +mole_3=..58b... +mole_4=..693... +mole_5=.4.a.e.. +mole_6=......g. diff --git a/katomic/levels/level_53 b/katomic/levels/level_53 new file mode 100644 index 00000000..85da6ee2 --- /dev/null +++ b/katomic/levels/level_53 @@ -0,0 +1,30 @@ +[Level] +Name=Crystal 3 +atom_1=o-bc +atom_2=o-bcf +atom_3=o-bcg +atom_4=o-bcfg +atom_5=o-cf +atom_6=o-bg +atom_7=o-cfg +atom_8=o-bfg +atom_9=o-fg +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=............... +feld_04=...#########... +feld_05=...#8.1.4..#... +feld_06=...#.6.4.4.#... +feld_07=...#8.3.4.9#... +feld_08=...#.3.2.7.#... +feld_09=...#..2.7.5#... +feld_10=...#########... +feld_11=............... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=...5779 +mole_1=..2448. +mole_2=.2448.. +mole_3=1336... diff --git a/katomic/levels/level_54 b/katomic/levels/level_54 new file mode 100644 index 00000000..07d710b0 --- /dev/null +++ b/katomic/levels/level_54 @@ -0,0 +1,39 @@ +[Level] +Name=Uric Acid +atom_1=1-d +atom_2=3-B +atom_3=1-b +atom_4=4-ceh +atom_5=2-aeD +atom_6=4-acf +atom_7=2-egA +atom_8=2-acC +atom_9=2-dgA +atom_a=4-bdg +atom_b=4-beh +atom_c=1-a +atom_d=1-f +atom_e=2-fhB +atom_f=3-D +atom_g=3-C +feld_00=............... +feld_01=.###########... +feld_02=.#.#.#.#...#... +feld_03=.#...#.#...#... +feld_04=.#.47.8#..g#... +feld_05=.###...#.#####. +feld_06=.#.5....b#9..#. +feld_07=.#.....cf....#. +feld_08=.#..2#.d....6#. +feld_09=.#####1#...###. +feld_10=...#...#.....#. +feld_11=...#...#.#...#. +feld_12=...#..3#e#a#.#. +feld_13=...###########. +feld_14=............... +mole_0=1.g.. +mole_1=.47.d +mole_2=258a. +mole_3=.69.ef +mole_4=3..b. +mole_5=...c. diff --git a/katomic/levels/level_55 b/katomic/levels/level_55 new file mode 100644 index 00000000..36fb3d64 --- /dev/null +++ b/katomic/levels/level_55 @@ -0,0 +1,36 @@ +[Level] +Name=Thymine +atom_1=1-c +atom_2=4-bdg +atom_3=1-b +atom_4=3-C +atom_5=2-cfA +atom_6=2-fhB +atom_7=2-bdD +atom_8=1-f +atom_9=2-cdeh +atom_a=1-a +atom_b=1-g +atom_c=1-h +atom_d=3-D +feld_00=............... +feld_01=.####....#####. +feld_02=.#31#....#.7.#. +feld_03=.#..######.#.#. +feld_04=.#..##28##.#.#. +feld_05=.#..........b#. +feld_06=.#.##....##a.#. +feld_07=.#..6.##..5#.#. +feld_08=.#4##....##..#. +feld_09=.#....##...#.#. +feld_10=.#.#.c.....#.#. +feld_11=.#.6##..##...#. +feld_12=.#....d2....9#. +feld_13=.#############. +feld_14=............... +mole_0=..4.8. +mole_1=..52.. +mole_2=12..6d +mole_3=..67.. +mole_4=.3..9b +mole_5=....ac diff --git a/katomic/levels/level_56 b/katomic/levels/level_56 new file mode 100644 index 00000000..6badd2f6 --- /dev/null +++ b/katomic/levels/level_56 @@ -0,0 +1,37 @@ +[Level] +Name=Aniline +atom_1=1-c +atom_2=1-d +atom_3=4-beg +atom_4=1-a +atom_5=2-ehB +atom_6=2-agC +atom_7=2-cfA +atom_8=1-e +atom_9=2-adD +atom_a=A- +atom_b=2-chC +atom_c=2-dgA +atom_d=1-g +atom_e=1-h +feld_00=............... +feld_01=.......#######. +feld_02=.#######c#...#. +feld_03=.#..34#......#. +feld_04=.#.......##7##. +feld_05=.#...##d1a###.. +feld_06=.##6..##....#.. +feld_07=.###...e.#..#.. +feld_08=.#1.........#.. +feld_09=.#..##...##.#.. +feld_10=.#..b##...####. +feld_11=.#..8......5.#. +feld_12=.#######9#..2#. +feld_13=.......#######. +feld_14=............... +mole_0=.2.8.. +mole_1=..59.. +mole_2=.16.bd +mole_3=..7ac. +mole_4=13...e +mole_5=.4.... diff --git a/katomic/levels/level_57 b/katomic/levels/level_57 new file mode 100644 index 00000000..6be47483 --- /dev/null +++ b/katomic/levels/level_57 @@ -0,0 +1,25 @@ +[Level] +Name=Chloroform +atom_1=7-d +atom_2=7-b +atom_3=2-bdfh +atom_4=1-f +atom_5=7-h +feld_00=............... +feld_01=......#######.. +feld_02=......#...#.#.. +feld_03=..#######.#.#.. +feld_04=..#...##....#.. +feld_05=..###1#.....#.. +feld_06=..#......4#.#.. +feld_07=..#..#...#3.#.. +feld_08=..#.2########.. +feld_09=..#..5.#....... +feld_10=..####.#....... +feld_11=..#....#....... +feld_12=..######....... +feld_13=............... +feld_14=............... +mole_0=1.4 +mole_1=.3. +mole_2=2.5 diff --git a/katomic/levels/level_58 b/katomic/levels/level_58 new file mode 100644 index 00000000..742d6eb0 --- /dev/null +++ b/katomic/levels/level_58 @@ -0,0 +1,28 @@ +[Level] +Name=Carbonic acid +atom_1=1-e +atom_2=3-ad +atom_3=3-be +atom_4=1-a +atom_5=2-fhB +atom_6=3-D +feld_00=............... +feld_01=............... +feld_02=..##########... +feld_03=..#...#.1..##.. +feld_04=..#...#.#...#.. +feld_05=..#2.##.##..#.. +feld_06=..##.6......#.. +feld_07=..##........#.. +feld_08=..#...##.##.#.. +feld_09=..#....#5#..#.. +feld_10=..###...3#..#.. +feld_11=....#....4..#.. +feld_12=....#########.. +feld_13=............... +feld_14=............... +mole_0=1. +mole_1=2. +mole_2=.56 +mole_3=3. +mole_4=4. diff --git a/katomic/levels/level_59 b/katomic/levels/level_59 new file mode 100644 index 00000000..1684a006 --- /dev/null +++ b/katomic/levels/level_59 @@ -0,0 +1,39 @@ +[Level] +Name=Crystal 4 +atom_1=o-be +atom_2=o-ad +atom_3=o-bdf +atom_4=o-bdh +atom_5=o-cf +atom_6=o-ceh +atom_7=o-acf +atom_8=o-ch +atom_9=o-dg +atom_a=o-beg +atom_b=o-adg +atom_c=o-bg +atom_d=o-dfh +atom_e=o-bfh +atom_f=o-eh +atom_g=o-af +feld_00=............... +feld_01=............... +feld_02=..##.......##.. +feld_03=..###########.. +feld_04=...#.56..d.#... +feld_05=...#...ba..#... +feld_06=...#97...f.#... +feld_07=...#.......#... +feld_08=...#2...e.3#... +feld_09=...#..84...#... +feld_10=...#c...g.1#... +feld_11=..###########.. +feld_12=..##.......##.. +feld_13=............... +feld_14=............... +mole_0=..59.. +mole_1=.3..d. +mole_2=1.6a.f +mole_3=2.7b.g +mole_4=.4..e. +mole_5=..8c.. diff --git a/katomic/levels/level_6 b/katomic/levels/level_6 new file mode 100644 index 00000000..f8de8bf9 --- /dev/null +++ b/katomic/levels/level_6 @@ -0,0 +1,26 @@ +[Level] +Name=Ethanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +feld_00=............... +feld_01=.......#####... +feld_02=.......#43.#... +feld_03=...#####.#.#... +feld_04=...#......4#... +feld_05=...###2...##... +feld_06=.....#.#1#.###. +feld_07=.....##2...#.#. +feld_08=..####.3.....#. +feld_09=..#.5....#...#. +feld_10=..#####.#..#.#. +feld_11=.....#....#.6#. +feld_12=.....#########. +feld_13=............... +feld_14=............... +mole_0=.22.. +mole_1=13356 +mole_2=.44.. diff --git a/katomic/levels/level_60 b/katomic/levels/level_60 new file mode 100644 index 00000000..479d51d2 --- /dev/null +++ b/katomic/levels/level_60 @@ -0,0 +1,28 @@ +[Level] +Name=Acrylo-Nitril +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-dfA +atom_5=2-fF +atom_6=1-h +atom_7=4-H +feld_00=............... +feld_01=............... +feld_02=..#####.#####.. +feld_03=..#..1#.#5..#.. +feld_04=..#..#####7.#.. +feld_05=..#..#.3.#..#.. +feld_06=..#..#...#..#.. +feld_07=..#.........#.. +feld_08=..##..###..##.. +feld_09=...#...4...#... +feld_10=...#.2.#.6.#... +feld_11=...#.#.#.#.#... +feld_12=...#########... +feld_13=............... +feld_14=............... +mole_0=1.57 +mole_1=.3. +mole_2=.4. +mole_3=2.6 diff --git a/katomic/levels/level_61 b/katomic/levels/level_61 new file mode 100644 index 00000000..03a30071 --- /dev/null +++ b/katomic/levels/level_61 @@ -0,0 +1,30 @@ +[Level] +Name=Furan +atom_1=1-d +atom_2=1-b +atom_3=2-bhC +atom_4=2-cfA +atom_5=3-df +atom_6=A- +atom_7=2-dgA +atom_8=1-f +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=.#############. +feld_04=.#1..#...#..8#. +feld_05=.#.#3.4#7.3#.#. +feld_06=.#..#.#.#.#..#. +feld_07=.#...#.5.#...#. +feld_08=.##.........##. +feld_09=.#...##.##...#. +feld_10=.#2.#..6..#.9#. +feld_11=.#############. +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=1.5.8 +mole_1=.3.3. +mole_2=.467. +mole_3=2...9 diff --git a/katomic/levels/level_62 b/katomic/levels/level_62 new file mode 100644 index 00000000..cd17f418 --- /dev/null +++ b/katomic/levels/level_62 @@ -0,0 +1,31 @@ +[Level] +Name=l-Lactic acid +atom_1=1-d +atom_2=1-b +atom_3=3-dh +atom_4=2-bdfh +atom_5=1-h +atom_6=3-C +atom_7=2-cfA +atom_8=3-cg +atom_9=1-g +feld_00=############### +feld_01=############### +feld_02=##1.#.......4## +feld_03=##..#3..#....## +feld_04=##.....#...#### +feld_05=##.....#...4.## +feld_06=##.#..7#6....## +feld_07=##..#######..## +feld_08=##....9#8..#.## +feld_09=##.1...#.....## +feld_10=####...#.....## +feld_11=##....#..5#..## +feld_12=##2.......#.5## +feld_13=############### +feld_14=############### +mole_0=1..6.. +mole_1=.3.789 +mole_2=1.4... +mole_3=.4.5.. +mole_4=2.5... diff --git a/katomic/levels/level_63 b/katomic/levels/level_63 new file mode 100644 index 00000000..8e4835bf --- /dev/null +++ b/katomic/levels/level_63 @@ -0,0 +1,34 @@ +[Level] +Name=Maleic Acid +atom_1=3-B +atom_2=3-be +atom_3=2-adD +atom_4=2-beD +atom_5=3-ad +atom_6=1-f +atom_7=2-bhC +atom_8=2-dfA +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=.#############. +feld_03=.#6...8#5...7#. +feld_04=.#.#..###..#.#. +feld_05=.##6...#...9##. +feld_06=..#..#1#3#..#.. +feld_07=..##..###..##.. +feld_08=..#....#....#.. +feld_09=.##1...#...2##. +feld_10=.#.#.......#.#. +feld_11=.#4.........9#. +feld_12=.#############. +feld_13=............... +feld_14=............... +mole_0=..6. +mole_1=.2.. +mole_2=13.6 +mole_3=..7. +mole_4=..8. +mole_5=14.9 +mole_6=.5.. +mole_7=..9. diff --git a/katomic/levels/level_64 b/katomic/levels/level_64 new file mode 100644 index 00000000..a2b06ed8 --- /dev/null +++ b/katomic/levels/level_64 @@ -0,0 +1,34 @@ +[Level] +Name=meso-Tartaric acid +atom_1=1-c +atom_2=3-dg +atom_3=2-chC +atom_4=3-C +atom_5=1-e +atom_6=2-aceg +atom_7=3-ae +atom_8=1-a +atom_9=2-dgA +atom_a=3-ch +atom_b=1-g +atom_c=3-A +feld_00=........###.... +feld_01=..#######2####. +feld_02=..#.3....5...#. +feld_03=.##.a#.....#.#. +feld_04=.#..#4#...#7.#. +feld_05=.#.#...#.#.5.#. +feld_06=.#...........#. +feld_07=.#...#.....#.#. +feld_08=.#87#6#...#..#. +feld_09=.#.#..6#.#bc.#. +feld_10=.#....#...#8.#. +feld_11=.###.......#.#. +feld_12=...#..9......#. +feld_13=...####1######. +feld_14=......###...... +mole_0=....5... +mole_1=12.574.. +mole_2=..3669.. +mole_3=..c78.ab +mole_4=...8.... diff --git a/katomic/levels/level_65 b/katomic/levels/level_65 new file mode 100644 index 00000000..8af4e9ae --- /dev/null +++ b/katomic/levels/level_65 @@ -0,0 +1,39 @@ +[Level] +Name=Crystal 5 +atom_1=o-cd +atom_2=o-bc +atom_3=o-de +atom_4=o-adg +atom_5=o-cfh +atom_6=o-beg +atom_7=o-ab +atom_8=o-beh +atom_9=o-adf +atom_a=o-ef +atom_b=o-acf +atom_c=o-bdg +atom_d=o-ceh +atom_e=o-ah +atom_f=o-fg +atom_g=o-gh +atom_h=o-abcdefgh +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=...#########... +feld_04=...#########... +feld_05=...##..3.a##... +feld_06=...##19c6b##... +feld_07=...##d.7.f##... +feld_08=...##ghe58##... +feld_09=...##2...4##... +feld_10=...#########... +feld_11=...#########... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=.3.a. +mole_1=148bf +mole_2=.5hc. +mole_3=269dg +mole_4=.7.e. diff --git a/katomic/levels/level_66 b/katomic/levels/level_66 new file mode 100644 index 00000000..8d4e0fa5 --- /dev/null +++ b/katomic/levels/level_66 @@ -0,0 +1,31 @@ +[Level] +Name=Formic acid ethyl ester +atom_1=1-c +atom_2=2-bgC +atom_3=3-A +atom_4=1-d +atom_5=3-bf +atom_6=2-bdfh +atom_7=1-h +atom_8=1-f +feld_00=............... +feld_01=.....#########. +feld_02=..####.....2.#. +feld_03=..#..#..###.6#. +feld_04=..#.4.......##. +feld_05=.##.###1.#...#. +feld_06=.#..........6#. +feld_07=.#.......4...#. +feld_08=.#8#.#.###...#. +feld_09=.###........##. +feld_10=...#.....7#3#.. +feld_11=...#7###.####.. +feld_12=...#.5...#..... +feld_13=...#######..... +feld_14=............... +mole_0=...4.8 +mole_1=..4.6. +mole_2=...6.7 +mole_3=..5.7. +mole_4=12.... +mole_5=.3 diff --git a/katomic/levels/level_67 b/katomic/levels/level_67 new file mode 100644 index 00000000..29f1b390 --- /dev/null +++ b/katomic/levels/level_67 @@ -0,0 +1,31 @@ +[Level] +Name=1,4-Cyclohexadiene +atom_1=1-c +atom_2=1-d +atom_3=2-bdfh +atom_4=1-b +atom_5=2-fhB +atom_6=2-bdD +atom_7=1-f +atom_8=2-cfh +atom_9=1-h +feld_00=............... +feld_01=............... +feld_02=....#######.... +feld_03=....#2...4#.... +feld_04=..###..#..###.. +feld_05=..#6#..#..#5#.. +feld_06=..#.4.....9.#.. +feld_07=..##..7#5..##.. +feld_08=..#.3.....3.#.. +feld_09=..#6#..#..#7#.. +feld_10=..###..#..###.. +feld_11=....#2...9#.... +feld_12=....#######.... +feld_13=............... +feld_14=............... +mole_0=.2..7. +mole_1=2.56.7 +mole_2=.3..3. +mole_3=4.56.9 +mole_4=.4..9. diff --git a/katomic/levels/level_68 b/katomic/levels/level_68 new file mode 100644 index 00000000..86addd87 --- /dev/null +++ b/katomic/levels/level_68 @@ -0,0 +1,39 @@ +[Level] +Name=Squaric acid +atom_1=1-c +atom_2=3-cg +atom_3=1-g +atom_4=a-a +atom_5=a-c +atom_6=a-c +atom_7=o-c +atom_8=C-c +atom_9=D-c +atom_a=2-ceA +atom_b=2-egA +atom_c=2-ag +atom_d=2-afB +atom_e=2-adD +atom_f=3-bg +atom_g=3-ch +atom_h=3-C +feld_00=............... +feld_01=..###########.. +feld_02=..#e..#.....#.. +feld_03=..#3.b#....a#.. +feld_04=..#.....#####.. +feld_05=..#.........#.. +feld_06=..#.........#.. +feld_07=..#...#.....#.. +feld_08=..#.h.#d#...#.. +feld_09=..#.###h#...#.. +feld_10=..#.gf####..#.. +feld_11=..#...#.....#.. +feld_12=..#.1.#.....#.. +feld_13=..###########.. +feld_14=............... +mole_0=..hh.. +mole_1=..ab.. +mole_2=..de.. +mole_3=1f..g3 +mole_4=...... diff --git a/katomic/levels/level_69 b/katomic/levels/level_69 new file mode 100644 index 00000000..691aa3b5 --- /dev/null +++ b/katomic/levels/level_69 @@ -0,0 +1,43 @@ +[Level] +Name=Ascorbic acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=3-ac +atom_7=2-bgB +atom_8=2-afB +atom_9=2-bdD +atom_a=2-fhB +atom_b=3-df +atom_c=2-befh +atom_d=3-D +atom_e=3-ch +atom_f=3-bg +atom_g=1-b +atom_h=2-bdfh +atom_i=3-bf +atom_j=1-f +atom_k=1-d +feld_00=############### +feld_01=#djgh.........# +feld_02=#k#........c#.# +feld_03=#a.#h......#b.# +feld_04=#...#.....#..g# +feld_05=#....#...#8..f# +feld_06=#.....#.#....5# +feld_07=#......#.....j# +feld_08=#.....#.#.....# +feld_09=#....#...#....# +feld_10=#...#.....#1..# +feld_11=#..#.......#..# +feld_12=#.#.........#9# +feld_13=#.i........eig# +feld_14=############### +mole_0=.k.j.j.. +mole_1=..h.i... +mole_2=.i.h.b.. +mole_3=g.g.c.ad +mole_4=...g89.. +mole_5=..1f..e5 diff --git a/katomic/levels/level_7 b/katomic/levels/level_7 new file mode 100644 index 00000000..35839e26 --- /dev/null +++ b/katomic/levels/level_7 @@ -0,0 +1,27 @@ +[Level] +Name=Iso-Propanol +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +feld_00=............... +feld_01=....###.###.... +feld_02=....#1#.#.#.... +feld_03=....#4.#..#.... +feld_04=.####....2####. +feld_05=.#...........#. +feld_06=.##.........##. +feld_07=..##...#...##.. +feld_08=.##.3.3....5##. +feld_09=.#.2....3....#. +feld_10=.####2...4####. +feld_11=....#6.#.4#.... +feld_12=....#.###.#.... +feld_13=....###.###.... +feld_14=............... +mole_0=.222. +mole_1=13336 +mole_2=.454. +mole_3=..4.. diff --git a/katomic/levels/level_70 b/katomic/levels/level_70 new file mode 100644 index 00000000..42d96c3f --- /dev/null +++ b/katomic/levels/level_70 @@ -0,0 +1,30 @@ +[Level] +Name=Phosgene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +atom_7=3-C +atom_8=2-dfA +atom_9=7-b +atom_a=7-h +feld_00=............... +feld_01=.#############. +feld_02=.#....a#89...#. +feld_03=.#.....#....7#. +feld_04=.#...#####...#. +feld_05=.#.....#.....#. +feld_06=.#.....#.....#. +feld_07=.#...........#. +feld_08=.#...........#. +feld_09=.#...........#. +feld_10=.#############. +feld_11=............... +feld_12=............... +feld_13=............... +feld_14=............... +mole_0=.7. +mole_1=.8. +mole_2=9.a diff --git a/katomic/levels/level_71 b/katomic/levels/level_71 new file mode 100644 index 00000000..0eb656de --- /dev/null +++ b/katomic/levels/level_71 @@ -0,0 +1,37 @@ +[Level] +Name=Thiophene +atom_1=1-d +atom_2=1-b +atom_3=2-fhB +atom_4=2-bdD +atom_5=1-f +atom_6=1-h +atom_7=1-h +atom_8=5- +atom_9=5-bh +atom_a=2-cfA +atom_b=2-dgA +atom_c=2-cgC +atom_d=A- +atom_e=2-bgC +atom_f=2-chC +atom_g=2-dfA +feld_00=............... +feld_01=............... +feld_02=.############## +feld_03=.#..g#....6#..# +feld_04=.#..5#........# +feld_05=.#...#........# +feld_06=.#...#........# +feld_07=.#...#...#.#### +feld_08=.#de..........# +feld_09=.##.......g#..# +feld_10=.#.f....#....## +feld_11=.#.#..#.#.....# +feld_12=.#91..#.#2....# +feld_13=.############## +feld_14=............... +mole_0=1...5 +mole_1=.fde. +mole_2=.g.g. +mole_3=2.9.6 diff --git a/katomic/levels/level_72 b/katomic/levels/level_72 new file mode 100644 index 00000000..3dec1394 --- /dev/null +++ b/katomic/levels/level_72 @@ -0,0 +1,38 @@ +[Level] +Name=Urea +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=2-bdD +atom_7=1-f +atom_8=1-h +atom_9=2-fhB +atom_a=3-D +atom_b=4-adf +atom_c=4-bdg +atom_d=1-b +atom_e=4-beg +atom_f=4-adg +feld_00=.##########... +feld_01=.##1..f#..#... +feld_02=.#.#...#..#### +feld_03=.#.#.........# +feld_04=.#.##..#.#.#.# +feld_05=.#2........#.# +feld_06=.#.######.##.# +feld_07=.#.1e.......a# +feld_08=.#4.#.#...#### +feld_09=.#..#.#...#... +feld_10=.#..#.#...#... +feld_11=.#.9#.#...#... +feld_12=.#..#.#...#... +feld_13=.###......#... +feld_14=...########... +mole_0=.2.. +mole_1=1f.. +mole_2=..9a +mole_3=1e.. +mole_4=.4.. +le_4=.4.. diff --git a/katomic/levels/level_73 b/katomic/levels/level_73 new file mode 100644 index 00000000..21de96a8 --- /dev/null +++ b/katomic/levels/level_73 @@ -0,0 +1,32 @@ +[Level] +Name=Pyruvic Acid +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-cg +atom_6=1-g +atom_7=2-ceA +atom_8=2-aeB +atom_9=3-C +atom_a=3-D +feld_00=............... +feld_01=############### +feld_02=#...........6a# +feld_03=###...#....7### +feld_04=..#...#....#..# +feld_05=..#...#....#..# +feld_06=..#.###....#..# +feld_07=..#..1#....#..# +feld_08=..#####.......# +feld_09=..#89#........# +feld_10=..#..#........# +feld_11=###..#........# +feld_12=#5#.......##..# +feld_13=#43.......6#### +feld_14=###########.... +mole_0=.9.. +mole_1=.756 +mole_2=.8a. +mole_3=136. +mole_4=.4.. diff --git a/katomic/levels/level_74 b/katomic/levels/level_74 new file mode 100644 index 00000000..166debb4 --- /dev/null +++ b/katomic/levels/level_74 @@ -0,0 +1,34 @@ +[Level] +Name=Ethylene oxide +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=3-ae +atom_6=1-g +atom_7=2-cdfh +atom_8=2-bdfg +atom_9=A-bdfg +atom_a=3-bh +atom_b=1-h +atom_c=1-b +atom_d=1-f +atom_e=1-d +feld_00=............... +feld_01=............... +feld_02=............... +feld_03=##############. +feld_04=#..a#..#....c#. +feld_05=#.........#..#. +feld_06=#..#b........#. +feld_07=#9..d#.#.....#. +feld_08=##...##...#..#. +feld_09=##...#.#..#..#. +feld_10=#............#. +feld_11=#............#. +feld_12=#.#..........#. +feld_13=#.8.e#..#7...#. +feld_14=##############. +mole_0=e...d +mole_1=.798. +mole_2=c.a.b diff --git a/katomic/levels/level_75 b/katomic/levels/level_75 new file mode 100644 index 00000000..4dd37688 --- /dev/null +++ b/katomic/levels/level_75 @@ -0,0 +1,30 @@ +[Level] +Name=Phosphoric Acid +atom_1=1-c +atom_2=3-cg +atom_3=1-g +atom_4=9-g +atom_5=9-A +atom_6=3-C +atom_7=3-ae +atom_8=9-cegA +atom_9=1-a +feld_00=############### +feld_01=#.#...........# +feld_02=#........#.#..# +feld_03=#.........#...# +feld_04=#.#......#.#..# +feld_05=#6#...........# +feld_06=###...........# +feld_07=#......#.#....# +feld_08=#.......#.....# +feld_09=#......#.#...2# +feld_10=##31..........# +feld_11=#.....7.#...29# +feld_12=#....##.##..### +feld_13=#..##.###....8# +feld_14=#####....###### +mole_0=..6.. +mole_1=12823 +mole_2=..7.. +mole_3=..9.. diff --git a/katomic/levels/level_76 b/katomic/levels/level_76 new file mode 100644 index 00000000..d6de2065 --- /dev/null +++ b/katomic/levels/level_76 @@ -0,0 +1,37 @@ +[Level] +Name=Diacetyl +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +feld_00=#######........ +feld_01=#9...9########. +feld_02=#.....#.....d#. +feld_03=#.....#......#. +feld_04=#.....#......#. +feld_05=#............#. +feld_06=#.........####. +feld_07=#....e....7b8.# +feld_08=######.......e# +feld_09=..#8..........# +feld_10=..#.......#...# +feld_11=..#.......#...# +feld_12=..#b6....c#...# +feld_13=..#########...# +feld_14=..........##### +mole_0=b.d. +mole_1=.8.. +mole_2=e.69 +mole_3=b.79 +mole_4=.8.. +mole_5=e.c. diff --git a/katomic/levels/level_77 b/katomic/levels/level_77 new file mode 100644 index 00000000..7fcd8795 --- /dev/null +++ b/katomic/levels/level_77 @@ -0,0 +1,38 @@ +[Level] +Name=trans-Dichloroethene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +feld_00=.............. +feld_01=.############# +feld_02=.#..i#.#.....# +feld_03=.#..g##...#..# +feld_04=.#...#...f#..# +feld_05=.#.....#..#### +feld_06=.#........#... +feld_07=.#........#... +feld_08=.#...#....#... +feld_09=.#...#....#... +feld_10=.#...#....#... +feld_11=.#...#.#.h#... +feld_12=.#...#..#c#... +feld_13=.#..b#...##... +feld_14=.##########... +mole_0=b..h +mole_1=.fg. +mole_2=i..c diff --git a/katomic/levels/level_78 b/katomic/levels/level_78 new file mode 100644 index 00000000..65624b05 --- /dev/null +++ b/katomic/levels/level_78 @@ -0,0 +1,41 @@ +[Level] +Name=Allylisothiocyanate +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +atom_j=4-fB +atom_k=2-BD +atom_l=5-D +feld_00=############### +feld_01=#gl.f#.......j# +feld_02=#..#####......# +feld_03=#...........#.# +feld_04=#......###..#.# +feld_05=#...#.......### +feld_06=#...#.......#.# +feld_07=#.#.#.....#.#.# +feld_08=#.#.......#...# +feld_09=###.......#...# +feld_10=#.#..###......# +feld_11=#.#...........# +feld_12=#......#####..# +feld_13=#......ce#b.k.# +feld_14=############### +mole_0=b..jkl +mole_1=.fg... +mole_2=e..c.. diff --git a/katomic/levels/level_79 b/katomic/levels/level_79 new file mode 100644 index 00000000..6e9e6cb5 --- /dev/null +++ b/katomic/levels/level_79 @@ -0,0 +1,57 @@ +[Level] +Name=Diketene +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=1-g +atom_6=2-ehB +atom_7=2-afB +atom_8=2-bdfh +atom_9=3-D +atom_a=3-ad +atom_b=1-d +atom_c=1-h +atom_d=1-f +atom_e=1-b +atom_f=2-fhB +atom_g=2-bdD +atom_h=7-f +atom_i=7-b +atom_j=4-fB +atom_k=2-BD +atom_l=5-D +atom_m=2-eg +atom_n=2-ce +atom_o=2-ac +atom_p=3-ag +atom_q=3-B +atom_r=C- +atom_s=A- +atom_t=2-ag +atom_u=2-acC +atom_v=3-ce +atom_w=3-A +atom_x=2-egA +atom_y=2-C +atom_z=2-bhC +feld_00=############### +feld_01=##4x........u## +feld_02=#..b........z.# +feld_03=#..#.#...#.#..# +feld_04=#...#.....#...# +feld_05=#5.#.#...#.#..# +feld_06=#w............# +feld_07=##...........## +feld_08=#v............# +feld_09=#..#.#...#.#..# +feld_10=#...#.....#...# +feld_11=#..#.#...#.#..# +feld_12=#3............# +feld_13=##.d.........## +feld_14=############### +mole_0=b.d +mole_1=.z. +mole_2=vx. +mole_3=u35 +mole_4=w4. diff --git a/katomic/levels/level_8 b/katomic/levels/level_8 new file mode 100644 index 00000000..5e762c7d --- /dev/null +++ b/katomic/levels/level_8 @@ -0,0 +1,26 @@ +[Level] +Name=Ethanal +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-agB +atom_6=3-D +feld_00=............... +feld_01=............... +feld_02=..###########.. +feld_03=..#.........#.. +feld_04=..#.##...##.#.. +feld_05=..#2.....3..#.. +feld_06=..#.###.###.#.. +feld_07=..#4...1....#.. +feld_08=..#5###2###.#.. +feld_09=..#.........#.. +feld_10=..#.##...##.#.. +feld_11=..#..6......#.. +feld_12=..###########.. +feld_13=............... +feld_14=............... +mole_0=.22. +mole_1=1356 +mole_2=.4.. diff --git a/katomic/levels/level_80 b/katomic/levels/level_80 new file mode 100644 index 00000000..5d6307b5 --- /dev/null +++ b/katomic/levels/level_80 @@ -0,0 +1,29 @@ +[Level] +Name=Acroleine +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +feld_00=............... +feld_01=############### +feld_02=#.....1#1.....# +feld_03=#.....###.....# +feld_04=#.............# +feld_05=#.............# +feld_06=#7#.........#2# +feld_07=###.........### +feld_08=###.........### +feld_09=#6#.........#3# +feld_10=#.............# +feld_11=#.............# +feld_12=#.....###.....# +feld_13=#.....5#4.....# +feld_14=############### +mole_0=..1. +mole_1=76.1 +mole_2=..5. +mole_3=..4. +mole_4=.3.2 diff --git a/katomic/levels/level_81 b/katomic/levels/level_81 new file mode 100644 index 00000000..8c62106a --- /dev/null +++ b/katomic/levels/level_81 @@ -0,0 +1,37 @@ +[Level] +Name=Malonic Acid +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +atom_8=2-bdfh +atom_9=2-cfA +atom_a=2-chC +atom_b=3-cg +atom_c=3-C +atom_d=3-A +atom_e=1-g +atom_f=1-d +feld_00=############### +feld_01=##c8d.......e## +feld_02=#e#.........#b# +feld_03=#............9# +feld_04=#.............# +feld_05=#.............# +feld_06=#.....#.#.....# +feld_07=#......#......# +feld_08=#.....#.#.....# +feld_09=#.............# +feld_10=#.............# +feld_11=#.............# +feld_12=#a#.........#f# +feld_13=##.........b3## +feld_14=############### +mole_0=..c.. +mole_1=f.9be +mole_2=.8... +mole_3=3.abe +mole_4=..d.. diff --git a/katomic/levels/level_82 b/katomic/levels/level_82 new file mode 100644 index 00000000..7d6fdfea --- /dev/null +++ b/katomic/levels/level_82 @@ -0,0 +1,44 @@ +[Level] +Name=Uracil +atom_1=1-f +atom_2=1-h +atom_3=1-b +atom_4=2-dfA +atom_5=2-bhC +atom_6=2-bdD +atom_7=3-B +atom_8=2-bdfh +atom_9=2-cfA +atom_a=2-chC +atom_b=3-cg +atom_c=3-C +atom_d=3-A +atom_e=1-g +atom_f=1-d +atom_g=2-bgC +atom_h=4-beh +atom_i=4-ceh +atom_j=2-afB +atom_k=3-D +atom_l=1-a +feld_00=############### +feld_01=#3..........4c# +feld_02=#l..........4.# +feld_03=#.#..#....#...# +feld_04=#.#..#........# +feld_05=#.#..#.#......# +feld_06=#..##k....#...# +feld_07=#.............# +feld_08=#....#je...#..# +feld_09=#..#.#i###....# +feld_10=#....###.#....# +feld_11=#.#...........# +feld_12=#...#.....#.f5# +feld_13=#............h# +feld_14=############### +mole_0=..c.. +mole_1=f.4.. +mole_2=.5.ie +mole_3=.4.jk +mole_4=3.h.. +mole_5=..l.. diff --git a/katomic/levels/level_83 b/katomic/levels/level_83 new file mode 100644 index 00000000..0c30ef6b --- /dev/null +++ b/katomic/levels/level_83 @@ -0,0 +1,46 @@ +[Level] +Name=Caffeine +atom_1=1-d +atom_2=1-e +atom_3=1-f +atom_4=2-abeh +atom_5=1-c +atom_6=2-adgh +atom_7=3-C +atom_8=4-adf +atom_9=4-ceh +atom_a=2-Acg +atom_b=2-bCg +atom_c=2-cCh +atom_d=1-g +atom_e=3-B +atom_f=2-acD +atom_g=4-ceg +atom_h=A- +atom_i=4-Ag +atom_j=2-adef +atom_k=1-b +atom_l=1-a +atom_m=1-h +feld_00=############### +feld_01=#1#.....#...3d# +feld_02=#2.........#### +feld_03=#......#.....4# +feld_04=#e#...........# +feld_05=#.#.#.a#.#....# +feld_06=###...#...#...# +feld_07=#5.#..........# +feld_08=#..9...#...#..# +feld_09=#.6.#...lm....# +feld_10=####.......j..# +feld_11=#12#.#kc.#....# +feld_12=#ab..#..####### +feld_13=#hi.....78..fg# +feld_14=############### +mole_0=....123. +mole_1=12...4.. +mole_2=56.7.8.. +mole_3=..9ab.cd +mole_4=.efgahi. +mole_5=...j.... +mole_6=..klm... diff --git a/katomic/levels/level_9 b/katomic/levels/level_9 new file mode 100644 index 00000000..ac49d58b --- /dev/null +++ b/katomic/levels/level_9 @@ -0,0 +1,27 @@ +[Level] +Name=Acetone +atom_1=1-c +atom_2=1-e +atom_3=2-aceg +atom_4=1-a +atom_5=2-cgC +atom_6=3-A +atom_7=1-g +feld_00=............... +feld_01=.#############. +feld_02=.####..##....#. +feld_03=.##.....#.#..#. +feld_04=.#.....6#.####. +feld_05=.#.#3#.4.5..2#. +feld_06=.#.#4###..1..#. +feld_07=.###..2....###. +feld_08=.#.....###.#.#. +feld_09=.#.....3.#.#.#. +feld_10=.####.#.....7#. +feld_11=.#..#.#.....##. +feld_12=.#....##..####. +feld_13=.#############. +feld_14=............... +mole_0=.2.2. +mole_1=13537 +mole_2=.464. diff --git a/katomic/main.cpp b/katomic/main.cpp new file mode 100644 index 00000000..91072a8b --- /dev/null +++ b/katomic/main.cpp @@ -0,0 +1,68 @@ +/* main.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + + +#include "toplevel.h" + +#include +#include +#include +#include + + +static const char description[] = + I18N_NOOP("KDE Atomic Entertainment Game"); + +static const char version[] = "2.0"; + + +// ########################## +// # Main # +// ########################## + +int main(int argc, char **argv) +{ + KAboutData aboutData( "katomic", I18N_NOOP("KAtomic"), + version, description, KAboutData::License_GPL, + "(c) 1998, Andreas Wuest"); + aboutData.addAuthor("Andreas Wuest", 0, "AndreasWuest@gmx.de"); + aboutData.addAuthor("Stephan Kulow", 0, "coolo@kde.org"); + aboutData.addAuthor("Cristian Tibirna", 0, "tibirna@kde.org"); + aboutData.addCredit("Carsten Pfeiffer"); + aboutData.addCredit("Dave Corrie"); + aboutData.addCredit("Kai Jung", I18N_NOOP("6 new levels"), "jung@fh-fresenius.de"); + aboutData.addCredit("Danny Allen", I18N_NOOP("Game graphics and application icon"), "danny@dannyallen.co.uk"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + QApplication::setColorSpec(QApplication::ManyColor); + KApplication a; + KGlobal::locale()->insertCatalogue("libkdegames"); + + if ( a.isRestored() ) + RESTORE(AtomTopLevel) + else { + AtomTopLevel *top = new AtomTopLevel; + top->show(); + a.setMainWidget(top); + } + return a.exec(); +} + diff --git a/katomic/molek.cpp b/katomic/molek.cpp new file mode 100644 index 00000000..3425ffe1 --- /dev/null +++ b/katomic/molek.cpp @@ -0,0 +1,158 @@ +/**************************************************************** +** +** Implementation Molek class, derieved from Qt tutorial 8 +** +****************************************************************/ + +// bemerkungen : wenn paintEvent aufgerufen wird, wird das komplette +// widget gelöscht und nur die sachen gezeichnet, die in +// paintEvent stehen ! sollen dinge z.b nur bei maustasten- +// druck gezeichnet werden, so muß dies in mousePressEvent +// stehen ! +// paintEvent wird aufgerufen, falls fenster überdeckt wird, +// oder auch einfach bewegt wird + +#include + +#include "molek.moc" +#include +#include +#include +#include +#include +#include + +#include + +extern int level; + +Molek::Molek( QWidget *parent, const char *name ) : QWidget( parent, name ), + data(locate("appdata", "pics/molek.png")) +{ + setBackgroundColor (QColor (0, 0, 0)); + setMinimumSize(240, 200); +} + +Molek::~Molek () +{ +} + +const atom& Molek::getAtom(uint index) const +{ + static atom none = { 0, "" }; + + if (index > atoms.count() || index == 0) + return none; + + return *atoms.at(index - 1); +} + +void Molek::load (const KSimpleConfig& config) +{ + atoms.clear(); + QString key; + + atom current; + + int atom_index = 1; + QString value; + while (true) { + key.sprintf("atom_%c", int2atom(atom_index)); + value = config.readEntry(key); + if (value.isEmpty()) + break; + + current.obj = value.at(0).latin1(); + value = value.mid(2); + if (value.isNull()) + value = ""; + + strlcpy(current.conn, value.ascii(), sizeof(current.conn)); + kdWarning( atoms.find(current) != atoms.end() ) + << "OOOPS, duplicate atom definition in " << key << endl; + atoms.append(current); + atom_index++; + } + + QString line; + + for (int j = 0; j < MOLEK_SIZE; j++) { + + key.sprintf("mole_%d", j); + line = config.readEntry(key); + + for (int i = 0; i < MOLEK_SIZE; i++) + molek[i][j] = atom2int(line.at(i).latin1()); + } + + mname = i18n(config.readEntry("Name", I18N_NOOP("Noname")).latin1()); + + int& height = _size.rheight(); + int& width = _size.rwidth(); + + height = 0; + width = 0; + + for (int i = 0; i < MOLEK_SIZE; i++) + for (int j = 0; j < MOLEK_SIZE; j++) { + if (molek [i][j] == 0) + continue; + if (i > width) width = i; + if (j > height) height = j; + } + height++; + width++; + + repaint (); +} + +void Molek::paintEvent( QPaintEvent * ) +{ + QString st = i18n("Level: %1").arg(level); + + QPainter paint (this); + paint.setPen (QColor (190, 190, 190)); + paint.drawText (7, height() - 36, mname); + paint.drawText (7, height() - 18, st); + // spielfeld gleich zeichnen + for (int i = 0; i < MOLEK_SIZE; i++) + for (int j = 0; j < MOLEK_SIZE; j++) { + int x = 10 + i * 15; + int y = 10 + j * 15; + + if (molek[i][j] == 0) + continue; + + // paints atoms + if (getAtom(molek [i] [j]).obj <= '9' && getAtom(molek [i] [j]).obj >= '1') + bitBlt (this, x, y, &data, (getAtom(molek [i] [j]).obj - '1') * 15, 0, 15, + 15, CopyROP); + + // paints cristals + if (getAtom(molek [i] [j]).obj == 'o') + bitBlt (this, x, y, &data, 10 * 15, 0, 15, 15, CopyROP); + + // paints connections + if (isdigit(getAtom(molek[i][j]).obj) || getAtom(molek[i][j]).obj == 'o') + for (int c = 0; c < MAX_CONNS_PER_ATOM; c++) { + char conn = getAtom(molek [i] [j]).conn[c]; + if (!conn) + break; + + if (conn >= 'a' && conn <= 'a' + 8) + bitBlt (this, x, y, &data, (conn - 'a') * 15, 16, 15, 15, XorROP); + else + bitBlt (this, x, y, &data, (conn - 'A') * 15, 34, 15, 15, XorROP); + + } + + + // paints connections + if (getAtom(molek[i][j]).obj >= 'A' && getAtom(molek[i][j]).obj <= 'F') + bitBlt (this, x, y, &data, (getAtom(molek[i][j]).obj - 'A' + 11) * 15 , 0, 15, 15, + CopyROP); + + } + + paint.end (); +} diff --git a/katomic/molek.h b/katomic/molek.h new file mode 100644 index 00000000..6189e4e8 --- /dev/null +++ b/katomic/molek.h @@ -0,0 +1,52 @@ +/**************************************************************** +** +** Definition of Molek class, +** +****************************************************************/ + +#ifndef MOLEK_H +#define MOLEK_H + +#include +#include +#include +#include +#include +#include +#include "atom.h" +#include + +class KSimpleConfig; + +#define MOLEK_SIZE 15 + +class Molek : public QWidget +{ + Q_OBJECT + +public: + Molek (QWidget *parent=0, const char *name=0); + ~Molek (); + + void load(const KSimpleConfig& config); + + const atom& getAtom(uint index) const; + int atomSize() const { return atoms.count(); } + + QSize molecSize() const { return _size; } + uint getAtom(int x, int y) const { return molek[x][y]; } + +protected: + void paintEvent( QPaintEvent * ); + +private: + QPixmap data; + uint molek[MOLEK_SIZE][MOLEK_SIZE]; // the indexes within atoms + QValueList atoms; + QString mname; + QSize _size; + + +}; + +#endif // MOLEK_H diff --git a/katomic/molek.png b/katomic/molek.png new file mode 100644 index 0000000000000000000000000000000000000000..d816bcd9b975a1189ec67aaee588839ef88367fa GIT binary patch literal 2441 zcmX9=dps5C8-FVY<YX;?>ot1K197Lrsg1#2` zR}^Ld1OUN*1+&iv2m&w+peTSCBC{a|YzhDp00IE!5CZ~|{RN`9Uon~u=r61<6h1Q$ z1_S{p3Xmia2$=3Dmx6FHj!-Z}L2Lr>#VCY92tx7s6w{1`AexU-0u298BNxzOl%_D6 zhA0Xm2#D#(WE4%o6f72#h=7R{Nse#Q4S^#x zbC^O1Sj=cb03Ri}T$~bHM15EOzS2Iccn7={r9Mp0BCz_?t9aS#F!AD|e(P{w^e3Ixm)iU2qQ zV8C1#W#&;BMR7@rMkt!ePzr}|E)267*-XVKq$!-hVF6AH7y~E*CvgETfH5|Tk_gEM z7{XjZOffYkz!(DvF3x8@A0NP6Kw^L2|g=sqg zq!jFU2i)U@GbPyp3SNrsj};XIoG#9I9;pi~7o!N*$LLbrL5mtGw~@;0Jv}*rMn{fV z7~Gc;I(CVEDs_Pu;y@RLGZw%F>v@RP1EO4e%EN=@D!Tf-VfrR>dSS*^mf zUEvWQ#HcW}Rb5s`Z#$Fo;iPGYrUS`EjxcKO)iY;>xRWwHM? z2~tQ)8EC0-*V~xtSzdlJinhB5mzUjxB%5k?TX}fI#ujLltoq*1n=h*S@blEPlHoDG zghaBTO(eQU&T-;arpR;wGBjksPq6UUMRIfEmTCg-yoVht*vnTHyxRLLrv zo0}-F({sK0voW4CKmXIPu@&UV3-Ut{#6GL+hs=VEPEU~aw3;F;4)-6pG*SdT+e)bh64-D6#f(sh=Il8sy%Nf}LUSPZSI(v#sdSv$Oz zJE1bX{6r~d=dMZvBeVU7Sd^ZRjCezOLkeP9&$7sNQEQ2t8i+;sC)4xggB=_y$|`R; zI7`*miW(={dP-iH%(j5Ozh;-Z`ky)_5vr1ksr+HZi|h2frAB_#ZWHP6P1wh!=(O~a za-;9=iR!+QIM8p|ozWt#H@fi~U&XMPCr>R8RtE%b)J*7?Hf#J?_HLN8x^OUwFt>O! z&ynSPt1pjC{2X{=OyY8cTE0(3;?~lprlutJwj0-Z&wqRC#+i#gN4q*LRF&NJ2oF&D zV^8gk5g(_6g#Fyv%_jfg(f9MiovH;* z1@|g%8dCb0-6j zcg*K}9+EU8V^s6%Rn<~Kk)vwfQWVHs<2>kf&P}FDR(tM+s$Ya#rBREGhNEids+$s4 zwz)^wKKoUoo97Hdv^|BBoEzP?F4fbMM?P#wh^uSX)y?*6AF6pmEWCT6uloS6$gMpj zTYG!G;)Z9B@%q@9n82vVZ-eWy0$={qZ9AYOBJKK5?6w`8(d2(W^sd!FKR!?xmG6DO zJ~Aa`Q;xjs?`O9xVl#@ahq0v8d&-7Bp8aB8FYl3IQgP{pwzzFIJIoXp78?>+i!9cT zZDHwy$%h?9sw$huPHlc9pGF2Yt7}Y0Y+VVd|NnOrvVW?pv6HMXuZ*c^c*#qTs}$8q zII8#ZT#q019$zWi-qq2O*;OdsL&ufo7B{|??{B@s-G23Q{Ph!Es-;C6dvna#cemaY z5fZ`n`nqG)<>iOrwH3)Sw-Z+vDcsxkXbtNSCMuLLph^#oZm4ba9H?_2wca<{5L!^Z zReK~dBGUT(lRNvSiPs9FJYneGm)Bk7(=F<>lE2CByH(t=Ip+HCY#y8ugzQp3w)@8$ zNyZ;T3l()@%jzzK)m6o~s6C(Yd9&00Y0K8zf9xEI3Tv#wVocdZdk4p+w z6_j4fja9ksH8Cxz5=+h+myb5A`V?SlrByxYpZMvWWAP{6^z44kn_Ws;_W7KHWxPxU nE&Hc~8mTK}nz%ss(2AtDbl~ft + +#define MAX_SPEED 10 + + +struct Options +{ + int anim_speed; + + bool changed; +}; + +#endif diff --git a/katomic/toplevel.cpp b/katomic/toplevel.cpp new file mode 100644 index 00000000..ca4de920 --- /dev/null +++ b/katomic/toplevel.cpp @@ -0,0 +1,119 @@ +/* toplevel.cpp + + Copyright (C) 1998 Andreas Wüst (AndreasWuest@gmx.de) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "gamewidget.h" +#include "toplevel.h" +#include "settings.h" +#include "configbox.h" +#include +#include +#include +#include + +extern Options settings; + +void AtomTopLevel::createMenu() +{ + KAction *act = KStdGameAction::highscores(main, SLOT(showHighscores()), actionCollection()); + act->setText(i18n("Show &Highscores")); + KStdGameAction::quit(this, SLOT(close()), actionCollection()); + KStdGameAction::restart(main, SLOT(restartLevel()), actionCollection()); + + KStdAction::preferences(this, SLOT(configopts()), actionCollection()); + + undoAction = KStdGameAction::undo (main, SLOT(doUndo()), actionCollection()); + redoAction = KStdGameAction::redo (main, SLOT(doRedo()), actionCollection()); + undoAction->setEnabled(false); + redoAction->setEnabled(false); + connect (main, SIGNAL (enableRedo(bool)), SLOT(enableRedo(bool))); + connect (main, SIGNAL (enableUndo(bool)), SLOT(enableUndo(bool))); + + new KAction(i18n("Atom Up"), Key_Up, main, SLOT(moveUp()), actionCollection(), "atom_up"); + new KAction(i18n("Atom Down"), Key_Down, main, SLOT(moveDown()), actionCollection(), "atom_down"); + new KAction(i18n("Atom Left"), Key_Left, main, SLOT(moveLeft()), actionCollection(), "atom_left"); + new KAction(i18n("Atom Right"), Key_Right, main, SLOT(moveRight()), actionCollection(), "atom_right"); + + new KAction(i18n("Next Atom"), Key_Tab, main, SLOT(nextAtom()), actionCollection(), "next_atom"); + new KAction(i18n("Previous Atom"), SHIFT+Key_Tab, main, SLOT(previousAtom()), actionCollection(), "prev_atom"); +} + +void AtomTopLevel::configopts() +{ + (new ConfigBox(this, "Options"))->show(); +} + +void AtomTopLevel::initConfig() +{ + config = KGlobal::config(); +} + +void AtomTopLevel::saveConfig() +{ + config = KGlobal::config(); + + if (settings.changed) { + config->setGroup("Options"); + config->writeEntry("Animation Speed", settings.anim_speed); + config->setGroup("Colors"); + } + config->sync(); +} + + +AtomTopLevel::AtomTopLevel() +{ + main = new GameWidget(this, "gamewidget"); + createMenu(); + initConfig(); + setCentralWidget(main); + + setupGUI( KMainWindow::Save | Keys | Create ); +} + +AtomTopLevel::~AtomTopLevel() +{ +} + +bool AtomTopLevel::queryExit() +{ + saveConfig(); + return true; +} + +void AtomTopLevel::enableRedo(bool enable) +{ + redoAction->setEnabled(enable); +} + +void AtomTopLevel::enableUndo(bool enable) +{ + undoAction->setEnabled(enable); +} + +#include "toplevel.moc" diff --git a/katomic/toplevel.h b/katomic/toplevel.h new file mode 100644 index 00000000..38c613d0 --- /dev/null +++ b/katomic/toplevel.h @@ -0,0 +1,66 @@ +/* toplevel.h + * + * Andreas Wüst + * + */ + +#ifndef TOPLEVEL_H +#define TOPLEVEL_H + +class GameWidget; +class KAction; +class KConfig; + +#include + +/** + * This is the class AtomTopLevel. The class is used only for the program + * AtomTopLevel. + * + * @short Basic class for AtomTopLevel + * @author Andreas Wüst + */ + +class AtomTopLevel : public KMainWindow +{ + Q_OBJECT + + public: + + AtomTopLevel(); + + ~AtomTopLevel(); + + protected: + + // Creates the menubar and connects the menu-entries to the + // appropriate functions + void createMenu(); + + // Get the configuration from the config-file. + void initConfig(); + + // Save the current configuration to the config-file. + void saveConfig(); + + // called before exiting -> save configuration + virtual bool queryExit(); + + KConfig *config; + + GameWidget *main; + + KAction *redoAction, *undoAction; + + protected slots: + void enableRedo(bool enable); + void enableUndo(bool enable); + +public slots: + + // Shows a dialog for options other than keys + void configopts(); + +}; + +#endif diff --git a/kbackgammon/AUTHORS b/kbackgammon/AUTHORS new file mode 100644 index 00000000..65b967eb --- /dev/null +++ b/kbackgammon/AUTHORS @@ -0,0 +1 @@ +Jens Hoefkens diff --git a/kbackgammon/Makefile.am b/kbackgammon/Makefile.am new file mode 100644 index 00000000..6f21f2ca --- /dev/null +++ b/kbackgammon/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = pics sounds icons engines + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame/ -I$(srcdir)/engines $(all_includes) +METASOURCES = AUTO + +bin_PROGRAMS = kbackgammon +kbackgammon_SOURCES = main.cpp kbg.cpp kbgboard.cpp kbgtextview.cpp \ + kbgstatus.cpp +kbackgammon_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kbackgammon_LDADD = $(LIB_KDEGAMES) $(LIB_KDEPRINT) ./engines/libkbgengines.la +kbackgammon_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +xdg_apps_DATA = kbackgammon.desktop + +rcdir = $(kde_datadir)/kbackgammon +rc_DATA = kbackgammonui.rc eventsrc + +messages: rc.cpp + LIST=`find . -name \*.h -o -name \*.hh -o -name \*.H -o -name \*.hxx -o -name \*.hpp -o -name \*.cpp -o -name \*.cc -o -name \*.c -o -name \*.ecpp -o -name \*.C`; \ + $(XGETTEXT) $$LIST -o $(podir)/kbackgammon.pot + diff --git a/kbackgammon/README b/kbackgammon/README new file mode 100644 index 00000000..6d818f2b --- /dev/null +++ b/kbackgammon/README @@ -0,0 +1,43 @@ +This file describes KBackgammon +------------------------------- + + +KBackgammon is a backgammon program for KDE2. It is based on the code, +ideas and concepts of KFibs (which is a FIBS client for KDE1). For a +short time, KBackgammon was called bacKgammon (if you know somebody +who is still using bacKgammon, please force them to upgrade :-)). + + +KBackgammon is a backgammon program built around a graphical backgammon +board. Since it uses moular backgammon engines, it can easily be extended +beyond the current set of engines. At the moment, the following types of +backgammon games are supported: + + FIBS - online games on the First Internet Backgammon Server. + the computer handles the network connection and + translates the textual messages from the server into + graphical representations. this engine offers separate + chat window and player list window to simplify the + interaction with the server + + Offline - play against yourself or a freind that is sitting next + to you. the most important role of the computer is rolling + the dice. + +In the near to mid future, the program will be extended with the following +two kinds of engines: + + GNUBg - the GNU backgammon program is a powerful neural network + and KBackgammon will soon allow you to play against it + from the convinience of your KDE desktop. + + NextGen - this extension of the offline engine will allow you to play + against other players on the network. the computer makes + sure that the dice are fair and it handles the netwok + communication. + +If you lust for other kinds of engines (besides FIBS, there are other +internet servers out there and it somebody might want to have access +to these as well), please contact the Jens Hoefkens +and make your wishes heard. + diff --git a/kbackgammon/TODO b/kbackgammon/TODO new file mode 100644 index 00000000..616fe58d --- /dev/null +++ b/kbackgammon/TODO @@ -0,0 +1,11 @@ +FIBS engine +----------- + +- add the accumulated online time in the lower right corner of the + main window status bar +- add user profiles with different username, etc. +- port the FIBS help system from KFibs +- add buttons for accept and reject ? +- automatically translate messages --> replace \"a by ae, etc ? +- toggle double is NOT automatically set at the beginning of 1 point games! +- clean the header file diff --git a/kbackgammon/engines/Makefile.am b/kbackgammon/engines/Makefile.am new file mode 100644 index 00000000..e599d9b2 --- /dev/null +++ b/kbackgammon/engines/Makefile.am @@ -0,0 +1,16 @@ +noinst_LTLIBRARIES = libkbgengines.la + +libkbgengines_la_SOURCES = dummy.cpp +libkbgengines_la_LIBADD = offline/libkbgoffline.la gnubg/libkbggnubg.la \ + generic/libkbggeneric.la fibs/libkbgfibs.la \ + nextgen/libkbgnextgen.la + +INCLUDES= $(all_includes) + +METASOURCES = AUTO + +SUBDIRS = offline generic fibs gnubg nextgen + +dummy.cpp: + echo > dummy.cpp + diff --git a/kbackgammon/engines/fibs/Makefile.am b/kbackgammon/engines/fibs/Makefile.am new file mode 100644 index 00000000..e32522de --- /dev/null +++ b/kbackgammon/engines/fibs/Makefile.am @@ -0,0 +1,9 @@ +noinst_LTLIBRARIES = libkbgfibs.la + +libkbgfibs_la_SOURCES = kbgfibs.cpp kplayerlist.cpp kbginvite.cpp kbgfibschat.cpp + +INCLUDES= -I$(top_srcdir)/kbackgammon/engines -I$(top_srcdir)/libkdegames \ + -I$(top_srcdir)/kbackgammon $(all_includes) + +METASOURCES = AUTO + diff --git a/kbackgammon/engines/fibs/clip.h b/kbackgammon/engines/fibs/clip.h new file mode 100644 index 00000000..e016eb5b --- /dev/null +++ b/kbackgammon/engines/fibs/clip.h @@ -0,0 +1,39 @@ +/* + + This file defines constants of the "CLIent Protocol" of FIBS. + It comes directly from Marvin and I guess it is copyrighted + by him. If you have questions regarding this file, try to + visit + + http://fibs.demon.co.uk/clip.html + +*/ + +#ifndef KFIBS_CLIP_H +#define KFIBS_CLIP_H + + +#define CLIP_VERSION 1008 + +#define CLIP_WELCOME 1 +#define CLIP_OWN_INFO 2 +#define CLIP_MOTD_BEGIN 3 +#define CLIP_MOTD_END 4 +#define CLIP_WHO_INFO 5 +#define CLIP_WHO_END 6 +#define CLIP_LOGIN 7 +#define CLIP_LOGOUT 8 +#define CLIP_MESSAGE 9 +#define CLIP_MESSAGE_DELIVERED 10 +#define CLIP_MESSAGE_SAVED 11 +#define CLIP_SAYS 12 +#define CLIP_SHOUTS 13 +#define CLIP_WHISPERS 14 +#define CLIP_KIBITZES 15 +#define CLIP_YOU_SAY 16 +#define CLIP_YOU_SHOUT 17 +#define CLIP_YOU_WHISPER 18 +#define CLIP_YOU_KIBITZ 19 + + +#endif // KFIBS_CLIP_H diff --git a/kbackgammon/engines/fibs/kbgfibs.cpp b/kbackgammon/engines/fibs/kbgfibs.cpp new file mode 100644 index 00000000..06fdaec7 --- /dev/null +++ b/kbackgammon/engines/fibs/kbgfibs.cpp @@ -0,0 +1,2314 @@ +/* Yo Emacs, this -*- C++ -*- + + Copyright (C) 1999-2001 Jens Hoefkens + jens@hoefkens.com + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + $Id$ + +*/ + +/* + + TODO: popup dialog for accept/reject and join ?? + clear the chat history? + game over, clear the caption? + need show saved + need buddy list + need wait for player,... + +*/ + +#include "kbgfibs.h" +#include "kbgfibs.moc" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include "kbgboard.h" +#include "kbgstatus.h" + +#include "clip.h" +#include "version.h" + + +void KBgEngineFIBS::start() +{ + // FIXME: open the child windows here and not in the constructor +} + +// == configuration handling =================================================== + +/* + * Restore settings and ask children to do the same + */ +void KBgEngineFIBS::readConfig() +{ + KConfig *config = kapp->config(); + config->setGroup("fibs engine"); + + // history variables + lastAway = config->readEntry("away_hist", ""); + + // various options + showMsg = config->readBoolEntry("pers_msg", false); + whoisInvite = config->readBoolEntry("whois_invite", false); + + // connection information + infoFIBS[FIBSHost] = config->readEntry("server", "fibs.com"); + infoFIBS[FIBSPort] = config->readEntry("port", "4321"); + infoFIBS[FIBSUser] = config->readEntry("user", ""); + infoFIBS[FIBSPswd] = config->readEntry("password", ""); + + // automatic messages + useAutoMsg[MsgBeg] = config->readBoolEntry("auto-beg", false); + useAutoMsg[MsgLos] = config->readBoolEntry("auto-los", false); + useAutoMsg[MsgWin] = config->readBoolEntry("auto-win", false); + + autoMsg[MsgBeg] = config->readEntry("msg-beg", ""); + autoMsg[MsgLos] = config->readEntry("msg-los", ""); + autoMsg[MsgWin] = config->readEntry("msg-win", ""); + + // ask the children to read their config options + playerlist->readConfig(); + chatWindow->readConfig(); +} + +/* + * Save the engine specific settings and tell all clients + */ +void KBgEngineFIBS::saveConfig() +{ + KConfig *config = kapp->config(); + config->setGroup("fibs engine"); + + // history variables + config->writeEntry("away_hist", lastAway); + + // various options + config->writeEntry("pers_msg", showMsg); + config->writeEntry("whois_invite", whoisInvite); + + // connection information + config->writeEntry("server", infoFIBS[FIBSHost]); + config->writeEntry("port", infoFIBS[FIBSPort]); + config->writeEntry("user", infoFIBS[FIBSUser]); + config->writeEntry("password", infoFIBS[FIBSPswd]); + + // automatic messages + config->writeEntry("auto-beg", useAutoMsg[MsgBeg]); + config->writeEntry("auto-los", useAutoMsg[MsgLos]); + config->writeEntry("auto-win", useAutoMsg[MsgWin]); + + config->writeEntry("msg-beg", autoMsg[MsgBeg]); + config->writeEntry("msg-los", autoMsg[MsgLos]); + config->writeEntry("msg-win", autoMsg[MsgWin]); + + // ask the children to read their config options + playerlist->saveConfig(); + chatWindow->saveConfig(); +} + +void KBgEngineFIBS::setupDefault() +{ + + cbp->setChecked(false); + cbi->setChecked(false); + + lec[FIBSHost]->setText("fibs.com"); + lec[FIBSPort]->setText("4321"); + + lec[FIBSUser]->clear(); + lec[FIBSPswd]->clear(); + + + chatWindow->setupDefault(); + playerlist->setupDefault(); +} + +void KBgEngineFIBS::setupCancel() +{ + chatWindow->setupCancel(); + playerlist->setupCancel(); +} + +/* + * Called when the setup dialog is positively closed + */ +void KBgEngineFIBS::setupOk() +{ + // various options + showMsg = cbp->isChecked(); + whoisInvite = cbi->isChecked(); + + // connection information + for (int i = 0; i < NumFIBS; i++) + infoFIBS[i] = lec[i]->text(); + + // automatic messages + for (int i = 0; i < NumMsg; i++) { + useAutoMsg[i] = cbm[i]->isChecked(); + autoMsg[i] = lem[i]->text(); + } + + chatWindow->setupOk(); + playerlist->setupOk(); + + // save settings + saveConfig(); +} + +/* + * Puts the FIBS specific setup into the dialog nb + */ +void KBgEngineFIBS::getSetupPages(KDialogBase *nb) +{ + /* + * Main Widget + */ + QVBox *vbp = nb->addVBoxPage(i18n("FIBS Engine"), i18n("Here you can configure the FIBS backgammon engine"), + kapp->iconLoader()->loadIcon(PROG_NAME "_engine", KIcon::Desktop)); + + /* + * Get a multi page work space + */ + KTabCtl *tc = new KTabCtl(vbp, "fibs tabs"); + + /* + * FIBS, local options + */ + QWidget *w = new QWidget(tc); + QGridLayout *gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + /* + * Group boxes + */ + QGroupBox *gbo = new QGroupBox(i18n("Options"), w); + QGroupBox *gbm = new QGroupBox(i18n("Automatic Messages"), w); + + gl->addWidget(gbo, 0, 0); + gl->addWidget(gbm, 1, 0); + + /* + * Options + */ + cbp = new QCheckBox(i18n("Show copy of personal messages in main window"), gbo); + cbi = new QCheckBox(i18n("Automatically request player info on invitation"), gbo); + + QWhatsThis::add(cbp, i18n("Usually, all messages sent directly to you by other players " + "are displayed only in the chat window. Check this box if you " + "would like to get a copy of these messages in the main window.")); + QWhatsThis::add(cbi, i18n("Check this box if you would like to receive information on " + "players that invite you to games.")); + + cbp->setChecked(showMsg); + cbi->setChecked(whoisInvite); + + gl = new QGridLayout(gbo, 2, 1, 20); + gl->addWidget(cbp, 0, 0); + gl->addWidget(cbi, 1, 0); + + /* + * Automatic messages + */ + gl = new QGridLayout(gbm, NumMsg, 2, 20); + + cbm[MsgBeg] = new QCheckBox(i18n("Start match:"), gbm); + cbm[MsgWin] = new QCheckBox(i18n("Win match:"), gbm); + cbm[MsgLos] = new QCheckBox(i18n("Lose match:"), gbm); + + QWhatsThis::add(cbm[MsgBeg], i18n("If you want to send a standard greeting to your " + "opponent whenever you start a new match, check " + "this box and write the message into the entry " + "field.")); + QWhatsThis::add(cbm[MsgWin], i18n("If you want to send a standard message to your " + "opponent whenever you won a match, check this box " + "and write the message into the entry field.")); + QWhatsThis::add(cbm[MsgLos], i18n("If you want to send a standard message to your " + "opponent whenever you lost a match, check this box " + "and write the message into the entry field.")); + + for (int i = 0; i < NumMsg; i++) { + lem[i] = new QLineEdit(autoMsg[i], gbm); + gl->addWidget(cbm[i], i, 0); + gl->addWidget(lem[i], i, 1); + connect(cbm[i], SIGNAL(toggled(bool)), lem[i], SLOT(setEnabled(bool))); + cbm[i]->setChecked(useAutoMsg[i]); + lem[i]->setEnabled(useAutoMsg[i]); + QWhatsThis::add(lem[i], QWhatsThis::textFor(cbm[i])); + } + + /* + * Put the page into the notebook + */ + gl->activate(); + tc->addTab(w, i18n("&Local")); + + + /* + * FIBS, connection setup + */ + w = new QWidget(tc); + gl = new QGridLayout(w, 3, 1, nb->spacingHint()); + + QGroupBox *gbc = new QGroupBox(i18n("Server"), w); + QGroupBox *gbk = new QGroupBox(i18n("Other"), w); + + gl->addWidget(gbc, 0, 0); + gl->addWidget(gbk, 1, 0); + + /* + * Server box + */ + gl = new QGridLayout(gbc, 4, 2, 20); + + QLabel *lbc[NumFIBS]; + + lbc[FIBSHost] = new QLabel(i18n("Server name:"), gbc); + lbc[FIBSPort] = new QLabel(i18n("Server port:"), gbc); + lbc[FIBSUser] = new QLabel(i18n("User name:"), gbc); + lbc[FIBSPswd] = new QLabel(i18n("Password:"), gbc); + + for (int i = 0; i < NumFIBS; i++) { + lec[i] = new QLineEdit(infoFIBS[i], gbc); + gl->addWidget(lbc[i], i, 0); + gl->addWidget(lec[i], i, 1); + } + lec[FIBSPswd]->setEchoMode(QLineEdit::Password); + + QWhatsThis::add(lec[FIBSHost], i18n("Enter here the host name of FIBS. With almost " + "absolute certainty this should be \"fibs.com\". " + "If you leave this blank, you will be asked again " + "at connection time.")); + QWhatsThis::add(lec[FIBSPort], i18n("Enter here the port number of FIBS. With almost " + "absolute certainty this should be \"4321\". " + "If you leave this blank, you will be asked again " + "at connection time.")); + QWhatsThis::add(lec[FIBSUser], i18n("Enter your login on FIBS here. If you do not have a " + "login yet, you should first create an account using " + "the corresponding menu entry. If you leave this blank, " + "you will be asked again at connection time.")); + QWhatsThis::add(lec[FIBSPswd], i18n("Enter your password on FIBS here. If you do not have a " + "login yet, you should first create an account using " + "the corresponding menu entry. If you leave this blank, " + "you will be asked again at connection time. The password " + "will not be visible.")); + + /* + * Connection keepalive + */ + cbk = new QCheckBox(i18n("Keep connections alive"), gbk); + + QWhatsThis::add(cbk, i18n("Usually, FIBS drops the connection after one hour of inactivity. When " + "you check this box, %1 will try to keep the connection alive, even " + "if you are not actually playing or chatting. Use this with caution " + "if you do not have flat-rate Internet access.").arg(PROG_NAME)); + + cbk->setChecked(keepalive); + + gl = new QGridLayout(gbk, 1, 1, nb->spacingHint()); + gl->addWidget(cbk, 0, 0); + + /* + * Done with the page, put it in + */ + gl->activate(); + tc->addTab(w, i18n("&Connection")); + + /* + * Ask children for settings + */ + chatWindow->getSetupPages(tc, nb->spacingHint()); + playerlist->getSetupPages(tc, nb->spacingHint()); + + /* + * TODO: future extensions + */ + w = new QWidget(tc); + tc->addTab(w, i18n("&Buddy List")); +} + + +// == functions related to the invitation menu ================================= + +/* + * Remove a player from the invitation list in the join menu + */ +void KBgEngineFIBS::cancelJoin(const QString &info) +{ + QRegExp patt = QRegExp("^" + info + " "); + + for (int i = 0; i <= numJoin; i++) { + if (actJoin[i]->text().contains(patt)) { + // move all entries starting at i+1 up by one... + for (int j = i; j < numJoin; j++) + actJoin[j]->setText(actJoin[j+1]->text()); + actJoin[numJoin--]->unplug(joinMenu); + break; + } + } +} + +/* + * Parse the information in info for the purposes of the invitation + * submenu + */ +void KBgEngineFIBS::changeJoin(const QString &info) +{ + char name_p[100], name_o[100]; + float rate; + int expi; + + /* + * Extract the name of the player, her opponent, rating and experience. + * It is okay to use latin1(), since the string is coming from FIBS. + */ + sscanf(info.latin1(), "%99s %99s %*s %*s %*s %f %i %*s %*s %*s %*s %*s", + name_p, name_o, &rate, &expi); + + QString name = name_p; + QString oppo = name_o; + + QString rate_s; rate_s.setNum(rate); + QString expi_s; expi_s.setNum(expi); + + QRegExp patt = QRegExp("^" + name + " "); + + /* + * We have essentially two lists of names to check against: the ones + * that have invited us and are not yet in the menu and the ones that + * are already in the menu. + */ + + if (numJoin > -1 && oppo != "-") + cancelJoin(name); + + for (QStringList::Iterator it = invitations.begin(); it != invitations.end(); ++it) { + + if ((*it).contains(patt)) { + + QString text, menu; + + if ((*it).contains(QRegExp(" r$"))) { + menu = i18n("R means resume", "%1 (R)").arg(name); + text = i18n("%1 (experience %2, rating %3) wants to resume a saved match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name); + KNotifyClient::event("invitation", i18n("%1 wants to resume a saved match with you"). + arg(name)); + } else if ((*it).contains(QRegExp(" u$"))) { + menu = i18n("U means unlimited", "%1 (U)").arg(name); + text = i18n("%1 (experience %2, rating %3) wants to play an unlimited match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %4').").arg(name).arg(expi_s).arg(rate_s).arg(name); + KNotifyClient::event("invitation", i18n("%1 has invited you to an unlimited match"). + arg(name)); + } else { + QString len = (*it).right((*it).length() - name.length() - 1); + menu = i18n("If the format of the (U) and (R) strings is changed, it should also be changed here", + "%1 (%2)").arg(name).arg(len); + text = i18n("%1 (experience %2, rating %3) wants to play a %4 point match with you. " + "If you want to play, use the corresponding menu entry to join (or type " + "'join %5').").arg(name).arg(expi_s).arg(rate_s).arg(len).arg(name); + KNotifyClient::event("invitation", i18n("%1 has invited you for a %2 point match"). + arg(name).arg(len)); + } + emit serverString("rawwho " + name); // this avoids a race + if (whoisInvite) { + emit serverString("whois " + name); + emit infoText("" + text + ""); + } else + emit infoText("" + text + "
    "); + + for (int i = 0; i <=numJoin; i++) + actJoin[i]->unplug(joinMenu); + + if (++numJoin > 7) numJoin = 7; + + for (int i = numJoin; i > 0; i--) + actJoin[i]->setText(actJoin[i-1]->text()); + + actJoin[0]->setText(menu); + + for (int i = 0; i <= numJoin; i++) + actJoin[i]->plug(joinMenu); + + invitations.remove(it); + break; + } + } + + /* + * If there are entries in the menu, enable it + */ + menu->setItemEnabled(joinMenuID, numJoin > -1); +} + + +// == various slots and functions ============================================== + +/* + * Keep the connection alive. + */ +void KBgEngineFIBS::keepAlive() +{ + emit serverString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +} + +/* + * Several bookkeeping operations that have to be done at the + * end of every game. Some of these may or may not be necessary + * at a particular time, but they don't hurt either. + */ +void KBgEngineFIBS::endGame() +{ + playing = false; + + emit serverString("rawwho " + infoFIBS[FIBSUser]); + + actConti->setEnabled(false); + actLeave->setEnabled(false); + + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit allowCommand(Load, false); + emit allowCommand(Undo, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Roll, false); +} + +/* + * Toggle visibility of the player list + */ +void KBgEngineFIBS::showList() +{ + playerlist->isVisible() ? playerlist->hide() : playerlist->show(); +} + +/* + * Toggle visibility of the chat window + */ +void KBgEngineFIBS::showChat() +{ + chatWindow->isVisible() ? chatWindow->hide() : chatWindow->show(); +} + +/* + * Process the last move coming from the board + */ +void KBgEngineFIBS::handleMove(QString *s) +{ + lastMove = *s; + QString t = lastMove.left(1); + int moves = t.toInt(); + + emit allowCommand(Done, moves == toMove); + emit allowCommand(Undo, moves > 0); + + /* + * Allow undo and possibly start the commit timer + */ + redoPossible &= ((moves < toMove) && (undoCounter > 0)); + emit allowCommand(Redo, redoPossible); + if (moves == toMove && cl >= 0) { + emit allowMoving(false); + ct->start(cl, true); + } +} + +/* + * Done with the move + */ +void KBgEngineFIBS::done() +{ + // prevent the timer from expiring again + ct->stop(); + + // no more moves + emit allowMoving(false); + + // no more commands until it's our turn + emit allowCommand(Load, false); + emit allowCommand(Undo, false); + emit allowCommand(Done, false); + emit allowCommand(Cube, false); + emit allowCommand(Roll, false); + + // Transform the string to FIBS cormat + lastMove.replace(0, 2, "move "); + lastMove.replace(pat[PlsChar], "-"); + + // sent it to the server + emit serverString(lastMove); +} + +/* + * Undo the last move + */ +void KBgEngineFIBS::undo() +{ + ct->stop(); + + redoPossible = true; + ++undoCounter; + + emit allowMoving(true); + emit allowCommand(Done, false); + emit allowCommand(Redo, true); + emit undoMove(); +} + +/* + * Redo the last undone move + */ +void KBgEngineFIBS::redo() +{ + --undoCounter; + emit redoMove(); +} + +/* + * Double the cube - coming from the board + */ +void KBgEngineFIBS::doubleCube(const int w) +{ + if (playing && w == US) cube(); +} + +/* + * Roll the dice - coming from the board + */ +void KBgEngineFIBS::rollDice(const int w) +{ + if (playing && w == US) roll(); +} + +/* + * This engine passes all commands unmodified to the server + */ +void KBgEngineFIBS::handleCommand(QString const &cmd) +{ + emit serverString(cmd); +} + +/* + * If we have a connection, we don't quit right away + */ +bool KBgEngineFIBS::queryClose() +{ + if (connection->state() == QSocket::Idle) + return true; + + switch (KMessageBox::warningYesNoCancel((QWidget *)parent(),i18n("Still connected. Log out first?"),QString::null,i18n("Log Out"), i18n("Stay Connected"))) { + case KMessageBox::Yes : + disconnectFIBS(); + return true; + case KMessageBox::No : + return true; + default: // cancel + return false; + } +} + +/* + * If we have a connection, we don't quit right away + */ +bool KBgEngineFIBS::queryExit() +{ + if( kapp->sessionSaving()) + return true; + if (connection->state() != QSocket::Idle) + disconnectFIBS(); + return true; +} + +/* + * This displays a copy of personal messages in the main window. + * Normally, these only get displayed in the chat window. + */ +void KBgEngineFIBS::personalMessage(const QString &msg) +{ + if (showMsg) + emit infoText(msg); +} + + +// == slots and functions for FIBS commands ==================================== + +/* + * Accept the offer + */ +void KBgEngineFIBS::accept() +{ + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit serverString("accept"); +} + +/* + * Reject the offer + */ +void KBgEngineFIBS::reject() +{ + actAccept->setEnabled(false); + actReject->setEnabled(false); + + emit serverString("reject"); +} + +/* + * Continue a multi game match + */ +void KBgEngineFIBS::match_conti() +{ + actConti->setEnabled(false); + actLeave->setEnabled(false); + + emit serverString("join"); +} + +/* + * Leave a multi game match + */ +void KBgEngineFIBS::match_leave() +{ + actConti->setEnabled(false); + actLeave->setEnabled(false); + + emit serverString("leave"); +} + +/* + * Go away from the server for a little while. Offer the last know away + * message as a default to the user. + */ +void KBgEngineFIBS::away() +{ + bool ret; + QString msg = KLineEditDlg::getText(i18n("Please type the message that should be displayed to other\n" + "users while you are away."), + lastAway, &ret, (QWidget *)parent()); + if (ret) { + lastAway = msg; + emit serverString("away " + msg); + actAway->setEnabled(false); + } +} + +/* + * Toggle being ready for games + */ +void KBgEngineFIBS::toggle_ready() +{ + emit serverString("toggle ready"); +} + +/* + * Toggle the use of greedy bearoffs + */ +void KBgEngineFIBS::toggle_greedy() +{ + emit serverString("toggle greedy"); +} + +/* + * Toggle whether we will be asked to double/roll or not + */ +void KBgEngineFIBS::toggle_double() +{ + emit serverString("toggle double"); +} + +/* + * Toggle whether we want to see details on rating computations + */ +void KBgEngineFIBS::toggle_ratings() +{ + emit serverString("toggle ratings"); +} + +/* + * Come back after being away. + */ +void KBgEngineFIBS::back() +{ + emit serverString("back"); +} + +/* + * Double the cube + */ +void KBgEngineFIBS::cube() +{ + emit serverString("double"); +} + +/* + * Roll the dice + */ +void KBgEngineFIBS::roll() +{ + emit serverString("roll"); +} + +/* + * Reload the board + */ +void KBgEngineFIBS::load() +{ + emit serverString("board"); +} + +/* + * Handle the menu short cuts for joining. This is not as pretty as it + * could or should be, but it works and is easy to understand. + */ +void KBgEngineFIBS::join(const QString &msg) +{ + emit serverString("join " + msg.left(msg.find('('))); +} +void KBgEngineFIBS::join_0() { join(actJoin[0]->text()); } +void KBgEngineFIBS::join_1() { join(actJoin[1]->text()); } +void KBgEngineFIBS::join_2() { join(actJoin[2]->text()); } +void KBgEngineFIBS::join_3() { join(actJoin[3]->text()); } +void KBgEngineFIBS::join_4() { join(actJoin[4]->text()); } +void KBgEngineFIBS::join_5() { join(actJoin[5]->text()); } +void KBgEngineFIBS::join_6() { join(actJoin[6]->text()); } +void KBgEngineFIBS::join_7() { join(actJoin[7]->text()); } + + +// == invitation handling ====================================================== + +/* + * Show the invitation dialog and set the name to player + */ +void KBgEngineFIBS::inviteDialog() +{ + fibsRequestInvitation(""); +} + +/* + * Show the invitation dialog and set the name to player + */ +void KBgEngineFIBS::fibsRequestInvitation(const QString &player) +{ + if (!invitationDlg) { + QString p = player; + invitationDlg = new KBgInvite("invite"); + connect(invitationDlg, SIGNAL(inviteCommand(const QString &)), this, SLOT(handleCommand(const QString &))); + connect(invitationDlg, SIGNAL(dialogDone()), this, SLOT(invitationDone())); + } + invitationDlg->setPlayer(player); + invitationDlg->show(); +} + +/* + * Finish off the invitation dialog + */ +void KBgEngineFIBS::invitationDone() +{ + delete invitationDlg; + invitationDlg = 0; +} + + +// == connection handling ====================================================== + +/* + * Establish a connection to the server and log in if the parameter login + * is true. + */ +void KBgEngineFIBS::connectFIBS() +{ + /* + * Make sure the connection parameter are properly set. + */ + if (!queryConnection(false)) + return; + + conAction->setEnabled(false); + newAction->setEnabled(false); + disAction->setEnabled(false); + + /* + * Connect + */ + emit infoText(i18n("Looking up %1").arg(infoFIBS[FIBSHost])); + connection->connectToHost(infoFIBS[FIBSHost], infoFIBS[FIBSPort].toUShort()); + + return; +} + +/* + * Hostname has been resolved. + */ +void KBgEngineFIBS::hostFound() +{ + emit infoText(i18n("Connecting to %1").arg(infoFIBS[FIBSHost])); +} + +/* + * An error has occurred. Reset and inform the user. + */ +void KBgEngineFIBS::connError(int f) +{ + switch (f) { + case QSocket::ErrConnectionRefused: + emit infoText(i18n("Error, connection has been refused")); + break; + case QSocket::ErrHostNotFound: + emit infoText(i18n("Error, nonexistent host or name server down.")); + break; + case QSocket::ErrSocketRead: + emit infoText(i18n("Error, reading data from socket")); + break; + } + connectionClosed(); + return; +} + +void KBgEngineFIBS::readData() +{ + QString line; + while(connection->canReadLine()) { + line = connection->readLine(); + if (line.length() > 2) { + line.truncate(line.length()-2); + handleServerData(line); + } + } +} + +/* + * Transmit the string s to the server + */ +void KBgEngineFIBS::sendData(const QString &s) +{ + connection->writeBlock((s+"\r\n").latin1(),2+s.length()); +} + +/* + * Connection has been established. Log in and update the menus & actions. + */ +void KBgEngineFIBS::connected() +{ + conAction->setEnabled(false); + newAction->setEnabled(false); + disAction->setEnabled(true); + + menu->setItemEnabled( cmdMenuID, true); + menu->setItemEnabled(respMenuID, true); + menu->setItemEnabled(optsMenuID, true); + + /* + * Initialize the rx state machine + */ + rxStatus = RxConnect; + rxCollect = ""; + + /* + * Depending on whether somebody else wants to handle the login or not + */ + if (login) { + + /* + * Make sure the player list is empty when the whole list comes + * right after login + */ + playerlist->clear(); + + /* + * Login, using the autologin feature of FIBS, before we even receive anything. + */ + QString entry; + entry.setNum(CLIP_VERSION); + emit serverString(QString("login ") + PROG_NAME + "-" + PROG_VERSION + " " + entry + " " + + infoFIBS[FIBSUser] + " " + infoFIBS[FIBSPswd]); + + } else { + + emit serverString("guest"); + login = true; + + } + + /* + * Some visual feedback and done + */ + emit infoText(i18n("Connected") + "
    "); +} + +/* + * Create a new account on FIBS. Obviously, this will also create + * a connection. The actual login is handled in the message parsing + * state machine. + */ +void KBgEngineFIBS::newAccount() +{ + if (!queryConnection(true)) + return; + + rxStatus = RxNewLogin; + rxCollect = ""; + login = false; + connectFIBS(); +} + +/* + * Send a disconnection request to the server. The server will disconnect + * and we will receive a connectionClosed() signal. + */ +void KBgEngineFIBS::disconnectFIBS() +{ + // send two lines in case we are stuck in the login phase + emit serverString("quit"); + emit serverString("quit"); +} + +/* + * Connection to the server is closed for some (unknown) reason. Delete + * the connection object and get the actions into a proper state. + */ +void KBgEngineFIBS::connectionClosed() +{ + /* + * Read remaining input + */ + readData(); + + /* + * Flush whatever is left in the rxBuffer and send a note + */ + emit infoText(rxCollect + "

    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 literal 0 HcmV?d00001 diff --git a/doc/ksmiletris/icon.png b/doc/ksmiletris/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9bdec8a4aabe6a7105d05a0175d96599d4254fd5 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!%mREuT>t<7A26e&=l?dK0Kkfp5MwLFR z^80GuptexBz=C0kEW;L+Hw+R-TXY$=hm^- zaAtU&@6B+Q=|l9!!U@Ump%r(TIe4jPf{F3$4PxI$D|J9z_ So4kh;6i1$}elF{r5}E)OuFO^d literal 0 HcmV?d00001 diff --git a/doc/ksmiletris/index.docbook b/doc/ksmiletris/index.docbook new file mode 100644 index 00000000..53f330f0 --- /dev/null +++ b/doc/ksmiletris/index.docbook @@ -0,0 +1,456 @@ + + + KSmiletris"> + + + + + JohnHayes'> + justlinux@bellsouth.net'> + SandroSigala'> + ssigala@globalnet.it'> + Tetris"> +]> + + + + +The &ksmiletris; Handbook + + + +&John.Hayes; &John.Hayes.mail; + + + +&Sandro.Sigala; &Sandro.Sigala.mail; + + + +&Frerich.Raabe; &Frerich.Raabe.mail; + + + + + + +2005 +&John.Hayes; + + +2005-12-14 +3.5.0 + +&FDLNotice; + + + + + +&ksmiletris; is a &Tetris; like game. + + + + +KDE +kdegames +KSmileTris +Tetris + + + + + +Introduction + +&ksmiletris; is a game similar to the &Tetris; game. If you have ever played &Tetris;, you will find &ksmiletris; easy to learn and play. + + + + + +Game Play + + +Starting a New Game + +When you start &ksmiletris;, the game shows a blank game area. To start playing either select New from the Game menu or press the key combination &Ctrl;N. + + + + +Aim of the Game + +The aim of the game is to complete each level and get the highest score possible. You score by stacking or moving the similar tiles together as they fall, and then disappear. As the tiles disappear, your score increases. + + + + +Playing the Game + +By default, you can use the Left Arrow and Right Arrow keys to position the falling tiles where you want them to stack, and the Up Arrow or Down Arrow keys to rotate the tiles left or right respectively. The Spacebar drops the tile all the way to the bottom - use it as a way to save time. + +All of the shortcuts can be customized by selecting SettingsConfigure Shortcuts from the menu. + +Four of more of the same type of connected tiles will change to a broken or shattered look. When four or more of the connected tiles are shattered, they will disappear and all pieces above them will settle down to the rows below them. + +Every time a piece shatters, you get 10 points for each tile, you get 20 more points for each tile when it disappears. There are 1000 points per level. As the level increases, more types of tiles will be introduced which makes it more challenging to find fits and the speed at which the tiles fall increases as well. + +The game ends when the center column fills to the top with tiles, and no more can fall. + + + + +The Game Screen + + + + + + + + + &ksmiletris; in Action + + + + +A quick explanation of the parts of the game screen... + + + + + +Game Screen +The Game screen is at the top left and is the largest section of the window. + + + +Preview Screen +The Preview screen at the top right, gives you a preview of the block that will drop out next. + + + +Alignment Screen +The Alignment screen is located just below the game screen. As the tiles are falling, the two tiles that will be on the bottom are shown in the alignment screen in the columns that they are located in. This screen helps you see where the tiles will be located when they get to the bottom. + + + +Statusbar +The status bar is located at the very bottom of the screen, and shows the current level you are on and your score. Display of the Statusbar can be toggled on or off by the menu SettingsHide Statusbar. + + + + + + + + + +&ksmiletris; Configuration and Default Shortcuts + + +Configuring Gameplay + +The configuration options are as follows: + + +Hide/Show Statusbar + + + + +SettingsHide Statusbar + +Hides the Statusbar. + + + + +SettingsShow Statusbar + +Shows the Statusbar. + + + + + + + + +Appearance Configuration + +The game appearance configuration can be set from SettingsPieces. Your options are: + + + + +SettingsPiecesSmiles +Sets the tile appearance to Smileys. + + + + + + + + + + + +SettingsPiecesSymbols +Sets the tile appearance to Symbols. + + + + + + + + + + + +SettingsPiecesIcons +Sets the tile appearance to Icons. + + + + + + + + + + + + + + + +Sound Configuration + + + + +SettingsSounds +Toggles sound effects on or off. + + + + + + + + +Default Shortcuts + +SettingsConfigure Shortcuts +allows you to change the default keyboard shortcuts. The default shortcuts are as follows: + + + + +Spacebar +Drops the tile to the bottom fast. + + + +Up Arrow +Rotate tile to the left. + + + +Down Arrow +Rotate tile to the right. + + + +Left Arrow +Move tile to the left. + + + +Right Arrow +Move tile to the right. + + + +P +Pause or resume the game. + + + +&Ctrl;N +Start a new game. + + + +&Ctrl;End +End the game. + + + +&Ctrl;Q +Quit the game. + + + +&Ctrl;H +Show the high scores. + + + + + + + + + + + +Menu Reference + + +The Main &ksmiletris; Window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +&Ctrl;End + +Game +End + +Ends the current game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Displays the high scores. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &ksmiletris; + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +Settings +Show/Hide Statusbar + +Shows or hides the statusbar. + + + + +Settings +Pieces + +Lets you select from three different appearances for the tiles. Your options are Smileys, Symbols or Icons. + + + + +Settings +Sounds + +Toggle effects on or off. + + + + +Settings +Configure Shortcuts... + +Allows you to change the default shortcuts. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + + +Credits and License + +&ksmiletris; + +Program Copyright (c) 1998 &Sandro.Sigala; &Sandro.Sigala.mail;. + + +All rights reserved. + +Documentation copyright 2005 &John.Hayes; &John.Hayes.mail;. + + + +&underFDL; + +&underGPL; + + + + diff --git a/doc/ksmiletris/smiley.png b/doc/ksmiletris/smiley.png new file mode 100644 index 0000000000000000000000000000000000000000..a83c133e5d168dfa8bec7d188e3337e3418f8f13 GIT binary patch literal 1458 zcmc&z|7+BB7=QLfxXm48&cmeSG^8Tb>D#Y`cIS3Gr*)n4&}~1M*7oKUcJs?Y1TNcT zixI^lojfx2BV=KP=+Kt135v^*Ck85+5$eJ(2%=6mW@GmBdVk12L3+d8=bn3>_v_np z*OpAggN0KHi6~gHysQ$>h9R=`K+oCErfPP!bs;OTfCj3vWlgp9hT zHDoPWCM#ss&9Wi3#7r#2>aL0*u_R0)Bnl)_LuyHxR7g<(8_>YQWlXE%amO0N#)vWM zXi-^fEE_Avs#Ad`Tf=Nv4674`S+Pdgh!|05#8so#C>s@{V1;UcK{QlC7J48CHlTrp z%jyid8i%bFYt{J!Eh=WqVp*MCs6bQLida#&hbYWiWvgNp_K*i;;x^)de~5rXs0J8B zLnTI`1H`}vG_ceq=h~=ogjrpX@CRB{A}oqrVHYaUWEEEAh@=q!bYUDp!!4pj9*~LK zhy$8n7!IKtU=WR)kOdte1~#D8t;kKIjT%R>F$4*JphYFBZh`*#oeF=WZlC^=oG3qc zw_ZO=r_oQxZQD2OB>NuR@_kv(fzJeJ{2Z_)ga=PP6~6iJ(AB4(p8a!A&d|H~2VBWk zCRRUtw*QBLhN7)KFj=s9dG$7;@e|!EhxT?(#+NT;V^!JGhWh%tOqNQw)@{hv)rLRZ zm`#UM>AG;d;+^FE+8G(5oD~&i(NtB-&5o7N?<@&KPR7iKV#g!^i{ z(pN4G4ww%D#eX$=ebY{5@@6hwON-|UyQ+c`{QHLp%ICEw(#`FZ=QWp_(fsMI`2Vg^1b$n%L2ob(p#qge*boUSNZWp z6K)(joXZQN`sGiNfwFL>xMnP#zo%&Q^Yo1KovHT+hDR&g<~DpCgSfcF6vh~gX0*MPyIsgCw literal 0 HcmV?d00001 diff --git a/doc/ksmiletris/symbol.png b/doc/ksmiletris/symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..abd928139923352e6d035f971b398d3b4b68bd96 GIT binary patch literal 369 zcmeAS@N?(olHy`uVBq!ia0vp^4nXY4!3-qBUI%#sDXjpX5Z8bI{{01lKVb0t_y7O@ z89KHw-21<$z~=w*ihvmllV6X)HL9d*FTR9qthGDs_h?4$=>PxHweY_q<=P|CZ?i>w)|O?U~F!YIPDk zA2RG(%)r4g;lM6-m4-L<%ttPF+IyteGMwpSdLH@MVV`@#+9oE2a#lZuJN$)QPZ@T~ zJ5+3%z_7RfQ@9-ai9UAL344Jdx1n}T_FugNpFMXAmTlB;E6UaKF + + + + +]> + + + +The &ksnake; Handbook + + +Michel +Filippi + + + + + +2000 +Michel Filippi + + +&FDLNotice; + +2005-12-17 +0.4.0 + + + +Snake Race is a game of speed and agility. You are a hungry snake and are trying +to eat all the apples in the room before getting out! + + + + +KDE +game +linux +race + + + + + +Introduction + + +&ksnake; is a game of speed and agility. You are a hungry snake and are trying +to eat all the apples in the room before getting out! + + + + + +Rules of the Game + + +To Win + + +To win &ksnake; you must eat all the apples in the room and leave through the +exit that opens at the top. + + + + + +Each apple you eat makes you grow longer. + + + + +If you hit a wall, you die. + + + + +If you hit yourself, you die. + + + + +If you are hit in the head by a bouncing ball, you die. + + + + +If you take too long to eat the apples, more of them appear. + + + + + + + +Scoring + + +Before the timer has run out + + + +Before the timer has run out, you score: + + + + + +1 point for a red apple + a bonus depending on your skill level. + + + + +5 points for a gold apple + a bonus for your skill level + 2 points for each +computer snake + 2 points for each bouncing ball. + + + + +Twice your level number when you exit the room + a bonus depending on your skill +level. + + + + + + + +After the timer has run out + + +After the timer has run out, you score: + + + + + +1 point for a red apple. + + + + +2 points for a gold apple. + + + + +Points equal to your level number when you exit the room + a bonus depending on +your skill level. + + + + + + + +When the computer snake eats an object + + +When the computer snake eats an object you lose: + + + + + +2 points for a red apple. + + + + +5 points for a gold apple. + + + + + + + +Other points + + +You score 20 points for killing a computer snake. + + + + + + + + +Customization + + +Create Your Own Levels + + +To create your own level, copy one of the levels from $KDEDIR/share/apps/ksnake/levels to +$HOME/.kde/share/apps/ksnake and +edit it with a bitmap editor. You can then select it in +Settings Configure &ksnake;... + in the First Level tab. + + + + + + + +Menu Reference + + +The <guimenu>Game</guimenu> Menu + + + + + +&Ctrl;N + +Game +New + + +Start a new game + + + + + + +P + +Game +Pause + + +Pause the game. + + + + + + +&Ctrl;H + +Game +Show Highscores... + + +Show the High Score dialog. + + + + + + +&Ctrl;Q + +Game +Quit + + +Quits &kappname;. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + +SettingsShow Statusbar + + +Shows or hides the &ksnake; status bar at the bottom of the screen. + + + + +Settings +Configure Shortcuts... + +Select this to open a dialog which lets you define shortcuts +for all menu items in &ksnake;. + + + + +SettingsConfigure &ksnake;... + + +Opens the &ksnake; configuration dialog to change &ksnake; +settings. +On the General tab you can set the speed of snakes, +choose the number of snakes and balls and their behaviour. +Select a background color or an image on the Appearance tab. +Choose a starting level from 1 to 25 on the First Level tab. + + + + + + + + +The <guimenu>Help</guimenu> Menu + +&help.menu.documentation; + + + + + + +Credits and Licenses + + +&ksnake; copyright 2000, Michel Filippi + + + +Documentation copyright 2000, Michel Filippi and Robert Williams. + + + + +&underFDL; +&underGPL; + + + + diff --git a/doc/ksokoban/Makefile.am b/doc/ksokoban/Makefile.am new file mode 100644 index 00000000..368fc0da --- /dev/null +++ b/doc/ksokoban/Makefile.am @@ -0,0 +1,4 @@ + +KDE_DOCS = AUTO +KDE_LANG = en + diff --git a/doc/ksokoban/index.docbook b/doc/ksokoban/index.docbook new file mode 100644 index 00000000..93fbfbea --- /dev/null +++ b/doc/ksokoban/index.docbook @@ -0,0 +1,429 @@ + + + + + +]> + + + +The &ksokoban; Handbook + + + +Anders +Widell + +

    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 + + + + +]> + + + +&kpoker; Handbook + + +Jochen +Tuchbreiter + +

    yl8lAXsqX$dcHRTRlm&WcVydaYFQh3E|o(i z%<=JPxS~+?8FLt7v`->s5Z>STA`Ww6g|elI8T9Sjk161HZrTcIC>y!`CR&F_KWZn` z_@S#Sf%RD6y$AXzTb21Tp6Yj&!kT<=hiD!iYrbP~F>yM%!-Xw;;!VwIFU7-`naSLi_v`*={^CbIAg;mnjy1?S7(VKc^gsdHVwt{c6sb6GRGA8KdD z+-SjGT~2^Q!S;S)+4iI11H+}4Jmy*iki5temOVb=>eKNg|7)%+9kNaLjA&uV>&rKd zl$}VYx)!BX!eJ-Z?7}zg2&ee#?;91nCHhUTn4yd>Bxoic*Q}{Y#PFG+FKYUXe_JYj zAYTO8SWHB;kaq?Q55ykDR&92ybQKo>mGDyU?7lOQWref?pMkcoiFsi1UOB^}Q(GU$ zxloaRu+&f%14~2}>k|3ttXEEQvBNuw+{C$THGW3^`Z$(Y$aZ$O)Ld7at0;NqiYR%~ zf2~EFm7gQnUhH^h3vmSoSFChL5^Sn5xn_A zL*^^^4D@n(xcP|?-0c#c@>MT=t>O_7Pq^6Kc^EKWB|?jJ6mM}McwxuHxpICT6o-!N zzMU*0UMN^r)}ruQm_Osin}V24PpZ-Q;<}}QN^bn&n@gu#L{{NF+@yS~34f1jb=VVcmzvS}Dsc5e-A~p3!iXGjz1MMcMg4$ek zUDSSkEB#kpA(3NN@80u65g)r|wY?qSA0C;ZhsuQU1kq%|P1dku0XqR1?*w2jYIj#m zpsDbJq#XL;V4>q;Z=qUevE|V~`VB=@Fxnm_=`i7|onEM@3E24RgmG_$y81ze=-Sd} z%+d*`(CW*=qzc$-ysOdQDzuN3bkCTEwwyr%A5m|;W zNnPIcNZWR3{`aJf5~WXa`Bo;29g7{eHyOF$ik@SM38x(5>wXq!>&OblY`ieUnD&lz zm6^M)Sk`fjv2+Qxm6^H;J7zl#r7L${H>kSP>Y}tvN-xRk&rl#f@v1=UZB68?JQ%+{ z9Wbml#A>F@?KvsSAZzjl^!qVH=WG_bopPKzpK8B@r*Ejj4*?OEs5H+)9$a=;$8Guw zYYaa)iNK`jMBzV_4xzyX@yj5LRLZf!bM7Wb&9ti0Te8tc8FyxOiCeG~HkYv`n^h^} zeB447c9X5>8-WK>Pq@m>wHU2Q`@3r5*B6D!Uhg!L8K1A4tbVDi`kH89f>jvJ*HAvN zS;322{8H04w~LEi)nRMEY_NUt)jd7D?eiA_Y=u5VbD5OjQI4V=Aq1R(a*&nUq&K4J<`)in+s)%Yx%dIB|CHg zZ&th#xW^>fL2s;Ncdh#L`1}TWuY(;e%P~>&q_Cfxf>y?uz%rroJbG(g(8-2OUeW8V z(0iQDCcg8MK~%#T6a*lYe#D_)TPs&*-HhLAKx42~8g9s@sU(+=-_S@@Y%4K!BHlvR z%RU)cDXjwA+2)HSfmE98AWXMNYK=4$j zgte~tXrWq>&>>k}=4QqBr7D^Df)@y3+4M6U%nb%Xq8pREEw$~*fMz|U4Nc$NZud^pFlU#(DhMRqbL zLFui@5CmoN3VNfW2JT!LZ$Bzm;E;`&b8AJ&_+Q6X?0~e?JG@ao0WU1~UW#4PzbG~< zE8bYyN*9R|HjzvO{ZrD;1%*?LyUZ=&Err`_7p-NL}B+1nWk8^yA9IVR7 zi| zu^g;=;O%lfUi!ieZLE2rw1q2dr064p8QN*6ID`GS?qdcTSkM2g}{y=Q9@vkV;VYz^m!^^b=;5BT4+b1&cMa8w%d#sh)bkpFj8KbGm=8YFfax%eLvHBbtzXO80KRVY^ zx}H}$*&sg|w2d`aW*lj%MIJtvEQ@ycmA)XId+|oqqIVGEh4yB0r{l@k?4O`a_fS`q zHt~H{|1-&4l&X8R20vQWWHPINcgcnB3>?8|pW@q@ZVVg+L3*v_d|8J}8lF0eHVq)o z=1iB@z8UFVlbFyk1Nso^M%D%q-K!nP&-H5QN}ih}+(d1kUI8B2_w#LYFkdXmze0~( z-kED03ONr(J}L?XXrcq-yEc;#Y1AGb5#e+@*7I>7U(&-7@Uq$%T<@zk2%_wFU;4m@!qu$HOB3=t z{yLfG{QXxjcq^ww*U)&jQP}oD;00Z|YFK}t>p!jSeIYHhHCxe4@!LYjDzU;{okm^S zlYd?x5Vy5*a0>kx7cRC`q1PK*<}zWo?~%C^JM7yvJsc4fRn*D9n%Q>|GoE)OfCx%! za4zE~xXTn=V7YqXkMGxB66R5D`)0Xss%1RsH1<@AOVVqKnN69Xo!zZ0q;B z`s+olEG`n5Pc5ZgE8`aqPG^lqyWOXrf_D^1Ob)6Kx}S~xAVnF-&^o94Q0CRo-|Ll( z$$6IOk;OQ$LLx=UT!>OLM_sXvCAvCuAvE@dM~u&v`rNBQU*qJ4pilXIG&uxHd&*Cm z*u;McRfc5)4RgEoOR0mx)s!StO~G9dRC#ILL>}RaN)WNm#5OHH%LWyfw!sHi|L59& z@2gLq9!8lNh?Kefl1vr|`ua1)W8PSJ$9u;o018eGP)8opW~D#}_YVxnshB)NP0tWT zDT<(xrQj-xvj@PJ4u}p=tEw2My3<`)4*KGqCl+U~RPSF&ei<9XQ z`mVcKt=v61h`oV&FxxCcoq+L3^rf4uAKw3LzbN0K-@bRjqI=1^MUfKHm(4R8CTS2^Qw=e1yfl+_#Eq|KNWJ7%4kA-#2c2lPnq#(JWO%PH+rIwF4pCS%eJ zMuSBU5N)~*o>EfcVdLX)VbYlu^M4w@kOU9RGcT_rLtAX^splMODPz@hpkK#dGu-*gW5&P{xe6+RUZ}G7 z+m&~s9=j*1VT5|H_VEvQQ`u~t8y|ObavVF}^T{UTUL?5Tb0>aw61^w|M*9BNK6oQt zzR2!ne|UsXC^2&A`Ttb|aypX*#$#aPa%Yo76d_0@j7F3j90HSm=j`Ntqf%ChuFe@- zt8e|3=vfhy+F8r$=24(^1Spvx8nHhsoX?e4g;}UaaS<~jia%nQyb&KjH{Gfa9~RT{ zFZXZ580t`t<7ia^T>HnyyZIZ%qeLkb7M&4lrr}M^=F95YF5RJaKhEcYV6q`82X7SW zAQmh=@+pmI^gk_CNLYxM{@cdIuK6a}GLJCWbYHCNu<(wT&3EG6DLUs43sffD{QWz6 z5s2L%Td`e!&)0)5x<$IEp%xN<)nv|O+KqL}$vGsrn0K#S%Y=;rj*+LAi^aiQaut(m zT_yP%rp*H?u=<(%p-19+HtR#ful(41`li%1XY}|Vx&u7a?-_0{Kf_;0yv@SIKjqu* zaGk1kRN!s9kCAx^F3L=9h~i<79h*^$#i_)?!#b;cLWQG~1exW1$y=1cXY>)JDLzW; zbGi8v#(eYhof2<%alSvII7-JbC_ zKX1ZCZ|4D2YA5IQ;>C_ziL!@8%H9D+xqiLLI~~a5?kVMm-X99UxIbIh1S8c|eOxaI z+~b}PIByudzBOSHsi7_Y)N!ChGes-&tIL^RnQX(XzoHX@MUJYyesWH9q)V?+4BqH* zUEah8f+9{)FdjhG0t$T;%mWo##r#JQ41UJD13ISzZpU5b`u-EcK720>Y6%0w9#OHM zr?VGhHsE{F(Wn*1-_@C;<VkhoG>=(unHeGevA zha^-x_KP=bX`(YC0salYW{4!^$iOw2>|T309NYtbzzye_a^RbO(M`LuYuB|}?^4jU zkg|+(1FEky8x02oeN!F>lAA}io?3V`9i>{={xh8N#{PeV(`)M{ zCzwq7UBmoS8((k55-%&@_PcrHT{8jgd;=76(rzF0t)toiqkI9P2I4AJ_>ORw)+u>M z6yU~pAV}aK1+(&^h%-YNF6(>us+{P_?yR$4p0EV7ve{b}cNR59e&kc4H$1VxFO)b< zb8byPU(QmhN(=Kh@0!0TxYm<9{tJ-NJV6IH&fE9*`;6O@Iu2H?llF)14RSkOiTm6K z(D5UK?}FmQ23Ch9N!HI`@5o*!z>Ntd=p;O0|US|29GxMVBDXe{}_S&o!T2U;4oJC(XoL^7sVvCaoHVZS{TWrZe`cr1cCqp~VI#-p?q#jQlDb2be^}^zC(-S4M;1lV?YrQk!vB@lB>zw7C>U8Z}qx!r)Q~ zRWiI*t=DWwg&*eQEz^9zt!-stg*#I>wxP+6tso?1xH<0JxGb9Jbwt)DMgqQndz8ns z4|T(c()G>dKWq4(7My4PDt2lMxFdG8#UxYMHB1V{QIunw)F?Ejv*SvZAyrO1#NA&W1uu zh#eo3bjBKs+|M#zty@{z2u4J4i``ktd~EDd5V7{HOY|$5wwaAjXM z*^hI(Ztm3n`zT>~pubHLb0)W`(GzRf=NUafz3=~PM!xnpF0}Xj;{Y)800jOEO=stq z0?@bd2*tzs8z=n-8&BW}2fP=Vm#45|;hU{zM)I>=`BLl%7ywY}@JL>(U3_D-X5y1WzH{{J22FTXY5bnrv1M9e}U0| zRY4J*ZNBSKookBvMV1#%Z~Y@9_#pJC<1M4g$;UyrqY=9dtYg%QRrSy|>$n`iST&u$ zqJT@f4M_jtQyTW+zucEPaM!?n{b*XXB0;2a_z1Nh#3K+iUNcSGoG-YA*mszjmt@M8 zBj-Je?(i}h?KQc$#A9qQc_t=70S*-5vkL4&&M^o&$3NgSc_SRg87_a}yjEWjF{@1d z7a(eNW+<3=y^^AD>M+1jQ+wgA2RMxj5EJ$w(BwP{zI0`sqA)sJYpaUVFgx1bs(QnX ze6#HH$1_)|j~}Bf<+=b}nYil?MKEuuwKcQ>S5{^n2SzZ2Veb<*%EQ<%X#CBib|wiBjpU16zt5}!azy7zt1N5~JQrzk{Pm z8=G2JmUXQ9B}|;X*YOuv|n!WCVxo)$>`$%?~k7S|3*2f9^V=mOGTNaTuMa zHgZZYF7m=HGj#GgUNKAE(UB)FC%oGRuj7pp_l}$$=Ck5OH297uJt&V0AA2BnWynMb zbZn*y7Y5l)II&f{Ik&Qf2=AW)7{4CQYs(l{uSiDYHutr0Z41-{5Mf4 zMZOy%KmexT2S@Xh^8dhKe@hv!{~+SPWjX=vOz2L4Wdt81&zYwP03JLHcyK$=_=k_? zVnVcjPcH`FrKj%-c)}4AzFjQfnT6brH11>;^Lw=YcbtJ9ruGo201880Lr_e(J8Aep z^1JIU8Dx^9xc#9BwKs(d=ul=l^jaWM)>9jBp#u{qMRWQ1>F?wKKFZkO5rwKx00kA& zPV?l&l(qbi{9>a*Z257RHwtjkR98c|ojp|xaJtFg=u+mtfOiD{-$8x&t{5{n;F5Vx z{CkX%7~?%wV{7|J_d7Wv)VEPwCTE-`C&Q0DD#W{wX{=oeZt@`yonyv7T9Jdf*@J8$3cZ66Q##|cYl zNktN2%rf@H`%)x_xHbwoo>%2PoY^BRC7CsUkCf*~_0-JD*#l+XEhbGf%`op3rnP5J zI|4t%c@Z0Lwwmz7f0NU0k*+(rV()CH;xXbJg8zD!O<~2iWTOK-Y5gmt_0$I6X=QX% zT(49z;-98V@bI?gyZ2#!Jm1wvO}IX}=RkEEFWxNV!y^u%>Dxl-y`#>!FN?MlSGF!M zhskF@ls6@V_1S&;F;Y%_!(o=I3$BekLqz}0>`U|U|Cr9-G^nfR?_I*7M+HL?UMk)X&F8%CL#IcdiVboFi5cI?IsSmA+$$3)(zlpC=Q+>G1icE zM#a!@dKMpTYJM#M)y)n)O7$U94ddS>GIupPhZkq1qD$xc&*B)46(ZA;@MxZGUU>W{ zAMMmmnM>|S5CYFE`|{_2P(xlBHHIQk5xw&TKL0;UicU z-ar;}GK#2be!mM&KJUf!O0oa3=85xd%fZ!1uF)8)0n4Z=X z1Pf$g1sTfx8wQd(^`2SYlr0;nt(fc0;@WZof0RfWYh6Xvdz$LK#TQwDOZ;gAB zSFQfFS;P^pX8lebOXEMh|A?su?DF^R%}I*g-ANNWd^O`*LWPRV-z*F5Ko}38BvdNM zLbVYomVkLwu?I68n_^+#OiOJqGZ4=I76{wvX5@Ykg#Rt>_a%=tbZ2W{`xD;30dWj7 zhHf0*PJ*v6li=v*`xh044=4Z%cs&cwgaDiZRN=tp(lSL;HE8fevi%$g9Kfe%VC=~b z>)PO@9vhpG^j%v-7M%vTub5rH&`^}(k@x_1mfrcM26fIOK{t;``EQ3=9?mi0KzGlChQ}lz9&#Sw>i2Rkr6IRWMRF(TSckbwg z5ytuxQq3jwzkgV@Fw)xr(q-oL8+qRt2(wK7MdoDki-y{Hu!mXH*vz8#z4QoEpK_O? zRSU&>zVK{^`gbv)RnB?$a|Ec(MR)XGZ(dhAdoO%iXhW!T-}9dlYC;=*XA0_wN_- z*4@`4!cyIB*L<~4mO?rzDe1ucxrkBzlvvoKdFI}=<}I|PG((Eb<-mv z??jdVs*wH$mbCsWyhC(rVQ<%9Wq!IJY+BW94aJLDBJ**psx58yd_8FOml;{3QCALc zas%0@TkJE*(2f6*-O>(X@|GoTfuxxJ)%=tuk2iUBym)0>IQ&0+=-|J3N+CqKY6k#H zTWbAvC$HPFc5TD$4U#*c*ZapJVf{Q1Y&U4}eMowh; zA!fYHGua6@*aeVUH>wgSr3SUsgke7Rpt0@8eu@&&F)l&;qKlFs5IC?~bFg&Apds>) z)VUS&KU%Y1bPEw@j)Eyk+CK+(AuI5gO4sHL$6Rh~m>o9^@nY_7y(3pU(ih%Hr^k3* zWGiS_{RvWbbXFLQ8fZcl58womd`{kaRm6pa(6<=i7Ravc%I%&p9=VXbFD z9nyRPQp&6vUZ{dUA2PwWxu7l-*)-g{x%u!7OVDqC^t34E&Z=S(ug+P8z82<#2hZ2w z6{|bU)x=p<{VKrDk8-3+x=ednT3O^#KUFs+c#whM6H6i;AdD7xe%K&`6CAWY+*8J} p43ISQ=Q< + + + + +]> + + + +The &kenolaba; Handbook + + + +&Josef.Weidendorfer; + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + +2006-01-13 +1.06b + + +2001 +&Philip.Rodrigues; + + +&FDLNotice; + +&kenolaba; is a simple board strategy game that is +played by two players. + + + +KDE +game +kenolaba +linux + + + + +Introduction + +&kenolaba; is a simple board strategy game that is played by two +players. There are red and yellow pieces for each player. Beginning from +a position where each player has 14 pieces, moves are drawn until +one player has pushed 6 of his opponent's pieces out of the +board. + +The original program was developed in 1993 for DOS and pure +Xlib. For &kde; there was a major rewrite. + + If you know the board game called Abalone, you will like this +program. In fact, this program was inspired by the mentioned game. +Abalone is a trademark of Abalone SA, France. + + + + +Rules of the Game + +Red always moves first. + +Two types of moves are allowed: + + + +Normal +One, two or three of your pieces in a row can be moved by one +space in the 6 directions. Side moves are also allowed. + +Press the &LMB; on the first piece you want to move. +The piece will then be highlighted. Now drag the mouse in the desired +direction. If the move is valid, the cursor will change to an arrow in +that direction and all the pieces of the move will be highlighted. If +this is the move you want to draw, release the mouse button. + +Side moves are handled another way: For two adjacent pieces click between +them and drag; for three pieces press the &MMB; on the middle piece - if a side +move is allowed for the pieces they will be highlighted. Otherwise only the +middle one is highlighted and you draw a normal move. + + + + + +Pushing + +You can push a maximum of two pieces of the opponent in +front of your own pieces as long as the number of pieces you move is +greater than the number of the opponent's pieces that you push. That +means with three of your pieces you can push one or two of the +opponent's and with two pieces one. + +Special pushing moves are those which push a piece of the opponent +out of the board. If you have pushed six of your opponent's pieces out +of the board you have won. + + + + + +If you still do not know what &kenolaba; is all about, look at a +game where the computer plays both sides. After starting &kenolaba;, +select SettingsComputer +PlayBoth and start +a new game. + + + + +Network Play + +General Network Play + + +&kenolaba; supports playing across a network. This allows two people to +play against each other on different computers, or one person to observe +the game play of another &kenolaba; program. To use &kenolaba;'s network +features, &kenolaba; must be set to Network Mode by selecting +GameNetwork Play. + + + +All running &kenolaba; programs which are in Network Mode broadcast +changed positions to each other. + +To exchange positions, the &kenolaba; programs have to be told +about each other. If they are running on the same machine, nothing is +needed (apart from switching to Network Mode). If running on different +computers you have to specify the other's machine on the command line +with the switch, as so: + +%kenolaba SomeHostName + + For insiders: &kenolaba;, when in Network Mode, listens on +a TCP socket for position change commands by other +&kenolaba; programs. You can specify the port number with the + command line switch. You need this if you want to +play two different &kenolaba; Network games. + + +Examples + + + +Chris on machine1 and +Mary on machine2 want to +play against each other: Mary simply starts &kenolaba;, sets +OptionsComputer +Play to None and +switches to Network Mode. Chris starts &kenolaba; with + +%kenolaba machine2 + +and then does the same as Mary. +Now one of the two can start the game and draw a red move. Each time the +position is changed in one program, it automatically changes in the other one +too. + + + +John wants to observe the game: He does the same as Chris, but +doesn't draw a move himself. + + + +Two different &kenolaba; Network Games will run among machines +m1 and m2. To distinguish the games, we choose +port number 12345 for one. On m1 we start as usual: + +%kenolaba + +for the first game, and (not so usual): + +%kenolaba 12345 + +for the second. On m2 we +start the first game with: + +%kenolaba m1 + +and the second with: + +%kenolaba m1:12345 + +(The colon separates host and port as in a &URL;). + + + + + + + + +Advanced Options + + +Modify Mode + + +You can edit the current board position by selecting +EditModify +to put &kenolaba; in Modify Mode.You can add red or yellow pieces or +delete them. The status line shows the number of red and yellow pieces, +the move number, the side which is to draw the next move, and whether +the position is valid: An exclamation sign means no, a checked symbol +yes. + + + + +If the position is invalid and you switch back to normal Play Mode, you +can not play! Only valid positions can be used as a starting position for +a game. + + + +MoveTake Back + and +MoveForward + simply decrement and increment the move number, but don't change +any pieces. + + +Usage + +By pressing the left or +right mouse button on an empty or yellow +field, you enter Red piece Adding mode. By pressing the +middle button on an empty or red field, you +enter Yellow piece Adding mode. Finally by pressing the +left or the right +mouse button on a red piece, or the middle +mouse button on an yellow piece, you enter Piece Deleting +Mode. Any piece that the mouse passes over with the button pressed will +be deleted. + + +To exit Modify Mode and re-enter Play Mode, just uncheck +Modify in the Edit +menu. + + +Using the Clipboard + +The current &kenolaba; board can be copied to the clipboard using +the Copy command in the +Edit menu, and then pasted back in using +Paste. This way you can copy the board +positions of one &kenolaba; program to another by pressing +Copy in one program and +Paste in the other, but this is better done +with Network Mode (see above). Another usage is to save positions into a +text file (using Copy and your Editor of +choice) and retrieve a position later by selecting it in the editor and +pasting in &kenolaba;. + + + + + +Computer Level +The computer can play at four different levels, namely: + +Easy + +Normal + +Hard + +Challenge + + + +The harder the setting, the longer the computer searches for a +move. You can interrupt the search by pressing the S +key. + + + + +Spying + + +Spying can be turned on and off from the +Settings menu, and the Spy +option. If it is your turn, you can see the computer's rating of the +move you want to play in the status bar. If it's the computer's turn you +can see (in status bar and highlighted pieces) the move he actually +thinks is the best to play. Of course this changes along his +search. + + + +Selecting what color the computer plays + + +You can make the computer play Red, Yellow or both sides. Choose between +these options before starting a new game. Of course it works in a game +too if you want to change sides. Choose None +to play against another human. This is very useful in Network +Mode. + + + + + +Advanced Configuration + +&kenolaba; offers advanced configuration options for the daring, enabling +you to change the whole scoring system if you wish. To access these advanced +options, select SettingsConfigure +Kenolaba.... As you change settings, the score that +your modified settings give for the current move is displayed at the bottom of +the dialog, next to Evaluation of actual position:. + + +The <guilabel>Moves</guilabel> Tab +The number of points added to the total for each type of move can be +modified here, depending on the type of move, and how many pieces are +involved. The moves are divided into three types: + + + + +Normal +A normal move is one in which you move one or more pieces of +your own, but do not push any of your opponent's pieces. + + + + +Push +A push move is one in which you push one or more of your +opponent's pieces, but they remain on the board. + + + + +Push Out +A push out move is one in which you push one or more of your +opponent's pieces off the board. + + + + + + + + +The <guilabel>Position</guilabel> Tab + +The number of points added to the total score for a board position is +dependent on which ring on the board the pieces are on. For each ring, from the +center out, an average score to add can be set, with a +/- range. The score +given for a particular position is varied randomly within the +/- range. This +is to stop computer-computer games going into an infinite loop. + + + + +The <guilabel>In-A-Row</guilabel> Tab + +For each time the number of pieces in a row occurs, the given number of points +is added to the score, ⪚ + + + + + + X O O O + + +adds the score for three in a row, and also two times the score for two in a row. + + + +The <guilabel>Count</guilabel> Tab + +Adds the given amount of points based on the difference in the number of balls +on both sides, from 1 Ball more: up to 5 Balls +more:. If there is a difference of 6 balls, the game is over. + + + + +The <guilabel>Evaluation Schemes</guilabel> Tab + +Your evaluation schemes, defined in all other tabs of this dialog, +can be stored or deleted here. + + + + + +Menu Reference + +Drawing moves is explained under Rules of the Game. The buttons in the +toolbar have the same meaning as entries in the Game +or Edit menu. + + +The <guimenu>Game</guimenu> Menu + + + + +&Ctrl;N + + +Game +New + +Starts a new game even if a game is +currently in play. + + + + + + + +N + +GameNetwork Play + + + +If this toggle is switched on, the application is in Network Mode. + + + + + + + + +&Ctrl;Q + +GameQuit + + + +Quits &kenolaba;. + + + + + + + +The <guimenu>Edit</guimenu> Menu + + + + + + + +&Ctrl;C + +EditCopy + + +Copy a representation of the &kenolaba; board to the clipboard + + + + + + +&Ctrl;V + +EditPaste + + +Interpret the content of the X clipboard as an ASCII representation of +a &kenolaba; board, and copy it to the current board. A valid +ASCII representation is generated by Copy. + + + + + + +&Ctrl;Insert + +EditModify + + +Switches &kenolaba; to Modify Mode. + + + + + + +&Ctrl;S + +EditSave Position + + +Save the actual position to be retrieved later with +Edit Restore Position +. + + + + + + + +&Ctrl;O + +EditRestore Position + + +Restore the board position stored in the &kenolaba; Configuration file +by Edit Save +Position. + + + + + + + +The <guimenu>Move</guimenu> Menu + + + + + + + +H + +MoveHint + + + + +A hint for your next move is shown. Only possible if you play at level +normal or above, the move number is 2 or greater and you have not undone +your move. + + + + + + + +S + +Move +Stop Search + + + + +When the computer is thinking, his depth search is interrupted and he +draws the best move he has found so far. + + + + + + + +PagegUp + +MoveTake Back + + + + +Take back your previous move. Two (!) moves are undone: the opponents +move and your last move. So it's your turn again. 100 moves are +remembered; so don't hesitate to take back moves until you are at the +beginning of the game. When in Modify mode (see +EditModify +), decrement move number of this position. + + + + + + + +PageDown + +MoveForward + + + +Only useful in Modify mode: Increment move number of this +position. + + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl; M + + +Settings +Show Menubar + + + +Shows or hides the &kenolaba; menubar. + + + + + +SettingsShow Toolbar + + + +Shows or hides the &kenolaba; toolbar. + + + + + +SettingsShow Statusbar + + +Shows or hides the &kenolaba; status bar at the bottom of the screen. + + + + + +SettingsSave Settings + + + +Saves the current settings to become the default for &kenolaba;. + + + + + +SettingsChoose Game Type + + +Select the level that the computer plays at. Can be one of: + +Easy + +Normal + +Hard + +Challenge + + + + + + + + +SettingsComputer +Play + + +Choose which colors the computer plays. Can be one of: + + +Red + + +Yellow + + +Both + + +None + + + + + + + + +SettingsMove Slow + + A move drawn by the computer when this option is +checked is shown by blinking and highlighting the pieces used in this +move. Uncheck this option if you don't want to wait for this animation: +Then only a quick highlighting is done. + + + + + +SettingsRender Balls + + + +If checked, the pieces are rendered online by an internal simple ray +tracer when needed (⪚ when resizing the window). + + + + + +SettingsSpy + + +If checked, turns on spying. + + + + + +SettingsConfigure Shortcuts... + + +Brings up the standard &kde; shortcuts configuration dialog to customize the shortcuts used by &kenolaba;. + + + + + +SettingsConfigure Toolbars... + + +Brings up the standard &kde; toolbar configuration dialog to customize the &kenolaba; toolbar. + + + + + +SettingsConfigure &kenolaba;... + + +Opens the &kenolaba; configuration dialog to change &kenolaba; +settings. See . + + + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + +Command line options + + +kenolaba + + +MyPortNumber + + + +host:port + + + +With you specify the port number of the +listening TCP socket when in Network Mode. + +With you specify a remote &kenolaba; process +(with optional port number, when not using the default port on the +remote machine) to communicate with when in Network mode. You can +specify multiple remote processes. + + + +Credits & Licenses + +&kenolaba; + +Program copyright 1997-2000 &Josef.Weidendorfer; +&Josef.Weidendorfer.mail; + + +Original Documentation by Robert Williams +rwilliams@kde.org and &Josef.Weidendorfer; + +Documentation maintained by &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; +&underGPL; + + + + +Installation + +How to obtain &kenolaba; + +&install.intro.documentation; + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + + + + + + + + diff --git a/doc/kenolaba/kenolaba-pieces.png b/doc/kenolaba/kenolaba-pieces.png new file mode 100644 index 0000000000000000000000000000000000000000..58869a478a37ebc5dff2421c4b4f2f385c2c4602 GIT binary patch literal 2114 zcmV-I2)*}-P)ND-C#)E8btRH}j?Q_-fvp{Z2!kf?DXj;Z6stmAC5-dV5rGTzzUK6uyhI&o}- zE#dPxd*(Oa`R3bm&ddn9u2)&1>ADWUp+kpO>dMg2(29NP=30C3?T%4eRWD%h#0j20 zaRLB0AOlA2w}IL~`}+D;W$C((rfEDK4lf+Lm$}-(tj7GhF%At4(ciD#Hr*i0VARlo z0e#gm3M*B1zczoN$7PwhN|o`lMFEHj0z3Ud)Ue9n*eI!Lg_Efiu=1V$y5>rruImie z3Smvn!J7Gm%$!+eeB7c?fLKgm=R-l%sLJ5qM@gkBy#Bhrq9>$Msp@hUt|vtSia=Zt zcr+ZLbyF9!Mv5;J6J)FsQJ2E(+#H5g;;m%zo&))|m@j?!;XM;mPqd;yu?X=_fkz*& znVA${j!clrl!&${%x32>j1n)scyDvrX{2A%=n@3{qR4Q$j2CE^?fCrx9BvPy%|k%h@y>HTaI!$MX6L_*Y}@75G79QFYv5(r&(W51YOswOXr+=rSys-6+o5k zM7=&Fn}j)MQUwBDA8LCXB^<$=F|f=c{!QIn{Ksh$MjCC&KsJn7o1c3h5UG?wjo69C zd`OanS*)2sA8Is?;*DSy46ITSf4G~ApPeQ#kw*JLqgkK-@3}x;tWy);=EfwZW#Mu-a5u+j`^nD%nEd10T>j@7nxn0Vg1GFW@lDM2h;v!3&UPTP+9) zP!$gjj}HKcOF<2UXbr10MOv`?gA0&<=Of6nOnx@Y?jy(fMI^HD+gh3L?%lh-X1~U^ zZ8cK@HR8eHteH*)wK+s8hDS{cO4a_SoxH-PhO0!UVH)j*W$y2nIrwt5tGk3#)7)dwdH6 z0Fc8GtYU$JF-@gdTsU6H7&ttQ6|+|6(sf|1;;bhHAlBYQFdU*>u9C}HSeAwC^erSB zK(?S_r5ST!Hz=;LMrRIAU$s0wF|~LU{kn3+EMv z&yyb;rQ>G@Is4-SY*`fChA|(I_jjC8{wHjJs-86VG6E*IF^tI&SmeniQ|?DQ}?TEQFaM2#;o zuhh(TxRp#VBztjni%Y9|K-c-WkS8LDNI-rzi(r#*#M%&S61j8=V=6%*IfY%7 zX>x*&pB^N4b&T{^;~U#FD)c=bqgXTiGW2Y#W^7ulnG_`ET8|$NY6_khOw}{gpy~mBu z&S7_Z=>FwV05aFE(E7XIZd`Lf=Z!aNX28$z`4L=~#@PN~FP%GHK?&5%#CzPhb`HD4 zL-%)TX6g#9kvq+LTlR7NOPB}m~$p-bBxUgUd8j+ZiavL8b&I~=>EM8`_;8D zd;akI6}@#ck7;!GNIZ97A0;zK;_4TeMH4j|WAn~e@igsb`0Q&K(@A>bE1N4OyWS3K z8a<*&Z%DiuyuYg8H>`PBqo+rt_dyl6M?sQg5))HQr;0qkclD-_ sN~Kmz_G&*%!TNk#TuW~~%+-4T2X$xj*5;Xd$^ZZW07*qoM6N<$g5S>{Qvd(} literal 0 HcmV?d00001 diff --git a/doc/kenolaba/man-kenolaba.6.docbook b/doc/kenolaba/man-kenolaba.6.docbook new file mode 100644 index 00000000..a89a79fd --- /dev/null +++ b/doc/kenolaba/man-kenolaba.6.docbook @@ -0,0 +1,80 @@ + + +]> + + + +JanSchaumann +jschauma@netmeister.org +April 8, 2003 + + + +kenolaba +6 + + + +kenolaba +A &kde; based game + + + + +kenolaba + + hostname + port + + + + + + + +Description + +&kenolaba; is a simple board strategy game that is played by two +players. There are red and yellow pieces for each player. Beginning +from a start position where each player has 14 pieces, moves are drawn +until one player has pushed 6 of his opponent's pieces out of the +board. + + + + +Options + + + hostname +Use hostname for network game. + + + port +Use port for network game. + + + + + + +See Also + +More detailed user documentation is available from help:/kenolaba (either enter this +URL into &konqueror;, or run +khelpcenter +help:/kenolaba). + + + + + +Bugs + +&kenolaba; was written by JosephWeidendorferkde@jowenn.at +This man page is based on the one prepared by JanSchaumann jschauma@netmeister.org for the Missing Man Pages Project at http://www.netmeister.org/misc/m2p2/index.html. + + + diff --git a/doc/kfouleggs/Makefile.am b/doc/kfouleggs/Makefile.am new file mode 100644 index 00000000..085981d9 --- /dev/null +++ b/doc/kfouleggs/Makefile.am @@ -0,0 +1,4 @@ + +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kfouleggs/gamescreen.png b/doc/kfouleggs/gamescreen.png new file mode 100644 index 0000000000000000000000000000000000000000..4f510ddb73828d312609d9ad2ad3196a4e2ee2e8 GIT binary patch literal 8273 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 + + + + +]> + + + + +The &kfouleggs; Handbook + + + +&Philip.Rodrigues; &Philip.Rodrigues.mail; + + + +&Nicolas.Hadacek; &Nicolas.Hadacek.mail; + + + + +&Lauri.Watts; &Lauri.Watts.mail; + + + + + + + + +2000-2003 +&Philip.Rodrigues; + + +&FDLNotice; + +2006-06-19 +2.1.11 + +&kfouleggs; is a clone of the Japanese PuyoPuyo game for +the K Desktop Environment. + + +KDE +kdegames +KFoulEggs +PuyoPuyo + + + + + +Introduction + +&kfouleggs; is a clone of the Japanese PuyoPuyo game, with advanced +features such as multiplayer games against human or AI. +If you have played Tetris, or one of +its many clones, you will find &kfouleggs; easy to learn. + + + + + +Game Play + + +Starting a New Game + +When you start &kfouleggs;, just click on the Start + button, or select New from the +Game menu at any time to start a new game. + + + + +Aim of the Game + +The aim of the game is to achieve the highest score possible by +moving the falling tiles in such a way that tiles of the same color are +adjacent, and disappear. The more tiles you remove, the higher your +score. + + + + +Playing the Game + +Use the Left and Right arrow +keys to move the falling tile in the relevant direction, and the +Up arrow and Return keys to rotate the +tile left and right respectively. The Shift key drops +the tile down one line, and the Down key drops the tile +all the way down - use it as a way of saving time. + +All of these shortcuts can be customized by selecting +Configure Shortcuts... from the +Settings menu. + +If two or more tiles of the same color come to rest horizontally +or vertically next to each other, they become glued. If +four or more pieces of the same color come to be glued, +they disappear, and any pieces above them fall down according to the law +of gravity ;-). If this causes four or more pieces to become +glued, then they will also disappear, and so on. + +Every time you remove 100 tiles, you advance to the next level, +where the tiles fall faster, and the game is more difficult. + +The game ends when the central column fills with tiles, and no +more can fall. + + + + +The Game Screen + + + + + + + + + &kfouleggs; in Action + + + + +A quick explanation of what each of the parts of the game screen are for... + + + +Along the left hand side, from top to bottom: + + + + +Score +Shows your current score. + + + + + +Puyos +Shows the number of removed sets of pieces. + + + + +Level +Shows the number of the level you are currently on. The first, +easiest level is level one, progressing to the last, hardest level, 20. + + + + + + + +In the center is the area where the action takes place. The tiles fall through +the rectangle (whose properties can be changed in +>SettingsConfigure KFoulEggs... +, see ) until reaching the +bottom. The two small black rectangles under the main one are the tile's +shadow which show where it will land. You can toggle the tile's +shadow in . + + + +On the right hand side, the Next Tile box shows what the +next tile to fall will be, if you have enabled it in . + + + + + + + + +&kfouleggs; Configuration and Default Shortcuts + + +Configuring Gameplay + +The &kfouleggs; configuration is accessed from +Settings +Configure &kfouleggs;.... The +options are as follows: + + + +Game Configuration + + + + +Initial level: +Set the level which you will play at startup, from 1 (easiest) +to 20 (hardest). Default is level one. + + + +Direct drop down +If checked, pressing the Down Arrow will cause +a tile to immediately fall to the bottom of the screen. If unchecked, pressing +the Down Arrow only causes the piece to fall until the key is released. + + + + + + + + +Appearance Configuration + + + + +Enable animations +If checked, pieces are shown with an +animation (a small rebound effect), when they touch the bottom. + + +Show piece's shadow +If checked, a shadow is placed beneath the game board +showing where the piece will fall. + + + +Show next piece +If checked, shows the next tile that will fall onto the +game board. + + +Show detailed "removed lines" +field +If checked, the Puyos counter on the left +of the screen shows how many times each number of Puyos have been removed. If +unchecked, just the total number removed is shown. + + + + +Background +Select the color and the opacity for &kfouleggs; background. +An opacity of zero makes the &kfouleggs; background +completely transparent, and a setting of one makes the &kfouleggs; background +completely opaque. + + + + + + + +Colors Configuration +Here you can select the colors used for the tiles in &kfouleggs;. + + + +A.I. Configuration +Here you can configure the A.I. in &kfouleggs;. + + + + +Configuring Highscores + +The configuration is accessed from +Settings +Configure Highscores.... The +options are as follows: + + + +The <guilabel>Main</guilabel> Tab + + + + +Nickname: +Displays your current nickname and allows you to change it. + + + + +Comment +A comment about yourself. You choose... + + + + +World-wide highscores enabled +If checked and if you are connected to the Internet, +&kfouleggs; will send your score automatically at the end of the game +to the highscore web server (kfouleggs.sf.net). + + + + + + +The <guilabel>Advanced</guilabel> Tab + +This tab displays your Registration Data +on kfouleggs.sf.net: + + + +Nickname: +Displays your current nickname from the Main tab. + + + + +Key: +This key was generated when you registered on kfouleggs.sf.net by +selecting World-wide highscores enabled the first time. +The registration key is used in conjunction with the +nickname to identify uniquely users, but users cannot have the same nickname. +Click on the Remove button to delete you from the world highscores +list. + + + + + + + + +Default Shortcuts + +The default shortcuts for &kfouleggs; in all player modes are as +follows: + + + + + +New +&Ctrl;N + + +Pause +P + + +Quit +&Ctrl;Q + + +Zoom In +&Ctrl;+ + + +Zoom Out +&Ctrl;- + + +Show Highscores +&Ctrl;H + + +Show Menubar +&Ctrl;M + + +&kfouleggs; Handbook +F1 + + +What's This? +&Shift;F1 + + + + + +The default shortcuts for &kfouleggs; in one-player mode are as +follows: + + + + + +Rotate Left +Up Arrow + + +Rotate Right +Return + + +Move Left +Left Arrow + + +Move Right +Right Arrow + + +Move to Left Column +&Ctrl;Left Arrow + + +Move to Right Column +&Ctrl;Right Arrow + + +Drop Down +Down Arrow + + + + + + + + + +Multiplayer Play + + +General Information + +&kfouleggs; supports two local multiplayer types - Human vs. Human +and Human vs. Computer. To play either of these, select it from the +Multiplayer menu. + + +Multiplayer Games + +In Human vs. Human or Human vs. Computer mode, two gameboards are +shown, one for the first player and one for the second. Each has its own +Score, Puyos and +Level indicator. The keys for the first human +player become, by default: + + + + + +Rotate Left +E + + +Rotate Right +C + + +Move Left +F + + +Move Right +G + + +Move to Left Column +&Shift;F + + +Move to Right Column +&Shift;G + + +Drop Down +D + + +Drop One Line +Space + + + + + +The keys for the second human player are the same as for the +player in single player mode. + +To return to single player mode, select +Multiplayer and Single +Human. + + + + + + + + +Command Reference + + +The Main &kfouleggs; window + + +The <guimenu>Game</guimenu> Menu + + + + + + +&Ctrl;N + +Game +New + +Starts a new game. + + + + + +P + +Game +Pause + +Pauses or resumes the game + + + + + +&Ctrl;H + +Game +Show Highscores + +Opens a dialog that displays different high score tables. +Clicking on the links below the tables downloads world-wide scores. + + + + + +&Ctrl;Q + +Game +Quit + +Quits &kfouleggs; + + + + + + + +The <guimenu>View</guimenu> Menu + + + + + +&Ctrl;+ +View +Zoom In + +Enlarges the game board + + + + + +&Ctrl;- +View +Zoom Out + +Reduces the game board size + + + + + + + + +The <guimenu>Multiplayer</guimenu> Menu + + + + + +Multiplayer +Single Human + +Sets the multiplayer mode to single +player + + + + +Multiplayer +Human vs Human + +Sets the multiplayer mode to two player with two human +players. + + + + +Multiplayer +Human vs Computer + +Sets the multiplayer mode to two player with one human +and one computer player. + + + + +Multiplayer +More... + +Displays the multiplayer +settings dialog. + + + + + + + +The <guimenu>Settings</guimenu> Menu + + + + + +&Ctrl;M + +Settings +Show Menubar + +Shows or hides the Menubar.To return the +menubar, right-click anywhere on the gameboard and select Show +Menubar. + + + + +Settings +Configure Shortcuts... + +Displays a standard &kde; key bindings +configuration dialog to change the keyboard shortcuts for &kfouleggs;. + + + + +Settings +Configure Notifications... + +Displays a standard &kde; notifications +configuration dialog to change the audio and visual notifications for &kfouleggs;. + + + + +Settings +Configure Highscores... + +Displays the high score configuration dialog, +in which you can change several settings that affect how &kfouleggs; treats highscores. + + + + +Settings +Configure &kfouleggs;... + +Displays the &kfouleggs; +configuration dialog. + + + + + + + +The <guimenuitem>Help</guimenuitem> Menu + +&help.menu.documentation; + + + + + + + + + +Credits and License + +&kfouleggs; + +Program core engine copyright 1995 Eirik End. +Program copyright 1996-2001 &Nicolas.Hadacek; &Nicolas.Hadacek.mail;. + +Documentation copyright 2000-2003 &Philip.Rodrigues; &Philip.Rodrigues.mail;. + + + +&underFDL; + +&underGPL; + + + + +Installation + + +How to obtain &kfouleggs; + +&install.intro.documentation; + + +Compilation and Installation + +&install.compile.documentation; + +Should you run into problems please report them to the &kde; +mailing list, or the program maintainer, &Nicolas.Hadacek;, at +&Nicolas.Hadacek.mail;. + + + + + + + +&documentation.index; + + + + + diff --git a/doc/kgoldrunner/Makefile.am b/doc/kgoldrunner/Makefile.am new file mode 100644 index 00000000..41691557 --- /dev/null +++ b/doc/kgoldrunner/Makefile.am @@ -0,0 +1,3 @@ +KDE_LANG = en +KDE_DOCS = AUTO + diff --git a/doc/kgoldrunner/editbar.png b/doc/kgoldrunner/editbar.png new file mode 100644 index 0000000000000000000000000000000000000000..72c16150e344514a9737a6a43a8b4637a7d2debd GIT binary patch literal 13120 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 literal 0 HcmV?d00001 diff --git a/doc/kgoldrunner/index.docbook b/doc/kgoldrunner/index.docbook new file mode 100644 index 00000000..b420d16d --- /dev/null +++ b/doc/kgoldrunner/index.docbook @@ -0,0 +1,1674 @@ + +KGoldrunner"> + + + + +]> + + + + +The &kgoldrunner; Handbook + + +Ian +Wadham +