diff options
Diffstat (limited to 'libkdegames')
763 files changed, 28717 insertions, 0 deletions
diff --git a/libkdegames/Makefile.am b/libkdegames/Makefile.am new file mode 100644 index 00000000..89477c44 --- /dev/null +++ b/libkdegames/Makefile.am @@ -0,0 +1,23 @@ + +lib_LTLIBRARIES = libkdegames.la +libkdegames_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) -no-undefined -version-info 3:0:2 +libkdegames_la_LIBADD = highscore/libkhighscore.la kgame/libkgame.la kgame/dialogs/libkgamedialogs.la \ + $(LIB_KSYCOCA) $(LIB_KDNSSD) + +libkdegames_la_SOURCES = kcarddialog.cpp kstdgameaction.cpp \ + kgamemisc.cpp kchatbase.cpp kchat.cpp \ + kchatdialog.cpp kgameprogress.cpp \ + kcanvasrootpixmap.cpp kgamelcd.cpp +include_HEADERS = kgamemisc.h kcarddialog.h kstdgameaction.h \ + kchatbase.h kchat.h kchatdialog.h \ + kgameprogress.h kcanvasrootpixmap.h kgamelcd.h kgrid2d.h + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +SUBDIRS = carddecks highscore kgame pics + +messages: + $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + +include ../admin/Doxyfile.am diff --git a/libkdegames/README b/libkdegames/README new file mode 100644 index 00000000..fae0e7ee --- /dev/null +++ b/libkdegames/README @@ -0,0 +1,25 @@ +This directory contains the library for the kdegames packege. +It is a collection of functions used by some games or which +are useful for other games. + +Packagers note: it is recommended to put the directory "carddecks" into a separate +package, as not all games using libkdegames use the carddecks as well. + +Contents: + kcarddialog: Access to carddeck selection and administration + for cardgames + kstdgameaction: just like kstdaction this provides some default action for + kde games. games often use different entries than other apps + (like "game" instead of "file"), so use this if possible + kgamemisc: some static method i didn't know a good class name for. it + currently features "randomName()" which will just give you a random name from + a list (translators note: happy translating ;) i copied kde-common/accounts + for this so there are nearly 300 entries...) + kgame: this is a complete network/game handling library. it constists of a lot + of classes which are explained in the kgame docu (as soon as it is + written) +if you use libkdegames in your game please also add +KGlobal::locale()->insertCatalogue("libkdegames"); +to main() +This will add libkdegames.pot to your game and therefore all libkdegames +strings get translated. diff --git a/libkdegames/TODO b/libkdegames/TODO new file mode 100644 index 00000000..136c4fdd --- /dev/null +++ b/libkdegames/TODO @@ -0,0 +1,10 @@ +- 17.04.2001: change version number of the kdenonbeta one +- 10.07.2001: scaling has been added to KCard and KCardDialog. Find out if there + is a memory leak!! +- 10.07.2001: better layout for the resize box +- 10.07.2001: global decks/carddirs need extensive testing + +-These pertain to the highscore widget +The Ok button when adding a name should be enabled when the lineedit is filled with a QString of length() > 0 + +The lineedit by default should come up with the user name that is loged in. diff --git a/libkdegames/carddecks/Makefile.am b/libkdegames/carddecks/Makefile.am new file mode 100644 index 00000000..8effea93 --- /dev/null +++ b/libkdegames/carddecks/Makefile.am @@ -0,0 +1,24 @@ +DECKDIRS = cards-aisleriot cards-dondorf-whist-b cards-gdkcard-bonded cards-hard-a-port \ + cards-penguins cards-spaced cards-xskat-french cards-default decks cards-konqi-modern cards-warwick cards-xskat-german + +deckdir = $(kde_datadir)/carddecks + +uninstall-local: + rm -rf $(DESTDIR)$(deckdir) + +install-data-local: + @$(mkinstalldirs) $(DESTDIR)$(deckdir) + @for i in $(DECKDIRS); do \ + if test -d $(DESTDIR)$(deckdir)/$$i; then \ + echo "refreshing and removing $$i" ;\ + rm -rf $(DESTDIR)$(deckdir)/$$i;\ + fi ;\ + echo "installing $$i" ;\ + mkdir $(DESTDIR)$(deckdir)/$$i ;\ + files=`cd $(srcdir)/$$i && ls -1d *` ;\ + for f in $$files; do \ + if test -f $(srcdir)/$$i/$$f; then \ + $(INSTALL_DATA) $(srcdir)/$$i/$$f $(DESTDIR)$(deckdir)/$$i/$$f ;\ + fi \ + done \ + done diff --git a/libkdegames/carddecks/README b/libkdegames/carddecks/README new file mode 100644 index 00000000..9beb8a87 --- /dev/null +++ b/libkdegames/carddecks/README @@ -0,0 +1,12 @@ +This directory contains all carddecks for KDE cardgames. + +The backsides of the cards are stored as PNG images in +the directory decks as deck0.png, deck1.png, ... + +The cardsets with 52 cards are stored in the directories +cards1,cards2,cards3,... where each directory contains 52 +PNG images of the cards labels 1.png,2.png,...52.png + +Access to these cards works by using the kcardialog library +in libkdegames. See for documentation there. + diff --git a/libkdegames/carddecks/cards-aisleriot/1.png b/libkdegames/carddecks/cards-aisleriot/1.png Binary files differnew file mode 100644 index 00000000..d08a67d8 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/1.png diff --git a/libkdegames/carddecks/cards-aisleriot/10.png b/libkdegames/carddecks/cards-aisleriot/10.png Binary files differnew file mode 100644 index 00000000..f9f00749 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/10.png diff --git a/libkdegames/carddecks/cards-aisleriot/11.png b/libkdegames/carddecks/cards-aisleriot/11.png Binary files differnew file mode 100644 index 00000000..036807ec --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/11.png diff --git a/libkdegames/carddecks/cards-aisleriot/12.png b/libkdegames/carddecks/cards-aisleriot/12.png Binary files differnew file mode 100644 index 00000000..2945ea93 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/12.png diff --git a/libkdegames/carddecks/cards-aisleriot/13.png b/libkdegames/carddecks/cards-aisleriot/13.png Binary files differnew file mode 100644 index 00000000..86ac688c --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/13.png diff --git a/libkdegames/carddecks/cards-aisleriot/14.png b/libkdegames/carddecks/cards-aisleriot/14.png Binary files differnew file mode 100644 index 00000000..e5b64e0f --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/14.png diff --git a/libkdegames/carddecks/cards-aisleriot/15.png b/libkdegames/carddecks/cards-aisleriot/15.png Binary files differnew file mode 100644 index 00000000..3522414c --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/15.png diff --git a/libkdegames/carddecks/cards-aisleriot/16.png b/libkdegames/carddecks/cards-aisleriot/16.png Binary files differnew file mode 100644 index 00000000..098a50f4 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/16.png diff --git a/libkdegames/carddecks/cards-aisleriot/17.png b/libkdegames/carddecks/cards-aisleriot/17.png Binary files differnew file mode 100644 index 00000000..a4abb694 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/17.png diff --git a/libkdegames/carddecks/cards-aisleriot/18.png b/libkdegames/carddecks/cards-aisleriot/18.png Binary files differnew file mode 100644 index 00000000..b231456b --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/18.png diff --git a/libkdegames/carddecks/cards-aisleriot/19.png b/libkdegames/carddecks/cards-aisleriot/19.png Binary files differnew file mode 100644 index 00000000..d77ff9c8 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/19.png diff --git a/libkdegames/carddecks/cards-aisleriot/2.png b/libkdegames/carddecks/cards-aisleriot/2.png Binary files differnew file mode 100644 index 00000000..5d610da7 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/2.png diff --git a/libkdegames/carddecks/cards-aisleriot/20.png b/libkdegames/carddecks/cards-aisleriot/20.png Binary files differnew file mode 100644 index 00000000..bf9d586f --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/20.png diff --git a/libkdegames/carddecks/cards-aisleriot/21.png b/libkdegames/carddecks/cards-aisleriot/21.png Binary files differnew file mode 100644 index 00000000..7ae9b457 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/21.png diff --git a/libkdegames/carddecks/cards-aisleriot/22.png b/libkdegames/carddecks/cards-aisleriot/22.png Binary files differnew file mode 100644 index 00000000..86dee123 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/22.png diff --git a/libkdegames/carddecks/cards-aisleriot/23.png b/libkdegames/carddecks/cards-aisleriot/23.png Binary files differnew file mode 100644 index 00000000..0f6aba24 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/23.png diff --git a/libkdegames/carddecks/cards-aisleriot/24.png b/libkdegames/carddecks/cards-aisleriot/24.png Binary files differnew file mode 100644 index 00000000..5cf8d35c --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/24.png diff --git a/libkdegames/carddecks/cards-aisleriot/25.png b/libkdegames/carddecks/cards-aisleriot/25.png Binary files differnew file mode 100644 index 00000000..a46de767 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/25.png diff --git a/libkdegames/carddecks/cards-aisleriot/26.png b/libkdegames/carddecks/cards-aisleriot/26.png Binary files differnew file mode 100644 index 00000000..a21f883d --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/26.png diff --git a/libkdegames/carddecks/cards-aisleriot/27.png b/libkdegames/carddecks/cards-aisleriot/27.png Binary files differnew file mode 100644 index 00000000..724146ca --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/27.png diff --git a/libkdegames/carddecks/cards-aisleriot/28.png b/libkdegames/carddecks/cards-aisleriot/28.png Binary files differnew file mode 100644 index 00000000..f446432e --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/28.png diff --git a/libkdegames/carddecks/cards-aisleriot/29.png b/libkdegames/carddecks/cards-aisleriot/29.png Binary files differnew file mode 100644 index 00000000..81060669 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/29.png diff --git a/libkdegames/carddecks/cards-aisleriot/3.png b/libkdegames/carddecks/cards-aisleriot/3.png Binary files differnew file mode 100644 index 00000000..9bc9a017 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/3.png diff --git a/libkdegames/carddecks/cards-aisleriot/30.png b/libkdegames/carddecks/cards-aisleriot/30.png Binary files differnew file mode 100644 index 00000000..d506216a --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/30.png diff --git a/libkdegames/carddecks/cards-aisleriot/31.png b/libkdegames/carddecks/cards-aisleriot/31.png Binary files differnew file mode 100644 index 00000000..e6643631 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/31.png diff --git a/libkdegames/carddecks/cards-aisleriot/32.png b/libkdegames/carddecks/cards-aisleriot/32.png Binary files differnew file mode 100644 index 00000000..225d9c3e --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/32.png diff --git a/libkdegames/carddecks/cards-aisleriot/33.png b/libkdegames/carddecks/cards-aisleriot/33.png Binary files differnew file mode 100644 index 00000000..bb5bd4ec --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/33.png diff --git a/libkdegames/carddecks/cards-aisleriot/34.png b/libkdegames/carddecks/cards-aisleriot/34.png Binary files differnew file mode 100644 index 00000000..a5a0f38d --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/34.png diff --git a/libkdegames/carddecks/cards-aisleriot/35.png b/libkdegames/carddecks/cards-aisleriot/35.png Binary files differnew file mode 100644 index 00000000..72cca325 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/35.png diff --git a/libkdegames/carddecks/cards-aisleriot/36.png b/libkdegames/carddecks/cards-aisleriot/36.png Binary files differnew file mode 100644 index 00000000..8c805d93 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/36.png diff --git a/libkdegames/carddecks/cards-aisleriot/37.png b/libkdegames/carddecks/cards-aisleriot/37.png Binary files differnew file mode 100644 index 00000000..36746eee --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/37.png diff --git a/libkdegames/carddecks/cards-aisleriot/38.png b/libkdegames/carddecks/cards-aisleriot/38.png Binary files differnew file mode 100644 index 00000000..58de88ae --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/38.png diff --git a/libkdegames/carddecks/cards-aisleriot/39.png b/libkdegames/carddecks/cards-aisleriot/39.png Binary files differnew file mode 100644 index 00000000..879c0f78 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/39.png diff --git a/libkdegames/carddecks/cards-aisleriot/4.png b/libkdegames/carddecks/cards-aisleriot/4.png Binary files differnew file mode 100644 index 00000000..6b723627 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/4.png diff --git a/libkdegames/carddecks/cards-aisleriot/40.png b/libkdegames/carddecks/cards-aisleriot/40.png Binary files differnew file mode 100644 index 00000000..e20553ee --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/40.png diff --git a/libkdegames/carddecks/cards-aisleriot/41.png b/libkdegames/carddecks/cards-aisleriot/41.png Binary files differnew file mode 100644 index 00000000..9a023831 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/41.png diff --git a/libkdegames/carddecks/cards-aisleriot/42.png b/libkdegames/carddecks/cards-aisleriot/42.png Binary files differnew file mode 100644 index 00000000..363e44b2 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/42.png diff --git a/libkdegames/carddecks/cards-aisleriot/43.png b/libkdegames/carddecks/cards-aisleriot/43.png Binary files differnew file mode 100644 index 00000000..e2e7b08c --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/43.png diff --git a/libkdegames/carddecks/cards-aisleriot/44.png b/libkdegames/carddecks/cards-aisleriot/44.png Binary files differnew file mode 100644 index 00000000..aeb617f6 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/44.png diff --git a/libkdegames/carddecks/cards-aisleriot/45.png b/libkdegames/carddecks/cards-aisleriot/45.png Binary files differnew file mode 100644 index 00000000..fee4dba0 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/45.png diff --git a/libkdegames/carddecks/cards-aisleriot/46.png b/libkdegames/carddecks/cards-aisleriot/46.png Binary files differnew file mode 100644 index 00000000..f5948439 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/46.png diff --git a/libkdegames/carddecks/cards-aisleriot/47.png b/libkdegames/carddecks/cards-aisleriot/47.png Binary files differnew file mode 100644 index 00000000..0079b371 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/47.png diff --git a/libkdegames/carddecks/cards-aisleriot/48.png b/libkdegames/carddecks/cards-aisleriot/48.png Binary files differnew file mode 100644 index 00000000..62a487da --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/48.png diff --git a/libkdegames/carddecks/cards-aisleriot/49.png b/libkdegames/carddecks/cards-aisleriot/49.png Binary files differnew file mode 100644 index 00000000..87ede197 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/49.png diff --git a/libkdegames/carddecks/cards-aisleriot/5.png b/libkdegames/carddecks/cards-aisleriot/5.png Binary files differnew file mode 100644 index 00000000..8c5df649 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/5.png diff --git a/libkdegames/carddecks/cards-aisleriot/50.png b/libkdegames/carddecks/cards-aisleriot/50.png Binary files differnew file mode 100644 index 00000000..a8a01853 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/50.png diff --git a/libkdegames/carddecks/cards-aisleriot/51.png b/libkdegames/carddecks/cards-aisleriot/51.png Binary files differnew file mode 100644 index 00000000..9f2d83b9 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/51.png diff --git a/libkdegames/carddecks/cards-aisleriot/52.png b/libkdegames/carddecks/cards-aisleriot/52.png Binary files differnew file mode 100644 index 00000000..c5c754b3 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/52.png diff --git a/libkdegames/carddecks/cards-aisleriot/6.png b/libkdegames/carddecks/cards-aisleriot/6.png Binary files differnew file mode 100644 index 00000000..d399ebbb --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/6.png diff --git a/libkdegames/carddecks/cards-aisleriot/7.png b/libkdegames/carddecks/cards-aisleriot/7.png Binary files differnew file mode 100644 index 00000000..754240cd --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/7.png diff --git a/libkdegames/carddecks/cards-aisleriot/8.png b/libkdegames/carddecks/cards-aisleriot/8.png Binary files differnew file mode 100644 index 00000000..805854c0 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/8.png diff --git a/libkdegames/carddecks/cards-aisleriot/9.png b/libkdegames/carddecks/cards-aisleriot/9.png Binary files differnew file mode 100644 index 00000000..9c032e48 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/9.png diff --git a/libkdegames/carddecks/cards-aisleriot/COPYRIGHT b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT new file mode 100644 index 00000000..bfbeeca7 --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/COPYRIGHT @@ -0,0 +1,10 @@ +This PySol cardset was adapted from gnome-games 0.27 (aisleriot). +http://www.gnome.org + +Copyright (C) 1998 Jonathan Blandford <jrb@mit.edu> +Copyright (C) 1998 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at> + +This cardset 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/libkdegames/carddecks/cards-aisleriot/index.desktop b/libkdegames/carddecks/cards-aisleriot/index.desktop new file mode 100644 index 00000000..e70c235b --- /dev/null +++ b/libkdegames/carddecks/cards-aisleriot/index.desktop @@ -0,0 +1,20 @@ +[KDE Backdeck] +Name=AisleRiot +Name[af]=Aisleriot +Name[be]=Эслрыёт (пасьянсы) +Name[bg]=Айсълриот +Name[bn]=এইসলরায়োট +Name[cs]=Pozdvižení v uličce +Name[eo]=Simpla +Name[hi]=एस्लेरियॉट +Name[hr]=Pobuna u prolazu +Name[ne]=आइस्ले रिओट +Name[pa]=ਇਲਸੀਰਓਟ +Name[ru]=ЭшлРиот (коллекция пасьянсов) +Name[sv]=Kyrkouppror +Name[ta]= அய்ஸில்ரியாட் +Name[uk]=Заколот на човні +Name[vi]= AisleRiot +Name[zu]=I-AisleRiot +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-default/1.png b/libkdegames/carddecks/cards-default/1.png Binary files differnew file mode 100644 index 00000000..8f5de97f --- /dev/null +++ b/libkdegames/carddecks/cards-default/1.png diff --git a/libkdegames/carddecks/cards-default/10.png b/libkdegames/carddecks/cards-default/10.png Binary files differnew file mode 100644 index 00000000..6c7b87ea --- /dev/null +++ b/libkdegames/carddecks/cards-default/10.png diff --git a/libkdegames/carddecks/cards-default/11.png b/libkdegames/carddecks/cards-default/11.png Binary files differnew file mode 100644 index 00000000..a5938708 --- /dev/null +++ b/libkdegames/carddecks/cards-default/11.png diff --git a/libkdegames/carddecks/cards-default/12.png b/libkdegames/carddecks/cards-default/12.png Binary files differnew file mode 100644 index 00000000..4d43a730 --- /dev/null +++ b/libkdegames/carddecks/cards-default/12.png diff --git a/libkdegames/carddecks/cards-default/13.png b/libkdegames/carddecks/cards-default/13.png Binary files differnew file mode 100644 index 00000000..c7d51207 --- /dev/null +++ b/libkdegames/carddecks/cards-default/13.png diff --git a/libkdegames/carddecks/cards-default/14.png b/libkdegames/carddecks/cards-default/14.png Binary files differnew file mode 100644 index 00000000..2efb9b18 --- /dev/null +++ b/libkdegames/carddecks/cards-default/14.png diff --git a/libkdegames/carddecks/cards-default/15.png b/libkdegames/carddecks/cards-default/15.png Binary files differnew file mode 100644 index 00000000..8208b271 --- /dev/null +++ b/libkdegames/carddecks/cards-default/15.png diff --git a/libkdegames/carddecks/cards-default/16.png b/libkdegames/carddecks/cards-default/16.png Binary files differnew file mode 100644 index 00000000..73c5fce5 --- /dev/null +++ b/libkdegames/carddecks/cards-default/16.png diff --git a/libkdegames/carddecks/cards-default/17.png b/libkdegames/carddecks/cards-default/17.png Binary files differnew file mode 100644 index 00000000..611b7aff --- /dev/null +++ b/libkdegames/carddecks/cards-default/17.png diff --git a/libkdegames/carddecks/cards-default/18.png b/libkdegames/carddecks/cards-default/18.png Binary files differnew file mode 100644 index 00000000..8b7d1a1e --- /dev/null +++ b/libkdegames/carddecks/cards-default/18.png diff --git a/libkdegames/carddecks/cards-default/19.png b/libkdegames/carddecks/cards-default/19.png Binary files differnew file mode 100644 index 00000000..0a9dac18 --- /dev/null +++ b/libkdegames/carddecks/cards-default/19.png diff --git a/libkdegames/carddecks/cards-default/2.png b/libkdegames/carddecks/cards-default/2.png Binary files differnew file mode 100644 index 00000000..b0c67169 --- /dev/null +++ b/libkdegames/carddecks/cards-default/2.png diff --git a/libkdegames/carddecks/cards-default/20.png b/libkdegames/carddecks/cards-default/20.png Binary files differnew file mode 100644 index 00000000..0657a091 --- /dev/null +++ b/libkdegames/carddecks/cards-default/20.png diff --git a/libkdegames/carddecks/cards-default/21.png b/libkdegames/carddecks/cards-default/21.png Binary files differnew file mode 100644 index 00000000..fe376ba1 --- /dev/null +++ b/libkdegames/carddecks/cards-default/21.png diff --git a/libkdegames/carddecks/cards-default/22.png b/libkdegames/carddecks/cards-default/22.png Binary files differnew file mode 100644 index 00000000..2a0e5d30 --- /dev/null +++ b/libkdegames/carddecks/cards-default/22.png diff --git a/libkdegames/carddecks/cards-default/23.png b/libkdegames/carddecks/cards-default/23.png Binary files differnew file mode 100644 index 00000000..52403931 --- /dev/null +++ b/libkdegames/carddecks/cards-default/23.png diff --git a/libkdegames/carddecks/cards-default/24.png b/libkdegames/carddecks/cards-default/24.png Binary files differnew file mode 100644 index 00000000..5af94ec7 --- /dev/null +++ b/libkdegames/carddecks/cards-default/24.png diff --git a/libkdegames/carddecks/cards-default/25.png b/libkdegames/carddecks/cards-default/25.png Binary files differnew file mode 100644 index 00000000..cc437aea --- /dev/null +++ b/libkdegames/carddecks/cards-default/25.png diff --git a/libkdegames/carddecks/cards-default/26.png b/libkdegames/carddecks/cards-default/26.png Binary files differnew file mode 100644 index 00000000..d034f06f --- /dev/null +++ b/libkdegames/carddecks/cards-default/26.png diff --git a/libkdegames/carddecks/cards-default/27.png b/libkdegames/carddecks/cards-default/27.png Binary files differnew file mode 100644 index 00000000..a23fe6ff --- /dev/null +++ b/libkdegames/carddecks/cards-default/27.png diff --git a/libkdegames/carddecks/cards-default/28.png b/libkdegames/carddecks/cards-default/28.png Binary files differnew file mode 100644 index 00000000..dcccaac2 --- /dev/null +++ b/libkdegames/carddecks/cards-default/28.png diff --git a/libkdegames/carddecks/cards-default/29.png b/libkdegames/carddecks/cards-default/29.png Binary files differnew file mode 100644 index 00000000..e19070bd --- /dev/null +++ b/libkdegames/carddecks/cards-default/29.png diff --git a/libkdegames/carddecks/cards-default/3.png b/libkdegames/carddecks/cards-default/3.png Binary files differnew file mode 100644 index 00000000..1854faba --- /dev/null +++ b/libkdegames/carddecks/cards-default/3.png diff --git a/libkdegames/carddecks/cards-default/30.png b/libkdegames/carddecks/cards-default/30.png Binary files differnew file mode 100644 index 00000000..d9b285c0 --- /dev/null +++ b/libkdegames/carddecks/cards-default/30.png diff --git a/libkdegames/carddecks/cards-default/31.png b/libkdegames/carddecks/cards-default/31.png Binary files differnew file mode 100644 index 00000000..b77a5d8d --- /dev/null +++ b/libkdegames/carddecks/cards-default/31.png diff --git a/libkdegames/carddecks/cards-default/32.png b/libkdegames/carddecks/cards-default/32.png Binary files differnew file mode 100644 index 00000000..0e0649bf --- /dev/null +++ b/libkdegames/carddecks/cards-default/32.png diff --git a/libkdegames/carddecks/cards-default/33.png b/libkdegames/carddecks/cards-default/33.png Binary files differnew file mode 100644 index 00000000..d1344b0f --- /dev/null +++ b/libkdegames/carddecks/cards-default/33.png diff --git a/libkdegames/carddecks/cards-default/34.png b/libkdegames/carddecks/cards-default/34.png Binary files differnew file mode 100644 index 00000000..9ff5f5aa --- /dev/null +++ b/libkdegames/carddecks/cards-default/34.png diff --git a/libkdegames/carddecks/cards-default/35.png b/libkdegames/carddecks/cards-default/35.png Binary files differnew file mode 100644 index 00000000..b247d95e --- /dev/null +++ b/libkdegames/carddecks/cards-default/35.png diff --git a/libkdegames/carddecks/cards-default/36.png b/libkdegames/carddecks/cards-default/36.png Binary files differnew file mode 100644 index 00000000..f5284e87 --- /dev/null +++ b/libkdegames/carddecks/cards-default/36.png diff --git a/libkdegames/carddecks/cards-default/37.png b/libkdegames/carddecks/cards-default/37.png Binary files differnew file mode 100644 index 00000000..e4a2fa70 --- /dev/null +++ b/libkdegames/carddecks/cards-default/37.png diff --git a/libkdegames/carddecks/cards-default/38.png b/libkdegames/carddecks/cards-default/38.png Binary files differnew file mode 100644 index 00000000..859e4a52 --- /dev/null +++ b/libkdegames/carddecks/cards-default/38.png diff --git a/libkdegames/carddecks/cards-default/39.png b/libkdegames/carddecks/cards-default/39.png Binary files differnew file mode 100644 index 00000000..b4392112 --- /dev/null +++ b/libkdegames/carddecks/cards-default/39.png diff --git a/libkdegames/carddecks/cards-default/4.png b/libkdegames/carddecks/cards-default/4.png Binary files differnew file mode 100644 index 00000000..136c3100 --- /dev/null +++ b/libkdegames/carddecks/cards-default/4.png diff --git a/libkdegames/carddecks/cards-default/40.png b/libkdegames/carddecks/cards-default/40.png Binary files differnew file mode 100644 index 00000000..2a30304a --- /dev/null +++ b/libkdegames/carddecks/cards-default/40.png diff --git a/libkdegames/carddecks/cards-default/41.png b/libkdegames/carddecks/cards-default/41.png Binary files differnew file mode 100644 index 00000000..2c619dc3 --- /dev/null +++ b/libkdegames/carddecks/cards-default/41.png diff --git a/libkdegames/carddecks/cards-default/42.png b/libkdegames/carddecks/cards-default/42.png Binary files differnew file mode 100644 index 00000000..5200e878 --- /dev/null +++ b/libkdegames/carddecks/cards-default/42.png diff --git a/libkdegames/carddecks/cards-default/43.png b/libkdegames/carddecks/cards-default/43.png Binary files differnew file mode 100644 index 00000000..ebd6db3f --- /dev/null +++ b/libkdegames/carddecks/cards-default/43.png diff --git a/libkdegames/carddecks/cards-default/44.png b/libkdegames/carddecks/cards-default/44.png Binary files differnew file mode 100644 index 00000000..8cdcd68f --- /dev/null +++ b/libkdegames/carddecks/cards-default/44.png diff --git a/libkdegames/carddecks/cards-default/45.png b/libkdegames/carddecks/cards-default/45.png Binary files differnew file mode 100644 index 00000000..9297d188 --- /dev/null +++ b/libkdegames/carddecks/cards-default/45.png diff --git a/libkdegames/carddecks/cards-default/46.png b/libkdegames/carddecks/cards-default/46.png Binary files differnew file mode 100644 index 00000000..1b47218e --- /dev/null +++ b/libkdegames/carddecks/cards-default/46.png diff --git a/libkdegames/carddecks/cards-default/47.png b/libkdegames/carddecks/cards-default/47.png Binary files differnew file mode 100644 index 00000000..8fd3853d --- /dev/null +++ b/libkdegames/carddecks/cards-default/47.png diff --git a/libkdegames/carddecks/cards-default/48.png b/libkdegames/carddecks/cards-default/48.png Binary files differnew file mode 100644 index 00000000..01eea71b --- /dev/null +++ b/libkdegames/carddecks/cards-default/48.png diff --git a/libkdegames/carddecks/cards-default/49.png b/libkdegames/carddecks/cards-default/49.png Binary files differnew file mode 100644 index 00000000..d6860e04 --- /dev/null +++ b/libkdegames/carddecks/cards-default/49.png diff --git a/libkdegames/carddecks/cards-default/5.png b/libkdegames/carddecks/cards-default/5.png Binary files differnew file mode 100644 index 00000000..64029ed0 --- /dev/null +++ b/libkdegames/carddecks/cards-default/5.png diff --git a/libkdegames/carddecks/cards-default/50.png b/libkdegames/carddecks/cards-default/50.png Binary files differnew file mode 100644 index 00000000..79c1ed05 --- /dev/null +++ b/libkdegames/carddecks/cards-default/50.png diff --git a/libkdegames/carddecks/cards-default/51.png b/libkdegames/carddecks/cards-default/51.png Binary files differnew file mode 100644 index 00000000..f396b946 --- /dev/null +++ b/libkdegames/carddecks/cards-default/51.png diff --git a/libkdegames/carddecks/cards-default/52.png b/libkdegames/carddecks/cards-default/52.png Binary files differnew file mode 100644 index 00000000..dcdc90e5 --- /dev/null +++ b/libkdegames/carddecks/cards-default/52.png diff --git a/libkdegames/carddecks/cards-default/6.png b/libkdegames/carddecks/cards-default/6.png Binary files differnew file mode 100644 index 00000000..1749b472 --- /dev/null +++ b/libkdegames/carddecks/cards-default/6.png diff --git a/libkdegames/carddecks/cards-default/7.png b/libkdegames/carddecks/cards-default/7.png Binary files differnew file mode 100644 index 00000000..2d33965c --- /dev/null +++ b/libkdegames/carddecks/cards-default/7.png diff --git a/libkdegames/carddecks/cards-default/8.png b/libkdegames/carddecks/cards-default/8.png Binary files differnew file mode 100644 index 00000000..165bd76b --- /dev/null +++ b/libkdegames/carddecks/cards-default/8.png diff --git a/libkdegames/carddecks/cards-default/9.png b/libkdegames/carddecks/cards-default/9.png Binary files differnew file mode 100644 index 00000000..9860c535 --- /dev/null +++ b/libkdegames/carddecks/cards-default/9.png diff --git a/libkdegames/carddecks/cards-default/index.desktop b/libkdegames/carddecks/cards-default/index.desktop new file mode 100644 index 00000000..63894a1b --- /dev/null +++ b/libkdegames/carddecks/cards-default/index.desktop @@ -0,0 +1,115 @@ +[KDE Backdeck] +Name=Standard +Name[af]=Standaard +Name[az]=Standart +Name[be]=Звычайная +Name[bg]=Стандарт +Name[bn]=প্রমিত +Name[br]=Reoliek +Name[ca]=Estàndard +Name[cs]=Standardní +Name[cy]=Safonol +Name[eo]=Normala +Name[es]=Estándar +Name[eu]=Estandarra +Name[fa]=استاندارد +Name[fi]=Normaali +Name[ga]=Caighdeánach +Name[gl]=Estándar +Name[hi]=मानक +Name[is]=Staðlað +Name[ja]=標準 +Name[km]=ខ្នាតគំរូ +Name[ko]=표준 +Name[lt]=Standartas +Name[lv]=Standarta +Name[mk]=Стандард +Name[ne]=मानक +Name[nl]=Standaard +Name[nso]=Lekanetse +Name[pa]=ਮਿਆਰ +Name[pt]=Normal +Name[pt_BR]=Padrão +Name[ru]=Стандарт +Name[se]=Standárda +Name[sk]=Štandard +Name[sr]=Стандардни +Name[sr@Latn]=Standardni +Name[ta]= தரம் +Name[tg]=Низоммеъёр +Name[th]=มาตรฐาน +Name[tr]=Standart +Name[uk]=Типові +Name[uz]=Andoza +Name[uz@cyrillic]=Андоза +Name[ven]=Murole +Name[vi]=Tiêu chuẩn +Name[wa]=Sitandård +Name[xh]=Umgangatho +Name[zh_CN]=标准 +Name[zh_TW]=標準 +Name[zu]=Okulingeneyo +Preview=11.png +PySol=false +Comment=Standard KDE card set\nGPL license +Comment[af]=Standaard Kde kaart stel\nGPL lisensie +Comment[az]=Standart KDE kart dəstəsi\nGPL license +Comment[be]=Звычайная калода картаў KDE\nGPL license +Comment[bg]=Стандартна колода карти KDE\nОПЛ лиценз(GPL) +Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট\nজি.পি.এল. লাইসেন্স +Comment[bs]=Standardni KDE špil karata\nGPL license +Comment[ca]=Joc de cartes estàndard per al KDE sota\nLlicència GPL +Comment[cs]=Standardní sada karet KDE\nGPL licence +Comment[cy]=Trwydded GPL safonol\nar gyfer set cerdiau KDE +Comment[da]=Standard KDE-kortspil\nGPL-licens +Comment[de]=Standardmäßige KDE-Karten\nGPL-Lizenz +Comment[el]=Προκαθορισμένο σετ καρτών του KDE\nΆδεια GPL +Comment[en_GB]=Standard KDE card set\nGPL licence +Comment[eo]=Normala KDEa kartaro\nGPL licenco +Comment[es]=Juego de cartas estándar de KDE\nLicencia GPL +Comment[et]=Standardne KDE kaardikomplekt\nGPL litsents +Comment[eu]=KDE-ren karta-sorta estandarra\nGPL lizentzia +Comment[fa]=مجموعه استاندارد کارت KDE\مجوز nGPL +Comment[fi]=Normaali KDE:n korttipakka\nGPL lisenssi +Comment[fr]=Jeu de cartes standard de KDE\nLicence GPL +Comment[gl]=Baralla de cartas estándar de KDE\nGPL license +Comment[he]=ערכת הקלפים הסטנדרטית של KDE\nרישיון GPL +Comment[hi]=मानक केडीई ताश सेट\nजीपीएल अनुज्ञापत्र +Comment[hr]=Standardni KDE komplet karata\nGPL licenca +Comment[hu]=Standard KDE kártyacsomag\n(GPL licencű) +Comment[is]=Venjulegi KDE spilastokkurinn\nGPL leyfi +Comment[it]=Insieme di carte standard di KDE\nLicenza GPL +Comment[ja]=標準 KDE カードセット\nGPL ライセンス +Comment[km]=បណ្ដុំបៀ KDE ខ្នាតគំរូ\nអជ្ញាបណ្ណ GPL +Comment[ko]=표준 KDE 카드 모음\nGPL 라이선스 +Comment[lt]=Standartinis KDE kortų rinkinys\nGPL licencija +Comment[lv]=Standarta KDE kāršu komplekts\n GPL licenze +Comment[mk]=Стандарден комплет на карти во KDE\nGPL-лиценца +Comment[mt]=Mazz karti standard KDE\nliċenzja GPL +Comment[nb]=Standard KDE sett med kort\nGPL-lisens +Comment[nds]=KDE-Standardkoorten\nGPL-Lizenz +Comment[ne]=मानक केडीई कार्ड सेट\nGPL इजाजतपत्र +Comment[nl]=Standaard KDE-kaartrug\nGPL-licentie +Comment[nn]=Standard KDE-kortstokk\nGPL-lisens +Comment[pl]=Standardowy zestaw kart KDE\nLicencja GPL +Comment[pt]=Baralho de cartas normal do KDE\nLicença GPL +Comment[pt_BR]=Jogo de cartas padrão do KDE\nLicença GPL +Comment[ro]=Set de cărţi KDE standard\nLicenţă GPL +Comment[ru]=Стандартная колода карт KDE\nЛицензия: GPL +Comment[sk]=Štandardný balíček kariet KDE\nlicencia GPL +Comment[sl]=Standardni nabor kart v KDE\nLicenca GPL +Comment[sr]=Стандардни KDE-ов шпил карата\nGPL лиценца +Comment[sr@Latn]=Standardni KDE-ov špil karata\nGPL licenca +Comment[sv]=Förvald KDE-kortlek\nGPL-licens +Comment[ta]=நிலையான கேடிஇ சீட்டு அமைப்பு \nGPL இயக்க ஆணை +Comment[tg]=Маҷмӯи кортҳои низомии KDE\nлитсензияи GPL +Comment[th]=ชุดของเกมไพ่ของ KDE\nลิขสิทธิ์แบบ GPL +Comment[tr]=Standart KDE kart seti\nGPL lisansı +Comment[uk]=Типовий набір карт KDE\nліцензія GPL +Comment[ven]=U vhekanya ha magarata a KDE ya Murole\tendelo ya nGPL +Comment[vi]=Thẻ KDE tiêu chuẩn đặt \nGPL license +Comment[wa]=Cwårdjeus standård di KDE\ndizo licince GPL +Comment[xh]=Umgangatho wekhadi le KDE iqela lekhadi \nGPL imvumelwano +Comment[zh_CN]=标准 KDE 牌面\nGPL 授权 +Comment[zh_TW]=標準 KDE 牌局\nGPL 授權 +Comment[zu]=Iqoqo lamakhadi elilingeneyo le-KDE\n ilayisensi ye-GPL diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/1.png b/libkdegames/carddecks/cards-dondorf-whist-b/1.png Binary files differnew file mode 100644 index 00000000..89c86909 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/1.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/10.png b/libkdegames/carddecks/cards-dondorf-whist-b/10.png Binary files differnew file mode 100644 index 00000000..5113cad8 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/10.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/11.png b/libkdegames/carddecks/cards-dondorf-whist-b/11.png Binary files differnew file mode 100644 index 00000000..11fc7e05 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/11.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/12.png b/libkdegames/carddecks/cards-dondorf-whist-b/12.png Binary files differnew file mode 100644 index 00000000..a2307ddc --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/12.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/13.png b/libkdegames/carddecks/cards-dondorf-whist-b/13.png Binary files differnew file mode 100644 index 00000000..0b9e20cd --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/13.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/14.png b/libkdegames/carddecks/cards-dondorf-whist-b/14.png Binary files differnew file mode 100644 index 00000000..1c49f750 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/14.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/15.png b/libkdegames/carddecks/cards-dondorf-whist-b/15.png Binary files differnew file mode 100644 index 00000000..ab295e25 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/15.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/16.png b/libkdegames/carddecks/cards-dondorf-whist-b/16.png Binary files differnew file mode 100644 index 00000000..d8bd8402 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/16.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/17.png b/libkdegames/carddecks/cards-dondorf-whist-b/17.png Binary files differnew file mode 100644 index 00000000..d3a3f6ed --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/17.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/18.png b/libkdegames/carddecks/cards-dondorf-whist-b/18.png Binary files differnew file mode 100644 index 00000000..12831991 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/18.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/19.png b/libkdegames/carddecks/cards-dondorf-whist-b/19.png Binary files differnew file mode 100644 index 00000000..e7199dce --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/19.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/2.png b/libkdegames/carddecks/cards-dondorf-whist-b/2.png Binary files differnew file mode 100644 index 00000000..93ec9944 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/2.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/20.png b/libkdegames/carddecks/cards-dondorf-whist-b/20.png Binary files differnew file mode 100644 index 00000000..90b043f0 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/20.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/21.png b/libkdegames/carddecks/cards-dondorf-whist-b/21.png Binary files differnew file mode 100644 index 00000000..905ccd6a --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/21.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/22.png b/libkdegames/carddecks/cards-dondorf-whist-b/22.png Binary files differnew file mode 100644 index 00000000..2eee5bbc --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/22.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/23.png b/libkdegames/carddecks/cards-dondorf-whist-b/23.png Binary files differnew file mode 100644 index 00000000..b717110e --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/23.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/24.png b/libkdegames/carddecks/cards-dondorf-whist-b/24.png Binary files differnew file mode 100644 index 00000000..4c05cd1d --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/24.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/25.png b/libkdegames/carddecks/cards-dondorf-whist-b/25.png Binary files differnew file mode 100644 index 00000000..c6701d0d --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/25.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/26.png b/libkdegames/carddecks/cards-dondorf-whist-b/26.png Binary files differnew file mode 100644 index 00000000..a737f469 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/26.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/27.png b/libkdegames/carddecks/cards-dondorf-whist-b/27.png Binary files differnew file mode 100644 index 00000000..c904309b --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/27.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/28.png b/libkdegames/carddecks/cards-dondorf-whist-b/28.png Binary files differnew file mode 100644 index 00000000..8da263d7 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/28.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/29.png b/libkdegames/carddecks/cards-dondorf-whist-b/29.png Binary files differnew file mode 100644 index 00000000..8ff7822d --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/29.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/3.png b/libkdegames/carddecks/cards-dondorf-whist-b/3.png Binary files differnew file mode 100644 index 00000000..7f5644f8 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/3.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/30.png b/libkdegames/carddecks/cards-dondorf-whist-b/30.png Binary files differnew file mode 100644 index 00000000..c93c64dc --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/30.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/31.png b/libkdegames/carddecks/cards-dondorf-whist-b/31.png Binary files differnew file mode 100644 index 00000000..912cbf79 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/31.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/32.png b/libkdegames/carddecks/cards-dondorf-whist-b/32.png Binary files differnew file mode 100644 index 00000000..9b72443c --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/32.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/33.png b/libkdegames/carddecks/cards-dondorf-whist-b/33.png Binary files differnew file mode 100644 index 00000000..20f863ac --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/33.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/34.png b/libkdegames/carddecks/cards-dondorf-whist-b/34.png Binary files differnew file mode 100644 index 00000000..7763d965 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/34.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/35.png b/libkdegames/carddecks/cards-dondorf-whist-b/35.png Binary files differnew file mode 100644 index 00000000..29eaef82 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/35.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/36.png b/libkdegames/carddecks/cards-dondorf-whist-b/36.png Binary files differnew file mode 100644 index 00000000..fd72b718 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/36.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/37.png b/libkdegames/carddecks/cards-dondorf-whist-b/37.png Binary files differnew file mode 100644 index 00000000..15a2b979 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/37.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/38.png b/libkdegames/carddecks/cards-dondorf-whist-b/38.png Binary files differnew file mode 100644 index 00000000..91551173 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/38.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/39.png b/libkdegames/carddecks/cards-dondorf-whist-b/39.png Binary files differnew file mode 100644 index 00000000..dcb66d00 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/39.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/4.png b/libkdegames/carddecks/cards-dondorf-whist-b/4.png Binary files differnew file mode 100644 index 00000000..1033ffd0 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/4.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/40.png b/libkdegames/carddecks/cards-dondorf-whist-b/40.png Binary files differnew file mode 100644 index 00000000..a1b9b2e9 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/40.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/41.png b/libkdegames/carddecks/cards-dondorf-whist-b/41.png Binary files differnew file mode 100644 index 00000000..8d7ca55b --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/41.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/42.png b/libkdegames/carddecks/cards-dondorf-whist-b/42.png Binary files differnew file mode 100644 index 00000000..4570430b --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/42.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/43.png b/libkdegames/carddecks/cards-dondorf-whist-b/43.png Binary files differnew file mode 100644 index 00000000..c889b795 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/43.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/44.png b/libkdegames/carddecks/cards-dondorf-whist-b/44.png Binary files differnew file mode 100644 index 00000000..b73bd4d1 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/44.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/45.png b/libkdegames/carddecks/cards-dondorf-whist-b/45.png Binary files differnew file mode 100644 index 00000000..de6730cd --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/45.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/46.png b/libkdegames/carddecks/cards-dondorf-whist-b/46.png Binary files differnew file mode 100644 index 00000000..f20edcc9 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/46.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/47.png b/libkdegames/carddecks/cards-dondorf-whist-b/47.png Binary files differnew file mode 100644 index 00000000..50ec6b95 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/47.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/48.png b/libkdegames/carddecks/cards-dondorf-whist-b/48.png Binary files differnew file mode 100644 index 00000000..c0bfdfbd --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/48.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/49.png b/libkdegames/carddecks/cards-dondorf-whist-b/49.png Binary files differnew file mode 100644 index 00000000..c8803ffa --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/49.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/5.png b/libkdegames/carddecks/cards-dondorf-whist-b/5.png Binary files differnew file mode 100644 index 00000000..0fb9ffcf --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/5.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/50.png b/libkdegames/carddecks/cards-dondorf-whist-b/50.png Binary files differnew file mode 100644 index 00000000..6d8c891e --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/50.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/51.png b/libkdegames/carddecks/cards-dondorf-whist-b/51.png Binary files differnew file mode 100644 index 00000000..fc2c76d0 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/51.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/52.png b/libkdegames/carddecks/cards-dondorf-whist-b/52.png Binary files differnew file mode 100644 index 00000000..6aef81aa --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/52.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/6.png b/libkdegames/carddecks/cards-dondorf-whist-b/6.png Binary files differnew file mode 100644 index 00000000..f70072cf --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/6.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/7.png b/libkdegames/carddecks/cards-dondorf-whist-b/7.png Binary files differnew file mode 100644 index 00000000..1dd352dc --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/7.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/8.png b/libkdegames/carddecks/cards-dondorf-whist-b/8.png Binary files differnew file mode 100644 index 00000000..087906ca --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/8.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/9.png b/libkdegames/carddecks/cards-dondorf-whist-b/9.png Binary files differnew file mode 100644 index 00000000..9bb290ee --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/9.png diff --git a/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT new file mode 100644 index 00000000..917bc1e3 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/COPYRIGHT @@ -0,0 +1,12 @@ +This PySol cardset was created by T. Kirk. + +Copyright (C) 2000 T. Kirk <grania@mailcity.com> + +This card set uses the court and Ace images from a deck published +by Dondorf and Co. of Frankfurt in 1896. The cards are double ended +and have different images on each end. Two card sets were produced. + +This card set 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/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop new file mode 100644 index 00000000..6626e825 --- /dev/null +++ b/libkdegames/carddecks/cards-dondorf-whist-b/index.desktop @@ -0,0 +1,17 @@ +[KDE Backdeck] +Name=Dondorf +Name[be]=Дандорф +Name[bg]=Дондроф +Name[bn]=ডনডর্ফ +Name[eo]=Bela +Name[hi]=डॉनड्रॉफ +Name[lv]=Dondorfa +Name[mk]=Дондорф +Name[ne]=डनडोर्फ +Name[ru]=Дондорф +Name[sr]=Дондорф +Name[ta]=டேன்டார்ஃப் +Name[tg]=Дондорф +Name[zu]=I-Dondorf +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/1.png b/libkdegames/carddecks/cards-gdkcard-bonded/1.png Binary files differnew file mode 100644 index 00000000..17c1ac79 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/1.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/10.png b/libkdegames/carddecks/cards-gdkcard-bonded/10.png Binary files differnew file mode 100644 index 00000000..f9fe505b --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/10.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/11.png b/libkdegames/carddecks/cards-gdkcard-bonded/11.png Binary files differnew file mode 100644 index 00000000..9fa6e5ec --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/11.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/12.png b/libkdegames/carddecks/cards-gdkcard-bonded/12.png Binary files differnew file mode 100644 index 00000000..09089e70 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/12.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/13.png b/libkdegames/carddecks/cards-gdkcard-bonded/13.png Binary files differnew file mode 100644 index 00000000..cb6b0e6f --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/13.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/14.png b/libkdegames/carddecks/cards-gdkcard-bonded/14.png Binary files differnew file mode 100644 index 00000000..027217be --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/14.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/15.png b/libkdegames/carddecks/cards-gdkcard-bonded/15.png Binary files differnew file mode 100644 index 00000000..b9054f4f --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/15.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/16.png b/libkdegames/carddecks/cards-gdkcard-bonded/16.png Binary files differnew file mode 100644 index 00000000..7b14d54a --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/16.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/17.png b/libkdegames/carddecks/cards-gdkcard-bonded/17.png Binary files differnew file mode 100644 index 00000000..e4d9b798 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/17.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/18.png b/libkdegames/carddecks/cards-gdkcard-bonded/18.png Binary files differnew file mode 100644 index 00000000..c0fe1954 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/18.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/19.png b/libkdegames/carddecks/cards-gdkcard-bonded/19.png Binary files differnew file mode 100644 index 00000000..7f24d523 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/19.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/2.png b/libkdegames/carddecks/cards-gdkcard-bonded/2.png Binary files differnew file mode 100644 index 00000000..7d8cbbcb --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/2.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/20.png b/libkdegames/carddecks/cards-gdkcard-bonded/20.png Binary files differnew file mode 100644 index 00000000..1a6e01c9 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/20.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/21.png b/libkdegames/carddecks/cards-gdkcard-bonded/21.png Binary files differnew file mode 100644 index 00000000..c217b982 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/21.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/22.png b/libkdegames/carddecks/cards-gdkcard-bonded/22.png Binary files differnew file mode 100644 index 00000000..82ce9196 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/22.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/23.png b/libkdegames/carddecks/cards-gdkcard-bonded/23.png Binary files differnew file mode 100644 index 00000000..03850bef --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/23.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/24.png b/libkdegames/carddecks/cards-gdkcard-bonded/24.png Binary files differnew file mode 100644 index 00000000..990289f6 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/24.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/25.png b/libkdegames/carddecks/cards-gdkcard-bonded/25.png Binary files differnew file mode 100644 index 00000000..04ea602f --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/25.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/26.png b/libkdegames/carddecks/cards-gdkcard-bonded/26.png Binary files differnew file mode 100644 index 00000000..705a22a2 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/26.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/27.png b/libkdegames/carddecks/cards-gdkcard-bonded/27.png Binary files differnew file mode 100644 index 00000000..cf5ea112 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/27.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/28.png b/libkdegames/carddecks/cards-gdkcard-bonded/28.png Binary files differnew file mode 100644 index 00000000..d613413c --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/28.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/29.png b/libkdegames/carddecks/cards-gdkcard-bonded/29.png Binary files differnew file mode 100644 index 00000000..8838c750 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/29.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/3.png b/libkdegames/carddecks/cards-gdkcard-bonded/3.png Binary files differnew file mode 100644 index 00000000..563a6385 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/3.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/30.png b/libkdegames/carddecks/cards-gdkcard-bonded/30.png Binary files differnew file mode 100644 index 00000000..4fb2901b --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/30.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/31.png b/libkdegames/carddecks/cards-gdkcard-bonded/31.png Binary files differnew file mode 100644 index 00000000..8bcd3090 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/31.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/32.png b/libkdegames/carddecks/cards-gdkcard-bonded/32.png Binary files differnew file mode 100644 index 00000000..c5fb66db --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/32.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/33.png b/libkdegames/carddecks/cards-gdkcard-bonded/33.png Binary files differnew file mode 100644 index 00000000..43d4df04 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/33.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/34.png b/libkdegames/carddecks/cards-gdkcard-bonded/34.png Binary files differnew file mode 100644 index 00000000..1bf6c975 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/34.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/35.png b/libkdegames/carddecks/cards-gdkcard-bonded/35.png Binary files differnew file mode 100644 index 00000000..ba85c37e --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/35.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/36.png b/libkdegames/carddecks/cards-gdkcard-bonded/36.png Binary files differnew file mode 100644 index 00000000..c45008af --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/36.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/37.png b/libkdegames/carddecks/cards-gdkcard-bonded/37.png Binary files differnew file mode 100644 index 00000000..05cac5a9 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/37.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/38.png b/libkdegames/carddecks/cards-gdkcard-bonded/38.png Binary files differnew file mode 100644 index 00000000..7c52ecbf --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/38.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/39.png b/libkdegames/carddecks/cards-gdkcard-bonded/39.png Binary files differnew file mode 100644 index 00000000..1404295a --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/39.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/4.png b/libkdegames/carddecks/cards-gdkcard-bonded/4.png Binary files differnew file mode 100644 index 00000000..fbf1b7d2 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/4.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/40.png b/libkdegames/carddecks/cards-gdkcard-bonded/40.png Binary files differnew file mode 100644 index 00000000..b1c0f317 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/40.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/41.png b/libkdegames/carddecks/cards-gdkcard-bonded/41.png Binary files differnew file mode 100644 index 00000000..a59a245c --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/41.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/42.png b/libkdegames/carddecks/cards-gdkcard-bonded/42.png Binary files differnew file mode 100644 index 00000000..104836a5 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/42.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/43.png b/libkdegames/carddecks/cards-gdkcard-bonded/43.png Binary files differnew file mode 100644 index 00000000..90b51009 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/43.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/44.png b/libkdegames/carddecks/cards-gdkcard-bonded/44.png Binary files differnew file mode 100644 index 00000000..66960d0d --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/44.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/45.png b/libkdegames/carddecks/cards-gdkcard-bonded/45.png Binary files differnew file mode 100644 index 00000000..7aabe88f --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/45.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/46.png b/libkdegames/carddecks/cards-gdkcard-bonded/46.png Binary files differnew file mode 100644 index 00000000..adfb2516 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/46.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/47.png b/libkdegames/carddecks/cards-gdkcard-bonded/47.png Binary files differnew file mode 100644 index 00000000..0f48a363 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/47.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/48.png b/libkdegames/carddecks/cards-gdkcard-bonded/48.png Binary files differnew file mode 100644 index 00000000..368c593a --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/48.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/49.png b/libkdegames/carddecks/cards-gdkcard-bonded/49.png Binary files differnew file mode 100644 index 00000000..a783b506 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/49.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/5.png b/libkdegames/carddecks/cards-gdkcard-bonded/5.png Binary files differnew file mode 100644 index 00000000..9f809535 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/5.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/50.png b/libkdegames/carddecks/cards-gdkcard-bonded/50.png Binary files differnew file mode 100644 index 00000000..dacc26cd --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/50.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/51.png b/libkdegames/carddecks/cards-gdkcard-bonded/51.png Binary files differnew file mode 100644 index 00000000..13a6e924 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/51.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/52.png b/libkdegames/carddecks/cards-gdkcard-bonded/52.png Binary files differnew file mode 100644 index 00000000..cce8c9f6 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/52.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/6.png b/libkdegames/carddecks/cards-gdkcard-bonded/6.png Binary files differnew file mode 100644 index 00000000..c679e0c3 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/6.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/7.png b/libkdegames/carddecks/cards-gdkcard-bonded/7.png Binary files differnew file mode 100644 index 00000000..53b35fbf --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/7.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/8.png b/libkdegames/carddecks/cards-gdkcard-bonded/8.png Binary files differnew file mode 100644 index 00000000..389d0069 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/8.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/9.png b/libkdegames/carddecks/cards-gdkcard-bonded/9.png Binary files differnew file mode 100644 index 00000000..59551a1a --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/9.png diff --git a/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT new file mode 100644 index 00000000..36052f95 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/COPYRIGHT @@ -0,0 +1,13 @@ +This PySol cardset was adapted from gnome-games 1.0.2 (gdk-card-image). +http://www.gnome.org + +Copyright (C) 1994 Heiko Eissfeldt <heiko@colossus.escape.de> +Copyright (C) 1994 Michael Bischoff <mbi@mo.math.nat.tu-bs.de> +Copyright (C) 1998 Felix Bellaby <felix@pooh.u-net.com> +Copyright (C) 1998 Ryu Changwoo <cwryu@eve.kaist.ac.kr> +Copyright (C) 1999 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at> + +This cardset 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/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop new file mode 100644 index 00000000..68f3ff21 --- /dev/null +++ b/libkdegames/carddecks/cards-gdkcard-bonded/index.desktop @@ -0,0 +1,37 @@ +[KDE Backdeck] +Name=Bonded +Name[af]=Geheg +Name[az]=Bağlı +Name[be]=Перапляценні +Name[bg]=Завързан +Name[bn]=আবদ্ধ +Name[ca]=Unides +Name[cs]=Lepenka +Name[de]=Verbunden +Name[eo]=Benda +Name[es]=Unidas +Name[eu]=Estekatua +Name[fi]=Sidottu +Name[fr]=Compact +Name[gl]=Unidas +Name[hi]=बॉन्डेड +Name[hr]=Povezano +Name[is]=Bundið +Name[lv]=Vienkāršs +Name[mk]=Врзани +Name[mt]=Mehmuż +Name[ne]=बन्डेड +Name[pt]=Unidos +Name[pt_BR]=Laçado +Name[ru]=Переплетения +Name[sr]=Везан +Name[sr@Latn]=Vezan +Name[ta]=பிணைக்கப்பட்ட +Name[tg]=Печдарпечшавӣ +Name[tr]=Bağlı +Name[uk]=Бони +Name[ven]=Zwo khwathiswa +Name[xh]=Dibeneyo +Name[zu]=Kuxhumene +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-hard-a-port/1.png b/libkdegames/carddecks/cards-hard-a-port/1.png Binary files differnew file mode 100644 index 00000000..05c56268 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/1.png diff --git a/libkdegames/carddecks/cards-hard-a-port/10.png b/libkdegames/carddecks/cards-hard-a-port/10.png Binary files differnew file mode 100644 index 00000000..ee2d2072 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/10.png diff --git a/libkdegames/carddecks/cards-hard-a-port/11.png b/libkdegames/carddecks/cards-hard-a-port/11.png Binary files differnew file mode 100644 index 00000000..4fd72b89 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/11.png diff --git a/libkdegames/carddecks/cards-hard-a-port/12.png b/libkdegames/carddecks/cards-hard-a-port/12.png Binary files differnew file mode 100644 index 00000000..7dd80ce7 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/12.png diff --git a/libkdegames/carddecks/cards-hard-a-port/13.png b/libkdegames/carddecks/cards-hard-a-port/13.png Binary files differnew file mode 100644 index 00000000..62edf27c --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/13.png diff --git a/libkdegames/carddecks/cards-hard-a-port/14.png b/libkdegames/carddecks/cards-hard-a-port/14.png Binary files differnew file mode 100644 index 00000000..5cfa05ed --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/14.png diff --git a/libkdegames/carddecks/cards-hard-a-port/15.png b/libkdegames/carddecks/cards-hard-a-port/15.png Binary files differnew file mode 100644 index 00000000..a950a87c --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/15.png diff --git a/libkdegames/carddecks/cards-hard-a-port/16.png b/libkdegames/carddecks/cards-hard-a-port/16.png Binary files differnew file mode 100644 index 00000000..8b0d76f3 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/16.png diff --git a/libkdegames/carddecks/cards-hard-a-port/17.png b/libkdegames/carddecks/cards-hard-a-port/17.png Binary files differnew file mode 100644 index 00000000..9625563e --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/17.png diff --git a/libkdegames/carddecks/cards-hard-a-port/18.png b/libkdegames/carddecks/cards-hard-a-port/18.png Binary files differnew file mode 100644 index 00000000..715c8443 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/18.png diff --git a/libkdegames/carddecks/cards-hard-a-port/19.png b/libkdegames/carddecks/cards-hard-a-port/19.png Binary files differnew file mode 100644 index 00000000..7a3cf664 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/19.png diff --git a/libkdegames/carddecks/cards-hard-a-port/2.png b/libkdegames/carddecks/cards-hard-a-port/2.png Binary files differnew file mode 100644 index 00000000..bf4d601f --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/2.png diff --git a/libkdegames/carddecks/cards-hard-a-port/20.png b/libkdegames/carddecks/cards-hard-a-port/20.png Binary files differnew file mode 100644 index 00000000..331abc3b --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/20.png diff --git a/libkdegames/carddecks/cards-hard-a-port/21.png b/libkdegames/carddecks/cards-hard-a-port/21.png Binary files differnew file mode 100644 index 00000000..59adfb08 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/21.png diff --git a/libkdegames/carddecks/cards-hard-a-port/22.png b/libkdegames/carddecks/cards-hard-a-port/22.png Binary files differnew file mode 100644 index 00000000..a1ca8484 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/22.png diff --git a/libkdegames/carddecks/cards-hard-a-port/23.png b/libkdegames/carddecks/cards-hard-a-port/23.png Binary files differnew file mode 100644 index 00000000..d97de844 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/23.png diff --git a/libkdegames/carddecks/cards-hard-a-port/24.png b/libkdegames/carddecks/cards-hard-a-port/24.png Binary files differnew file mode 100644 index 00000000..65e3da3c --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/24.png diff --git a/libkdegames/carddecks/cards-hard-a-port/25.png b/libkdegames/carddecks/cards-hard-a-port/25.png Binary files differnew file mode 100644 index 00000000..14f82b7d --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/25.png diff --git a/libkdegames/carddecks/cards-hard-a-port/26.png b/libkdegames/carddecks/cards-hard-a-port/26.png Binary files differnew file mode 100644 index 00000000..e2601df6 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/26.png diff --git a/libkdegames/carddecks/cards-hard-a-port/27.png b/libkdegames/carddecks/cards-hard-a-port/27.png Binary files differnew file mode 100644 index 00000000..d18b29d7 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/27.png diff --git a/libkdegames/carddecks/cards-hard-a-port/28.png b/libkdegames/carddecks/cards-hard-a-port/28.png Binary files differnew file mode 100644 index 00000000..3dcc0842 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/28.png diff --git a/libkdegames/carddecks/cards-hard-a-port/29.png b/libkdegames/carddecks/cards-hard-a-port/29.png Binary files differnew file mode 100644 index 00000000..a0cc5866 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/29.png diff --git a/libkdegames/carddecks/cards-hard-a-port/3.png b/libkdegames/carddecks/cards-hard-a-port/3.png Binary files differnew file mode 100644 index 00000000..b78502b9 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/3.png diff --git a/libkdegames/carddecks/cards-hard-a-port/30.png b/libkdegames/carddecks/cards-hard-a-port/30.png Binary files differnew file mode 100644 index 00000000..e659dc83 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/30.png diff --git a/libkdegames/carddecks/cards-hard-a-port/31.png b/libkdegames/carddecks/cards-hard-a-port/31.png Binary files differnew file mode 100644 index 00000000..db6f599c --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/31.png diff --git a/libkdegames/carddecks/cards-hard-a-port/32.png b/libkdegames/carddecks/cards-hard-a-port/32.png Binary files differnew file mode 100644 index 00000000..9e42582f --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/32.png diff --git a/libkdegames/carddecks/cards-hard-a-port/33.png b/libkdegames/carddecks/cards-hard-a-port/33.png Binary files differnew file mode 100644 index 00000000..c19f1520 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/33.png diff --git a/libkdegames/carddecks/cards-hard-a-port/34.png b/libkdegames/carddecks/cards-hard-a-port/34.png Binary files differnew file mode 100644 index 00000000..139dc9b4 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/34.png diff --git a/libkdegames/carddecks/cards-hard-a-port/35.png b/libkdegames/carddecks/cards-hard-a-port/35.png Binary files differnew file mode 100644 index 00000000..62d8a9bc --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/35.png diff --git a/libkdegames/carddecks/cards-hard-a-port/36.png b/libkdegames/carddecks/cards-hard-a-port/36.png Binary files differnew file mode 100644 index 00000000..510d150e --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/36.png diff --git a/libkdegames/carddecks/cards-hard-a-port/37.png b/libkdegames/carddecks/cards-hard-a-port/37.png Binary files differnew file mode 100644 index 00000000..2e03c9e2 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/37.png diff --git a/libkdegames/carddecks/cards-hard-a-port/38.png b/libkdegames/carddecks/cards-hard-a-port/38.png Binary files differnew file mode 100644 index 00000000..e12131f0 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/38.png diff --git a/libkdegames/carddecks/cards-hard-a-port/39.png b/libkdegames/carddecks/cards-hard-a-port/39.png Binary files differnew file mode 100644 index 00000000..eae23793 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/39.png diff --git a/libkdegames/carddecks/cards-hard-a-port/4.png b/libkdegames/carddecks/cards-hard-a-port/4.png Binary files differnew file mode 100644 index 00000000..9d7f80c1 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/4.png diff --git a/libkdegames/carddecks/cards-hard-a-port/40.png b/libkdegames/carddecks/cards-hard-a-port/40.png Binary files differnew file mode 100644 index 00000000..733be70f --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/40.png diff --git a/libkdegames/carddecks/cards-hard-a-port/41.png b/libkdegames/carddecks/cards-hard-a-port/41.png Binary files differnew file mode 100644 index 00000000..b3e51b07 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/41.png diff --git a/libkdegames/carddecks/cards-hard-a-port/42.png b/libkdegames/carddecks/cards-hard-a-port/42.png Binary files differnew file mode 100644 index 00000000..a6f69d9f --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/42.png diff --git a/libkdegames/carddecks/cards-hard-a-port/43.png b/libkdegames/carddecks/cards-hard-a-port/43.png Binary files differnew file mode 100644 index 00000000..49c20875 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/43.png diff --git a/libkdegames/carddecks/cards-hard-a-port/44.png b/libkdegames/carddecks/cards-hard-a-port/44.png Binary files differnew file mode 100644 index 00000000..6fed6ead --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/44.png diff --git a/libkdegames/carddecks/cards-hard-a-port/45.png b/libkdegames/carddecks/cards-hard-a-port/45.png Binary files differnew file mode 100644 index 00000000..81be70a2 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/45.png diff --git a/libkdegames/carddecks/cards-hard-a-port/46.png b/libkdegames/carddecks/cards-hard-a-port/46.png Binary files differnew file mode 100644 index 00000000..d82c5f60 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/46.png diff --git a/libkdegames/carddecks/cards-hard-a-port/47.png b/libkdegames/carddecks/cards-hard-a-port/47.png Binary files differnew file mode 100644 index 00000000..f6056465 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/47.png diff --git a/libkdegames/carddecks/cards-hard-a-port/48.png b/libkdegames/carddecks/cards-hard-a-port/48.png Binary files differnew file mode 100644 index 00000000..6369f53b --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/48.png diff --git a/libkdegames/carddecks/cards-hard-a-port/49.png b/libkdegames/carddecks/cards-hard-a-port/49.png Binary files differnew file mode 100644 index 00000000..49b5b53e --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/49.png diff --git a/libkdegames/carddecks/cards-hard-a-port/5.png b/libkdegames/carddecks/cards-hard-a-port/5.png Binary files differnew file mode 100644 index 00000000..6ff98a13 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/5.png diff --git a/libkdegames/carddecks/cards-hard-a-port/50.png b/libkdegames/carddecks/cards-hard-a-port/50.png Binary files differnew file mode 100644 index 00000000..ce8e46db --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/50.png diff --git a/libkdegames/carddecks/cards-hard-a-port/51.png b/libkdegames/carddecks/cards-hard-a-port/51.png Binary files differnew file mode 100644 index 00000000..46552bee --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/51.png diff --git a/libkdegames/carddecks/cards-hard-a-port/52.png b/libkdegames/carddecks/cards-hard-a-port/52.png Binary files differnew file mode 100644 index 00000000..b03db4a0 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/52.png diff --git a/libkdegames/carddecks/cards-hard-a-port/6.png b/libkdegames/carddecks/cards-hard-a-port/6.png Binary files differnew file mode 100644 index 00000000..24fa84b0 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/6.png diff --git a/libkdegames/carddecks/cards-hard-a-port/7.png b/libkdegames/carddecks/cards-hard-a-port/7.png Binary files differnew file mode 100644 index 00000000..11561878 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/7.png diff --git a/libkdegames/carddecks/cards-hard-a-port/8.png b/libkdegames/carddecks/cards-hard-a-port/8.png Binary files differnew file mode 100644 index 00000000..08d124b7 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/8.png diff --git a/libkdegames/carddecks/cards-hard-a-port/9.png b/libkdegames/carddecks/cards-hard-a-port/9.png Binary files differnew file mode 100644 index 00000000..14962b11 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/9.png diff --git a/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT new file mode 100644 index 00000000..f5575c7b --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/COPYRIGHT @@ -0,0 +1,14 @@ +This PySol cardset was created by T. Kirk. + +Copyright (C) 2000 T. Kirk <grania@mailcity.com> + +This transformation card set uses images from a set of trade +cards produced in the late 19th century in the United States +of America. Cards of this type were given away as premiums +with the purchase of various products. Each of the cards +has a different image. + +This card set 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/libkdegames/carddecks/cards-hard-a-port/index.desktop b/libkdegames/carddecks/cards-hard-a-port/index.desktop new file mode 100644 index 00000000..d8e49aa5 --- /dev/null +++ b/libkdegames/carddecks/cards-hard-a-port/index.desktop @@ -0,0 +1,29 @@ +[KDE Backdeck] +Name=Hard a Port +Name[af]=Hard 'n Poort +Name[be]=Пампушка +Name[bg]=Дамски +Name[bn]=হার্ড এ পোর্ট +Name[de]=Hart Backbord +Name[eo]=Tenta +Name[hi]=हार्ड ए पोर्ट +Name[hr]=Tvrdi na portu +Name[is]=Hart á bakborða +Name[it]=Donnine +Name[km]=ច្រករឹង +Name[lv]=Klasisks +Name[nds]=Hatt Backboord +Name[ne]=कडा एउटा पोर्ट +Name[pt_BR]=Duramente um Porto +Name[ro]=Licenţioase +Name[ru]=Пышка +Name[sr]=Хард-а-порт +Name[sr@Latn]=Hard-a-port +Name[sv]=Dikt babord +Name[ta]=முனையத்தை கடினமாக்கு +Name[tg]=Даргоҳи Сахт +Name[ven]=Port yo Khwathaho +Name[xh]=Izibuko Esport eqinileyo +Name[zu]=Qinisa isikhumulo +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-konqi-modern/1.png b/libkdegames/carddecks/cards-konqi-modern/1.png Binary files differnew file mode 100644 index 00000000..68dfb0ff --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/1.png diff --git a/libkdegames/carddecks/cards-konqi-modern/10.png b/libkdegames/carddecks/cards-konqi-modern/10.png Binary files differnew file mode 100644 index 00000000..6df3ad8d --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/10.png diff --git a/libkdegames/carddecks/cards-konqi-modern/11.png b/libkdegames/carddecks/cards-konqi-modern/11.png Binary files differnew file mode 100644 index 00000000..665cb2a6 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/11.png diff --git a/libkdegames/carddecks/cards-konqi-modern/12.png b/libkdegames/carddecks/cards-konqi-modern/12.png Binary files differnew file mode 100644 index 00000000..1f23d99e --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/12.png diff --git a/libkdegames/carddecks/cards-konqi-modern/13.png b/libkdegames/carddecks/cards-konqi-modern/13.png Binary files differnew file mode 100644 index 00000000..f1027fdc --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/13.png diff --git a/libkdegames/carddecks/cards-konqi-modern/14.png b/libkdegames/carddecks/cards-konqi-modern/14.png Binary files differnew file mode 100644 index 00000000..80d11335 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/14.png diff --git a/libkdegames/carddecks/cards-konqi-modern/15.png b/libkdegames/carddecks/cards-konqi-modern/15.png Binary files differnew file mode 100644 index 00000000..72913f7b --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/15.png diff --git a/libkdegames/carddecks/cards-konqi-modern/16.png b/libkdegames/carddecks/cards-konqi-modern/16.png Binary files differnew file mode 100644 index 00000000..225f7774 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/16.png diff --git a/libkdegames/carddecks/cards-konqi-modern/17.png b/libkdegames/carddecks/cards-konqi-modern/17.png Binary files differnew file mode 100644 index 00000000..0444e010 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/17.png diff --git a/libkdegames/carddecks/cards-konqi-modern/18.png b/libkdegames/carddecks/cards-konqi-modern/18.png Binary files differnew file mode 100644 index 00000000..f325c781 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/18.png diff --git a/libkdegames/carddecks/cards-konqi-modern/19.png b/libkdegames/carddecks/cards-konqi-modern/19.png Binary files differnew file mode 100644 index 00000000..570e951e --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/19.png diff --git a/libkdegames/carddecks/cards-konqi-modern/2.png b/libkdegames/carddecks/cards-konqi-modern/2.png Binary files differnew file mode 100644 index 00000000..89b2b4d5 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/2.png diff --git a/libkdegames/carddecks/cards-konqi-modern/20.png b/libkdegames/carddecks/cards-konqi-modern/20.png Binary files differnew file mode 100644 index 00000000..d4b47c7d --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/20.png diff --git a/libkdegames/carddecks/cards-konqi-modern/21.png b/libkdegames/carddecks/cards-konqi-modern/21.png Binary files differnew file mode 100644 index 00000000..9ab04cc5 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/21.png diff --git a/libkdegames/carddecks/cards-konqi-modern/22.png b/libkdegames/carddecks/cards-konqi-modern/22.png Binary files differnew file mode 100644 index 00000000..387c3203 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/22.png diff --git a/libkdegames/carddecks/cards-konqi-modern/23.png b/libkdegames/carddecks/cards-konqi-modern/23.png Binary files differnew file mode 100644 index 00000000..fcec2343 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/23.png diff --git a/libkdegames/carddecks/cards-konqi-modern/24.png b/libkdegames/carddecks/cards-konqi-modern/24.png Binary files differnew file mode 100644 index 00000000..48c9ed02 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/24.png diff --git a/libkdegames/carddecks/cards-konqi-modern/25.png b/libkdegames/carddecks/cards-konqi-modern/25.png Binary files differnew file mode 100644 index 00000000..80bbfdf1 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/25.png diff --git a/libkdegames/carddecks/cards-konqi-modern/26.png b/libkdegames/carddecks/cards-konqi-modern/26.png Binary files differnew file mode 100644 index 00000000..2a879ec5 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/26.png diff --git a/libkdegames/carddecks/cards-konqi-modern/27.png b/libkdegames/carddecks/cards-konqi-modern/27.png Binary files differnew file mode 100644 index 00000000..1e6141c0 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/27.png diff --git a/libkdegames/carddecks/cards-konqi-modern/28.png b/libkdegames/carddecks/cards-konqi-modern/28.png Binary files differnew file mode 100644 index 00000000..1403437e --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/28.png diff --git a/libkdegames/carddecks/cards-konqi-modern/29.png b/libkdegames/carddecks/cards-konqi-modern/29.png Binary files differnew file mode 100644 index 00000000..415b48cc --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/29.png diff --git a/libkdegames/carddecks/cards-konqi-modern/3.png b/libkdegames/carddecks/cards-konqi-modern/3.png Binary files differnew file mode 100644 index 00000000..43067744 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/3.png diff --git a/libkdegames/carddecks/cards-konqi-modern/30.png b/libkdegames/carddecks/cards-konqi-modern/30.png Binary files differnew file mode 100644 index 00000000..8a1b885d --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/30.png diff --git a/libkdegames/carddecks/cards-konqi-modern/31.png b/libkdegames/carddecks/cards-konqi-modern/31.png Binary files differnew file mode 100644 index 00000000..c7de49ed --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/31.png diff --git a/libkdegames/carddecks/cards-konqi-modern/32.png b/libkdegames/carddecks/cards-konqi-modern/32.png Binary files differnew file mode 100644 index 00000000..57e0015f --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/32.png diff --git a/libkdegames/carddecks/cards-konqi-modern/33.png b/libkdegames/carddecks/cards-konqi-modern/33.png Binary files differnew file mode 100644 index 00000000..a502ae94 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/33.png diff --git a/libkdegames/carddecks/cards-konqi-modern/34.png b/libkdegames/carddecks/cards-konqi-modern/34.png Binary files differnew file mode 100644 index 00000000..524768dc --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/34.png diff --git a/libkdegames/carddecks/cards-konqi-modern/35.png b/libkdegames/carddecks/cards-konqi-modern/35.png Binary files differnew file mode 100644 index 00000000..d15a240e --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/35.png diff --git a/libkdegames/carddecks/cards-konqi-modern/36.png b/libkdegames/carddecks/cards-konqi-modern/36.png Binary files differnew file mode 100644 index 00000000..b8dce3f8 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/36.png diff --git a/libkdegames/carddecks/cards-konqi-modern/37.png b/libkdegames/carddecks/cards-konqi-modern/37.png Binary files differnew file mode 100644 index 00000000..2f193cc1 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/37.png diff --git a/libkdegames/carddecks/cards-konqi-modern/38.png b/libkdegames/carddecks/cards-konqi-modern/38.png Binary files differnew file mode 100644 index 00000000..097d63ff --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/38.png diff --git a/libkdegames/carddecks/cards-konqi-modern/39.png b/libkdegames/carddecks/cards-konqi-modern/39.png Binary files differnew file mode 100644 index 00000000..a94a6009 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/39.png diff --git a/libkdegames/carddecks/cards-konqi-modern/4.png b/libkdegames/carddecks/cards-konqi-modern/4.png Binary files differnew file mode 100644 index 00000000..b4890c03 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/4.png diff --git a/libkdegames/carddecks/cards-konqi-modern/40.png b/libkdegames/carddecks/cards-konqi-modern/40.png Binary files differnew file mode 100644 index 00000000..d533469a --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/40.png diff --git a/libkdegames/carddecks/cards-konqi-modern/41.png b/libkdegames/carddecks/cards-konqi-modern/41.png Binary files differnew file mode 100644 index 00000000..f524f7ea --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/41.png diff --git a/libkdegames/carddecks/cards-konqi-modern/42.png b/libkdegames/carddecks/cards-konqi-modern/42.png Binary files differnew file mode 100644 index 00000000..9e6ff5af --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/42.png diff --git a/libkdegames/carddecks/cards-konqi-modern/43.png b/libkdegames/carddecks/cards-konqi-modern/43.png Binary files differnew file mode 100644 index 00000000..59e9e273 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/43.png diff --git a/libkdegames/carddecks/cards-konqi-modern/44.png b/libkdegames/carddecks/cards-konqi-modern/44.png Binary files differnew file mode 100644 index 00000000..a3114bd9 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/44.png diff --git a/libkdegames/carddecks/cards-konqi-modern/45.png b/libkdegames/carddecks/cards-konqi-modern/45.png Binary files differnew file mode 100644 index 00000000..2614080b --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/45.png diff --git a/libkdegames/carddecks/cards-konqi-modern/46.png b/libkdegames/carddecks/cards-konqi-modern/46.png Binary files differnew file mode 100644 index 00000000..a3118981 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/46.png diff --git a/libkdegames/carddecks/cards-konqi-modern/47.png b/libkdegames/carddecks/cards-konqi-modern/47.png Binary files differnew file mode 100644 index 00000000..423ec06e --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/47.png diff --git a/libkdegames/carddecks/cards-konqi-modern/48.png b/libkdegames/carddecks/cards-konqi-modern/48.png Binary files differnew file mode 100644 index 00000000..5153c779 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/48.png diff --git a/libkdegames/carddecks/cards-konqi-modern/49.png b/libkdegames/carddecks/cards-konqi-modern/49.png Binary files differnew file mode 100644 index 00000000..99f8ae96 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/49.png diff --git a/libkdegames/carddecks/cards-konqi-modern/5.png b/libkdegames/carddecks/cards-konqi-modern/5.png Binary files differnew file mode 100644 index 00000000..5e167b1a --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/5.png diff --git a/libkdegames/carddecks/cards-konqi-modern/50.png b/libkdegames/carddecks/cards-konqi-modern/50.png Binary files differnew file mode 100644 index 00000000..a9c41757 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/50.png diff --git a/libkdegames/carddecks/cards-konqi-modern/51.png b/libkdegames/carddecks/cards-konqi-modern/51.png Binary files differnew file mode 100644 index 00000000..b322fc1b --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/51.png diff --git a/libkdegames/carddecks/cards-konqi-modern/52.png b/libkdegames/carddecks/cards-konqi-modern/52.png Binary files differnew file mode 100644 index 00000000..d614733f --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/52.png diff --git a/libkdegames/carddecks/cards-konqi-modern/6.png b/libkdegames/carddecks/cards-konqi-modern/6.png Binary files differnew file mode 100644 index 00000000..d614e646 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/6.png diff --git a/libkdegames/carddecks/cards-konqi-modern/7.png b/libkdegames/carddecks/cards-konqi-modern/7.png Binary files differnew file mode 100644 index 00000000..961ac50f --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/7.png diff --git a/libkdegames/carddecks/cards-konqi-modern/8.png b/libkdegames/carddecks/cards-konqi-modern/8.png Binary files differnew file mode 100644 index 00000000..1769d3db --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/8.png diff --git a/libkdegames/carddecks/cards-konqi-modern/9.png b/libkdegames/carddecks/cards-konqi-modern/9.png Binary files differnew file mode 100644 index 00000000..f86dcd48 --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/9.png diff --git a/libkdegames/carddecks/cards-konqi-modern/index.desktop b/libkdegames/carddecks/cards-konqi-modern/index.desktop new file mode 100644 index 00000000..4dc5fe5c --- /dev/null +++ b/libkdegames/carddecks/cards-konqi-modern/index.desktop @@ -0,0 +1,62 @@ +[KDE Backdeck] +Name=Konqi +Name[be]=Конкі +Name[bg]=Конки +Name[bn]=কনকি +Name[cs]=Konqui +Name[eo]=Konĉja +Name[hi]=के-ऑन्गी +Name[it]=Konqui +Name[lv]=Konvi +Name[ne]=कोन्क्वी +Name[ru]=Конки +Name[sr]=Конки +Name[sr@Latn]=Konki +Name[ta]=கான்கி +Name[tg]=Конки +Name[uk]=Конкі +Name[zu]=I-Konqi +Preview=11.png +PySol=false +Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[bn]=আধুনিক কনকি - একটি পারিবারিক তাস খেলা\nডিজাইন: লরা লেল্যান্ড\n <l_layland@hotmail.com>\n Katie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nকনকি: স্টিফেন স্পাজ\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[bs]=Moderni Konqi - igrajte sa porodičnim špilom\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n <l_layland@hotmail.com>\nKatie per Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi per Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n <l_layland@hotmail.com>\nKatie vytvořila Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqiho vytvořil Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n <l_layland@hotmail.com>\nKatie gan Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi gan Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie von Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi von Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[el]=Μοντέρνος Konqi - play the family θέμα καρτών\nΣχεδίαση: Laura Layland\n <l_layland@hotmail.com>\nKatie από Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi από Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n <l_layland@hotmail.com>\nKonjo de Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonĉjo de Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n <l_layland@hotmail.com>\nKatie-ren egilea: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi-ren egilea Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند <l_layland@hotmail.com>\nKatie توسط آگنیسکا زاکووسکا\n <agnieszka@imagegalaxy.de>\nKonqi توسط استفان اسپاتز\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n <l_layland@hotmail.com>a\nKatie Agnieszka Czajkowskaa\n <agnieszka@imagegalaxy.de>a\nKonqi Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n <l_layland@hotmail.com>\nKatie par Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi par Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n <l_layland@hotmail.com>\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[is]=Nútíma Konqi - spilastokkur fjölskyldunnar\nHönnun: Laura Layland\n <l_layland@hotmail.com>\nKatie e. Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi e. Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[it]=Konqui moderno - carte familiari\nDesign: Laura Layland\n<l_layland@hotmail.com>\nKatie di Agnieszka Czajkowska\n<agnieszka@imagegalaxy.de>\nKonqi di Stefan Spatz\n<stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ja]=モダン Konqi - ファミリ向けカードデッキ\nデザイン: Laura Layland\n <l_layland@hotmail.com>\nKatie の作者: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi の作者: Stefan Spatz \n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[lt]=Modern Konqi - žaisktie šeimos kortų žaidimą\nDizainas: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[lv]=Modernais Konkvi - spēlēt pie ģimenes kāršu galda\n Dizains: Laura Layland\n <l_layland@hotmail.com>\n Katie no Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\n Konkvi no Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[mk]=Модерен Konqi - играјте со семејниот шпил карти\nДизајн: Laura Layland\n<l_layland@hotmail.com>\nKatie од Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi од Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de> +Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie vun Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi vun Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ne]=आधुनिक कोन्क्की - परिवारिक कार्डडेक \nडिजाइन प्ले: लाउरा लेल्यान्ड\n <l_layland@hotmail.com>\nKatie, अग्निज्का जज्कोस्काद्वारा\n <agnieszka@imagegalaxy.de>\nKonqi, स्टेफान स्पार्टजद्वारा \n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie door Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi door Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de> +Comment[pl]=Nowoczesny Konqi - zagraj w grę rodzinną\nProjekt: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie: Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>a\nKonqi: Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie por Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ru]=Современная колода с семейством Конки\nДизайн: Лаура Лейлэнд (Laura Layland) <l_layland@hotmail.com>\nКэйт нарисована Агниежкой Зайковской (Agnieszka Czajkowska) <agnieszka@imagegalaxy.de>\nКонки нарисован Штефаном Спартцом (Stefan Spatz) <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie od Agnieszky Czajkowskeja\n <agnieszka@imagegalaxy.de>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Laylanda\n <l_layland@hotmail.com>a\nKatie od Agnieszke Czajkowske\n <agnieszka@imagegalaxy.de>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sr]=Модеран Конки - играјте са породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n <l_layland@hotmail.com>\nКети: Агњешка Чајковска (Agnieszka Czajkowska)\n <agnieszka@imagegalaxy.de>\nКонки: Штефан Шпац (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sr@Latn]=Moderan Konki - igrajte sa porodičnim špilom\nDizajn: Lora Lejlend (Laura Layland)\n <l_layland@hotmail.com>\nKeti: Agnješka Čajkovska (Agnieszka Czajkowska)\n <agnieszka@imagegalaxy.de>\nKonki: Štefan Špac (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKatie av Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi av Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ta]=மார்டன் கான்கி - புதிய குடும்பச் சீட்டுத் தளத்தை விளையாடு\nவடிவமைப்பு: லெளரா லேலேண்டு\n <l_ayland@hotmail.com>\n Katie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[uk]=Сучасний Конкі - зіграйте у сімейні карти\nРозробка: Laura Laylanda\n <l_layland@hotmail.com>\nKatie від Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi від Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[wa]=Modiene Konqi - cwårdjeus des familes\nDessins: Laura Layland\n <l_layland@hotmail.com>\nKatie pa Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\nKonqi pa Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[zh_TW]=現代 Konqi - 玩家庭牌局\n設計︰Laura Layland...<l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska...<agnieszka@imagegalaxy.de>\nKonqi by Stefan Spatz...<stefan.spatz@stud-mail.uni-wuerzburg.de> diff --git a/libkdegames/carddecks/cards-penguins/1.png b/libkdegames/carddecks/cards-penguins/1.png Binary files differnew file mode 100644 index 00000000..b59022e8 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/1.png diff --git a/libkdegames/carddecks/cards-penguins/10.png b/libkdegames/carddecks/cards-penguins/10.png Binary files differnew file mode 100644 index 00000000..d54669fe --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/10.png diff --git a/libkdegames/carddecks/cards-penguins/11.png b/libkdegames/carddecks/cards-penguins/11.png Binary files differnew file mode 100644 index 00000000..02c2464c --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/11.png diff --git a/libkdegames/carddecks/cards-penguins/12.png b/libkdegames/carddecks/cards-penguins/12.png Binary files differnew file mode 100644 index 00000000..d179eb99 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/12.png diff --git a/libkdegames/carddecks/cards-penguins/13.png b/libkdegames/carddecks/cards-penguins/13.png Binary files differnew file mode 100644 index 00000000..135af493 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/13.png diff --git a/libkdegames/carddecks/cards-penguins/14.png b/libkdegames/carddecks/cards-penguins/14.png Binary files differnew file mode 100644 index 00000000..4a179917 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/14.png diff --git a/libkdegames/carddecks/cards-penguins/15.png b/libkdegames/carddecks/cards-penguins/15.png Binary files differnew file mode 100644 index 00000000..b5238d00 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/15.png diff --git a/libkdegames/carddecks/cards-penguins/16.png b/libkdegames/carddecks/cards-penguins/16.png Binary files differnew file mode 100644 index 00000000..33fe6b8e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/16.png diff --git a/libkdegames/carddecks/cards-penguins/17.png b/libkdegames/carddecks/cards-penguins/17.png Binary files differnew file mode 100644 index 00000000..99b77ce8 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/17.png diff --git a/libkdegames/carddecks/cards-penguins/18.png b/libkdegames/carddecks/cards-penguins/18.png Binary files differnew file mode 100644 index 00000000..b88ae958 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/18.png diff --git a/libkdegames/carddecks/cards-penguins/19.png b/libkdegames/carddecks/cards-penguins/19.png Binary files differnew file mode 100644 index 00000000..532899aa --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/19.png diff --git a/libkdegames/carddecks/cards-penguins/2.png b/libkdegames/carddecks/cards-penguins/2.png Binary files differnew file mode 100644 index 00000000..090cfd02 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/2.png diff --git a/libkdegames/carddecks/cards-penguins/20.png b/libkdegames/carddecks/cards-penguins/20.png Binary files differnew file mode 100644 index 00000000..ed129a32 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/20.png diff --git a/libkdegames/carddecks/cards-penguins/21.png b/libkdegames/carddecks/cards-penguins/21.png Binary files differnew file mode 100644 index 00000000..5bf532f7 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/21.png diff --git a/libkdegames/carddecks/cards-penguins/22.png b/libkdegames/carddecks/cards-penguins/22.png Binary files differnew file mode 100644 index 00000000..07c61ced --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/22.png diff --git a/libkdegames/carddecks/cards-penguins/23.png b/libkdegames/carddecks/cards-penguins/23.png Binary files differnew file mode 100644 index 00000000..ecce302b --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/23.png diff --git a/libkdegames/carddecks/cards-penguins/24.png b/libkdegames/carddecks/cards-penguins/24.png Binary files differnew file mode 100644 index 00000000..feee0a9e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/24.png diff --git a/libkdegames/carddecks/cards-penguins/25.png b/libkdegames/carddecks/cards-penguins/25.png Binary files differnew file mode 100644 index 00000000..d42f2b2b --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/25.png diff --git a/libkdegames/carddecks/cards-penguins/26.png b/libkdegames/carddecks/cards-penguins/26.png Binary files differnew file mode 100644 index 00000000..cc5e930f --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/26.png diff --git a/libkdegames/carddecks/cards-penguins/27.png b/libkdegames/carddecks/cards-penguins/27.png Binary files differnew file mode 100644 index 00000000..4ae7702a --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/27.png diff --git a/libkdegames/carddecks/cards-penguins/28.png b/libkdegames/carddecks/cards-penguins/28.png Binary files differnew file mode 100644 index 00000000..ee9b40ea --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/28.png diff --git a/libkdegames/carddecks/cards-penguins/29.png b/libkdegames/carddecks/cards-penguins/29.png Binary files differnew file mode 100644 index 00000000..ba2dfe7d --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/29.png diff --git a/libkdegames/carddecks/cards-penguins/3.png b/libkdegames/carddecks/cards-penguins/3.png Binary files differnew file mode 100644 index 00000000..7f53daf9 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/3.png diff --git a/libkdegames/carddecks/cards-penguins/30.png b/libkdegames/carddecks/cards-penguins/30.png Binary files differnew file mode 100644 index 00000000..1486cf98 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/30.png diff --git a/libkdegames/carddecks/cards-penguins/31.png b/libkdegames/carddecks/cards-penguins/31.png Binary files differnew file mode 100644 index 00000000..805d39e9 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/31.png diff --git a/libkdegames/carddecks/cards-penguins/32.png b/libkdegames/carddecks/cards-penguins/32.png Binary files differnew file mode 100644 index 00000000..4a1b12f0 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/32.png diff --git a/libkdegames/carddecks/cards-penguins/33.png b/libkdegames/carddecks/cards-penguins/33.png Binary files differnew file mode 100644 index 00000000..b8ae8965 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/33.png diff --git a/libkdegames/carddecks/cards-penguins/34.png b/libkdegames/carddecks/cards-penguins/34.png Binary files differnew file mode 100644 index 00000000..e2ce160c --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/34.png diff --git a/libkdegames/carddecks/cards-penguins/35.png b/libkdegames/carddecks/cards-penguins/35.png Binary files differnew file mode 100644 index 00000000..580c4d12 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/35.png diff --git a/libkdegames/carddecks/cards-penguins/36.png b/libkdegames/carddecks/cards-penguins/36.png Binary files differnew file mode 100644 index 00000000..296d4c7c --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/36.png diff --git a/libkdegames/carddecks/cards-penguins/37.png b/libkdegames/carddecks/cards-penguins/37.png Binary files differnew file mode 100644 index 00000000..fbbefa84 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/37.png diff --git a/libkdegames/carddecks/cards-penguins/38.png b/libkdegames/carddecks/cards-penguins/38.png Binary files differnew file mode 100644 index 00000000..7d9a34bd --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/38.png diff --git a/libkdegames/carddecks/cards-penguins/39.png b/libkdegames/carddecks/cards-penguins/39.png Binary files differnew file mode 100644 index 00000000..23ffffaa --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/39.png diff --git a/libkdegames/carddecks/cards-penguins/4.png b/libkdegames/carddecks/cards-penguins/4.png Binary files differnew file mode 100644 index 00000000..79ced119 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/4.png diff --git a/libkdegames/carddecks/cards-penguins/40.png b/libkdegames/carddecks/cards-penguins/40.png Binary files differnew file mode 100644 index 00000000..b9bed320 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/40.png diff --git a/libkdegames/carddecks/cards-penguins/41.png b/libkdegames/carddecks/cards-penguins/41.png Binary files differnew file mode 100644 index 00000000..c5a65385 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/41.png diff --git a/libkdegames/carddecks/cards-penguins/42.png b/libkdegames/carddecks/cards-penguins/42.png Binary files differnew file mode 100644 index 00000000..aba32e3e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/42.png diff --git a/libkdegames/carddecks/cards-penguins/43.png b/libkdegames/carddecks/cards-penguins/43.png Binary files differnew file mode 100644 index 00000000..04623c1e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/43.png diff --git a/libkdegames/carddecks/cards-penguins/44.png b/libkdegames/carddecks/cards-penguins/44.png Binary files differnew file mode 100644 index 00000000..7e6069e6 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/44.png diff --git a/libkdegames/carddecks/cards-penguins/45.png b/libkdegames/carddecks/cards-penguins/45.png Binary files differnew file mode 100644 index 00000000..36b67487 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/45.png diff --git a/libkdegames/carddecks/cards-penguins/46.png b/libkdegames/carddecks/cards-penguins/46.png Binary files differnew file mode 100644 index 00000000..80216f18 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/46.png diff --git a/libkdegames/carddecks/cards-penguins/47.png b/libkdegames/carddecks/cards-penguins/47.png Binary files differnew file mode 100644 index 00000000..9fb16882 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/47.png diff --git a/libkdegames/carddecks/cards-penguins/48.png b/libkdegames/carddecks/cards-penguins/48.png Binary files differnew file mode 100644 index 00000000..12f928e6 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/48.png diff --git a/libkdegames/carddecks/cards-penguins/49.png b/libkdegames/carddecks/cards-penguins/49.png Binary files differnew file mode 100644 index 00000000..9119c351 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/49.png diff --git a/libkdegames/carddecks/cards-penguins/5.png b/libkdegames/carddecks/cards-penguins/5.png Binary files differnew file mode 100644 index 00000000..f18a3a57 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/5.png diff --git a/libkdegames/carddecks/cards-penguins/50.png b/libkdegames/carddecks/cards-penguins/50.png Binary files differnew file mode 100644 index 00000000..e3dacdba --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/50.png diff --git a/libkdegames/carddecks/cards-penguins/51.png b/libkdegames/carddecks/cards-penguins/51.png Binary files differnew file mode 100644 index 00000000..fa46ff72 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/51.png diff --git a/libkdegames/carddecks/cards-penguins/52.png b/libkdegames/carddecks/cards-penguins/52.png Binary files differnew file mode 100644 index 00000000..1522ed3f --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/52.png diff --git a/libkdegames/carddecks/cards-penguins/6.png b/libkdegames/carddecks/cards-penguins/6.png Binary files differnew file mode 100644 index 00000000..9d84eb8d --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/6.png diff --git a/libkdegames/carddecks/cards-penguins/7.png b/libkdegames/carddecks/cards-penguins/7.png Binary files differnew file mode 100644 index 00000000..2b8d0768 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/7.png diff --git a/libkdegames/carddecks/cards-penguins/8.png b/libkdegames/carddecks/cards-penguins/8.png Binary files differnew file mode 100644 index 00000000..933be6e7 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/8.png diff --git a/libkdegames/carddecks/cards-penguins/9.png b/libkdegames/carddecks/cards-penguins/9.png Binary files differnew file mode 100644 index 00000000..669d6e00 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/9.png diff --git a/libkdegames/carddecks/cards-penguins/COPYRIGHT b/libkdegames/carddecks/cards-penguins/COPYRIGHT new file mode 100644 index 00000000..cf87594e --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/COPYRIGHT @@ -0,0 +1,10 @@ +This PySol cardset was adapted from the Ace of Penguins 1.0. +http://www.delorie.com/store/ace/ + +Copyright (C) 1998 DJ Delorie <dj@delorie.com> +Copyright (C) 1998 Markus F.X.J. Oberhumer <markus.oberhumer@jk.uni-linz.ac.at> + +This cardset 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/libkdegames/carddecks/cards-penguins/index.desktop b/libkdegames/carddecks/cards-penguins/index.desktop new file mode 100644 index 00000000..2fe12197 --- /dev/null +++ b/libkdegames/carddecks/cards-penguins/index.desktop @@ -0,0 +1,67 @@ +[KDE Backdeck] +Name=Penguins +Name[af]=Pikkewyne +Name[ar]=بطاريق +Name[az]=Pinqvinlər +Name[be]=Пінгвіны +Name[bg]=Пингвин +Name[bn]=পেঙ্গুইন +Name[br]=Pennoù-gwenn +Name[bs]=Pingvini +Name[ca]=Pingüins +Name[cs]=Tučňáci +Name[da]=Pingviner +Name[de]=Pinguine +Name[el]=Πιγκουίνοι +Name[eo]=Pingvenoj +Name[es]=Pingüinos +Name[et]=Pingviinid +Name[eu]=Pinguinoak +Name[fa]=پنگوئنها +Name[fi]=Pingviinit +Name[fr]=Pingouins +Name[gl]=Pingüíns +Name[he]=פנגוינים +Name[hi]=पेंग्विन्स +Name[hr]=Pingvini +Name[hu]=Pingvinek +Name[is]=Mörgæsir +Name[it]=Pinguini +Name[km]=ភេនឃ្វីន +Name[ko]=펭귄 +Name[lt]=Pingvinai +Name[lv]=Pingvīni +Name[mk]=Пингвини +Name[mt]=Pingwini +Name[nb]=Pingviner +Name[nds]=Pinguins +Name[ne]=पेन्गुइन +Name[nl]=Pinguïns +Name[nn]=Pingvinar +Name[pa]=ਪੈਂਗੂਇਨ +Name[pl]=Pingwiny +Name[pt]=Pinguins +Name[pt_BR]=Pingüins +Name[ro]=Pinguini +Name[ru]=Пингвины +Name[se]=Pingviinnat +Name[sk]=Tučniaci +Name[sl]=Pingvini +Name[sr]=Пингвини +Name[sr@Latn]=Pingvini +Name[sv]=Pingviner +Name[ta]=பென்குயின்கள் +Name[tg]=Пингвинҳо +Name[th]=เพนกวิน +Name[tr]=Penguenler +Name[uk]=Пінгвіни +Name[uz]=Pingvinlar +Name[uz@cyrillic]=Пингвинлар +Name[vi]=Chim cánh cụt +Name[wa]=Pingwins +Name[xh]=iintaka zaselwandle +Name[zh_CN]=企鹅 +Name[zh_TW]=企鵝 +Name[zu]=Izinyoni zasemanzini +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-spaced/1.png b/libkdegames/carddecks/cards-spaced/1.png Binary files differnew file mode 100644 index 00000000..62220b38 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/1.png diff --git a/libkdegames/carddecks/cards-spaced/10.png b/libkdegames/carddecks/cards-spaced/10.png Binary files differnew file mode 100644 index 00000000..d0bcd828 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/10.png diff --git a/libkdegames/carddecks/cards-spaced/11.png b/libkdegames/carddecks/cards-spaced/11.png Binary files differnew file mode 100644 index 00000000..b4e6016e --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/11.png diff --git a/libkdegames/carddecks/cards-spaced/12.png b/libkdegames/carddecks/cards-spaced/12.png Binary files differnew file mode 100644 index 00000000..45ab5cf5 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/12.png diff --git a/libkdegames/carddecks/cards-spaced/13.png b/libkdegames/carddecks/cards-spaced/13.png Binary files differnew file mode 100644 index 00000000..37e7c2ce --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/13.png diff --git a/libkdegames/carddecks/cards-spaced/14.png b/libkdegames/carddecks/cards-spaced/14.png Binary files differnew file mode 100644 index 00000000..97af1f02 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/14.png diff --git a/libkdegames/carddecks/cards-spaced/15.png b/libkdegames/carddecks/cards-spaced/15.png Binary files differnew file mode 100644 index 00000000..fb2fcc76 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/15.png diff --git a/libkdegames/carddecks/cards-spaced/16.png b/libkdegames/carddecks/cards-spaced/16.png Binary files differnew file mode 100644 index 00000000..9042b161 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/16.png diff --git a/libkdegames/carddecks/cards-spaced/17.png b/libkdegames/carddecks/cards-spaced/17.png Binary files differnew file mode 100644 index 00000000..a67b2832 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/17.png diff --git a/libkdegames/carddecks/cards-spaced/18.png b/libkdegames/carddecks/cards-spaced/18.png Binary files differnew file mode 100644 index 00000000..f4c8a04b --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/18.png diff --git a/libkdegames/carddecks/cards-spaced/19.png b/libkdegames/carddecks/cards-spaced/19.png Binary files differnew file mode 100644 index 00000000..fac5b199 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/19.png diff --git a/libkdegames/carddecks/cards-spaced/2.png b/libkdegames/carddecks/cards-spaced/2.png Binary files differnew file mode 100644 index 00000000..af3c76fd --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/2.png diff --git a/libkdegames/carddecks/cards-spaced/20.png b/libkdegames/carddecks/cards-spaced/20.png Binary files differnew file mode 100644 index 00000000..12d43520 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/20.png diff --git a/libkdegames/carddecks/cards-spaced/21.png b/libkdegames/carddecks/cards-spaced/21.png Binary files differnew file mode 100644 index 00000000..ea715adc --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/21.png diff --git a/libkdegames/carddecks/cards-spaced/22.png b/libkdegames/carddecks/cards-spaced/22.png Binary files differnew file mode 100644 index 00000000..13e6d7ae --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/22.png diff --git a/libkdegames/carddecks/cards-spaced/23.png b/libkdegames/carddecks/cards-spaced/23.png Binary files differnew file mode 100644 index 00000000..f43a745f --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/23.png diff --git a/libkdegames/carddecks/cards-spaced/24.png b/libkdegames/carddecks/cards-spaced/24.png Binary files differnew file mode 100644 index 00000000..810f59ef --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/24.png diff --git a/libkdegames/carddecks/cards-spaced/25.png b/libkdegames/carddecks/cards-spaced/25.png Binary files differnew file mode 100644 index 00000000..836b9863 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/25.png diff --git a/libkdegames/carddecks/cards-spaced/26.png b/libkdegames/carddecks/cards-spaced/26.png Binary files differnew file mode 100644 index 00000000..27bc29bf --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/26.png diff --git a/libkdegames/carddecks/cards-spaced/27.png b/libkdegames/carddecks/cards-spaced/27.png Binary files differnew file mode 100644 index 00000000..a44baa51 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/27.png diff --git a/libkdegames/carddecks/cards-spaced/28.png b/libkdegames/carddecks/cards-spaced/28.png Binary files differnew file mode 100644 index 00000000..af480ddd --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/28.png diff --git a/libkdegames/carddecks/cards-spaced/29.png b/libkdegames/carddecks/cards-spaced/29.png Binary files differnew file mode 100644 index 00000000..622968f4 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/29.png diff --git a/libkdegames/carddecks/cards-spaced/3.png b/libkdegames/carddecks/cards-spaced/3.png Binary files differnew file mode 100644 index 00000000..5b31e13b --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/3.png diff --git a/libkdegames/carddecks/cards-spaced/30.png b/libkdegames/carddecks/cards-spaced/30.png Binary files differnew file mode 100644 index 00000000..e43e9bd0 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/30.png diff --git a/libkdegames/carddecks/cards-spaced/31.png b/libkdegames/carddecks/cards-spaced/31.png Binary files differnew file mode 100644 index 00000000..382d0c11 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/31.png diff --git a/libkdegames/carddecks/cards-spaced/32.png b/libkdegames/carddecks/cards-spaced/32.png Binary files differnew file mode 100644 index 00000000..969e921d --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/32.png diff --git a/libkdegames/carddecks/cards-spaced/33.png b/libkdegames/carddecks/cards-spaced/33.png Binary files differnew file mode 100644 index 00000000..e9dbd109 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/33.png diff --git a/libkdegames/carddecks/cards-spaced/34.png b/libkdegames/carddecks/cards-spaced/34.png Binary files differnew file mode 100644 index 00000000..49d26d66 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/34.png diff --git a/libkdegames/carddecks/cards-spaced/35.png b/libkdegames/carddecks/cards-spaced/35.png Binary files differnew file mode 100644 index 00000000..f60149e5 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/35.png diff --git a/libkdegames/carddecks/cards-spaced/36.png b/libkdegames/carddecks/cards-spaced/36.png Binary files differnew file mode 100644 index 00000000..49b7044a --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/36.png diff --git a/libkdegames/carddecks/cards-spaced/37.png b/libkdegames/carddecks/cards-spaced/37.png Binary files differnew file mode 100644 index 00000000..dafb3402 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/37.png diff --git a/libkdegames/carddecks/cards-spaced/38.png b/libkdegames/carddecks/cards-spaced/38.png Binary files differnew file mode 100644 index 00000000..f1d7e030 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/38.png diff --git a/libkdegames/carddecks/cards-spaced/39.png b/libkdegames/carddecks/cards-spaced/39.png Binary files differnew file mode 100644 index 00000000..8e045381 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/39.png diff --git a/libkdegames/carddecks/cards-spaced/4.png b/libkdegames/carddecks/cards-spaced/4.png Binary files differnew file mode 100644 index 00000000..e8ceab32 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/4.png diff --git a/libkdegames/carddecks/cards-spaced/40.png b/libkdegames/carddecks/cards-spaced/40.png Binary files differnew file mode 100644 index 00000000..de378577 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/40.png diff --git a/libkdegames/carddecks/cards-spaced/41.png b/libkdegames/carddecks/cards-spaced/41.png Binary files differnew file mode 100644 index 00000000..0e4f3382 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/41.png diff --git a/libkdegames/carddecks/cards-spaced/42.png b/libkdegames/carddecks/cards-spaced/42.png Binary files differnew file mode 100644 index 00000000..910524a2 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/42.png diff --git a/libkdegames/carddecks/cards-spaced/43.png b/libkdegames/carddecks/cards-spaced/43.png Binary files differnew file mode 100644 index 00000000..f9f218bf --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/43.png diff --git a/libkdegames/carddecks/cards-spaced/44.png b/libkdegames/carddecks/cards-spaced/44.png Binary files differnew file mode 100644 index 00000000..fe64a155 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/44.png diff --git a/libkdegames/carddecks/cards-spaced/45.png b/libkdegames/carddecks/cards-spaced/45.png Binary files differnew file mode 100644 index 00000000..d914f7b1 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/45.png diff --git a/libkdegames/carddecks/cards-spaced/46.png b/libkdegames/carddecks/cards-spaced/46.png Binary files differnew file mode 100644 index 00000000..f6953f9d --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/46.png diff --git a/libkdegames/carddecks/cards-spaced/47.png b/libkdegames/carddecks/cards-spaced/47.png Binary files differnew file mode 100644 index 00000000..bfaa56ff --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/47.png diff --git a/libkdegames/carddecks/cards-spaced/48.png b/libkdegames/carddecks/cards-spaced/48.png Binary files differnew file mode 100644 index 00000000..b5d46c34 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/48.png diff --git a/libkdegames/carddecks/cards-spaced/49.png b/libkdegames/carddecks/cards-spaced/49.png Binary files differnew file mode 100644 index 00000000..f890b5db --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/49.png diff --git a/libkdegames/carddecks/cards-spaced/5.png b/libkdegames/carddecks/cards-spaced/5.png Binary files differnew file mode 100644 index 00000000..e160f9b3 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/5.png diff --git a/libkdegames/carddecks/cards-spaced/50.png b/libkdegames/carddecks/cards-spaced/50.png Binary files differnew file mode 100644 index 00000000..488eb5fa --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/50.png diff --git a/libkdegames/carddecks/cards-spaced/51.png b/libkdegames/carddecks/cards-spaced/51.png Binary files differnew file mode 100644 index 00000000..4d1f41b0 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/51.png diff --git a/libkdegames/carddecks/cards-spaced/52.png b/libkdegames/carddecks/cards-spaced/52.png Binary files differnew file mode 100644 index 00000000..345579f4 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/52.png diff --git a/libkdegames/carddecks/cards-spaced/6.png b/libkdegames/carddecks/cards-spaced/6.png Binary files differnew file mode 100644 index 00000000..90e8e998 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/6.png diff --git a/libkdegames/carddecks/cards-spaced/7.png b/libkdegames/carddecks/cards-spaced/7.png Binary files differnew file mode 100644 index 00000000..374ee771 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/7.png diff --git a/libkdegames/carddecks/cards-spaced/8.png b/libkdegames/carddecks/cards-spaced/8.png Binary files differnew file mode 100644 index 00000000..987a24c6 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/8.png diff --git a/libkdegames/carddecks/cards-spaced/9.png b/libkdegames/carddecks/cards-spaced/9.png Binary files differnew file mode 100644 index 00000000..f2855e9f --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/9.png diff --git a/libkdegames/carddecks/cards-spaced/COPYRIGHT b/libkdegames/carddecks/cards-spaced/COPYRIGHT new file mode 100644 index 00000000..f54e3a4e --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/COPYRIGHT @@ -0,0 +1,16 @@ +The backs for these cards came from the U.S. National Aeronautics +and Space Administration. The original images can be found at: +http://antwrp.gsfc.nasa.gov/apod/archivepix.html +along with a lot more just like 'em. + +The penguins are by "The PAPA" <papalini@biancaneve.ing.unifi.it> +and can be found at: +http://biancaneve.ing.unifi.it/~papalini/ +and there's a lot more of those too. + +Copyright (C) 1999 T. Kirk <grania@mailcity.com> + +This card set 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/libkdegames/carddecks/cards-spaced/index.desktop b/libkdegames/carddecks/cards-spaced/index.desktop new file mode 100644 index 00000000..0e9eadf0 --- /dev/null +++ b/libkdegames/carddecks/cards-spaced/index.desktop @@ -0,0 +1,46 @@ +[KDE Backdeck] +Name=Spaced +Name[af]=Gespasieer +Name[az]=Boşluqlu +Name[be]=Космас +Name[bg]=Звезден +Name[bn]=স্পেসযুক্ত +Name[ca]=Espaiades +Name[cs]=Vesmír +Name[de]=Mit Abstand +Name[eo]=Komika +Name[es]=Espaciadas +Name[eu]=Tartea +Name[fr]=Spatial +Name[gl]=Espaciadas +Name[hi]=स्पेस्ड +Name[hu]=Űr +Name[is]=Speisað +Name[it]=Spaziale +Name[ko]=우주 +Name[lv]=Kosmisks +Name[mk]=Раздалечени +Name[mt]=Spazzjat +Name[nb]=Rom-duell +Name[nds]=Afstand +Name[ne]=खाली स्थान +Name[nl]=Ruimtelijk +Name[nso]=Beetswe Sekgoba +Name[pl]=Przestrzenny +Name[pt]=Espacial +Name[pt_BR]=Espaçado +Name[ro]=Spaţiat +Name[ru]=Космос +Name[sl]=Vesoljski dvoboj +Name[sr]=Размакнут +Name[sr@Latn]=Razmaknut +Name[sv]=Spejsad +Name[ta]=இடம் விடப்பட்ட +Name[tg]=Кайҳонӣ +Name[tr]=Boşluklu +Name[uk]=Космічний +Name[ven]=Hu na zwikala +Name[xh]=ukugqagqeneyo +Name[zu]=Kunezikhala +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-warwick/0.png b/libkdegames/carddecks/cards-warwick/0.png Binary files differnew file mode 100644 index 00000000..e8fc5c76 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/0.png diff --git a/libkdegames/carddecks/cards-warwick/1.png b/libkdegames/carddecks/cards-warwick/1.png Binary files differnew file mode 100644 index 00000000..79fc960c --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/1.png diff --git a/libkdegames/carddecks/cards-warwick/10.png b/libkdegames/carddecks/cards-warwick/10.png Binary files differnew file mode 100644 index 00000000..9b459b5c --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/10.png diff --git a/libkdegames/carddecks/cards-warwick/105.png b/libkdegames/carddecks/cards-warwick/105.png Binary files differnew file mode 100644 index 00000000..0f49bdd9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/105.png diff --git a/libkdegames/carddecks/cards-warwick/106.png b/libkdegames/carddecks/cards-warwick/106.png Binary files differnew file mode 100644 index 00000000..4e2371c1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/106.png diff --git a/libkdegames/carddecks/cards-warwick/107.png b/libkdegames/carddecks/cards-warwick/107.png Binary files differnew file mode 100644 index 00000000..c7a19052 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/107.png diff --git a/libkdegames/carddecks/cards-warwick/108.png b/libkdegames/carddecks/cards-warwick/108.png Binary files differnew file mode 100644 index 00000000..09ee0a0a --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/108.png diff --git a/libkdegames/carddecks/cards-warwick/109.png b/libkdegames/carddecks/cards-warwick/109.png Binary files differnew file mode 100644 index 00000000..a48b134c --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/109.png diff --git a/libkdegames/carddecks/cards-warwick/11.png b/libkdegames/carddecks/cards-warwick/11.png Binary files differnew file mode 100644 index 00000000..a78f94b7 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/11.png diff --git a/libkdegames/carddecks/cards-warwick/110.png b/libkdegames/carddecks/cards-warwick/110.png Binary files differnew file mode 100644 index 00000000..2a3fccd9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/110.png diff --git a/libkdegames/carddecks/cards-warwick/111.png b/libkdegames/carddecks/cards-warwick/111.png Binary files differnew file mode 100644 index 00000000..20c9f0f9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/111.png diff --git a/libkdegames/carddecks/cards-warwick/112.png b/libkdegames/carddecks/cards-warwick/112.png Binary files differnew file mode 100644 index 00000000..90935873 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/112.png diff --git a/libkdegames/carddecks/cards-warwick/113.png b/libkdegames/carddecks/cards-warwick/113.png Binary files differnew file mode 100644 index 00000000..7df5d9b1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/113.png diff --git a/libkdegames/carddecks/cards-warwick/114.png b/libkdegames/carddecks/cards-warwick/114.png Binary files differnew file mode 100644 index 00000000..062ae319 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/114.png diff --git a/libkdegames/carddecks/cards-warwick/115.png b/libkdegames/carddecks/cards-warwick/115.png Binary files differnew file mode 100644 index 00000000..fd4b83b4 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/115.png diff --git a/libkdegames/carddecks/cards-warwick/116.png b/libkdegames/carddecks/cards-warwick/116.png Binary files differnew file mode 100644 index 00000000..7f1c0dcd --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/116.png diff --git a/libkdegames/carddecks/cards-warwick/12.png b/libkdegames/carddecks/cards-warwick/12.png Binary files differnew file mode 100644 index 00000000..41d93ef2 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/12.png diff --git a/libkdegames/carddecks/cards-warwick/13.png b/libkdegames/carddecks/cards-warwick/13.png Binary files differnew file mode 100644 index 00000000..b990c512 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/13.png diff --git a/libkdegames/carddecks/cards-warwick/14.png b/libkdegames/carddecks/cards-warwick/14.png Binary files differnew file mode 100644 index 00000000..45b94a15 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/14.png diff --git a/libkdegames/carddecks/cards-warwick/15.png b/libkdegames/carddecks/cards-warwick/15.png Binary files differnew file mode 100644 index 00000000..46c1f3f6 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/15.png diff --git a/libkdegames/carddecks/cards-warwick/16.png b/libkdegames/carddecks/cards-warwick/16.png Binary files differnew file mode 100644 index 00000000..d8d118f9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/16.png diff --git a/libkdegames/carddecks/cards-warwick/17.png b/libkdegames/carddecks/cards-warwick/17.png Binary files differnew file mode 100644 index 00000000..e6e0aa93 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/17.png diff --git a/libkdegames/carddecks/cards-warwick/18.png b/libkdegames/carddecks/cards-warwick/18.png Binary files differnew file mode 100644 index 00000000..eb84255b --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/18.png diff --git a/libkdegames/carddecks/cards-warwick/19.png b/libkdegames/carddecks/cards-warwick/19.png Binary files differnew file mode 100644 index 00000000..88d4d6d9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/19.png diff --git a/libkdegames/carddecks/cards-warwick/2.png b/libkdegames/carddecks/cards-warwick/2.png Binary files differnew file mode 100644 index 00000000..98e798a1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/2.png diff --git a/libkdegames/carddecks/cards-warwick/20.png b/libkdegames/carddecks/cards-warwick/20.png Binary files differnew file mode 100644 index 00000000..4f75de50 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/20.png diff --git a/libkdegames/carddecks/cards-warwick/21.png b/libkdegames/carddecks/cards-warwick/21.png Binary files differnew file mode 100644 index 00000000..39bba2f2 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/21.png diff --git a/libkdegames/carddecks/cards-warwick/22.png b/libkdegames/carddecks/cards-warwick/22.png Binary files differnew file mode 100644 index 00000000..733c396d --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/22.png diff --git a/libkdegames/carddecks/cards-warwick/23.png b/libkdegames/carddecks/cards-warwick/23.png Binary files differnew file mode 100644 index 00000000..ab00c341 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/23.png diff --git a/libkdegames/carddecks/cards-warwick/24.png b/libkdegames/carddecks/cards-warwick/24.png Binary files differnew file mode 100644 index 00000000..eeb73bdd --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/24.png diff --git a/libkdegames/carddecks/cards-warwick/25.png b/libkdegames/carddecks/cards-warwick/25.png Binary files differnew file mode 100644 index 00000000..25715b2f --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/25.png diff --git a/libkdegames/carddecks/cards-warwick/26.png b/libkdegames/carddecks/cards-warwick/26.png Binary files differnew file mode 100644 index 00000000..e248ded1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/26.png diff --git a/libkdegames/carddecks/cards-warwick/27.png b/libkdegames/carddecks/cards-warwick/27.png Binary files differnew file mode 100644 index 00000000..1bb2ab90 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/27.png diff --git a/libkdegames/carddecks/cards-warwick/28.png b/libkdegames/carddecks/cards-warwick/28.png Binary files differnew file mode 100644 index 00000000..33085ffb --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/28.png diff --git a/libkdegames/carddecks/cards-warwick/29.png b/libkdegames/carddecks/cards-warwick/29.png Binary files differnew file mode 100644 index 00000000..3f15d8d9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/29.png diff --git a/libkdegames/carddecks/cards-warwick/3.png b/libkdegames/carddecks/cards-warwick/3.png Binary files differnew file mode 100644 index 00000000..7f298774 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/3.png diff --git a/libkdegames/carddecks/cards-warwick/30.png b/libkdegames/carddecks/cards-warwick/30.png Binary files differnew file mode 100644 index 00000000..ce1f2dd9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/30.png diff --git a/libkdegames/carddecks/cards-warwick/31.png b/libkdegames/carddecks/cards-warwick/31.png Binary files differnew file mode 100644 index 00000000..fadee1b1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/31.png diff --git a/libkdegames/carddecks/cards-warwick/32.png b/libkdegames/carddecks/cards-warwick/32.png Binary files differnew file mode 100644 index 00000000..4d4391d2 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/32.png diff --git a/libkdegames/carddecks/cards-warwick/33.png b/libkdegames/carddecks/cards-warwick/33.png Binary files differnew file mode 100644 index 00000000..7d1a106f --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/33.png diff --git a/libkdegames/carddecks/cards-warwick/34.png b/libkdegames/carddecks/cards-warwick/34.png Binary files differnew file mode 100644 index 00000000..8d2197e7 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/34.png diff --git a/libkdegames/carddecks/cards-warwick/35.png b/libkdegames/carddecks/cards-warwick/35.png Binary files differnew file mode 100644 index 00000000..e8f5789d --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/35.png diff --git a/libkdegames/carddecks/cards-warwick/36.png b/libkdegames/carddecks/cards-warwick/36.png Binary files differnew file mode 100644 index 00000000..32bd75b8 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/36.png diff --git a/libkdegames/carddecks/cards-warwick/37.png b/libkdegames/carddecks/cards-warwick/37.png Binary files differnew file mode 100644 index 00000000..8a85b206 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/37.png diff --git a/libkdegames/carddecks/cards-warwick/38.png b/libkdegames/carddecks/cards-warwick/38.png Binary files differnew file mode 100644 index 00000000..edc98fa9 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/38.png diff --git a/libkdegames/carddecks/cards-warwick/39.png b/libkdegames/carddecks/cards-warwick/39.png Binary files differnew file mode 100644 index 00000000..6ac9b1ab --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/39.png diff --git a/libkdegames/carddecks/cards-warwick/4.png b/libkdegames/carddecks/cards-warwick/4.png Binary files differnew file mode 100644 index 00000000..ba94daeb --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/4.png diff --git a/libkdegames/carddecks/cards-warwick/40.png b/libkdegames/carddecks/cards-warwick/40.png Binary files differnew file mode 100644 index 00000000..046f281e --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/40.png diff --git a/libkdegames/carddecks/cards-warwick/41.png b/libkdegames/carddecks/cards-warwick/41.png Binary files differnew file mode 100644 index 00000000..278f7242 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/41.png diff --git a/libkdegames/carddecks/cards-warwick/42.png b/libkdegames/carddecks/cards-warwick/42.png Binary files differnew file mode 100644 index 00000000..e0c8857d --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/42.png diff --git a/libkdegames/carddecks/cards-warwick/43.png b/libkdegames/carddecks/cards-warwick/43.png Binary files differnew file mode 100644 index 00000000..126b5d8e --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/43.png diff --git a/libkdegames/carddecks/cards-warwick/44.png b/libkdegames/carddecks/cards-warwick/44.png Binary files differnew file mode 100644 index 00000000..220f6e45 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/44.png diff --git a/libkdegames/carddecks/cards-warwick/45.png b/libkdegames/carddecks/cards-warwick/45.png Binary files differnew file mode 100644 index 00000000..0a5742e1 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/45.png diff --git a/libkdegames/carddecks/cards-warwick/46.png b/libkdegames/carddecks/cards-warwick/46.png Binary files differnew file mode 100644 index 00000000..f4312a3b --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/46.png diff --git a/libkdegames/carddecks/cards-warwick/47.png b/libkdegames/carddecks/cards-warwick/47.png Binary files differnew file mode 100644 index 00000000..a4495a2e --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/47.png diff --git a/libkdegames/carddecks/cards-warwick/48.png b/libkdegames/carddecks/cards-warwick/48.png Binary files differnew file mode 100644 index 00000000..039755f8 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/48.png diff --git a/libkdegames/carddecks/cards-warwick/49.png b/libkdegames/carddecks/cards-warwick/49.png Binary files differnew file mode 100644 index 00000000..dd7b9779 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/49.png diff --git a/libkdegames/carddecks/cards-warwick/5.png b/libkdegames/carddecks/cards-warwick/5.png Binary files differnew file mode 100644 index 00000000..d881d1a8 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/5.png diff --git a/libkdegames/carddecks/cards-warwick/50.png b/libkdegames/carddecks/cards-warwick/50.png Binary files differnew file mode 100644 index 00000000..5840d1a7 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/50.png diff --git a/libkdegames/carddecks/cards-warwick/51.png b/libkdegames/carddecks/cards-warwick/51.png Binary files differnew file mode 100644 index 00000000..04628627 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/51.png diff --git a/libkdegames/carddecks/cards-warwick/52.png b/libkdegames/carddecks/cards-warwick/52.png Binary files differnew file mode 100644 index 00000000..07efb60a --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/52.png diff --git a/libkdegames/carddecks/cards-warwick/6.png b/libkdegames/carddecks/cards-warwick/6.png Binary files differnew file mode 100644 index 00000000..dbe64d0a --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/6.png diff --git a/libkdegames/carddecks/cards-warwick/7.png b/libkdegames/carddecks/cards-warwick/7.png Binary files differnew file mode 100644 index 00000000..16176ad2 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/7.png diff --git a/libkdegames/carddecks/cards-warwick/8.png b/libkdegames/carddecks/cards-warwick/8.png Binary files differnew file mode 100644 index 00000000..f62b3441 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/8.png diff --git a/libkdegames/carddecks/cards-warwick/9.png b/libkdegames/carddecks/cards-warwick/9.png Binary files differnew file mode 100644 index 00000000..a773d309 --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/9.png diff --git a/libkdegames/carddecks/cards-warwick/index.desktop b/libkdegames/carddecks/cards-warwick/index.desktop new file mode 100644 index 00000000..042b6f1b --- /dev/null +++ b/libkdegames/carddecks/cards-warwick/index.desktop @@ -0,0 +1,126 @@ +[KDE Backdeck] +Name=Blue Balloon +Name[af]=Blou Ballon +Name[ar]=بالون أزرق +Name[az]=Göy Balon +Name[be]=Паветраны шар +Name[bg]=Син балон +Name[bn]=নীল বেলুন +Name[br]=Boull glas +Name[bs]=Plavi balon +Name[ca]=Pilota blava +Name[cs]=Modrý balón +Name[cy]=Balwn Glas +Name[da]=Blå ballon +Name[de]=Blauer Ballon +Name[el]=Μπλε μπαλόνι +Name[eo]=Blua balono +Name[es]=Globo azul +Name[et]=Sinine õhupall +Name[eu]=Globo urdina +Name[fi]=Sininen pallo +Name[fr]=Ballon bleu +Name[gl]=Globo azul +Name[he]=בלון כחול +Name[hi]=ब्लू बलून +Name[hr]=Plavi balon +Name[hu]=Kék léggömb +Name[is]=Blá blaðra +Name[it]=Pallone blu +Name[ja]=青風船 +Name[km]=បាល់ខៀវ +Name[ko]=파란 풍선 +Name[lt]=Mėlynas balionas +Name[lv]=Zili baloni +Name[mk]=Син балон +Name[mt]=Bużżieqa Blu +Name[nb]=Blå ballong +Name[nds]=Blaag Ballon +Name[ne]=निलो बेलुन +Name[nl]=Blauwe ballon +Name[nn]=Blå ballong +Name[pa]=ਨੀਲਾ ਗੁਬਰਾ +Name[pl]=Niebieski balon +Name[pt]=Balão Azul +Name[pt_BR]=Balão Azul +Name[ro]=Balon albastru +Name[ru]=Воздушный шар +Name[se]=Alit balloŋga +Name[sk]=Modrý balón +Name[sl]=Modri balon +Name[sr]=Плави балон +Name[sr@Latn]=Plavi balon +Name[sv]=Blå ballong +Name[ta]=நீல பலூன் +Name[tg]=Курраҳои Ҳавоӣ +Name[th]=ลูกโป่งสีฟ้า - K +Name[tr]=Mavi Balon +Name[uk]=Блакитна кулька +Name[ven]=Baloni la muvhala wa Lutombo +Name[vi]=Bóng bay xanh +Name[xh]=Ibhaluni eblowu +Name[zh_CN]=蓝气球 +Name[zh_TW]=藍色氣球 +Name[zu]=Ibhelunde eliluhlaza +Preview=11.png +PySol=false +Comment=Card set supplied by Warwick Allison +Comment[af]=Kaart stel verskaf deur Warwick Allison +Comment[az]=Warwick Allison tərəfindən düzəldilən kart dəstəsi +Comment[be]=Калода картаў ад Ворвіка Элісана (Warwick Allison) +Comment[bg]=Колода карти от Warwick Allison +Comment[bn]=কার্ডের সেট সরবরাহ করেছেন ওয়ারউইক এল্লিসন +Comment[bs]=Špil je dostavio Warwick Allison +Comment[ca]=Joc de cartes aportat per Warwick Allison +Comment[cs]=Sada karet od Warwicka Allisona +Comment[cy]=Set cerdiau wedi ei ddarparu gan Warwick Allison +Comment[da]=Kortspil fra Warwick Allison +Comment[de]=Karten von Warwick Allison +Comment[el]=Σετ καρτών από τον Warwick Allison +Comment[eo]=Kartaro donita de Warwick Allison +Comment[es]=Juego de cartas suministrado por Warwick Allison +Comment[et]=Warwick Allison'i poolt pakutud kaardipakk +Comment[eu]=Warwick Allison-ek emandako karta-sorta +Comment[fa]=مجموعه کارت توسط وارویک آلیسون تهیه شد +Comment[fi]=Warwick Allison toimittama korttipakka +Comment[fr]=Jeu de cartes fourni par Warwick Allison +Comment[gl]=Baralla proporcionada por Warwick Allison +Comment[he]=ערכת הקלפים סופקה על ידי וורויק אליסון +Comment[hi]=ताश की गड्डी वारविक एलीसन द्वारा प्रदत्त किया गया +Comment[hr]=Set karata, poklonio Warwick Allison +Comment[hu]=Warwick Allison kártyacsomagja +Comment[is]=Spilastokkur eftir Warwick Allison +Comment[it]=Mazzo di carte fornito da Warwick Allison +Comment[ja]=Warwick Allison 作のカードセット +Comment[km]=បៀរត្រូវកំណត់ផ្ដល់ដោយ Warwick Allison +Comment[ko]=Warwick Allison이 만든 카드 셋 +Comment[lt]=Warwick Allison kortų rinkinys +Comment[lv]=Kāršu komplekts no Warwick Allison +Comment[mk]=Комплетот карти е обезбеден од Ворвик Алисон (Warwick Allison) +Comment[mt]=Sett ta' karti mogħti minn Warwick Allison +Comment[nb]=Kortstokk levert av Warwick Allison +Comment[nds]=Koorten vun Warwick Allison +Comment[ne]=वारविक एलिसोनद्वारा वितरण गरिएको कार्ड सेट +Comment[nl]=Kaartset, geleverd door Warwick Allison +Comment[nn]=Kortstokk frå Warwick Allison +Comment[pl]=Zestaw kart dostarczony przez Warwicka Allisona +Comment[pt]=Baralho de cartas fornecido por Warwick Allison +Comment[pt_BR]=Jogo de cartas fornecido por Warwick Allison +Comment[ro]=Set de cărţi de joc de Warwick Allison +Comment[ru]=Колода карт от Warwick Allison +Comment[sk]=Balíček kariet od Warwicka Allisona +Comment[sl]=Nabor kart od Warwicka Allisona +Comment[sr]=Шпил карата кога је обезбедио Варвик Алисон (Warwick Allison) +Comment[sr@Latn]=Špil karata koga je obezbedio Varvik Alison (Warwick Allison) +Comment[sv]=Kortuppsättning tillhandahållen av Warwick Allison +Comment[ta]=சீட்டுக்கட்டு வில்லியம் எலிசனால் அனுப்பப்பட்டது +Comment[tg]=Маҷмӯи Кортҳо, ки аз тарафи Warwick Allison пешниҳод шудаанд +Comment[th]=ชุดไพ่สนับสนุนโดย Warwick Allison +Comment[tr]=Kart kümeleri Warwick Allison tarafından sağlanmıştır +Comment[uk]=Набір карт від Warwick Allison +Comment[ven]=Garata yo diswa nga Warwick Allison +Comment[vi]=BềEthẻ được cung cấp bởi Warwick Allison +Comment[xh]=Imfano nye yamakhadi inikwe ngu Warwick Allison +Comment[zh_CN]=牌面由 Warwick Allison 提供 +Comment[zh_TW]=牌局由 Warwick Allison 提供 +Comment[zu]=Iqoqo lamakhadi linikelwe ngu-Warwick Allison diff --git a/libkdegames/carddecks/cards-xskat-french/1.png b/libkdegames/carddecks/cards-xskat-french/1.png Binary files differnew file mode 100644 index 00000000..fdc71a26 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/1.png diff --git a/libkdegames/carddecks/cards-xskat-french/10.png b/libkdegames/carddecks/cards-xskat-french/10.png Binary files differnew file mode 100644 index 00000000..9dede1c7 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/10.png diff --git a/libkdegames/carddecks/cards-xskat-french/11.png b/libkdegames/carddecks/cards-xskat-french/11.png Binary files differnew file mode 100644 index 00000000..4c1dbf2a --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/11.png diff --git a/libkdegames/carddecks/cards-xskat-french/12.png b/libkdegames/carddecks/cards-xskat-french/12.png Binary files differnew file mode 100644 index 00000000..e34fc6bb --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/12.png diff --git a/libkdegames/carddecks/cards-xskat-french/13.png b/libkdegames/carddecks/cards-xskat-french/13.png Binary files differnew file mode 100644 index 00000000..842935ca --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/13.png diff --git a/libkdegames/carddecks/cards-xskat-french/14.png b/libkdegames/carddecks/cards-xskat-french/14.png Binary files differnew file mode 100644 index 00000000..e87706aa --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/14.png diff --git a/libkdegames/carddecks/cards-xskat-french/15.png b/libkdegames/carddecks/cards-xskat-french/15.png Binary files differnew file mode 100644 index 00000000..28d82c71 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/15.png diff --git a/libkdegames/carddecks/cards-xskat-french/16.png b/libkdegames/carddecks/cards-xskat-french/16.png Binary files differnew file mode 100644 index 00000000..4920f2f4 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/16.png diff --git a/libkdegames/carddecks/cards-xskat-french/17.png b/libkdegames/carddecks/cards-xskat-french/17.png Binary files differnew file mode 100644 index 00000000..27658c7c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/17.png diff --git a/libkdegames/carddecks/cards-xskat-french/18.png b/libkdegames/carddecks/cards-xskat-french/18.png Binary files differnew file mode 100644 index 00000000..ea34c973 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/18.png diff --git a/libkdegames/carddecks/cards-xskat-french/19.png b/libkdegames/carddecks/cards-xskat-french/19.png Binary files differnew file mode 100644 index 00000000..80a0c22b --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/19.png diff --git a/libkdegames/carddecks/cards-xskat-french/2.png b/libkdegames/carddecks/cards-xskat-french/2.png Binary files differnew file mode 100644 index 00000000..de465ef6 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/2.png diff --git a/libkdegames/carddecks/cards-xskat-french/20.png b/libkdegames/carddecks/cards-xskat-french/20.png Binary files differnew file mode 100644 index 00000000..4a0aca66 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/20.png diff --git a/libkdegames/carddecks/cards-xskat-french/21.png b/libkdegames/carddecks/cards-xskat-french/21.png Binary files differnew file mode 100644 index 00000000..b7c6027f --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/21.png diff --git a/libkdegames/carddecks/cards-xskat-french/22.png b/libkdegames/carddecks/cards-xskat-french/22.png Binary files differnew file mode 100644 index 00000000..ae77e460 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/22.png diff --git a/libkdegames/carddecks/cards-xskat-french/23.png b/libkdegames/carddecks/cards-xskat-french/23.png Binary files differnew file mode 100644 index 00000000..967385fc --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/23.png diff --git a/libkdegames/carddecks/cards-xskat-french/24.png b/libkdegames/carddecks/cards-xskat-french/24.png Binary files differnew file mode 100644 index 00000000..e8b84baf --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/24.png diff --git a/libkdegames/carddecks/cards-xskat-french/25.png b/libkdegames/carddecks/cards-xskat-french/25.png Binary files differnew file mode 100644 index 00000000..f933ca25 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/25.png diff --git a/libkdegames/carddecks/cards-xskat-french/26.png b/libkdegames/carddecks/cards-xskat-french/26.png Binary files differnew file mode 100644 index 00000000..ab981154 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/26.png diff --git a/libkdegames/carddecks/cards-xskat-french/27.png b/libkdegames/carddecks/cards-xskat-french/27.png Binary files differnew file mode 100644 index 00000000..f34477ff --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/27.png diff --git a/libkdegames/carddecks/cards-xskat-french/28.png b/libkdegames/carddecks/cards-xskat-french/28.png Binary files differnew file mode 100644 index 00000000..0d1d71ff --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/28.png diff --git a/libkdegames/carddecks/cards-xskat-french/29.png b/libkdegames/carddecks/cards-xskat-french/29.png Binary files differnew file mode 100644 index 00000000..80e86965 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/29.png diff --git a/libkdegames/carddecks/cards-xskat-french/3.png b/libkdegames/carddecks/cards-xskat-french/3.png Binary files differnew file mode 100644 index 00000000..e4cc9d63 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/3.png diff --git a/libkdegames/carddecks/cards-xskat-french/30.png b/libkdegames/carddecks/cards-xskat-french/30.png Binary files differnew file mode 100644 index 00000000..a935d55e --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/30.png diff --git a/libkdegames/carddecks/cards-xskat-french/31.png b/libkdegames/carddecks/cards-xskat-french/31.png Binary files differnew file mode 100644 index 00000000..6c8d2883 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/31.png diff --git a/libkdegames/carddecks/cards-xskat-french/32.png b/libkdegames/carddecks/cards-xskat-french/32.png Binary files differnew file mode 100644 index 00000000..5af0268b --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/32.png diff --git a/libkdegames/carddecks/cards-xskat-french/33.png b/libkdegames/carddecks/cards-xskat-french/33.png Binary files differnew file mode 100644 index 00000000..7bd0fbed --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/33.png diff --git a/libkdegames/carddecks/cards-xskat-french/34.png b/libkdegames/carddecks/cards-xskat-french/34.png Binary files differnew file mode 100644 index 00000000..dc8bb29f --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/34.png diff --git a/libkdegames/carddecks/cards-xskat-french/35.png b/libkdegames/carddecks/cards-xskat-french/35.png Binary files differnew file mode 100644 index 00000000..7eb3f06f --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/35.png diff --git a/libkdegames/carddecks/cards-xskat-french/36.png b/libkdegames/carddecks/cards-xskat-french/36.png Binary files differnew file mode 100644 index 00000000..01902b33 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/36.png diff --git a/libkdegames/carddecks/cards-xskat-french/37.png b/libkdegames/carddecks/cards-xskat-french/37.png Binary files differnew file mode 100644 index 00000000..80599469 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/37.png diff --git a/libkdegames/carddecks/cards-xskat-french/38.png b/libkdegames/carddecks/cards-xskat-french/38.png Binary files differnew file mode 100644 index 00000000..fb4a37e4 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/38.png diff --git a/libkdegames/carddecks/cards-xskat-french/39.png b/libkdegames/carddecks/cards-xskat-french/39.png Binary files differnew file mode 100644 index 00000000..01fe85ae --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/39.png diff --git a/libkdegames/carddecks/cards-xskat-french/4.png b/libkdegames/carddecks/cards-xskat-french/4.png Binary files differnew file mode 100644 index 00000000..dff28353 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/4.png diff --git a/libkdegames/carddecks/cards-xskat-french/40.png b/libkdegames/carddecks/cards-xskat-french/40.png Binary files differnew file mode 100644 index 00000000..a9e28170 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/40.png diff --git a/libkdegames/carddecks/cards-xskat-french/41.png b/libkdegames/carddecks/cards-xskat-french/41.png Binary files differnew file mode 100644 index 00000000..66e69166 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/41.png diff --git a/libkdegames/carddecks/cards-xskat-french/42.png b/libkdegames/carddecks/cards-xskat-french/42.png Binary files differnew file mode 100644 index 00000000..92e21830 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/42.png diff --git a/libkdegames/carddecks/cards-xskat-french/43.png b/libkdegames/carddecks/cards-xskat-french/43.png Binary files differnew file mode 100644 index 00000000..b39a4afa --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/43.png diff --git a/libkdegames/carddecks/cards-xskat-french/44.png b/libkdegames/carddecks/cards-xskat-french/44.png Binary files differnew file mode 100644 index 00000000..94bf0fc7 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/44.png diff --git a/libkdegames/carddecks/cards-xskat-french/45.png b/libkdegames/carddecks/cards-xskat-french/45.png Binary files differnew file mode 100644 index 00000000..bbdb0ad6 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/45.png diff --git a/libkdegames/carddecks/cards-xskat-french/46.png b/libkdegames/carddecks/cards-xskat-french/46.png Binary files differnew file mode 100644 index 00000000..ed610b6a --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/46.png diff --git a/libkdegames/carddecks/cards-xskat-french/47.png b/libkdegames/carddecks/cards-xskat-french/47.png Binary files differnew file mode 100644 index 00000000..2688622a --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/47.png diff --git a/libkdegames/carddecks/cards-xskat-french/48.png b/libkdegames/carddecks/cards-xskat-french/48.png Binary files differnew file mode 100644 index 00000000..0dc1d52e --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/48.png diff --git a/libkdegames/carddecks/cards-xskat-french/49.png b/libkdegames/carddecks/cards-xskat-french/49.png Binary files differnew file mode 100644 index 00000000..4499c0fd --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/49.png diff --git a/libkdegames/carddecks/cards-xskat-french/5.png b/libkdegames/carddecks/cards-xskat-french/5.png Binary files differnew file mode 100644 index 00000000..702b65bc --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/5.png diff --git a/libkdegames/carddecks/cards-xskat-french/50.png b/libkdegames/carddecks/cards-xskat-french/50.png Binary files differnew file mode 100644 index 00000000..9b8b6619 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/50.png diff --git a/libkdegames/carddecks/cards-xskat-french/51.png b/libkdegames/carddecks/cards-xskat-french/51.png Binary files differnew file mode 100644 index 00000000..1edf32d5 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/51.png diff --git a/libkdegames/carddecks/cards-xskat-french/52.png b/libkdegames/carddecks/cards-xskat-french/52.png Binary files differnew file mode 100644 index 00000000..0fe25caa --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/52.png diff --git a/libkdegames/carddecks/cards-xskat-french/6.png b/libkdegames/carddecks/cards-xskat-french/6.png Binary files differnew file mode 100644 index 00000000..642225b1 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/6.png diff --git a/libkdegames/carddecks/cards-xskat-french/7.png b/libkdegames/carddecks/cards-xskat-french/7.png Binary files differnew file mode 100644 index 00000000..a16236b0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/7.png diff --git a/libkdegames/carddecks/cards-xskat-french/8.png b/libkdegames/carddecks/cards-xskat-french/8.png Binary files differnew file mode 100644 index 00000000..3cb261c4 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/8.png diff --git a/libkdegames/carddecks/cards-xskat-french/9.png b/libkdegames/carddecks/cards-xskat-french/9.png Binary files differnew file mode 100644 index 00000000..2b1d9c43 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/9.png diff --git a/libkdegames/carddecks/cards-xskat-french/COPYRIGHT b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT new file mode 100644 index 00000000..dc217dc3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/COPYRIGHT @@ -0,0 +1,8 @@ +This PySol cardset was adapted from the game XSkat 4.0 + +Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de) + +This cardset 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/libkdegames/carddecks/cards-xskat-french/index.desktop b/libkdegames/carddecks/cards-xskat-french/index.desktop new file mode 100644 index 00000000..3dec4a0c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-french/index.desktop @@ -0,0 +1,55 @@ +[KDE Backdeck] +Name=XSkat French +Name[be]=Французскі XSkat +Name[bg]=Френски модел +Name[bn]=ফরাসি এক্স-স্কাট +Name[br]=XSkat gallek +Name[bs]=XSkat Francuski +Name[ca]=XSkat francès +Name[cs]=Francouzský XSkat +Name[cy]=XSkat Ffrangeg +Name[da]=XSkat-fransk +Name[de]=XSkat Französisch +Name[el]=XSkat Γαλλικό +Name[eo]=XSkat Franca +Name[es]=XSkat francés +Name[et]=XSkat (Prantsuse) +Name[eu]=XSkat Frantsesa +Name[fa]=XSkat فرانسوی +Name[fi]=XSkat Ranska +Name[fr]=XSkat français +Name[he]=XSkat צרפתי +Name[hr]=Francuski XSkat +Name[hu]=FRancia XSkat +Name[is]=XSkat á frönsku +Name[it]=XSkat francese +Name[ja]=XSkat フランス語 +Name[km]=XSkat បារាំង +Name[ko]=프랑스 XSkat +Name[lt]=XSkat Prancūziškai +Name[lv]=XSkat Franču +Name[mk]=Француски XSkat +Name[nb]=Fransk XSkat +Name[nds]=XSkat (Franzöösch) +Name[ne]=एक्स स्क्याट फ्रेन्च +Name[nl]=XSkat Frans +Name[nn]=Fransk XSkat +Name[pa]=XSkat ਫਰੈਂਚ +Name[pl]=Francuski skat +Name[pt]=XSkat Francês +Name[pt_BR]=XSkat Francês +Name[ru]=Французский XSkat +Name[se]=Fránskkalaš XSkat +Name[sk]=XSkat francúzsky +Name[sl]=Francoski XSkat +Name[sr]=XSkat француски +Name[sr@Latn]=XSkat francuski +Name[sv]=Fransk X-skat +Name[ta]=XSkat ஃபிரஞ்ச் +Name[tg]=XSkat Фаронсавӣ +Name[tr]=XSkat Fransızca +Name[uk]=Французький XSkat +Name[zh_CN]=XSkat 法语 +Name[zh_TW]=XSkat 法語 +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/cards-xskat-german/1.png b/libkdegames/carddecks/cards-xskat-german/1.png Binary files differnew file mode 100644 index 00000000..e679884b --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/1.png diff --git a/libkdegames/carddecks/cards-xskat-german/10.png b/libkdegames/carddecks/cards-xskat-german/10.png Binary files differnew file mode 100644 index 00000000..c9590dbb --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/10.png diff --git a/libkdegames/carddecks/cards-xskat-german/11.png b/libkdegames/carddecks/cards-xskat-german/11.png Binary files differnew file mode 100644 index 00000000..d1c6f500 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/11.png diff --git a/libkdegames/carddecks/cards-xskat-german/12.png b/libkdegames/carddecks/cards-xskat-german/12.png Binary files differnew file mode 100644 index 00000000..5c92d83a --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/12.png diff --git a/libkdegames/carddecks/cards-xskat-german/13.png b/libkdegames/carddecks/cards-xskat-german/13.png Binary files differnew file mode 100644 index 00000000..d68f0c00 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/13.png diff --git a/libkdegames/carddecks/cards-xskat-german/14.png b/libkdegames/carddecks/cards-xskat-german/14.png Binary files differnew file mode 100644 index 00000000..324e3756 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/14.png diff --git a/libkdegames/carddecks/cards-xskat-german/15.png b/libkdegames/carddecks/cards-xskat-german/15.png Binary files differnew file mode 100644 index 00000000..9a516e30 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/15.png diff --git a/libkdegames/carddecks/cards-xskat-german/16.png b/libkdegames/carddecks/cards-xskat-german/16.png Binary files differnew file mode 100644 index 00000000..9d9dec03 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/16.png diff --git a/libkdegames/carddecks/cards-xskat-german/17.png b/libkdegames/carddecks/cards-xskat-german/17.png Binary files differnew file mode 100644 index 00000000..5dc10fa2 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/17.png diff --git a/libkdegames/carddecks/cards-xskat-german/18.png b/libkdegames/carddecks/cards-xskat-german/18.png Binary files differnew file mode 100644 index 00000000..90562629 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/18.png diff --git a/libkdegames/carddecks/cards-xskat-german/19.png b/libkdegames/carddecks/cards-xskat-german/19.png Binary files differnew file mode 100644 index 00000000..7e4552ab --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/19.png diff --git a/libkdegames/carddecks/cards-xskat-german/2.png b/libkdegames/carddecks/cards-xskat-german/2.png Binary files differnew file mode 100644 index 00000000..1c1f8118 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/2.png diff --git a/libkdegames/carddecks/cards-xskat-german/20.png b/libkdegames/carddecks/cards-xskat-german/20.png Binary files differnew file mode 100644 index 00000000..a15534b6 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/20.png diff --git a/libkdegames/carddecks/cards-xskat-german/21.png b/libkdegames/carddecks/cards-xskat-german/21.png Binary files differnew file mode 100644 index 00000000..7aca79e7 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/21.png diff --git a/libkdegames/carddecks/cards-xskat-german/22.png b/libkdegames/carddecks/cards-xskat-german/22.png Binary files differnew file mode 100644 index 00000000..b487a1c8 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/22.png diff --git a/libkdegames/carddecks/cards-xskat-german/23.png b/libkdegames/carddecks/cards-xskat-german/23.png Binary files differnew file mode 100644 index 00000000..761d3bfe --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/23.png diff --git a/libkdegames/carddecks/cards-xskat-german/24.png b/libkdegames/carddecks/cards-xskat-german/24.png Binary files differnew file mode 100644 index 00000000..537e590c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/24.png diff --git a/libkdegames/carddecks/cards-xskat-german/25.png b/libkdegames/carddecks/cards-xskat-german/25.png Binary files differnew file mode 100644 index 00000000..09567171 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/25.png diff --git a/libkdegames/carddecks/cards-xskat-german/26.png b/libkdegames/carddecks/cards-xskat-german/26.png Binary files differnew file mode 100644 index 00000000..761fc42d --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/26.png diff --git a/libkdegames/carddecks/cards-xskat-german/27.png b/libkdegames/carddecks/cards-xskat-german/27.png Binary files differnew file mode 100644 index 00000000..05083aa2 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/27.png diff --git a/libkdegames/carddecks/cards-xskat-german/28.png b/libkdegames/carddecks/cards-xskat-german/28.png Binary files differnew file mode 100644 index 00000000..e885a908 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/28.png diff --git a/libkdegames/carddecks/cards-xskat-german/29.png b/libkdegames/carddecks/cards-xskat-german/29.png Binary files differnew file mode 100644 index 00000000..5b7244a0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/29.png diff --git a/libkdegames/carddecks/cards-xskat-german/3.png b/libkdegames/carddecks/cards-xskat-german/3.png Binary files differnew file mode 100644 index 00000000..978e63c0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/3.png diff --git a/libkdegames/carddecks/cards-xskat-german/30.png b/libkdegames/carddecks/cards-xskat-german/30.png Binary files differnew file mode 100644 index 00000000..3f8b03ef --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/30.png diff --git a/libkdegames/carddecks/cards-xskat-german/31.png b/libkdegames/carddecks/cards-xskat-german/31.png Binary files differnew file mode 100644 index 00000000..9ab830ce --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/31.png diff --git a/libkdegames/carddecks/cards-xskat-german/32.png b/libkdegames/carddecks/cards-xskat-german/32.png Binary files differnew file mode 100644 index 00000000..b87d68d0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/32.png diff --git a/libkdegames/carddecks/cards-xskat-german/33.png b/libkdegames/carddecks/cards-xskat-german/33.png Binary files differnew file mode 100644 index 00000000..078abf39 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/33.png diff --git a/libkdegames/carddecks/cards-xskat-german/34.png b/libkdegames/carddecks/cards-xskat-german/34.png Binary files differnew file mode 100644 index 00000000..94c5297f --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/34.png diff --git a/libkdegames/carddecks/cards-xskat-german/35.png b/libkdegames/carddecks/cards-xskat-german/35.png Binary files differnew file mode 100644 index 00000000..241ba5dc --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/35.png diff --git a/libkdegames/carddecks/cards-xskat-german/36.png b/libkdegames/carddecks/cards-xskat-german/36.png Binary files differnew file mode 100644 index 00000000..339a155a --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/36.png diff --git a/libkdegames/carddecks/cards-xskat-german/37.png b/libkdegames/carddecks/cards-xskat-german/37.png Binary files differnew file mode 100644 index 00000000..106023f4 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/37.png diff --git a/libkdegames/carddecks/cards-xskat-german/38.png b/libkdegames/carddecks/cards-xskat-german/38.png Binary files differnew file mode 100644 index 00000000..848f9696 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/38.png diff --git a/libkdegames/carddecks/cards-xskat-german/39.png b/libkdegames/carddecks/cards-xskat-german/39.png Binary files differnew file mode 100644 index 00000000..43eaba16 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/39.png diff --git a/libkdegames/carddecks/cards-xskat-german/4.png b/libkdegames/carddecks/cards-xskat-german/4.png Binary files differnew file mode 100644 index 00000000..d69848a0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/4.png diff --git a/libkdegames/carddecks/cards-xskat-german/40.png b/libkdegames/carddecks/cards-xskat-german/40.png Binary files differnew file mode 100644 index 00000000..87619dce --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/40.png diff --git a/libkdegames/carddecks/cards-xskat-german/41.png b/libkdegames/carddecks/cards-xskat-german/41.png Binary files differnew file mode 100644 index 00000000..6b09c88c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/41.png diff --git a/libkdegames/carddecks/cards-xskat-german/42.png b/libkdegames/carddecks/cards-xskat-german/42.png Binary files differnew file mode 100644 index 00000000..04a1993b --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/42.png diff --git a/libkdegames/carddecks/cards-xskat-german/43.png b/libkdegames/carddecks/cards-xskat-german/43.png Binary files differnew file mode 100644 index 00000000..d5b92e29 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/43.png diff --git a/libkdegames/carddecks/cards-xskat-german/44.png b/libkdegames/carddecks/cards-xskat-german/44.png Binary files differnew file mode 100644 index 00000000..6cf2e09b --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/44.png diff --git a/libkdegames/carddecks/cards-xskat-german/45.png b/libkdegames/carddecks/cards-xskat-german/45.png Binary files differnew file mode 100644 index 00000000..b6a6a2a7 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/45.png diff --git a/libkdegames/carddecks/cards-xskat-german/46.png b/libkdegames/carddecks/cards-xskat-german/46.png Binary files differnew file mode 100644 index 00000000..05f686ab --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/46.png diff --git a/libkdegames/carddecks/cards-xskat-german/47.png b/libkdegames/carddecks/cards-xskat-german/47.png Binary files differnew file mode 100644 index 00000000..f31c89f3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/47.png diff --git a/libkdegames/carddecks/cards-xskat-german/48.png b/libkdegames/carddecks/cards-xskat-german/48.png Binary files differnew file mode 100644 index 00000000..a8e1c51e --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/48.png diff --git a/libkdegames/carddecks/cards-xskat-german/49.png b/libkdegames/carddecks/cards-xskat-german/49.png Binary files differnew file mode 100644 index 00000000..2a473ac3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/49.png diff --git a/libkdegames/carddecks/cards-xskat-german/5.png b/libkdegames/carddecks/cards-xskat-german/5.png Binary files differnew file mode 100644 index 00000000..66a4b568 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/5.png diff --git a/libkdegames/carddecks/cards-xskat-german/50.png b/libkdegames/carddecks/cards-xskat-german/50.png Binary files differnew file mode 100644 index 00000000..3a7d6ac0 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/50.png diff --git a/libkdegames/carddecks/cards-xskat-german/51.png b/libkdegames/carddecks/cards-xskat-german/51.png Binary files differnew file mode 100644 index 00000000..09e25a6c --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/51.png diff --git a/libkdegames/carddecks/cards-xskat-german/52.png b/libkdegames/carddecks/cards-xskat-german/52.png Binary files differnew file mode 100644 index 00000000..e7d74ae4 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/52.png diff --git a/libkdegames/carddecks/cards-xskat-german/6.png b/libkdegames/carddecks/cards-xskat-german/6.png Binary files differnew file mode 100644 index 00000000..e4b1d7de --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/6.png diff --git a/libkdegames/carddecks/cards-xskat-german/7.png b/libkdegames/carddecks/cards-xskat-german/7.png Binary files differnew file mode 100644 index 00000000..09e99d70 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/7.png diff --git a/libkdegames/carddecks/cards-xskat-german/8.png b/libkdegames/carddecks/cards-xskat-german/8.png Binary files differnew file mode 100644 index 00000000..58fdc56e --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/8.png diff --git a/libkdegames/carddecks/cards-xskat-german/9.png b/libkdegames/carddecks/cards-xskat-german/9.png Binary files differnew file mode 100644 index 00000000..b55ee847 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/9.png diff --git a/libkdegames/carddecks/cards-xskat-german/COPYRIGHT b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT new file mode 100644 index 00000000..dc217dc3 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/COPYRIGHT @@ -0,0 +1,8 @@ +This PySol cardset was adapted from the game XSkat 4.0 + +Copyright (C) 2004 Gunter Gerhardt (http://www.xskat.de) + +This cardset 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/libkdegames/carddecks/cards-xskat-german/index.desktop b/libkdegames/carddecks/cards-xskat-german/index.desktop new file mode 100644 index 00000000..3f19d526 --- /dev/null +++ b/libkdegames/carddecks/cards-xskat-german/index.desktop @@ -0,0 +1,55 @@ +[KDE Backdeck] +Name=XSkat German +Name[be]=Нямецкі XSkat +Name[bg]=Немски модел +Name[bn]=জর্মন এক্স-স্কাট +Name[br]=XSkat alamanek +Name[bs]=XSkat Njemački +Name[ca]=XSkat alemany +Name[cs]=Německý XSkat +Name[cy]=XSkat Almaeneg +Name[da]=XSkat-tysk +Name[de]=XSkat Deutsch +Name[el]=XSkat Γερμανικό +Name[eo]=XSkat Germana +Name[es]=XSkat alemán +Name[et]=XSkat (Saksa) +Name[eu]=XSkat alemaniera +Name[fa]=آلمانی XSkat +Name[fi]=XSkat Saksa +Name[fr]=XSkat allemand +Name[he]=XSkat גרמני +Name[hr]=Njemački XSkat +Name[hu]=Német XSkat +Name[is]=XSkat á þýsku +Name[it]=XSkat tedesco +Name[ja]=XSkat ドイツ語 +Name[km]=XSkat អាល្លឺម៉ង់ +Name[ko]=독일 XSkat +Name[lt]=XSkat Vokiškai +Name[lv]=XSkat Vācu +Name[mk]=Германски XSkat +Name[nb]=Tysk XSkat +Name[nds]=XSkat (Düütsch) +Name[ne]=एक्स स्क्याट जर्मन +Name[nl]=XSkat Duits +Name[nn]=Tysk XSkat +Name[pa]=XSkat ਜਰਮਨ +Name[pl]=Niemiecki skat +Name[pt]=XSkat Alemã +Name[pt_BR]=XSkat Alemão +Name[ru]=Немецкий XSkat +Name[se]=Duiskkalaš XSkat +Name[sk]=XScat nemecký +Name[sl]=Nemški XSkat +Name[sr]=XSkat немачки +Name[sr@Latn]=XSkat nemački +Name[sv]=Tysk X-skat +Name[ta]=XSkat ஜெர்மன் +Name[tg]=XSkat Олмонӣ +Name[tr]=XSkat Almanca +Name[uk]=Німецький XSkat +Name[zh_CN]=XSkat 德语 +Name[zh_TW]=XSkat 德語 +Preview=11.png +PySol=yes diff --git a/libkdegames/carddecks/convertpysols b/libkdegames/carddecks/convertpysols new file mode 100755 index 00000000..5ed10b25 --- /dev/null +++ b/libkdegames/carddecks/convertpysols @@ -0,0 +1,69 @@ +#! /bin/sh + +cd $1 + +convert -format png -geometry "72x96" 01c.gif 1.png +convert -format png -geometry "72x96" 01s.gif 2.png +convert -format png -geometry "72x96" 01h.gif 3.png +convert -format png -geometry "72x96" 01d.gif 4.png + +convert -format png -geometry "72x96" 13c.gif 5.png +convert -format png -geometry "72x96" 13s.gif 6.png +convert -format png -geometry "72x96" 13h.gif 7.png +convert -format png -geometry "72x96" 13d.gif 8.png + +convert -format png -geometry "72x96" 12c.gif 9.png +convert -format png -geometry "72x96" 12s.gif 10.png +convert -format png -geometry "72x96" 12h.gif 11.png +convert -format png -geometry "72x96" 12d.gif 12.png + +convert -format png -geometry "72x96" 11c.gif 13.png +convert -format png -geometry "72x96" 11s.gif 14.png +convert -format png -geometry "72x96" 11h.gif 15.png +convert -format png -geometry "72x96" 11d.gif 16.png + +convert -format png -geometry "72x96" 10c.gif 17.png +convert -format png -geometry "72x96" 10s.gif 18.png +convert -format png -geometry "72x96" 10h.gif 19.png +convert -format png -geometry "72x96" 10d.gif 20.png + +convert -format png -geometry "72x96" 09c.gif 21.png +convert -format png -geometry "72x96" 09s.gif 22.png +convert -format png -geometry "72x96" 09h.gif 23.png +convert -format png -geometry "72x96" 09d.gif 24.png + +convert -format png -geometry "72x96" 08c.gif 25.png +convert -format png -geometry "72x96" 08s.gif 26.png +convert -format png -geometry "72x96" 08h.gif 27.png +convert -format png -geometry "72x96" 08d.gif 28.png + +convert -format png -geometry "72x96" 07c.gif 29.png +convert -format png -geometry "72x96" 07s.gif 30.png +convert -format png -geometry "72x96" 07h.gif 31.png +convert -format png -geometry "72x96" 07d.gif 32.png + +convert -format png -geometry "72x96" 06c.gif 33.png +convert -format png -geometry "72x96" 06s.gif 34.png +convert -format png -geometry "72x96" 06h.gif 35.png +convert -format png -geometry "72x96" 06d.gif 36.png + +convert -format png -geometry "72x96" 05c.gif 37.png +convert -format png -geometry "72x96" 05s.gif 38.png +convert -format png -geometry "72x96" 05h.gif 39.png +convert -format png -geometry "72x96" 05d.gif 40.png + +convert -format png -geometry "72x96" 04c.gif 41.png +convert -format png -geometry "72x96" 04s.gif 42.png +convert -format png -geometry "72x96" 04h.gif 43.png +convert -format png -geometry "72x96" 04d.gif 44.png + +convert -format png -geometry "72x96" 03c.gif 45.png +convert -format png -geometry "72x96" 03s.gif 46.png +convert -format png -geometry "72x96" 03h.gif 47.png +convert -format png -geometry "72x96" 03d.gif 48.png + +convert -format png -geometry "72x96" 02c.gif 49.png +convert -format png -geometry "72x96" 02s.gif 50.png +convert -format png -geometry "72x96" 02h.gif 51.png +convert -format png -geometry "72x96" 02d.gif 52.png + diff --git a/libkdegames/carddecks/decks/deck0.desktop b/libkdegames/carddecks/decks/deck0.desktop new file mode 100644 index 00000000..0388ea4b --- /dev/null +++ b/libkdegames/carddecks/decks/deck0.desktop @@ -0,0 +1,113 @@ +[KDE Cards] +Name=Technics +Name[az]=Texnik +Name[be]=Тэхніка +Name[bg]=Техника +Name[bn]=টেকনিক্স +Name[bs]=Tehnički +Name[ca]=Tècniques +Name[cs]=Technika +Name[eo]=Tekniko +Name[es]=Técnicas +Name[et]=Tehnika +Name[eu]=Teknikoa +Name[fi]=Tekniikka +Name[fr]=Technique +Name[gl]=Técnicas +Name[hi]=टेक्निक्स +Name[hr]=Tehnika +Name[hu]=Technikai +Name[id]=Teknik +Name[is]=Tækni +Name[it]=Tecnico +Name[km]=វិធីសាស្ត្រ +Name[lt]=Technika +Name[lv]=Tehnika +Name[mk]=Техника +Name[mt]=Tekniċi +Name[nb]=Teknisk +Name[nds]=Technik +Name[ne]=उपाय +Name[nl]=Technisch +Name[nn]=Teknisk +Name[pt]=Técnico +Name[pt_BR]=Técnicas +Name[ro]=Tehnic +Name[ru]=Шестерёнки +Name[se]=Teknihkalaš +Name[sk]=Technické +Name[sl]=Tehnika +Name[sr]=Техникс +Name[sr@Latn]=Tehniks +Name[sv]=Teknik +Name[ta]=நுணுக்கங்கள் +Name[tg]=Шомгонаҳо +Name[th]=เทคนิค +Name[tr]=Teknik +Name[uk]=Шестерні +Name[ven]=Madaela +Name[vi]=Kĩ thuật +Name[xh]=iindlela zokudlala +Name[zh_CN]=工艺 +Name[zh_TW]=工藝 +Comment=Standard KDE card deck +Comment[af]=Standaard Kde kaart pak +Comment[az]=Standart KDE kart dəstəsi +Comment[be]=Стандартная калода картаў KDE +Comment[bg]=Стандартна колода карти за KDE +Comment[bn]=কে.ডি.ই.-র সাধারণ তাস সেট +Comment[bs]=Standardni KDE špil karata +Comment[ca]=Joc de cartes estàndard del KDE +Comment[cs]=Standardní sada karet KDE +Comment[cy]=Set cerdiau safonol KDE +Comment[da]=Standard KDE-kortspil +Comment[de]=Standardmäßige KDE-Karten +Comment[el]=Προκαθορισμένο σύνολο τράπουλας του KDE +Comment[eo]=Normala KDEa kartaro +Comment[es]=Baraja de cartas estándar de KDE +Comment[et]=Standardne KDE kaardipakk +Comment[eu]=KDE-ren karta-sorta +Comment[fa]=دسته کارت استاندارد KDE +Comment[fi]=Normaali KDE:n korttipakka +Comment[fr]=Jeu de cartes standard de KDE +Comment[gl]=Baralla de cartas estándar de KDE +Comment[he]=חפיסת הקלפים הסטנדרטית של KDE +Comment[hi]=मानक केडीई ताश गड्डी +Comment[hr]=Standardni KDE komplet karata +Comment[hu]=Standard KDE kártyacsomag +Comment[is]=Venjulegi KDE spilastokkurinn +Comment[it]=Mazzo di carte standard di KDE +Comment[ja]=KDE 標準カードデッキ +Comment[km]=ហូបៀ KDE ខ្នាតគំរូ +Comment[ko]=표준 KDE 카드 모음 +Comment[lt]=Standartinė KDE kortų kaladė +Comment[lv]=Standarta KDE kāršu galds +Comment[mk]=Стандарден шпил карти на KDE +Comment[mt]=Mazz karti standard tal-KDE +Comment[nb]=Standard KDE-kortstokk +Comment[nds]=KDE-Standardkoorten +Comment[ne]=मानक केडीई कार्ड डेक +Comment[nl]=Standaard KDE-kaartrug +Comment[nn]=Standard KDE-kortstokk +Comment[pa]=ਮਿਆਰੀ KDE ਤਾਸ਼ ਪੱਤੇ +Comment[pl]=Standardowy zestaw kart KDE +Comment[pt]=Baralho de cartas por omissão do KDE +Comment[pt_BR]=Baralho padrão do KDE +Comment[ro]=Set de cărţi de joc KDE standard +Comment[ru]=Стандартная колода карт KDE +Comment[sk]=Štandardný balíček kariet KDE +Comment[sl]=Standardni komplet kart za KDE +Comment[sr]=Стандардни KDE-ов шпил карата +Comment[sr@Latn]=Standardni KDE-ov špil karata +Comment[sv]=Förvald KDE-kortlek +Comment[ta]=நிலையான கேடிஇ சீட்டின் மேல் தளம் +Comment[tg]=Маҷмӯи кортҳои муқаррарии KDE +Comment[th]=ชุดไพ่มาตรฐานของ KDE +Comment[tr]=Standart KDE kart destesi +Comment[uk]=Типовий малюнок карт KDE +Comment[ven]=Tshidzhumba tsha magarata tsha murole wa KDE +Comment[vi]=KDE card deck chuẩn +Comment[xh]=Umgangatho womphezulu wamakhadi eKDE +Comment[zh_CN]=标准 KDE 牌垛 +Comment[zh_TW]=標準 KDE 紙牌花色 +Comment[zu]=Isigaxa samakhadi esilinganisiwe we-KDE diff --git a/libkdegames/carddecks/decks/deck0.png b/libkdegames/carddecks/decks/deck0.png Binary files differnew file mode 100644 index 00000000..0a877a51 --- /dev/null +++ b/libkdegames/carddecks/decks/deck0.png diff --git a/libkdegames/carddecks/decks/deck1.desktop b/libkdegames/carddecks/decks/deck1.desktop new file mode 100644 index 00000000..0a8c2cf7 --- /dev/null +++ b/libkdegames/carddecks/decks/deck1.desktop @@ -0,0 +1,56 @@ +[KDE Cards] +Name=Fairy +Name[af]=Fee +Name[az]=Pəri +Name[be]=Фея +Name[bg]=Фея +Name[bn]=পরী +Name[ca]=Fada +Name[cs]=Víla +Name[cy]=Tylwyth Teg +Name[da]=Fe +Name[de]=Elfe +Name[el]=Νεράιδα +Name[eo]=Elfo +Name[es]=Hada +Name[et]=Haldjas +Name[eu]=Maitagarria +Name[fa]=جن و پری +Name[fi]=Keijukainen +Name[fr]=Féérie +Name[gl]=Fada +Name[hi]=परी +Name[hr]=Vila +Name[hu]=Tündéres +Name[is]=Álfur +Name[it]=Fata +Name[km]=ទេវតា +Name[ko]=요정 +Name[lt]=Fėja +Name[lv]=Pasaku +Name[mk]=Самовила +Name[mt]=Għafrid +Name[nb]=Fe +Name[nds]=Fee +Name[ne]=अप्सरा +Name[nl]=Fee +Name[nn]=Fe +Name[pl]=Wróżkarski +Name[pt]=Fada +Name[pt_BR]=Mágico +Name[ro]=Basm +Name[ru]=Фея +Name[sr]=Вила +Name[sr@Latn]=Vila +Name[sv]=Älva +Name[ta]=தேவதை +Name[tg]=Парӣ +Name[tr]=Peri +Name[uk]=Чарівний +Name[uz]=Pari +Name[uz@cyrillic]=Пари +Name[ven]=Zwi a pfesesea +Name[vi]=Xinh đẹp +Name[zh_CN]=仙女 +Name[zh_TW]=仙女 +Name[zu]=Okunomlingo diff --git a/libkdegames/carddecks/decks/deck1.png b/libkdegames/carddecks/decks/deck1.png Binary files differnew file mode 100644 index 00000000..247875a2 --- /dev/null +++ b/libkdegames/carddecks/decks/deck1.png diff --git a/libkdegames/carddecks/decks/deck10.desktop b/libkdegames/carddecks/decks/deck10.desktop new file mode 100644 index 00000000..5e871ede --- /dev/null +++ b/libkdegames/carddecks/decks/deck10.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Classic Blue +Name[af]=Klasieke Blou +Name[ar]=أزرق كلاسيكي +Name[az]=Klassik Göy +Name[be]=Класічны сіні +Name[bg]=Класическо синьо +Name[bn]=ক্লাসিক নীল +Name[bs]=Klasični plavi +Name[ca]=Blau clàssic +Name[cs]=Klasická modrá +Name[cy]=Glas Clasurol +Name[da]=Klassisk blå +Name[de]=Klassisches Blau +Name[el]=Κλασσικό μπλε +Name[eo]=Klasika bluo +Name[es]=Azul clásico +Name[et]=Klassikaline sinine +Name[eu]=Urdin klasikoa +Name[fa]=آبی کلاسیک +Name[fi]=Klassinen sininen +Name[fr]=Bleu classique +Name[gl]=Azul clásico +Name[he]=כחול קלסי +Name[hi]=आदर्श नीला +Name[hr]=Klasično plava +Name[hu]=Klasszikus kék +Name[id]=Biru klasik +Name[is]=Klassískur blár +Name[it]=Blu classico +Name[ja]=クラシックブルー +Name[km]=ខៀវក្លាស៊ិក +Name[ko]=고전 파란색 +Name[lt]=Klasikinė mėlyna +Name[lv]=Klasiski zils +Name[mk]=Класично сино +Name[mt]=Blu Klassiku +Name[nb]=Klassisk Blå +Name[nds]=Klass'sch Blaag +Name[ne]=उत्कृष्ट निलो +Name[nl]=Klassiek blauw +Name[nn]=Klassisk blå +Name[pa]=ਟਕਸਾਲੀ ਨੀਲੇ +Name[pl]=Klasyczny niebieski +Name[pt]=Azul Clássico +Name[pt_BR]=Azul Clássico +Name[ro]=Albastru clasic +Name[ru]=Классический синий +Name[se]=Klassihkalaš alit +Name[sk]=Klasické modré +Name[sl]=Klasična modra +Name[sr]=Класични плави +Name[sr@Latn]=Klasični plavi +Name[sv]=Klassisk blå +Name[ta]=சிறந்த நீலம் +Name[tg]=Кабуди Классикӣ +Name[th]=ฟ้าคลาสสิค +Name[tr]=Klasik Mavi +Name[uk]=Блакитний +Name[ven]=Muvhala wa Lutombo wa Maimo +Name[vi]=Màu xanh cềEđiển +Name[wa]=Bleu classike +Name[xh]=Umbala oblowu omdala +Name[zh_CN]=经典蓝 +Name[zh_TW]=古典藍 +Name[zu]=Ubuluhlaza obuhle diff --git a/libkdegames/carddecks/decks/deck10.png b/libkdegames/carddecks/decks/deck10.png Binary files differnew file mode 100644 index 00000000..c84dcf73 --- /dev/null +++ b/libkdegames/carddecks/decks/deck10.png diff --git a/libkdegames/carddecks/decks/deck11.desktop b/libkdegames/carddecks/decks/deck11.desktop new file mode 100644 index 00000000..8d0fbb44 --- /dev/null +++ b/libkdegames/carddecks/decks/deck11.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Classic Red +Name[af]=Klasieke Rooi +Name[ar]=أحمر كلاسيكي +Name[az]=Klassik Qırmızı +Name[be]=Класічны чырвоны +Name[bg]=Класическо червено +Name[bn]=ক্লাসিক লাল +Name[bs]=Klasični crveni +Name[ca]=Vermell clàssic +Name[cs]=Klasická červená +Name[cy]=Coch Clasurol +Name[da]=Klassisk Rød +Name[de]=Klassisches Rot +Name[el]=Κλασσικό κόκκινο +Name[eo]=Klasika ruĝo +Name[es]=Rojo clásico +Name[et]=Klassikaline punane +Name[eu]=Gorri klasikoa +Name[fa]=قرمز کلاسیک +Name[fi]=Klassinen punainen +Name[fr]=Rouge classique +Name[gl]=Vermello clásico +Name[he]=אדום קלסי +Name[hi]=आदर्श लाल +Name[hr]=Klasično crvena +Name[hu]=klasszikus vörös +Name[id]=Merah klasik +Name[is]=Klassískur rauður +Name[it]=Rosso classico +Name[ja]=クラシックレッド +Name[km]=ក្រហមក្លាស៊ិក +Name[ko]=고전 빨간색 +Name[lt]=Klasikinė raudona +Name[lv]=Klasiski sarkans +Name[mk]=Класично црвено +Name[mt]=Aħmar Klassiku +Name[nb]=Klassisk Rød +Name[nds]=Klass'sch Root +Name[ne]=उत्कृष्ट रातो +Name[nl]=Klassiek rood +Name[nn]=Klassisk raud +Name[pa]=ਟਕਸਾਲੀ ਲਾਲ +Name[pl]=Klasyczny czerwony +Name[pt]=Vermelho Clássico +Name[pt_BR]=Vermelho Clássico +Name[ro]=Roşu clasic +Name[ru]=Классический красный +Name[se]=Klassihkalaš ruoksat +Name[sk]=Klasické červené +Name[sl]=Klasična rdeča +Name[sr]=Класични црвени +Name[sr@Latn]=Klasični crveni +Name[sv]=Klassisk röd +Name[ta]= சிறந்த சிகப்பு +Name[tg]=Сурхи Классикӣ +Name[th]=แดงคลาสสิค +Name[tr]=Klasik Kırmızı +Name[uk]=Червоний +Name[ven]=Muvhala Mutshuku wa Maimo +Name[vi]=ĐềEcềEđiển +Name[wa]=Rodje classike +Name[xh]=Umbala obomvu wakudala +Name[zh_CN]=经典红 +Name[zh_TW]=古典紅 +Name[zu]=Ububomvu obuhle diff --git a/libkdegames/carddecks/decks/deck11.png b/libkdegames/carddecks/decks/deck11.png Binary files differnew file mode 100644 index 00000000..0474a551 --- /dev/null +++ b/libkdegames/carddecks/decks/deck11.png diff --git a/libkdegames/carddecks/decks/deck12.desktop b/libkdegames/carddecks/decks/deck12.desktop new file mode 100644 index 00000000..aa0918bd --- /dev/null +++ b/libkdegames/carddecks/decks/deck12.desktop @@ -0,0 +1,41 @@ +[KDE Cards] +Name=Chin +Name[af]=Ken +Name[az]=Çənə +Name[be]=Памада +Name[bg]=Чин +Name[bn]=চিবুক +Name[cs]=Brada +Name[cy]=Gên +Name[eo]=Vizaĝo +Name[eu]=Kokotsa +Name[fr]=Visage +Name[hi]=ठोढ़ी +Name[hr]=Brada +Name[is]=Haka +Name[it]=Mento +Name[ja]=中国 +Name[km]=ចង្កា +Name[lt]=Smakras +Name[lv]=Seja +Name[mk]=Брада +Name[nds]=Kinn +Name[ne]=चिन +Name[nl]=Kin +Name[pl]=Podbródek +Name[ro]=Bărbie +Name[ru]=Помада +Name[sl]=Džin +Name[sr]=Брада +Name[sr@Latn]=Brada +Name[sv]=Pingla +Name[ta]=தாடை +Name[tg]=Манаҳ +Name[tr]=Çene +Name[uk]=Красуня +Name[uz@cyrillic]=Чин +Name[ven]=Tshitefu +Name[xh]=Isilevu +Name[zh_CN]=头像 +Name[zh_TW]=中國 +Name[zu]=Isilevu diff --git a/libkdegames/carddecks/decks/deck12.png b/libkdegames/carddecks/decks/deck12.png Binary files differnew file mode 100644 index 00000000..df9f6b71 --- /dev/null +++ b/libkdegames/carddecks/decks/deck12.png diff --git a/libkdegames/carddecks/decks/deck13.desktop b/libkdegames/carddecks/decks/deck13.desktop new file mode 100644 index 00000000..37180379 --- /dev/null +++ b/libkdegames/carddecks/decks/deck13.desktop @@ -0,0 +1,63 @@ +[KDE Cards] +Name=Copy +Name[af]=Kopie +Name[ar]=إنسخ +Name[az]=Köçür +Name[be]=Скапіяваць +Name[bg]=Ръка +Name[bn]=কপি +Name[br]=Eilañ +Name[bs]=Kopiraj +Name[ca]=Còpia +Name[cs]=Kopie +Name[cy]=Copio +Name[da]=Kopi +Name[de]=Kopie +Name[eo]=Mano +Name[es]=Copia +Name[et]=Koopia +Name[eu]=Kopiatu +Name[fi]=Kopio +Name[fo]=Avrit +Name[fr]=Copie +Name[ga]=Cóipeáil +Name[gl]=Copia +Name[hi]=नक़ल +Name[hr]=Kopija +Name[hu]=Másolás +Name[id]=Salin +Name[is]=Afrit +Name[it]=Impronta +Name[ja]=コピー +Name[km]=ចម្លង +Name[lt]=Kopija +Name[lv]=Kopija +Name[mk]=Копија +Name[mt]=Kopja +Name[nb]=Kopier +Name[nds]=Kopie +Name[ne]=प्रतिलिपि +Name[nl]=Hand +Name[nn]=Kopi +Name[pl]=Kopia +Name[pt]=Cópia +Name[pt_BR]=Cópia +Name[ro]=Copie +Name[ru]=Отпечаток +Name[se]=Máŋgus +Name[sl]=Prepis +Name[sr]=Копија +Name[sr@Latn]=Kopija +Name[sv]=Kopia +Name[ta]=படியெடு +Name[tg]=Нақш +Name[tr]=Kopyala +Name[uk]=Копія +Name[uz]=Nusxa +Name[uz@cyrillic]=Нусха +Name[ven]=Tshikopololwa +Name[wa]=Copyî +Name[xh]=Ukukopa +Name[zh_CN]=复制 +Name[zh_TW]=復制品 +Name[zu]=Khiphela diff --git a/libkdegames/carddecks/decks/deck13.png b/libkdegames/carddecks/decks/deck13.png Binary files differnew file mode 100644 index 00000000..377ec7a1 --- /dev/null +++ b/libkdegames/carddecks/decks/deck13.png diff --git a/libkdegames/carddecks/decks/deck14.desktop b/libkdegames/carddecks/decks/deck14.desktop new file mode 100644 index 00000000..fd5df9f8 --- /dev/null +++ b/libkdegames/carddecks/decks/deck14.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Penguin +Name[af]=Pikkewyn +Name[ar]=بطريق +Name[az]=Pinqvin +Name[be]=Пінгвін +Name[bg]=Пингвин +Name[bn]=পেঙ্গুইন +Name[br]=Penn-gwenn +Name[bs]=Pingvin +Name[ca]=Pingüí +Name[cs]=Tučňák +Name[cy]=Pengwin +Name[da]=Pingvin +Name[de]=Pinguin +Name[el]=Πιγκουίνος +Name[eo]=Pingveno +Name[es]=Pingüino +Name[et]=Pingviin +Name[eu]=Pinguinoa +Name[fa]=پنگوئن +Name[fi]=Pingviini +Name[fr]=Pingouin +Name[gl]=Pingüín +Name[he]=פינגווין +Name[hi]=पेंग्विन +Name[hr]=Pingvin +Name[hu]=Pingvin +Name[is]=Mörgæs +Name[it]=Pinguino +Name[km]=ផេនឃ្វីន +Name[ko]=펭귄 +Name[lt]=Pingvinas +Name[lv]=Pingvīns +Name[mk]=Пингвин +Name[mt]=Pingwin +Name[nb]=Pingvin +Name[nds]=Pinguin +Name[ne]=पेन्गुइन +Name[nl]=Pinguïn +Name[nn]=Pingvin +Name[pa]=ਪੈਂਗੂਇਨ +Name[pl]=Pingwin +Name[pt]=Pinguim +Name[pt_BR]=Pingüim +Name[ro]=Pinguin +Name[ru]=Пингвин +Name[se]=Piŋviidna +Name[sk]=Tučniak +Name[sl]=Pingvin +Name[sr]=Пингвин +Name[sr@Latn]=Pingvin +Name[sv]=Pingvin +Name[ta]=பென்குயின் +Name[tg]=Пингвин +Name[th]=เพนกวิน +Name[tr]=Penguen +Name[uk]=Пінгвін +Name[uz]=Pingvin +Name[uz@cyrillic]=Пингвин +Name[vi]=Chim cánh cụt +Name[wa]=Pingwin +Name[xh]=Ipenguin +Name[zh_CN]=企鹅 +Name[zh_TW]=企鵝 +Name[zu]=Inyoni yasemanzini diff --git a/libkdegames/carddecks/decks/deck14.png b/libkdegames/carddecks/decks/deck14.png Binary files differnew file mode 100644 index 00000000..694632bd --- /dev/null +++ b/libkdegames/carddecks/decks/deck14.png diff --git a/libkdegames/carddecks/decks/deck15.desktop b/libkdegames/carddecks/decks/deck15.desktop new file mode 100644 index 00000000..92015b81 --- /dev/null +++ b/libkdegames/carddecks/decks/deck15.desktop @@ -0,0 +1,19 @@ +[KDE Cards] +Name=Tristan +Name[be]=Трыстан +Name[bg]=Тристан +Name[bn]=ত্রিস্তান +Name[eo]=Ornamo +Name[hi]=ट्राईस्टान +Name[hu]=Trisztán +Name[lv]=Tristans +Name[mk]=Тристан +Name[ne]=ट्रिस्टान +Name[pt]=Tristão +Name[ru]=Тристан +Name[sr]=Тристан +Name[ta]=ட்ரிஸ்டன் +Name[tg]=Тристан +Name[uk]=Трістан +Name[uz@cyrillic]=Тристан +Name[zu]=I-Tristan diff --git a/libkdegames/carddecks/decks/deck15.png b/libkdegames/carddecks/decks/deck15.png Binary files differnew file mode 100644 index 00000000..73c3b92c --- /dev/null +++ b/libkdegames/carddecks/decks/deck15.png diff --git a/libkdegames/carddecks/decks/deck16.desktop b/libkdegames/carddecks/decks/deck16.desktop new file mode 100644 index 00000000..11ec9761 --- /dev/null +++ b/libkdegames/carddecks/decks/deck16.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Grandma +Name[af]=Ouma +Name[ar]=جدَة +Name[az]=Nənə +Name[be]=Бабуля +Name[bg]=Стар стил +Name[bn]=ঠাকুরমা, দাদী, নানী ইত্যাদি +Name[br]=Mamm-goz +Name[ca]=Àvia +Name[cs]=Babička +Name[cy]=Nain +Name[da]=Bedstemor +Name[de]=Großmutter +Name[el]=Γιαγιά +Name[eo]=Avinjo +Name[es]=Abuela +Name[et]=Vanaema +Name[eu]=Amona +Name[fi]=Isoäiti +Name[fo]=Omma +Name[fr]=Grand-mère +Name[gl]=Aboa +Name[he]=סבתא +Name[hi]=दादी अम्मा +Name[hr]=Baka +Name[hu]=Nagyi +Name[is]=Amma +Name[it]=Nonna +Name[km]=យាយ +Name[ko]=할머니 +Name[lv]=Vecmāmiņa +Name[mk]=Старовремско +Name[mt]=Nanna +Name[nb]=Bestemor +Name[nds]=Oma +Name[ne]=हजुरआमा +Name[nl]=Grootmoeder +Name[nn]=Bestemor +Name[pl]=Babcia +Name[pt]=Avó +Name[pt_BR]=Vovó +Name[ro]=Bunica +Name[ru]=Бабуля +Name[se]=Áhkku +Name[sk]=Stará mama +Name[sl]=Babica +Name[sr]=Бака +Name[sr@Latn]=Baka +Name[sv]=Farmor +Name[ta]=பாட்டி +Name[tg]=Модаркалон +Name[tr]=Büyük anne +Name[uk]=Бабуся +Name[ven]=Makhulu vha mukegulu +Name[wa]=Grand-mere +Name[xh]=Umakhulu +Name[zh_CN]=祖母 +Name[zh_TW]=祖母 +Name[zu]=Ugogo diff --git a/libkdegames/carddecks/decks/deck16.png b/libkdegames/carddecks/decks/deck16.png Binary files differnew file mode 100644 index 00000000..8310ecbe --- /dev/null +++ b/libkdegames/carddecks/decks/deck16.png diff --git a/libkdegames/carddecks/decks/deck17.desktop b/libkdegames/carddecks/decks/deck17.desktop new file mode 100644 index 00000000..1537939f --- /dev/null +++ b/libkdegames/carddecks/decks/deck17.desktop @@ -0,0 +1,65 @@ +[KDE Cards] +Name=Modern Red +Name[af]=Moderne Rooi +Name[ar]=أحمر حديث +Name[az]=Çağdaş Qırmızı +Name[be]=Сучасны чырвоны +Name[bg]=Модерно червено +Name[bn]=আধুনিক লাল +Name[bs]=Moderna crvena +Name[ca]=Vermell modern +Name[cs]=Moderní červená +Name[cy]=Coch Cyfoes +Name[da]=Moderne rød +Name[de]=Modernes Rot +Name[el]=Μοντέρνο κόκκινο +Name[eo]=Moderna ruĝo +Name[es]=Rojo moderno +Name[et]=Modernpunane +Name[eu]=Gorri modernoa +Name[fa]=قرمز مدرن +Name[fi]=Moderni punainen +Name[fr]=Rouge moderne +Name[gl]=Vermello moderno +Name[he]=אדום מודרני +Name[hi]=आधुनिक लाल +Name[hr]=Suvremeno crvena +Name[hu]=Modern vörös +Name[is]=Nútíma rauður +Name[it]=Rosso moderno +Name[ja]=モダンレッド +Name[km]=ពណ៌ក្រហមទំនើប +Name[ko]=현대적 빨간색 +Name[lt]=Moderni raudona +Name[lv]=Moderni sarkans +Name[mk]=Модерно црвено +Name[mt]=Aħmar modern +Name[nb]=Moderne Rød +Name[nds]=Modern Root +Name[ne]=उन्नत रातो +Name[nl]=Modern rood +Name[nn]=Moderne raud +Name[pa]=ਨਵਾਂ ਲਾਲ +Name[pl]=Nowoczesny czerwony +Name[pt]=Vermelho Moderno +Name[pt_BR]=Vermelho Moderno +Name[ro]=Roşu modern +Name[ru]=Современный красный +Name[se]=Ođđaáigásaš ruoksat +Name[sk]=Moderné červené +Name[sl]=Moderna rdeča +Name[sr]=Модерни црвени +Name[sr@Latn]=Moderni crveni +Name[sv]=Modern röd +Name[ta]=நவீன சிகப்பு +Name[tg]=Сурхи Замонавӣ +Name[th]=แดงทันสมัย +Name[tr]=Modern Kırmızı +Name[uk]=Плетінь +Name[ven]=Muvhala Mutshuku wa Tshizwino +Name[vi]=Màu đềEhiện đại +Name[wa]=Rodje modiene +Name[xh]=Umbala omtsha obomvu +Name[zh_CN]=现代红 +Name[zh_TW]=現代紅 +Name[zu]=Ububomvu besimanje diff --git a/libkdegames/carddecks/decks/deck17.png b/libkdegames/carddecks/decks/deck17.png Binary files differnew file mode 100644 index 00000000..71054897 --- /dev/null +++ b/libkdegames/carddecks/decks/deck17.png diff --git a/libkdegames/carddecks/decks/deck18.desktop b/libkdegames/carddecks/decks/deck18.desktop new file mode 100644 index 00000000..c3266a98 --- /dev/null +++ b/libkdegames/carddecks/decks/deck18.desktop @@ -0,0 +1,19 @@ +[KDE Cards] +Name=Holstentor +Name[be]=Палац +Name[bg]=Замък +Name[bn]=হোলস্টেনটর +Name[eo]=Holsten-pordego +Name[hi]=हॉल्स्टेंटर +Name[lv]=Holšteina +Name[mk]=Холстентор +Name[nds]=Holstendoor +Name[ne]=होल्सटेन्टर +Name[ru]=Замок +Name[sr]=Холстентор +Name[ta]=ஹால்ஸ்டென்டர் +Name[tg]=Қулф +Name[uk]=Замок +Name[ven]=Vhushavhelo +Name[zh_CN]=豪斯顿门 +Name[zu]=I-Holstentor diff --git a/libkdegames/carddecks/decks/deck18.png b/libkdegames/carddecks/decks/deck18.png Binary files differnew file mode 100644 index 00000000..fc93b02f --- /dev/null +++ b/libkdegames/carddecks/decks/deck18.png diff --git a/libkdegames/carddecks/decks/deck19.desktop b/libkdegames/carddecks/decks/deck19.desktop new file mode 100644 index 00000000..d77c2c98 --- /dev/null +++ b/libkdegames/carddecks/decks/deck19.desktop @@ -0,0 +1,63 @@ +[KDE Cards] +Name=Horizon +Name[af]=Horison +Name[ar]=الأفق +Name[az]=Üfüq +Name[be]=Гарызонт +Name[bg]=Хоризонт +Name[bn]=দিগন্ত +Name[br]=Dremmwel +Name[bs]=Horizont +Name[ca]=Horitzó +Name[cs]=Horizont +Name[cy]=Gorwel +Name[da]=Horisont +Name[de]=Horizont +Name[el]=Ορίζοντας +Name[eo]=Horizonto +Name[es]=Horizonte +Name[et]=Horisont +Name[eu]=Zeruertza +Name[fa]=افق +Name[fi]=Horisontti +Name[ga]=Léaslíne +Name[gl]=Horizonte +Name[he]=אופק +Name[hi]=क्षितिज +Name[hr]=Horizont +Name[hu]=Horizont +Name[is]=Sjóndeildarhringur +Name[it]=Orizzonte +Name[km]=ជើងមេឃ +Name[ko]=수평선 +Name[lv]=Horizonts +Name[mk]=Хоризонт +Name[mt]=Orizzont +Name[nb]=Horisont +Name[nds]=Kimm +Name[ne]=क्षितिज +Name[nn]=Horisont +Name[pa]=ਖੇਤਰ +Name[pl]=Horyzont +Name[pt]=Horizonte +Name[pt_BR]=Horizonte +Name[ro]=Orizont +Name[ru]=Горизонт +Name[sk]=Horizont +Name[sl]=Obzorje +Name[sr]=Хоризонт +Name[sr@Latn]=Horizont +Name[sv]=Horisont +Name[ta]=ஹாரிஸான் +Name[tg]=Уфуқ +Name[tr]=Ufuk +Name[uk]=Обрій +Name[uz]=Ufq +Name[uz@cyrillic]=Уфқ +Name[ven]=Magumoni a shango +Name[vi]=Chân trời +Name[wa]=Roye di cir +Name[xh]=Ulundi +Name[zh_CN]=地平线 +Name[zh_TW]=地平線 +Name[zu]=Okusesibhakabhakeni diff --git a/libkdegames/carddecks/decks/deck19.png b/libkdegames/carddecks/decks/deck19.png Binary files differnew file mode 100644 index 00000000..1526e0b3 --- /dev/null +++ b/libkdegames/carddecks/decks/deck19.png diff --git a/libkdegames/carddecks/decks/deck2.desktop b/libkdegames/carddecks/decks/deck2.desktop new file mode 100644 index 00000000..4e3906b2 --- /dev/null +++ b/libkdegames/carddecks/decks/deck2.desktop @@ -0,0 +1,49 @@ +[KDE Cards] +Name=Starrise +Name[ar]=ظهور النجوم +Name[be]=Узыход зоркі +Name[bg]=Звезди +Name[bn]=তারার উদয় +Name[ca]=Estrellat +Name[cs]=Úsvit +Name[cy]=Codiad y sêr +Name[de]=Sternenaufgang +Name[eo]=Stellumo +Name[et]=Tähetõus +Name[eu]=Izarrak +Name[fa]=پدیدار شدن ستاره +Name[fi]=Tähdennousu +Name[fr]=Étoiles +Name[he]=צאת הכוכבים +Name[hi]=स्टार-राइज़ +Name[hr]=Uspon zvijezda +Name[hu]=Csillagok +Name[is]=Ris stjarna +Name[it]=Alba spaziale +Name[ko]=별무리 +Name[lv]=Zvaigžņu lēkts +Name[mk]=Изгрев +Name[mt]=SemaStilel +Name[nb]=Stjerner +Name[nds]=Steernopgang +Name[ne]=स्टारराइज +Name[nl]=Sterrijzenis +Name[nn]=Stjerner +Name[pa]=ਚੜਦਾ ਤਾਰਾ +Name[pl]=Wschód gwiazdy +Name[ro]=Stelar +Name[ru]=Восход звезды +Name[se]=Násttit +Name[sk]=Východ hviezd +Name[sl]=Vzhod zvezde +Name[sr]=Сазвежђе +Name[sr@Latn]=Sazvežđe +Name[sv]=Stjärnuppgång +Name[ta]=நட்சத்திர உதயம் +Name[tg]=Пайдоиши ситора +Name[uk]=Схід зірки +Name[ven]=Vhubva naledzi +Name[xh]=Inkwenkwezi ephumayo +Name[zh_CN]=星空 +Name[zh_TW]=星空 +Name[zu]=Ukuvuka kwezinkanyezi diff --git a/libkdegames/carddecks/decks/deck2.png b/libkdegames/carddecks/decks/deck2.png Binary files differnew file mode 100644 index 00000000..52f1b67a --- /dev/null +++ b/libkdegames/carddecks/decks/deck2.png diff --git a/libkdegames/carddecks/decks/deck20.desktop b/libkdegames/carddecks/decks/deck20.desktop new file mode 100644 index 00000000..3403b23f --- /dev/null +++ b/libkdegames/carddecks/decks/deck20.desktop @@ -0,0 +1,71 @@ +[KDE Cards] +Name=Flowers +Name[af]=Blomme +Name[ar]=أزهار +Name[az]=Çiçəklər +Name[be]=Кветкі +Name[bg]=Цветя +Name[bn]=ফুল +Name[br]=Bleunioù +Name[bs]=Cvijeće +Name[ca]=Flors +Name[cs]=Květiny +Name[cy]=Blodau +Name[da]=Blomster +Name[de]=Blumen +Name[el]=Λουλούδια +Name[eo]=Floroj +Name[es]=Flores +Name[et]=Lilled +Name[eu]=Loreak +Name[fa]=گلها +Name[fi]=Kukat +Name[fo]=Blómur +Name[fr]=Fleurs +Name[ga]=Bláthanna +Name[gl]=Flores +Name[he]=פרחים +Name[hi]=पुष्प +Name[hr]=Cvjetovi +Name[hu]=Virágok +Name[is]=Blóm +Name[it]=Fiori +Name[ja]=花 +Name[km]=ផ្កា +Name[ko]=꽃 +Name[lt]=Gėlės +Name[lv]=Puķes +Name[mk]=Цвеќиња +Name[mt]=Fjuri +Name[nb]=Blomster +Name[nds]=Blomen +Name[ne]=फूल +Name[nl]=Bloemen +Name[nn]=Blomar +Name[nso]=Maloba +Name[pa]=ਫੁੱਲ +Name[pl]=Kwiaty +Name[pt]=Flores +Name[pt_BR]=Flores +Name[ro]=Flori +Name[ru]=Цветы +Name[se]=Lieđit +Name[sk]=Kvety +Name[sl]=Rože +Name[sr]=Цвеће +Name[sr@Latn]=Cveće +Name[sv]=Blommor +Name[ta]=மலர்கள் +Name[tg]=Гулҳо +Name[th]=ดอกไม้ +Name[tr]=Çiçekler +Name[uk]=Квіти +Name[uz]=Gullar +Name[uz@cyrillic]=Гуллар +Name[ven]=Maluvha +Name[vi]=Hoa +Name[wa]=Fleurs +Name[xh]=Iintyatyambo +Name[zh_CN]=鲜花 +Name[zh_TW]=鮮花 +Name[zu]=Izimbali diff --git a/libkdegames/carddecks/decks/deck20.png b/libkdegames/carddecks/decks/deck20.png Binary files differnew file mode 100644 index 00000000..73d76887 --- /dev/null +++ b/libkdegames/carddecks/decks/deck20.png diff --git a/libkdegames/carddecks/decks/deck21.desktop b/libkdegames/carddecks/decks/deck21.desktop new file mode 100644 index 00000000..b170ee53 --- /dev/null +++ b/libkdegames/carddecks/decks/deck21.desktop @@ -0,0 +1,65 @@ +[KDE Cards] +Name=Carpet +Name[af]=Tapyt +Name[ar]=سجادة +Name[az]=Xalı +Name[be]=Дыван +Name[bg]=Килим +Name[bn]=গালিচা +Name[bs]=Ćilim +Name[ca]=Catifa +Name[cs]=Koberec +Name[cy]=Carped +Name[da]=Tæppe +Name[de]=Teppich +Name[el]=Μοκέτα +Name[eo]=Tapiŝo +Name[es]=Alfombra +Name[et]=Vaip +Name[eu]=Tapiza +Name[fa]=فرش +Name[fi]=Matto +Name[fr]=Tapis +Name[gl]=Alfombra +Name[he]=שטיח +Name[hi]=कालीन +Name[hr]=Tepih +Name[hu]=Szőnyeg +Name[is]=Teppi +Name[it]=Tappeto +Name[km]=កម្រាលព្រំ +Name[ko]=양탄자 +Name[lt]=Kilimas +Name[lv]=Paklājs +Name[mk]=Килим +Name[mt]=Tapit +Name[nb]=Teppe +Name[nds]=Teppich +Name[ne]=कार्पेट +Name[nl]=Tapijt +Name[nn]=Teppe +Name[pa]=ਦਰੀ +Name[pl]=Dywan +Name[pt]=Carpete +Name[pt_BR]=Carpete +Name[ro]=Carpetă +Name[ru]=Ковёр +Name[se]=Máhttá +Name[sl]=Preproga +Name[sr]=Тепих +Name[sr@Latn]=Tepih +Name[sv]=Matta +Name[ta]=கம்பளம் +Name[tg]=Гилем +Name[th]=พรม +Name[tr]=Halı +Name[uk]=Килим +Name[uz]=Gilam +Name[uz@cyrillic]=Гилам +Name[ven]=Dzithovho +Name[vi]=Thảm +Name[wa]=Carpete +Name[xh]=Ikhaphethi +Name[zh_CN]=地毯 +Name[zh_TW]=地毯 +Name[zu]=UKhaphethi diff --git a/libkdegames/carddecks/decks/deck21.png b/libkdegames/carddecks/decks/deck21.png Binary files differnew file mode 100644 index 00000000..204156ba --- /dev/null +++ b/libkdegames/carddecks/decks/deck21.png diff --git a/libkdegames/carddecks/decks/deck22.desktop b/libkdegames/carddecks/decks/deck22.desktop new file mode 100644 index 00000000..940b4f53 --- /dev/null +++ b/libkdegames/carddecks/decks/deck22.desktop @@ -0,0 +1,58 @@ +[KDE Cards] +Name=Bathing +Name[af]=Swem +Name[ar]=يتحمم +Name[az]=Çimmə +Name[be]=Купанне +Name[bg]=Море +Name[bn]=গোসল, স্নান +Name[bs]=Kupanje +Name[ca]=Balneari +Name[cs]=Koupel +Name[cy]=Ymolchi +Name[da]=Badning +Name[de]=Baden +Name[eo]=Banloko +Name[es]=Balneario +Name[et]=Suplus +Name[eu]=Bainua +Name[fi]=Kylpy +Name[fr]=Baignade +Name[gl]=Baño +Name[he]=מקלחת +Name[hi]=बाथिंग +Name[hr]=Kupanje +Name[hu]=Fürdő +Name[is]=Í baði +Name[it]=Balneare +Name[km]=កំពុងងូតទឹក +Name[ko]=목욕 +Name[lt]=Maudynės +Name[lv]=Peldēšanās +Name[mk]=Капење +Name[mt]=Għawm +Name[nb]=Bading +Name[nds]=Baden +Name[ne]=नुहाएको +Name[nl]=Baden +Name[nn]=Bad +Name[pl]=Kąpiel +Name[pt]=Banho +Name[pt_BR]=Banho +Name[ro]=Îmbăiere +Name[ru]=Купание +Name[se]=Lávgudeapmi +Name[sl]=Kopel +Name[sr]=Купање +Name[sr@Latn]=Kupanje +Name[sv]=Bad +Name[ta]=குளியல் +Name[tg]=Оббозӣ +Name[tr]=Yüzme +Name[uk]=Купання +Name[ven]=Hu khou tambiwa +Name[vi]=Tắm +Name[xh]=Ukuhlamba +Name[zh_CN]=游泳 +Name[zh_TW]=游泳 +Name[zu]=Ukugeza diff --git a/libkdegames/carddecks/decks/deck22.png b/libkdegames/carddecks/decks/deck22.png Binary files differnew file mode 100644 index 00000000..ff3f9834 --- /dev/null +++ b/libkdegames/carddecks/decks/deck22.png diff --git a/libkdegames/carddecks/decks/deck23.desktop b/libkdegames/carddecks/decks/deck23.desktop new file mode 100644 index 00000000..ef096f15 --- /dev/null +++ b/libkdegames/carddecks/decks/deck23.desktop @@ -0,0 +1,55 @@ +[KDE Cards] +Name=Oasis +Name[af]=Oase +Name[ar]=واحة +Name[az]=Oazis +Name[be]=Аазіс +Name[bg]=Оазис +Name[bn]=মরুদ্যান +Name[br]=Oazis +Name[bs]=Oaza +Name[cs]=Oáza +Name[da]=Oase +Name[de]=Oase +Name[el]=Όαση +Name[eo]=Oazo +Name[et]=Oaas +Name[eu]=Oasia +Name[fa]=آبادی +Name[fo]=Oasa +Name[hi]=ओएसिस +Name[hr]=Oaza +Name[hu]=Oázis +Name[is]=Vin +Name[it]=Oasi +Name[ko]=오아시스 +Name[lt]=Oazė +Name[lv]=Oāze +Name[mk]=Оаза +Name[mt]=Oażi +Name[nb]=Oase +Name[nds]=Oaas +Name[ne]=ओसिस +Name[nl]=Oase +Name[nn]=Oase +Name[pl]=Oaza +Name[pt]=Oásis +Name[pt_BR]=Oásis +Name[ru]=Оазис +Name[sk]=Oáza +Name[sl]=Oaza +Name[sr]=Оаза +Name[sr@Latn]=Oaza +Name[sv]=Oas +Name[ta]=ஒயாஸிஸ் +Name[tg]=Оазис +Name[th]=โอเอซิส +Name[tr]=Vaha +Name[uk]=Оазис +Name[uz]=Voha +Name[uz@cyrillic]=Воҳа +Name[vi]=Ô'c đảo +Name[xh]=Indawo esentlango enamanzi +Name[zh_CN]=绿洲 +Name[zh_TW]=綠洲 +Name[zu]=Indawo evundileyo diff --git a/libkdegames/carddecks/decks/deck23.png b/libkdegames/carddecks/decks/deck23.png Binary files differnew file mode 100644 index 00000000..a3025212 --- /dev/null +++ b/libkdegames/carddecks/decks/deck23.png diff --git a/libkdegames/carddecks/decks/deck24.desktop b/libkdegames/carddecks/decks/deck24.desktop new file mode 100644 index 00000000..002af784 --- /dev/null +++ b/libkdegames/carddecks/decks/deck24.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Konqi +Name[be]=Конкі +Name[bg]=Конки +Name[bn]=কনকি +Name[cs]=Konqui +Name[eo]=Konĉja +Name[hi]=के-ऑन्गी +Name[it]=Konqui +Name[lv]=Konvi +Name[ne]=कोन्क्वी +Name[ru]=Конки +Name[sr]=Конки +Name[sr@Latn]=Konki +Name[ta]=கான்கி +Name[tg]=Конки +Name[uk]=Конкі +Name[zu]=I-Konqi +Comment=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[bn]=আধুনিক কনকি - একটি পারিবারিক তাস খেলা\nডিজাইন: লরা লেল্যান্ড\n <l_layland@hotmail.com>\nকনকি: স্টিফেন স্পাজ\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[bs]=Modern Konqi - igrajte sa porodičnim špilom\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ca]=Konqi modern - jugueu amb la baralla familiar\nDisseny: Laura Layland\n <l_layland@hotmail.com>\nKonqi per Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[cs]=Moderní Konqi - hrajte rodinnou hru\nNávrh: Laura Laylanda\n <l_layland@hotmail.com>\nKonqiho vytvořil Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[cy]=Set cerdiau cyfoes Konqi - chwarae yn erbyn y teulu\nDylunio:Laura Layland\n <l_layland@hotmail.com>\nKonqi gan Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[da]=Modern Konqi - spil familiekortspillet\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[de]=Modernes Konqi - Spielen Sie das Familienspiel\nDesign: Laura Laylanda\n <l_layland@hotmail.com>\nKonqi von Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[el]=Μοντέρνος Konqi - play the family θέμα καρτών\nΣχεδίαση: Laura Layland\n <l_layland@hotmail.com>\nKonqi από Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[eo]=Moderna Konĉjo - ludu per la familiokartaro\nDesegno: Laura Layland\n <l_layland@hotmail.com>\nKonĉjo de Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[es]=Konqi moderno - juegue con la baraja familiar\nDiseño: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[et]=Modern Konqi - play the family carddeck\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[eu]=Konqi modernoa - kartetan jokatzeko\nDiseinua: Laura Layland\n <l_layland@hotmail.com>\nKonqi-ren egilea Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fa]=Konqi مدرن - بازی خانوادگی carddeck\nطرح: لورا \n لایلند <l_layland@hotmail.com>\nKonqi توسط استفان اسپاتز\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fi]=Moderni Konqi - perheen korttipakka\nSuunnittelu: Laura Layland\n <l_layland@hotmail.com>\nKonqi Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[fr]=Konqi moderne - pour jouer aux cartes\nConception : Laura Layland\n <l_layland@hotmail.com>\nKonqi par Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[he]=קונקי מודרני - חפיסת קלים לכל השפחה\nעיצוב: Laura Layland\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[hr]=Suvremeni Konqi - obiteljska igra s kartama\nDizajn: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[hu]=Modern Konqi - családi kártyacsomag\nTervezte: Laura Layland\n <l_layland@hotmail.com>\nKonqi: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[it]=Konqui Moderno - carte familiari\nDesign: Laura Layland\n<l_layland@hotmail.com>\nKonqui di Stefan Spatz\n<stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ja]=モダン Konqi - ファミリ向けカードデッキ\nデザイン: Laura Layland\n <l_layland@hotmail.com>\nKonqi の作者: Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[lt]=Modern Konqi - žaisktie šeimos kortų žaidimą\nDizainas: Laura Layland\n <l_layland@hotmail.com>\nKatie by Agnieszka Czajkowska\n <agnieszka@imagegalaxy.de>\n +Comment[lv]=Modernais Konkvi - spēlēt pie ģimenes kāršu galda\n Dizains: Laura Layland\n <l_layland@hotmail.com>\n Konkvi no Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[mk]=Модерен Konqi - играјте со семејниот шпил карти\nДизајн: Laura Layland\n<l_layland@hotmail.com>\nKatie од Agnieszka Czajkowska\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nb]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de> +Comment[nds]=Modern Konqi - Speel mit de Familienkoorten\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi vun Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ne]=आधुनिक क्वोन्की - पारिवारिक कार्डडेक\nडिजाइन प्ले: लाउरा लेल्यान्ड\n <l_layland@hotmail.com>\nKonqi, स्टेफन स्पार्टजद्वारा\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nl]=Modern Konqi - speel met de familie-kaartdek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi door Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[nn]=Moderne Konqi – familiekortstokken\nUtforming: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerxburg.de> +Comment[pl]=Nowoczesny Konqi - zagraj w grę rodzinną\nProjekt: Laura Laylanda\n <l_layland@hotmail.com>a\nKonqi: Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[pt]=Konqi Moderno - o baralho de cartas familiar\nConcepção: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[pt_BR]=Konqi Moderno - jogue com o baralho da família\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi por Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ru]=Колода с семейством Конки\nДизайн: Лаура Лейлэнд (Laura Layland) <l_layland@hotmail.com>\nКонки нарисован Штефаном Спартцом (Stefan Spatz) <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sk]=Moderný Konqi - hrajte rodinné kartové hry\nDesign: Laura Laylanda\n <l_layland@hotmail.com>a\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sl]=Moderni Konqi - igrajte z družinskim kupom kart\nOblikovanje: Laura Layland\n <l_layland@hotmail.com>\nKonqi od Stefana Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sr]=Модеран Конки - играјте са породичним шпилом\nДизајн: Лора Лејленд (Laura Layland)\n <l_layland@hotmail.com>\nКонки: Штефан Шпац (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sr@Latn]=Moderan Konki - igrajte sa porodičnim špilom\nDizajn: Lora Lejlend (Laura Layland)\n <l_layland@hotmail.com>\nKonki: Štefan Špac (Stefan Spatz)\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[sv]=Modern Konqi - spela familjens kortlek\nDesign: Laura Layland\n <l_layland@hotmail.com>\nKonqi av Stefan Spatza\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[ta]=மார்டன் கான்கி - குடும்பச் சீட்டுத் தளத்தை விளையாடு\nவடிவமைப்பு: லௌரா லேலாண்டு\n <l_layland@hotmail.com>\nKonqi by Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[uk]=Сучасний Конкі - зіграйте у сімейні карти\nРозробка: Laura Layland\n <l_layland@hotmail.com>\nKatie від Agnieszka Czajkowska\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[wa]=Modiene Konqi - cwårdjeus des familes\nDessins: Laura Layland\n <l_layland@hotmail.com>\nKonqi pa Stefan Spatz\n <stefan.spatz@stud-mail.uni-wuerzburg.de> +Comment[zh_TW]=現代 Konqi - 玩家庭牌局\n設計︰Laura Layland...<l_layland@hotmail.com>\nKonqi by Stefan Spatz...<stefan.spatz@stud-mail.uni-wuerzburg.de> diff --git a/libkdegames/carddecks/decks/deck24.png b/libkdegames/carddecks/decks/deck24.png Binary files differnew file mode 100644 index 00000000..0596e919 --- /dev/null +++ b/libkdegames/carddecks/decks/deck24.png diff --git a/libkdegames/carddecks/decks/deck3.desktop b/libkdegames/carddecks/decks/deck3.desktop new file mode 100644 index 00000000..a383495e --- /dev/null +++ b/libkdegames/carddecks/decks/deck3.desktop @@ -0,0 +1,60 @@ +[KDE Cards] +Name=Romantic +Name[af]=Romantiese +Name[ar]=رومانسي +Name[az]=Romantik +Name[be]=Рамантыка +Name[bg]=Романтика +Name[bn]=রোমান্টিক +Name[bs]=Romantika +Name[ca]=Romàntic +Name[cs]=Romantika +Name[cy]=Rhamantus +Name[da]=Romantisk +Name[de]=Romantisch +Name[el]=Ρομαντικό +Name[eo]=Romantiko +Name[es]=Romántico +Name[et]=Romantika +Name[eu]=Erromantikoa +Name[fa]=خیالانگیز +Name[fi]=Romanttinen +Name[fr]=Romantique +Name[gl]=Romántica +Name[he]=רומנטי +Name[hi]=रूमानी +Name[hr]=Romantika +Name[hu]=Romantikus +Name[is]=Rómantískt +Name[it]=Romantico +Name[km]=មនោសញ្ចេតនា +Name[ko]=로맨틱 +Name[lt]=Romantika +Name[lv]=Romantika +Name[mk]=Романтика +Name[mt]=Romantiku +Name[nb]=Romantisk +Name[nds]=Romantsch +Name[ne]=रोमान्टिक +Name[nl]=Romantisch +Name[nn]=Romantisk +Name[pa]=ਰੁਮਾਂਸਵਾਦੀ +Name[pl]=Romantyczny +Name[pt]=Romântico +Name[pt_BR]=Romântico +Name[ru]=Романтика +Name[se]=Romántalaš +Name[sk]=Romantické +Name[sl]=Romantično +Name[sr]=Романтичан +Name[sr@Latn]=Romantičan +Name[sv]=Romantisk +Name[ta]=ரொமான்டிக் +Name[tg]=Хаёлпарастӣ +Name[th]=โรแมนติก +Name[uk]=Романтичний +Name[vi]=Lãng mạn +Name[wa]=Romantike +Name[zh_CN]=浪漫 +Name[zh_TW]=浪漫的 +Name[zu]=I-Romantic diff --git a/libkdegames/carddecks/decks/deck3.png b/libkdegames/carddecks/decks/deck3.png Binary files differnew file mode 100644 index 00000000..dced5af7 --- /dev/null +++ b/libkdegames/carddecks/decks/deck3.png diff --git a/libkdegames/carddecks/decks/deck4.desktop b/libkdegames/carddecks/decks/deck4.desktop new file mode 100644 index 00000000..c23f4963 --- /dev/null +++ b/libkdegames/carddecks/decks/deck4.desktop @@ -0,0 +1,93 @@ +[KDE Cards] +Name=Panda +Name[ar]=باندا +Name[be]=Панда +Name[bg]=Панда +Name[bn]=পান্ডা +Name[el]=Πάντα +Name[eo]=Pando +Name[fa]=پاندا +Name[he]=פנדה +Name[hi]=पाण्डा +Name[is]=Pandabjörn +Name[ko]=팬더 +Name[mk]=Панда +Name[ne]=पान्डा +Name[pa]=ਪਾਂਡਾ +Name[ru]=Панда +Name[sr]=Панда +Name[ta]=பாண்டா +Name[tg]=Панда +Name[th]=แพนด้า +Name[uk]=Панда +Name[uz@cyrillic]=Панда +Name[ven]=Tshivhingwi +Name[vi]=Gấu mèo +Name[xh]=Ibhere +Name[zh_CN]=熊猫 +Name[zh_TW]=熊貓 +Name[zu]=Ibhele +Comment=Dedicated to WWF +Comment[af]=Opgedra na Wwf +Comment[ar]=إهداء إلى المؤسسة العالمية للحفاظ على البيئة (WWF) +Comment[az]=WWFyə hasr edilib +Comment[be]=Прысвячаецца WWF +Comment[bg]=Посвещава се на WWF +Comment[bn]=WWF-এর প্রতি উত্সর্গীকৃত +Comment[bs]=Posvećen WWFu +Comment[ca]=Dedicat a WWF +Comment[cs]=Věnované WWF +Comment[cy]=Cyflwynedig i\'r WWF +Comment[da]=Dedikeret til WWF +Comment[de]=Dem WWF gewidmet +Comment[el]=Αφιερωμένο στο WWF +Comment[eo]=Dediĉita al WWF +Comment[es]=Dedicado a WWF +Comment[et]=Pühendatud organisatsioonile WWF +Comment[eu]=WWF-ren ohorean +Comment[fa]=مختص WWF +Comment[fi]=Omistettu WWF:lle +Comment[fr]=Dédicacé au WWF +Comment[gl]=Adicado a WWF +Comment[he]=מוקדש לקרן העולמית +Comment[hi]=डब्लयूडब्लयूएफ को समर्पित +Comment[hr]=Posvećeno WWF-u +Comment[hu]=A WWF-nek dedikálva +Comment[is]=Tileinkað WWF +Comment[it]=Dedicato al WWF +Comment[ja]=WWF に捧ぐ +Comment[km]=ឧទ្ទិសជូន WWF +Comment[ko]=WWF에 바침 +Comment[lt]=Skirta WWF +Comment[lv]=Veltīts WWF +Comment[mk]=Посветено на WWF (светска фондација за дивиот свет) +Comment[mt]=Dedikat lill-WWF +Comment[nb]=Dedikert til WWF +Comment[nds]=Den WWF toeegt +Comment[ne]=डब्लूडब्लूएफ प्रति समर्पित +Comment[nl]=Opgedragen aan WWF (WereldNatuurFonds) +Comment[nn]=Tileigna WWF +Comment[pa]=WWF ਨੂੰ ਸਮਰਪਤ +Comment[pl]=Przeznaczony do WWF +Comment[pt]=Dedicado ao WWF +Comment[pt_BR]=Dedicado ao WWF +Comment[ro]=Dedicat lui WWF +Comment[ru]=Посвящается WWF +Comment[sk]=Venované WWF +Comment[sl]=Posvečeno WWF +Comment[sr]=Посвећено WWF-у +Comment[sr@Latn]=Posvećeno WWF-u +Comment[sv]=Tillägnad WWF +Comment[ta]= WWF-க்கு அர்ப்பணிக்கப்பட்டது +Comment[tg]=Бахшида шудааст ба WWF +Comment[tr]=WWF'e adanmış +Comment[uk]=Присвячено WWF +Comment[uz]=WWF tashkilotiga bagʻishlangan +Comment[uz@cyrillic]=WWF ташкилотига бағишланган +Comment[ven]=Yo livhiswa kha WWF +Comment[vi]=Dâng tặng WWF +Comment[wa]=Dicåçté å WWF +Comment[xh]=Yenzelwe ku WWF +Comment[zh_CN]=献给 WWF +Comment[zh_TW]=獻給 WWF +Comment[zu]=Inikelwe kwi-WWF diff --git a/libkdegames/carddecks/decks/deck4.png b/libkdegames/carddecks/decks/deck4.png Binary files differnew file mode 100644 index 00000000..23fa4010 --- /dev/null +++ b/libkdegames/carddecks/decks/deck4.png diff --git a/libkdegames/carddecks/decks/deck5.desktop b/libkdegames/carddecks/decks/deck5.desktop new file mode 100644 index 00000000..5cbe2d6f --- /dev/null +++ b/libkdegames/carddecks/decks/deck5.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Water +Name[ar]=ماء +Name[az]=Su +Name[be]=Вада +Name[bg]=Вода +Name[bn]=পানি +Name[br]=Dou +Name[bs]=Voda +Name[ca]=Aigua +Name[cs]=Voda +Name[cy]=Dŵr +Name[da]=Vand +Name[de]=Wasser +Name[el]=Νερό +Name[eo]=Akvo +Name[es]=Agua +Name[et]=Vesi +Name[eu]=Ura +Name[fa]=آب +Name[fi]=Vesi +Name[fo]=Vatn +Name[fr]=Eau +Name[ga]=Uisce +Name[gl]=Auga +Name[he]=מים +Name[hi]=पानी +Name[hr]=Voda +Name[hu]=Víz +Name[is]=Vatn +Name[it]=Acqua +Name[ja]=水 +Name[km]=ទឹក +Name[ko]=물 +Name[lt]=Vanduo +Name[lv]=Ūdens +Name[mk]=Вода +Name[mt]=Ilma +Name[nb]=Vann +Name[ne]=पानी +Name[nn]=Vatn +Name[nso]=Meetse +Name[pa]=ਪਾਣੀ +Name[pl]=Woda +Name[pt]=Água +Name[pt_BR]=Água +Name[ro]=Apă +Name[ru]=Вода +Name[se]=Čáhci +Name[sk]=Voda +Name[sl]=Voda +Name[sr]=Вода +Name[sr@Latn]=Voda +Name[sv]=Vatten +Name[ta]=தண்ணீர் +Name[tg]=Об +Name[th]=น้ำ +Name[uk]=Вода +Name[uz]=Suv +Name[uz@cyrillic]=Сув +Name[ven]=Madi +Name[vi]=Nước +Name[wa]=Aiwe +Name[xh]=Amanzi +Name[zh_CN]=水 +Name[zh_TW]=水 +Name[zu]=Amanzi diff --git a/libkdegames/carddecks/decks/deck5.png b/libkdegames/carddecks/decks/deck5.png Binary files differnew file mode 100644 index 00000000..63cb6762 --- /dev/null +++ b/libkdegames/carddecks/decks/deck5.png diff --git a/libkdegames/carddecks/decks/deck6.desktop b/libkdegames/carddecks/decks/deck6.desktop new file mode 100644 index 00000000..6d84a8ad --- /dev/null +++ b/libkdegames/carddecks/decks/deck6.desktop @@ -0,0 +1,66 @@ +[KDE Cards] +Name=Beach +Name[af]=Strand +Name[ar]=شاطئ +Name[az]=Çimərlik +Name[be]=Пляж +Name[bg]=Плаж +Name[bn]=সৈকত +Name[br]=Traezhenn +Name[bs]=Plaža +Name[ca]=Platja +Name[cs]=Pláž +Name[cy]=Traeth +Name[da]=Strand +Name[de]=Strand +Name[el]=Παραλία +Name[eo]=Plaĝo +Name[es]=Playa +Name[et]=Rand +Name[eu]=Hondartza +Name[fa]=ساحل +Name[fi]=Ranta +Name[fr]=Plage +Name[gl]=Praia +Name[he]=חוף +Name[hi]=समुद्र तट +Name[hr]=Plaža +Name[hu]=Tengerpart +Name[is]=Strönd +Name[it]=Spiaggia +Name[km]=ឆ្នេរខ្សាច់ +Name[ko]=바닷가 +Name[lt]=Paplūdimys +Name[lv]=Pludmale +Name[mk]=Плажа +Name[mt]=Ramla +Name[nb]=Strand +Name[nds]=Strand +Name[ne]=किनारा +Name[nl]=Strand +Name[nn]=Strand +Name[nso]=Lewatle +Name[pa]=ਬੀਚ +Name[pl]=Plaża +Name[pt]=Praia +Name[pt_BR]=Praia +Name[ro]=Plajă +Name[ru]=Пляж +Name[se]=Fiervá +Name[sk]=Pláž +Name[sl]=Obala +Name[sr]=Плажа +Name[sr@Latn]=Plaža +Name[sv]=Strand +Name[ta]=கடல் +Name[tg]=Пляж +Name[th]=อ่าว +Name[tr]=Sahil +Name[uk]=Пляж +Name[ven]=Bitshini +Name[vi]=Bãi biển +Name[wa]=Pladje +Name[xh]=Ulwande +Name[zh_CN]=海滩 +Name[zh_TW]=海灘 +Name[zu]=Ulwandle diff --git a/libkdegames/carddecks/decks/deck6.png b/libkdegames/carddecks/decks/deck6.png Binary files differnew file mode 100644 index 00000000..1708d784 --- /dev/null +++ b/libkdegames/carddecks/decks/deck6.png diff --git a/libkdegames/carddecks/decks/deck7.desktop b/libkdegames/carddecks/decks/deck7.desktop new file mode 100644 index 00000000..3590a15e --- /dev/null +++ b/libkdegames/carddecks/decks/deck7.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Sunset +Name[af]=Sonsondergang +Name[ar]=غروب الشمس +Name[az]=Gün doğuşu +Name[be]=Заход +Name[bg]=Залез +Name[bn]=সূর্যাস্ত +Name[br]=Kuzh-heol +Name[bs]=Zalazak +Name[ca]=Posta de sol +Name[cs]=Západ slunce +Name[cy]=Machlud +Name[da]=Solnedgang +Name[de]=Sonnenuntergang +Name[el]=Ηλιοβασίλεμα +Name[eo]=Sunsubiro +Name[es]=Puesta de sol +Name[et]=Loojang +Name[eu]=Eguzki-sartzea +Name[fa]=غروب +Name[fi]=Auringonlasku +Name[fo]=Sólsetur +Name[fr]=Soleil +Name[gl]=Entre lusco e fusco +Name[he]=שקיעה +Name[hi]=सूर्यास्त +Name[hr]=Zalazak sunca +Name[hu]=Naplemente +Name[is]=Sólsetur +Name[it]=Tramonto +Name[km]=សុរិយាអស្ដង្គត +Name[ko]=저녁놀 +Name[lt]=Saulėlydis +Name[lv]=Saullēkts +Name[mk]=Зајдисонце +Name[mt]=Għabex +Name[nb]=Solnedgang +Name[nds]=Sünnünnergang +Name[ne]=सुर्यास्त +Name[nl]=Zonsondergang +Name[nn]=Solrenning +Name[pa]=ਡੁੱਬਦਾ ਸੂਰਜ +Name[pl]=Zachód słońca +Name[pt]=Pôr-do-Sol +Name[pt_BR]=Pôr-do-sol +Name[ro]=Asfinţit +Name[ru]=Закат +Name[sk]=Západ slnka +Name[sl]=Sončni zahod +Name[sr]=Залазак сунца +Name[sr@Latn]=Zalazak sunca +Name[sv]=Solnedgång +Name[ta]=சூரிய அஸ்தமம் +Name[tg]=Ғуруби Офтоб +Name[th]=พระอาทิตย์ตก +Name[tr]=Gündoğumu +Name[uk]=Захід сонця +Name[uz]=Oqshom +Name[uz@cyrillic]=Оқшом +Name[ven]=Vhukovhela +Name[vi]=Hoàng hôn +Name[wa]=Solea djus +Name[xh]=Ukutshona kwelanga +Name[zh_CN]=日落 +Name[zh_TW]=日落 +Name[zu]=Ukushona kwelanga diff --git a/libkdegames/carddecks/decks/deck7.png b/libkdegames/carddecks/decks/deck7.png Binary files differnew file mode 100644 index 00000000..c7f043db --- /dev/null +++ b/libkdegames/carddecks/decks/deck7.png diff --git a/libkdegames/carddecks/decks/deck8.desktop b/libkdegames/carddecks/decks/deck8.desktop new file mode 100644 index 00000000..0c68bc9f --- /dev/null +++ b/libkdegames/carddecks/decks/deck8.desktop @@ -0,0 +1,67 @@ +[KDE Cards] +Name=Road +Name[af]=Pad +Name[ar]=طريق +Name[az]=Yol +Name[be]=Дарога +Name[bg]=Път +Name[bn]=পথ +Name[bs]=Put +Name[ca]=Carretera +Name[cs]=Cesta +Name[cy]=Ffordd +Name[da]=Vej +Name[de]=Straße +Name[el]=Δρόμος +Name[eo]=Strato +Name[es]=Carretera +Name[et]=Maantee +Name[eu]=Errepidea +Name[fa]=جاده +Name[fi]=Tie +Name[fo]=Vegur +Name[fr]=Route +Name[gl]=Estrada +Name[he]=בדרכים +Name[hi]=रास्ता +Name[hr]=Cesta +Name[hu]=Országút +Name[is]=Vegur +Name[it]=Strada +Name[km]=ផ្លូវ +Name[ko]=길 +Name[lt]=Kelias +Name[lv]=Ceļš +Name[mk]=Пат +Name[mt]=Triq +Name[nb]=Vei +Name[nds]=Straat +Name[ne]=बाटो +Name[nl]=Weg +Name[nn]=Veg +Name[nso]=Tsela +Name[pa]=ਰਾਹ +Name[pl]=Droga +Name[pt]=Estrada +Name[pt_BR]=Estrada +Name[ro]=Şosea +Name[ru]=Дорога +Name[se]=Geaidnu +Name[sk]=Cesta +Name[sl]=Cesta +Name[sr]=Пут +Name[sr@Latn]=Put +Name[sv]=Väg +Name[ta]=சாலை +Name[tg]=Роҳ +Name[th]=ถนน +Name[tr]=Yol +Name[uk]=Дорога +Name[uz]=Yoʻl +Name[uz@cyrillic]=Йўл +Name[ven]=Bada +Name[vi]=Con đường +Name[xh]=Indlela +Name[zh_CN]=路 +Name[zh_TW]=道路 +Name[zu]=Umgwaqo diff --git a/libkdegames/carddecks/decks/deck8.png b/libkdegames/carddecks/decks/deck8.png Binary files differnew file mode 100644 index 00000000..4fed86e6 --- /dev/null +++ b/libkdegames/carddecks/decks/deck8.png diff --git a/libkdegames/carddecks/decks/deck9.desktop b/libkdegames/carddecks/decks/deck9.desktop new file mode 100644 index 00000000..2541e22a --- /dev/null +++ b/libkdegames/carddecks/decks/deck9.desktop @@ -0,0 +1,7 @@ +[KDE Cards] +Name=KDE +Name[af]=Kde +Name[bn]=কে.ডি.ই. +Name[hi]=केडीई +Name[ne]=केडीई +Name[ta]=கேடிஇ diff --git a/libkdegames/carddecks/decks/deck9.png b/libkdegames/carddecks/decks/deck9.png Binary files differnew file mode 100644 index 00000000..35abd0a8 --- /dev/null +++ b/libkdegames/carddecks/decks/deck9.png diff --git a/libkdegames/configure.in.in b/libkdegames/configure.in.in new file mode 100644 index 00000000..ffcaf9c9 --- /dev/null +++ b/libkdegames/configure.in.in @@ -0,0 +1,49 @@ +dnl AB: checking for a system-wide highscore file. If "no" then the default +dnl (just kapp->config()) is used. See KHighscore for details. + +AC_MSG_CHECKING(whether to use system-wide highscores) +AC_ARG_ENABLE(highscore-dir, +AC_HELP_STRING([--enable-highscore-dir=DIR], [system-wide highscore table @<:@default=no@:>@]), [use_highscore_dir=yes], [use_highscore_dir=no]) + +if test "$use_highscore_dir" = "no"; then + AC_MSG_RESULT(no) + AC_SUBST(HIGHSCORE_DIRECTORY, "") +else + case "${enableval}" in + yes) highscore_dir='${localstatedir}/games' ;; + no) ;; + *) highscore_dir=${enableval} ;; + esac + AC_DEFINE_UNQUOTED(HIGHSCORE_DIRECTORY, "$highscore_dir", [The system-wide highscore directory]) + AC_SUBST(HIGHSCORE_DIRECTORY, $highscore_dir) + AC_MSG_RESULT($use_highscore_dir) +fi + +AC_MSG_CHECKING(whether to setgid binaries) +AC_ARG_ENABLE(setgid, + [ --enable-setgid Enable the use of setgid binaries], + [case "${enableval}" in + yes) + case "$use_highscore_dir" in + yes) setgid=true;; + no) setgid=false;; + esac ;; + no) setgid=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-setgid) ;; + esac],[setgid=false]) +AC_SUBST(setgid) +AC_MSG_RESULT($setgid) + +AC_MSG_CHECKING(what group to use for the highscore tables and binaries) +AC_ARG_WITH(highscore-group, +[ --with-highscore-group=group Group for the highscore tables and binaries], +highscore_group="$withval",highscore_group="games") +AC_SUBST(highscore_group) +AC_MSG_RESULT($highscore_group) + +AC_MSG_CHECKING(what user to use for the highscore tables and binaries) +AC_ARG_WITH(highscore-user, +[ --with-highscore-user=user User for the highscore tables], +highscore_user="$withval",highscore_user="games") +AC_SUBST(highscore_user) +AC_MSG_RESULT($highscore_user) diff --git a/libkdegames/highscore/INSTALL b/libkdegames/highscore/INSTALL new file mode 100644 index 00000000..a16fa57d --- /dev/null +++ b/libkdegames/highscore/INSTALL @@ -0,0 +1,12 @@ +Installation notes for the highscore files ; this is only relevant if you +configured libkdegames with option --enable-highscore-dir=DIR (usually DIR is +/var/games) for using system-wide highscore files. + +For each game using the highscore system : + +- the game executable "mygame" should be installed sgid "games" + +- an empty file "mygame.scores" should be created in the directory pointed by +the configuration option. It should be owned by group "games" with read and +write permissions and should -not- be world readable (since it can contains +possibly sensitive information associating username with game usage). diff --git a/libkdegames/highscore/Makefile.am b/libkdegames/highscore/Makefile.am new file mode 100644 index 00000000..6fa18cc0 --- /dev/null +++ b/libkdegames/highscore/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libkhighscore.la + +INCLUDES = $(all_includes) + +libkhighscore_la_SOURCES = kconfigrawbackend.cpp \ + kfilelock.cpp khighscore.cpp kscoredialog.cpp \ + kexthighscore_item.cpp kexthighscore_internal.cpp \ + kexthighscore_tab.cpp kexthighscore_gui.cpp \ + kexthighscore.cpp + +include_HEADERS = khighscore.h kscoredialog.h \ + kexthighscore_item.h kexthighscore.h + +noinst_HEADERS = kconfigrawbackend.h \ + kfilelock.h kexthighscore_internal.h kexthighscore_tab.h \ + kexthighscore_gui.h + +METASOURCES = kconfigrawbackend.moc khighscore.moc kscoredialog.moc \ + kexthighscore_tab.moc kexthighscore_gui.moc diff --git a/libkdegames/highscore/kconfigrawbackend.cpp b/libkdegames/highscore/kconfigrawbackend.cpp new file mode 100644 index 00000000..a379ba23 --- /dev/null +++ b/libkdegames/highscore/kconfigrawbackend.cpp @@ -0,0 +1,62 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 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 "kconfigrawbackend.h" +#include "kconfigrawbackend.moc" + +#include <unistd.h> +#include <qfile.h> + + +KConfigRawBackEnd::KConfigRawBackEnd(KConfigBase *_config, int fd) + : KConfigINIBackEnd(_config, QString::null, "config", false), + _fd(fd), _stream(0) +{ + _file.open(IO_ReadOnly, _fd); +} + +KConfigRawBackEnd::~KConfigRawBackEnd() +{ + if (_stream) fclose(_stream); +} + +bool KConfigRawBackEnd::parseConfigFiles() +{ + _file.reset(); + parseSingleConfigFile(_file); + return true; +} + +void KConfigRawBackEnd::sync(bool bMerge) +{ + // write-sync is only necessary if there are dirty entries + if ( !pConfig->isDirty() || pConfig->isReadOnly() ) return; + + _file.reset(); + KEntryMap aTempMap; + getEntryMap(aTempMap, false, bMerge ? &_file : 0); + + if ( _stream==0 ) { + _stream = fdopen(_fd, "w"); + if ( _stream==0 ) return; + } + ftruncate(_fd, 0); + writeEntries(_stream, aTempMap); + fflush(_stream); +} diff --git a/libkdegames/highscore/kconfigrawbackend.h b/libkdegames/highscore/kconfigrawbackend.h new file mode 100644 index 00000000..4b780320 --- /dev/null +++ b/libkdegames/highscore/kconfigrawbackend.h @@ -0,0 +1,57 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 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 _KCONFIGRAWBACKEND_H +#define _KCONFIGRAWBACKEND_H + +#include <qfile.h> + +#include <kconfigbackend.h> +#include <ksimpleconfig.h> + + +class KConfigRawBackEnd : public KConfigINIBackEnd +{ +public: + KConfigRawBackEnd(KConfigBase *_config, int fd); + ~KConfigRawBackEnd(); + + bool parseConfigFiles(); + + void sync(bool bMerge = true); + +private: + int _fd; + FILE *_stream; + QFile _file; + + class KConfigRawBackEndPrivate; + KConfigRawBackEndPrivate *d; +}; + +class KRawConfig : public KSimpleConfig +{ + Q_OBJECT +public: + KRawConfig(int fd, bool readOnly) + : KSimpleConfig(new KConfigRawBackEnd(this, fd), readOnly) {} +}; + + +#endif diff --git a/libkdegames/highscore/kexthighscore.cpp b/libkdegames/highscore/kexthighscore.cpp new file mode 100644 index 00000000..0ad9b3af --- /dev/null +++ b/libkdegames/highscore/kexthighscore.cpp @@ -0,0 +1,289 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-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 "kexthighscore.h" + +#include <qlayout.h> + +#include <kdebug.h> + +#include "kexthighscore_internal.h" +#include "kexthighscore_gui.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +ManagerPrivate *internal = 0; + +uint gameType() +{ + internal->checkFirst(); + return internal->gameType(); +} + +void setGameType(uint type) +{ + internal->setGameType(type); +} + +bool configure(QWidget *parent) +{ + internal->checkFirst(); + ConfigDialog *cd = new ConfigDialog(parent); + cd->exec(); + bool saved = cd->hasBeenSaved(); + delete cd; + return saved; +} + +void show(QWidget *parent, int rank) +{ + HighscoresDialog *hd = new HighscoresDialog(rank, parent); + hd->exec(); + delete hd; +} + +void submitScore(const Score &score, QWidget *widget) +{ + int rank = internal->submitScore(score, widget, + internal->showMode!=Manager::NeverShow); + + switch (internal->showMode) { + case Manager::AlwaysShow: + show(widget, -1); + break; + case Manager::ShowForHigherScore: + if ( rank!=-1) show(widget, rank); + break; + case Manager::ShowForHighestScore: + if ( rank==0 ) show(widget, rank); + break; + case Manager::NeverShow: + break; + } +} + +void show(QWidget *widget) +{ + internal->checkFirst(); + show(widget, -1); +} + +Score lastScore() +{ + internal->checkFirst(); + internal->hsConfig().readCurrentConfig(); + uint nb = internal->scoreInfos().maxNbEntries(); + return internal->readScore(nb-1); +} + +Score firstScore() +{ + internal->checkFirst(); + internal->hsConfig().readCurrentConfig(); + return internal->readScore(0); +} + + +//----------------------------------------------------------------------------- +Manager::Manager(uint nbGameTypes, uint maxNbEntries) +{ + Q_ASSERT(nbGameTypes); + Q_ASSERT(maxNbEntries); + if (internal) + kdFatal(11002) << "A highscore object already exists" << endl; + internal = new ManagerPrivate(nbGameTypes, *this); + internal->init(maxNbEntries); +} + +Manager::~Manager() +{ + delete internal; + internal = 0; +} + +void Manager::setTrackLostGames(bool track) +{ + internal->trackLostGames = track; +} + +void Manager::setTrackDrawGames(bool track) +{ + internal->trackDrawGames = track; +} + +void Manager::setShowStatistics(bool show) +{ + internal->showStatistics = show; +} + +void Manager::showStatistics(bool show) +{ + internal->showStatistics = show; +} + +void Manager::setShowDrawGamesStatistic(bool show) +{ + internal->showDrawGames = show; +} + +void Manager::setWWHighscores(const KURL &url, const QString &version) +{ + Q_ASSERT( url.isValid() ); + internal->serverURL = url; + const char *HS_WW_URL = "ww hs url"; + ConfigGroup cg; + if ( cg.config()->hasKey(HS_WW_URL) ) + internal->serverURL = cg.config()->readEntry(HS_WW_URL); + else cg.config()->writeEntry(HS_WW_URL, url.url()); + internal->version = version; +} + +void Manager::setScoreHistogram(const QMemArray<uint> &scores, + ScoreTypeBound type) +{ + Q_ASSERT( scores.size()>=2 ); + for (uint i=0; i<scores.size()-1; i++) + Q_ASSERT( scores[i]<scores[i+1] ); + internal->playerInfos().createHistoItems(scores, type==ScoreBound); +} + +void Manager::setShowMode(ShowMode mode) +{ + internal->showMode = mode; +} + +void Manager::setScoreType(ScoreType type) +{ + switch (type) { + case Normal: + return; + case MinuteTime: { + Item *item = createItem(ScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setScoreItem(0, item); + + item = createItem(MeanScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setPlayerItem(MeanScore, item); + + item = createItem(BestScoreDefault); + item->setPrettyFormat(Item::MinuteTime); + setPlayerItem(BestScore, item); + return; + } + } +} + +void Manager::submitLegacyScore(const Score &score) const +{ + internal->submitLocal(score); +} + +bool Manager::isStrictlyLess(const Score &s1, const Score &s2) const +{ + return s1.score()<s2.score(); +} + +Item *Manager::createItem(ItemType type) +{ + Item *item = 0; + switch (type) { + case ScoreDefault: + item = new Item((uint)0, i18n("Score"), Qt::AlignRight); + break; + case MeanScoreDefault: + item = new Item((double)0, i18n("Mean Score"), Qt::AlignRight); + item->setPrettyFormat(Item::OneDecimal); + item->setPrettySpecial(Item::DefaultNotDefined); + break; + case BestScoreDefault: + item = new Item((uint)0, i18n("Best Score"), Qt::AlignRight); + item->setPrettySpecial(Item::DefaultNotDefined); + break; + case ElapsedTime: + item = new Item((uint)0, i18n("Elapsed Time"), Qt::AlignRight); + item->setPrettyFormat(Item::MinuteTime); + item->setPrettySpecial(Item::ZeroNotDefined); + break; + } + return item; +} + +void Manager::setScoreItem(uint worstScore, Item *item) +{ + item->setDefaultValue(worstScore); + internal->scoreInfos().setItem("score", item); + internal->playerInfos().item("mean score") + ->item()->setDefaultValue(double(worstScore)); + internal->playerInfos().item("best score") + ->item()->setDefaultValue(worstScore); +} + +void Manager::addScoreItem(const QString &name, Item *item) +{ + internal->scoreInfos().addItem(name, item, true); +} + +void Manager::setPlayerItem(PlayerItemType type, Item *item) +{ + const Item *scoreItem = internal->scoreInfos().item("score")->item(); + uint def = scoreItem->defaultValue().toUInt(); + QString name; + switch (type) { + case MeanScore: + name = "mean score"; + item->setDefaultValue(double(def)); + break; + case BestScore: + name = "best score"; + item->setDefaultValue(def); + break; + } + internal->playerInfos().setItem(name, item); +} + +QString Manager::gameTypeLabel(uint gameType, LabelType type) const +{ + if ( gameType!=0 ) + kdFatal(11002) << "You need to reimplement KExtHighscore::Manager for " + << "multiple game types" << endl; + switch (type) { + case Icon: + case Standard: + case I18N: break; + case WW: return "normal"; + } + return QString::null; +} + +void Manager::addToQueryURL(KURL &url, const QString &item, + const QString &content) +{ + Q_ASSERT( !item.isEmpty() && url.queryItem(item).isNull() ); + + QString query = url.query(); + if ( !query.isEmpty() ) query += '&'; + query += item + '=' + KURL::encode_string(content); + url.setQuery(query); +} + +} // namescape diff --git a/libkdegames/highscore/kexthighscore.h b/libkdegames/highscore/kexthighscore.h new file mode 100644 index 00000000..2484f97b --- /dev/null +++ b/libkdegames/highscore/kexthighscore.h @@ -0,0 +1,367 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-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 KEXTHIGHSCORE_H +#define KEXTHIGHSCORE_H + +#include "kexthighscore_item.h" + +#include <kurl.h> +#include <kdemacros.h> + +class QTabWidget; + + +namespace KExtHighscore +{ + +class Score; +class Item; + +class ManagerPrivate; +extern ManagerPrivate *internal; + +/** + * Get the current game type. + */ +KDE_EXPORT uint gameType(); + +/** + * Set the current game type. + */ +KDE_EXPORT void setGameType(uint gameType); + +/** + * Configure the highscores. + * @return true if the configuration has been modified and saved + */ +KDE_EXPORT bool configure(QWidget *parent); + +/** + * Show the highscores lists. + */ +KDE_EXPORT void show(QWidget *parent); + +/** + * Submit a score. See @ref Manager for usage example. + * + * @param widget a widget used as parent for error message box. + */ +KDE_EXPORT void submitScore(const Score &score, QWidget *widget); + +/** + * @return the last score in the local list of highscores. The worst possible + * score if there are less items than the maximum number. + */ +KDE_EXPORT Score lastScore(); + +/** + * @return the first score in the local list of highscores (the worst possible + * score if there is no entry). + */ +KDE_EXPORT Score firstScore(); + +/** + * This class manages highscores and players entries (several players can + * share the same highscores list if the libkdegame library is built to + * support a common highscores file; NOTE that to correctly implement such + * feature we probably need a locking mechanism in @ref KHighscore). + * + * You need one instance of this class during the application lifetime ; in + * main() just insert + * \code + * KExtHighscore::Manager highscoresManager; + * \endcode + * with the needed arguments. Use the derived class if you need to + * reimplement some of the default methods. + * + * This class has three functions : + * <ul> + * <li> Update the highscores list when new entries are submitted </li> + * <li> Display the highscores list and the players list </li> + * <li> Send query to an optionnal web server to support world-wide + * highscores </li> + * </ul> + * + * The highscores and the players lists contain several items described by + * the @ref Item class. + * + * The highscores list contains by default : + * <ul> + * <li> the player name (automatically set from the config value)</li> + * <li> the score value </li> + * <li> the time and date of the highscore (automatically set) </li> + * </ul> + * You can replace the score item (for e.g. displaying it differently) with + * setScoreItem or add an item with addScoreItem. + * + * The players list contains : + * <ul> + * <li> the player name (as defined by the user in the configuration + * dialog) </li> + * <li> the number of games played </li> + * <li> the mean score </li> + * <li> the best score </li> + * <li> the best score time and date </li> + * <li> the player comment (as defined by the user in the + * configuration dialog) </li> + * </ul> + * You can replace the best score and the mean score items + * by calling setPlayerItem. + * + * To submit a new score at game end, just construct a Score, set the + * score data and then call submitScore(). + * \code + * KExtHighscore::Score score(KExtHighscore::Won); + * score.setScore(myScore); + * KExtHighscore::submitScore(score, widget); + * \endcode + * You only need to set the score value with Score::setScore() + * and the value of the items that you have optionnally added + * with Score::setData() ; player name and date are set automatically. + */ +class KDE_EXPORT Manager +{ + public: + /** + * Constructor + * + * @param nbGameTypes the number of different game types (usually one). + * For example KMines has easy, normal and expert levels. + * @param maxNbEntries the maximum numbers of highscores entries (by game + * types) + */ + Manager(uint nbGameTypes = 1, uint maxNbEntries = 10); + virtual ~Manager(); + + /** + * Set the world-wide highscores. + * By default there is no world-wide highscores. + * + * Note: should be called at construction time. + * + * @param url the web server url + * @param version the game version which is sent to the web server (it can + * be useful for backward compatibility on the server side). + */ + void setWWHighscores(const KURL &url, const QString &version); + + /** + * Set if the number of lost games should be track for the world-wide + * highscores statistics. By default, there is no tracking. + * False by default. + * + * Note: should be called at construction time. + */ + void setTrackLostGames(bool track); + + /** + * @since 3.3 + * Set if the number of "draw" games should be track for the world-wide + * highscores statistics. By default, there is no tracking. + * False by default. + * + * Note: should be called at construction time. + */ + void setTrackDrawGames(bool track); + + /** + * @since 3.3 + * Set if the statistics tab should be shown in the highscores dialog. + * You only want to show this tab if it makes sense to lose or to win the + * game (for e.g. it makes no sense for a tetris game but it does for a + * minesweeper game). + * False by default. + * + * Note: should be called at construction time. + */ + void setShowStatistics(bool show); + + /** @obsolete */ + // KDE4 remove this + void showStatistics(bool show) KDE_DEPRECATED; + + /** + * @since 3.3 + * Set if draw games statistics should be shown (enable this if + * draws are possible in your game). + * False by default. + */ + void setShowDrawGamesStatistic(bool show); + + enum ScoreTypeBound { ScoreNotBound, ScoreBound }; + /** + * Set the ranges for the score histogram. + * + * Note: should be called at construction time. + */ + void setScoreHistogram(const QMemArray<uint> &scores, ScoreTypeBound type); + + /** + * Enumerate different conditions under which to show the + * high score dialog. + */ + enum ShowMode { AlwaysShow, ///< Always show the dialog + NeverShow, ///< Never show the dialog + ShowForHigherScore, ///< Show if score has improved + ShowForHighestScore ///< Only for the top spot + }; + /** + * Set how the highscores dialog is shown at game end. + * By default, the mode is ShowForHigherScore. + * + * Note: should be called at construction time. + */ + void setShowMode(ShowMode mode); + + /** + * Score type (@see setScoreType). + * @p Normal default score (unsigned integer without upper bound) + * @p MinuteTime score by time bound at 3599 seconds (for e.g. kmines) + */ + enum ScoreType { Normal, MinuteTime }; + /** + * Set score type. Helper method to quickly set the type of score. + * By default the type is Normal. + * + * Note: should be called at construction time. + */ + void setScoreType(ScoreType type); + + /** + * Some predefined item types. + * @p ScoreDefault default item for the score in the highscores list. + * @p MeanScoreDefault default item for the mean score (only show one decimal and + * 0 is shown as "--". + * @p BestScoreDefault default item for the best score (0 is shown as "--"). + * @p ElapsedTime optionnal item for elapsed time (maximum value is 3599 seconds). + */ + enum ItemType { ScoreDefault, MeanScoreDefault, BestScoreDefault, + ElapsedTime }; + /** + * Create a predefined item. + */ + static Item *createItem(ItemType type); + + /** + * Replace the default score item in the highscores list by the given one. + * @p worstScore is the worst possible score. By default it is 0. + * + * Note : This method should be called at construction time. + */ + void setScoreItem(uint worstScore, Item *item); + + /** + * Add an item in the highscores list (it will add a column to this list). + * + * Note : This method should be called at construction time. + */ + void addScoreItem(const QString &name, Item *item); + + enum PlayerItemType { MeanScore, BestScore }; + /** + * Replace an item in the players list. + * + * Note : This method should be called at construction time. + */ + void setPlayerItem(PlayerItemType type, Item *item); + + /** + * @return true if the first score is strictly worse than the second one. + * By default return <pre>s1.score()<s2.score()</pre>. You can reimplement + * this method if additional items added to @ref Score can further + * differentiate the scores (for e.g. the time spent). + * + * Note that you do not need to use directly this method, simply write + * <pre>s1<s2</pre> since the operator calls this method. + */ + virtual bool isStrictlyLess(const Score &s1, const Score &s2) const; + + /** + * Possible type of label (@see gameTypeLabel). + * @p Standard label used in config file. + * @p I18N label used to display the game type. + * @p WW label used when contacting the world-wide highscores server. + * @p Icon label used to load the icon corresponding to the game type. + */ + enum LabelType { Standard, I18N, WW, Icon }; + + /** + * @return the label corresponding to the game type. The default + * implementation works only for one game type : you need to reimplement + * this method if the number of game types is more than one. + */ + virtual QString gameTypeLabel(uint gameType, LabelType type) const; + + protected: + /** + * This method is called once for each player (ie for each user). You + * can reimplement it to convert old style highscores to the new mechanism + * (@see submitLegacyScore). By default this method does nothing. + * + * @param gameType the game type + */ + virtual void convertLegacy(uint gameType) { Q_UNUSED(gameType); } + + /** + * This method should be called from @ref convertLegacy. It is used + * to submit an old highscore (it will not be send over the network). + * For each score do something like: + * \code + * Score score(Won); + * score.setScore(oldScore); + * score.setData("name", name); + * submitLegacyScore(score); + * \endcode + * Note that here you can set the player "name" and the highscore "date" + * if they are known. + */ + void submitLegacyScore(const Score &score) const; + + /** + * This method is called before submitting a score to the world-wide + * highscores server. You can reimplement this method to add an entry + * with @ref addToQueryURL. By default this method does nothing. + * + * @param url the URL to query + * @param score the score to be submitted. + */ + virtual void additionalQueryItems(KURL &url, const Score &score) const + { Q_UNUSED(url); Q_UNUSED(score); } + + /** + * Add an entry to the url to be submitted (@see additionalQueryItems). + * + * @param url the URL to query + * @param item the item name + * @param content the item content + */ + static void addToQueryURL(KURL &url, const QString &item, + const QString &content); + + friend class ManagerPrivate; + + private: + Manager(const Manager &); + Manager &operator =(const Manager &); +}; + +} // namespace + +#endif diff --git a/libkdegames/highscore/kexthighscore_gui.cpp b/libkdegames/highscore/kexthighscore_gui.cpp new file mode 100644 index 00000000..547a885c --- /dev/null +++ b/libkdegames/highscore/kexthighscore_gui.cpp @@ -0,0 +1,552 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2003 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 "kexthighscore_gui.h" +#include "kexthighscore_gui.moc" + +#include <qlayout.h> +#include <qtextstream.h> +#include <qheader.h> +#include <qgrid.h> +#include <qvgroupbox.h> + +#include <kapplication.h> +#include <kmessagebox.h> +#include <kurllabel.h> +#include <kopenwith.h> +#include <krun.h> +#include <kfiledialog.h> +#include <ktempfile.h> +#include <kio/netaccess.h> +#include <kiconloader.h> + +#include "kexthighscore_internal.h" +#include "kexthighscore.h" +#include "kexthighscore_tab.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +ShowItem::ShowItem(QListView *list, bool highlight) + : KListViewItem(list), _highlight(highlight) +{} + +void ShowItem::paintCell(QPainter *p, const QColorGroup &cg, + int column, int width, int align) +{ + QColorGroup cgrp(cg); + if (_highlight) cgrp.setColor(QColorGroup::Text, red); + KListViewItem::paintCell(p, cgrp, column, width, align); +} + +//----------------------------------------------------------------------------- +ScoresList::ScoresList(QWidget *parent) + : KListView(parent) +{ + setSelectionMode(QListView::NoSelection); + setItemMargin(3); + setAllColumnsShowFocus(true); + setSorting(-1); + header()->setClickEnabled(false); + header()->setMovingEnabled(false); +} + +void ScoresList::addHeader(const ItemArray &items) +{ + addLineItem(items, 0, 0); +} + +QListViewItem *ScoresList::addLine(const ItemArray &items, + uint index, bool highlight) +{ + QListViewItem *item = new ShowItem(this, highlight); + addLineItem(items, index, item); + return item; +} + +void ScoresList::addLineItem(const ItemArray &items, + uint index, QListViewItem *line) +{ + uint k = 0; + for (uint i=0; i<items.size(); i++) { + const ItemContainer &container = *items[i]; + if ( !container.item()->isVisible() ) continue; + if (line) line->setText(k, itemText(container, index)); + else { + addColumn( container.item()->label() ); + setColumnAlignment(k, container.item()->alignment()); + } + k++; + } +} + +//----------------------------------------------------------------------------- +HighscoresList::HighscoresList(QWidget *parent) + : ScoresList(parent) +{} + +QString HighscoresList::itemText(const ItemContainer &item, uint row) const +{ + return item.pretty(row); +} + +void HighscoresList::load(const ItemArray &items, int highlight) +{ + clear(); + QListViewItem *line = 0; + for (int j=items.nbEntries()-1; j>=0; j--) { + QListViewItem *item = addLine(items, j, j==highlight); + if ( j==highlight ) line = item; + } + if (line) ensureItemVisible(line); +} + +//----------------------------------------------------------------------------- +HighscoresWidget::HighscoresWidget(QWidget *parent) + : QWidget(parent, "show_highscores_widget"), + _scoresUrl(0), _playersUrl(0), _statsTab(0), _histoTab(0) +{ + const ScoreInfos &s = internal->scoreInfos(); + const PlayerInfos &p = internal->playerInfos(); + + QVBoxLayout *vbox = new QVBoxLayout(this, KDialogBase::spacingHint()); + + _tw = new QTabWidget(this); + connect(_tw, SIGNAL(currentChanged(QWidget *)), SLOT(tabChanged())); + vbox->addWidget(_tw); + + // scores tab + _scoresList = new HighscoresList(_tw); + _scoresList->addHeader(s); + _tw->addTab(_scoresList, i18n("Best &Scores")); + + // players tab + _playersList = new HighscoresList(_tw); + _playersList->addHeader(p); + _tw->addTab(_playersList, i18n("&Players")); + + // statistics tab + if ( internal->showStatistics ) { + _statsTab = new StatisticsTab(_tw); + _tw->addTab(_statsTab, i18n("Statistics")); + } + + // histogram tab + if ( p.histogram().size()!=0 ) { + _histoTab = new HistogramTab(_tw); + _tw->addTab(_histoTab, i18n("Histogram")); + } + + // url labels + if ( internal->isWWHSAvailable() ) { + KURL url = internal->queryURL(ManagerPrivate::Scores); + _scoresUrl = new KURLLabel(url.url(), + i18n("View world-wide highscores"), this); + connect(_scoresUrl, SIGNAL(leftClickedURL(const QString &)), + SLOT(showURL(const QString &))); + vbox->addWidget(_scoresUrl); + + url = internal->queryURL(ManagerPrivate::Players); + _playersUrl = new KURLLabel(url.url(), + i18n("View world-wide players"), this); + connect(_playersUrl, SIGNAL(leftClickedURL(const QString &)), + SLOT(showURL(const QString &))); + vbox->addWidget(_playersUrl); + } +} + +void HighscoresWidget::changeTab(int i) +{ + if ( i!=_tw->currentPageIndex() ) + _tw->setCurrentPage(i); +} + +void HighscoresWidget::showURL(const QString &url) const +{ + (void)new KRun(KURL(url)); +} + +void HighscoresWidget::load(int rank) +{ + _scoresList->load(internal->scoreInfos(), rank); + _playersList->load(internal->playerInfos(), internal->playerInfos().id()); + if (_scoresUrl) + _scoresUrl->setURL(internal->queryURL(ManagerPrivate::Scores).url()); + if (_playersUrl) + _playersUrl->setURL(internal->queryURL(ManagerPrivate::Players).url()); + if (_statsTab) _statsTab->load(); + if (_histoTab) _histoTab->load(); +} + +//----------------------------------------------------------------------------- +HighscoresDialog::HighscoresDialog(int rank, QWidget *parent) + : KDialogBase(internal->nbGameTypes()>1 ? TreeList : Plain, + i18n("Highscores"), Close|User1|User2, Close, + parent, "show_highscores", true, true, + KGuiItem(i18n("Configure..."), "configure"), + KGuiItem(i18n("Export..."))), _rank(rank), _tab(0) +{ + _widgets.resize(internal->nbGameTypes(), 0); + + if ( internal->nbGameTypes()>1 ) { + for (uint i=0; i<internal->nbGameTypes(); i++) { + QString title = internal->manager.gameTypeLabel(i, Manager::I18N); + QString icon = internal->manager.gameTypeLabel(i, Manager::Icon); + QWidget *w = addVBoxPage(title, QString::null, + BarIcon(icon, KIcon::SizeLarge)); + if ( i==internal->gameType() ) createPage(w); + } + + connect(this, SIGNAL(aboutToShowPage(QWidget *)), + SLOT(createPage(QWidget *))); + showPage(internal->gameType()); + } else { + QVBoxLayout *vbox = new QVBoxLayout(plainPage()); + createPage(plainPage()); + vbox->addWidget(_widgets[0]); + setMainWidget(_widgets[0]); + } +} + +void HighscoresDialog::createPage(QWidget *page) +{ + internal->hsConfig().readCurrentConfig(); + _current = page; + bool several = ( internal->nbGameTypes()>1 ); + int i = (several ? pageIndex(page) : 0); + if ( _widgets[i]==0 ) { + _widgets[i] = new HighscoresWidget(page); + connect(_widgets[i], SIGNAL(tabChanged(int)), SLOT(tabChanged(int))); + } + uint type = internal->gameType(); + if (several) internal->setGameType(i); + _widgets[i]->load(uint(i)==type ? _rank : -1); + if (several) setGameType(type); + _widgets[i]->changeTab(_tab); +} + +void HighscoresDialog::slotUser1() +{ + if ( KExtHighscore::configure(this) ) + createPage(_current); +} + +void HighscoresDialog::slotUser2() +{ + KURL url = KFileDialog::getSaveURL(QString::null, QString::null, this); + if ( url.isEmpty() ) return; + if ( KIO::NetAccess::exists(url, true, this) ) { + KGuiItem gi = KStdGuiItem::save(); + gi.setText(i18n("Overwrite")); + int res = KMessageBox::warningContinueCancel(this, + i18n("The file already exists. Overwrite?"), + i18n("Export"), gi); + if ( res==KMessageBox::Cancel ) return; + } + KTempFile tmp; + internal->exportHighscores(*tmp.textStream()); + tmp.close(); + KIO::NetAccess::upload(tmp.name(), url, this); + tmp.unlink(); +} + +//----------------------------------------------------------------------------- +LastMultipleScoresList::LastMultipleScoresList( + const QValueVector<Score> &scores, QWidget *parent) + : ScoresList(parent), _scores(scores) +{ + const ScoreInfos &s = internal->scoreInfos(); + addHeader(s); + for (uint i=0; i<scores.size(); i++) addLine(s, i, false); +} + +void LastMultipleScoresList::addLineItem(const ItemArray &si, + uint index, QListViewItem *line) +{ + uint k = 1; // skip "id" + for (uint i=0; i<si.size()-2; i++) { + if ( i==3 ) k = 5; // skip "date" + const ItemContainer *container = si[k]; + k++; + if (line) line->setText(i, itemText(*container, index)); + else { + addColumn( container->item()->label() ); + setColumnAlignment(i, container->item()->alignment()); + } + } +} + +QString LastMultipleScoresList::itemText(const ItemContainer &item, + uint row) const +{ + QString name = item.name(); + if ( name=="rank" ) + return (_scores[row].type()==Won ? i18n("Winner") : QString::null); + QVariant v = _scores[row].data(name); + if ( name=="name" ) return v.toString(); + return item.item()->pretty(row, v); +} + +//----------------------------------------------------------------------------- +TotalMultipleScoresList::TotalMultipleScoresList( + const QValueVector<Score> &scores, QWidget *parent) + : ScoresList(parent), _scores(scores) +{ + const ScoreInfos &s = internal->scoreInfos(); + addHeader(s); + for (uint i=0; i<scores.size(); i++) addLine(s, i, false); +} + +void TotalMultipleScoresList::addLineItem(const ItemArray &si, + uint index, QListViewItem *line) +{ + const PlayerInfos &pi = internal->playerInfos(); + uint k = 1; // skip "id" + for (uint i=0; i<4; i++) { // skip additional fields + const ItemContainer *container; + if ( i==2 ) container = pi.item("nb games"); + else if ( i==3 ) container = pi.item("mean score"); + else { + container = si[k]; + k++; + } + if (line) line->setText(i, itemText(*container, index)); + else { + QString label = + (i==2 ? i18n("Won Games") : container->item()->label()); + addColumn(label); + setColumnAlignment(i, container->item()->alignment()); + } + } +} + +QString TotalMultipleScoresList::itemText(const ItemContainer &item, + uint row) const +{ + QString name = item.name(); + if ( name=="rank" ) return QString::number(_scores.size()-row); + if ( name=="nb games" ) + return QString::number( _scores[row].data("nb won games").toUInt() ); + QVariant v = _scores[row].data(name); + if ( name=="name" ) return v.toString(); + return item.item()->pretty(row, v); +} + + +//----------------------------------------------------------------------------- +ConfigDialog::ConfigDialog(QWidget *parent) + : KDialogBase(Swallow, i18n("Configure Highscores"), + Ok|Apply|Cancel, Cancel, + parent, "configure_highscores", true, true), + _saved(false), _WWHEnabled(0) +{ + QWidget *page = 0; + QTabWidget *tab = 0; + if ( internal->isWWHSAvailable() ) { + tab = new QTabWidget(this); + setMainWidget(tab); + page = new QWidget(tab); + tab->addTab(page, i18n("Main")); + } else { + page = new QWidget(this); + setMainWidget(page); + } + + QGridLayout *pageTop = + new QGridLayout(page, 2, 2, spacingHint(), spacingHint()); + + QLabel *label = new QLabel(i18n("Nickname:"), page); + pageTop->addWidget(label, 0, 0); + _nickname = new QLineEdit(page); + connect(_nickname, SIGNAL(textChanged(const QString &)), + SLOT(modifiedSlot())); + connect(_nickname, SIGNAL(textChanged(const QString &)), + SLOT(nickNameChanged(const QString &))); + + _nickname->setMaxLength(16); + pageTop->addWidget(_nickname, 0, 1); + + label = new QLabel(i18n("Comment:"), page); + pageTop->addWidget(label, 1, 0); + _comment = new QLineEdit(page); + connect(_comment, SIGNAL(textChanged(const QString &)), + SLOT(modifiedSlot())); + _comment->setMaxLength(50); + pageTop->addWidget(_comment, 1, 1); + + if (tab) { + _WWHEnabled + = new QCheckBox(i18n("World-wide highscores enabled"), page); + connect(_WWHEnabled, SIGNAL(toggled(bool)), + SLOT(modifiedSlot())); + pageTop->addMultiCellWidget(_WWHEnabled, 2, 2, 0, 1); + + // advanced tab + QWidget *page = new QWidget(tab); + tab->addTab(page, i18n("Advanced")); + QVBoxLayout *pageTop = + new QVBoxLayout(page, spacingHint(), spacingHint()); + + QVGroupBox *group = new QVGroupBox(i18n("Registration Data"), page); + pageTop->addWidget(group); + QGrid *grid = new QGrid(2, group); + grid->setSpacing(spacingHint()); + + label = new QLabel(i18n("Nickname:"), grid); + _registeredName = new KLineEdit(grid); + _registeredName->setReadOnly(true); + + label = new QLabel(i18n("Key:"), grid); + _key = new KLineEdit(grid); + _key->setReadOnly(true); + + KGuiItem gi = KStdGuiItem::clear(); + gi.setText(i18n("Remove")); + _removeButton = new KPushButton(gi, grid); + connect(_removeButton, SIGNAL(clicked()), SLOT(removeSlot())); + } + + load(); + enableButtonOK( !_nickname->text().isEmpty() ); + enableButtonApply(false); +} + +void ConfigDialog::nickNameChanged(const QString &text) +{ + enableButtonOK( !text.isEmpty() ); +} + + +void ConfigDialog::modifiedSlot() +{ + enableButtonApply(true && !_nickname->text().isEmpty() ); +} + +void ConfigDialog::accept() +{ + if ( save() ) { + KDialogBase::accept(); + kapp->config()->sync(); // safer + } +} + +void ConfigDialog::removeSlot() +{ + KGuiItem gi = KStdGuiItem::clear(); + gi.setText(i18n("Remove")); + int res = KMessageBox::warningContinueCancel(this, + i18n("This will permanently remove your " + "registration key. You will not be able to use " + "the currently registered nickname anymore."), + QString::null, gi); + if ( res==KMessageBox::Continue ) { + internal->playerInfos().removeKey(); + _registeredName->clear(); + _key->clear(); + _removeButton->setEnabled(false); + _WWHEnabled->setChecked(false); + modifiedSlot(); + } +} + +void ConfigDialog::load() +{ + internal->hsConfig().readCurrentConfig(); + const PlayerInfos &infos = internal->playerInfos(); + _nickname->setText(infos.isAnonymous() ? QString::null : infos.name()); + _comment->setText(infos.comment()); + if (_WWHEnabled) { + _WWHEnabled->setChecked(infos.isWWEnabled()); + if ( !infos.key().isEmpty() ) { + _registeredName->setText(infos.registeredName()); + _registeredName->home(false); + _key->setText(infos.key()); + _key->home(false); + } + _removeButton->setEnabled(!infos.key().isEmpty()); + } +} + +bool ConfigDialog::save() +{ + bool enabled = (_WWHEnabled ? _WWHEnabled->isChecked() : false); + + // do not bother the user with "nickname empty" if he has not + // messed with nickname settings ... + QString newName = _nickname->text(); + if ( newName.isEmpty() && !internal->playerInfos().isAnonymous() + && !enabled ) return true; + + if ( newName.isEmpty() ) { + KMessageBox::sorry(this, i18n("Please choose a non empty nickname.")); + return false; + } + if ( internal->playerInfos().isNameUsed(newName) ) { + KMessageBox::sorry(this, i18n("Nickname already in use. Please " + "choose another one")); + return false; + } + + int res = + internal->modifySettings(newName, _comment->text(), enabled, this); + if (res) { + load(); // needed to update view when "apply" is clicked + enableButtonApply(false); + } + _saved = true; + return res; +} + +//----------------------------------------------------------------------------- +AskNameDialog::AskNameDialog(QWidget *parent) + : KDialogBase(Plain, i18n("Enter Your Nickname"), Ok | Cancel, Ok, + parent, "ask_name_dialog") +{ + internal->hsConfig().readCurrentConfig(); + + QVBoxLayout *top = + new QVBoxLayout(plainPage(), marginHint(), spacingHint()); + QLabel *label = + new QLabel(i18n("Congratulations, you have won!"), plainPage()); + top->addWidget(label); + + QHBoxLayout *hbox = new QHBoxLayout(top); + label = new QLabel(i18n("Enter your nickname:"), plainPage()); + hbox->addWidget(label); + _edit = new QLineEdit(plainPage()); + _edit->setFocus(); + connect(_edit, SIGNAL(textChanged(const QString &)), SLOT(nameChanged())); + hbox->addWidget(_edit); + + top->addSpacing(spacingHint()); + _checkbox = new QCheckBox(i18n("Do not ask again."), plainPage()); + top->addWidget(_checkbox); + + nameChanged(); +} + +void AskNameDialog::nameChanged() +{ + enableButtonOK( !name().isEmpty() + && !internal->playerInfos().isNameUsed(name()) ); +} + +} // namespace diff --git a/libkdegames/highscore/kexthighscore_gui.h b/libkdegames/highscore/kexthighscore_gui.h new file mode 100644 index 00000000..e721299a --- /dev/null +++ b/libkdegames/highscore/kexthighscore_gui.h @@ -0,0 +1,207 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-02 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 KEXTHIGHSCORE_GUI_H +#define KEXTHIGHSCORE_GUI_H + +#include <qcheckbox.h> +#include <qlabel.h> +#include <qvbox.h> +#include <qtabwidget.h> + +#include <klistview.h> +#include <klineedit.h> +#include <kpushbutton.h> +#include <kdialogbase.h> + +#include "kexthighscore.h" + + +namespace KExtHighscore +{ + +class ItemContainer; +class ItemArray; +class Score; +class AdditionalTab; + +//----------------------------------------------------------------------------- +class ShowItem : public KListViewItem +{ + public: + ShowItem(QListView *, bool highlight); + + protected: + virtual void paintCell(QPainter *, const QColorGroup &, int column, + int width, int align); + + private: + bool _highlight; +}; + +class ScoresList : public KListView +{ + Q_OBJECT + public: + ScoresList(QWidget *parent); + + void addHeader(const ItemArray &); + + protected: + QListViewItem *addLine(const ItemArray &, uint index, bool highlight); + virtual QString itemText(const ItemContainer &, uint row) const = 0; + + private: + virtual void addLineItem(const ItemArray &, uint index, + QListViewItem *item); +}; + +//----------------------------------------------------------------------------- +class HighscoresList : public ScoresList +{ + Q_OBJECT + public: + HighscoresList(QWidget *parent); + + void load(const ItemArray &, int highlight); + + protected: + QString itemText(const ItemContainer &, uint row) const; +}; + +class HighscoresWidget : public QWidget +{ + Q_OBJECT + public: + HighscoresWidget(QWidget *parent); + + void load(int rank); + + signals: + void tabChanged(int i); + + public slots: + void changeTab(int i); + + private slots: + void showURL(const QString &) const; + void tabChanged() { emit tabChanged(_tw->currentPageIndex()); } + + private: + QTabWidget *_tw; + HighscoresList *_scoresList, *_playersList; + KURLLabel *_scoresUrl, *_playersUrl; + AdditionalTab *_statsTab, *_histoTab; +}; + +class HighscoresDialog : public KDialogBase +{ + Q_OBJECT + public: + HighscoresDialog(int rank, QWidget *parent); + + private slots: + void slotUser1(); + void slotUser2(); + void tabChanged(int i) { _tab = i; } + void createPage(QWidget *); + + private: + int _rank, _tab; + QWidget *_current; + QValueVector<HighscoresWidget *> _widgets; +}; + +//----------------------------------------------------------------------------- +class LastMultipleScoresList : public ScoresList +{ + Q_OBJECT +public: + LastMultipleScoresList(const QValueVector<Score> &, QWidget *parent); + +private: + void addLineItem(const ItemArray &, uint index, QListViewItem *line); + QString itemText(const ItemContainer &, uint row) const; + +private: + const QValueVector<Score> &_scores; +}; + +class TotalMultipleScoresList : public ScoresList +{ + Q_OBJECT +public: + TotalMultipleScoresList(const QValueVector<Score> &, QWidget *parent); + +private: + void addLineItem(const ItemArray &, uint index, QListViewItem *line); + QString itemText(const ItemContainer &, uint row) const; + +private: + const QValueVector<Score> &_scores; +}; + +//----------------------------------------------------------------------------- +class ConfigDialog : public KDialogBase +{ + Q_OBJECT + public: + ConfigDialog(QWidget *parent); + + bool hasBeenSaved() const { return _saved; } + + private slots: + void modifiedSlot(); + void removeSlot(); + void accept(); + void slotApply() { save(); } + void nickNameChanged(const QString &); + + private: + bool _saved; + QCheckBox *_WWHEnabled; + QLineEdit *_nickname, *_comment; + KLineEdit *_key, *_registeredName; + KPushButton *_removeButton; + + void load(); + bool save(); +}; + +//----------------------------------------------------------------------------- +class AskNameDialog : public KDialogBase +{ + Q_OBJECT + public: + AskNameDialog(QWidget *parent); + + QString name() const { return _edit->text(); } + bool dontAskAgain() const { return _checkbox->isChecked(); } + + private slots: + void nameChanged(); + + private: + QLineEdit *_edit; + QCheckBox *_checkbox; +}; + +} // namespace + +#endif diff --git a/libkdegames/highscore/kexthighscore_internal.cpp b/libkdegames/highscore/kexthighscore_internal.cpp new file mode 100644 index 00000000..a8395753 --- /dev/null +++ b/libkdegames/highscore/kexthighscore_internal.cpp @@ -0,0 +1,868 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-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 "kexthighscore_internal.h" + +#include <pwd.h> +#include <sys/types.h> +#include <unistd.h> + +#include <qfile.h> +#include <qlayout.h> +#include <qdom.h> + +#include <kglobal.h> +#include <kio/netaccess.h> +#include <kio/job.h> +#include <kmessagebox.h> +#include <kmdcodec.h> +#include <kdebug.h> + +#include "config.h" +#include "kexthighscore.h" +#include "kexthighscore_gui.h" +#include "kemailsettings.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +const char ItemContainer::ANONYMOUS[] = "_"; +const char ItemContainer::ANONYMOUS_LABEL[] = I18N_NOOP("anonymous"); + +ItemContainer::ItemContainer() + : _item(0) +{} + +ItemContainer::~ItemContainer() +{ + delete _item; +} + +void ItemContainer::setItem(Item *item) +{ + delete _item; + _item = item; +} + +QString ItemContainer::entryName() const +{ + if ( _subGroup.isEmpty() ) return _name; + return _name + "_" + _subGroup; +} + +QVariant ItemContainer::read(uint i) const +{ + Q_ASSERT(_item); + + QVariant v = _item->defaultValue(); + if ( isStored() ) { + internal->hsConfig().setHighscoreGroup(_group); + v = internal->hsConfig().readPropertyEntry(i+1, entryName(), v); + } + return _item->read(i, v); +} + +QString ItemContainer::pretty(uint i) const +{ + Q_ASSERT(_item); + return _item->pretty(i, read(i)); +} + +void ItemContainer::write(uint i, const QVariant &value) const +{ + Q_ASSERT( isStored() ); + Q_ASSERT( internal->hsConfig().isLocked() ); + internal->hsConfig().setHighscoreGroup(_group); + internal->hsConfig().writeEntry(i+1, entryName(), value); +} + +uint ItemContainer::increment(uint i) const +{ + uint v = read(i).toUInt() + 1; + write(i, v); + return v; +} + +//----------------------------------------------------------------------------- +ItemArray::ItemArray() + : _group(""), _subGroup("") // no null groups +{} + +ItemArray::~ItemArray() +{ + for (uint i=0; i<size(); i++) delete at(i); +} + +int ItemArray::findIndex(const QString &name) const +{ + for (uint i=0; i<size(); i++) + if ( at(i)->name()==name ) return i; + return -1; +} + +const ItemContainer *ItemArray::item(const QString &name) const +{ + int i = findIndex(name); + if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name + << "\"" << endl; + return at(i); +} + +ItemContainer *ItemArray::item(const QString &name) +{ + int i = findIndex(name); + if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name + << "\"" << endl; + return at(i); +} + +void ItemArray::setItem(const QString &name, Item *item) +{ + int i = findIndex(name); + if ( i==-1 ) kdError(11002) << k_funcinfo << "no item named \"" << name + << "\"" << endl; + bool stored = at(i)->isStored(); + bool canHaveSubGroup = at(i)->canHaveSubGroup(); + _setItem(i, name, item, stored, canHaveSubGroup); +} + +void ItemArray::addItem(const QString &name, Item *item, + bool stored, bool canHaveSubGroup) +{ + if ( findIndex(name)!=-1 ) + kdError(11002) << "item already exists \"" << name << "\"" << endl; + uint i = size(); + resize(i+1); + at(i) = new ItemContainer; + _setItem(i, name, item, stored, canHaveSubGroup); +} + +void ItemArray::_setItem(uint i, const QString &name, Item *item, + bool stored, bool canHaveSubGroup) +{ + at(i)->setItem(item); + at(i)->setName(name); + at(i)->setGroup(stored ? _group : QString::null); + at(i)->setSubGroup(canHaveSubGroup ? _subGroup : QString::null); +} + +void ItemArray::setGroup(const QString &group) +{ + Q_ASSERT( !group.isNull() ); + _group = group; + for (uint i=0; i<size(); i++) + if ( at(i)->isStored() ) at(i)->setGroup(group); +} + +void ItemArray::setSubGroup(const QString &subGroup) +{ + Q_ASSERT( !subGroup.isNull() ); + _subGroup = subGroup; + for (uint i=0; i<size(); i++) + if ( at(i)->canHaveSubGroup() ) at(i)->setSubGroup(subGroup); +} + +void ItemArray::read(uint k, Score &data) const +{ + for (uint i=0; i<size(); i++) { + if ( !at(i)->isStored() ) continue; + data.setData(at(i)->name(), at(i)->read(k)); + } +} + +void ItemArray::write(uint k, const Score &data, uint nb) const +{ + for (uint i=0; i<size(); i++) { + if ( !at(i)->isStored() ) continue; + for (uint j=nb-1; j>k; j--) at(i)->write(j, at(i)->read(j-1)); + at(i)->write(k, data.data(at(i)->name())); + } +} + +void ItemArray::exportToText(QTextStream &s) const +{ + for (uint k=0; k<nbEntries()+1; k++) { + for (uint i=0; i<size(); i++) { + const Item *item = at(i)->item(); + if ( item->isVisible() ) { + if ( i!=0 ) s << '\t'; + if ( k==0 ) s << item->label(); + else s << at(i)->pretty(k-1); + } + } + s << endl; + } +} + +//----------------------------------------------------------------------------- +class ScoreNameItem : public NameItem +{ + public: + ScoreNameItem(const ScoreInfos &score, const PlayerInfos &infos) + : _score(score), _infos(infos) {} + + QString pretty(uint i, const QVariant &v) const { + uint id = _score.item("id")->read(i).toUInt(); + if ( id==0 ) return NameItem::pretty(i, v); + return _infos.prettyName(id-1); + } + + private: + const ScoreInfos &_score; + const PlayerInfos &_infos; +}; + +//----------------------------------------------------------------------------- +ScoreInfos::ScoreInfos(uint maxNbEntries, const PlayerInfos &infos) + : _maxNbEntries(maxNbEntries) +{ + addItem("id", new Item((uint)0)); + addItem("rank", new RankItem, false); + addItem("name", new ScoreNameItem(*this, infos)); + addItem("score", Manager::createItem(Manager::ScoreDefault)); + addItem("date", new DateItem); +} + +uint ScoreInfos::nbEntries() const +{ + uint i = 0; + for (; i<_maxNbEntries; i++) + if ( item("score")->read(i)==item("score")->item()->defaultValue() ) + break; + return i; +} + +//----------------------------------------------------------------------------- +const char *HS_ID = "player id"; +const char *HS_REGISTERED_NAME = "registered name"; +const char *HS_KEY = "player key"; +const char *HS_WW_ENABLED = "ww hs enabled"; + +PlayerInfos::PlayerInfos() +{ + setGroup("players"); + + // standard items + addItem("name", new NameItem); + Item *it = new Item((uint)0, i18n("Games Count"),Qt::AlignRight); + addItem("nb games", it, true, true); + it = Manager::createItem(Manager::MeanScoreDefault); + addItem("mean score", it, true, true); + it = Manager::createItem(Manager::BestScoreDefault); + addItem("best score", it, true, true); + addItem("date", new DateItem, true, true); + it = new Item(QString::null, i18n("Comment"), Qt::AlignLeft); + addItem("comment", it); + + // statistics items + addItem("nb black marks", new Item((uint)0), true, true); // legacy + addItem("nb lost games", new Item((uint)0), true, true); + addItem("nb draw games", new Item((uint)0), true, true); + addItem("current trend", new Item((int)0), true, true); + addItem("max lost trend", new Item((uint)0), true, true); + addItem("max won trend", new Item((uint)0), true, true); + + struct passwd *pwd = getpwuid(getuid()); + QString username = pwd->pw_name; +#ifdef HIGHSCORE_DIRECTORY + internal->hsConfig().setHighscoreGroup("players"); + for (uint i=0; ;i++) { + if ( !internal->hsConfig().hasEntry(i+1, "username") ) { + _newPlayer = true; + _id = i; + break; + } + if ( internal->hsConfig().readEntry(i+1, "username")==username ) { + _newPlayer = false; + _id = i; + return; + } + } +#endif + internal->hsConfig().lockForWriting(); + KEMailSettings emailConfig; + emailConfig.setProfile(emailConfig.defaultProfileName()); + QString name = emailConfig.getSetting(KEMailSettings::RealName); + if ( name.isEmpty() || isNameUsed(name) ) name = username; + if ( isNameUsed(name) ) name= QString(ItemContainer::ANONYMOUS); +#ifdef HIGHSCORE_DIRECTORY + internal->hsConfig().writeEntry(_id+1, "username", username); + item("name")->write(_id, name); +#endif + + ConfigGroup cg; + _oldLocalPlayer = cg.config()->hasKey(HS_ID); + _oldLocalId = cg.config()->readUnsignedNumEntry(HS_ID); +#ifdef HIGHSCORE_DIRECTORY + if (_oldLocalPlayer) { // player already exists in local config file + // copy player data + QString prefix = QString("%1_").arg(_oldLocalId+1); + QMap<QString, QString> entries = + cg.config()->entryMap("KHighscore_players"); + QMap<QString, QString>::const_iterator it; + for (it=entries.begin(); it!=entries.end(); ++it) { + QString key = it.key(); + if ( key.find(prefix)==0 ) { + QString name = key.right(key.length()-prefix.length()); + if ( name!="name" || !isNameUsed(it.data()) ) + internal->hsConfig().writeEntry(_id+1, name, it.data()); + } + } + } +#else + _newPlayer = !_oldLocalPlayer; + if (_oldLocalPlayer) _id = _oldLocalId; + else { + _id = nbEntries(); + cg.config()->writeEntry(HS_ID, _id); + item("name")->write(_id, name); + } +#endif + _bound = true; + internal->hsConfig().writeAndUnlock(); +} + +void PlayerInfos::createHistoItems(const QMemArray<uint> &scores, bool bound) +{ + Q_ASSERT( _histogram.size()==0 ); + _bound = bound; + _histogram = scores; + for (uint i=1; i<histoSize(); i++) + addItem(histoName(i), new Item((uint)0), true, true); +} + +bool PlayerInfos::isAnonymous() const +{ + return ( name()==ItemContainer::ANONYMOUS ); +} + +uint PlayerInfos::nbEntries() const +{ + internal->hsConfig().setHighscoreGroup("players"); + QStringList list = internal->hsConfig().readList("name", -1); + return list.count(); +} + +QString PlayerInfos::key() const +{ + ConfigGroup cg; + return cg.config()->readEntry(HS_KEY, QString::null); +} + +bool PlayerInfos::isWWEnabled() const +{ + ConfigGroup cg; + return cg.config()->readBoolEntry(HS_WW_ENABLED, false); +} + +QString PlayerInfos::histoName(uint i) const +{ + const QMemArray<uint> &sh = _histogram; + Q_ASSERT( i<sh.size() || (_bound || i==sh.size()) ); + if ( i==sh.size() ) + return QString("nb scores greater than %1").arg(sh[sh.size()-1]); + return QString("nb scores less than %1").arg(sh[i]); +} + +uint PlayerInfos::histoSize() const +{ + return _histogram.size() + (_bound ? 0 : 1); +} + +void PlayerInfos::submitScore(const Score &score) const +{ + // update counts + uint nbGames = item("nb games")->increment(_id); + switch (score.type()) { + case Lost: + item("nb lost games")->increment(_id); + break; + case Won: break; + case Draw: + item("nb draw games")->increment(_id); + break; + }; + + // update mean + if ( score.type()==Won ) { + uint nbWonGames = nbGames - item("nb lost games")->read(_id).toUInt() + - item("nb draw games")->read(_id).toUInt() + - item("nb black marks")->read(_id).toUInt(); // legacy + double mean = (nbWonGames==1 ? 0.0 + : item("mean score")->read(_id).toDouble()); + mean += (double(score.score()) - mean) / nbWonGames; + item("mean score")->write(_id, mean); + } + + // update best score + Score best = score; // copy optionnal fields (there are not taken into account here) + best.setScore( item("best score")->read(_id).toUInt() ); + if ( best<score ) { + item("best score")->write(_id, score.score()); + item("date")->write(_id, score.data("date").toDateTime()); + } + + // update trends + int current = item("current trend")->read(_id).toInt(); + switch (score.type()) { + case Won: { + if ( current<0 ) current = 0; + current++; + uint won = item("max won trend")->read(_id).toUInt(); + if ( (uint)current>won ) item("max won trend")->write(_id, current); + break; + } + case Lost: { + if ( current>0 ) current = 0; + current--; + uint lost = item("max lost trend")->read(_id).toUInt(); + uint clost = -current; + if ( clost>lost ) item("max lost trend")->write(_id, clost); + break; + } + case Draw: + current = 0; + break; + } + item("current trend")->write(_id, current); + + // update histogram + if ( score.type()==Won ) { + const QMemArray<uint> &sh = _histogram; + for (uint i=1; i<histoSize(); i++) + if ( i==sh.size() || score.score()<sh[i] ) { + item(histoName(i))->increment(_id); + break; + } + } +} + +bool PlayerInfos::isNameUsed(const QString &newName) const +{ + if ( newName==name() ) return false; // own name... + for (uint i=0; i<nbEntries(); i++) + if ( newName.lower()==item("name")->read(i).toString().lower() ) return true; + if ( newName==i18n(ItemContainer::ANONYMOUS_LABEL) ) return true; + return false; +} + +void PlayerInfos::modifyName(const QString &newName) const +{ + item("name")->write(_id, newName); +} + +void PlayerInfos::modifySettings(const QString &newName, + const QString &comment, bool WWEnabled, + const QString &newKey) const +{ + modifyName(newName); + item("comment")->write(_id, comment); + ConfigGroup cg; + cg.config()->writeEntry(HS_WW_ENABLED, WWEnabled); + if ( !newKey.isEmpty() ) cg.config()->writeEntry(HS_KEY, newKey); + if (WWEnabled) cg.config()->writeEntry(HS_REGISTERED_NAME, newName); +} + +QString PlayerInfos::registeredName() const +{ + ConfigGroup cg; + return cg.config()->readEntry(HS_REGISTERED_NAME, QString::null); +} + +void PlayerInfos::removeKey() +{ + ConfigGroup cg; + + // save old key/nickname + uint i = 0; + QString str = "%1 old #%2"; + QString sk; + do { + i++; + sk = str.arg(HS_KEY).arg(i); + } while ( !cg.config()->readEntry(sk, QString::null).isEmpty() ); + cg.config()->writeEntry(sk, key()); + cg.config()->writeEntry(str.arg(HS_REGISTERED_NAME).arg(i), + registeredName()); + + // clear current key/nickname + cg.config()->deleteEntry(HS_KEY); + cg.config()->deleteEntry(HS_REGISTERED_NAME); + cg.config()->writeEntry(HS_WW_ENABLED, false); +} + +//----------------------------------------------------------------------------- +ManagerPrivate::ManagerPrivate(uint nbGameTypes, Manager &m) + : manager(m), showStatistics(false), showDrawGames(false), + trackLostGames(false), trackDrawGames(false), + showMode(Manager::ShowForHigherScore), + _first(true), _nbGameTypes(nbGameTypes), _gameType(0) +{} + +void ManagerPrivate::init(uint maxNbEntries) +{ + _hsConfig = new KHighscore(false, 0); + _playerInfos = new PlayerInfos; + _scoreInfos = new ScoreInfos(maxNbEntries, *_playerInfos); +} + +ManagerPrivate::~ManagerPrivate() +{ + delete _scoreInfos; + delete _playerInfos; + delete _hsConfig; +} + +KURL ManagerPrivate::queryURL(QueryType type, const QString &newName) const +{ + KURL url = serverURL; + QString nameItem = "nickname"; + QString name = _playerInfos->registeredName(); + bool withVersion = true; + bool key = false; + bool level = false; + + switch (type) { + case Submit: + url.addPath("submit.php"); + level = true; + key = true; + break; + case Register: + url.addPath("register.php"); + name = newName; + break; + case Change: + url.addPath("change.php"); + key = true; + if ( newName!=name ) + Manager::addToQueryURL(url, "new_nickname", newName); + break; + case Players: + url.addPath("players.php"); + nameItem = "highlight"; + withVersion = false; + break; + case Scores: + url.addPath("highscores.php"); + withVersion = false; + if ( _nbGameTypes>1 ) level = true; + break; + } + + if (withVersion) Manager::addToQueryURL(url, "version", version); + if ( !name.isEmpty() ) Manager::addToQueryURL(url, nameItem, name); + if (key) Manager::addToQueryURL(url, "key", _playerInfos->key()); + if (level) { + QString label = manager.gameTypeLabel(_gameType, Manager::WW); + if ( !label.isEmpty() ) Manager::addToQueryURL(url, "level", label); + } + + return url; +} + +// strings that needs to be translated (coming from the highscores server) +const char *DUMMY_STRINGS[] = { + I18N_NOOP("Undefined error."), + I18N_NOOP("Missing argument(s)."), + I18N_NOOP("Invalid argument(s)."), + + I18N_NOOP("Unable to connect to MySQL server."), + I18N_NOOP("Unable to select database."), + I18N_NOOP("Error on database query."), + I18N_NOOP("Error on database insert."), + + I18N_NOOP("Nickname already registered."), + I18N_NOOP("Nickname not registered."), + I18N_NOOP("Invalid key."), + I18N_NOOP("Invalid submit key."), + + I18N_NOOP("Invalid level."), + I18N_NOOP("Invalid score.") +}; + +const char *UNABLE_TO_CONTACT = + I18N_NOOP("Unable to contact world-wide highscore server"); + +bool ManagerPrivate::doQuery(const KURL &url, QWidget *parent, + QDomNamedNodeMap *map) +{ + KIO::http_update_cache(url, true, 0); // remove cache ! + + QString tmpFile; + if ( !KIO::NetAccess::download(url, tmpFile, parent) ) { + QString details = i18n("Server URL: %1").arg(url.host()); + KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details); + return false; + } + + QFile file(tmpFile); + if ( !file.open(IO_ReadOnly) ) { + KIO::NetAccess::removeTempFile(tmpFile); + QString details = i18n("Unable to open temporary file."); + KMessageBox::detailedSorry(parent, i18n(UNABLE_TO_CONTACT), details); + return false; + } + + QTextStream t(&file); + QString content = t.read().stripWhiteSpace(); + file.close(); + KIO::NetAccess::removeTempFile(tmpFile); + + QDomDocument doc; + if ( doc.setContent(content) ) { + QDomElement root = doc.documentElement(); + QDomElement element = root.firstChild().toElement(); + if ( element.tagName()=="success" ) { + if (map) *map = element.attributes(); + return true; + } + if ( element.tagName()=="error" ) { + QDomAttr attr = element.attributes().namedItem("label").toAttr(); + if ( !attr.isNull() ) { + QString msg = i18n(attr.value().latin1()); + QString caption = i18n("Message from world-wide highscores " + "server"); + KMessageBox::sorry(parent, msg, caption); + return false; + } + } + } + QString msg = i18n("Invalid answer from world-wide highscores server."); + QString details = i18n("Raw message: %1").arg(content); + KMessageBox::detailedSorry(parent, msg, details); + return false; +} + +bool ManagerPrivate::getFromQuery(const QDomNamedNodeMap &map, + const QString &name, QString &value, + QWidget *parent) +{ + QDomAttr attr = map.namedItem(name).toAttr(); + if ( attr.isNull() ) { + KMessageBox::sorry(parent, + i18n("Invalid answer from world-wide " + "highscores server (missing item: %1).").arg(name)); + return false; + } + value = attr.value(); + return true; +} + +Score ManagerPrivate::readScore(uint i) const +{ + Score score(Won); + _scoreInfos->read(i, score); + return score; +} + +int ManagerPrivate::rank(const Score &score) const +{ + uint nb = _scoreInfos->nbEntries(); + uint i = 0; + for (; i<nb; i++) + if ( readScore(i)<score ) break; + return (i<_scoreInfos->maxNbEntries() ? (int)i : -1); +} + +bool ManagerPrivate::modifySettings(const QString &newName, + const QString &comment, bool WWEnabled, + QWidget *widget) +{ + QString newKey; + bool newPlayer = false; + + if (WWEnabled) { + newPlayer = _playerInfos->key().isEmpty() + || _playerInfos->registeredName().isEmpty(); + KURL url = queryURL((newPlayer ? Register : Change), newName); + Manager::addToQueryURL(url, "comment", comment); + + QDomNamedNodeMap map; + bool ok = doQuery(url, widget, &map); + if ( !ok || (newPlayer && !getFromQuery(map, "key", newKey, widget)) ) + return false; + } + + bool ok = _hsConfig->lockForWriting(widget); // no GUI when locking + if (ok) { + // check again name in case the config file has been changed... + // if it has, it is unfortunate because the WWW name is already + // committed but should be very rare and not really problematic + ok = ( !_playerInfos->isNameUsed(newName) ); + if (ok) + _playerInfos->modifySettings(newName, comment, WWEnabled, newKey); + _hsConfig->writeAndUnlock(); + } + return ok; +} + +void ManagerPrivate::convertToGlobal() +{ + // read old highscores + KHighscore *tmp = _hsConfig; + _hsConfig = new KHighscore(true, 0); + QValueVector<Score> scores(_scoreInfos->nbEntries()); + for (uint i=0; i<scores.count(); i++) + scores[i] = readScore(i); + + // commit them + delete _hsConfig; + _hsConfig = tmp; + _hsConfig->lockForWriting(); + for (uint i=0; i<scores.count(); i++) + if ( scores[i].data("id").toUInt()==_playerInfos->oldLocalId()+1 ) + submitLocal(scores[i]); + _hsConfig->writeAndUnlock(); +} + +void ManagerPrivate::setGameType(uint type) +{ + if (_first) { + _first = false; + if ( _playerInfos->isNewPlayer() ) { + // convert legacy highscores + for (uint i=0; i<_nbGameTypes; i++) { + setGameType(i); + manager.convertLegacy(i); + } + +#ifdef HIGHSCORE_DIRECTORY + if ( _playerInfos->isOldLocalPlayer() ) { + // convert local to global highscores + for (uint i=0; i<_nbGameTypes; i++) { + setGameType(i); + convertToGlobal(); + } + } +#endif + } + } + + Q_ASSERT( type<_nbGameTypes ); + _gameType = kMin(type, _nbGameTypes-1); + QString str = "scores"; + QString lab = manager.gameTypeLabel(_gameType, Manager::Standard); + if ( !lab.isEmpty() ) { + _playerInfos->setSubGroup(lab); + str += "_" + lab; + } + _scoreInfos->setGroup(str); +} + +void ManagerPrivate::checkFirst() +{ + if (_first) setGameType(0); +} + +int ManagerPrivate::submitScore(const Score &ascore, + QWidget *widget, bool askIfAnonymous) +{ + checkFirst(); + + Score score = ascore; + score.setData("id", _playerInfos->id() + 1); + score.setData("date", QDateTime::currentDateTime()); + + // ask new name if anonymous and winner + const char *dontAskAgainName = "highscore_ask_name_dialog"; + QString newName; + KMessageBox::ButtonCode dummy; + if ( score.type()==Won && askIfAnonymous && _playerInfos->isAnonymous() + && KMessageBox::shouldBeShownYesNo(dontAskAgainName, dummy) ) { + AskNameDialog d(widget); + if ( d.exec()==QDialog::Accepted ) newName = d.name(); + if ( d.dontAskAgain() ) + KMessageBox::saveDontShowAgainYesNo(dontAskAgainName, + KMessageBox::No); + } + + int rank = -1; + if ( _hsConfig->lockForWriting(widget) ) { // no GUI when locking + // check again new name in case the config file has been changed... + if ( !newName.isEmpty() && !_playerInfos->isNameUsed(newName) ) + _playerInfos->modifyName(newName); + + // commit locally + _playerInfos->submitScore(score); + if ( score.type()==Won ) rank = submitLocal(score); + _hsConfig->writeAndUnlock(); + } + + if ( _playerInfos->isWWEnabled() ) + submitWorldWide(score, widget); + + return rank; +} + +int ManagerPrivate::submitLocal(const Score &score) +{ + int r = rank(score); + if ( r!=-1 ) { + uint nb = _scoreInfos->nbEntries(); + if ( nb<_scoreInfos->maxNbEntries() ) nb++; + _scoreInfos->write(r, score, nb); + } + return r; +} + +bool ManagerPrivate::submitWorldWide(const Score &score, + QWidget *widget) const +{ + if ( score.type()==Lost && !trackLostGames ) return true; + if ( score.type()==Draw && !trackDrawGames ) return true; + + KURL url = queryURL(Submit); + manager.additionalQueryItems(url, score); + int s = (score.type()==Won ? score.score() : (int)score.type()); + QString str = QString::number(s); + Manager::addToQueryURL(url, "score", str); + KMD5 context(QString(_playerInfos->registeredName() + str).latin1()); + Manager::addToQueryURL(url, "check", context.hexDigest()); + + return doQuery(url, widget); +} + +void ManagerPrivate::exportHighscores(QTextStream &s) +{ + uint tmp = _gameType; + + for (uint i=0; i<_nbGameTypes; i++) { + setGameType(i); + if ( _nbGameTypes>1 ) { + if ( i!=0 ) s << endl; + s << "--------------------------------" << endl; + s << "Game type: " + << manager.gameTypeLabel(_gameType, Manager::I18N) + << endl; + s << endl; + } + s << "Players list:" << endl; + _playerInfos->exportToText(s); + s << endl; + s << "Highscores list:" << endl; + _scoreInfos->exportToText(s); + } + + setGameType(tmp); +} + +} // namespace diff --git a/libkdegames/highscore/kexthighscore_internal.h b/libkdegames/highscore/kexthighscore_internal.h new file mode 100644 index 00000000..3b206877 --- /dev/null +++ b/libkdegames/highscore/kexthighscore_internal.h @@ -0,0 +1,277 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-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 KEXTHIGHSCORE_INTERNAL_H +#define KEXTHIGHSCORE_INTERNAL_H + +#include <kapplication.h> +#include <kconfig.h> +#include <klocale.h> +#include <kurl.h> + +#include "khighscore.h" +#include "kexthighscore.h" + +class QTextStream; +class QTabWidget; +class QDomNamedNodeMap; + + +namespace KExtHighscore +{ + +class PlayerInfos; +class Score; +class Manager; + + +//----------------------------------------------------------------------------- +class RankItem : public Item +{ + public: + RankItem() + : Item((uint)0, i18n("Rank"), Qt::AlignRight) {} + + QVariant read(uint rank, const QVariant &) const { return rank; } + QString pretty(uint rank, const QVariant &) const + { return QString::number(rank+1); } +}; + +class NameItem : public Item +{ + public: + NameItem() + : Item(QString::null, i18n("Name"), Qt::AlignLeft) { + setPrettySpecial(Anonymous); + } +}; + +class DateItem : public Item +{ + public: + DateItem() + : Item(QDateTime(), i18n("Date"), Qt::AlignRight) { + setPrettyFormat(DateTime); + } +}; + +class SuccessPercentageItem : public Item +{ + public: + SuccessPercentageItem() + : Item((double)-1, i18n("Success"), Qt::AlignRight) { + setPrettyFormat(Percentage); + setPrettySpecial(NegativeNotDefined); + } +}; + +//----------------------------------------------------------------------------- +class ItemContainer +{ + public: + ItemContainer(); + ~ItemContainer(); + + void setItem(Item *item); + const Item *item() const { return _item; } + Item *item() { return _item; } + + void setName(const QString &name) { _name = name; } + QString name() const { return _name; } + + void setGroup(const QString &group) { _group = group; } + bool isStored() const { return !_group.isNull(); } + + void setSubGroup(const QString &subGroup) { _subGroup = subGroup; } + bool canHaveSubGroup() const { return !_subGroup.isNull(); } + + static const char ANONYMOUS[]; // name assigned to anonymous players + static const char ANONYMOUS_LABEL[]; + + QVariant read(uint i) const; + QString pretty(uint i) const; + void write(uint i, const QVariant &value) const; + // for UInt QVariant (return new value) + uint increment(uint i) const; + + private: + Item *_item; + QString _name, _group, _subGroup; + + QString entryName() const; + + ItemContainer(const ItemContainer &); + ItemContainer &operator =(const ItemContainer &); +}; + +//----------------------------------------------------------------------------- +/** + * Manage a bunch of @ref Item which are saved under the same group + * in KHighscores config file. + */ +class ItemArray : public QMemArray<ItemContainer *> +{ + public: + ItemArray(); + virtual ~ItemArray(); + + virtual uint nbEntries() const = 0; + + const ItemContainer *item(const QString &name) const; + ItemContainer *item(const QString &name); + + void addItem(const QString &name, Item *, bool stored = true, + bool canHaveSubGroup = false); + void setItem(const QString &name, Item *); + int findIndex(const QString &name) const; + + void setGroup(const QString &group); + void setSubGroup(const QString &subGroup); + + void read(uint k, Score &data) const; + void write(uint k, const Score &data, uint maxNbLines) const; + + void exportToText(QTextStream &) const; + + private: + QString _group, _subGroup; + + void _setItem(uint i, const QString &name, Item *, bool stored, + bool canHaveSubGroup); + + ItemArray(const ItemArray &); + ItemArray &operator =(const ItemArray &); +}; + +//----------------------------------------------------------------------------- +class ScoreInfos : public ItemArray +{ + public: + ScoreInfos(uint maxNbEntries, const PlayerInfos &infos); + + uint nbEntries() const; + uint maxNbEntries() const { return _maxNbEntries; } + + private: + uint _maxNbEntries; +}; + +//----------------------------------------------------------------------------- +class ConfigGroup : public KConfigGroupSaver +{ + public: + ConfigGroup(const QString &group = QString::null) + : KConfigGroupSaver(kapp->config(), group) {} +}; + +//----------------------------------------------------------------------------- +class PlayerInfos : public ItemArray +{ + public: + PlayerInfos(); + + bool isNewPlayer() const { return _newPlayer; } + bool isOldLocalPlayer() const { return _oldLocalPlayer; } + uint nbEntries() const; + QString name() const { return item("name")->read(_id).toString(); } + bool isAnonymous() const; + QString prettyName() const { return prettyName(_id); } + QString prettyName(uint id) const { return item("name")->pretty(id); } + QString registeredName() const; + QString comment() const { return item("comment")->pretty(_id); } + bool isWWEnabled() const; + QString key() const; + uint id() const { return _id; } + uint oldLocalId() const { return _oldLocalId; } + + void createHistoItems(const QMemArray<uint> &scores, bool bound); + QString histoName(uint i) const; + uint histoSize() const; + const QMemArray<uint> &histogram() const { return _histogram; } + + void submitScore(const Score &) const; + // return true if the nickname is already used locally + bool isNameUsed(const QString &name) const; + void modifyName(const QString &newName) const; + void modifySettings(const QString &newName, const QString &comment, + bool WWEnabled, const QString &newKey) const; + void removeKey(); + + private: + bool _newPlayer, _bound, _oldLocalPlayer; + uint _id, _oldLocalId; + QMemArray<uint> _histogram; +}; + +//----------------------------------------------------------------------------- +class ManagerPrivate +{ + public: + ManagerPrivate(uint nbGameTypes, Manager &manager); + void init(uint maxNbentries); + ~ManagerPrivate(); + + bool modifySettings(const QString &newName, const QString &comment, + bool WWEnabled, QWidget *widget); + + void setGameType(uint type); + void checkFirst(); + int submitLocal(const Score &score); + int submitScore(const Score &score, QWidget *widget, bool askIfAnonymous); + Score readScore(uint i) const; + + uint gameType() const { return _gameType; } + uint nbGameTypes() const { return _nbGameTypes; } + bool isWWHSAvailable() const { return !serverURL.isEmpty(); } + ScoreInfos &scoreInfos() { return *_scoreInfos; } + PlayerInfos &playerInfos() { return *_playerInfos; } + KHighscore &hsConfig() { return *_hsConfig; } + enum QueryType { Submit, Register, Change, Players, Scores }; + KURL queryURL(QueryType type, const QString &newName=QString::null) const; + + void exportHighscores(QTextStream &); + + Manager &manager; + KURL serverURL; + QString version; + bool showStatistics, showDrawGames, trackLostGames, trackDrawGames; + Manager::ShowMode showMode; + + private: + KHighscore *_hsConfig; + PlayerInfos *_playerInfos; + ScoreInfos *_scoreInfos; + bool _first; + const uint _nbGameTypes; + uint _gameType; + + // return -1 if not a local best score + int rank(const Score &score) const; + + bool submitWorldWide(const Score &score, QWidget *parent) const; + static bool doQuery(const KURL &url, QWidget *parent, + QDomNamedNodeMap *map = 0); + static bool getFromQuery(const QDomNamedNodeMap &map, const QString &name, + QString &value, QWidget *parent); + void convertToGlobal(); +}; + +} // namespace + +#endif diff --git a/libkdegames/highscore/kexthighscore_item.cpp b/libkdegames/highscore/kexthighscore_item.cpp new file mode 100644 index 00000000..48556e02 --- /dev/null +++ b/libkdegames/highscore/kexthighscore_item.cpp @@ -0,0 +1,312 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2003 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 "kexthighscore_item.h" + +#include <qlayout.h> +#include <kglobal.h> +#include <kdialogbase.h> +#include <kdebug.h> + +#include "khighscore.h" +#include "kexthighscore_internal.h" +#include "kexthighscore_gui.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +Item::Item(const QVariant &def, const QString &label, int alignment) + : _default(def), _label(label), _alignment(alignment), + _format(NoFormat), _special(NoSpecial) +{} + +Item::~Item() +{} + +QVariant Item::read(uint, const QVariant &value) const +{ + return value; +} + +void Item::setPrettyFormat(Format format) +{ + bool buint = ( _default.type()==QVariant::UInt ); + bool bdouble = ( _default.type()==QVariant::Double ); + bool bnum = ( buint || bdouble || _default.type()==QVariant::Int ); + + switch (format) { + case OneDecimal: + case Percentage: + Q_ASSERT(bdouble); + break; + case MinuteTime: + Q_ASSERT(bnum); + break; + case DateTime: + Q_ASSERT( _default.type()==QVariant::DateTime ); + break; + case NoFormat: + break; + } + + _format = format; +} + +void Item::setPrettySpecial(Special special) +{ + bool buint = ( _default.type()==QVariant::UInt ); + bool bnum = ( buint || _default.type()==QVariant::Double + || _default.type()==QVariant::Int ); + + switch (special) { + case ZeroNotDefined: + Q_ASSERT(bnum); + break; + case NegativeNotDefined: + Q_ASSERT(bnum && !buint); + break; + case DefaultNotDefined: + break; + case Anonymous: + Q_ASSERT( _default.type()==QVariant::String ); + break; + case NoSpecial: + break; + } + + _special = special; +} + +QString Item::timeFormat(uint n) +{ + Q_ASSERT( n<=3600 && n!=0 ); + n = 3600 - n; + return QString::number(n / 60).rightJustify(2, '0') + ':' + + QString::number(n % 60).rightJustify(2, '0'); +} + +QString Item::pretty(uint, const QVariant &value) const +{ + switch (_special) { + case ZeroNotDefined: + if ( value.toUInt()==0 ) return "--"; + break; + case NegativeNotDefined: + if ( value.toInt()<0 ) return "--"; + break; + case DefaultNotDefined: + if ( value==_default ) return "--"; + break; + case Anonymous: + if ( value.toString()==ItemContainer::ANONYMOUS ) + return i18n(ItemContainer::ANONYMOUS_LABEL); + break; + case NoFormat: + break; + } + + switch (_format) { + case OneDecimal: + return QString::number(value.toDouble(), 'f', 1); + case Percentage: + return QString::number(value.toDouble(), 'f', 1) + "%"; + case MinuteTime: + return timeFormat(value.toUInt()); + case DateTime: + if ( value.toDateTime().isNull() ) return "--"; + return KGlobal::locale()->formatDateTime(value.toDateTime()); + case NoSpecial: + break; + } + + return value.toString(); +} + +//----------------------------------------------------------------------------- +Score::Score(ScoreType type) + : _type(type) +{ + const ItemArray &items = internal->scoreInfos(); + for (uint i=0; i<items.size(); i++) + _data[items[i]->name()] = items[i]->item()->defaultValue(); +} + +Score::~Score() +{} + +const QVariant &Score::data(const QString &name) const +{ + Q_ASSERT( _data.contains(name) ); + return _data[name]; +} + +void Score::setData(const QString &name, const QVariant &value) +{ + Q_ASSERT( _data.contains(name) ); + Q_ASSERT( _data[name].type()==value.type() ); + _data[name] = value; +} + +bool Score::isTheWorst() const +{ + Score s; + return score()==s.score(); +} + +bool Score::operator <(const Score &score) +{ + return internal->manager.isStrictlyLess(*this, score); +} + +QDataStream &operator <<(QDataStream &s, const Score &score) +{ + s << (Q_UINT8)score.type(); + s << score._data; + return s; +} + +QDataStream &operator >>(QDataStream &s, Score &score) +{ + Q_UINT8 type; + s >> type; + score._type = (ScoreType)type; + s >> score._data; + return s; +} + +//----------------------------------------------------------------------------- +MultiplayerScores::MultiplayerScores() +{} + +MultiplayerScores::~MultiplayerScores() +{} + +void MultiplayerScores::clear() +{ + Score score; + for (uint i=0; i<_scores.size(); i++) { + _nbGames[i] = 0; + QVariant name = _scores[i].data("name"); + _scores[i] = score; + _scores[i].setData("name", name); + _scores[i]._data["mean score"] = double(0); + _scores[i]._data["nb won games"] = uint(0); + } +} + +void MultiplayerScores::setPlayerCount(uint nb) +{ + _nbGames.resize(nb); + _scores.resize(nb); + clear(); +} + +void MultiplayerScores::setName(uint i, const QString &name) +{ + _scores[i].setData("name", name); +} + +void MultiplayerScores::addScore(uint i, const Score &score) +{ + QVariant name = _scores[i].data("name"); + double mean = _scores[i].data("mean score").toDouble(); + uint won = _scores[i].data("nb won games").toUInt(); + _scores[i] = score; + _scores[i].setData("name", name); + _nbGames[i]++; + mean += (double(score.score()) - mean) / _nbGames[i]; + _scores[i]._data["mean score"] = mean; + if ( score.type()==Won ) won++; + _scores[i]._data["nb won games"] = won; +} + +void MultiplayerScores::show(QWidget *parent) +{ + // check consistency + if ( _nbGames.size()<2 ) kdWarning(11002) << "less than 2 players" << endl; + else { + bool ok = true; + uint nb = _nbGames[0]; + for (uint i=1; i<_nbGames.size(); i++) + if ( _nbGames[i]!=nb ) ok = false; + if (!ok) + kdWarning(11002) << "players have not same number of games" << endl; + } + + // order the players according to the number of won games + QValueVector<Score> ordered; + for (uint i=0; i<_scores.size(); i++) { + uint won = _scores[i].data("nb won games").toUInt(); + double mean = _scores[i].data("mean score").toDouble(); + QValueVector<Score>::iterator it; + for(it = ordered.begin(); it!=ordered.end(); ++it) { + uint cwon = (*it).data("nb won games").toUInt(); + double cmean = (*it).data("mean score").toDouble(); + if ( won<cwon || (won==cwon && mean<cmean) ) { + ordered.insert(it, _scores[i]); + break; + } + } + if ( it==ordered.end() ) ordered.push_back(_scores[i]); + } + + // show the scores + KDialogBase dialog(KDialogBase::Plain, i18n("Multiplayers Scores"), + KDialogBase::Close, KDialogBase::Close, + parent, "show_multiplayers_score", true, true); + QHBoxLayout *hbox = new QHBoxLayout(dialog.plainPage(), + KDialog::marginHint(), KDialog::spacingHint()); + + QVBox *vbox = new QVBox(dialog.plainPage()); + hbox->addWidget(vbox); + if ( _nbGames[0]==0 ) (void)new QLabel(i18n("No game played."), vbox); + else { + (void)new QLabel(i18n("Scores for last game:"), vbox); + (void)new LastMultipleScoresList(ordered, vbox); + } + + if ( _nbGames[0]>1 ) { + vbox = new QVBox(dialog.plainPage()); + hbox->addWidget(vbox); + (void)new QLabel(i18n("Scores for the last %1 games:") + .arg(_nbGames[0]), vbox); + (void)new TotalMultipleScoresList(ordered, vbox); + } + + dialog.enableButtonSeparator(false); + dialog.exec(); +} + +QDataStream &operator <<(QDataStream &s, const MultiplayerScores &score) +{ + s << score._scores; + s << score._nbGames; + return s; +} + +QDataStream &operator >>(QDataStream &s, MultiplayerScores &score) +{ + s >> score._scores; + s >> score._nbGames; + return s; +} + +} // namespace diff --git a/libkdegames/highscore/kexthighscore_item.h b/libkdegames/highscore/kexthighscore_item.h new file mode 100644 index 00000000..0200fabd --- /dev/null +++ b/libkdegames/highscore/kexthighscore_item.h @@ -0,0 +1,317 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2003 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 KEXTHIGHSCORE_ITEM_H +#define KEXTHIGHSCORE_ITEM_H + +#include <qvariant.h> +#include <qnamespace.h> +#include <qmap.h> +#include <qvaluevector.h> +#include <kdemacros.h> +class QWidget; + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +/** + * This class defines how to convert and how to display + * a highscore element (such as the score, the date, ...) or a player + * info (such as the player name, the best score, ...). + */ +class KDE_EXPORT Item +{ + public: + /** + * Possible display format. + * <ul> + * <li> @p NoFormat : no formatting (default) </li> + * <li> @p OneDecimal : with one decimal (only for Double) </li> + * <li> @p Percentage : with one decimal + % (only for Double) </li> + * <li> @p MinuteTime : MM:SS ie 3600 is 00:00, 1 is 59:59 and 0 is + * undefined (only for UInt, Int and Double) </li> + * <li> @p DateTime : date and time according to locale (only for + * DateTime) </li> + * </ul> + */ + enum Format { NoFormat, OneDecimal, Percentage, MinuteTime, + DateTime }; + + /** + * Possible special value for display format. + * <ul> + * <li> @p NoSpecial : no special value ; a null DateTime is replaced by + * "--" (default) </li> + * <li> ZeroNotDefined : 0 is replaced by "--" (only for UInt, Int and + * Double) </li> + * <li> @p NegativeNotDefined : negative values are replaced by "--" (only + * for Int and Double) </li> + * <li> @p DefaultNotDefined : default value is replaced by "--" </li> + * <li> @p Anonymous : replace the special value ItemBase::ANONYMOUS + * by i18n("anonymous") (only for String) </li> + * </ul> + */ + enum Special { NoSpecial, ZeroNotDefined, NegativeNotDefined, + DefaultNotDefined, Anonymous }; + + /** + * Constructor. + * + * @param def default value ; the QVariant also gives the type of data. + * Be sure to cast the value to the required type (for e.g. with uint). + * @param label the label corresponding to the item. If empty, the item + * is not shown. + * @param alignment the alignment of the item. + */ + Item(const QVariant &def = QVariant::Invalid, + const QString &label = QString::null, int alignment = Qt::AlignRight); + + virtual ~Item(); + + /** + * Set the display format. + * @see Format + */ + void setPrettyFormat(Format format); + + /** + * Set the special value for display. + * @see Special + */ + void setPrettySpecial(Special special); + + /** + * @return if the item is shown. + */ + bool isVisible() const { return !_label.isEmpty(); } + + /** + * Set the label. + */ + void setLabel(const QString &label) { _label = label; } + + /** + * @return the label. + */ + QString label() const { return _label; } + + /** + * @return the alignment. + */ + int alignment() const { return _alignment; } + + /** + * Set default value. + */ + void setDefaultValue(const QVariant &value) { _default = value; } + + /** + * @return the default value. + */ + const QVariant &defaultValue() const { return _default; } + + /** + * @return the converted value (by default the value is left + * unchanged). Most of the time you don't need to reimplement this method. + * + * @param i the element index ("rank" for score / "id" for player) + * @param value the value to convert + */ + virtual QVariant read(uint i, const QVariant &value) const; + + /** + * @return the string to be displayed. You may need to reimplement this + * method for special formatting (different from the standard ones). + * + * @param i the element index ("rank" for score / "id" for player) + * @param value the value to convert + */ + virtual QString pretty(uint i, const QVariant &value) const; + + private: + QVariant _default; + QString _label; + int _alignment; + Format _format; + Special _special; + + class ItemPrivate; + ItemPrivate *d; + + static QString timeFormat(uint); +}; + +//----------------------------------------------------------------------------- +/** + * Possible score type. + * @p Won the game has been won. + * @p Lost the game has been lost or has been aborted. + * @p Draw the game is a draw. + */ +enum ScoreType { Won = 0, Lost = -1, Draw = -2 }; + +/** + * This class contains data for a score. You should not inherit from + * this class but reimplement the methods in Highscores. + */ +class KDE_EXPORT Score +{ + public: + Score(ScoreType type = Won); + + ~Score(); + + /** + * @return the game type. + */ + ScoreType type() const { return _type; } + + /** + * Set the game type. + */ + void setType(ScoreType type) { _type = type; } + + /** + * @return the data associated with the named Item. + */ + const QVariant &data(const QString &name) const; + + /** + * Set the data associated with the named Item. Note that the + * value should have the type of the default value of the + * Item. + */ + void setData(const QString &name, const QVariant &value); + + /** + * @return the score value. + * + * Equivalent to <pre>data("score").toUInt()</pre>. + */ + uint score() const { return data("score").toUInt(); } + + /** + * Set the score value. + * + * Equivalent to <pre>setData("score", score)</pre>. + */ + void setScore(uint score) { setData("score", score); } + + /** + * @return true if this is the worst possible score (ie the default + * argument of ScoreItem). + */ + bool isTheWorst() const; + + /** + * Comparison operator. + * + * @see Manager::isStrictlyLess + */ + bool operator <(const Score &score); + + private: + ScoreType _type; + QMap<QString, QVariant> _data; + + class ScorePrivate; + ScorePrivate *d; + + friend class MultiplayerScores; + + friend QDataStream &operator <<(QDataStream &stream, const Score &score); + friend QDataStream &operator >>(QDataStream &stream, Score &score); +}; + +KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const Score &score); +KDE_EXPORT QDataStream &operator >>(QDataStream &stream, Score &score); + +/** + * This class is used to store and show scores for multiplayer games. + * + * Example of use: + * Initialize the class: + * <pre> + * KExtHighscore::MultiScore ms(2); + * ms.setPlayerName(0, "player 1"); + * ms.setPlayerName(1, "player 2"); + * </pre> + * At the end of each game, add the score of each players: + * <pre> + * KExtHighscore::Score score(KExtHighscore::Won); + * score.setScore(100); + * ms.addScore(0, score); + * score.setType(KExtHighscore::Lost); + * score.setScore(20); + * ms.addScore(1, score); + * </pre> + */ +class KDE_EXPORT MultiplayerScores +{ + public: + MultiplayerScores(); + + ~MultiplayerScores(); + + /** + * Set the number of players and clear the scores. + */ + void setPlayerCount(uint nb); + + /** + * Set the name of player. + */ + void setName(uint player, const QString &name); + + /** + * Add the score of player. + */ + void addScore(uint player, const Score &score); + + /** + * Clear all scores. + */ + void clear(); + + /** + * Show scores. + */ + void show(QWidget *parent); + + private: + QValueVector<uint> _nbGames; + QValueVector<Score> _scores; + + class MultiplayerScoresPrivate; + MultiplayerScoresPrivate *d; + + friend QDataStream &operator <<(QDataStream &stream, + const MultiplayerScores &score); + friend QDataStream &operator >>(QDataStream &stream, + MultiplayerScores &score); +}; + +KDE_EXPORT QDataStream &operator <<(QDataStream &stream, const MultiplayerScores &score); +KDE_EXPORT QDataStream &operator >>(QDataStream &stream, MultiplayerScores &score); + +} // namespace + +#endif diff --git a/libkdegames/highscore/kexthighscore_tab.cpp b/libkdegames/highscore/kexthighscore_tab.cpp new file mode 100644 index 00000000..3e9cbe8a --- /dev/null +++ b/libkdegames/highscore/kexthighscore_tab.cpp @@ -0,0 +1,281 @@ +/* + This file is part of the KDE games library + Copyright (C) 2002 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 "kexthighscore_tab.h" +#include "kexthighscore_tab.moc" + +#include <qlayout.h> +#include <qlabel.h> +#include <qvgroupbox.h> +#include <qgrid.h> +#include <qheader.h> + +#include <kdialogbase.h> +#include <klistview.h> +#include <kdebug.h> +#include <kglobal.h> + +#include "kexthighscore.h" +#include "kexthighscore_internal.h" + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +PlayersCombo::PlayersCombo(QWidget *parent, const char *name) + : QComboBox(parent, name) +{ + const PlayerInfos &p = internal->playerInfos(); + for (uint i = 0; i<p.nbEntries(); i++) + insertItem(p.prettyName(i)); + insertItem(QString("<") + i18n("all") + '>'); + connect(this, SIGNAL(activated(int)), SLOT(activatedSlot(int))); +} + +void PlayersCombo::activatedSlot(int i) +{ + const PlayerInfos &p = internal->playerInfos(); + if ( i==(int)p.nbEntries() ) emit allSelected(); + else if ( i==(int)p.nbEntries()+1 ) emit noneSelected(); + else emit playerSelected(i); +} + +void PlayersCombo::load() +{ + const PlayerInfos &p = internal->playerInfos(); + for (uint i = 0; i<p.nbEntries(); i++) + changeItem(p.prettyName(i), i); +} + +//----------------------------------------------------------------------------- +AdditionalTab::AdditionalTab(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + QVBoxLayout *top = new QVBoxLayout(this, KDialogBase::marginHint(), + KDialogBase::spacingHint()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QLabel *label = new QLabel(i18n("Select player:"), this); + hbox->addWidget(label); + _combo = new PlayersCombo(this); + connect(_combo, SIGNAL(playerSelected(uint)), + SLOT(playerSelected(uint))); + connect(_combo, SIGNAL(allSelected()), SLOT(allSelected())); + hbox->addWidget(_combo); + hbox->addStretch(1); +} + +void AdditionalTab::init() +{ + uint id = internal->playerInfos().id(); + _combo->setCurrentItem(id); + playerSelected(id); +} + +void AdditionalTab::allSelected() +{ + display(internal->playerInfos().nbEntries()); +} + +QString AdditionalTab::percent(uint n, uint total, bool withBraces) +{ + if ( n==0 || total==0 ) return QString::null; + QString s = QString("%1%").arg(100.0 * n / total, 0, 'f', 1); + return (withBraces ? QString("(") + s + ")" : s); +} + +void AdditionalTab::load() +{ + _combo->load(); +} + + +//----------------------------------------------------------------------------- +const char *StatisticsTab::COUNT_LABELS[Nb_Counts] = { + I18N_NOOP("Total:"), I18N_NOOP("Won:"), I18N_NOOP("Lost:"), + I18N_NOOP("Draw:") +}; +const char *StatisticsTab::TREND_LABELS[Nb_Trends] = { + I18N_NOOP("Current:"), I18N_NOOP("Max won:"), I18N_NOOP("Max lost:") +}; + +StatisticsTab::StatisticsTab(QWidget *parent) + : AdditionalTab(parent, "statistics_tab") +{ + // construct GUI + QVBoxLayout *top = static_cast<QVBoxLayout *>(layout()); + + QHBoxLayout *hbox = new QHBoxLayout(top); + QVBoxLayout *vbox = new QVBoxLayout(hbox); + QVGroupBox *group = new QVGroupBox(i18n("Game Counts"), this); + vbox->addWidget(group); + QGrid *grid = new QGrid(3, group); + grid->setSpacing(KDialogBase::spacingHint()); + for (uint k=0; k<Nb_Counts; k++) { + if ( Count(k)==Draw && !internal->showDrawGames ) continue; + (void)new QLabel(i18n(COUNT_LABELS[k]), grid); + _nbs[k] = new QLabel(grid); + _percents[k] = new QLabel(grid); + } + + group = new QVGroupBox(i18n("Trends"), this); + vbox->addWidget(group); + grid = new QGrid(2, group); + grid->setSpacing(KDialogBase::spacingHint()); + for (uint k=0; k<Nb_Trends; k++) { + (void)new QLabel(i18n(TREND_LABELS[k]), grid); + _trends[k] = new QLabel(grid); + } + + hbox->addStretch(1); + top->addStretch(1); +} + +void StatisticsTab::load() +{ + AdditionalTab::load(); + const PlayerInfos &pi = internal->playerInfos(); + uint nb = pi.nbEntries(); + _data.resize(nb+1); + for (uint i=0; i<_data.size()-1; i++) { + _data[i].count[Total] = pi.item("nb games")->read(i).toUInt(); + _data[i].count[Lost] = pi.item("nb lost games")->read(i).toUInt() + + pi.item("nb black marks")->read(i).toUInt(); // legacy + _data[i].count[Draw] = pi.item("nb draw games")->read(i).toUInt(); + _data[i].count[Won] = _data[i].count[Total] - _data[i].count[Lost] + - _data[i].count[Draw]; + _data[i].trend[CurrentTrend] = + pi.item("current trend")->read(i).toInt(); + _data[i].trend[WonTrend] = pi.item("max won trend")->read(i).toUInt(); + _data[i].trend[LostTrend] = + -(int)pi.item("max lost trend")->read(i).toUInt(); + } + + for (uint k=0; k<Nb_Counts; k++) _data[nb].count[k] = 0; + for (uint k=0; k<Nb_Trends; k++) _data[nb].trend[k] = 0; + for (uint i=0; i<_data.size()-1; i++) { + for (uint k=0; k<Nb_Counts; k++) + _data[nb].count[k] += _data[i].count[k]; + for (uint k=0; k<Nb_Trends; k++) + _data[nb].trend[k] += _data[i].trend[k]; + } + for (uint k=0; k<Nb_Trends; k++) + _data[nb].trend[k] /= (_data.size()-1); + + init(); +} + +QString StatisticsTab::percent(const Data &d, Count count) const +{ + if ( count==Total ) return QString::null; + return AdditionalTab::percent(d.count[count], d.count[Total], true); +} + +void StatisticsTab::display(uint i) +{ + const Data &d = _data[i]; + for (uint k=0; k<Nb_Counts; k++) { + if ( Count(k) && !internal->showDrawGames ) continue; + _nbs[k]->setText(QString::number(d.count[k])); + _percents[k]->setText(percent(d, Count(k))); + } + for (uint k=0; k<Nb_Trends; k++) { + QString s; + if ( d.trend[k]>0 ) s = '+'; + int prec = (i==internal->playerInfos().nbEntries() ? 1 : 0); + _trends[k]->setText(s + QString::number(d.trend[k], 'f', prec)); + } +} + +//----------------------------------------------------------------------------- +HistogramTab::HistogramTab(QWidget *parent) + : AdditionalTab(parent, "histogram_tab") +{ + // construct GUI + QVBoxLayout *top = static_cast<QVBoxLayout *>(layout()); + + _list = new KListView(this); + _list->setSelectionMode(QListView::NoSelection); + _list->setItemMargin(3); + _list->setAllColumnsShowFocus(true); + _list->setSorting(-1); + _list->header()->setClickEnabled(false); + _list->header()->setMovingEnabled(false); + top->addWidget(_list); + + _list->addColumn(i18n("From")); + _list->addColumn(i18n("To")); + _list->addColumn(i18n("Count")); + _list->addColumn(i18n("Percent")); + for (uint i=0; i<4; i++) _list->setColumnAlignment(i, AlignRight); + _list->addColumn(QString::null); + + const Item *sitem = internal->scoreInfos().item("score")->item(); + const PlayerInfos &pi = internal->playerInfos(); + const QMemArray<uint> &sh = pi.histogram(); + for (uint k=1; k<pi.histoSize(); k++) { + QString s1 = sitem->pretty(0, sh[k-1]); + QString s2; + if ( k==sh.size() ) s2 = "..."; + else if ( sh[k]!=sh[k-1]+1 ) s2 = sitem->pretty(0, sh[k]); + (void)new KListViewItem(_list, s1, s2); + } +} + +void HistogramTab::load() +{ + AdditionalTab::load(); + const PlayerInfos &pi = internal->playerInfos(); + uint n = pi.nbEntries(); + uint s = pi.histoSize() - 1; + _counts.resize((n+1) * s); + _data.fill(0, n+1); + for (uint k=0; k<s; k++) { + _counts[n*s + k] = 0; + for (uint i=0; i<n; i++) { + uint nb = pi.item(pi.histoName(k+1))->read(i).toUInt(); + _counts[i*s + k] = nb; + _counts[n*s + k] += nb; + _data[i] += nb; + _data[n] += nb; + } + } + + init(); +} + +void HistogramTab::display(uint i) +{ + const PlayerInfos &pi = internal->playerInfos(); + QListViewItem *item = _list->firstChild(); + uint s = pi.histoSize() - 1; + for (int k=s-1; k>=0; k--) { + uint nb = _counts[i*s + k]; + item->setText(2, QString::number(nb)); + item->setText(3, percent(nb, _data[i])); + uint width = (_data[i]==0 ? 0 : qRound(150.0 * nb / _data[i])); + QPixmap pixmap(width, 10); + pixmap.fill(blue); + item->setPixmap(4, pixmap); + item = item->nextSibling(); + } +} + +} // namespace diff --git a/libkdegames/highscore/kexthighscore_tab.h b/libkdegames/highscore/kexthighscore_tab.h new file mode 100644 index 00000000..ce47a75f --- /dev/null +++ b/libkdegames/highscore/kexthighscore_tab.h @@ -0,0 +1,117 @@ +/* + This file is part of the KDE games library + Copyright (C) 2002 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 KEXTHIGHSCORE_TAB_H +#define KEXTHIGHSCORE_TAB_H + +#include <qcombobox.h> +#include <qmemarray.h> + +class QLabel; +class KListView; + + +namespace KExtHighscore +{ + +//----------------------------------------------------------------------------- +class PlayersCombo : public QComboBox +{ + Q_OBJECT + public: + PlayersCombo(QWidget *parent = 0, const char *name = 0); + + void load(); + + signals: + void playerSelected(uint i); + void allSelected(); + void noneSelected(); + + private slots: + void activatedSlot(int i); +}; + +//----------------------------------------------------------------------------- +class AdditionalTab : public QWidget +{ + Q_OBJECT + public: + AdditionalTab(QWidget *parent, const char *name); + + virtual void load(); + + private slots: + void playerSelected(uint i) { display(i) ; } + void allSelected(); + + protected: + void init(); + static QString percent(uint n, uint total, bool withBraces = false); + virtual void display(uint i) = 0; + + private: + PlayersCombo *_combo; +}; + +//----------------------------------------------------------------------------- +class StatisticsTab : public AdditionalTab +{ + Q_OBJECT + public: + StatisticsTab(QWidget *parent); + + void load(); + + private: + enum Count { Total = 0, Won, Lost, Draw, Nb_Counts }; + static const char *COUNT_LABELS[Nb_Counts]; + enum Trend { CurrentTrend = 0, WonTrend, LostTrend, Nb_Trends }; + static const char *TREND_LABELS[Nb_Trends]; + struct Data { + uint count[Nb_Counts]; + double trend[Nb_Trends]; + }; + QMemArray<Data> _data; + QLabel *_nbs[Nb_Counts], *_percents[Nb_Counts], *_trends[Nb_Trends]; + + QString percent(const Data &, Count) const; + void display(uint i); +}; + +//----------------------------------------------------------------------------- +class HistogramTab : public AdditionalTab +{ + Q_OBJECT + public: + HistogramTab(QWidget *parent); + + void load(); + + private: + QMemArray<uint> _counts; + QMemArray<uint> _data; + KListView *_list; + + void display(uint i); +}; + +} // namespace + +#endif diff --git a/libkdegames/highscore/kfilelock.cpp b/libkdegames/highscore/kfilelock.cpp new file mode 100644 index 00000000..7dc83b96 --- /dev/null +++ b/libkdegames/highscore/kfilelock.cpp @@ -0,0 +1,88 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 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 "kfilelock.h" + +#include <unistd.h> +#include <sys/file.h> +#include <errno.h> + +#include <kdebug.h> + + +KFileLock::KFileLock(int fd) + : _fd(fd), _locked(false) +{} + +int KFileLock::lock() +{ + kdDebug(11002) << "lock fd=" << _fd << endl; +#ifdef F_SETLK +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif + struct flock lock_data; + lock_data.l_type = F_WRLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + if ( fcntl(_fd, F_SETLK, &lock_data)==-1 ) { + if ( errno==EAGAIN ) return -2; + return -1; + } +#else +# ifdef LOCK_EX + if ( flock (_fd, LOCK_EX|LOCK_NB)==-1 ) { + if ( errno==EWOULDBLOCK ) return -2; + return -1; + } +# else + if ( lockf(_fd, F_TLOCK, 0)==-1 ) { + if ( errno==EACCES ) return -2; + return -1; + } +# endif +#endif + _locked = true; + return 0; +} + +KFileLock::~KFileLock() +{ + unlock(); +} + +void KFileLock::unlock() +{ + if ( !_locked ) return; + kdDebug(11002) << "unlock" << endl; +# ifdef F_SETLK + struct flock lock_data; + lock_data.l_type = F_UNLCK; + lock_data.l_whence = SEEK_SET; + lock_data.l_start = lock_data.l_len = 0; + (void)fcntl(_fd, F_SETLK, &lock_data); +# else +# ifdef F_ULOCK + lockf(_fd, F_ULOCK, 0); +# else + flock(_fd, LOCK_UN); +# endif +# endif + _locked = false; +} diff --git a/libkdegames/highscore/kfilelock.h b/libkdegames/highscore/kfilelock.h new file mode 100644 index 00000000..2e1841ba --- /dev/null +++ b/libkdegames/highscore/kfilelock.h @@ -0,0 +1,53 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 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 KFILELOCK_H +#define KFILELOCK_H + + +class KFileLock +{ +public: + KFileLock(int fd); + + /** Call unlock(). */ + ~KFileLock(); + + /** @return the file descriptor. */ + int fd() const { return _fd; } + + /* + * Lock the file. + * @return 0 on success, -1 on failure (no permission) and -2 if another + * process is currently locking the file. + */ + int lock(); + + /** Unlock the file. */ + void unlock(); + + /** @return true if we currently lock the file. */ + bool isLocked() const { return _locked; } + +private: + int _fd; + bool _locked; +}; + + +#endif diff --git a/libkdegames/highscore/khighscore.cpp b/libkdegames/highscore/khighscore.cpp new file mode 100644 index 00000000..4e1f68e5 --- /dev/null +++ b/libkdegames/highscore/khighscore.cpp @@ -0,0 +1,262 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 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. +*/ +/* + $Id$ +*/ + +#include <config.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/file.h> + +#include <kapplication.h> +#include <ksimpleconfig.h> +#include <kglobal.h> +#include <kstdguiitem.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kdebug.h> +#include <kstaticdeleter.h> + +#include "khighscore.h" +#include "kconfigrawbackend.h" +#include "kfilelock.h" + +#define GROUP "KHighscore" + +class KHighscorePrivate +{ +public: + KHighscorePrivate() {} + + QString group; + bool global; +}; + +KFileLock *KHighscore::_lock = 0; +KRawConfig *KHighscore::_config = 0; +static KStaticDeleter<KFileLock> lockSD; +static KStaticDeleter<KRawConfig> configSD; + + +KHighscore::KHighscore(QObject* parent) + : QObject(parent) +{ + init(true); +} + +KHighscore::KHighscore(bool forceLocal, QObject* parent) + : QObject(parent) +{ + init(forceLocal); +} + +void KHighscore::init(bool forceLocal) +{ + d = new KHighscorePrivate; +#ifdef HIGHSCORE_DIRECTORY + d->global = !forceLocal; + if ( d->global && _lock==0 ) + kdFatal(11002) << "KHighscore::init should be called before!!" << endl; +#else + d->global = false; + Q_UNUSED(forceLocal); +#endif + readCurrentConfig(); +} + +bool KHighscore::isLocked() const +{ + return (d->global ? _lock->isLocked() : true); +} + +void KHighscore::readCurrentConfig() +{ + if ( d->global ) _config->reparseConfiguration(); +} + +void KHighscore::init(const char *appname) +{ +#ifdef HIGHSCORE_DIRECTORY + const QString filename = QString::fromLocal8Bit("%1/%2.scores") + .arg(HIGHSCORE_DIRECTORY).arg(appname); + int fd = open(filename.local8Bit(), O_RDWR); + if ( fd<0 ) kdFatal(11002) << "cannot open global highscore file \"" + << filename << "\"" << endl; + lockSD.setObject(_lock, new KFileLock(fd)); + configSD.setObject(_config, new KRawConfig(fd, true)); // read-only + + // drop the effective gid + int gid = getgid(); + setregid(gid, gid); +#else + Q_UNUSED(appname); +#endif +} + +bool KHighscore::lockForWriting(QWidget *widget) +{ + if ( isLocked() ) return true; + + bool first = true; + for (;;) { + kdDebug(11002) << "try locking" << endl; + // lock the highscore file (it should exist) + int result = _lock->lock(); + bool ok = ( result==0 ); + kdDebug(11002) << "locking system-wide highscore file res=" + << result << " (ok=" << ok << ")" << endl; + if (ok) { + readCurrentConfig(); + _config->setReadOnly(false); + return true; + } + + if ( !first ) { + KGuiItem item = KStdGuiItem::cont(); + item.setText(i18n("Retry")); + int res = KMessageBox::warningContinueCancel(widget, i18n("Cannot access the highscore file. Another user is probably currently writing to it."), QString::null, item, "ask_lock_global_highscore_file"); + if ( res==KMessageBox::Cancel ) break; + } else sleep(1); + first = false; + } + return false; +} + +void KHighscore::writeAndUnlock() +{ + if ( !d->global ) { + kapp->config()->sync(); + return; + } + if ( !isLocked() ) return; + + kdDebug(11002) << "unlocking" << endl; + _config->sync(); // write config + _lock->unlock(); + _config->setReadOnly(true); +} + +KHighscore::~KHighscore() +{ + writeAndUnlock(); + delete d; +} + +KConfig* KHighscore::config() const +{ + return (d->global ? _config : kapp->config()); +} + +void KHighscore::writeEntry(int entry, const QString& key, const QVariant& value) +{ + Q_ASSERT( isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +void KHighscore::writeEntry(int entry, const QString& key, int value) +{ + Q_ASSERT( isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +void KHighscore::writeEntry(int entry, const QString& key, const QString &value) +{ + Q_ASSERT (isLocked() ); + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + cg.config()->writeEntry(confKey, value); +} + +QVariant KHighscore::readPropertyEntry(int entry, const QString& key, const QVariant& pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readPropertyEntry(confKey, pDefault); +} + +QString KHighscore::readEntry(int entry, const QString& key, const QString& pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readEntry(confKey, pDefault); +} + +int KHighscore::readNumEntry(int entry, const QString& key, int pDefault) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->readNumEntry(confKey, pDefault); +} + +bool KHighscore::hasEntry(int entry, const QString& key) const +{ + KConfigGroupSaver cg(config(), group()); + QString confKey = QString("%1_%2").arg(entry).arg(key); + return cg.config()->hasKey(confKey); +} + +QStringList KHighscore::readList(const QString& key, int lastEntry) const +{ + QStringList list; + for (int i = 1; hasEntry(i, key) && ((lastEntry > 0) ? (i <= lastEntry) : true); i++) { + list.append(readEntry(i, key)); + } + return list; +} + +void KHighscore::writeList(const QString& key, const QStringList& list) +{ + for (int unsigned i = 1; i <= list.count(); i++) { + writeEntry(i, key, list[i - 1]); + } +} + +void KHighscore::setHighscoreGroup(const QString& group) +{ + d->group = group; +} + +const QString& KHighscore::highscoreGroup() const +{ + return d->group; +} + +QString KHighscore::group() const +{ + if ( highscoreGroup().isNull() ) + return (d->global ? QString::null : GROUP); + return (d->global ? highscoreGroup() + : QString("%1_%2").arg(GROUP).arg(highscoreGroup())); +} + +bool KHighscore::hasTable() const +{ return config()->hasGroup(group()); } + +void KHighscore::sync() +{ + writeAndUnlock(); +} + +#include "khighscore.moc" diff --git a/libkdegames/highscore/khighscore.h b/libkdegames/highscore/khighscore.h new file mode 100644 index 00000000..b1e3d25f --- /dev/null +++ b/libkdegames/highscore/khighscore.h @@ -0,0 +1,311 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 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. +*/ +/* + $Id$ +*/ +#ifndef __KHIGHSCORE_H__ +#define __KHIGHSCORE_H__ + +#include <qstring.h> +#include <qobject.h> +#include <kdemacros.h> +class KConfig; +class KFileLock; +class KRawConfig; +class KHighscorePrivate; + +/** + * @short Class for managing highscore tables + * + * This is the KDE class for saving and reading highscore tables. It offers the + * possibility for system-wide highscore tables (configure with e.g. + * --enable-highscore-dir=/var/games) and a theoretically unlimited number of + * entries. + * + * You can specify different "keys" for an entry - just like the KConfig + * keys. But it will be prefixed with the number of the entry. For example you + * will probably use something like this to save the name of the player on the + * top of the list (ie the winner): + * \code + * highscore->writeEntry(1, "name", myPlayer->name()); + * \endcode + * Note that it doesn't really matter if you use "0" or "1" as the first entry + * of the list as long as your program always uses the same for the first + * entry. I recommend to use "1", as several convenience methods use this. + * + * You can also specify different groups using setHighscoreGroup. Just + * like the keys mentioned above the groups behave like groups in KConfig + * but are prefixed with "KHighscore_". The default group is just "KHighscore". + * You might use this e.g. to create different highscore tables like + * \code + * table->setHighscoreGroup("Easy"); + * // write the highscores for level "easy" to the table + * writeEasyHighscores(table); + * + * table->setHighscore("Player_1"); + * // write player specific highscores to the table + * writePlayerHighscores(table); + * \endcode + * As you can see above you can also use this to write the highscores of a + * single player, so the "best times" of a player. To write highscores for a + * specific player in a specific level you will have to use a more complex way: + * \code + * QString group = QString("%1_%2").arg(player).arg(level); + * table->setGroup(group); + * writeHighscore(table, player, level); + * \endcode + * + * Also note that you MUST NOT mark the key or the group for translation! I.e. + * don't use i18n() for the keys or groups! Here is the code to read the above + * written entry: + * \code + * QString firstName = highscore->readEntry(0, "name"); + * \endcode + * Easy, what? + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KHighscore : public QObject +{ + Q_OBJECT +public: + /** @obsolete + * Constructor. The highscore file is forced to be local to support + * games using the old behaviour. + */ + KHighscore(QObject* parent = 0); + + /** + * Constructor. + * + * @param forceLocal if true, the local highscore file is used even + * when the configuration has been set to use a system-wide file. This + * is convenient for converting highscores from legacy applications. + * @param parent parent widget for this widget + * @since 3.2 + */ + KHighscore(bool forceLocal, QObject *parent); + + /** + * Read the current state of the highscore file. Remember that when + * it's not locked for writing, this file can change at any time. + * (This method is only useful for a system-wide highscore file). + * @since 3.2 + */ + void readCurrentConfig(); + + /** @since 3.2 + * This method open the system-wide highscore file using the effective + * group id of the game executable (which should be "games"). The + * effective group id is completely dropped afterwards. + * + * Note: this method should be called in main() before creating a + * KApplication and doing anything else (KApplication checks that the + * program is not suid/sgid and will exit the program for security + * reason if it is the case). + */ + static void init(const char *appname); + + /** @since 3.2 + * Lock the system-wide highscore file for writing (does nothing and + * return true if the local file is used). + * You should perform writing without GUI interaction to avoid + * blocking and don't forget to unlock the file as soon as possible + * with writeAndUnlock(). + * + * If the config file cannot be locked, + * the method waits for 1 second and, if it failed again, displays + * a message box asking for retry or cancel. + * @param widget used as the parent of the message box. + * + * @return false on error or if the config file is locked by another + * process. In such case, the config stays read-only. + */ + bool lockForWriting(QWidget *widget = 0); + + /** + * Effectively write and unlock the system-wide highscore file + * (@see lockForWriting). + * If using a local highscore file, it will sync the config. + * @since 3.2 + */ + void writeAndUnlock(); + + /** + * @return true if the highscore file is locked or if a local + * file is used. + * @since 3.2 + */ + bool isLocked() const; + + /** + * Destructor. + * If necessary, write and unlock the highscore file. + */ + ~KHighscore(); + + /** + * @param entry The number of the entry / the placing of the player + * @param key A key for this entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param value The value of this entry + **/ + void writeEntry(int entry, const QString& key, const QString& value); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + void writeEntry(int entry, const QString& key, int value); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + * See KConfigBase documentation for allowed QVariant::Type. + **/ + void writeEntry(int entry, const QString& key, const QVariant &value); + + /** + * Reads an entry from the highscore table. + * @param entry The number of the entry / the placing to be read + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param pDefault This will be used as default value if the key+pair + * entry can't be found. + * @return The value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist + **/ + QString readEntry(int entry, const QString& key, const QString& pDefault = QString::null) const; + + /** + * Read a numeric value. + * @param entry The number of the entry / the placing to be read + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param pDefault This will be used as default value if the key+pair + * entry can't be found. + * @return The value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist + **/ + int readNumEntry(int entry, const QString& key, int pDefault = -1) const; + + /** + * Read a QVariant entry. + * See KConfigBase documentation for allowed QVariant::Type. + * + * @return the value of this entry+key pair or pDefault if the entry+key + * pair doesn't exist or + */ + QVariant readPropertyEntry(int entry, const QString &key, const QVariant &pDefault) const; + + /** + * @return True if the highscore table conatins the entry/key pair, + * otherwise false + **/ + bool hasEntry(int entry, const QString& key) const; + + /** + * Reads a list of entries from the highscore table starting at 1 until + * lastEntry. If an entry between those numbers doesn't exist the + * function aborts reading even if after the missing entry is an + * existing one. The first entry of the list is the first placing, the + * last on is the last placing. + * @return A list of the entries of this key. You could also call + * readEntry(i, key) where i is from 1 to 20. Note that this function + * depends on "1" as the first entry! + * @param key The key of the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param lastEntry the last entry which will be includes into the list. + * 1 will include a list with maximal 1 entry - 20 a list with maximal + * 20 entries. If lastEntry is <= 0 then rading is only stopped when when an + * entry does not exist. + **/ + QStringList readList(const QString& key, int lastEntry = 20) const; + + /** + * Writes a list of entries to the highscore table. + * + * The first entry is prefixed with "1". Using this method is a short + * way of calling writeEntry(i, key, list[i]) from i = 1 to + * list.count() + * @param key A key for the entry. E.g. "name" for the name of the + * player. Nearly the same as the usual keys in KConfig - but they + * are prefixed with the entry number + * @param list The list of values + **/ + void writeList(const QString& key, const QStringList& list); + + /** + * @return Whether a highscore table exists. You can use this + * function to indicate whether KHighscore created a highscore table + * before and - if not - read your old (non-KHighscore) table instead. + * This way you can safely read an old table and save it using + * KHighscore without losing any data + **/ + bool hasTable() const; + + /** @obsolete + * This does the same as writeAndUnlock(). + */ + void sync(); + + /** + * Set the new highscore group. The group is being prefixed with + * "KHighscore_" in the table. + * @param groupname The new groupname. E.g. use "easy" for the easy + * level of your game. If you use QString::null (the default) the + * default group is used. + **/ + void setHighscoreGroup(const QString& groupname = QString::null); + + /** + * @return The currently used group. This doesn't contain the prefix + * ("KHighscore_") but the same as setHighscoreGroup uses. The + * default is QString::null + **/ + const QString& highscoreGroup() const; + +protected: + /** + * @return A groupname to be used in KConfig. Used internally to + * prefix the value from highscoreGroup() with "KHighscore_" + **/ + QString group() const; + + /** + * @return A pointer to the KConfig object to be used. This is + * either kapp->config() (default) or a KSimpleConfig object for + * a system-wide highscore file. + **/ + KConfig* config() const; + + void init(bool forceLocal); + +private: + KHighscorePrivate* d; + + static KFileLock *_lock; // lock on system-wide highscore file + static KRawConfig *_config; // config for system-wide highscore file +}; + +#endif diff --git a/libkdegames/highscore/kscoredialog.cpp b/libkdegames/highscore/kscoredialog.cpp new file mode 100644 index 00000000..37155650 --- /dev/null +++ b/libkdegames/highscore/kscoredialog.cpp @@ -0,0 +1,411 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala <ssigala@globalnet.it>. +Copyright (c) 2001 Waldo Bastian <bastian@kde.org> +All rights reserved. + +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 the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author 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. +****************************************************************/ + +#include "config.h" + +#include <qlabel.h> +#include <qlayout.h> +#include <qlineedit.h> +#include <qwidgetstack.h> +#include <qtimer.h> +#include <qevent.h> +#include <qptrvector.h> + +#include <kapplication.h> +#include <kconfig.h> +#include <klocale.h> +#include <kseparator.h> + +#include "kscoredialog.h" + +class KScoreDialog::KScoreDialogPrivate +{ +public: + QPtrList<FieldInfo> scores; + QWidget *page; + QGridLayout *layout; + QLineEdit *edit; + QPtrVector<QWidgetStack> stack; + QPtrVector<QLabel> labels; + QLabel *commentLabel; + QString comment; + int fields; + int newName; + int latest; + int nrCols; + bool loaded; + QString configGroup; + + QMap<int, int> col; + QMap<int, QString> header; + QMap<int, QString> key; + QString player; +}; + + +KScoreDialog::KScoreDialog(int fields, QWidget *parent, const char *oname) + : KDialogBase(parent, oname, true, i18n("High Scores"), Ok, Ok, true) +{ + d = new KScoreDialogPrivate(); + d->edit = 0; + d->fields = fields; + d->newName = -1; + d->latest = -1; + d->loaded = false; + d->nrCols = 0; + d->configGroup = "High Score"; + + d->scores.setAutoDelete(true); + d->header[Name] = i18n("Name"); + d->key[Name] = "Name"; + + d->header[Date] = i18n("Date"); + d->key[Date] = "Date"; + + d->header[Level] = i18n("Level"); + d->key[Level] = "Level"; + + d->header[Score] = i18n("Score"); + d->key[Score] = "Score"; + d->page = makeMainWidget(); + + connect(this, SIGNAL(okClicked()), SLOT(slotGotName())); +} + +KScoreDialog::~KScoreDialog() +{ + delete d; +} + +void KScoreDialog::setConfigGroup(const QString &group) +{ + d->configGroup = group; + d->loaded = false; +} + +void KScoreDialog::setComment(const QString &comment) +{ + d->comment = comment; +} + +void KScoreDialog::addField(int field, const QString &header, const QString &key) +{ + d->fields |= field; + d->header[field] = header; + d->key[field] = key; +} + +void KScoreDialog::setupDialog() +{ + d->nrCols = 1; + + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + d->col[field] = d->nrCols++; + } + + d->layout = new QGridLayout(d->page, 15, d->nrCols, marginHint() + 20, spacingHint()); + d->layout->addRowSpacing(4, 15); + + d->commentLabel = new QLabel(d->page); + d->commentLabel->setAlignment(AlignVCenter | AlignHCenter); + d->layout->addMultiCellWidget(d->commentLabel, 1, 1, 0, d->nrCols-1); + + QFont bold = font(); + bold.setBold(true); + + QLabel *label; + d->layout->addColSpacing(0, 50); + label = new QLabel(i18n("Rank"), d->page); + d->layout->addWidget(label, 3, 0); + label->setFont(bold); + + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + d->layout->addColSpacing(d->col[field], 50); + + label = new QLabel(d->header[field], d->page); + d->layout->addWidget(label, 3, d->col[field], field <= Name ? AlignLeft : AlignRight); + label->setFont(bold); + } + } + + KSeparator *sep = new KSeparator(Horizontal, d->page); + d->layout->addMultiCellWidget(sep, 4, 4, 0, d->nrCols-1); + + d->labels.resize(d->nrCols * 10); + d->stack.resize(10); + + QString num; + for (int i = 1; i <= 10; ++i) { + QLabel *label; + num.setNum(i); + label = new QLabel(i18n("#%1").arg(num), d->page); + d->labels.insert((i-1)*d->nrCols + 0, label); + d->layout->addWidget(label, i+4, 0); + if (d->fields & Name) + { + QWidgetStack *stack = new QWidgetStack(d->page); + d->stack.insert(i-1, stack); + d->layout->addWidget(stack, i+4, d->col[Name]); + label = new QLabel(d->page); + d->labels.insert((i-1)*d->nrCols + d->col[Name], label); + stack->addWidget(label); + stack->raiseWidget(label); + } + for(int field = Name * 2; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + label = new QLabel(d->page); + d->labels.insert((i-1)*d->nrCols + d->col[field], label); + d->layout->addWidget(label, i+4, d->col[field], AlignRight); + } + } + } +} + +void KScoreDialog::aboutToShow() +{ + if (!d->loaded) + loadScores(); + + if (!d->nrCols) + setupDialog(); + + d->commentLabel->setText(d->comment); + if (d->comment.isEmpty()) + { + d->commentLabel->setMinimumSize(QSize(1,1)); + d->commentLabel->hide(); + d->layout->addRowSpacing(0, -15); + d->layout->addRowSpacing(2, -15); + } + else + { + d->commentLabel->setMinimumSize(d->commentLabel->sizeHint()); + d->commentLabel->show(); + d->layout->addRowSpacing(0, -10); + d->layout->addRowSpacing(2, 10); + } + d->comment = QString::null; + + QFont normal = font(); + QFont bold = normal; + bold.setBold(true); + + QString num; + for (int i = 1; i <= 10; ++i) { + QLabel *label; + num.setNum(i); + FieldInfo *score = d->scores.at(i-1); + label = d->labels[(i-1)*d->nrCols + 0]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + + if (d->fields & Name) + { + if (d->newName == i) + { + QWidgetStack *stack = d->stack[i-1]; + d->edit = new QLineEdit(d->player, stack); + d->edit->setMinimumWidth(40); + stack->addWidget(d->edit); + stack->raiseWidget(d->edit); + d->edit->setFocus(); + connect(d->edit, SIGNAL(returnPressed()), + this, SLOT(slotGotReturn())); + } + else + { + label = d->labels[(i-1)*d->nrCols + d->col[Name]]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + label->setText((*score)[Name]); + } + + } + for(int field = Name * 2; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + label = d->labels[(i-1)*d->nrCols + d->col[field]]; + if (i == d->latest) + label->setFont(bold); + else + label->setFont(normal); + label->setText((*score)[field]); + } + } + } + d->latest = -1; + setFixedSize(minimumSizeHint()); +} + +void KScoreDialog::loadScores() +{ + QString key, value; + d->loaded = true; + d->scores.clear(); + KConfigGroup config(kapp->config(), d->configGroup.utf8()); + + d->player = config.readEntry("LastPlayer"); + + QString num; + for (int i = 1; i <= 10; ++i) { + num.setNum(i); + FieldInfo *score = new FieldInfo(); + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + key = "Pos" + num + d->key[field]; + (*score)[field] = config.readEntry(key, "-"); + } + } + d->scores.append(score); + } +} + +void KScoreDialog::saveScores() +{ + QString key, value; + KConfigGroup config(kapp->config(), d->configGroup.utf8()); + + config.writeEntry("LastPlayer", d->player); + + QString num; + for (int i = 1; i <= 10; ++i) { + num.setNum(i); + FieldInfo *score = d->scores.at(i-1); + for(int field = 1; field < d->fields; field = field * 2) + { + if (d->fields & field) + { + key = "Pos" + num + d->key[field]; + config.writeEntry(key, (*score)[field]); + } + } + } + kapp->config()->sync(); +} + +int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName) +{ + return addScore(newScore, newInfo, askName, false); +} + +int KScoreDialog::addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore) +{ + if (!d->loaded) + loadScores(); + FieldInfo *score = d->scores.first(); + int i = 1; + for(; score; score = d->scores.next(), i++) + { + bool ok; + int num_score = (*score)[Score].toLong(&ok); + if (lessIsMore && !ok) + num_score = 1 << 30; + if (((newScore > num_score) && !lessIsMore) || + ((newScore < num_score) && lessIsMore)) + { + score = new FieldInfo(newInfo); + (*score)[Score].setNum(newScore); + d->scores.insert(i-1, score); + d->scores.remove(10); + d->latest = i; + if (askName) + d->newName = i; + else + saveScores(); + if (i == 1) + d->comment = i18n("Excellent!\nYou have a new high score!"); + else + d->comment = i18n("Well done!\nYou made it to the high score list!"); + return i; + } + } + return 0; +} + +void KScoreDialog::show() +{ + aboutToShow(); + KDialogBase::show(); +} + +void KScoreDialog::slotGotReturn() +{ + QTimer::singleShot(0, this, SLOT(slotGotName())); +} + +void KScoreDialog::slotGotName() +{ + if (d->newName == -1) return; + + d->player = d->edit->text(); + + (*d->scores.at(d->newName-1))[Name] = d->player; + saveScores(); + + QFont bold = font(); + bold.setBold(true); + + QLabel *label = d->labels[(d->newName-1)*d->nrCols + d->col[Name]]; + label->setFont(bold); + label->setText(d->player); + d->stack[(d->newName-1)]->raiseWidget(label); + delete d->edit; + d->edit = 0; + d->newName = -1; +} + +int KScoreDialog::highScore() +{ + if (!d->loaded) + loadScores(); + + return (*d->scores.first())[Score].toInt(); +} + +void KScoreDialog::keyPressEvent( QKeyEvent *ev) +{ + if ((d->newName != -1) && (ev->key() == Key_Return)) + { + ev->ignore(); + return; + } + KDialogBase::keyPressEvent(ev); +} + + +#include "kscoredialog.moc" diff --git a/libkdegames/highscore/kscoredialog.h b/libkdegames/highscore/kscoredialog.h new file mode 100644 index 00000000..4d4a76db --- /dev/null +++ b/libkdegames/highscore/kscoredialog.h @@ -0,0 +1,125 @@ +/**************************************************************** +Copyright (c) 1998 Sandro Sigala <ssigala@globalnet.it>. +Copyright (c) 2001 Waldo Bastian <bastian@kde.org> +All rights reserved. + +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 the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of the author not be used in +advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +The author disclaim all warranties with regard to this +software, including all implied warranties of merchantability +and fitness. In no event shall the author 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. +****************************************************************/ + +#ifndef KSCOREDIALOG_H +#define KSCOREDIALOG_H + +#include <qmap.h> +#include <qptrlist.h> + +#include <kdialogbase.h> +#include <kdemacros.h> +class QGridLayout; +class QLineEdit; +class QWidgetStack; + +/** + * A simple high score dialog. + */ +class KDE_EXPORT KScoreDialog : public KDialogBase { + Q_OBJECT + +public: + enum Fields { Name = 1 << 0, + Level = 1 << 1, + + Custom1 = 1 << 10, + Custom2 = 1 << 11, + Custom3 = 1 << 12, + + Date = 1 << 27, + Time = 1 << 28, + Score = 1 << 29 }; + + typedef QMap<int, QString> FieldInfo; + + /** + * @param fields Which fields should be listed. + * @param parent passed to parent QWidget constructor + * @param name passed to parent QWidget constructor + */ + KScoreDialog(int fields, QWidget *parent=0, const char *name=0); + + ~KScoreDialog(); + + /** + * @param group to use for reading/writing highscores from/to. By default + * the class will use "High Score" + */ + void setConfigGroup(const QString &group); + + /** + * @param comment to add when showing high-scores. + * The comment is only used once. + */ + void setComment(const QString &comment); + + /** + * Define an extra FieldInfo entry. + * @param field Id of this field + * @param header Header shown in the dialog for this field + * @param key used to store this field with. + */ + void addField(int field, const QString &header, const QString &key); + + /** + * Adds a new score to the list. + * + * @param newScore the score of this game. + * @param newInfo additional info about the score. + * @param askName Whether to prompt for the players name. + * @param lessIsMore If true, the lowest score is the best score. + * + * @returns The highscore position if the score was good enough to + * make it into the list (1 being topscore) or 0 otherwise. + */ + int addScore(int newScore, const FieldInfo &newInfo, bool askName, bool lessIsMore); + int addScore(int newScore, const FieldInfo &newInfo, bool askName=true); + + /** + * Returns the current best score. + */ + int highScore(); + + virtual void show(); + +private slots: + void slotGotReturn(); + void slotGotName(); + +private: + /* read scores */ + void loadScores(); + void saveScores(); + + void aboutToShow(); + void setupDialog(); + void keyPressEvent( QKeyEvent *ev); + +private: + class KScoreDialogPrivate; + KScoreDialogPrivate *d; +}; + +#endif // !KSCOREDIALOG_H diff --git a/libkdegames/kcanvasrootpixmap.cpp b/libkdegames/kcanvasrootpixmap.cpp new file mode 100644 index 00000000..14592c66 --- /dev/null +++ b/libkdegames/kcanvasrootpixmap.cpp @@ -0,0 +1,39 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 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 "kcanvasrootpixmap.h" + +#include <qcanvas.h> + + +KCanvasRootPixmap::KCanvasRootPixmap(QCanvasView *view, const char *name) + : KRootPixmap(view, name), _view(view) +{ + setCustomPainting(true); + connect(this, SIGNAL(backgroundUpdated(const QPixmap &)), + SLOT(backgroundUpdatedSlot(const QPixmap &))); +} + +void KCanvasRootPixmap::backgroundUpdatedSlot(const QPixmap &pixmap) +{ + if ( _view && _view->canvas() ) + _view->canvas()->setBackgroundPixmap(pixmap); +} + +#include "kcanvasrootpixmap.moc" diff --git a/libkdegames/kcanvasrootpixmap.h b/libkdegames/kcanvasrootpixmap.h new file mode 100644 index 00000000..3eedb7e1 --- /dev/null +++ b/libkdegames/kcanvasrootpixmap.h @@ -0,0 +1,61 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 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 KCANVASROOTPIXMAP_H +#define KCANVASROOTPIXMAP_H + +#include <krootpixmap.h> +#include <kdemacros.h> + +class QCanvasView; + +/** + * Implement KRootPixmap for a QCanvasView. + * + * The pixmap will be set as the background of the + * QCanvas associated with the view : + * <ul> + * <li>for correct positioning of the background pixmap, the given + * QCanvasView should be positioned at the origin of the canvas.</li> + * <li>no other view of the same canvas should use KCanvasRootPixmap.</li> + * <li>other views of the canvas will have the same background pixmap.</li> + * </ul> + */ +class KDE_EXPORT KCanvasRootPixmap : public KRootPixmap +{ + Q_OBJECT + + public: + /** + * Constructor. + */ + KCanvasRootPixmap(QCanvasView *view, const char *name = 0); + + private slots: + void backgroundUpdatedSlot(const QPixmap &); + + private: + QCanvasView *_view; + + class KCanvasRootPixmapPrivate; + KCanvasRootPixmapPrivate *d; +}; + +#endif + diff --git a/libkdegames/kcarddialog.cpp b/libkdegames/kcarddialog.cpp new file mode 100644 index 00000000..a4d2ac20 --- /dev/null +++ b/libkdegames/kcarddialog.cpp @@ -0,0 +1,808 @@ +/* + This file is part of the KDE games library + Copyright (C) 2000 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ + +#include <stdio.h> +#include <assert.h> + +#include <qgroupbox.h> +#include <qlabel.h> +#include <qcheckbox.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qslider.h> +#include <qwmatrix.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kiconview.h> +#include <ksimpleconfig.h> + +#include "kcarddialog.h" +#include <qpushbutton.h> +#include <kdebug.h> + +#define KCARD_DEFAULTDECK QString::fromLatin1("deck0.png") +#define KCARD_DEFAULTCARD QString::fromLatin1("11.png") +#define KCARD_DEFAULTCARDDIR QString::fromLatin1("cards-default/") + +// values for the resize slider +#define SLIDER_MIN 400 +#define SLIDER_MAX 3000 + +// KConfig entries +#define CONF_GROUP "KCardDialog" +#define CONF_RANDOMDECK QString::fromLatin1("RandomDeck") +#define CONF_DECK QString::fromLatin1("Deck") +#define CONF_CARDDIR QString::fromLatin1("CardDir") +#define CONF_RANDOMCARDDIR QString::fromLatin1("RandomCardDir") +#define CONF_USEGLOBALDECK QString::fromLatin1("GlobalDeck") +#define CONF_USEGLOBALCARDDIR QString::fromLatin1("GlobalCardDir") +#define CONF_SCALE QString::fromLatin1("Scale") + +#define CONF_GLOBAL_GROUP QString::fromLatin1("KCardDialog Settings") +#define CONF_GLOBAL_DECK QString::fromLatin1("GlobalDeck") +#define CONF_GLOBAL_CARDDIR QString::fromLatin1("GlobalCardDir") +#define CONF_GLOBAL_RANDOMDECK QString::fromLatin1("GlobalRandomDeck") +#define CONF_GLOBAL_RANDOMCARDDIR QString::fromLatin1("GlobalRandomCardDir") + + +class KCardDialogPrivate +{ +public: + KCardDialogPrivate() + { + deckLabel = 0; + cardLabel = 0; + deckIconView = 0; + cardIconView = 0; + randomDeck = 0; + randomCardDir = 0; + cPreview = 0; + scaleSlider = 0; + globalDeck = 0; + globalCardDir = 0; + + cScale = 1; + } + + QLabel* deckLabel; + QLabel* cardLabel; + KIconView* deckIconView; + KIconView* cardIconView; + QCheckBox* randomDeck; + QCheckBox* randomCardDir; + QCheckBox* globalDeck; + QCheckBox* globalCardDir; + + QSlider* scaleSlider; + QPixmap cPreviewPix; + QLabel* cPreview; + + QMap<QIconViewItem*, QString> deckMap; + QMap<QIconViewItem*, QString> cardMap; + QMap<QString, QString> helpMap; + + //set query variables + KCardDialog::CardFlags cFlags; + QString cDeck; + QString cCardDir; + double cScale; +}; + +int KCardDialog::getCardDeck(QString &pDeck, QString &pCardDir, QWidget *pParent, + CardFlags pFlags, bool* pRandomDeck, bool* pRandomCardDir, + double* pScale, KConfig* pConf) +{ + KCardDialog dlg(pParent, "dlg", pFlags); + + dlg.setDeck(pDeck); + dlg.setCardDir(pCardDir); + + dlg.setupDialog(pScale != 0); + dlg.loadConfig(pConf); + dlg.showRandomDeckBox(pRandomDeck != 0); + dlg.showRandomCardDirBox(pRandomCardDir != 0); + int result=dlg.exec(); + if (result==QDialog::Accepted) + { + // TODO check for global cards/decks!!!! + pDeck=dlg.deck(); + pCardDir=dlg.cardDir(); + if (!pCardDir.isNull() && pCardDir.right(1)!=QString::fromLatin1("/")) + { + pCardDir+=QString::fromLatin1("/"); + } + if (pRandomDeck) + { + *pRandomDeck = dlg.isRandomDeck(); + } + if (pRandomCardDir) + { + *pRandomCardDir = dlg.isRandomCardDir(); + } + if (pScale) + { + *pScale = dlg.cardScale(); + } + + if (dlg.isGlobalDeck()) + { + kdDebug(11000) << "use global deck" << endl; + bool random; + getGlobalDeck(pDeck, random); + kdDebug(11000) << "use: " << pDeck<< endl; + if (pRandomDeck) + { + *pRandomDeck=random; + if (random) + kdDebug(11000) << "use random deck" << endl; + } + } + if (dlg.isGlobalCardDir()) + { + kdDebug(11000) << "use global carddir" << endl; + bool random; + getGlobalCardDir(pCardDir, random); + kdDebug(11000) << "use: " << pCardDir << endl; + if (pRandomCardDir) + { + *pRandomCardDir=random; + if (random) + kdDebug(11000) << "use random carddir" << endl; + } + } + } + dlg.saveConfig(pConf); + return result; +} + +void KCardDialog::getConfigCardDeck(KConfig* conf, QString &pDeck, QString &pCardDir, double& pScale) +{ +// TODO check for global cards/decks!!!! + if (!conf) { + return; + } + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (conf->readBoolEntry(CONF_RANDOMDECK) || !conf->hasKey(CONF_DECK)) { + pDeck = getRandomDeck(); + } else { + pDeck = conf->readEntry(CONF_DECK); + } + if (conf->readBoolEntry(CONF_RANDOMCARDDIR) || !conf->hasKey(CONF_CARDDIR)) { + pCardDir = getRandomCardDir(); + } else { + pCardDir = conf->readPathEntry(CONF_CARDDIR); + } + pScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0); + + if (conf->readBoolEntry(CONF_USEGLOBALDECK, false)) { + bool random; + getGlobalDeck(pCardDir, random); + if (random || pDeck.isNull() ) { + pDeck = getRandomDeck(); + } + } + if (conf->readBoolEntry(CONF_USEGLOBALCARDDIR, false)) { + bool random; + getGlobalCardDir(pCardDir, random); + if (random || pCardDir.isNull() ) { + pCardDir = getRandomCardDir(); + } + } + + conf->setGroup(origGroup); +} + +QString KCardDialog::getDefaultDeck() +{ + KCardDialog::init(); + return locate("cards", QString::fromLatin1("decks/") + KCARD_DEFAULTDECK); +} + +QString KCardDialog::getDefaultCardDir() +{ + KCardDialog::init(); + + QString file = KCARD_DEFAULTCARDDIR + KCARD_DEFAULTCARD; + return KGlobal::dirs()->findResourceDir("cards",file) + KCARD_DEFAULTCARDDIR; +} + +QString KCardDialog::getCardPath(const QString &carddir, int index) +{ + KCardDialog::init(); + + QString entry = carddir + QString::number(index); + if (KStandardDirs::exists(entry + QString::fromLatin1(".png"))) + return entry + QString::fromLatin1(".png"); + + // rather theoretical + if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm"))) + return entry + QString::fromLatin1(".xpm"); + + return QString::null; +} + +const QString& KCardDialog::deck() const { return d->cDeck; } +void KCardDialog::setDeck(const QString& file) { d->cDeck=file; } +const QString& KCardDialog::cardDir() const { return d->cCardDir; } +void KCardDialog::setCardDir(const QString& dir) { d->cCardDir=dir; } +KCardDialog::CardFlags KCardDialog::flags() const { return d->cFlags; } +double KCardDialog::cardScale() const { return d->cScale; } +bool KCardDialog::isRandomDeck() const +{ return (d->randomDeck ? d->randomDeck->isChecked() : false); } +bool KCardDialog::isRandomCardDir() const +{ return (d->randomCardDir ? d->randomCardDir->isChecked() : false); } +bool KCardDialog::isGlobalDeck() const +{ return (d->globalDeck ? d->globalDeck->isChecked() : false); } +bool KCardDialog::isGlobalCardDir() const +{ return (d->globalCardDir ? d->globalCardDir->isChecked() : false); } + +void KCardDialog::setupDialog(bool showResizeBox) +{ + QHBoxLayout* topLayout = new QHBoxLayout(plainPage(), spacingHint()); + QVBoxLayout* cardLayout = new QVBoxLayout(topLayout); + QString path, file; + QWMatrix m; + m.scale(0.8,0.8); + + setInitialSize(QSize(600,400)); + + if (! (flags() & NoDeck)) + { + QHBoxLayout* layout = new QHBoxLayout(cardLayout); + + // Deck iconview + QGroupBox* grp1 = new QGroupBox(1, Horizontal, i18n("Choose Backside"), plainPage()); + layout->addWidget(grp1); + + d->deckIconView = new KIconView(grp1,"decks"); + d->deckIconView->setSpacing(8); + /* + deckIconView->setGridX(-1); + deckIconView->setGridY(50); + */ + d->deckIconView->setGridX(82); + d->deckIconView->setGridY(106); + d->deckIconView->setSelectionMode(QIconView::Single); + d->deckIconView->setResizeMode(QIconView::Adjust); + d->deckIconView->setMinimumWidth(360); + d->deckIconView->setMinimumHeight(170); + d->deckIconView->setWordWrapIconText(false); + d->deckIconView->showToolTips(); + + // deck select + QVBoxLayout* l = new QVBoxLayout(layout); + QGroupBox* grp3 = new QGroupBox(i18n("Backside"), plainPage()); + grp3->setFixedSize(100, 130); + l->addWidget(grp3, 0, AlignTop|AlignHCenter); + d->deckLabel = new QLabel(grp3); + d->deckLabel->setText(i18n("empty")); + d->deckLabel->setAlignment(AlignHCenter|AlignVCenter); + d->deckLabel->setGeometry(10, 20, 80, 90); + + d->randomDeck = new QCheckBox(plainPage()); + d->randomDeck->setChecked(false); + connect(d->randomDeck, SIGNAL(toggled(bool)), this, + SLOT(slotRandomDeckToggled(bool))); + d->randomDeck->setText(i18n("Random backside")); + l->addWidget(d->randomDeck, 0, AlignTop|AlignHCenter); + + d->globalDeck = new QCheckBox(plainPage()); + d->globalDeck->setChecked(false); + d->globalDeck->setText(i18n("Use global backside")); + l->addWidget(d->globalDeck, 0, AlignTop|AlignHCenter); + + QPushButton* b = new QPushButton(i18n("Make Backside Global"), plainPage()); + connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalDeck())); + l->addWidget(b, 0, AlignTop|AlignHCenter); + + connect(d->deckIconView,SIGNAL(clicked(QIconViewItem *)), + this,SLOT(slotDeckClicked(QIconViewItem *))); + } + + if (! (flags() & NoCards)) + { + // Cards iconview + QHBoxLayout* layout = new QHBoxLayout(cardLayout); + QGroupBox* grp2 = new QGroupBox(1, Horizontal, i18n("Choose Frontside"), plainPage()); + layout->addWidget(grp2); + + d->cardIconView =new KIconView(grp2,"cards"); + /* + cardIconView->setGridX(36); + cardIconView->setGridY(50); + */ + d->cardIconView->setGridX(82); + d->cardIconView->setGridY(106); + d->cardIconView->setResizeMode(QIconView::Adjust); + d->cardIconView->setMinimumWidth(360); + d->cardIconView->setMinimumHeight(170); + d->cardIconView->setWordWrapIconText(false); + d->cardIconView->showToolTips(); + + // Card select + QVBoxLayout* l = new QVBoxLayout(layout); + QGroupBox* grp4 = new QGroupBox(i18n("Frontside"), plainPage()); + grp4->setFixedSize(100, 130); + l->addWidget(grp4, 0, AlignTop|AlignHCenter); + d->cardLabel = new QLabel(grp4); + d->cardLabel->setText(i18n("empty")); + d->cardLabel->setAlignment(AlignHCenter|AlignVCenter); + d->cardLabel->setGeometry(10, 20, 80, 90 ); + + d->randomCardDir = new QCheckBox(plainPage()); + d->randomCardDir->setChecked(false); + connect(d->randomCardDir, SIGNAL(toggled(bool)), this, + SLOT(slotRandomCardDirToggled(bool))); + d->randomCardDir->setText(i18n("Random frontside")); + l->addWidget(d->randomCardDir, 0, AlignTop|AlignHCenter); + + d->globalCardDir = new QCheckBox(plainPage()); + d->globalCardDir->setChecked(false); + d->globalCardDir->setText(i18n("Use global frontside")); + l->addWidget(d->globalCardDir, 0, AlignTop|AlignHCenter); + + QPushButton* b = new QPushButton(i18n("Make Frontside Global"), plainPage()); + connect(b, SIGNAL(pressed()), this, SLOT(slotSetGlobalCardDir())); + l->addWidget(b, 0, AlignTop|AlignHCenter); + + connect(d->cardIconView,SIGNAL(clicked(QIconViewItem *)), + this,SLOT(slotCardClicked(QIconViewItem *))); + } + + // Insert deck icons + // First find the default or alternate path + if (! (flags() & NoDeck)) + { + insertDeckIcons(); + d->deckIconView->arrangeItemsInGrid(); + + // Set default icons if given + if (!deck().isNull()) + { + file=deck(); + QPixmap pixmap(file); + pixmap=pixmap.xForm(m); + d->deckLabel->setPixmap(pixmap); + QToolTip::add(d->deckLabel,d->helpMap[file]); + } + } + + // Insert card icons + if (! (flags() & NoCards)) + { + insertCardIcons(); + d->cardIconView->arrangeItemsInGrid(); + + // Set default icons if given + if (!cardDir().isNull()) + { + file = cardDir() + KCARD_DEFAULTCARD; + QPixmap pixmap(file); + pixmap = pixmap.xForm(m); + d->cardLabel->setPixmap(pixmap); + QToolTip::add(d->cardLabel,d->helpMap[cardDir()]); + } + } + + // insert resize box + if (showResizeBox) + { + // this part is a little bit...tricky. + // i'm sure there is a cleaner way but i cannot find it. + // whenever the pixmap is resized (aka scaled) the box is resized, too. This + // leads to an always resizing dialog which is *very* ugly. i worked around + // this by using a QWidget which is the only child widget of the group box. + // The other widget are managed inside this QWidget - a stretch area on the + // right ensures that the KIconViews are not resized... + + // note that the dialog is still resized if you you scale the pixmap very + // large. This is desired behaviour as i don't want to make the box even + // larger but i want the complete pixmap to be displayed. the dialog is not + // resized if you make the pixmap smaller again. + QVBoxLayout* layout = new QVBoxLayout(topLayout); + QGroupBox* grp = new QGroupBox(1, Horizontal, i18n("Resize Cards"), plainPage()); + layout->setResizeMode(QLayout::Fixed); + layout->addWidget(grp); + QWidget* box = new QWidget(grp); + QHBoxLayout* hbox = new QHBoxLayout(box, 0, spacingHint()); + QVBoxLayout* boxLayout = new QVBoxLayout(hbox); + hbox->addStretch(0); + + d->scaleSlider = new QSlider(1, SLIDER_MAX, 1, (-1000+SLIDER_MIN+SLIDER_MAX), Horizontal, box); + d->scaleSlider->setMinValue(SLIDER_MIN); + connect(d->scaleSlider, SIGNAL(valueChanged(int)), this, SLOT(slotCardResized(int))); + boxLayout->addWidget(d->scaleSlider, 0, AlignLeft); + + QPushButton* b = new QPushButton(i18n("Default Size"), box); + connect(b, SIGNAL(pressed()), this, SLOT(slotDefaultSize())); + boxLayout->addWidget(b, 0, AlignLeft); + + QLabel* l = new QLabel(i18n("Preview:"), box); + boxLayout->addWidget(l); + d->cPreviewPix.load(getDefaultDeck()); + d->cPreview = new QLabel(box); + boxLayout->addWidget(d->cPreview, 0, AlignCenter|AlignVCenter); + + slotCardResized(d->scaleSlider->value()); + } +} + +void KCardDialog::insertCardIcons() +{ + QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop", false, true); + // kdDebug(11000) << "insert " << list.count() << endl; + if (list.isEmpty()) + return; + + // We shrink the icons a little + // + QWMatrix m; + m.scale(0.8,0.8); + + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + KSimpleConfig cfg(*it); + cfg.setGroup(QString::fromLatin1("KDE Backdeck")); + QString path = (*it).left((*it).findRev('/') + 1); + assert(path[path.length() - 1] == '/'); + QPixmap pixmap(path + cfg.readEntry("Preview", "12c.png")); + + if (pixmap.isNull()) + continue; + + QString name=cfg.readEntry("Name", i18n("unnamed")); + QIconViewItem *item= new QIconViewItem(d->cardIconView, name, pixmap); + + item->setDragEnabled(false); + item->setDropEnabled(false); + item->setRenameEnabled(false); + item->setSelectable(true); + + d->cardMap[item] = path; + d->helpMap[path] = cfg.readEntry("Comment",name); + } +} + +void KCardDialog::insertDeckIcons() +{ + QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop", false, true); + if (list.isEmpty()) + return; + + QString label; + + // We shrink the icons a little + QWMatrix m; + m.scale(0.8,0.8); + + for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) + { + KSimpleConfig cfg(*it); + QPixmap pixmap(getDeckName(*it)); + if (pixmap.isNull()) + continue; + + // pixmap=pixmap.xForm(m); + + cfg.setGroup(QString::fromLatin1("KDE Cards")); + QString name=cfg.readEntry("Name", i18n("unnamed")); + QIconViewItem *item= new QIconViewItem(d->deckIconView,name, pixmap); + + item->setDragEnabled(false); + item->setDropEnabled(false); + item->setRenameEnabled(false); + + d->deckMap[item] = getDeckName(*it); + d->helpMap[d->deckMap[item]] = cfg.readEntry("Comment",name); + } +} + + +KCardDialog::~KCardDialog() +{ + delete d; +} + + +// Create the dialog +KCardDialog::KCardDialog( QWidget *parent, const char *name, CardFlags mFlags) + : KDialogBase( Plain, i18n("Carddeck Selection"), Ok|Cancel, Ok, parent, name, true, true) +{ + KCardDialog::init(); + + d = new KCardDialogPrivate; + d->cFlags = mFlags; +} + +void KCardDialog::slotDeckClicked(QIconViewItem *item) +{ + if (item && item->pixmap()) + { + d->deckLabel->setPixmap(* (item->pixmap())); + QToolTip::remove( d->deckLabel ); + QToolTip::add(d->deckLabel,d->helpMap[d->deckMap[item]]); + setDeck(d->deckMap[item]); + } +} +void KCardDialog::slotCardClicked(QIconViewItem *item) +{ + if (item && item->pixmap()) + { + d->cardLabel->setPixmap(* (item->pixmap())); + QString path = d->cardMap[item]; + QToolTip::remove( d->deckLabel ); + QToolTip::add(d->cardLabel,d->helpMap[path]); + setCardDir(path); + } +} + +QString KCardDialog::getDeckName(const QString &desktop) +{ + QString entry = desktop.left(desktop.length() - strlen(".desktop")); + if (KStandardDirs::exists(entry + QString::fromLatin1(".png"))) + return entry + QString::fromLatin1(".png"); + + // rather theoretical + if (KStandardDirs::exists(entry + QString::fromLatin1(".xpm"))) + return entry + QString::fromLatin1(".xpm"); + return QString::null; +} + +QString KCardDialog::getRandomDeck() +{ + KCardDialog::init(); + + QStringList list = KGlobal::dirs()->findAllResources("cards", "decks/*.desktop"); + if (list.isEmpty()) + return QString::null; + + int d = KApplication::random() % list.count(); + return getDeckName(*list.at(d)); +} + +QString KCardDialog::getRandomCardDir() +{ + KCardDialog::init(); + + QStringList list = KGlobal::dirs()->findAllResources("cards", "card*/index.desktop"); + if (list.isEmpty()) + return QString::null; + + int d = KApplication::random() % list.count(); + QString entry = *list.at(d); + return entry.left(entry.length() - strlen("index.desktop")); +} + +void KCardDialog::showRandomDeckBox(bool s) +{ + if (!d->randomDeck) + return; + + if (s) + d->randomDeck->show(); + else + d->randomDeck->hide(); +} + +void KCardDialog::showRandomCardDirBox(bool s) +{ + if (!d->randomCardDir) + return; + + if (s) + d->randomCardDir->show(); + else + d->randomCardDir->hide(); +} + +void KCardDialog::slotRandomDeckToggled(bool on) +{ + if (on) { + d->deckLabel->setText("random"); + setDeck(getRandomDeck()); + } else { + d->deckLabel->setText("empty"); + setDeck(0); + } +} + +void KCardDialog::slotRandomCardDirToggled(bool on) +{ + if (on) { + d->cardLabel->setText("random"); + setCardDir(getRandomCardDir()); + if (cardDir().length()>0 && cardDir().right(1)!=QString::fromLatin1("/")) { + setCardDir(cardDir() + QString::fromLatin1("/")); + } + } else { + d->cardLabel->setText("empty"); + setCardDir(0); + } +} + +void KCardDialog::loadConfig(KConfig* conf) +{ + if (!conf) { + return; + } + + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (! (flags() & NoDeck)) { + if (conf->hasKey(CONF_DECK)) { + setDeck(conf->readEntry(CONF_DECK)); + } + + bool random = conf->readBoolEntry(CONF_RANDOMDECK, false); + d->randomDeck->setChecked(random); + slotRandomDeckToggled(random); + + if (conf->hasKey(CONF_USEGLOBALDECK) && conf->readBoolEntry(CONF_USEGLOBALDECK)) { + d->globalDeck->setChecked(true); + } else { + d->globalDeck->setChecked(false); + } + } + if (! (flags() & NoCards)) { + if (conf->hasKey(CONF_CARDDIR)) { + setCardDir(conf->readPathEntry(CONF_CARDDIR)); + } + + bool random = conf->readBoolEntry(CONF_RANDOMCARDDIR, false); + d->randomCardDir->setChecked(random); + slotRandomCardDirToggled(random); + + if (conf->hasKey(CONF_USEGLOBALCARDDIR) && conf->readBoolEntry(CONF_USEGLOBALCARDDIR)) { + d->globalCardDir->setChecked(true); + } else { + d->globalCardDir->setChecked(false); + } + } + + d->cScale = conf->readDoubleNumEntry(CONF_SCALE, 1.0); + + conf->setGroup(origGroup); +} + +void KCardDialog::slotCardResized(int s) +{ + if (!d->cPreview) { + return; + } + if (s < SLIDER_MIN || s > SLIDER_MAX) { + kdError(11000) << "invalid scaling value!" << endl; + return; + } + + s *= -1; + s += (SLIDER_MIN + SLIDER_MAX); + + QWMatrix m; + double scale = (double)1000/s; + m.scale(scale, scale); + QPixmap pix = d->cPreviewPix.xForm(m); + d->cPreview->setPixmap(pix); + d->cScale = scale; +} + +void KCardDialog::slotDefaultSize() +{ + if (!d->scaleSlider) { + return; + } + d->scaleSlider->setValue(-1000 + SLIDER_MIN + SLIDER_MAX); +} + +void KCardDialog::saveConfig(KConfig* conf) +{ + if (!conf) { + return; + } + QString origGroup = conf->group(); + + conf->setGroup(CONF_GROUP); + if (! (flags() & NoDeck)) { + conf->writeEntry(CONF_DECK, deck()); + conf->writeEntry(CONF_RANDOMDECK, isRandomDeck()); + conf->writeEntry(CONF_USEGLOBALDECK, d->globalDeck->isChecked()); + } + if (! (flags() & NoCards)) { + conf->writePathEntry(CONF_CARDDIR, cardDir()); + conf->writeEntry(CONF_RANDOMCARDDIR, isRandomCardDir()); + conf->writeEntry(CONF_USEGLOBALCARDDIR, d->globalCardDir->isChecked()); + } + conf->writeEntry(CONF_SCALE, d->cScale); + + conf->setGroup(origGroup); +} + +void KCardDialog::slotSetGlobalDeck() +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false); + conf->setGroup(CONF_GLOBAL_GROUP); + + conf->writeEntry(CONF_GLOBAL_DECK, deck()); + conf->writeEntry(CONF_GLOBAL_RANDOMDECK, isRandomDeck()); + + delete conf; +} + +void KCardDialog::slotSetGlobalCardDir() +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), false); + conf->setGroup(CONF_GLOBAL_GROUP); + + conf->writePathEntry(CONF_GLOBAL_CARDDIR, cardDir()); + conf->writeEntry(CONF_GLOBAL_RANDOMCARDDIR, isRandomCardDir()); + + delete conf; +} + +void KCardDialog::getGlobalDeck(QString& deck, bool& random) +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true); + conf->setGroup(CONF_GLOBAL_GROUP); + + if (!conf->hasKey(CONF_GLOBAL_DECK) || conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false)) { + deck = getRandomDeck(); + random = true; + } else { + deck = conf->readEntry(CONF_GLOBAL_DECK); + random = conf->readBoolEntry(CONF_GLOBAL_RANDOMDECK, false); + } + + delete conf; +} + +void KCardDialog::getGlobalCardDir(QString& dir, bool& random) +{ + KSimpleConfig* conf = new KSimpleConfig(QString::fromLatin1("kdeglobals"), true); + conf->setGroup(CONF_GLOBAL_GROUP); + + if (!conf->hasKey(CONF_GLOBAL_CARDDIR) || conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false)) { + dir = getRandomCardDir(); + random = true; + } else { + dir = conf->readPathEntry(CONF_GLOBAL_CARDDIR); + random = conf->readBoolEntry(CONF_GLOBAL_RANDOMCARDDIR, false); + } + + delete conf; +} + +void KCardDialog::init() +{ + static bool _inited = false; + if (_inited) + return; + KGlobal::dirs()->addResourceType("cards", KStandardDirs::kde_default("data") + QString::fromLatin1("carddecks/")); + + KGlobal::locale()->insertCatalogue("libkdegames"); + _inited = true; +} + +#include "kcarddialog.moc" diff --git a/libkdegames/kcarddialog.h b/libkdegames/kcarddialog.h new file mode 100644 index 00000000..b32fd636 --- /dev/null +++ b/libkdegames/kcarddialog.h @@ -0,0 +1,345 @@ +/* + This file is part of the KDE games library + Copyright (C) 2000 Martin Heni (martin@heni-online.de) + + 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 __KCARDDIALOG_H_ +#define __KCARDDIALOG_H_ + +#include <qstring.h> +#include <kdialogbase.h> +#include <qmap.h> // TODO: remove - it is in kcarddialog.cpp now; left here for source compatibility + +#include <kdemacros.h> +class QIconViewItem; + +class KConfig; + +class KCardDialogPrivate; + +/** + * @short A carddeck selection dialog for card games. + * + * The KCardDialog provides a dialog for interactive carddeck selection. + * It gives cardgames an easy to use interface to select front and + * back of the card sets. As card sets the KDE default cardsets are + * offered as well as used specified ones. + * + * In most cases, the simplest + * use of this class is the static method KCardDialog::getCardDeck, + * which pops up the dialog, allows the user to select a carddeck, and + * returns when the dialog is closed. Only if you really need some specific + * behaviour or if you overwrite the dialog you need all the other access + * functions. + * + * Example: + * + * \code + * QString deck,card; + * int result = KCardDialog::getCardDeck(deck,card ); + * if ( result == KCardDialog::Accepted ) + * ... + * \endcode + * + * Here you can see a card dialog in action + * @image html "kcarddialog.png" KCarddialog + * + * KCardDialog::getCardDeck takes a lot of different parameters which are + * probably very useful. You can e.g. use the parameters randomDeck and + * randomCardDir to give the end-user the ability to choose a random + * deck/carddir. You have to save the value of those parameters in your config + * file - that's why the parameters are needed. + * + * You can also provide a KConfig pointer (usually kapp->config()). This + * pointer is used to store information about the dialog in an own group + * ("KCardDailog"). + * So you can just ignore the randomCardDir and randomDeck + * values and call KCardDialog::getConfigCardDeck instead. The only reson + * for this function is to read a previously written configuration and give you + * the information about it. This way you don't have to save any configuration + * on your own - KCardDialog does this for you. + * + * Another Parameter for KCardDialog::getCardDeck is scale. This pointer + * to a double variable contains the scaling factor the user has chosen in the + * dialog (the scale box won't be shown if you don't provide this parameter). + * You might want to check out QPixmap::xFrom which gives you access to + * scaling. You can e.g. use + * \code + * QWMatrix m; + * m.scale(s,s); + * pixmap.xForm(m); + * \endcode + * to scale your pixmap. + * + * @author Martin Heni <martin@heni-online.de> + * @version $Id$ + */ +class KDE_EXPORT KCardDialog : public KDialogBase +{ + Q_OBJECT + +public: + + /** + * @li @p Both - both are shown + * @li @p NoDeck - The deck (back) selection is not shown + * @li @p NoCards - The cards (front) selection is not shown + */ + enum CardFlags { Both=0, NoDeck=0x01, NoCards=0x02 }; + + /** + * Constructs a card deck selection dialog. + * + * @param parent The parent widget of the dialog, if any. + * @param name The name of the dialog. + * @param flags Specifies whether the dialog is modal or not. + */ + KCardDialog (QWidget* parent = NULL,const char* name = NULL, + CardFlags flags = Both); + /** + * Destructs a card deck selection dialog. + */ + ~KCardDialog(); + + /** + * Creates a modal carddeck dialog, lets the user choose a deck, + * and returns when the dialog is closed. + * + * @param deck a reference to the filename used as backside of the + * cards. It is an absolute path and can directly be loaded as + * pixmap. + * + * @param carddir a reference to the directory name used as front of the + * cards. The directory contains the card images as 1.png to 52.png + * + * @param parent an optional pointer to the parent window of the dialog + * + * @param flags what to show + * + * @param randomDeck if this pointer is non-zero, *ok is set to TRUE if + * the user wants a random deck otherwise to FALSE. Use this in the + * config file of your game to load a random deck on startup. + * See @ref getRandomDeck() + * + * @param randomCardDir if this pointer is non-zero, *ok is set to TRUE if + * the user wants a random card otherwise to FALSE. + * Use this in the config file of your game to load a random card + * foregrounds on startup. + * See @ref getRandomCardDir() + * + * @param scale If non-zero a box is shown which provides the possibility to + * change the size of the cards. The desired scaling factor is returned to the + * game in this variable. + * + * @param conf If non-zero KCardDialog reads the initial settings for + * this dialog from the applications config file and stores them there + * when the dialog is closed. You can just use getConfigCardDeck + * to get the deck/carddir the user selected before. Note that the + * parameters randomDeck and randomCardDir overwrite the initial settings from the + * config file. + * + * @return QDialog::result(). + */ + static int getCardDeck(QString &deck,QString &carddir, QWidget *parent=0, + CardFlags flags=Both, bool* randomDeck=0, + bool* randomCardDir=0, double* scale=0, KConfig* conf=0); + + /** + * Read the configuration from the applications rc file and put the + * previously chosen deck/frontside in the parameter deck and carddir. + * + * You probably want to use this function on startup of your program so that + * the user gets exactly the card/frontside he/she chose before. Note that + * you don't have to care whether the user wants to get a random carddeck or + * not as this function takes care of this. + * @param conf The config file to read from + * @param deck This will contain the chosen deck from the config file (or a + * random deck if this is desired according to the config) + * @param cardDir This will contain the chosen cardDir from the config file (or a + * random cardDir if this is desired according to the config) + * @param scale The scaling factor (usually 1) + **/ + static void getConfigCardDeck(KConfig* conf, QString& deck, QString& cardDir, double& scale); + + /** + * Returns the default path to the card deck backsides. You want + * to use this usually before the user used the card dialog the first + * time to get a default deck. You can assume that + * \code + * getDefaultDeckPath() + * \endcode + * is a valid deck. + * + * @return The default path + */ + static QString getDefaultDeck(); + + /** + * Returns the default path to the card frontsides. You want + * to use this usually before the user used the card dialog the first + * time to get an default deck. You can assume that + * \code + * getCardPath(getDefaultCardPath(), *) + * \endcode + * are valid cards for * from 1 to 52. + * + * @return returns the path to the card directory + */ + static QString getDefaultCardDir(); + + /** + * Returns the path to the card frontside specified in dir carddir + * + * @param index the card to open + * @param carddir The carddir which's path shall be searched for + * @return returns the path to the card + */ + static QString getCardPath(const QString &carddir, int index); + + /** + * Returns a random deck in deckPath() + * @return A random deck + **/ + static QString getRandomDeck(); + + /** + * Returns a random directory of cards + * @return A random card dir + **/ + static QString getRandomCardDir(); + + /** + * Show or hides the "random backside" checkbox + * @param s Shows the checkbox if true otherwise hides it + **/ + void showRandomDeckBox(bool s); + + /** + * Show or hides the "random foreside" checkbox + * @param s Shows the checkbox if true otherwise hides it + **/ + void showRandomCardDirBox(bool s); + + /** + * Returns the chosen deck, which is a valid path to a imagefile. + * + * @return The deck + */ + const QString& deck() const; + + /** + * Sets the default deck. + * @param file The full path to an image file + */ + void setDeck(const QString& file); + + /** + * @return The chosen card directory + */ + const QString& cardDir() const; + + /** + * Sets the default card directory. + * @param dir The full path to an card directory + */ + void setCardDir(const QString& dir); + + /** + * @return the flags set to the dialog + */ + CardFlags flags() const; + + /** + * Creates the default widgets in the dialog. Must be called after + * all flags are set. This is only needed if you do NOT use the + * getCardDeck static function which provides all calls for you. + */ + void setupDialog(bool showResizeBox = false); + + /** + * @return TRUE if the selected deck is a random deck (i.e. the user checked + * the random checkbox) otherwise FALSE + **/ + bool isRandomDeck() const; + + /** + * @return TRUE if the selected carddir is a random dir (i.e. the user + * checked the random checkbox) otherwise FALSE + **/ + bool isRandomCardDir() const; + + /** + * @return TRUE if the global checkbox was selected + **/ + bool isGlobalDeck() const; + + /** + * @return TRUE if the global checkbox was selected + **/ + bool isGlobalCardDir() const; + + /** + * @return The scaling factor of the card pixmap + **/ + double cardScale() const; + + /** + * Load the default settings into the dialog (e.g. whether the "use random + * deck" checkbox is checked or not). + **/ + void loadConfig(KConfig* conf); + + /** + * Saves the KCardDialog config into a config file. This should be the + * applications config file - KCardDialog creates an own group + * ("KCardDialog"). These settings are used by @ref loadConfig and @ref + * getConfigCardDeck. + **/ + void saveConfig(KConfig* conf); + + +protected: + void insertCardIcons(); + void insertDeckIcons(); + + static void getGlobalDeck(QString& cardDir, bool& random); + static void getGlobalCardDir(QString& deck, bool& random); + + static QString getDeckName(const QString& desktop); + + /** + * @return the groupname used by functions like @ref saveConfig and @ref + * loadConfig. + **/ + static QString group(); + +protected slots: + void slotDeckClicked(QIconViewItem *); + void slotCardClicked(QIconViewItem *); + void slotRandomCardDirToggled(bool on); + void slotRandomDeckToggled(bool on); + void slotCardResized(int); + void slotDefaultSize(); + void slotSetGlobalDeck(); + void slotSetGlobalCardDir(); + +private: + static void init(); + + KCardDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kcarddialog.png b/libkdegames/kcarddialog.png Binary files differnew file mode 100644 index 00000000..3446c461 --- /dev/null +++ b/libkdegames/kcarddialog.png diff --git a/libkdegames/kchat.cpp b/libkdegames/kchat.cpp new file mode 100644 index 00000000..d4ffd7ec --- /dev/null +++ b/libkdegames/kchat.cpp @@ -0,0 +1,115 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 <klocale.h> +#include <kdebug.h> + +#include "kchat.h" + +class KChatPrivate +{ +public: + KChatPrivate() + { + } + + bool mAutoAddMessages; + + QMap<int, QString> mPlayerMap; + int mPlayerId; + int mFromId; +}; + +KChat::KChat(QWidget* parent, bool twoPlayerGame) : KChatBase(parent, twoPlayerGame) +{ + init(); +} + +KChat::~KChat() +{ + kdDebug(11000) << "DESTRUCT KChat " << this << endl; + delete d; +} + +void KChat::init() +{ + kdDebug(11001) << "INIT KChat " << this << endl; + d = new KChatPrivate; + d->mAutoAddMessages = true; + d->mPlayerId = 1; + d->mFromId = 1; +} + +void KChat::setFromNickname(const QString& n) +{ d->mFromId = addPlayer(n); } +const QString& KChat::fromName() const +{ return player(fromId()); } +void KChat::setAutoAddMessages(bool add) +{ d->mAutoAddMessages = add; } +bool KChat::autoAddMessages() const +{ return d->mAutoAddMessages; } +int KChat::uniqueId() +{ return d->mPlayerId++; } +int KChat::fromId() const +{ return d->mFromId; } +const QString& KChat::player(int id) const +{ return d->mPlayerMap[id]; } + +void KChat::returnPressed(const QString& text) +{ + int id = fromId(); + if (id < 0) { + // don't return - just display "unknown" as name + kdWarning(11000) << "KChat: no fromNickname has been set!" << endl; + } + emit signalSendMessage(id, text); + if (autoAddMessages()) { + QString p = player(id); + if (p.isNull()) { + p = i18n("Unknown"); + } + kdDebug(11000) << "auto adding message from player " << p << " ;id=" << id << endl; + addMessage(p, text); + } +} + +int KChat::addPlayer(const QString& nickname) +{ + int id = uniqueId(); + d->mPlayerMap.insert(id, nickname); + return id; +} + +void KChat::removePlayer(int id) +{ + d->mPlayerMap.remove(id); +} + +void KChat::removePlayer(const QString& nickname) +{ + QMap<int, QString>::Iterator it; + for (it = d->mPlayerMap.begin(); it != d->mPlayerMap.end(); ++it) { + if (it.data() == nickname) { + d->mPlayerMap.remove(it); + } + } +} + + +#include "kchat.moc" diff --git a/libkdegames/kchat.h b/libkdegames/kchat.h new file mode 100644 index 00000000..db479bc0 --- /dev/null +++ b/libkdegames/kchat.h @@ -0,0 +1,147 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KCHAT_H__ +#define __KCHAT_H__ + +#include <qstring.h> + +#include "kchatbase.h" +#include <kdemacros.h> + +class KChatPrivate; + +/** + * @short A chat widget for non-KGame games + * + * Docu is TODO + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KChat : public KChatBase +{ + Q_OBJECT +public: + /** + * @param parent The parent widget for this widget. + * @param twoPlayerGame If true the combo box where the player can + * choose to send to a single player or to all players will not be added + * as you will hardly need it in 2-player games. + **/ + KChat(QWidget* parent, bool twoPlayerGame = false); + + virtual ~KChat(); + + /** + * Equivalent to player(fromId()) + * @return The name that will be shown for messages from this widget. + * That is the string from @ref setFromNickname + **/ + virtual const QString& fromName() const; + + /** + * This sets the name that will be shown on all chat widgets if this + * widget sends a message. See signalSendMessage + * @param name The name of the player owning this widget + **/ + void setFromNickname(const QString& name); + +// TODO: +// void setPlayerList(QIntDict<QString>);// use this for non-KGame use + + /** + * Adds a player nickname. + * @return The unique ID of the player + **/ + int addPlayer(const QString& nick); + + /** + * Removes all players with this nickname. Better don't use this as it + * will remove *all* players with this nickname. Save the id instead and + * call removePlayer(id) + * @param nick The nickname of the removed players + **/ + void removePlayer(const QString& nick); + + /** + * Removes the player with this id, as returned by @ref addPlayer + * @param id The id of the player to be removed + **/ + void removePlayer(int id); + + + /** + * @return true if the messages which will be sent from here will be + * added automatically using @ref KChatBase::addMessage. See also @ref + * setAutoAddMessages + **/ + bool autoAddMessages() const; + + /** + * Usually the messages which will be sent from here (see @ref + * signalSendMessage) are added autmatically to this widget. But under + * some circumstances that would be very unhandy. So you can deactivate + * this behaviour here and call @ref KChatBase::addMessage yourself + * @param add If true (default) messages sent from here will be added + * automatically. Otherwise you will have to add them yourself + **/ + void setAutoAddMessages(bool add); + + /** + * @return The nickname of the player which belongs to this id + **/ + const QString& player(int id) const; + + /** + * @return The ID that belongs to the local player. + * @see setFromNickname + **/ + int fromId() const; + + +signals: + /** + * This signal is emitted when the player wants to send a message. + * + * The message is added automatically using @ref KChatBase::addMessage if @ref + * autoAddMessages is enabled. + * @param id The id of the player who sends the message - see + * setFromNickname and player + * @param msg The message itself + **/ + void signalSendMessage(int id, const QString& msg); + +protected: + /** + * This emits @ref signalSendMessage and, if @ref autoAddMessages is + * true, calls @ref KChatBase::addMessage + **/ + virtual void returnPressed(const QString&); + + /** + * The Id of the next player. Incremented after every call. + **/ + int uniqueId(); + +private: + void init(); + + KChatPrivate* d; +}; + +#endif diff --git a/libkdegames/kchatbase.cpp b/libkdegames/kchatbase.cpp new file mode 100644 index 00000000..4ccd7b08 --- /dev/null +++ b/libkdegames/kchatbase.cpp @@ -0,0 +1,530 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 "kchatbase.h" + +#include <klineedit.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kdebug.h> + +#include <qlayout.h> +#include <qcombobox.h> +#include <qpainter.h> + +class KChatBaseTextPrivate +{ +public: + KChatBaseTextPrivate() + { + mNameFont = 0; + mMessageFont = 0; + } + + QString mName; + QString mMessage; + + const QFont* mNameFont; + const QFont* mMessageFont; +}; + + +KChatBaseText::KChatBaseText(const QString& name, const QString& message) : QListBoxText() +{ + init(); + setName(name); + setMessage(message); +} + +KChatBaseText::KChatBaseText(const QString& message) : QListBoxText() +{ + init(); + setMessage(message); +} + +KChatBaseText::~KChatBaseText() +{ + delete d; +} + +void KChatBaseText::init() +{ + d = new KChatBaseTextPrivate; +} + +void KChatBaseText::setName(const QString& n) +{ +// d->mName = n; + d->mName = QString("%1: ").arg(n); + setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting +} + +void KChatBaseText::setMessage(const QString& m) +{ + d->mMessage = m; + setText(QString("%1: %2").arg(name()).arg(message())); // esp. for sorting +} + +const QString& KChatBaseText::name() const +{ return d->mName; } + +const QString& KChatBaseText::message() const +{ return d->mMessage; } + +QFont KChatBaseText::nameFont() const +{ + if (d->mNameFont) { + return *d->mNameFont; + } else if (listBox()) { + return listBox()->font(); + } else { + return QFont(); + } +} + +QFont KChatBaseText::messageFont() const +{ + if (d->mMessageFont) { + return *d->mMessageFont; + } else if (listBox()) { + return listBox()->font(); + } else { + return QFont(); + } +} + +void KChatBaseText::setNameFont(const QFont* f) +{ d->mNameFont = f; } + +void KChatBaseText::setMessageFont(const QFont* f) +{ d->mMessageFont = f; } + +void KChatBaseText::paint(QPainter* painter) +{ + QFontMetrics fm = painter->fontMetrics(); + painter->setFont(nameFont()); + painter->drawText(3, fm.ascent() + fm.leading()/2, name()); + painter->setFont(messageFont()); + painter->drawText(3 + QFontMetrics(nameFont()).width(name()), fm.ascent() + fm.leading()/2, message()); +} + +int KChatBaseText::width(QListBox* lb) const +{ + int w = 0; + if (lb) { + w += 6; + w += QFontMetrics(nameFont()).width(name()); + w += QFontMetrics(messageFont()).width(message()); + } +// int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0; // QT orig + return QMAX(w, QApplication::globalStrut().width()); +} + +int KChatBaseText::height(QListBox* lb) const +{ + int h = 0; + if (lb) { + h += 2; + // AB: is lineSpacing still correct? + if (QFontMetrics(nameFont()).lineSpacing() > QFontMetrics(messageFont()).lineSpacing()) { + h += QFontMetrics(nameFont()).lineSpacing(); + } else { + h += QFontMetrics(messageFont()).lineSpacing(); + } + } +// int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; // QT orig + return QMAX(h, QApplication::globalStrut().height()); +} + + + +class KChatBasePrivate +{ +public: + KChatBasePrivate() + { + mBox = 0; + mEdit = 0; + mCombo = 0; + + mAcceptMessage = true; + mMaxItems = -1; + } + QListBox* mBox; + KLineEdit* mEdit; + QComboBox* mCombo; + bool mAcceptMessage; + int mMaxItems; + + QValueList<int> mIndex2Id; + + QFont mNameFont; + QFont mMessageFont; + QFont mSystemNameFont; + QFont mSystemMessageFont; +}; + +KChatBase::KChatBase(QWidget* parent, bool noComboBox) : QFrame(parent) +{ + init(noComboBox); +} + +KChatBase::~KChatBase() +{ +// kdDebug(11000) << "KChatBase: DESTRUCT (" << this << ")" << endl; + saveConfig(); + delete d; +} + +void KChatBase::init(bool noComboBox) +{ +// kdDebug(11000) << "KChatBase: INIT (" << this << ")" << endl; + + d = new KChatBasePrivate; + + setMinimumWidth(100); + setMinimumHeight(150); + + QVBoxLayout* l = new QVBoxLayout(this); + + d->mBox = new QListBox(this); + connect(d->mBox, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&)), + this, SIGNAL(rightButtonClicked(QListBoxItem*, const QPoint&))); + l->addWidget(d->mBox); + d->mBox->setVScrollBarMode(QScrollView::AlwaysOn); + d->mBox->setHScrollBarMode(QScrollView::AlwaysOff); + d->mBox->setFocusPolicy(QWidget::NoFocus); +// d->mBox->setSelectionMode(QListBox::NoSelection); + d->mBox->setSelectionMode(QListBox::Single); + + l->addSpacing(5); + + QHBoxLayout* h = new QHBoxLayout(l); + d->mEdit = new KLineEdit(this); + d->mEdit->setHandleSignals(false); + d->mEdit->setTrapReturnKey(true); + d->mEdit->completionObject(); // add the completion object + d->mEdit->setCompletionMode(KGlobalSettings::CompletionNone); + connect(d->mEdit, SIGNAL(returnPressed(const QString&)), this, SLOT(slotReturnPressed(const QString&))); + h->addWidget(d->mEdit); + + if (!noComboBox) { + d->mCombo = new QComboBox(this); + h->addWidget(d->mCombo); + addSendingEntry(i18n("Send to All Players"), SendToAll);//FIXME: where to put the id? + } + + d->mAcceptMessage = true; // by default + setMaxItems(-1); // unlimited + + if (kapp) { + // kapp might be NULL as well - in case we are in Qt designer. + readConfig(); + } +} + +bool KChatBase::acceptMessage() const +{ return d->mAcceptMessage; } + +void KChatBase::setAcceptMessage(bool a) +{ d->mAcceptMessage = a; } + +bool KChatBase::addSendingEntry(const QString& text, int id) +{ +//FIXME: is ID used correctly? +// do we need ID at all? +// what the hell should be here? +// d->mCombo->insertItem(i18n("Send to All Players"), SendToAll); + return insertSendingEntry(text, id); +} + +bool KChatBase::insertSendingEntry(const QString& text, int id, int index) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot add an entry to the combo box" << endl; + return false; + } + if (d->mIndex2Id.findIndex(id) != -1) { + kdError(11000) << "KChatBase: Cannot add more than one entry with the same ID! " << endl; + kdError(11000) << "KChatBase: Text="<<text<<endl; + return false; + } + d->mCombo->insertItem(text, index); + if (index < 0) { + d->mIndex2Id.append(id); + } else { + d->mIndex2Id.insert(d->mIndex2Id.at(index), id); + } + if (d->mIndex2Id.count() != (uint)d->mCombo->count()) { + kdError(11000) << "KChatBase: internal ERROR - local IDs do not match combo box entries!" << endl; + } + return true; +} + +int KChatBase::sendingEntry() const +{ + if (!d->mCombo) { + kdWarning(11001) << "Cannot retrieve index from NULL combo box" << endl; + return -1; + } + int index = d->mCombo->currentItem(); + if (d->mIndex2Id.at(index) == d->mIndex2Id.end()) { + kdWarning(11000) << "could not find the selected sending entry!" << endl; + return -1; + } + return d->mIndex2Id[index]; +} + +void KChatBase::removeSendingEntry(int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot remove an entry from the combo box" << endl; + return; + } + d->mCombo->removeItem(findIndex(id)); + d->mIndex2Id.remove(id); +} + +void KChatBase::changeSendingEntry(const QString& text, int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot change an entry in the combo box" << endl; + return; + } + int index = findIndex(id); + d->mCombo->changeItem(text, index); +} + +void KChatBase::setSendingEntry(int id) +{ + if (!d->mCombo) { + kdWarning(11000) << "KChatBase: Cannot set an entry in the combo box" << endl; + return; + } + d->mCombo->setCurrentItem(findIndex(id)); +} + +int KChatBase::findIndex(int id) const +{ + return d->mIndex2Id.findIndex(id); +} + +int KChatBase::nextId() const +{ + int i = SendToAll + 1; + while (d->mIndex2Id.findIndex(i) != -1) { + i++; + } + return i; +} + +void KChatBase::addItem(const QListBoxItem* text) +{ + d->mBox->insertItem(text); + int index = d->mBox->count() -1; + d->mBox->setBottomItem(index);//FIXME: don't scroll to bottom if user scrolled down manually + if (maxItems() >= 0 && d->mBox->count() > (unsigned int)maxItems()) { + d->mBox->removeItem(0); + } +} + +void KChatBase::addMessage(const QString& fromName, const QString& text) +{ +//maybe "%1 says: %2" or so + addItem(layoutMessage(fromName, text)); +} + +void KChatBase::addSystemMessage(const QString& fromName, const QString& text) +{ + addItem(layoutSystemMessage(fromName, text)); +} + +QListBoxItem* KChatBase::layoutMessage(const QString& fromName, const QString& text) +{ + //TODO: KChatBaseConfigure? - e.g. color + QListBoxItem* message; + if (text.startsWith("/me ")) { + // replace "/me" by a nice star. leave one space after the star + QPixmap pix; + pix.load(locate("data", QString::fromLatin1("kdegames/pics/star.png"))); + + //TODO KChatBasePixmap? Should change the font here! + + message = (QListBoxItem*)new QListBoxPixmap(pix, i18n("%1 %2").arg(fromName).arg(text.mid(3))); + } else { + // the text is not edited in any way. just return an item + KChatBaseText* m = new KChatBaseText(fromName, text); + m->setNameFont(&d->mNameFont); + m->setMessageFont(&d->mMessageFont); + message = (QListBoxItem*)m; + } + return message; +} + +QListBoxItem* KChatBase::layoutSystemMessage(const QString& fromName, const QString& text) +{ + //TODO: KChatBaseConfigure? - e.g. color + + // no need to check for /me etc. + KChatBaseText* m = new KChatBaseText(i18n("--- %1").arg(fromName), text); + m->setNameFont(&d->mSystemNameFont); + m->setMessageFont(&d->mSystemMessageFont); + return (QListBoxItem*)m; +} + +void KChatBase::slotReturnPressed(const QString& text) +{ + if (text.length() <= 0) { + // no text entered - probably hit return by accident + return; + } else if (!acceptMessage()) { + return; + } + d->mEdit->completionObject()->addItem(text); +// connect(d->mEdit, SIGNAL(returnPressed(const QString&)), comp, SLOT(addItem(const QString&))); + d->mEdit->clear(); + returnPressed(text); +} + +QString KChatBase::comboBoxItem(const QString& name) const +{ // TODO: such a function for "send to all" and "send to my group" + return i18n("Send to %1").arg(name); +} + +void KChatBase::slotClear() +{ + d->mBox->clear(); +} + +void KChatBase::setCompletionMode(KGlobalSettings::Completion mode) +{ d->mEdit->setCompletionMode(mode); } + +void KChatBase::setNameFont(const QFont& font) +{ + d->mNameFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setMessageFont(const QFont& font) +{ + d->mMessageFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setBothFont(const QFont& font) +{ + setNameFont(font); + setMessageFont(font); +} + +const QFont& KChatBase::nameFont() const +{ return d->mNameFont; } + +const QFont& KChatBase::messageFont() const +{ return d->mMessageFont; } + +void KChatBase::setSystemNameFont(const QFont& font) +{ + d->mSystemNameFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setSystemMessageFont(const QFont& font) +{ + d->mSystemMessageFont = font; + d->mBox->triggerUpdate(false); +} + +void KChatBase::setSystemBothFont(const QFont& font) +{ + setSystemNameFont(font); + setSystemMessageFont(font); +} + +const QFont& KChatBase::systemNameFont() const +{ return d->mSystemNameFont; } + +const QFont& KChatBase::systemMessageFont() const +{ return d->mSystemMessageFont; } + +void KChatBase::saveConfig(KConfig* conf) +{ + QString oldGroup; + if (!conf) { + conf = kapp->config(); + oldGroup = conf->group(); + conf->setGroup("KChatBase"); + } + + conf->writeEntry("NameFont", nameFont()); + conf->writeEntry("MessageFont", messageFont()); + conf->writeEntry("SystemNameFont", systemNameFont()); + conf->writeEntry("SystemMessageFont", systemMessageFont()); + conf->writeEntry("MaxMessages", maxItems()); + + if (!oldGroup.isNull()) { + conf->setGroup(oldGroup); + } +} + +void KChatBase::readConfig(KConfig* conf) +{ + QString oldGroup; + if (!conf) { + conf = kapp->config(); + oldGroup = conf->group(); + conf->setGroup("KChatBase"); + } + + setNameFont(conf->readFontEntry("NameFont")); + setMessageFont(conf->readFontEntry("MessageFont")); + setSystemNameFont(conf->readFontEntry("SystemNameFont")); + setSystemMessageFont(conf->readFontEntry("SystemMessageFont")); + setMaxItems(conf->readNumEntry("MaxMessages", -1)); + + if (!oldGroup.isNull()) { + conf->setGroup(oldGroup); + } +} + +void KChatBase::clear() +{ + d->mBox->clear(); +} + +void KChatBase::setMaxItems(int maxItems) +{ + d->mMaxItems = maxItems; + //TODO cut too many messages + if (maxItems == 0) { + clear(); + } else if (maxItems > 0) { + while (d->mBox->count() > (unsigned int)maxItems) { + d->mBox->removeItem(0); + } + } +} + +int KChatBase::maxItems() const +{ return d->mMaxItems; } + + +#include "kchatbase.moc" diff --git a/libkdegames/kchatbase.h b/libkdegames/kchatbase.h new file mode 100644 index 00000000..07f786f9 --- /dev/null +++ b/libkdegames/kchatbase.h @@ -0,0 +1,510 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KCHATBASE_H__ +#define __KCHATBASE_H__ + +#include <qframe.h> +#include <qstring.h> +#include <qlistbox.h> + +#include <kglobalsettings.h> +#include <kdemacros.h> +class QListBoxItem; + +class KConfig; + + +class KChatBaseTextPrivate; + +/** + * A QListBoxText implementation for KChatBase. + * + * It supports different colors, text fonts, ... + * + * A KChatBaseText consists of two text items: first the player part then the + * text part. This honors KChatBase::addMessage which also uses both. + * You can leave the player part out if you don't need it - there won't be any + * difference. + * + * You can set different colors and fonts for both parts. In the future there + * will probably some kind of KChatBaseDialog which offers the user the ability + * to configure things like color and font on the fly. + **/ +class KChatBaseText : public QListBoxText +{ +public: + + /** + * Constructs a KChatBaseText object with the player and text part + **/ + KChatBaseText(const QString& player, const QString& text); + + /** + * Constructs a KChatBaseText object without player part + **/ + KChatBaseText(const QString& text); + + /** + * Destruct a KChatBaseText object. + **/ + virtual ~KChatBaseText(); + + /** + * Set the name part of a message. A message is usually shown like + * "name: text" and you can change both parts independently. + * + * @see setMessage + * @param name The name of the sender (e.g. the player) + **/ + void setName(const QString& name); + + /** + * Set the text part of a message. A message is usually shown like + * "name: message" and you can change both parts independently. + * + * See also setName + * @param message The message that has been sent + **/ + void setMessage(const QString& message); + + /** + * @return The name part of a message. + * @see setName + **/ + const QString& name() const; + + /** + * @return The message text. + * @see setMessage + **/ + const QString& message() const; + + /** + * You can set the font of the sender name independently of the message + * itself. This font is used as the "name: " part of the message. + * @return The font that is used for the name + **/ + QFont nameFont() const; + + /** + * You can set the font of the message independently of the sender name. + * This font is used as the text part of the message. + * @return The font thaz is used for message text + **/ + QFont messageFont() const; + + /** + * Set the font for the name. + * @see nameFont + * @param font A pointer to the name font. Only the pointer is stored so + * don't delete the object. This way there is only one object for a lot + * of messages in memory. + **/ + void setNameFont(const QFont* font); + + /** + * Set the font for the message text. + * @see messageFont + * @param font A pointer to the message font. Only the pointer is stored so + * don't delete the object! This way there is only one object for a lot + * of messages in memory. + **/ + void setMessageFont(const QFont* font); + + /** + **/ + virtual int width(QListBox* ) const; + + /** + **/ + virtual int height(QListBox* ) const; + +protected: + /** + **/ + virtual void paint(QPainter*); + +private: + void init(); + +private: + KChatBaseTextPrivate* d; +}; + + +class KChatBasePrivate; + +/** + * @short The base class for chat widgets + * + * This is the base class for both KChat and KGameChat. KGameChat is the class + * you want to use if you write a KGame based game as it will do most things for + * you. KChat is more or less the same but not KGame dependant + * + * KChatBase provides a complete chat widget, featuring different sending means + * (e.g. "send to all", "send to player1", "send to group2" and so on - see + * addSendingEntry). It also provides full auto-completion capabilities (see + * KCompletion and KLineEdit) which defaults to disabled. The user can + * change this by right-clicking on the KLineEdit widget and selecting the + * desired behaviour. You can also change this manually by calling + * setCompletionMode. + * + * To make KChatBase useful you have to overwrite at least returnPressed. + * Here you should send the message to all of your clients (or just some of + * them, depending on sendingEntry). + * + * To add a message just call addMessage with the nickname of the player + * who sent the message and the message itself. If you don't want to use + * layoutMessage by any reason you can also call addItem directly. But you + * should better replace layoutMessage instead. + * + * You probably don't want to use the abstract class KChatBase directly but use + * one of the derived classess KChat or KGameChat. The latter is the + * widget of choice if you develop a KGame application as you don't have to + * do anything but providing a KGame object. + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KChatBase : public QFrame +{ + Q_OBJECT +public: + /** + * @param parent The parent widget for this widget. + * @param noComboBox If true then the combo box where the player can + * choose where to send messages to (either globally or just to some + * players) will not be added. + **/ + KChatBase(QWidget* parent, bool noComboBox = false); + + /** + * Destruct the KChatBase object + * + * Also calls saveConfig + **/ + virtual ~KChatBase(); + + enum SendingIds { + SendToAll = 0 + }; + + /** + * @return The name that will be shown for messages from this widget. Either the + * string that was set by setFromName or the name of the player + * that was set by setFromPlayer + **/ + virtual const QString& fromName() const = 0; + + /** + * Adds a new entry in the combo box. The default is "send to all + * players" only. This function is provided for convenience. You can + * also call inserSendingEntry with index = -1. + * See also nextId! + * @param text The text of the new entry + * @param id An ID for this entry. This must be unique for this + * entry. It has nothing to do with the position of the entry in the + * combo box. See nextId + * @return True if successful, otherwise false (e.g. if the id is already used) + **/ + bool addSendingEntry(const QString& text, int id); + + /** + * Inserts a new entry in the combo box. + * @param text The entry + * @param id An ID for this entry. This must be unique for this + * entry. It has nothing to do with the position of the entry in the + * combo box! + * @see nextId + * @param index The position of the entry. If -1 the entry will be added + * at the bottom + * @return True if successful, otherwise false (e.g. if the id is already used) + **/ + bool insertSendingEntry(const QString& text, int id, int index = -1); + + /** + * This changes a combo box entry. + * @param text The new text of the entry + * @param id The ID of the item to be changed + **/ + void changeSendingEntry(const QString& text, int id); + + /** + * This selects a combo box entry. + * @param id The ID of the item to be selected + **/ + void setSendingEntry(int id); + + /** + * Removes the entry with the ID id from the combo box. Note that id is + * _not_ the index of the entry! + * @see addSendingEntry + * @param id The unique id of the entry + **/ + void removeSendingEntry(int id); + + /** + * @return The _unique ID_ of the sending entry that has been selected. + * @see addSendingEntry + * + * Note that the entry "send to all" _always_ uses + * KChatBase::SendToAll, i.e. 0 as id! + **/ + int sendingEntry() const; + + /** + * @return The index of the combo box entry with the given id + **/ + int findIndex(int id) const; + + /** + * @return An ID that has not yet been used in the combo box. + * @see addSendingEntry + **/ + int nextId() const; + + /** + * @return True if this widget is able to send messages (see + * returnPressed) and false if not. The default implementation returns + * the value which has been set by setAcceptMessage (true by + * default) + **/ + virtual bool acceptMessage() const; + + /** + * See KLineEdit::setCompletionMode + **/ + void setCompletionMode(KGlobalSettings::Completion mode); + + /** + * Set the font that used used for the name part of a message. See also + * nameFont and setBothFont + **/ + void setNameFont(const QFont& font); + + /** + * Set the font that used used for the message part of a message. + * @see messageFont, setBothFont + **/ + void setMessageFont(const QFont& font); + + /** + * This sets both - nameFont and messageFont to font. You + * probably want to use this if you don't wish to distinguish between + * these parts of a message. + * @param font A font used for both nameFont and messageFont + **/ + void setBothFont(const QFont& font); + + /** + * Same as setNameFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemNameFont(const QFont& font); + + /** + * Same as setMessageFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemMessageFont(const QFont& font); + + /** + * Same as setBothFont but applies only to system messages. + * @see layoutSystemMessage + **/ + void setSystemBothFont(const QFont& font); + + /** + * This font should be used for the name (the "from: " part) of a + * message. layoutMessage uses this to set the font using + * KChatBaseText::setNameFont but if you want to overwrite + * layoutMessage you should do this yourself. + * @return The font that is used for the name part of the message. + **/ + const QFont& nameFont() const; + + /** + * This font should be used for a message. layoutMessage sets the + * font of a message using KChatBaseText::setMessageFont but if ypu + * replace layoutMessage with your own function you should use + * messageFont() yourself. + * @return The font that is used for a message + **/ + const QFont& messageFont() const; + + /** + * Same as systemNameFont but applies only to system messages. + * @see layoutSystemMessage + **/ + const QFont& systemNameFont() const; + + /** + * Same as systemMessageFont but applies only to system messages. + * @see layoutSystemMessage + **/ + const QFont& systemMessageFont() const; + + /** + * Save the configuration of the dialog to a KConfig object. If + * the supplied KConfig pointer is NULL then kapp->config() is used + * instead (and the group is changed to "KChatBase") butr the current + * group is restored at the end. + * @param conf A pointer to the KConfig object to save the config + * to. If you use 0 then kapp->config() is used and the group is changed + * to "KChatBase" (the current group is restored at the end). + **/ + virtual void saveConfig(KConfig* conf = 0); + + /** + * Read the configuration from a KConfig object. If the pointer is + * NULL kapp->config() is used and the group is changed to "KChatBase". + * The current KConfig::group is restored after this call. + **/ + virtual void readConfig(KConfig* conf = 0); + + /** + * Set the maximum number of items in the list. If the number of item + * exceeds the maximum as many items are deleted (oldest first) as + * necessary. The number of items will never exceed this value. + * @param maxItems the maximum number of items. -1 (default) for + * unlimited. + **/ + void setMaxItems(int maxItems); + + /** + * Clear all messages in the list. + **/ + void clear(); + + /** + * @return The maximum number of messages in the list. -1 is unlimited. See also + * setMaxItems + **/ + int maxItems() const; + + +public slots: + /** + * Add a text in the listbox. See also signalSendMessage() + * + * Maybe you want to replace this with a function that creates a nicer text + * than "fromName: text" + * + * Update: the function layoutMessage is called by this now. This + * means that you will get user defined outlook on the messages :-) + * @param fromName The player who sent this message + * @param text The text to be added + **/ + virtual void addMessage(const QString& fromName, const QString& text); + + /** + * This works just like addMessage but adds a system message. + * layoutSystemMessage is used to generate the displayed item. System + * messages will have a different look than player messages. + * + * You may wish to use this to display status information from your game. + **/ + virtual void addSystemMessage(const QString& fromName, const QString& text); + + /** + * This member function is mainly internally used to add a message. It + * is called by addMessage which creates a single text from a + * player name and a text. You will hardly ever use this - but if you + * need it it will be here ;-) + * + * But you may want to replace this in a derived class to create a + * non-default (maybe nicer ;-) ) behaviour + * @param item The QListBoxItem that is being added + **/ + virtual void addItem(const QListBoxItem* item); + + + /** + * This clears all messages in the view. Note that only the messages are + * cleared, not the sender names in the combo box! + **/ + void slotClear(); + + /** + * @param a If false this widget cannot send a message until + * setAcceptMessage(true) is called + **/ + void setAcceptMessage(bool a); + +signals: + /** + * Emitted when the user right-clicks on a list item. + * @see QListBox::rightButtonClicked + **/ + void rightButtonClicked(QListBoxItem*, const QPoint&); + +protected: + /** + * This is called whenever the user pushed return ie wants to send a + * message. + * + * Note that you MUST add the message to the widget when this function + * is called as it has already been added to the KCompletion object + * of the KLineEdit widget! + * + * Must be implemented in derived classes + * @param text The message to be sent + **/ + virtual void returnPressed(const QString& text) = 0; + + /** + * Replace to customise the combo box. + * + * Default: i18n("Send to %1).arg(name) + * @param name The name of the player + * @return The string as it will be shown in the combo box + **/ + virtual QString comboBoxItem(const QString& name) const; + + /** + * Create a QListBoxItem for this message. This function is not yet + * written usefully - currently just a QListBoxTex object is + * created which shows the message in this format: "fromName: text". + * This should fit most peoples needs but needs further improvements. + **/ + virtual QListBoxItem* layoutMessage(const QString& fromName, const QString& text); + + /** + * Create a QListBoxItem for this message. This does the same as + * layoutMessage but generates a system message. You might want to + * use such a message to display e.g. status information from your game. + * + * The default implementation just prepends "--- ". + **/ + virtual QListBoxItem* layoutSystemMessage(const QString& fromName, const QString& text); + +private slots: + /** + * Check if a text was entered and if acceptMessage returns true. + * Then add the message to the KCompletion object of the KLineEdit + * widget and call returnPressed + **/ + void slotReturnPressed(const QString&); + +private: + void init(bool noComboBox); + + KChatBasePrivate* d; +}; + +#endif diff --git a/libkdegames/kchatdialog.cpp b/libkdegames/kchatdialog.cpp new file mode 100644 index 00000000..bd196e7c --- /dev/null +++ b/libkdegames/kchatdialog.cpp @@ -0,0 +1,253 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 "kchatdialog.h" + +#include "kchatbase.h" + +#include <klocale.h> +#include <kfontdialog.h> + +#include <qlayout.h> +#include <qlabel.h> +#include <qpushbutton.h> + +class KChatDialogPrivate +{ + public: + KChatDialogPrivate() + { + mTextPage = 0; + + mNamePreview = 0; + mTextPreview = 0; + mSystemNamePreview = 0; + mSystemTextPreview = 0; + + mChat = 0; + } + + QFrame* mTextPage; + + QLabel* mNamePreview; + QLabel* mTextPreview; + QLabel* mSystemNamePreview; + QLabel* mSystemTextPreview; + + QLineEdit* mMaxMessages; + + KChatBase* mChat; +}; + +KChatDialog::KChatDialog(KChatBase* chat, QWidget* parent, bool modal) +// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) + : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) +{ + init(); + plugChatWidget(chat); +} + +KChatDialog::KChatDialog(QWidget* parent, bool modal) +// : KDialogBase(Tabbed, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) + : KDialogBase(Plain, i18n("Configure Chat"), Ok|Default|Apply|Cancel, Ok, parent, 0, modal, true) +{ + init(); +} + +KChatDialog::~KChatDialog() +{ + delete d; +} + +void KChatDialog::init() +{ + d = new KChatDialogPrivate; +// d->mTextPage = addPage(i18n("&Messages"));// not a good name - game Messages? + d->mTextPage = plainPage(); + QGridLayout* layout = new QGridLayout(d->mTextPage, 7, 2, KDialog::marginHint(), KDialog::spacingHint()); + +// General fonts + QPushButton* nameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); + connect(nameFont, SIGNAL(pressed()), this, SLOT(slotGetNameFont())); + layout->addWidget(nameFont, 0, 0); + QPushButton* textFont = new QPushButton(i18n("Text Font..."), d->mTextPage); + connect(textFont, SIGNAL(pressed()), this, SLOT(slotGetTextFont())); + layout->addWidget(textFont, 0, 1); + + QFrame* messagePreview = new QFrame(d->mTextPage); + messagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + QHBoxLayout* messageLayout = new QHBoxLayout(messagePreview); + layout->addMultiCellWidget(messagePreview, 1, 1, 0, 1); + + d->mNamePreview = new QLabel(i18n("Player: "), messagePreview); + messageLayout->addWidget(d->mNamePreview, 0); + d->mTextPreview = new QLabel(i18n("This is a player message"), messagePreview); + messageLayout->addWidget(d->mTextPreview, 1); + + layout->addRowSpacing(2, 10); + +// System Message fonts + QLabel* systemMessages = new QLabel(i18n("System Messages - Messages directly sent from the game"), d->mTextPage); + layout->addMultiCellWidget(systemMessages, 3, 3, 0, 1); + QPushButton* systemNameFont = new QPushButton(i18n("Name Font..."), d->mTextPage); + connect(systemNameFont, SIGNAL(pressed()), this, SLOT(slotGetSystemNameFont())); + layout->addWidget(systemNameFont, 4, 0); + QPushButton* systemTextFont = new QPushButton(i18n("Text Font..."), d->mTextPage); + connect(systemTextFont, SIGNAL(pressed()), this, SLOT(slotGetSystemTextFont())); + layout->addWidget(systemTextFont, 4, 1); + + QFrame* systemMessagePreview = new QFrame(d->mTextPage); + systemMessagePreview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); + QHBoxLayout* systemMessageLayout = new QHBoxLayout(systemMessagePreview); + layout->addMultiCellWidget(systemMessagePreview, 5, 5, 0, 1); + + d->mSystemNamePreview = new QLabel(i18n("--- Game: "), systemMessagePreview); + systemMessageLayout->addWidget(d->mSystemNamePreview, 0); + d->mSystemTextPreview = new QLabel(i18n("This is a system message"), systemMessagePreview); + systemMessageLayout->addWidget(d->mSystemTextPreview, 1); + +// message count + QLabel* maxMessages = new QLabel(i18n("Maximal number of messages (-1 = unlimited):"), d->mTextPage); + layout->addWidget(maxMessages, 6, 0); + d->mMaxMessages = new QLineEdit(d->mTextPage); + d->mMaxMessages->setText(QString::number(-1)); + layout->addWidget(d->mMaxMessages, 6, 1); +} + +void KChatDialog::slotGetNameFont() +{ + QFont font = nameFont(); + KFontDialog::getFont(font); + setNameFont(font); +} + +void KChatDialog::slotGetTextFont() +{ + QFont font = textFont(); + KFontDialog::getFont(font); + setTextFont(font); +} + +void KChatDialog::slotGetSystemNameFont() +{ + QFont font = systemNameFont(); + KFontDialog::getFont(font); + setSystemNameFont(font); +} + +void KChatDialog::slotGetSystemTextFont() +{ + QFont font = systemTextFont(); + KFontDialog::getFont(font); + setSystemTextFont(font); +} + +QFont KChatDialog::nameFont() const +{ + return d->mNamePreview->font(); +} + +QFont KChatDialog::textFont() const +{ + return d->mTextPreview->font(); +} + +QFont KChatDialog::systemNameFont() const +{ + return d->mSystemNamePreview->font(); +} + +QFont KChatDialog::systemTextFont() const +{ + return d->mSystemTextPreview->font(); +} + +void KChatDialog::plugChatWidget(KChatBase* widget, bool applyFonts) +{ + d->mChat = widget; + if (applyFonts && d->mChat) { + setNameFont(d->mChat->nameFont()); + setTextFont(d->mChat->messageFont()); + setSystemNameFont(d->mChat->systemNameFont()); + setSystemTextFont(d->mChat->systemMessageFont()); + setMaxMessages(d->mChat->maxItems()); + } +} + +void KChatDialog::configureChatWidget(KChatBase* widget) +{ + if (!widget) { + return; + } + widget->setNameFont(nameFont()); + widget->setMessageFont(textFont()); + + widget->setSystemNameFont(systemNameFont()); + widget->setSystemMessageFont(systemTextFont()); + + widget->setMaxItems(maxMessages()); +} + +void KChatDialog::slotOk() +{ + slotApply(); + KDialogBase::slotOk(); +} + +void KChatDialog::slotApply() +{ + configureChatWidget(d->mChat); +} + +void KChatDialog::setNameFont(QFont f) +{ + d->mNamePreview->setFont(f); +} + +void KChatDialog::setTextFont(QFont f) +{ + d->mTextPreview->setFont(f); +} + +void KChatDialog::setSystemNameFont(QFont f) +{ + d->mSystemNamePreview->setFont(f); +} + +void KChatDialog::setSystemTextFont(QFont f) +{ + d->mSystemTextPreview->setFont(f); +} + +void KChatDialog::setMaxMessages(int max) +{ + d->mMaxMessages->setText(QString::number(max)); +} + +int KChatDialog::maxMessages() const +{ + bool ok; + int max = d->mMaxMessages->text().toInt(&ok); + if (!ok) { + return -1; // unlimited is default + } + return max; +} + +#include "kchatdialog.moc" diff --git a/libkdegames/kchatdialog.h b/libkdegames/kchatdialog.h new file mode 100644 index 00000000..96b3eef2 --- /dev/null +++ b/libkdegames/kchatdialog.h @@ -0,0 +1,119 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KCHATDIALOG_H__ +#define __KCHATDIALOG_H__ + +#include <kdialogbase.h> +#include <kdemacros.h> + +class KChatBase; + +class KChatDialogPrivate; + +class KDE_EXPORT KChatDialog : public KDialogBase +{ + Q_OBJECT +public: + /** + * Construct a KChatDialog widget + **/ + KChatDialog(QWidget* parent, bool modal = false); + + /** + * Construct a KChatDialog widget which automatically configures the + * @ref KChatBase widget. You probably want to use this as you don't + * have to care about the configuration stuff yourself. + **/ + KChatDialog(KChatBase* chatWidget, QWidget* parent, bool modal = false); + + /** + * Destruct the dialog + **/ + ~KChatDialog(); + + /** + * @return The font that shall be used as the "name: " part of a normal + * message. + **/ + QFont nameFont() const; + + /** + * @return The font that shall be used for normal messages. + **/ + QFont textFont() const; + + /** + * @return The font that shall be used as the "name: " part of a system + * (game) message. + **/ + QFont systemNameFont() const; + + /** + * @return The font that shall be used for a system (game) message. + **/ + QFont systemTextFont() const; + + /** + * Set the widget that will be configured by the dialog. Use this if you + * don't want to configure the widget yourself. + * @param widget The chat widget that shall be configured + * @param Whether you want to have the current @ref KChatBase fonts as + * defaults in the dialog + **/ + void plugChatWidget(KChatBase* widget, bool applyFonts = true); + + /** + * Used to configure the chat widget according to the user settings. + * This is called automatically if @ref plugChatWidget was called + * before. + * @param widget The chat widget that shall be configured + **/ + void configureChatWidget(KChatBase* widget); + + /** + * @return The maximal allowed messages in the chat widget. -1 is + * unlimited + **/ + int maxMessages() const; + +protected slots: + void slotGetNameFont(); + void slotGetTextFont(); + void slotGetSystemNameFont(); + void slotGetSystemTextFont(); + + virtual void slotApply(); + virtual void slotOk(); + +private: + void setNameFont(QFont); + void setTextFont(QFont); + void setSystemNameFont(QFont); + void setSystemTextFont(QFont); + void setMaxMessages(int max); + +private: + void init(); + +private: + KChatDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/COMPAT b/libkdegames/kgame/COMPAT new file mode 100644 index 00000000..146d3a88 --- /dev/null +++ b/libkdegames/kgame/COMPAT @@ -0,0 +1,55 @@ +06.09.2001: replace the signal signalCreatePlayer by the virtual function + createPlayer. It has the same arguments but the return value + is the new player +06.09.2001: the KGameConfig dialog changes the parameter initConfigs from bool + to long. Use the ConfigOptions to specify what options you want + to have enabled. Default is all +06.09.2001: some int->Q_UINT32 in sender, receiver and player parameters. maybe + more will follow. +06.09.2001: KGameIO::signalPrepareMove(..., bool&) -> + KGameIO::signalPrepareMove(..., bool*): don't know why this was + necessary but it didn't work anymore... +16.09.2001: KGamePropertyHandler uses bool* for the sent parameter now. This is + because QT3 obviously doesn't honor referneces in signals/slots. + This might even be a QT bug. Bad situation - we use references + everywhere in KGame... hope nothing else is affecterd by this + problem (signalPrepareMove was fixed already by me) +18.09.2001: bool* for Key/Mouseevents and IOAdded in kgameio.h too +19.09.2001: Kgame:nextPlayer retunrs the KPlayer *nextplayer instead of bool +19.09.2001: gameOver() renamed to checkGameOver() !!!!! +18.09.2001: Question: Should the signal signalPlayerInput(QDataStream &,KPlayer *)) + be made a virtual function? + MH: This is done now. As this is a central function your programs will + not run anymore. Fix: rename your slot which is connected to the above + signal to playerInput() and return TRUE in it. This will make it 100% + compatible to the old version. I think this chagne is necessary especially + as a signal is of no use here as you cannot read twice from the same stream. + Therefore there can be only one function processing the input. If you really + need a signal, you can of course simply emit it in the overwritten playerInput + function +20.09.2001 playerInputFinished(void->KPlayer *) +--------------------- KGAME_ALPHA_1 --------------------- +06.10.2001 adding KGameNetwork::signalAdminStatusChanged - needed for + KGameDialog +06.10.2001 KGame::loadGame() doesn't call setPolicy() anymore! +08.10.2001 KGamePropertyList now honor policies! Use setPolicy(PolicyDirty) to + get the old behavior! + The behavior of KGamePropertyArray may have changed in this turn, + too! + The API stays the same. +11.10.2001 KGameDialogGeneralConfig now doesn't provide setMin/maxPlayers() + anymore. The game should manage this internally. layout() is + obsolete as well +18.10.2001 KPlayer::signalNetworkData contained QDataStream& instead of const + QByteArray& parameter (oops!). This is fixed now. All apps which + used this signal must be changed. +18.10.2001 KGame::sendProperty(), KGame::sendPlayerProperty(), + KPlayer::sendProperty() and related functions contain a "int msgid" + parameter. This is the id() of the property handler. This parameter + enables us to easily add any number of property handler to a game + just by connecting it to existing send slots and call + processMessage() in slotNetworkData() +03.11.2001 KPlayer::signalNetworkData now emits msgid-KGameMessage::IdUser just + like KGame::signalNetworkData does +06.11.2001 KGameDialog has some small improvements - easier and IMHO better + constructor code. Most code should be compatible :-) diff --git a/libkdegames/kgame/DESIGN b/libkdegames/kgame/DESIGN new file mode 100644 index 00000000..b1c48146 --- /dev/null +++ b/libkdegames/kgame/DESIGN @@ -0,0 +1,407 @@ +This document tries to describe the design of KGame - the KDE multiplayer +library. +This document has been written by: + Andreas Beckermann <b_mann@gmx.de> + M. Heni <martin@heni-online.de> + Burkhard Lehner <Burkhard.Lehner@gmx.de> + +This document is published under the terms of the GNU FDL + +!!! +Note that this is the initial version of this document and has not yet been +aproved by all core developers (and is far from being complete) +AB: please remove this comments as soon as all KGame hackers have read the +document +!!! + +Please refer the API documentation of every KGame class if you want up tp date +information. + + +0. Contents +----------- + +1. DEFINITIONS +1.1 Message Server +1.2 Client or Message Client +1.3 Master +1.4 Admin +1.5 Server +1.6 Player + +2. Game Negotiation (M.Heni 20.05.2001) + +AB: 3.x is obsolete! +3. Game Properties (Andreas Beckermann 28.07.2001) ( not yet completed ) +3.1 Using KGameProperty +3.2 Custom Classes +3.3 Concepts + +4. KGameIO (Andreas Beckermann 10.08.2001) + +5. Debugging (Andreas Beckermann 06.10.2001) TODO! +5.1 KGameDebugDialog +5.1.1 Debug KGame +5.1.3 Debug Messages + +--------------------------------------------------------------------- +1. DEFINITIONS +-------------- + +First we have to clear some words. The main expressions used in KGame which +need a definition are + +1.1 Message Server +1.2 Client or Message Client +1.3 Master +1.4 Admin +1.5 Server +1.6 Player + +The most important and confusing ones are Master, Admin and Server. We make +quite big differerences between those inside KGame. + +1.1 Message Server: +------------------- +A game has always exactly one object of this class, for local games as well as +for network games. For network games, this object can be on one of the users +processes (usually inside KGame), or it can also be on an independant computer, +that has no idea about what game is played on it. + +A KMessageClient object can connect to it. It's main purpose is transmitting +messages between KMessageClient objects. + +The Message Server is the main communication object. It is represented by the +class KMessageServer. Note that there is also a "Master" and a "Server" which +both differ heavily from the Message Server! + +1.2 Client, Message Client: +--------------------------- +Each process that wants to take part in the game must have a +KMessageClient object, that is connected to the Message Server. KGame creates +this object and connects it to the Messager Server, so that you usually don't +need to create these of your own. Even in a local game (no network) there +must be a message server and one message client connected to it. This is usually +done by the KGame object itself. + +Each message client has a unique ID number (a positive integer value, not zero). +The KMessageClient object, which does the communication with the Message Server +is called "Message Client" and to simplify the use we call any KGame object (or +even the game process) that is connected to a game (i.e. even the Master) just +"Client". + +The main purpose of a Client is to connect to a Master (i.e. to a game) and to +communicate with it. A client has always a KGame object. + +1.3 Master: +----------- +The process that contains the Message Server is called "Master". In any local +game this is the game process. The Message Server is started by KGame using +KGame::setMaster(true) which is automatically done on startup. The Message +Server is deleted automatically as soon as you connect to another Master. +So in most cases there is exactly one KGame object / Client which is Master. But +in one case there can be no KGame object / Client that is Master - if the +Message Server is started as an own process. This "Message-Server-only" process +is called "Master" then, although there is no KGame object which is Master. See +also the definition of Admin! + +1.4 Admin: +---------- +One (and only one) of the Clients is the Admin. He can configure the Message +Server and the game in general in several ways. He can limit the maximum number +of connected message clients and can drop the connection to some other clients, +as well as he can configure game specific ssettings (like max/min players, start +money, ...). The Admin also initializes newly connected Clients. If the Admin +himself disconnects, another Client becomes Admin (The Admin can himself elect +some other Client to become Admin. He himself loses that Admin status then). +An Admin is *alway* a KGame object. The Admin is usually the same as the Master, +but if the Master is an own process (i.e. the Message Server has been started +outside KGame) then Master and Admin differ. An Admin *must* be a KGame object +while the Master doesn't have to be. + +1.5 Server: +----------- +The definition of Server differs quite much from the definition of Master. +A Master just accepts connections and forwards messages. The Server on the other +side checks these messages, calculates results and sends the results to the +Clients. That means the Server does all game calculations and doesn't directly +forward the messages from one Clients to all other Clients. +KGamer makes it possible to write multiplayer games even without a Server. All +Clients just send their moves to the Master which forwards them to all Clients. +Now all Clients calculate the result. +E.g. in a poker game a player selects two of five cards to be exchanges and +clicks on "draw" then the client sends the message "Exchange Card-1 and Card-2" +to the Master. A no-Server solution forwards this to all Clients, and these +Clients exchange the cards of the player. Note that in a no-Server solution +(you can also see it as a "every-Client-is-a-Server solution") all Clients must +have the same random seed and must be of the same version, i.e. the result must +be the same on all Clients. +In a Server-Solution on the other hand the Master forwards the Message +("Exchange Card-1 and Card-2") to the Server only. This Server now calculates +the result, and sends the new cards back to the Client. +Both concepts have advantages and disadvantages. It is on you - the game +developer - to decide which way is better for you. +E.g. the Server-Solution makes it easier for you to write games. The version +must not necessarily be the same, you have one central computer which does the +calcultations. The No-Server-Solution on the other hand decreases network +traffik as the Clients just send their moves and all Clients can calculate the +reactions. I'm sure there are a lot of advantages/disadvantages more for both +concepts. + +1.6 Player: +----------- +A KPlayer object is always connected to a KGame object and represents a +player that participates the game. In a network game, every KPlayer object is +duplicated on every other KGame object connected to the message server with +virtual KPlayer objects. So at every time in the game, every KGame object has +the same number of KPlayer objects. + + +2. Game negotiation +------------------- +Upon connection of a client the admin and the client try to negotiate +the game setup. Basically this means the game of the admin is transferred +(saved) on the client. However, the client's players are added to the game +as far as possible. If the addition of the client's players would add more +players than allowed some players are inactivated. Which players are +inactivated depends on their networkPriority(). This procedure allows +easy replacement of players in a constant number game (e.g. chess). If +this feature is of no interest simply keep the priorities equal (all 0) +and the client will only add only players if the number of players is +less or equal the maximum player number. + +The following is the negotiation procedure as started by the connection +of a client. It is initiated in the negotiateNetworkGame() virtual function +of KGame: + +admin: client: +------------ ------------ +IdSetupGame + QINT16 Library + Version + QINT32 Application + cookie + IdSetupGameContinue; + QValueList<int> player id's + QValueList<int> network priority's + +IdGameLoad + all game data + +IdGameReactivate + QValueList<int> id's + +IdSyncRandom + int randomseed + + +3. Game Properties +------------------ +A very hard task in a network game is consistency. You have to achieve that all +properties of the game and of all players have the same value on all clients +every time. This is because +a) the user might be confused if he sees "Player has $0" on client A but +"Player has $10" on client B and +b) Often game handling depends on those values, e.g. if the example above +happens the computer might quit the game for the Player on client A because +he/she doesn't have enough money. But the game continues on client B. +Another not that easy task is the network protocol itself. You have to write +several send() and receive() functions which apply changed values of properties +to the local property. + +KGameProperty is designed to do all of this for you. KGameProperty is +implemented as a template so you can use it theoretically for every type of data +- even for your self defined classes. + + +3.1 Using KGameProperty +----------------------- +It is basically very easy to use a KGameProperty. You first create your own +class containing the property, e.g: +class MyGame : public KGame +{ +[...] +protected: + KGamePropertyInt money; + KGamePropertyQString name; + KGameProperty<AntotherClass> myProperty; +}; +KGamePropertyInt is just a typedef for KGameProperty<int> - just like +KGamePropertyQString. Now you need to register the properties in the constructor +of the class to the KGamePropertyHandler: +MyGame::MyGame() : KGame(myCookie) +{ + money.registerData(KGamePropertyBase::IdUser+1, dataHandler(), "Money"); + name.registerData(KGamePropertyBase::IdUser+2, this, "Name"); + myProperty.registerData(KGamePropertyBase::IdUser+3, dataHandler(), "MyProperty"); +} +-> You need to specify a *unique* ID. This ID must be greater than +KGamePropertyBase::IdUser. IDs below this are reserved for KGame. Probably this +will be changed so that you cannot use IDs below IdUser in the future. Then you +have to specify the dataHandler(). You can also use a KGame or KPlayer pointer. +This will automatically use KGame::dataHandler() or KPlayer::dataHandler(). +Finally you *can* provide a name for the property. This will be used for +debugging in KGameDebugDialog. If you want to save some memory you can leave +this out. +Note that if you use pointers to create the properties dynamically they are +*not* deleted automatically! You MUST delete them yourself! +Now you can use the KGameProperty like every variable else. See also Section +"3.3 Concepts" for restrictions in use. + +3.2 Custom Classes +------------------ +To make custom classes possible you have to implement several operators for your +them: you need at least << and >> for QDataStream as well as "==" for your own +class. To overload the "<<" you would e.g. do something like this: +QDataStream& operator<<(QDataStream& stream, MyData& data) +{ + int type = data.type; + QString name = data.name; + stream << type << name; + return stream; +} +So you basically just have to split your class to several basic types and stream +them. + +3.3 Concepts +------------ +You can use KGameProperty basically in two completely different ways. You can +also use a mixture of both but this is not recommended. The default behaviour +and therefore also the recommended is the "clean" way: +a) Always Consistent. This means that a KGameProperty has always the same value +on *every* client. This is achieved by using KGameProperty::send() whenever you +want to change the value using "=". You can still use changeValue() or +setLocal() but send() will be the default. If you use send() then the value of +the property does *NOT* change immediately. It is just sent to the +KMessageServer which forwards the value to all clients. As soon as the new value +is received from the message server the KGamePropertyHandler (a collection class +for KGameProperty) calls KGameProperty::load() and changes the value of the +property. So the game first has to go into the event loop, where the message is +received. This means to you that you cannot do this: +myIntProperty = 10; +int value = myIntProperty; +As myIntPoperty still has the old value when "value = myIntProperty" is called. +This might seem to be quite complex, but +KGamePropertyHandler::signalPropertyChanged() is emitted whenever a new value is +assigned so you can connect to this and work immediately with the new value. +You gain the certainty that the value is the same on every client every time. +That will safe you a lot of time debugging! +Another way is the "dirty" way: +b) Not Always Consistent. Sometimes you really *want* to do something like +myIntProperty = 10; +int value = myIntProperty; +but this is not possible with the default behaviour. If you call +KGameProperty::setAlwaysConsistent(false) in the constructor (right after +registerData()) you get another behaviour. "=" means changeValue() now. +changeValue() also uses send() to change the value but additionally calls +setLocal() to create a local copy of the property. This copy now has the value +you supplied with "=" and is deleted again as soon as any value from the network +is received. + +4. KGameIO +---------- +The class KGameIO is used to let the players communicate with the server. You +can plug as many KGameIO objects into a player as you want, e.g. you can plug a +KGameMouseIO and a KGameKeyIO into a player so that you can control the player +with the mouse and the keyboard - e.g. in a breakout game. +You can probably see the advantage: as most of the control stuff is common in a +lot of games you can use the same IO class in many different games with very +small adjustments. +You could also put all the IO stuff directly into your KPlayer object, like +sendBet(int money) for a poker game. But there is a major disadvantage and I'm +very sure you don't want to use a KPlayer object for your IO stuff as soon as +you know which disadvantage: +KGameIO is designed to be able to switch between different IOs "on the fly". So +you might have a KGamePlayerIO, derived from KGameIO, for your game. But now +this player (who "owns"/uses the KGamePlayerIO) leaves the game (e.g. because he +was a remote player). So now the game would be over for every player as one +player is now out of order. But with KGameIO you can just let any of the +remaining clients create a KGameComputerIO and plug this into the player. So the +player now is controlled by the computer and the game can continue. + +Think about it! You don't have to care about removing players when a player +leaves as you can just replace it! The same works the other way round: imagine a +game with 10 player (e.g. 5 human and 5 computer players) that has already +started. You cannot add any further players without restarting. So if there are +any additional player you can just call KPlayer::removeGameIO() which removes +the IO of a computer player and then call KPlayer::addGameIO() for the same +player which adds a GameIO for new human player. That's all! + +To achieve this you just have to make sure that you make *all* of your IO +operations through a KGameIO! So instead of using MyPlayer::sendBet(int money) +you should use something like MyIO::sendBet(). The amount of money would +probably be calculated by the game IO itself. + + + +5. Debugging +------------ +The general debugging concept (if there is one at all) or general debugging +hints are not yet written. Feel free to do so + +5.1 KGameDebugDialog +-------------------- +A nice way of debugging a KGame based game is the KGameDebugDialog. Basically +all you have to do is to add something like "Debug" to your game's menu and add +a slot like +slotDebug() +{ + KGameDebugDialog* dialog = new KGameDebugDialog(mGame, this); + connect(dialog, SIGNAL(finished()), dialog, SLOT(slotDelayedDestruct())); + dialog->show(); +} +that's it. +You can now click on that menu entry and you get a non-modal dialog where you +can start to debug :-) +The dialog consist of several pages. You can easily add your own using +KDialogBase::addVBoxPage() (for example). + +5.1.1 Debug KGame +----------------- +The first page, "Debug KGame" shows on the left most or even all status values of +KGame. That contains e.g. minPlayers(), isAdmin(), gameStatus(), ... +The right side is probably the more important one. It lists *all* KGameProperties +which have been inserted to this KGame object (only to this KGame object - not +the ones that have been added to the players!). Most of the status variables of +the left side are here again as they are implemented as KGameProperty. You can +see the name of the property (together with its ID), its value and the policy +this property uses. Note that "unknwon" will be displayed as name of the +property if you haven't supplied one. See KGamePropertyBase::registerData() for +info. You probably always want to supply a name for the property to debug it +easily. In the future there will be something like +KGamePropertyHandler::setDebug() so that you can switch off debugging and save +the memory of the names in a release version. +For as long as you use standard types for your properties (int, long, bool, +...) you should always be able to see the value of the property. If you just see +"unknown" then this type has not been implemented. You can connect to the signal +KGamePropertyHandler::signalRequestValue() and supply a QString with the value +yourself. If you do so for a standard type please also submit a bug report! + +Currently the dialog does *not* update automatically! So you alway have to click +the "update" button when you want a current value. There are several reasons for +this (and one of them is that i'm too lazy to implement the update ;)). E.g. +often (very often) a property is just in the background - stores e.g. the +available money in a game. But you don't want it to update whenever the value +changes (a player receives/pays money) but only when the value on the screen +changes. + +5.1.2 Debug Players +------------------- +This page consists of three widgets. On the very left there is a list of all +players in the game. Only the IDs are displayed to save space. If you click one +the other widgets are filled with content. These widgets are quite much the same +as the ones in "Debug KGame" - the left shows the value of the functions and the +right one displays all KProperties of a player. Not much to say here - except: +See "Debug KGame". + +If you change to another player the value are also updated. + +5.1.3 Debug Messages +-------------------- +This page is probably not as important as the other ones. It displays *every* +message that is sent through the KGame object. As a KGameProperry also send +messages you probably get a lot of them... +You can exclude message IDs from being displayed (e.g. all game properties). +You can also change the sorting of the list to see all messages of a certain ID. +The default is to sort by time (which is displayed on the left side). + diff --git a/libkdegames/kgame/Makefile.am b/libkdegames/kgame/Makefile.am new file mode 100644 index 00000000..e0117780 --- /dev/null +++ b/libkdegames/kgame/Makefile.am @@ -0,0 +1,29 @@ + +noinst_LTLIBRARIES = libkgame.la + +# compile-order doesn't matter here but maybe we will split these section soon + +KGAME = kgame.cpp kplayer.cpp kgamenetwork.cpp kgameproperty.cpp \ + kgamemessage.cpp kgameio.cpp kgameprocess.cpp kgamechat.cpp \ + kgamepropertyhandler.cpp kgameerror.cpp kgamesequence.cpp +KGAME_H = kgame.h kplayer.h kgamenetwork.h kgameproperty.h kgamemessage.h \ + kgameio.h kgameprocess.h kgamepropertyarray.h \ + kgamepropertylist.h kgamechat.h kgamepropertyhandler.h \ + kgameerror.h kgamesequence.h kgameversion.h + +KMESSAGE = kmessageio.cpp kmessageserver.cpp kmessageclient.cpp +KMESSAGE_H = kmessageio.h kmessageserver.h kmessageclient.h + +libkgameincludedir=$(includedir)/kgame +libkgame_la_SOURCES = $(KMESSAGE) $(KGAME) + +libkgameinclude_HEADERS = $(KMESSAGE_H) $(KGAME_H) + +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) +METASOURCES = AUTO + +SUBDIRS = . dialogs + +messages: +# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + diff --git a/libkdegames/kgame/README.LIB b/libkdegames/kgame/README.LIB new file mode 100644 index 00000000..512edbac --- /dev/null +++ b/libkdegames/kgame/README.LIB @@ -0,0 +1,12 @@ +some thoughts and comments about the lib - usually for KGame hackers + +- setMin/MaxPlayers() etc. use KGameProperty::changeValue() which is slightly + unclean but as these functions can only called by the ADMIN it doesn't matter. +- AB: KGamePropertyList && KGamePropertyArray: + for PolicyClean||PolicyDirty the values are streamed into a QDataStream as usual + for PolicyDirty||PolicyLocal the values are streamed as well but + additionally command() is called immediately. The values are read from + the stream there. This is some kind of performance loss as it would be + faster *not* to stream it but imediately call e.g. insert(). But it will + probably save a *lot* of bugs! + diff --git a/libkdegames/kgame/TODO b/libkdegames/kgame/TODO new file mode 100644 index 00000000..2f100b8a --- /dev/null +++ b/libkdegames/kgame/TODO @@ -0,0 +1,41 @@ +- 28.02.2001: Direct computer player for kpoker like games support needs to be + improved. UPDATE (01/10/06): but what is needed there? +- 05.03.2001: Documentation. I am thinking of an explaination of the + class + methods and example code for the "key" methods. A sample + implementation in a small game (like the current kdenonbeta/kgame + but with a real sense - mabye use the QT tic-tac-toe example?) + would be very great (this could be stuff of a tutorial instead of + KGame documentation) + MH: Even better idea +- 03.06.2001: can KGameNetwork::sendSystemMessage be made protected (maybe using + friends)? sendSystenMessage AND sendMessage is very confusing to + the user... +- 03.06.2001: can we translate the group of a KPlayer? Probably not as there are + no international connections possible then... maybe a group id? +- 05.06.2001: KGameDialog::saveConfig(KConfig*) might be useful (as well as + KGameDialog::loadConfig(KConfig*). Should set an own group in the + config file (setGroup("KGameDialog")). Problem: shalll network + settings be saved? Could be used for startup configuration (i.e. + load the config of the previous game) otherwise. +- 21.06.2001: KPlayerPropertyArray does not yet support at() and operator[] + assignments. Need to check whether the method from QBitArray + can be applied +- 02.04.2001: VERY DANGEROUS: property1=property2 does NOT assign the values, e.g. int + but assignes the whole property, i.e. you have then two properties with + the same id and everything is wrong + 01/09/09: FIXED! (AB) TODO: check if this behavior also appears in + KGamePropertyList and KGamePropertyArray. Althogh this should not + be the case +- 23.09.2001: does the virtual destructor make sense for KGamePropertyBase? +- 29.09.2001: GGZ integration. I (Andi) already volunteered for this - it's just + here so that I don't forget it +- 06.10.2001: add KGamePropertyHandler::setDebug(false) to clear all debug names + (and to not accept new names) of KGameProperty. Will save some + memory +- 06.10.2001: If one kicks a player (in KGameDialog) the client should be kicked + as well. Perhaps always disconnect a client when all players from + it have disappeared? +- 07.10.2001: display (List) or (Array) for KGameProperty[List|Array] in + KGameDebugDialog as value +- 08.10.2001: KGamePropertyList|KGamePropertyArray must be ported to new QT3 API + (e.g. erase instead of remove, ...) diff --git a/libkdegames/kgame/dialogs/Makefile.am b/libkdegames/kgame/dialogs/Makefile.am new file mode 100644 index 00000000..1b9de53c --- /dev/null +++ b/libkdegames/kgame/dialogs/Makefile.am @@ -0,0 +1,17 @@ + +noinst_LTLIBRARIES = libkgamedialogs.la + +# compile-order doesn't matter here but maybe we will split these section soon + + +libkgamedialogs_la_SOURCES = kgamedialog.cpp kgameconnectdialog.cpp kgameerrordialog.cpp kgamedebugdialog.cpp kgamedialogconfig.cpp + +libkgamedialogsincludedir=$(includedir)/kgame +libkgamedialogsinclude_HEADERS = kgamedialog.h kgameconnectdialog.h kgameerrordialog.h kgamedebugdialog.h kgamedialogconfig.h + +INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame $(all_includes) +METASOURCES = AUTO + +messages: +# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot + diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.cpp b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp new file mode 100644 index 00000000..4d2d90e0 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp @@ -0,0 +1,278 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 "kgameconnectdialog.h" + +#include <knuminput.h> +#include <klocale.h> + +#include <qlineedit.h> +#include <qcombobox.h> +#include <qvbuttongroup.h> +#include <qlayout.h> +#include <qradiobutton.h> +#include <qlabel.h> +#include <dnssd/servicebrowser.h> +#include <qpushbutton.h> +#include <qgrid.h> + +class KGameConnectWidgetPrivate +{ + public: + KGameConnectWidgetPrivate() + { + mPort = 0; + mHost = 0; + mButtonGroup = 0; + mBrowser = 0; + } + + KIntNumInput* mPort; + QLineEdit* mHost; //KLineEdit? + QVButtonGroup* mButtonGroup; + QComboBox *mClientName; + QLabel *mClientNameLabel; + DNSSD::ServiceBrowser *mBrowser; + QLabel *mServerNameLabel; + QLineEdit *mServerName; + QString mType; +}; + +KGameConnectWidget::KGameConnectWidget(QWidget* parent) : QWidget(parent) +{ + d = new KGameConnectWidgetPrivate; + + QVBoxLayout* vb = new QVBoxLayout(this, KDialog::spacingHint()); + d->mButtonGroup = new QVButtonGroup(this); + vb->addWidget(d->mButtonGroup); + connect(d->mButtonGroup, SIGNAL(clicked(int)), this, SLOT(slotTypeChanged(int))); + (void)new QRadioButton(i18n("Create a network game"), d->mButtonGroup); + (void)new QRadioButton(i18n("Join a network game"), d->mButtonGroup); + + QGrid* g = new QGrid(2, this); + vb->addWidget(g); + g->setSpacing(KDialog::spacingHint()); + d->mServerNameLabel = new QLabel(i18n("Game name:"), g); + d->mServerName = new QLineEdit(g); + d->mClientNameLabel = new QLabel(i18n("Network games:"), g); + d->mClientName = new QComboBox(g); + connect(d->mClientName,SIGNAL(activated(int)),SLOT(slotGameSelected(int))); + (void)new QLabel(i18n("Port to connect to:"), g); + d->mPort = new KIntNumInput(g); + (void)new QLabel(i18n("Host to connect to:"), g); + d->mHost = new QLineEdit(g); + + QPushButton *button=new QPushButton(i18n("&Start Network"), this); + connect(button, SIGNAL(clicked()), this, SIGNAL(signalNetworkSetup())); + vb->addWidget(button); + // Hide until type is set + d->mClientName->hide(); + d->mClientNameLabel->hide(); + d->mServerName->hide(); + d->mServerNameLabel->hide(); +} + +void KGameConnectWidget::showDnssdControls() +{ + if (!d->mBrowser) return; + if (d->mHost->isEnabled()) { // client + d->mClientName->show(); + d->mClientNameLabel->show(); + d->mServerName->hide(); + d->mServerNameLabel->hide(); + slotGameSelected(d->mClientName->currentItem()); + } else { + d->mClientName->hide(); + d->mClientNameLabel->hide(); + d->mServerName->show(); + d->mServerNameLabel->show(); + } +} + +void KGameConnectWidget::setType(const QString& type) +{ + d->mType = type; + delete d->mBrowser; + d->mBrowser = new DNSSD::ServiceBrowser(type); + connect(d->mBrowser,SIGNAL(finished()),SLOT(slotGamesFound())); + d->mBrowser->startBrowse(); + showDnssdControls(); +} + +void KGameConnectWidget::slotGamesFound() +{ + bool autoselect=false; + if (!d->mClientName->count()) autoselect=true; + d->mClientName->clear(); + QStringList names; + QValueList<DNSSD::RemoteService::Ptr>::ConstIterator itEnd = d->mBrowser->services().end(); + for (QValueList<DNSSD::RemoteService::Ptr>::ConstIterator it = d->mBrowser->services().begin(); + it!=itEnd; ++it) names << (*it)->serviceName(); + d->mClientName->insertStringList(names); + if (autoselect && d->mClientName->count()) slotGameSelected(0); +} + +void KGameConnectWidget::setName(const QString& name) +{ + d->mServerName->setText(name); +} + +QString KGameConnectWidget::gameName() const +{ + return d->mServerName->text(); +} + +QString KGameConnectWidget::type() const +{ + return d->mType; +} + +void KGameConnectWidget::slotGameSelected(int nr) +{ + if (nr>=(d->mBrowser->services().count()) || nr<0) return; + if (!d->mHost->isEnabled()) return; // this is server mode, do not overwrite host and port controls + DNSSD::RemoteService::Ptr srv = d->mBrowser->services()[nr]; + if (!srv->isResolved() && !srv->resolve()) return; + d->mHost->setText(srv->hostName()); + d->mPort->setValue(srv->port()); +} +KGameConnectWidget::~KGameConnectWidget() +{ + delete d->mBrowser; + delete d; +} + +QString KGameConnectWidget::host() const +{ + if (d->mHost->isEnabled()) { + return d->mHost->text(); + } else { + return QString::null; + } +} + +unsigned short int KGameConnectWidget::port() const +{ + return d->mPort->value(); +} + +void KGameConnectWidget::setHost(const QString& host) +{ + d->mHost->setText(host); +} + +void KGameConnectWidget::setPort(unsigned short int port) +{ + d->mPort->setValue(port); +} + +void KGameConnectWidget::setDefault(int state) +{ + d->mButtonGroup->setButton(state); + slotTypeChanged(state); +} + +void KGameConnectWidget::slotTypeChanged(int t) +{ + if (t == 0) { + d->mHost->setEnabled(false); + } else if (t == 1) { + d->mHost->setEnabled(true); + } + showDnssdControls(); + emit signalServerTypeChanged(t); +} + +class KGameConnectDialogPrivate +{ + public: + KGameConnectDialogPrivate() + { + mConnect = 0; + } + + KGameConnectWidget* mConnect; +}; + +// buttonmask =Ok|Cancel +KGameConnectDialog::KGameConnectDialog(QWidget* parent,int buttonmask) : KDialogBase(Plain, + i18n("Network Game"),buttonmask , Ok, parent, 0, true, buttonmask!=0) +{ + d = new KGameConnectDialogPrivate; + QVBoxLayout* vb = new QVBoxLayout(plainPage(), spacingHint()); + d->mConnect = new KGameConnectWidget(plainPage()); + vb->addWidget(d->mConnect); +} + +KGameConnectDialog::~KGameConnectDialog() +{ + delete d; +} + +int KGameConnectDialog::initConnection( unsigned short int& port, + QString& host, QWidget* parent, bool server) +{ + KGameConnectDialog d(parent); + d.setHost(host); + d.setPort(port); + if (server) { + d.setDefault(0); + } else { + d.setDefault(1); + } + + int result = d.exec(); + if (result == QDialog::Accepted) { + host = d.host(); + port = d.port(); + } + return result; +} + +QString KGameConnectDialog::host() const +{ + return d->mConnect->host(); +} + +unsigned short int KGameConnectDialog::port() const +{ + return d->mConnect->port(); +} + +void KGameConnectDialog::setHost(const QString& host) +{ + d->mConnect->setHost(host); +} + +void KGameConnectDialog::setPort(unsigned short int port) +{ + d->mConnect->setPort(port); +} + +void KGameConnectDialog::setDefault(int state) +{ + d->mConnect->setDefault(state); +} + + + +#include "kgameconnectdialog.moc" + diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.h b/libkdegames/kgame/dialogs/kgameconnectdialog.h new file mode 100644 index 00000000..acbf21d2 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameconnectdialog.h @@ -0,0 +1,169 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KGAMECONNECTDIALOG_H__ +#define __KGAMECONNECTDIALOG_H__ + +#include <kdialogbase.h> + +class KGameConnectDialogPrivate; +class KGameConnectWidgetPrivate; + +class KGameConnectWidget : public QWidget +{ + Q_OBJECT +public: + KGameConnectWidget(QWidget* parent); + virtual ~KGameConnectWidget(); + + /** + * @param host The host to connect to by default + **/ + void setHost(const QString& host); + + /** + * @return The host to connect to or QString::null if the user wants to + * be the MASTER + **/ + QString host() const; + + /** + * @param port The port that will be shown by default + **/ + void setPort(unsigned short int port); + + /** + * @return The port to connect to / to listen + **/ + unsigned short int port() const; + + /** + * Specifies which state is the default (0 = server game; 1 = join game) + * @param state The default state. 0 For a server game, 1 to join a game + **/ + void setDefault(int state); + + /** + * Sets DNS-SD service type, both for publishing and browsing + * @param type Service type (something like _kwin4._tcp). + * It should be unique for application. + * @since 3.4 + **/ + void setType(const QString& type); + + /** + * @return service type + */ + QString type() const; + + /** + * Set game name for publishing. + * @param name Game name. Important only for server mode. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + */ + void setName(const QString& name); + + /** + * @return game name. + */ + QString gameName() const; + +protected slots: + /** + * The type has changed, ie the user switched between creating or + * joining. + **/ + void slotTypeChanged(int); + void slotGamesFound(); + void slotGameSelected(int); + +signals: + void signalNetworkSetup(); + void signalServerTypeChanged(int); + +private: + void showDnssdControls(); + KGameConnectWidgetPrivate* d; + +}; + +/** + * @short Dialog to ask for host and port + * + * This Dialog is used to create a game. You call initConnection(port, + * QString::null, parent, true) to create a network game (as a server) + * or initConnection(port, host, parent) to join a network game. + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameConnectDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameConnectDialog(QWidget* parent = 0,int buttonmask=Ok|Cancel); + virtual ~KGameConnectDialog(); + + /** + * Shows a dialog to either connect to an existing game or to create a + * server game, depending on user's choice. + * @param port The port the user wants to connect to. + * @param host The host the user wants to connect to. Will be + * QString::null if server game is chosen + * @param parent The parent of the dialog + * @param server True to create a network game per default, false to + * join a game by default + **/ + static int initConnection(unsigned short int& port, QString& host, QWidget* parent, bool server = false); + + /** + * @param host The host to connect to by default + **/ + void setHost(const QString& host); + + /** + * @return The host to connect to or QString::null if the user wants to + * be the MASTER + **/ + QString host() const; + + /** + * @param port The port that will be shown by default + **/ + void setPort(unsigned short int port); + + /** + * @return The port to connect to / to listen + **/ + unsigned short int port() const; + + /** + * Specifies which state is the default (0 = server game; 1 = join game) + * @param state The default state. 0 For a server game, 1 to join a game + **/ + void setDefault(int state); + +signals: + void signalNetworkSetup(); + +private: + KGameConnectDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.cpp b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp new file mode 100644 index 00000000..b112b04c --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp @@ -0,0 +1,548 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 "kgamedebugdialog.h" + +#include "kgamemessage.h" +#include "kgame.h" +#include "kplayer.h" +#include "kgamepropertyhandler.h" + +#include <klistview.h> +#include <klistbox.h> +#include <klocale.h> +#include <kdebug.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> + +#include <qlayout.h> +#include <qstring.h> +#include <qintdict.h> +#include <qlabel.h> +#include <qdatetime.h> + +#include <typeinfo> + + +class KGameDebugDialogPrivate +{ +public: + KGameDebugDialogPrivate() + { + mGame = 0; + + mGamePage = 0; + mGameProperties = 0; + mGameAddress = 0; + mGameId = 0; + mGameCookie = 0; + mGameMaster = 0; + mGameAdmin = 0; + mGameOffering = 0; + mGameStatus = 0; + mGameRunning = 0; + mGameMaxPlayers = 0; + mGameMinPlayers = 0; + mGamePlayerCount = 0; + + mPlayerPage = 0; + mPlayerList = 0; + mPlayerProperties = 0; + mPlayerAddress = 0; + mPlayerId = 0; + mPlayerName = 0; + mPlayerGroup = 0; + mPlayerUserId = 0; + mPlayerMyTurn = 0; + mPlayerAsyncInput= 0; + mPlayerKGameAddress = 0; + mPlayerVirtual = 0; + mPlayerActive = 0; + mPlayerRtti = 0; + mPlayerNetworkPriority = 0; + + mMessagePage = 0; + mMessageList = 0; + mHideIdList = 0; + } + + const KGame* mGame; + + QFrame* mGamePage; + KListView* mGameProperties; + QListViewItem* mGameAddress; + QListViewItem* mGameId; + QListViewItem* mGameCookie; + QListViewItem* mGameMaster; + QListViewItem* mGameAdmin; + QListViewItem* mGameOffering; + QListViewItem* mGameStatus; + QListViewItem* mGameRunning; + QListViewItem* mGameMaxPlayers; + QListViewItem* mGameMinPlayers; + QListViewItem* mGamePlayerCount; + + QFrame* mPlayerPage; + KListBox* mPlayerList; + KListView* mPlayerProperties; + QListViewItem* mPlayerAddress; + QListViewItem* mPlayerId; + QListViewItem* mPlayerName; + QListViewItem* mPlayerGroup; + QListViewItem* mPlayerUserId; + QListViewItem* mPlayerMyTurn; + QListViewItem* mPlayerAsyncInput; + QListViewItem* mPlayerKGameAddress; + QListViewItem* mPlayerVirtual; + QListViewItem* mPlayerActive; + QListViewItem* mPlayerRtti; + QListViewItem* mPlayerNetworkPriority; + + QFrame* mMessagePage; + KListView* mMessageList; + KListBox* mHideIdList; +}; + +KGameDebugDialog::KGameDebugDialog(KGame* g, QWidget* parent, bool modal) : + KDialogBase(Tabbed, i18n("KGame Debug Dialog"), Close, Close, + parent, 0, modal, true) +{ + d = new KGameDebugDialogPrivate; + + initGamePage(); + initPlayerPage(); + initMessagePage(); + + setKGame(g); +} + +KGameDebugDialog::~KGameDebugDialog() +{ + delete d; +} + +void KGameDebugDialog::initGamePage() +{ + d->mGamePage = addPage(i18n("Debug &KGame")); + QVBoxLayout* topLayout = new QVBoxLayout(d->mGamePage, marginHint(), spacingHint()); + QHBoxLayout* layout = new QHBoxLayout(topLayout); + + KListView* v = new KListView(d->mGamePage); + v->addColumn(i18n("Data")); + v->addColumn(i18n("Value")); + layout->addWidget(v); + + d->mGameProperties = new KListView(d->mGamePage); + d->mGameProperties->addColumn(i18n("Property")); + d->mGameProperties->addColumn(i18n("Value")); + d->mGameProperties->addColumn(i18n("Policy")); + layout->addWidget(d->mGameProperties); + + QPushButton* b = new QPushButton(i18n("Update"), d->mGamePage); + connect(b, SIGNAL(pressed()), this, SLOT(slotUpdateGameData())); + topLayout->addWidget(b); + +// game data + d->mGameAddress = new QListViewItem(v, i18n("KGame Pointer")); + d->mGameId = new QListViewItem(v, i18n("Game ID")); + d->mGameCookie = new QListViewItem(v, i18n("Game Cookie")); + d->mGameMaster = new QListViewItem(v, i18n("Is Master")); + d->mGameAdmin = new QListViewItem(v, i18n("Is Admin")); + d->mGameOffering = new QListViewItem(v, i18n("Is Offering Connections")); + d->mGameStatus = new QListViewItem(v, i18n("Game Status")); + d->mGameRunning = new QListViewItem(v, i18n("Game is Running")); + d->mGameMaxPlayers = new QListViewItem(v, i18n("Maximal Players")); + d->mGameMinPlayers = new QListViewItem(v, i18n("Minimal Players")); + d->mGamePlayerCount = new QListViewItem(v, i18n("Players")); +} + +void KGameDebugDialog::initPlayerPage() +{ + d->mPlayerPage = addPage(i18n("Debug &Players")); + QVBoxLayout* topLayout = new QVBoxLayout(d->mPlayerPage, marginHint(), spacingHint()); + QHBoxLayout* layout = new QHBoxLayout(topLayout); + + //TODO: connect to the KGame signals for joined/removed players!!! + QVBoxLayout* listLayout = new QVBoxLayout(layout); + QLabel* listLabel = new QLabel(i18n("Available Players"), d->mPlayerPage); + listLayout->addWidget(listLabel); + d->mPlayerList = new KListBox(d->mPlayerPage); + connect(d->mPlayerList, SIGNAL(executed(QListBoxItem*)), this, SLOT(slotUpdatePlayerData(QListBoxItem*))); + listLayout->addWidget(d->mPlayerList); + d->mPlayerList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + + KListView* v = new KListView(d->mPlayerPage); + layout->addWidget(v); + v->addColumn(i18n("Data")); + v->addColumn(i18n("Value")); + + d->mPlayerProperties = new KListView(d->mPlayerPage); + d->mPlayerProperties->addColumn(i18n("Property")); + d->mPlayerProperties->addColumn(i18n("Value")); + d->mPlayerProperties->addColumn(i18n("Policy")); + layout->addWidget(d->mPlayerProperties); + + QPushButton* b = new QPushButton(i18n("Update"), d->mPlayerPage); + connect(b, SIGNAL(pressed()), this, SLOT(slotUpdatePlayerList())); + topLayout->addWidget(b); + + d->mPlayerAddress = new QListViewItem(v, i18n("Player Pointer")); + d->mPlayerId = new QListViewItem(v, i18n("Player ID")); + d->mPlayerName = new QListViewItem(v, i18n("Player Name")); + d->mPlayerGroup = new QListViewItem(v, i18n("Player Group")); + d->mPlayerUserId = new QListViewItem(v, i18n("Player User ID")); + d->mPlayerMyTurn = new QListViewItem(v, i18n("My Turn")); + d->mPlayerAsyncInput = new QListViewItem(v, i18n("Async Input")); + d->mPlayerKGameAddress = new QListViewItem(v, i18n("KGame Address")); + d->mPlayerVirtual = new QListViewItem(v, i18n("Player is Virtual")); + d->mPlayerActive = new QListViewItem(v, i18n("Player is Active")); + d->mPlayerRtti = new QListViewItem(v, i18n("RTTI")); + d->mPlayerNetworkPriority = new QListViewItem(v, i18n("Network Priority")); +} + +void KGameDebugDialog::initMessagePage() +{ + d->mMessagePage = addPage(i18n("Debug &Messages")); + QGridLayout* layout = new QGridLayout(d->mMessagePage, 11, 7, marginHint(), spacingHint()); + d->mMessageList = new KListView(d->mMessagePage); + layout->addMultiCellWidget(d->mMessageList, 0, 9, 0, 3); + d->mMessageList->addColumn(i18n("Time")); + d->mMessageList->addColumn(i18n("ID")); + d->mMessageList->addColumn(i18n("Receiver")); + d->mMessageList->addColumn(i18n("Sender")); + d->mMessageList->addColumn(i18n("ID - Text")); + + QPushButton* hide = new QPushButton(i18n("&>>"), d->mMessagePage); + connect(hide, SIGNAL(pressed()), this, SLOT(slotHideId())); + layout->addWidget(hide, 4, 4); + + QPushButton* show = new QPushButton(i18n("&<<"), d->mMessagePage); + connect(show, SIGNAL(pressed()), this, SLOT(slotShowId())); + layout->addWidget(show, 6, 4); + + QLabel* l = new QLabel(i18n("Do not show IDs:"), d->mMessagePage); + layout->addMultiCellWidget(l, 0, 0, 5, 6); + d->mHideIdList = new KListBox(d->mMessagePage); + layout->addMultiCellWidget(d->mHideIdList, 1, 8, 5, 6); + + QPushButton* clear = new KPushButton(KStdGuiItem::clear(), d->mMessagePage); + connect(clear, SIGNAL(pressed()), this, SLOT(slotClearMessages())); + layout->addMultiCellWidget(clear, 10, 10, 0, 6); + //TODO: "show all but..." and "show nothing but..." +} + +void KGameDebugDialog::clearPlayerData() +{ + d->mPlayerAddress->setText(1, ""); + d->mPlayerId->setText(1, ""); + d->mPlayerName->setText(1, ""); + d->mPlayerGroup->setText(1, ""); + d->mPlayerUserId->setText(1, ""); + d->mPlayerMyTurn->setText(1, ""); + d->mPlayerAsyncInput->setText(1, ""); + d->mPlayerKGameAddress->setText(1, ""); + d->mPlayerVirtual->setText(1, ""); + d->mPlayerActive->setText(1, ""); + d->mPlayerRtti->setText(1, ""); + d->mPlayerNetworkPriority->setText(1, ""); + + d->mPlayerProperties->clear(); +} + +void KGameDebugDialog::clearGameData() +{ + d->mGameAddress->setText(1, ""); + d->mGameId->setText(1, ""); + d->mGameCookie->setText(1, ""); + d->mGameMaster->setText(1, ""); + d->mGameAdmin->setText(1, ""); + d->mGameOffering->setText(1, ""); + d->mGameStatus->setText(1, ""); + d->mGameRunning->setText(1, ""); + d->mGameMaxPlayers->setText(1, ""); + d->mGameMinPlayers->setText(1, ""); + + d->mGameProperties->clear(); +} + +void KGameDebugDialog::slotUpdatePlayerData() +{ + if (!d->mGame || d->mPlayerList->currentItem() == -1) { + return; + } + slotUpdatePlayerData(d->mPlayerList->item(d->mPlayerList->currentItem())); +} + +void KGameDebugDialog::slotUpdatePlayerList() +{ + QListBoxItem* i = d->mPlayerList->firstItem(); + for (; i; i = d->mPlayerList->firstItem()) { + removePlayer(i); + } + + QPtrList<KPlayer> list = *d->mGame->playerList(); + for (KPlayer* p = list.first(); p; p = list.next()) { + addPlayer(p); + } +} + +void KGameDebugDialog::slotUpdateGameData() +{ + if (!d->mGame) { + d->mGameAddress->setText(1, i18n("NULL pointer")); + return; +} + + clearGameData(); + + QString buf; + buf.sprintf("%p", d->mGame); + d->mGameAddress->setText(1, buf); + d->mGameId->setText(1, QString::number(d->mGame->gameId())); + d->mGameCookie->setText(1, QString::number(d->mGame->cookie())); + d->mGameMaster->setText(1, d->mGame->isMaster() ? i18n("True") : i18n("False")); + d->mGameAdmin->setText(1, d->mGame->isAdmin() ? i18n("True") : i18n("False")); + d->mGameOffering->setText(1, d->mGame->isOfferingConnections() ? i18n("True") : i18n("False")); + d->mGameStatus->setText(1, QString::number(d->mGame->gameStatus())); + d->mGameRunning->setText(1, d->mGame->isRunning() ? i18n("True") : i18n("False")); + d->mGameMaxPlayers->setText(1, QString::number(d->mGame->maxPlayers())); + d->mGameMinPlayers->setText(1, QString::number(d->mGame->minPlayers())); + d->mGamePlayerCount->setText(1, QString::number(d->mGame->playerCount())); + +//TODO ios + + KGamePropertyHandler* handler = d->mGame->dataHandler(); + QIntDictIterator<KGamePropertyBase> it(handler->dict()); + while (it.current()) { + QString policy; + switch (it.current()->policy()) { + case KGamePropertyBase::PolicyClean: + policy = i18n("Clean"); + break; + case KGamePropertyBase::PolicyDirty: + policy = i18n("Dirty"); + break; + case KGamePropertyBase::PolicyLocal: + policy = i18n("Local"); + break; + case KGamePropertyBase::PolicyUndefined: + default: + policy = i18n("Undefined"); + break; + } + (void) new QListViewItem(d->mGameProperties, + handler->propertyName(it.current()->id()), + handler->propertyValue(it.current()), + policy); +// kdDebug(11001) << k_funcinfo << ": checking for all game properties: found property name " << name << endl; + ++it; + } +} + +void KGameDebugDialog::slotUpdatePlayerData(QListBoxItem* item) +{ + if (!item || !d->mGame) { + return; + } + + KPlayer* p = d->mGame->findPlayer(item->text().toInt()); + + if (!p) { + kdError(11001) << k_funcinfo << ": cannot find player" << endl; + return; + } + + clearPlayerData(); + + QString buf; + buf.sprintf("%p", p); + d->mPlayerAddress->setText(1, buf); + d->mPlayerId->setText(1, QString::number(p->id())); + d->mPlayerName->setText(1, p->name()); + d->mPlayerGroup->setText(1, p->group()); + d->mPlayerUserId->setText(1, QString::number(p->userId())); + d->mPlayerMyTurn->setText(1, p->myTurn() ? i18n("True") : i18n("False")); + d->mPlayerAsyncInput->setText(1, p->asyncInput() ? i18n("True") : i18n("False")); + buf.sprintf("%p", p->game()); + d->mPlayerKGameAddress->setText(1, buf); + d->mPlayerVirtual->setText(1, p->isVirtual() ? i18n("True") : i18n("False")); + d->mPlayerActive->setText(1, p->isActive() ? i18n("True") : i18n("False")); + d->mPlayerRtti->setText(1, QString::number(p->rtti())); + d->mPlayerNetworkPriority->setText(1, QString::number(p->networkPriority())); + +//TODO ios + +// Properties + KGamePropertyHandler * handler = p->dataHandler(); + QIntDictIterator<KGamePropertyBase> it((handler->dict())); + while (it.current()) { + QString policy; + switch (it.current()->policy()) { + case KGamePropertyBase::PolicyClean: + policy = i18n("Clean"); + break; + case KGamePropertyBase::PolicyDirty: + policy = i18n("Dirty"); + break; + case KGamePropertyBase::PolicyLocal: + policy = i18n("Local"); + break; + case KGamePropertyBase::PolicyUndefined: + default: + policy = i18n("Undefined"); + break; + } + (void)new QListViewItem(d->mPlayerProperties, + handler->propertyName(it.current()->id()), + handler->propertyValue(it.current()), + policy); + ++it; + } +} + +void KGameDebugDialog::clearPages() +{ + clearPlayerData(); + clearGameData(); + d->mPlayerList->clear(); + slotClearMessages(); +} + +void KGameDebugDialog::setKGame(const KGame* g) +{ + slotUnsetKGame(); + d->mGame = g; + if (g) { + //TODO: connect to the KGame signals for joined/removed players!!! + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); +// connect(); + + QPtrList<KPlayer> list = *d->mGame->playerList(); + for (KPlayer* p = list.first(); p; p = list.next()) { + addPlayer(p); + } + + slotUpdateGameData(); + + connect(d->mGame, SIGNAL(signalMessageUpdate(int, Q_UINT32, Q_UINT32)), this, SLOT(slotMessageUpdate(int, Q_UINT32, Q_UINT32))); + } +} + +void KGameDebugDialog::slotUnsetKGame() +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = 0; + clearPages(); +} + +void KGameDebugDialog::addPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << "trying to add NULL player" << endl; + return; + } + + (void) new QListBoxText(d->mPlayerList, QString::number(p->id())); + //TODO connect to signals, like deleted/removed, ... +} + +void KGameDebugDialog::removePlayer(QListBoxItem* i) +{ + if (!i || !d->mGame) { + return; + } + KPlayer* p = d->mGame->findPlayer(i->text().toInt()); + if (!p) { + return; + } + disconnect(p, 0, this, 0); + if (i->isSelected()) { + clearPlayerData(); + } + delete i; +} + +void KGameDebugDialog::slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + if (!showId(msgid)) { + return; + } + QString msgidText = KGameMessage::messageId2Text(msgid); + if (msgidText.isNull()) { + if (msgid > KGameMessage::IdUser) { + emit signalRequestIdName(msgid-KGameMessage::IdUser, true, msgidText); + } else { + emit signalRequestIdName(msgid, false, msgidText); + } + if (msgidText.isNull()) { + msgidText = i18n("Unknown"); + } + } + (void) new QListViewItem( d->mMessageList, QTime::currentTime().toString(), + QString::number(msgid), QString::number(receiver), + QString::number(sender), msgidText); +} + +void KGameDebugDialog::slotClearMessages() +{ + d->mMessageList->clear(); +} + +void KGameDebugDialog::slotShowId() +{ +/* QListBoxItem* i = d->mHideIdList->firstItem(); + for (; i; i = i->next()) { + if (i->selected()) { + d->mHideIdList->removeItem(i->); + } + }*/ + if (!d->mHideIdList->currentItem()) { + return; + } + d->mHideIdList->removeItem(d->mHideIdList->currentItem()); +} + +void KGameDebugDialog::slotHideId() +{ + if (!d->mMessageList->currentItem()) { + return; + } + int msgid = d->mMessageList->currentItem()->text(1).toInt(); + if (!showId(msgid)) { + return; + } + (void)new QListBoxText(d->mHideIdList, QString::number(msgid)); +} + +bool KGameDebugDialog::showId(int msgid) +{ + QListBoxItem* i = d->mHideIdList->firstItem(); + for (; i; i = i->next()) { + if (i->text().toInt() == msgid) { + return false; + } + } + return true; +} + + +#include "kgamedebugdialog.moc" diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.h b/libkdegames/kgame/dialogs/kgamedebugdialog.h new file mode 100644 index 00000000..65afc92a --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedebugdialog.h @@ -0,0 +1,149 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 __KGAMEDEBUGDIALOG_H__ +#define __KGAMEDEBUGDIALOG_H__ + +#include <kdialogbase.h> +#include <kdemacros.h> + +class KGame; +class KGameIO; +class KPlayer; +class KGamePropertyBase; + +class KGameDebugDialogPrivate; + +class KDE_EXPORT KGameDebugDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameDebugDialog(KGame* g, QWidget* parent, bool modal = false); + ~KGameDebugDialog(); + + /** + * Automatically connects the KGame object to all error dependant slots. + * Create a KGameErrorDialog object, call this function and forget + * everything. + * @param g The KGame which will emit the erorrs (or not ;-) ) + **/ + void setKGame(const KGame* g); + +public slots: + /** + * Unsets a @ref KGame which has been set using @ref setKGame before. + * This is called automatically when the @ref KGame object is destroyed + * and you normally don't have to call this yourself. + * + * Note that @ref setKGame also unsets an already existing @ref KGame + * object if exising. + **/ + void slotUnsetKGame(); + + /** + * Update the data of the @ref KGame object + **/ + void slotUpdateGameData(); + + /** + * Update the properties of the currently selected player + **/ + void slotUpdatePlayerData(); + + /** + * Updates the list of players and calls @ref clearPlayerData. Note that + * after this call NO player is selected anymore. + **/ + void slotUpdatePlayerList(); + + void slotClearMessages(); + +signals: + /** + * This signal is emitted when the "debug messages" page couldn't find + * the name of a message id. This is usually the case for user-defined + * messages. KGameDebugDialog asks you to give the msgid a name. + * @param messageid The ID of the message. As given to @ref + * KGame::sendMessage + * @param userid User defined msgIds are internally increased by + * @ref KGameMessage::IdUser. You don't have to care about this but if + * this signal is emitted with userid=false (shouldn't happen) then the + * name of an internal message as defined in @ref + * KGameMessage::GameMessageIds couldn't be found. + * @param name The name of the msgid. You have to fill this! + **/ + void signalRequestIdName(int messageid, bool userid, QString& name); + +protected: + void clearPages(); + + /** + * Clear the data of the player view. Note that the player list is NOT + * cleared. + **/ + void clearPlayerData(); + + /** + * Clear the data view of the @ref KGame object + **/ + void clearGameData(); + + /** + * Add a new player to the player list + **/ + void addPlayer(KPlayer* p); + + /** + * Remove a player from the list + **/ + void removePlayer(QListBoxItem* item); + + /** + * @return Whether messages with this msgid shall be displayed or not + **/ + bool showId(int msgid); + +protected slots: + /** + * Update the data of the player specified in item + * @param item The @ref QListBoxItem of the player to be updated. Note + * that the text of this item MUST be the ID of the player + **/ + void slotUpdatePlayerData(QListBoxItem* item); + + void slotShowId(); + void slotHideId(); + + /** + * A message has been received - see @ref KGame::signalMessageUpdate + **/ + void slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender); + +private: + void initGamePage(); + void initPlayerPage(); + void initMessagePage(); + +private: + KGameDebugDialogPrivate* d; +}; + + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedialog.cpp b/libkdegames/kgame/dialogs/kgamedialog.cpp new file mode 100644 index 00000000..dc564b8e --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialog.cpp @@ -0,0 +1,347 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 <qlayout.h> +#include <qvbox.h> + +#include <klocale.h> + +#include "kgame.h" +#include "kplayer.h" +#include "kgamedialogconfig.h" + +#include "kgamedialog.h" + +#include "kgamedialog.moc" + +class KGameDialogPrivate +{ +public: + KGameDialogPrivate() + { + mGamePage = 0; + mNetworkPage = 0; + mMsgServerPage = 0; + mTopLayout = 0; + + mNetworkConfig = 0; + mGameConfig = 0; + + mOwner = 0; + mGame = 0; + } + + QVBox* mGamePage; + QVBox* mNetworkPage; + QVBox* mMsgServerPage;// unused here? + QVBoxLayout* mTopLayout; + KGameDialogNetworkConfig* mNetworkConfig; + KGameDialogGeneralConfig* mGameConfig; + +// a list of all config widgets added to this dialog + QPtrList<KGameDialogConfig> mConfigWidgets; + +// just pointers: + KPlayer* mOwner; + KGame* mGame; +}; + +KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, bool modal) + : KDialogBase(Tabbed, title, Ok|Default|Apply, + Ok, parent, 0, modal, true) +{ + init(g, owner); +} + +KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, long initConfigs, int chatMsgId, bool modal) + : KDialogBase(Tabbed, title, Ok|Default|Apply, + Ok, parent, 0, modal, true) +{ + init(g, owner); + if ((ConfigOptions)initConfigs!=NoConfig) { + initDefaultDialog((ConfigOptions)initConfigs, chatMsgId); + } +} + +void KGameDialog::init(KGame* g, KPlayer* owner) +{ +//AB: do we need a "Cancel" Button? currently removed + +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogPrivate; + + setOwner(owner); + setKGame(g); + if (g) { + setAdmin(g->isAdmin()); + } else { + setAdmin(false); + } +} + +void KGameDialog::initDefaultDialog(ConfigOptions initConfigs, int chatMsgId) +{ + if (initConfigs & GameConfig) { + kdDebug() << "add gameconf" << endl; + addGameConfig(new KGameDialogGeneralConfig(0)); + } + if (initConfigs & NetworkConfig) { + addNetworkConfig(new KGameDialogNetworkConfig(0)); + } + if (initConfigs & (MsgServerConfig) ) { + addMsgServerConfig(new KGameDialogMsgServerConfig(0)); + } + if (initConfigs & ChatConfig) { + KGameDialogChatConfig * c = new KGameDialogChatConfig(chatMsgId, 0); + if (d->mGamePage) { + addChatWidget(c, d->mGamePage); + } else { + addConfigPage(c, i18n("&Chat")); + } + } + if (initConfigs & BanPlayerConfig) { + // add the connection management system - ie the widget where the ADMIN can + // kick players out + if (d->mNetworkPage) { + // put it on the network page + addConnectionList(new KGameDialogConnectionConfig(0), d->mNetworkPage); + } else { + // if no network page available put it on an own page + addConfigPage(new KGameDialogConnectionConfig(0), i18n("C&onnections")); + } + } +} + +KGameDialog::~KGameDialog() +{ +// kdDebug(11001) << "DESTRUCT KGameDialog" << this << endl; + d->mConfigWidgets.setAutoDelete(true); + d->mConfigWidgets.clear(); + delete d; +} + +void KGameDialog::addGameConfig(KGameDialogGeneralConfig* conf) +{ + if (!conf) { + return; + } + d->mGameConfig = conf; + d->mGamePage = addConfigPage(d->mGameConfig, i18n("&Game")); +} + +void KGameDialog::addNetworkConfig(KGameDialogNetworkConfig* netConf) +{ + if (!netConf) { + return; + } + d->mNetworkConfig = netConf; + d->mNetworkPage = addConfigPage(netConf, i18n("&Network")); +} + +void KGameDialog::addMsgServerConfig(KGameDialogMsgServerConfig* msgConf) +{ + if (!msgConf) { + return; + } + d->mMsgServerPage = addConfigPage(msgConf, i18n("&Message Server")); +} + +void KGameDialog::addChatWidget(KGameDialogChatConfig* chat, QVBox* parent) +{ + if (!chat) { + return; + } + if (!parent) { + parent = d->mGamePage; + } + if (!parent) { + kdError(11001) << "cannot add chat widget without page" << endl; + return; + } + addConfigWidget(chat, parent); +} + +void KGameDialog::addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent) +{ + if (!c) { + return; + } + if (!parent) { + parent = d->mNetworkPage; + } + if (!parent) { + kdError(11001) << "Cannot add connection list without page" << endl; + return; + } + addConfigWidget(c, parent); +} + +QVBox *KGameDialog::configPage(ConfigOptions which) +{ + QVBox *box = 0; + switch(which) + { + case NetworkConfig: + box = d->mNetworkPage; + break; + case GameConfig: + box = d->mGamePage; + break; + case MsgServerConfig: + box = d->mMsgServerPage; + break; + default: + kdError(11001) << k_funcinfo << ": Parameter " << which << " not supported" << endl; + } + return box; +} + +QVBox* KGameDialog::addConfigPage(KGameDialogConfig* widget, const QString& title) +{ + if (!widget) { + kdError(11001) << "Cannot add NULL config widget" << endl; + return 0; + } + QVBox* page = addVBoxPage(title); + addConfigWidget(widget, page); + return page; +} + +void KGameDialog::addConfigWidget(KGameDialogConfig* widget, QWidget* parent) +{ + if (!widget) { + kdError(11001) << "Cannot add NULL config widget" << endl; + return; + } + if (!parent) { + kdError(11001) << "Cannot reparent to NULL widget" << endl; + return; + } +// kdDebug(11001) << "reparenting widget" << endl; + widget->reparent(parent, QPoint(0,0)); + d->mConfigWidgets.append(widget); + connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(slotRemoveConfigWidget(QObject*))); + if (!d->mGame) { + kdWarning(11001) << "No game has been set!" << endl; + } else { + widget->setKGame(d->mGame); + widget->setAdmin(d->mGame->isAdmin()); + } + if (!d->mOwner) { + kdWarning(11001) << "No player has been set!" << endl; + } else { + widget->setOwner(d->mOwner); + } + widget->show(); +} + +KGameDialogGeneralConfig* KGameDialog::gameConfig() const +{ return d->mGameConfig; } +KGameDialogNetworkConfig* KGameDialog::networkConfig() const +{ return d->mNetworkConfig; } + +void KGameDialog::slotApply() +{ + submitToKGame(); +} + +void KGameDialog::slotDefault() +{ + if (!d->mGame) { + return; + } + +//TODO *only* call setKGame/setOwner for the *current* page!! + setKGame(d->mGame); + setOwner(d->mOwner); +} + +void KGameDialog::slotOk() +{ + slotApply(); + QDialog::accept(); +} + +void KGameDialog::setOwner(KPlayer* owner) +{ +//AB: note: NULL player is ok! + d->mOwner = owner; + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + if (d->mConfigWidgets.at(i)) { + d->mConfigWidgets.at(i)->setOwner(d->mOwner); + //TODO: hide playerName in KGameDialogGeneralConfig + } else { + kdError(11001) << "NULL widget??" << endl; + } + } +} + +void KGameDialog::setKGame(KGame* g) +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = g; + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + d->mConfigWidgets.at(i)->setKGame(d->mGame); + } + if (d->mGame) { + setAdmin(d->mGame->isAdmin()); + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + connect(d->mGame, SIGNAL(signalAdminStatusChanged(bool)), + this, SLOT(setAdmin(bool))); + } +} + +void KGameDialog::setAdmin(bool admin) +{ + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { + d->mConfigWidgets.at(i)->setAdmin(admin); + } +} + +void KGameDialog::slotUnsetKGame() // called when KGame is destroyed +{ setKGame(0); } + +void KGameDialog::submitToKGame() +{ + if (!d->mGame) { + kdError(11001) << k_funcinfo << ": no game has been set" << endl; + return; + } + if (!d->mOwner) { + kdError(11001) << k_funcinfo << ": no player has been set" << endl; + return; + } + + for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) { +// kdDebug(11001) << "submit to kgame " << i << endl; + d->mConfigWidgets.at(i)->submitToKGame(d->mGame, d->mOwner); +// kdDebug(11001) << "done: submit to kgame " << i << endl; + } +} + +void KGameDialog::slotRemoveConfigWidget(QObject* configWidget) +{ + d->mConfigWidgets.removeRef((KGameDialogConfig*)configWidget); +} + diff --git a/libkdegames/kgame/dialogs/kgamedialog.h b/libkdegames/kgame/dialogs/kgamedialog.h new file mode 100644 index 00000000..f7a0c6e5 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialog.h @@ -0,0 +1,320 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ + +// NAMING +// please follow these naming rules if you add/change classes: +// the main dialog is named KGameDialog and the base config widget +// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where +// XYZ = the name of the config widget, like "general" or "network") and are +// inherited from KGameDialogConfig. + +#ifndef __KGAMEDIALOG_H__ +#define __KGAMEDIALOG_H__ + +#include <kdialogbase.h> +#include <kdemacros.h> +class QGridLayout; +class QVBoxLayout; +class QListBoxItem; + +class KGame; +class KPlayer; +class KGamePropertyBase; + +class KGameDialogConfig; +class KGameDialogGeneralConfig; +class KGameDialogNetworkConfig; +class KGameDialogMsgServerConfig; +class KGameDialogChatConfig; +class KGameDialogConnectionConfig; + +class KGameDialogPrivate; +/** + * TODO: rewrite entire documentation. Nearly nothing is valid anymore. + * The main configuration dialog for KGame. Here all players meat each other, + * every player can see how many players connected (and their names) and the + * ADMIN can even "kick" players out. You can talk to each other (using + * KGameChat and the ADMIN can define the maxPlayers/minPlayers as well as the + * number of computer players. + * + * + * AB: setDefaultXYZ is obsolete!! + * You will usually create an instance of KGameDialog or any derived class and + * call setDefaultXYZ methods. Example (maybe + * obsoleted parameters - docu is currently changing very fast): + * \code + * KGameDialog dlg(kgame, i18n("New Game"), localPlayer, this, true, + * ID_CHAT); + * dlg.setDefaultNetworkInfo(port, host); // AB: obsolete! + * dlg.exec(); + * \endcode + * This will create a default modal dialog with the title "New Game". You don't + * have to do more than this. + * + * @short Main configuration dialog for KGame + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KGameDialog : public KDialogBase +{ + Q_OBJECT +public: + + enum ConfigOptions + { + NoConfig = 0, + ChatConfig = 1, + GameConfig = 2, + NetworkConfig = 4, + MsgServerConfig = 8, + BanPlayerConfig = 16, + AllConfig = 0xffff + }; + + /** + * Create an empty KGameDialog. You can add widgets using + * addConfigPage. + * @param g The KGame object of this game + * @param owner The KPlayer object who is responsible for this + * dialog, aka "the local player" + * @param title The title of the dialog - see KDialog::setCaption + * @param parent The parent of the dialog + * @param modal Whether the dialog is modal or not + **/ + KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, bool modal = false); + + /** + * Create a KGameDialog with the standard configuration widgets. This + * creates the following widgets: + * <ul> + * <li> KGameDialogGeneralConfig + * <li> KGameDialogNetworkConfig + * <li> KGameDialogMsgServerConfig + * <li> KGameDialogChatConfig + * <li> KGameDialogConnectionConfig + * </ul> + * If you want to use your own implementations (or none) of the widgets + * above you should subclass KGameDialog. Use addGameConfig, + * addNetworkConfig, addMsgConfig, addChatWidget and + * addConnectionList in this case. + * + * If you want to add further configuration widget you can simply use + * addConfigPage + * @param g The KGame object of this game + * @param owner The KPlayer object who is responsible for this + * dialog, aka "the local player" + * @param title The title of the dialog - see KDialog::setCaption + * @param parent The parent of the dialog + * @param modal Whether the dialog is modal or not + * @param initConfigs whether the default KGameDialogConfig widgets + * shall be created using initDefaultDialog. Use false if you want + * to use custom widgets. + * @param chatMsgId The ID of Chat messages. See KGameChat. Unused + * if initConfigs = false + **/ + KGameDialog(KGame* g, KPlayer* owner, const QString& title, + QWidget* parent, long initConfigs = AllConfig, + int chatMsgId = 15432, bool modal = false); + + virtual ~KGameDialog(); + + + /** + * Change the owner of the dialog. This will be used as the fromPlayer in + * KGameChat and will receive the entered player name. + * @param owner The owner of the dialog. It must already be added to the + * KGame object! + * + * Calls the KGameDialogConfig::setOwner implementation of all + * widgets that have been added by addConfigWidget + * @param owner The new owner player of this dialog must already be + * added to the KGame object. Can even be NULL (then no player + * configuration is made) + **/ + void setOwner(KPlayer* owner); + + /** + * Change the KGame object this dialog is used for. + * + * Calls the KGameDialogConfig::setKGame implementation of all + * widgets that have been added by addConfigWidget + * @param g The new KGame object + **/ + void setKGame(KGame* g); + + /** + * This will submit all configuration data to the KGame object. + * Automatically called by slotApply and slotOk + * There is no need to replace this unless you + * want to add widgets which are not derived from those classes + **/ + virtual void submitToKGame(); + + /** + * Adds a KGameChat to the dialog. If no parent is specified the + * game page will be used. + * @param chat The chat widget + * @param parent The parent of the chat widget. This MUST be an + * already added config widget. Note that the game page will be used + * if parent is 0. + **/ + void addChatWidget(KGameDialogChatConfig* chat, QVBox* parent = 0); + + /** + * Add a connection list to the dialog. The list consists of a + * KLisBox containing all players in the current game (see + * KGame::playerList). The admin can "ban" players, ie kick them out of + * the game. + * + * This is another not-really-config-config-widget. It just displays the + * connections and lets you ban players. + * @param c The KGameDialogConnectionConfig object + * @param parent The parent of the widget. If 0 the networkConfig + * page is used. + **/ + void addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent = 0); + + /** + * Add a new page to the dialog. The page will contain you new config + * widget and will have your provided title. + * + * The widget will be reparented to this dialog. This also calls + * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner. + * @param widget The new config widget + * @param title The title of the newly added page. + * @return The newly added page which contains your config widget. + **/ + QVBox* addConfigPage(KGameDialogConfig* widget, const QString& title); + + /** + * @return The QVBox of the given key, The key is from ConfigOptions + * Note that not all are supported yet + **/ + QVBox *configPage(ConfigOptions which); + + /** + * @return The default netowrk config. Note that this always returns 0 if + * you did not specify NetworkConfig in the constructor! + **/ + KGameDialogNetworkConfig* networkConfig() const; + + /** + * @return The default game config. Note that this always returns 0 if + * you did not specify GameConfig in the constructor! + **/ + KGameDialogGeneralConfig* gameConfig() const; + + /** + * Add a config widget to the specified parent. Usually you call + * addConfigPage for one widget and addConfigWidget for another to add + * it to the same page. Just use the returned page of + * addConfigPage. + **/ + void addConfigWidget(KGameDialogConfig* widget, QWidget* parent); + + /** + * Used to add the main network config widget in a new page. Use this to + * make networkConfig return something useful. + **/ + void addNetworkConfig(KGameDialogNetworkConfig* netConf); + + /** + * Add the main game config widget in a new page. Use this to make + * gameConfig return something useful. + **/ + void addGameConfig(KGameDialogGeneralConfig* conf); + + /** + * Used to add the message server config widget in a new page. + **/ + void addMsgServerConfig(KGameDialogMsgServerConfig* conf); + +protected: + + /** + * This is used to create a dialog containing all the default widgets. + * + * You may want to use this if you just want to use your own + * configuration widgets which inherit the standard ones. + * + * Note that if one of the widgets is NULL the default implementation + * will be used! (except the chat widget - you need to create it + * yourself as you have to provide a message id) + * @param initConfigs The widgets to be created + * @param chatMsgId The msgid for the chat config (only if specified in + * initConfigs) - see KGameDialogChatConfig + **/ + void initDefaultDialog(ConfigOptions initConfigs, int chatMsgId = 15432); + + /** + * Go through all config widgets and call their + * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner implementation + * + * This function could be private and probably will be very soon. + * Don't use it yourself + **/ + void configureConfigWidgets(); + +protected slots: + /** + * Called when the user clicks on Ok. Calls slotApply and + * QDialog::accept() + **/ + virtual void slotOk(); + + /** + * Just calls submitToKGame() + **/ + virtual void slotApply(); + + /** + * Sets the default values for the configuration widgets. Set these + * values by (e.g.) setDefaultMaxPlayers() + * @deprecated + **/ + virtual void slotDefault(); + + /** + * Called when the KGame object is destroyed. Calls setKGame(0) so + * that all widgets can disconnect their slots and so on. + **/ + void slotUnsetKGame(); + + /** + * Called when the ADMIN status of this KGame client changes. See + * KGameNetwork::signalAdminStatusChanged + * @param isAdmin TRUE if this client is now the ADMIN otherwise FALSE + **/ + void setAdmin(bool isAdmin); + + /** + * Remove a config widget from the widget list. + * @see QObject::destroyed + **/ + void slotRemoveConfigWidget(QObject* configWidget); + +private: + void init(KGame*, KPlayer*); + +private: + KGameDialogPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.cpp b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp new file mode 100644 index 00000000..27f91cd1 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp @@ -0,0 +1,773 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 "kgamedialogconfig.h" + +#include "kgame.h" +#include "kplayer.h" +#include "kgamechat.h" +#include "kgameconnectdialog.h" + +#include <klocale.h> +#include <knuminput.h> +#include <kdialog.h> +#include <klistbox.h> +#include <kmessagebox.h> + +#include <qlayout.h> +#include <qhgroupbox.h> +#include <qlabel.h> +#include <qpushbutton.h> +#include <qlineedit.h> +#include <qvbox.h> +#include <qptrdict.h> + +#include "kgamedialogconfig.moc" + +class KGameDialogConfigPrivate +{ +public: + KGameDialogConfigPrivate() + { + mOwner = 0; + mGame = 0; + + mAdmin = false; + } + + bool mAdmin; + KGame* mGame; + KPlayer* mOwner; +}; + +KGameDialogConfig::KGameDialogConfig(QWidget* parent) : QWidget(parent) +{ + d = new KGameDialogConfigPrivate; +} + +KGameDialogConfig::~KGameDialogConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogConfig::setKGame(KGame* g) +{ + d->mGame = g; +} + +void KGameDialogConfig::setOwner(KPlayer* p) +{ + d->mOwner = p; +} + +void KGameDialogConfig::setAdmin(bool a) +{ + d->mAdmin = a; +} + +KGame* KGameDialogConfig::game() const +{ return d->mGame; } +bool KGameDialogConfig::admin() const +{ return d->mAdmin; } +KPlayer* KGameDialogConfig::owner() const +{ return d->mOwner; } + +/////////////////////////// KGameDialogNetworkConfig ///////////////////////// +class KGameDialogNetworkConfigPrivate +{ +public: + KGameDialogNetworkConfigPrivate() + { + mInitConnection = 0; + mNetworkLabel = 0; + mDisconnectButton = 0; + mConnect = 0; + mDefaultServer=true; + + } + + // QPushButton* mInitConnection; + QHGroupBox* mInitConnection; + QLabel* mNetworkLabel; + QPushButton *mDisconnectButton; + + bool mDefaultServer; + QString mDefaultHost; + unsigned short int mDefaultPort; + KGameConnectWidget *mConnect; +}; + + +KGameDialogNetworkConfig::KGameDialogNetworkConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogNetworkConfigPrivate(); + + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint(), "toplayout"); + + QHBoxLayout *hb = new QHBoxLayout(topLayout, KDialog::spacingHint()); + + d->mNetworkLabel = new QLabel(this); + hb->addWidget(d->mNetworkLabel); + + d->mDisconnectButton=new QPushButton(i18n("Disconnect"),this); + connect(d->mDisconnectButton, SIGNAL(clicked()), this, SLOT(slotExitConnection())); + hb->addWidget(d->mDisconnectButton); + + d->mInitConnection = new QHGroupBox(i18n("Network Configuration"), this); + topLayout->addWidget(d->mInitConnection); + + d->mConnect = new KGameConnectWidget(d->mInitConnection); + connect(d->mConnect, SIGNAL(signalNetworkSetup()), this, SLOT(slotInitConnection())); + connect(d->mConnect, SIGNAL(signalServerTypeChanged(int)), + this, SIGNAL(signalServerTypeChanged(int))); + + // Needs to be AFTER the creation of the dialogs + setConnected(false); + setDefaultNetworkInfo("localhost", 7654,true); +} + +KGameDialogNetworkConfig::~KGameDialogNetworkConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogNetworkConfig::slotExitConnection() +{ + kdDebug(11001) << k_funcinfo << " !!!!!!!!!!!!!!!!!!!!!!!" << endl; + if (game()) game()->disconnect(); + setConnected(false,false); +} + +void KGameDialogNetworkConfig::slotInitConnection() +{ + kdDebug(11001) << k_funcinfo << endl; + bool connected = false; + bool master = true; + unsigned short int port = d->mConnect->port(); + QString host = d->mConnect->host(); + + if (host.isNull()) { + master = true; + if (game()) { + game()->setDiscoveryInfo(d->mConnect->type(),d->mConnect->gameName()); + connected = game()->offerConnections(port); + } + } else { + master = false; + if (game()) { + connected = game()->connectToServer(host, port); + } + // We need to learn about failed connections + if (game()) { + connect(game(), SIGNAL(signalConnectionBroken()), + this, SLOT(slotConnectionBroken())); + } + } + setConnected(connected, master); +} + +void KGameDialogNetworkConfig::slotConnectionBroken() +{ + kdDebug(11001) << k_funcinfo << endl; + setConnected(false,false); + KMessageBox::error(this, i18n("Cannot connect to the network")); +} + +void KGameDialogNetworkConfig::setConnected(bool connected, bool master) +{ + if (!connected) { + d->mNetworkLabel->setText(i18n("Network status: No Network")); + d->mInitConnection->setEnabled(true); + d->mDisconnectButton->setEnabled(false); + return; + } + if (master) { + d->mNetworkLabel->setText(i18n("Network status: You are MASTER")); + } else { + d->mNetworkLabel->setText(i18n("Network status: You are connected")); + } + d->mInitConnection->setEnabled(false); + d->mDisconnectButton->setEnabled(true); +} + +void KGameDialogNetworkConfig::submitToKGame(KGame* , KPlayer* ) +{ +} + +void KGameDialogNetworkConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + if (!game()) { + setConnected(false); + return; + } + setConnected(game()->isNetwork(), game()->isMaster()); +} + +void KGameDialogNetworkConfig::setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server) +{ + d->mDefaultPort = port; + d->mDefaultHost = host; + d->mDefaultServer = server; + + d->mConnect->setHost(host); + d->mConnect->setPort(port); + if (server) { + d->mConnect->setDefault(0); + } else { + d->mConnect->setDefault(1); + } +} + +void KGameDialogNetworkConfig::setDiscoveryInfo(const QString& type, const QString& name) +{ + d->mConnect->setType(type); + d->mConnect->setName(name); +} + +/////////////////////////// KGameDialogGeneralConfig ///////////////////////// +class KGameDialogGeneralConfigPrivate +{ +public: + KGameDialogGeneralConfigPrivate() + { + mTopLayout = 0; + mName = 0; + } + + QLineEdit* mName; + + QVBoxLayout* mTopLayout; +}; + +KGameDialogGeneralConfig::KGameDialogGeneralConfig(QWidget* parent, bool initializeGUI) + : KGameDialogConfig(parent) +{ +// kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGameDialogGeneralConfigPrivate; + + if (initializeGUI) { + d->mTopLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + d->mTopLayout->setAutoAdd(true); + + QWidget* nameWidget = new QWidget(this); + QHBoxLayout* l = new QHBoxLayout(nameWidget); + QLabel* nameLabel = new QLabel(i18n("Your name:"), nameWidget); + l->addWidget(nameLabel); + d->mName = new QLineEdit(nameWidget); + l->addWidget(d->mName); + } +} + +KGameDialogGeneralConfig::~KGameDialogGeneralConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogGeneralConfig::setPlayerName(const QString& name) +{ + if (d->mName) { + d->mName->setText(name); + } +} + +QString KGameDialogGeneralConfig::playerName() const +{ + return d->mName ? d->mName->text() : QString::null; +} + +void KGameDialogGeneralConfig::setOwner(KPlayer* p) +{ + if (owner()) { + owner()->disconnect(this); + } + KGameDialogConfig::setOwner(p); + if (!owner()) { + // can this config be used at all? + // maybe call hide() + return; + } + connect(owner(), SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + setPlayerName(p->name()); + //TODO: connect signalPropertyChanged and check for playername changes! +} + +void KGameDialogGeneralConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + if (!g) { + // TODO + // can this config be used at all? + // maybe call hide() + return; + } +} + +void KGameDialogGeneralConfig::setAdmin(bool admin) +{ + KGameDialogConfig::setAdmin(admin); +// enable/disable widgets + +} + +void KGameDialogGeneralConfig::submitToKGame(KGame* g, KPlayer* p) +{ +//FIXME + if (p) { + p->setName(playerName()); + } + if (g) { + } +} + +void KGameDialogGeneralConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p) +{ + if (!prop || !p || p != owner()) { + return; + } + switch (prop->id()) { + case KGamePropertyBase::IdName: + setPlayerName(p->name()); + break; + default: + break; + } +} + +class KGameDialogMsgServerConfigPrivate +{ +public: + KGameDialogMsgServerConfigPrivate() + { + senderLayout = 0; + localLayout = 0; + + changeMaxClients = 0; + changeAdmin= 0; + removeClient= 0; + noAdmin = 0; + + noMaster = 0; + } + + QVBoxLayout* senderLayout; + QHBoxLayout* localLayout; + + QPushButton* changeMaxClients; + QPushButton* changeAdmin; + QPushButton* removeClient; + QLabel* noAdmin; + + QLabel* noMaster; +}; + + +// TODO: change ADMIN ID, remove CLIENTS, change MAXCLIENTS +// we do everything here with QPushButtons as we want to wait a moment before +// continuing - the message must be sent over network first +KGameDialogMsgServerConfig::KGameDialogMsgServerConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ + d = new KGameDialogMsgServerConfigPrivate; + + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + d->senderLayout = new QVBoxLayout(topLayout); + d->localLayout = new QHBoxLayout(topLayout); +} + +KGameDialogMsgServerConfig::~KGameDialogMsgServerConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogMsgServerConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + //TODO display the ID of the admin if we aren't + // connect(g, SIGNAL(signalAdminChanged(int)), this, SLOT(slotChangeIsAdmin(int)));//TODO + if (!game()) { + // we cannot do anything without a KGame object! + setAdmin(false); + return; + } + setAdmin(game()->isAdmin()); + setHasMsgServer(game()->messageServer()); +} + + +void KGameDialogMsgServerConfig::slotChangeMaxClients() +{ + if (!game()) { + kdError(11001) << k_funcinfo << ": no valid game object available!" << endl; + return; + } + if (!game()->isAdmin()) { + kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl; + return; + } + int max; +// edit->setText(QString::number()); // current max clients! //TODO + + QDialog* dialog = new QDialog(); + dialog->setCaption(i18n("Maximal Number of Clients")); + QHBoxLayout* l = new QHBoxLayout(dialog, KDialog::marginHint(), KDialog::spacingHint()); + l->setAutoAdd(true); + + (void) new QLabel(i18n("Maximal number of clients (-1 = infinite):"), dialog); + QLineEdit* edit = new QLineEdit(dialog);//TODO: use KIntNumInput +// edit->setText(QString::number(max)); // current max clients! //TODO + if (dialog->exec() == QDialog::Accepted) { + bool ok; + max = edit->text().toInt(&ok); + if (ok) { + game()->setMaxClients(max); + } + } + +} + +void KGameDialogMsgServerConfig::slotRemoveClient() +{ +} + +void KGameDialogMsgServerConfig::slotChangeAdmin() +{ + if (!game()) { + kdError(11001) << k_funcinfo << ": no valid game object available!" << endl; + return; + } + if (!admin()) { + kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl; + return; + } + //TODO + Q_UINT32 newAdmin = 0; +// newAdmin = ; + game()->electAdmin(newAdmin); +} + +void KGameDialogMsgServerConfig::removeClient(Q_UINT32 /*id*/) +{ +//TODO +} + +void KGameDialogMsgServerConfig::setAdmin(bool a) +{ + if (admin() == a) { + // no need to do anything + return; + } + KGameDialogConfig::setAdmin(a); + if (admin()) { + if (d->noAdmin) { + delete d->noAdmin; + d->noAdmin = 0; + } + d->changeMaxClients = new QPushButton(i18n("Change Maximal Number of Clients"), this); + connect(d->changeMaxClients, SIGNAL(pressed()), this, SLOT(slotChangeMaxClients())); + d->changeAdmin = new QPushButton(i18n("Change Admin"), this); + connect(d->changeAdmin, SIGNAL(pressed()), this, SLOT(slotChangeAdmin())); + d->removeClient = new QPushButton(i18n("Remove Client with All Players"), this); + connect(d->removeClient, SIGNAL(pressed()), this, SLOT(slotRemoveClient())); + d->senderLayout->addWidget(d->changeMaxClients); + d->senderLayout->addWidget(d->changeAdmin); + d->senderLayout->addWidget(d->removeClient); + } else { + if (d->changeMaxClients) { + delete d->changeMaxClients; + d->changeMaxClients = 0; + } + if (d->changeAdmin) { + delete d->changeAdmin; + d->changeAdmin = 0; + } + if (d->removeClient) { + delete d->removeClient; + d->removeClient = 0; + } + d->noAdmin = new QLabel(i18n("Only the admin can configure the message server!"), this); + d->senderLayout->addWidget(d->noAdmin); + } +} + + +void KGameDialogMsgServerConfig::setHasMsgServer(bool has) +{ + if (!has) { + // delete all inputs + if (!d->noMaster) { + d->noMaster = new QLabel(i18n("You don't own the message server"), this); + d->localLayout->addWidget(d->noMaster); + } + return; + } + if (d->noMaster) { + delete d->noMaster; + d->noMaster = 0; + } + //TODO + // list all connections, data (max clients) and so on + // cannot be done above (together with QPushButtons) as it is possible that + // this client is ADMIN but not MASTER (i.e. doesn't own the messageserver) +} + + +class KGameDialogChatConfigPrivate +{ +public: + KGameDialogChatConfigPrivate() + { + mChat = 0; + } + + KGameChat* mChat; +}; + +KGameDialogChatConfig::KGameDialogChatConfig(int chatMsgId, QWidget* parent) + : KGameDialogConfig(parent) +{ + d = new KGameDialogChatConfigPrivate; + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + topLayout->setAutoAdd(true); + QHGroupBox* b = new QHGroupBox(i18n("Chat"), this); + d->mChat = new KGameChat(0, chatMsgId, b); +} + +KGameDialogChatConfig::~KGameDialogChatConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameDialogChatConfig::setKGame(KGame* g) +{ + KGameDialogConfig::setKGame(g); + d->mChat->setKGame(game()); + if (!game()) { + hide(); + } else { + show(); + } +} + +void KGameDialogChatConfig::setOwner(KPlayer* p) +{ + KGameDialogConfig::setOwner(p); + if (!owner()) { + hide(); + return; + } + d->mChat->setFromPlayer(owner()); + show(); +} + + + +class KGameDialogConnectionConfigPrivate +{ +public: + KGameDialogConnectionConfigPrivate() + { + mPlayerBox = 0; + } + + QPtrDict<KPlayer> mItem2Player; + KListBox* mPlayerBox; +}; + +KGameDialogConnectionConfig::KGameDialogConnectionConfig(QWidget* parent) + : KGameDialogConfig(parent) +{ + //TODO: prevent player to ban himself + d = new KGameDialogConnectionConfigPrivate; + QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint()); + topLayout->setAutoAdd(true); + QHGroupBox* b = new QHGroupBox(i18n("Connected Players"), this); + d->mPlayerBox = new KListBox(b); + setMinimumHeight(100); +} + +KGameDialogConnectionConfig::~KGameDialogConnectionConfig() +{ + kdDebug(11001) << k_funcinfo << endl; + // d->mIem2Player.clear(); + delete d; +} + +void KGameDialogConnectionConfig::setKGame(KGame* g) +{ + if (game()) { + disconnect(game(), 0, this, 0); + } + KGameDialogConfig::setKGame(g); + slotClearPlayers(); + if (game()) { +// react to changes in KGame::playerList() + connect(game(), SIGNAL(signalPlayerJoinedGame(KPlayer*)), + this, SLOT(slotPlayerJoinedGame(KPlayer*))); + connect(game(), SIGNAL(signalPlayerLeftGame(KPlayer*)), + this, SLOT(slotPlayerLeftGame(KPlayer*))); + + KGame::KGamePlayerList l = *game()->playerList(); + for (KPlayer* p = l.first(); p; p = l.next()) { + slotPlayerJoinedGame(p); + } + } +} + +void KGameDialogConnectionConfig::setOwner(KPlayer* p) +{ + KGameDialogConfig::setOwner(p); +} + +void KGameDialogConnectionConfig::setAdmin(bool a) +{ + if (!game()) {// not possible... in theory + return; + } + if (admin()) { + disconnect(game(), SIGNAL(executed(QListBoxItem*)), this, 0); + } + KGameDialogConfig::setAdmin(a); + if (admin()) { + connect(d->mPlayerBox, SIGNAL(executed(QListBoxItem*)), this, + SLOT(slotKickPlayerOut(QListBoxItem*))); + } +} + +QListBoxItem* KGameDialogConnectionConfig::item(KPlayer* p) const +{ + QPtrDictIterator<KPlayer> it(d->mItem2Player); + while (it.current()) { + if (it.current() == p) { + return (QListBoxItem*)it.currentKey(); + } + ++it; + } + return 0; +} + +void KGameDialogConnectionConfig::slotClearPlayers() +{ + QPtrDictIterator<KPlayer> it(d->mItem2Player); + while (it.current()) { + slotPlayerLeftGame(it.current()); + ++it; + } + + if (d->mItem2Player.count() > 0) { + kdWarning(11001) << k_funcinfo << ": itemList wasn't cleared properly" << endl; + d->mItem2Player.clear(); + } + if (d->mPlayerBox->count() > 0) { + kdWarning(11001) << k_funcinfo << ": listBox wasn't cleared properly" << endl; + d->mPlayerBox->clear(); + } + +} + +void KGameDialogConnectionConfig::slotPlayerJoinedGame(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": Cannot add NULL player" << endl; + } + if (d->mItem2Player[p]) { + kdError(11001) << k_funcinfo << ": attempt to double add player" << endl; + return; + } + kdDebug(11001) << k_funcinfo << ": add player " << p->id() << endl; + QListBoxText* t = new QListBoxText(p->name()); + d->mItem2Player.insert(t, p); + d->mPlayerBox->insertItem(t); + + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + +} + +void KGameDialogConnectionConfig::slotPlayerLeftGame(KPlayer* p) +{ + // disconnect first + this->disconnect(p); + if (!item(p)) { + kdError(11001) << k_funcinfo << ": cannot find " << p->id() + << " in list" << endl; + return; + } + d->mPlayerBox->removeItem(d->mPlayerBox->index(item(p))); + +} + +void KGameDialogConnectionConfig::slotKickPlayerOut(QListBoxItem* item) +{ + kdDebug(11001) << "kick player out" << endl; + KPlayer* p = d->mItem2Player[item]; + if (!p) { + kdError(11001) << "invalid item selected - no player found" << endl; + return; + } + if (!game()) { + kdWarning(11001) << "no game set" << endl; + return; + } + if (!admin()) { + kdDebug(11001) << "Only the ADMIN can kick players" << endl; + return; + } + if (p == owner()) { // you wanna ban the ADMIN ?? + kdDebug(11001) << "you cannot kick the ADMIN" << endl; + return; + } + + if (KMessageBox::questionYesNo(this, i18n("Do you want to ban player \"%1\" from the game?").arg( + p->name()), QString::null, i18n("Ban Player"), i18n("Do Not Ban")) == KMessageBox::Yes) { + kdDebug(11001) << "will remove player " << p << endl; + game()->removePlayer(p); +// d->mPlayerBox->removeItem(d->mPlayerBox->index(item)); // should be done by signalPlayerLeftGame + } else { + kdDebug(11001) << "will NOT remove player " << p << endl; + } +} + +void KGameDialogConnectionConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player) +{ + if(prop->id() == KGamePropertyBase::IdName) { + QListBoxText* old = 0; + QPtrDictIterator<KPlayer> it(d->mItem2Player); + while (it.current() && !old) { + if (it.current() == player) { + old = (QListBoxText*)it.currentKey(); + } + ++it; + } + QListBoxText* t = new QListBoxText(player->name()); + d->mPlayerBox->changeItem(t, d->mPlayerBox->index(old)); + d->mItem2Player.remove(old); + d->mItem2Player.insert(t, player); + } +} + diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.h b/libkdegames/kgame/dialogs/kgamedialogconfig.h new file mode 100644 index 00000000..b7d30b23 --- /dev/null +++ b/libkdegames/kgame/dialogs/kgamedialogconfig.h @@ -0,0 +1,362 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ + +// NAMING +// please follow these naming rules if you add/change classes: +// the main dialog is named KGameDialog and the base config widget +// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where +// XYZ = the name of the config widget, like "general" or "network") and are +// inherited from KGameDialogConfig. + +#ifndef __KGAMEDIALOGCONFIG_H__ +#define __KGAMEDIALOGCONFIG_H__ + +#include <qwidget.h> +#include <kdemacros.h> + +class QGridLayout; +class QVBoxLayout; +class QListBoxItem; + +class KGame; +class KPlayer; +class KGamePropertyBase; + +class KGameDialogConfigPrivate; +/** + * Base class for configuration widgets. + * + * You can inherit from this and implement @ref submitToKGame, @ref + * setOwner and @ref setKGame to create your personal @ref KGame configuration widget :-) + * @short Base class for configuration widgets + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KGameDialogConfig : public QWidget +{ + Q_OBJECT +public: + KGameDialogConfig(QWidget* parent = 0); + virtual ~KGameDialogConfig(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p) = 0; + + /** + * The owner player of the dialog has been changed. The default + * changes the pointer for owner so don't forget to call the + * default implementation if you overwrite this! + * + * You can use this e.g. to change a line edit widget containing the + * player name. + * + * Note: even NULL players are allowed! + * @param p The new owner player of the dialog + **/ + virtual void setOwner(KPlayer* p); + + /** + * The KGame object of the dialog has been changed. The default + * implementation changes the pointer for game so don't forget to + * call the default implementation if you overwrite this! + * + * You can use this e.g. to re-read the min/max player settings. + * @param g The KGame object + **/ + virtual void setKGame(KGame* g); + + /** + * The admin status has been changed. + * If the KGame object of this config widget is the + * admin the user is allowed to configure it. Otherwise most + * widgets will have to be disabled. Note that you don't necessarily + * need to deactivate all widget - e.g. the player name must be + * configured by the player. Mainly the KGame configuration can be done + * by the admin only. + * + * By default this does nothing. Changes the value for admin so + * don't forget to call the default implementation in derived classes! + * @param admin Whether the KGame object of this dialog can be + * configured + **/ + virtual void setAdmin(bool admin); + + /** + * A pointer to the KGame object that has been set by @ref setKGame. + * + * Note that NULL is allowed! + * @return The KGame object assigned to this dialog + **/ + KGame* game() const; + + /** + * A pointer to the KPlayer object that has been set by @ref + * setOwner. + * + * Note that NULL is allowed! + * @return The owner of the dialog + **/ + KPlayer* owner() const; + + /** + * @return True if the owner is ADMIN otherwise FALSE. See also + * @ref setAdmin + **/ + bool admin() const; + +protected: + +private: + KGameDialogConfigPrivate* d; +}; + +/** + * The main game configuration widget. + * + * It currently contains a line edit for the name of the player only. You can + * add widgets by using the KGameDialogGeneralConfig as parent parameter as it + * uses QLayout::autoAdd == true. + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameDialogGeneralConfigPrivate; +class KGameDialogGeneralConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + /** + * Construct a KGameDialogGeneralConfig. Currently it contains a line + * edit widget to change the player name only. + * + * If you just want to add more widgets you can just create your widgets + * with the KGameDialogGeneralConfig as parent as it uses + * QLayout::setAutoAdd(true). + * + * @param parent Parent widget for this dialog. + * @param initializeGUI If you really don't want to use the + * predefined widget and/or layout use FALSE here. Note that then none + * of the predefined widgets (currently only the name of the player) + * will exist anymore. + * + **/ + KGameDialogGeneralConfig(QWidget* parent = 0, bool initializeGUI = true); + virtual ~KGameDialogGeneralConfig(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p); + + /** + * Change the owner of the config widget. + * + * Changes the playername in the line edit + * @param p The new owner player + **/ + virtual void setOwner(KPlayer* p); + + /** + * See @ref KGameDialogConfig::setKGame + * + * Sets the default values of all KGame related predefined widgets + * (currently none) + **/ + virtual void setKGame(KGame* g); + + /** + * See @ref KGameDialogConfig::setAdmin + * + * This deactivates the min/max player widgets + **/ + virtual void setAdmin(bool admin); + +protected slots: + void slotPropertyChanged(KGamePropertyBase*, KPlayer*); + +protected: + void setPlayerName(const QString& name); + + QString playerName() const; + +private: + KGameDialogGeneralConfigPrivate* d; +}; + +class KGameDialogNetworkConfigPrivate; +class KDE_EXPORT KGameDialogNetworkConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogNetworkConfig(QWidget* parent = 0); + virtual ~KGameDialogNetworkConfig(); + + + void disableInitConnection(); + + /** + * Called by @ref KGameDialog to submit all settings to the KGame + * Object. + * You have to replace this if you add your own widgets! + * @param g A pointer to your KGame. + * @param p A pointer to the player owning this dialog + **/ + virtual void submitToKGame(KGame* g, KPlayer* p); + + virtual void setKGame(KGame* g); + + /** + * This sets the default port and host used in @ref KGameConnectDialog. + * The user will be able to change these defaults! + * + * If you don't call this then host "localhost" and port "0" is used. + * You are strongly encouraged to change at least the port! + * @param port The default port to connect to / listen on + * @param host The default host to connect to + **/ + void setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server=true); + + /** + * Set service type that will be published or browsed for and game name that will be displayed in + * server browser. Without this publishing and discovery of LAN servers will not be enabled. + * @param name Game name. Important only for server mode. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + * @param type Service type (something like _kwin4._tcp). It should be unique for application. + * @since 3.4 + **/ + void setDiscoveryInfo(const QString& type, const QString& name=QString::null); + +signals: + /** + * This signal is emmited if the user changes the server type (client/server) + * in the network configuration dialog. + * + * @param t - type type (0/1) of the connection + **/ + void signalServerTypeChanged(int); + + +protected: + void setConnected(bool connected, bool master = false); + +protected slots: + void slotInitConnection(); + void slotExitConnection(); + void slotConnectionBroken(); + + +private: + KGameDialogNetworkConfigPrivate* d; +}; + +class KGameDialogMsgServerConfigPrivate; +class KGameDialogMsgServerConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogMsgServerConfig(QWidget* parent = 0); + virtual ~KGameDialogMsgServerConfig(); + + virtual void submitToKGame(KGame*, KPlayer*) {} + + void setHasMsgServer(bool); + + virtual void setKGame(KGame* g); + virtual void setAdmin(bool); + +protected slots: + void slotChangeMaxClients(); + void slotChangeAdmin(); + void slotRemoveClient(); + +protected: + void removeClient(Q_UINT32 id); + +private: + KGameDialogMsgServerConfigPrivate* d; +}; + +class KGameDialogChatConfigPrivate; +/** + * This is not really a configuration widget but rather a simple chat widget. + * This widget does nothing but just providing a @ref KGameChat object. + * @short A chat widget inside a @ref KGameDialog + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameDialogChatConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogChatConfig(int chatMsgId, QWidget* parent = 0); + virtual ~KGameDialogChatConfig(); + + virtual void setKGame(KGame* g); + virtual void setOwner(KPlayer* p); + + virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); } + +private: + KGameDialogChatConfigPrivate* d; +}; + +/** + * @short Lists all connected players and gives the ability to kick them off the + * game + **/ +class KGameDialogConnectionConfigPrivate; +class KGameDialogConnectionConfig : public KGameDialogConfig +{ + Q_OBJECT +public: + KGameDialogConnectionConfig(QWidget* parent = 0); + virtual ~KGameDialogConnectionConfig(); + + virtual void setKGame(KGame* g); + virtual void setOwner(KPlayer* p); + virtual void setAdmin(bool admin); + + virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); } + +protected: + /** + * @param p A player + * @return The QListBoxItem that belongs to the player @p p + **/ + QListBoxItem* item(KPlayer* p) const; + +protected slots: + void slotKickPlayerOut(QListBoxItem* item); + void slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p); + void slotPlayerLeftGame(KPlayer* p); + void slotPlayerJoinedGame(KPlayer* p); + void slotClearPlayers(); + +private: + KGameDialogConnectionConfigPrivate* d; + +}; +#endif diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.cpp b/libkdegames/kgame/dialogs/kgameerrordialog.cpp new file mode 100644 index 00000000..1750892c --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameerrordialog.cpp @@ -0,0 +1,129 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 <kmessagebox.h> +#include <klocale.h> +#include <kdebug.h> + +#include "kgame.h" + +#include "kgameerrordialog.h" + +class KGameErrorDialogPrivate +{ +public: + KGameErrorDialogPrivate() + { + mGame = 0; + } + + const KGame* mGame; +}; + +KGameErrorDialog::KGameErrorDialog(QWidget* parent) : QObject(parent) +{ + d = new KGameErrorDialogPrivate; +} + +KGameErrorDialog::~KGameErrorDialog() +{ + delete d; +} + +void KGameErrorDialog::setKGame(const KGame* g) +{ + slotUnsetKGame(); + d->mGame = g; + + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + +// the error signals: + connect(d->mGame, SIGNAL(signalNetworkErrorMessage(int, QString)), + this, SLOT(slotError(int, QString))); + connect(d->mGame, SIGNAL(signalConnectionBroken()), + this, SLOT(slotServerConnectionLost())); + connect(d->mGame, SIGNAL(signalClientDisconnected(Q_UINT32,bool)), + this, SLOT(slotClientConnectionLost(Q_UINT32,bool))); +} + +void KGameErrorDialog::slotUnsetKGame() +{ + if (d->mGame) { + disconnect(d->mGame, 0, this, 0); + } + d->mGame = 0; +} + +void KGameErrorDialog::error(const QString& errorText, QWidget* parent) +{ KMessageBox::error(parent, errorText); } + +void KGameErrorDialog::slotServerConnectionLost() +{ +// TODO: add IP/port of the server + QString message = i18n("Connection to the server has been lost!"); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::slotClientConnectionLost(Q_UINT32 /*id*/,bool) +{ +//TODO: add IP/port of the client + QString message; +// if (c) { +// message = i18n("Connection to client has been lost!\nID: %1\nIP: %2").arg(c->id()).arg(c->IP()); +// } else { +// message = i18n("Connection to client has been lost!"); +// } + message = i18n("Connection to client has been lost!"); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::slotError(int errorNo, QString text) +{ + QString message = i18n("Received a network error!\nError number: %1\nError message: %2").arg(errorNo).arg(text); + error(message, (QWidget*)parent()); +} + +void KGameErrorDialog::connectionError(QString s) +{ + QString message; + if (s.isNull()) { + message = i18n("No connection could be created."); + } else { + message = i18n("No connection could be created.\nThe error message was:\n%1").arg(s); + } + error(message, (QWidget*)parent()); +} + + + +// should become the real dialog - currently we just use messageboxes +// -> maybe unused forever +KGameErrorMessageDialog::KGameErrorMessageDialog(QWidget* parent) + : KDialogBase(Plain, i18n("Error"), Ok, Ok, parent, 0, true, true) +{ +} + +KGameErrorMessageDialog::~KGameErrorMessageDialog() +{ +} + + + +#include "kgameerrordialog.moc" diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.h b/libkdegames/kgame/dialogs/kgameerrordialog.h new file mode 100644 index 00000000..c1dbd1ca --- /dev/null +++ b/libkdegames/kgame/dialogs/kgameerrordialog.h @@ -0,0 +1,113 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 __KGAMEERRORDIALOG_H__ +#define __KGAMEERRORDIALOG_H__ + +#include <kdialogbase.h> + +class KGame; +class KGameErrorDialogPrivate; + +/** + * Use error(), warning() and information() to display the information about a + * network game. Maybe a better solution is to use KMessageBoxes + * You can connect to the public slots, too - they will call the static + * functions, so that you can always have a KGameErrorDialog object lying around + * without losing much memory (a KGameErrorMessageDialog Object will be + * created) + * @short Error handling for KGame + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameErrorDialog : public QObject +{ + Q_OBJECT +public: + KGameErrorDialog(QWidget* parent); + ~KGameErrorDialog(); + + /** + * Automatically connects the KGame object to all error dependant slots. + * Create a KGameErrorDialog object, call this function and forget + * everything. + * @param g The KGame which will emit the erorrs (or not ;-) ) + **/ + void setKGame(const KGame* g); + + /** + * KGame couldn't establish a connection. Use this if + * KGame::initConnection returns false + * @param s A string that describes the error further (like port is + * already in use). Will be ignored if QString::null + **/ + void connectionError(QString s = QString::null); + +public slots: + void slotError(int error, QString text); + + /** + * The connection to the @ref KMessageServer has been lost + * + * See @ref KGameNetwork::signalConnectionBroken + **/ + void slotServerConnectionLost(); + + /** + * The connection to a client has been lost by accident + * + * See @ref KGameNetwork::signalClientDisconnected + **/ + void slotClientConnectionLost(Q_UINT32 clientID,bool broken); + + /** + * Unsets a @ref KGame which has been set using @ref setKGame before. + * This is called automatically when the @ref KGame object is destroyed + * and you normally don't have to call this yourself. + * + * Note that @ref setKGame also unsets an already existing @ref KGame + * object if exising. + **/ + void slotUnsetKGame(); + +protected: + void error(const QString& errorText, QWidget* parent = 0); + +private: + KGameErrorDialogPrivate* d; +}; + +/** + * The real class for error messages. KGameErrorDialog uses this to create error + * messages (not yet). + * Use @ref KGameErrorDialog instead. + * @short Internally used by @ref KGameErrorDialog + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameErrorMessageDialog : public KDialogBase +{ + Q_OBJECT +public: + KGameErrorMessageDialog(QWidget* parent); + ~KGameErrorMessageDialog(); + +private: +}; + +#endif diff --git a/libkdegames/kgame/kgame.cpp b/libkdegames/kgame/kgame.cpp new file mode 100644 index 00000000..101dbfcc --- /dev/null +++ b/libkdegames/kgame/kgame.cpp @@ -0,0 +1,1475 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgame.h" +#include "kgame.moc" +#include "kgamepropertyhandler.h" +#include "kgameproperty.h" +#include "kplayer.h" +#include "kgameio.h" +#include "kgameerror.h" +#include "kgamesequence.h" + +#include "kgamemessage.h" + +#include <unistd.h> +#include <stdio.h> +#include <assert.h> + +#include <qbuffer.h> +#include <qtimer.h> +#include <qptrqueue.h> +#include <qfile.h> + +#include <klocale.h> +#include <krandomsequence.h> +#include <kdebug.h> + +#define KGAME_LOAD_COOKIE 4210 + +// try to place as much as possible here +// many things are *not* possible here as KGame has to use some inline function +class KGamePrivate +{ +public: + KGamePrivate() + { + mUniquePlayerNumber = 0; + mPolicy=KGame::PolicyLocal; + mGameSequence = 0; + } + + int mUniquePlayerNumber; + QPtrQueue<KPlayer> mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu + KRandomSequence* mRandom; + KGame::GamePolicy mPolicy; + KGameSequence* mGameSequence; + + + KGamePropertyHandler* mProperties; + + // player lists + KGame::KGamePlayerList mPlayerList; + KGame::KGamePlayerList mInactivePlayerList; + + //KGamePropertys + KGamePropertyInt mMaxPlayer; + KGamePropertyUInt mMinPlayer; + KGamePropertyInt mGameStatus; // Game running? + QValueList<int> mInactiveIdList; + +}; + +// ------------------- GAME CLASS -------------------------- +KGame::KGame(int cookie,QObject* parent) : KGameNetwork(cookie,parent) +{ + kdDebug(11001) << k_funcinfo << " - " << this << ", sizeof(KGame)=" << sizeof(KGame) << endl; + d = new KGamePrivate; + + d->mProperties = new KGamePropertyHandler(this); + + d->mProperties->registerHandler(KGameMessage::IdGameProperty, + this,SLOT(sendProperty(int, QDataStream&, bool* )), + SLOT(emitSignal(KGamePropertyBase *))); + d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers")); + d->mMaxPlayer.setLocal(-1); // Infinite + d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers")); + d->mMinPlayer.setLocal(0); // Always ok + d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus")); + d->mGameStatus.setLocal(Init); + // d->mUniquePlayerNumber = 0; + d->mRandom = new KRandomSequence; + d->mRandom->setSeed(0); + + connect(this, SIGNAL(signalClientConnected(Q_UINT32)), + this, SLOT(slotClientConnected(Q_UINT32))); + connect(this, SIGNAL(signalClientDisconnected(Q_UINT32,bool)), + this, SLOT(slotClientDisconnected(Q_UINT32,bool))); + connect(this, SIGNAL(signalConnectionBroken()), + this, SLOT(slotServerDisconnected())); + + setGameSequence(new KGameSequence()); + + // BL: FIXME This signal does no longer exist. When we are merging + // MH: super....and how do I find out about the lost conenction now? + // KGame and KGameNetwork, this could be improved! +// connect(this,SIGNAL(signalConnectionLost(KGameClient *)), +// this,SLOT(slotConnectionLost(KGameClient *))); +} + +KGame::~KGame() +{ + kdDebug(11001) << k_funcinfo << endl; +// Debug(); + reset(); + delete d->mGameSequence; + delete d->mRandom; + delete d; + kdDebug(11001) << k_funcinfo << " done" << endl; +} + +bool KGame::reset() +{ + deletePlayers(); + deleteInactivePlayers(); + return true; +} + +void KGame::deletePlayers() +{ +// kdDebug(11001) << k_funcinfo << endl; + KGamePlayerList tmp = d->mPlayerList; // in case of PolicyClean player=d->mPlayerList.first() is infinite + KPlayer *player; + while((player=tmp.first())) + { + delete player; // delete and removes the player + tmp.removeFirst(); + } +// kdDebug(11001) << k_funcinfo << " done" << endl; +} + +void KGame::deleteInactivePlayers() +{ + KPlayer *player; + while((player=d->mInactivePlayerList.first())) + { + //player->setGame(0); // prevent call backs + d->mInactivePlayerList.remove(player); + delete player; + } +} + +bool KGame::load(QString filename,bool reset) +{ + if (filename.isNull()) + { + return false; + } + QFile f(filename); + if (!f.open(IO_ReadOnly)) + { + return false; + } + QDataStream s( &f ); + load(s,reset); + f.close(); + return true; +} + +bool KGame::load(QDataStream &stream,bool reset) +{ return loadgame(stream, false,reset); } + +bool KGame::loadgame(QDataStream &stream, bool network,bool resetgame) +{ + // Load Game Data + + // internal data + Q_INT32 c; + stream >> c; // cookie + + if (c!=cookie()) + { + kdWarning(11001) << "Trying to load different game version we="<<cookie() << " saved=" << c << endl; + bool result=false; + emit signalLoadError(stream,network,(int)c,result); + return result; + } + if (resetgame) reset(); + + uint i; + stream >> i; +// setPolicy((GamePolicy)i); + + stream >> d->mUniquePlayerNumber; + + if (gameSequence()) + { + gameSequence()->setCurrentPlayer(0); // TODO !!! + } + int newseed; + stream >> newseed; + d->mRandom->setSeed(newseed); + + // Switch off the direct emitting of signals while + // loading properties. This can cause inconsistencies + // otherwise if a property emits and this emit accesses + // a property not yet loaded + // Note we habe to have this external locking to prevent the games unlocking + // to access the players + dataHandler()->lockDirectEmit(); + KPlayer *player; + for ( player=playerList()->first(); player != 0; player=playerList()->next() ) + { + player->dataHandler()->lockDirectEmit(); + // kdDebug(11001) << "Player "<<player->id() << " to indirect emit" <<endl; + } + + // Properties + dataHandler()->load(stream); + + // If there is additional data to be loaded before players are loaded then do + // this here. + emit signalLoadPrePlayers(stream); + + // Load Playerobjects + uint playercount; + stream >> playercount; + kdDebug(11001) << "Loading KGame " << playercount << " KPlayer objects " << endl; + for (i=0;i<playercount;i++) + { + KPlayer *newplayer=loadPlayer(stream,network); + systemAddPlayer(newplayer); + } + + Q_INT16 cookie; + stream >> cookie; + if (cookie==KGAME_LOAD_COOKIE) { + kdDebug(11001) << " Game loaded propertly"<<endl; + } else { + kdError(11001) << " Game loading error. probably format error"<<endl; + } + + // Switch back on the direct emitting of signals and emit the + // queued signals. + // Note we habe to have this external locking to prevent the games unlocking + // to access the players + dataHandler()->unlockDirectEmit(); + for ( player=playerList()->first(); player != 0; player=playerList()->next() ) + { + player->dataHandler()->unlockDirectEmit(); + // kdDebug(11001) << "Player "<<player->id() << " to direct emit" <<endl; + } + + emit signalLoad(stream); + return true; +} + +bool KGame::save(QString filename,bool saveplayers) +{ + if (filename.isNull()) + { + return false; + } + QFile f(filename); + if (!f.open(IO_WriteOnly)) + { + return false; + } + QDataStream s( &f ); + save(s,saveplayers); + f.close(); + return true; +} + +bool KGame::save(QDataStream &stream,bool saveplayers) +{ return savegame(stream, false,saveplayers); } + +bool KGame::savegame(QDataStream &stream,bool /*network*/,bool saveplayers) +{ + // Save Game Data + + // internal variables + Q_INT32 c=cookie(); + stream << c; + + uint p=(uint)policy(); + stream << p; + stream << d->mUniquePlayerNumber; + int newseed=(int)d->mRandom->getLong(65535); + stream << newseed; + d->mRandom->setSeed(newseed); + + // Properties + dataHandler()->save(stream); + + // Save all data that need to be saved *before* the players are saved + emit signalSavePrePlayers(stream); + + if (saveplayers) + { + savePlayers(stream,playerList()); + } + else + { + stream << (uint)0; // no players saved + } + + stream << (Q_INT16)KGAME_LOAD_COOKIE; + + emit signalSave(stream); + return true; +} + +void KGame::savePlayer(QDataStream &stream,KPlayer* p) +{ +// this could be in KGameMessage as well + stream << (Q_INT32)p->rtti(); + stream << (Q_INT32)p->id(); + stream << (Q_INT32)p->calcIOValue(); + p->save(stream); +} + +void KGame::savePlayers(QDataStream &stream, KGamePlayerList *list) +{ + if (!list) + { + list=playerList(); + } + + Q_INT32 cnt=list->count(); + kdDebug(11001) << "Saving KGame " << cnt << " KPlayer objects " << endl; + stream << cnt; + KPlayer *player; + for ( player=list->first(); player != 0; player=list->next() ) + { + savePlayer(stream,player); + } +} + +KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/) +{ + kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; + return new KPlayer; +} +KPlayer *KGame::loadPlayer(QDataStream& stream,bool isvirtual) +{ + Q_INT32 rtti,id,iovalue; + stream >> rtti >> id >> iovalue; + KPlayer *newplayer=findPlayer(id); + if (!newplayer) + { + kdDebug(11001) << k_funcinfo << "Player "<< id << " not found...asking user to create one " << endl; + newplayer=createPlayer(rtti,iovalue,isvirtual); + //emit signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this); + } + /* + if (!newplayer) + { + kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl; + newplayer=new KPlayer; + } + else + { + kdDebug(11001) << " USER Player " << newplayer << " done player->rtti=" << newplayer->rtti() << " rtti=" << rtti << endl; + } + */ + newplayer->load(stream); + if (isvirtual) + { + newplayer->setVirtual(true); + } + return newplayer; +} + +// ----------------- Player handling ----------------------- + +KPlayer * KGame::findPlayer(Q_UINT32 id) const +{ + for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it) + { + if (it.current()->id() == id) + { + return it.current(); + } + } + for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it) + { + if (it.current()->id() == id) + { + return it.current(); + } + } + return 0; +} + +// it is necessary that addPlayer and systemAddPlayer are called in the same +// order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must +// not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the +// mAddPlayerList would get confused. Should be no problem as long as comServer +// and the clients are working correctly. +// BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar) +// does, we would be in trouble... +void KGame::addPlayer(KPlayer* newplayer) +{ + kdDebug(11001) << k_funcinfo << ": " << "; maxPlayers=" << maxPlayers() << " playerCount=" << playerCount() << endl; + if (!newplayer) + { + kdFatal(11001) << "trying to add NULL player in KGame::addPlayer()" << endl; + return ; + } + + if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers()) + { + kdWarning(11001) << "cannot add more than " << maxPlayers() << " players - deleting..." << endl; + delete newplayer; + return; + } + + if (newplayer->id() == 0) + { + d->mUniquePlayerNumber++; + newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId())); + kdDebug(11001) << k_funcinfo << "NEW!!! player " << newplayer << " now has id " << newplayer->id() << endl; + } + else + { + // this could happen in games which use their own ID management by certain + // reasons. that is NOT recommended + kdDebug(11001) << k_funcinfo << "player " << newplayer << " already has an id: " << newplayer->id() << endl; + } + + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + // We distinguis here what policy we have + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemAddPlayer(newplayer); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + savePlayer(stream,newplayer); + // Store the player for delayed clean adding + if (policy()==PolicyClean) + { + d->mAddPlayerList.enqueue(newplayer); + } + sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0); + } +} + +void KGame::systemAddPlayer(KPlayer* newplayer) +{ + if (!newplayer) + { + kdFatal(11001) << "trying to add NULL player in KGame::systemAddPlayer()" << endl; + return ; + } + if (newplayer->id() == 0) + { + kdWarning(11001) << k_funcinfo << "player " << newplayer << " has no ID" << endl; + } + + if (findPlayer(newplayer->id())) + { + kdError(11001) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again" << endl; + delete newplayer; + } + else + { + kdDebug(11001) << "Trying to add player " << newplayer <<" maxPlayers="<<maxPlayers()<<" playerCount="<<playerCount() << endl; + // Add the player to the game + d->mPlayerList.append(newplayer); + newplayer->setGame(this); + kdDebug(11001) << "Player: isVirtual=" << newplayer->isVirtual() << endl; + kdDebug(11001) << " id=" << newplayer->id() << " #Players=" + << d->mPlayerList.count() << " added " << newplayer + << " (virtual=" << newplayer->isVirtual() << ")" << endl; + emit signalPlayerJoinedGame(newplayer); + } +} + +// Called by the KPlayer destructor +void KGame::playerDeleted(KPlayer *player) +{ + kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemRemovePlayer(player,false); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (!player->isVirtual()) + { + kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl; + sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0); + } + } +} + +bool KGame::removePlayer(KPlayer * player, Q_UINT32 receiver) +{//transmit to all clients, or to receiver only + if (!player) + { + kdFatal(11001) << "trying to remove NULL player in KGame::removePlayer()" << endl; + return false; + } + kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemRemovePlayer(player,true); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl; + sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver); + } + return true; + // we will receive the message in networkTransmission() +} + +void KGame::systemRemovePlayer(KPlayer* player,bool deleteit) +{ + kdDebug(11001) << k_funcinfo << endl; + if (!player) + { + kdWarning(11001) << "cannot remove NULL player" << endl; + return; + } + if (!systemRemove(player,deleteit)) + { + kdWarning(11001) << "player " << player << "(" << player->id() << ") Could not be found!" << endl; + } + + if (gameStatus()==(int)Run && playerCount()<minPlayers()) + { + kdWarning(11001) << k_funcinfo ": not enough players, PAUSING game\n" << endl; + setGameStatus(Pause); + } +} + +bool KGame::systemRemove(KPlayer* p,bool deleteit) +{ + if (!p) + { + kdWarning(11001) << "cannot remove NULL player" << endl; + return false; + } + bool result; + kdDebug(11001) << k_funcinfo << ": Player (" << p->id() << ") to be removed " << p << endl; + + if (d->mPlayerList.count() == 0) + { + result = false; + } + else + { + result = d->mPlayerList.remove(p); + } + + emit signalPlayerLeftGame(p); + + p->setGame(0); + if (deleteit) + { + delete p; + } + + return result; +} + +bool KGame::inactivatePlayer(KPlayer* player) +{ + if (!player) + { + return false; + } + kdDebug(11001) << "Inactivate player " << player->id() << endl; + + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemInactivatePlayer(player); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer); + } + + return true; +} + +bool KGame::systemInactivatePlayer(KPlayer* player) +{ + if (!player || !player->isActive()) + { + return false; + } + kdDebug(11001) << " Inactivate player " << player->id() << endl; + + int pid=player->id(); + // Virtual players cannot be deactivated. They will be removed + if (player->isVirtual()) + { + systemRemovePlayer(player,true); + } + else + { + d->mPlayerList.remove(player); + d->mInactivePlayerList.prepend(player); + player->setActive(false); + } + emit signalPlayerLeftGame(player); + if (isAdmin()) + { + d->mInactiveIdList.prepend(pid); + } + return true; +} + +bool KGame::activatePlayer(KPlayer * player) +{ + if (!player) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + systemActivatePlayer(player); + } + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer); + } + return true; +} + +bool KGame::systemActivatePlayer(KPlayer* player) +{ + if (!player || player->isActive()) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl; + + d->mInactivePlayerList.remove(player); + player->setActive(true); + addPlayer(player); + if (isAdmin()) + { + d->mInactiveIdList.remove(player->id()); + } + return true; +} + +// -------------------- Properties --------------------------- + +void KGame::setMaxPlayers(uint maxnumber) +{ if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } } + +void KGame::setMinPlayers(uint minnumber) +{ if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } } + +uint KGame::minPlayers() const +{ return d->mMinPlayer.value(); } + +int KGame::maxPlayers() const +{ return d->mMaxPlayer.value(); } + +uint KGame::playerCount() const +{ return d->mPlayerList.count(); } + +int KGame::gameStatus() const +{ return d->mGameStatus.value(); } + +bool KGame::isRunning() const +{ return d->mGameStatus.value() == Run; } + +KGamePropertyHandler* KGame::dataHandler() const +{ return d->mProperties; } + + +KGame::KGamePlayerList* KGame::inactivePlayerList() +{ return &d->mInactivePlayerList; } + +const KGame::KGamePlayerList* KGame::inactivePlayerList() const +{ return &d->mInactivePlayerList; } + +KGame::KGamePlayerList* KGame::playerList() +{ return &d->mPlayerList; } + +const KGame::KGamePlayerList* KGame::playerList() const +{ return &d->mPlayerList; } + +KRandomSequence* KGame::random() const +{ return d->mRandom; } + + +bool KGame::sendPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender) +{ + if (!player) + { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return false; + } + if (!isRunning()) + { + kdError(11001) << k_funcinfo << ": game not running" << endl; + return false; + } + + kdDebug(11001) << k_funcinfo << ": transmitting playerInput over network" << endl; + sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender); + return true; +} + +bool KGame::systemPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender) +{ + if (!player) + { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return false; + } + if (!isRunning()) + { + kdError(11001) << k_funcinfo << ": game not running" << endl; + return false; + } + kdDebug(11001) << "KGame: Got playerInput from messageServer... sender: " << sender << endl; + if (playerInput(msg,player)) + { + playerInputFinished(player); + } + else + { + kdDebug(11001) << k_funcinfo<<": switching off player input"<<endl; + // TODO: (MH 03-2003): We need an return option from playerInput so that + // the player's is not automatically disabled here + if (!player->asyncInput()) + { + player->setTurn(false); // in turn based games we have to switch off input now + } + } + return true; +} + + +KPlayer * KGame::playerInputFinished(KPlayer *player) +{ + kdDebug(11001) << k_funcinfo<<"player input finished for "<<player->id()<<endl; + // Check for game over and if not allow the next player to move + int gameOver = 0; + if (gameSequence()) + { + gameSequence()->setCurrentPlayer(player); + } + // do not call gameSequence()->checkGameOver() to keep backward compatibility! + gameOver = checkGameOver(player); + if (gameOver!=0) + { + if (player) + { + player->setTurn(false); + } + setGameStatus(End); + emit signalGameOver(gameOver,player,this); + } + else if (!player->asyncInput()) + { + player->setTurn(false); // in turn based games we have to switch off input now + if (gameSequence()) + { + QTimer::singleShot(0,this,SLOT(prepareNext())); + } + } + return player; +} + +// Per default we do not do anything +int KGame::checkGameOver(KPlayer *player) +{ + if (gameSequence()) + { + return gameSequence()->checkGameOver(player); + } + return 0; +} + +void KGame::setGameSequence(KGameSequence* sequence) +{ + delete d->mGameSequence; + d->mGameSequence = sequence; + if (d->mGameSequence) + { + d->mGameSequence->setGame(this); + } +} + +KGameSequence* KGame::gameSequence() const +{ + return d->mGameSequence; +} + +void KGame::prepareNext() +{ + if (gameSequence()) + { + // we don't call gameSequence->nextPlayer() to keep old code working + nextPlayer(gameSequence()->currentPlayer()); + } +} + +KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive) +{ + if (gameSequence()) + { + return gameSequence()->nextPlayer(last, exclusive); + } + return 0; +} + +void KGame::setGameStatus(int status) +{ + kdDebug(11001) << k_funcinfo << ": GAMESTATUS CHANGED to" << status << endl; + if (status==(int)Run && playerCount()<minPlayers()) + { + kdDebug(11001) << k_funcinfo << ": not enough players, pausing game\n" << endl; + status=Pause; + } + d->mGameStatus = status; +} + +void KGame::networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 /*clientID*/) +{//clientID is unused + // message targets a playerobject. If we find it we forward the message to the + // player. Otherwise we proceed here and hope the best that the user processes + // the message + +// kdDebug(11001) << k_funcinfo << ": we="<<(int)gameId()<<" id="<<msgid<<" recv=" << receiver << " sender=" << sender << endl; + + + // *first* notice the game that something has changed - so no return prevents + // this + emit signalMessageUpdate(msgid, receiver, sender); + if (KGameMessage::isPlayer(receiver)) + { + //kdDebug(11001) << "message id " << msgid << " seems to be for a player ("<<active=p->isActive()<<" recv="<< receiver << endl; + KPlayer *p=findPlayer(receiver); + if (p && p->isActive()) + { + p->networkTransmission(stream,msgid,sender); + return; + } + if (p) + { + kdDebug(11001) << "player is here but not active" << endl; + } + else + { + kdDebug(11001) << "no player found" << endl; + } + } + // If it is not for a player it is meant for us!!!! Otherwise the + // gamenetwork would not have passed the message to us! + + // GameProperties processed + if (d->mProperties->processMessage(stream, msgid, sender == gameId())) + { +// kdDebug(11001 ) << "KGame: message taken by property - returning" << endl; + return ; + } + + switch(msgid) + { + case KGameMessage::IdSetupGame: // Client: First step in setup game + { + Q_INT16 v; + Q_INT32 c; + stream >> v >> c; + kdDebug(11001) << " ===================> (Client) " << k_funcinfo << ": Got IdSetupGame ================== " << endl; + kdDebug(11001) << "our game id is " << gameId() << " Lib version=" << v << " App Cookie=" << c << endl; + // Verify identity of the network partners + if (c!=cookie()) + { + kdError(11001) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<<cookie()<<" master="<<c<<endl; + sendError(KGameError::Cookie, KGameError::errCookie(cookie(), c)); + disconnect(); // disconnect from master + } + else if (v!=KGameMessage::version()) + { + sendError(KGameError::Version, KGameError::errVersion(v)); + disconnect(); // disconnect from master + } + else + { + setupGame(sender); + } + kdDebug(11001) << "========== (Client) Setup game done\n"; + } + break; + case KGameMessage::IdSetupGameContinue: // Master: second step in game setup + { + kdDebug(11001) << "=====>(Master) " << k_funcinfo << " - IdSetupGameContinue" << endl; + setupGameContinue(stream, sender); + } + break; + case KGameMessage::IdActivatePlayer: // Activate Player + { + int id; + stream >> id; + kdDebug(11001) << "Got IdActivatePlayer id=" << id << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemActivatePlayer(findPlayer(id)); + } + } + break; + case KGameMessage::IdInactivatePlayer: // Inactivate Player + { + int id; + stream >> id; + kdDebug(11001) << "Got IdInactivatePlayer id=" << id << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemInactivatePlayer(findPlayer(id)); + } + } + break; + case KGameMessage::IdAddPlayer: + { + kdDebug(11001) << k_funcinfo << ": Got IdAddPlayer" << endl; + if (sender!=gameId() || policy()!=PolicyDirty) + { + KPlayer *newplayer=0; + // We sent the message so the player is already available + if (sender==gameId()) + { + kdDebug(11001) << "dequeue previously added player" << endl; + newplayer = d->mAddPlayerList.dequeue(); + } + else + { + newplayer=loadPlayer(stream,true); + } + systemAddPlayer(newplayer);// the final, local, adding + //systemAddPlayer(stream); + } + } + break; + case KGameMessage::IdRemovePlayer: // Client should delete player id + { + int id; + stream >> id; + kdDebug(11001) << k_funcinfo << ": Got IdRemovePlayer " << id << endl; + KPlayer *p=findPlayer(id); + if (p) + { + // Otherwise the player is already removed + if (sender!=gameId() || policy()!=PolicyDirty) + { + systemRemovePlayer(p,true); + } + } + else + { + kdWarning(11001) << k_funcinfo << "Cannot find player " << id << endl; + } + } + break; + case KGameMessage::IdGameLoad: + { + kdDebug(11001) << "====> (Client) " << k_funcinfo << ": Got IdGameLoad" << endl; + loadgame(stream,true,false); + } + break; + case KGameMessage::IdGameSetupDone: + { + int cid; + stream >> cid; + kdDebug(11001) << "====> (CLIENT) " << k_funcinfo << ": Got IdGameSetupDone for client " + << cid << " we are =" << gameId() << endl; + sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0); + } + break; + case KGameMessage::IdGameConnected: + { + int cid; + stream >> cid; + kdDebug(11001) << "====> (ALL) " << k_funcinfo << ": Got IdGameConnected for client "<< cid << " we are =" << gameId() << endl; + emit signalClientJoinedGame(cid,this); + } + break; + + case KGameMessage::IdSyncRandom: // Master forces a new random seed on us + { + int newseed; + stream >> newseed; + kdDebug(11001) << "CLIENT: setting random seed to " << newseed << endl; + d->mRandom->setSeed(newseed); + } + break; + case KGameMessage::IdDisconnect: + { + // if we disconnect we *always* start a local game. + // this could lead into problems if we just change the message server + if (sender != gameId()) + { + kdDebug(11001) << "client " << sender << " leaves game" << endl; + return; + } + kdDebug(11001) << "leaving the game" << endl; + // start a new local game + // no other client is by default connected to this so this call should be + // enough + setMaster(); + } + break; + default: + { + if (msgid < KGameMessage::IdUser) + { + kdError(11001) << "incorrect message id " << msgid << " - emit anyway" + << endl; + } + kdDebug(11001) << k_funcinfo << ": User data msgid " << msgid << endl; + emit signalNetworkData(msgid - KGameMessage::IdUser,((QBuffer*)stream.device())->readAll(),receiver,sender); + } + break; + } + +} + +// called by the IdSetupGameContinue Message - MASTER SIDE +// Here the master needs to decide which players can take part at the game +// and which will be deactivated +void KGame::setupGameContinue(QDataStream& stream, Q_UINT32 sender) +{ + KPlayer *player; + Q_INT32 cnt; + int i; + stream >> cnt; + + QValueList<int> inactivateIds; + + KGamePlayerList newPlayerList; + newPlayerList.setAutoDelete(true); + for (i=0;i<cnt;i++) + { + player=loadPlayer(stream,true); + kdDebug(11001) << " Master got player " << player->id() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << " from sender " << sender << endl; + if (KGameMessage::rawGameId(player->id()) != sender) + { + kdError(11001) << "Client tries to add player with wrong game id - cheat possible" << endl; + } + else + { + newPlayerList.append(player); + kdDebug(11001) << " newplayerlist appended " << player->id() << endl; + } + } + + newPlayersJoin(playerList(),&newPlayerList,inactivateIds); + + + kdDebug(11001) << " Master calculates how many players to activate client has cnt=" << cnt << endl; + kdDebug(11001) << " The game has " << playerCount() << " active players" << endl; + kdDebug(11001) << " The user deactivated "<< inactivateIds.count() << " player already " << endl; + kdDebug(11001) << " MaxPlayers for this game is " << maxPlayers() << endl; + + // Do we have too many players? (After the programmer disabled some?) + // MH: We cannot use have player here as it CHANGES in the loop + // int havePlayers = cnt+playerCount()-inactivateIds.count(); + kdDebug(11001) << " havePlayers " << cnt+playerCount()-inactivateIds.count() << endl; + while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count())) + { + kdDebug(11001) << " Still to deacticvate " + << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers() + << endl; + KPlayer *currentPlayer=0; + int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list + // find lowest network priority which is not yet in the newPlayerList + // do this for the new players + for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) + { + // Already in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + if (player->networkPriority()<currentPriority) + { + currentPriority=player->networkPriority(); + currentPlayer=player; + } + } + + // find lowest network priority which is not yet in the newPlayerList + // Do this for the network players + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + // Already in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + if (player->networkPriority()<currentPriority) + { + currentPriority=player->networkPriority(); + currentPlayer=player; + } + } + + // add it to inactivateIds + if (currentPlayer) + { + kdDebug(11001) << "Marking player " << currentPlayer->id() << " for inactivation" << endl; + inactivateIds.append(currentPlayer->id()); + } + else + { + kdError(11001) << "Couldn't find a player to dectivate..That is not so good..." << endl; + break; + } + } + + kdDebug(11001) << "Alltogether deactivated " << inactivateIds.count() << " players" << endl; + + QValueList<int>::Iterator it; + for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) + { + int pid=*it; + kdDebug(11001) << " pid=" << pid << endl; + } + + // Now deactivate the network players from the inactivateId list + //QValueList<int>::Iterator it; + for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it ) + { + int pid=*it; + if (KGameMessage::rawGameId(pid) == sender) + { + continue; // client's player + } + kdDebug(11001) << " -> the network needs to deactivate " << pid <<endl; + player=findPlayer(pid); + if (player) + { + // We have to make REALLY sure that the player is gone. With any policy + systemInactivatePlayer(player); + if (policy()!=PolicyLocal) + { + sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer); + } + } + else + { + kdError(11001) << " We should deactivate a player, but cannot find it...not good." << endl; + } + } + + // Now send out the player list which the client can activate + for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() ) + { + kdDebug(11001) << " newplayerlist contains " << player->id() << endl; + // Only activate what is not in the list + if (inactivateIds.find(player->id())!=inactivateIds.end()) + { + continue; + } + kdDebug(11001) << " -> the client can ******** reactivate ******** " << player->id() << endl; + sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender); + } + + // Save the game over the network + QByteArray bufferS; + QDataStream streamS(bufferS,IO_WriteOnly); + // Save game over netowrk and save players + savegame(streamS,true,true); + sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender); + + + // Only to the client first , as the client will add players + sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender); +} + +// called by the IdSetupGame Message - CLIENT SIDE +// Client needs to prepare for network transfer +void KGame::setupGame(Q_UINT32 sender) +{ + QByteArray bufferS; + QDataStream streamS(bufferS,IO_WriteOnly); + + // Deactivate all players + KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes + Q_INT32 cnt=mTmpList.count(); + kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl; + + streamS << cnt; + + QPtrListIterator<KPlayer> it(mTmpList); + KPlayer *player; + while (it.current()) + { + player=it.current(); + systemInactivatePlayer(player); + // Give the new game id to all players (which are inactivated now) + player->setId(KGameMessage::createPlayerId(player->id(),gameId())); + + // Save it for the master to decide what to do + savePlayer(streamS,player); + + ++it; + --cnt; + } + if (d->mPlayerList.count() > 0 || cnt!=0) + { + kdFatal(11001) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <<cnt << endl; + } + + sendSystemMessage(streamS,KGameMessage::IdSetupGameContinue,sender); +} + +// unused by KGame +void KGame::syncRandom() +{ + int newseed=(int)d->mRandom->getLong(65535); + sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast + d->mRandom->setSeed(newseed); +} + +void KGame::Debug() +{ + KGameNetwork::Debug(); + kdDebug(11001) << "------------------- KGAME -------------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "uniquePlayer " << d->mUniquePlayerNumber << endl; + kdDebug(11001) << "gameStatus " << gameStatus() << endl; + kdDebug(11001) << "MaxPlayers : " << maxPlayers() << endl; + kdDebug(11001) << "NoOfPlayers : " << playerCount() << endl; + kdDebug(11001) << "NoOfInactive: " << d->mInactivePlayerList.count() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +void KGame::slotClientConnected(Q_UINT32 clientID) +{ + if (isAdmin()) + { + negotiateNetworkGame(clientID); + } +} + +void KGame::slotServerDisconnected() // Client side +{ + kdDebug(11001) << "======= SERVER DISCONNECT ======="<<endl; + kdDebug(11001) << "+++ (CLIENT)++++++++" << k_funcinfo << ": our GameID="<<gameId() << endl; + + int oldgamestatus=gameStatus(); + + KPlayer *player; + KGamePlayerList removeList; + kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl; + kdDebug(11001) << "Inactive Playerlist of client=" << d->mInactivePlayerList.count() << " count" << endl; + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + // TODO: CHECK: id=0, could not connect to server in the first place?? + if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0) + { + kdDebug(11001) << "Player " << player->id() << " belongs to a removed game" << endl; + removeList.append(player); + } + } + + for ( player=removeList.first(); player != 0; player=removeList.next() ) + { + bool remove = true; + emit signalReplacePlayerIO(player, &remove); + if (remove) + { + kdDebug(11001) << " ---> Removing player " << player->id() << endl; + systemRemovePlayer(player,true); // no network necessary + } + } + + setMaster(); + kdDebug(11001) << " our game id is after setMaster " << gameId() << endl; + + KGamePlayerList mReList(d->mInactivePlayerList); + for ( player=mReList.first(); player != 0; player=mReList.next() ) + { + // TODO ?check for priority? Sequence should be ok + if ((int)playerCount()<maxPlayers() || maxPlayers()<0) + { + systemActivatePlayer(player); + } + } + kdDebug(11001) << " Players activated player-cnt=" << playerCount() << endl; + + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + int oldid=player->id(); + d->mUniquePlayerNumber++; + player->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber,gameId())); + kdDebug(11001) << "Player id " << oldid <<" changed to " << player->id() << " as we are now local" << endl; + } + // TODO clear inactive lists ? + Debug(); + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + player->Debug(); + } + kdDebug(11001) << "+++++++++++" << k_funcinfo << " DONE=" << endl; + emit signalClientLeftGame(0,oldgamestatus,this); +} + +void KGame::slotClientDisconnected(Q_UINT32 clientID,bool /*broken*/) // server side +{ + kdDebug(11001) << "++++(SERVER)+++++++" << k_funcinfo << " clientId=" << clientID << endl; + + int oldgamestatus=gameStatus(); + + KPlayer *player; + KGamePlayerList removeList; + kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl; + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + if (KGameMessage::rawGameId(player->id())==clientID) + { + kdDebug(11001) << "Player " << player->id() << " belongs to the removed game" << endl; + removeList.append(player); + } + } + + for ( player=removeList.first(); player != 0; player=removeList.next() ) + { + // try to replace the KGameIO first + bool remove = true; + emit signalReplacePlayerIO(player, &remove); + if (remove) { + // otherwise (no new KGameIO) remove the player + kdDebug(11001) << " ---> Removing player " << player->id() << endl; + removePlayer(player,0); + } + } + + // Now add inactive players - sequence should be ok + // TODO remove players from removed game + for (unsigned int idx=0;idx<d->mInactiveIdList.count();idx++) + { + QValueList<int>::Iterator it1 = d->mInactiveIdList.at(idx); + player = findPlayer(*it1); + if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(*it1) != clientID) + { + activatePlayer(player); + } + } + emit signalClientLeftGame(clientID,oldgamestatus,this); +} + + +// -------------------- Synchronisation ----------------------- + +// this initializes a newly connected client. +// we send the number of players (including type) as well as game status and +// properties to the client. After the initialization has been completed both +// clients should have the same status (ie players, properties, etc) +void KGame::negotiateNetworkGame(Q_UINT32 clientID) +{ + kdDebug(11001) << "===========================" << k_funcinfo << ": clientID=" << clientID << " =========================== "<< endl; + if (!isAdmin()) + { + kdError(11001) << k_funcinfo << ": Serious WARNING..only gameAdmin should call this" << endl; + return ; + } + + QByteArray buffer; + QDataStream streamGS(buffer,IO_WriteOnly); + + // write Game setup specific data + //streamGS << (Q_INT32)maxPlayers(); + //streamGS << (Q_INT32)minPlayers(); + + // send to the newly connected client *only* + Q_INT16 v=KGameMessage::version(); + Q_INT32 c=cookie(); + streamGS << v << c; + sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID); +} + +bool KGame::sendGroupMessage(const QByteArray &msg, int msgid, Q_UINT32 sender, const QString& group) +{ +// AB: group must not be i18n'ed!! we should better use an id for group and use +// a groupName() for the name // FIXME + KPlayer *player; + for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() ) + { + if (player && player->group()==group) + { + sendMessage(msg,msgid,player->id(), sender); + } + } + return true; +} + +bool KGame::sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group) +{ return sendGroupMessage(((QBuffer*)msg.device())->buffer(), msgid, sender, group); } + +bool KGame::sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group) +{ + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + stream << msg; + return sendGroupMessage(stream, msgid, sender, group); +} + +bool KGame::addProperty(KGamePropertyBase* data) +{ return dataHandler()->addProperty(data); } + +bool KGame::sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId) +{ return sendSystemMessage(s, msgid, playerId); } + +void KGame::sendProperty(int msgid, QDataStream& stream, bool* sent) +{ + bool s = sendSystemMessage(stream, msgid); + if (s) + { + *sent = true; + } +} + +void KGame::emitSignal(KGamePropertyBase *me) +{ + emit signalPropertyChanged(me,this); +} + +KGamePropertyBase* KGame::findProperty(int id) const +{ return d->mProperties->find(id); } + +KGame::GamePolicy KGame::policy() const +{ + return d->mPolicy; +} +void KGame::setPolicy(GamePolicy p,bool recursive) +{ + // Set KGame policy + d->mPolicy=p; + if (recursive) + { + // Set all KGame property policy + dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + + // Set all KPLayer (active or inactive) property policy + for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it) + { + it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + } + for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it) + { + it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false); + } + } +} + +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgame.h b/libkdegames/kgame/kgame.h new file mode 100644 index 00000000..37d8d974 --- /dev/null +++ b/libkdegames/kgame/kgame.h @@ -0,0 +1,932 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAME_H_ +#define __KGAME_H_ + +#include <qstring.h> +#include <qptrlist.h> +#include <qvaluelist.h> + +#include "kgamenetwork.h" +#include <kdemacros.h> +class KRandomSequence; + +class KPlayer; +class KGamePropertyBase; +class KGamePropertyHandler; +class KGameSequence; + +class KGamePrivate; + +/** + * @short The main KDE game object + * + * The KGame class is the central game object. A game basically + * consists of following features: + * - Player handling (add, remove,...) + * - Game status (end,start,pause,...) + * - load/save + * - Move (and message) handling + * - nextPlayer and gameOver() + * - Network connection (for KGameNetwork) + * + * Example: + * \code + * KGame *game=new KGame; + * \endcode + * + * + * @author Martin Heni <martin@heni-online.de> + * + */ +class KDE_EXPORT KGame : public KGameNetwork +{ + Q_OBJECT + +public: + typedef QPtrList<KPlayer> KGamePlayerList; + + /** + * The policy of the property. This can be PolicyClean (setVale uses + * send), PolicyDirty (setValue uses changeValue) or + * PolicyLocal (setValue uses setLocal). + * + * A "clean" policy means that the property is always the same on every + * client. This is achieved by calling send which actually changes + * the value only when the message from the MessageServer is received. + * + * A "dirty" policy means that as soon as setValue is called the + * property is changed immediately. And additionally sent over network. + * This can sometimes lead to bugs as the other clients do not + * immediately have the same value. For more information see + * changeValue. + * + * PolicyLocal means that a KGameProperty behaves like ever + * "normal" variable. Whenever setValue is called (e.g. using "=") + * the value of the property is changes immediately without sending it + * over network. You might want to use this if you are sure that all + * clients set the property at the same time. + **/ + enum GamePolicy + { + PolicyUndefined = 0, + PolicyClean = 1, + PolicyDirty = 2, + PolicyLocal = 3 + }; + + /** + * Create a KGame object. The cookie is used to identify your + * game in load/save and network operations. Change this between + * games. + */ + KGame(int cookie=42,QObject* parent=0); + + /** + * Destructs the game + */ + virtual ~KGame(); + + /** + * Gives debug output of the game status + */ + virtual void Debug(); + + /** + * Game status - Use this to Control the game flow. + * The KGame e.g. sets the status to Pause when you have + * less player than the minimum amount + */ + enum GameStatus + { + Init = 0, + Run = 1, + Pause = 2, + End = 3, + Abort = 4, + SystemPause = 5, + Intro = 6, + UserStatus = 7 + }; + + // Properties + /** + * Returns a list of all active players + * + * @return the list of players + */ + KGamePlayerList *playerList(); + + /** + * The same as @ref playerList but returns a const pointer. + **/ + const KGamePlayerList *playerList() const; + + /** + * Returns a list of all inactive players + * @return the list of players + */ + KGamePlayerList *inactivePlayerList(); + + /** + * The same as @ref inactivePlayerList but returns a const pointer. + **/ + const KGamePlayerList *inactivePlayerList() const; + + /** + * Returns a pointer to the game's KRandomSequence. This sequence is + * identical for all network players! + * @return KRandomSequence pointer + */ + KRandomSequence *random() const; + + /** + * @return The KGameSequence object that is currently in use. + * @see setGameSequence + **/ + KGameSequence *gameSequence() const; + + /** + * Is the game running + * @return true/false + */ + bool isRunning() const; + + // Player handling + /** + * Returns the player object for a given player id + * @param id Player id + * @return player object + */ + KPlayer *findPlayer(Q_UINT32 id) const; + + /** + * Set a new @ref KGameSequence to control player management. By default + * KGame uses a normal @ref KGameSequence object. You might want to subclass + * that and provide your own object. + * + * The previous sequence will get deleted. + * @param sequence The new game sequence object. KGame takes ownership and + * will delete it on destruction! + **/ + void setGameSequence(KGameSequence* sequence); + + /** + * Note that KPlayer::save must be implemented properly, as well as + * KPlayer::rtti + * This will only send a message to all clients. The player is _not_ added + * directly! + * See also playerInput which will be called as soon as the + * player really has been added. + * + * Note that an added player will first get into a "queue" and won't be in + * the game. It will be added to the game as soon as systemAddPlayer is + * called what will happen as soon as IdAddPlayer is received. + * + * Note: you probably want to connect to signalPlayerJoinedGame for + * further initialization! + * @param newplayer The player you want to add. KGame will send a message to + * all clients and add the player using systemAddPlayer + **/ + void addPlayer(KPlayer* newplayer); + + /** + * Sends a message over the network, msgid=IdRemovePlayer. + * + * As soon as this message is received by networkTransmission + * systemRemovePlayer is called and the player is removed. + **/ + //AB: TODO: make sendMessage to return if the message will be able to be + //sent, eg if a socket is connected, etc. If sendMessage returns false + //remove the player directly using systemRemovePlayer + bool removePlayer(KPlayer * player) { return removePlayer(player, 0); } + + /** + * Called by the destructor of KPlayer to remove itself from the game + * + **/ + void playerDeleted(KPlayer * player); + + /** + * sends activate player: internal use only? + */ + bool activatePlayer(KPlayer *player); + + /** + * sends inactivate player: internal use only? + */ + bool inactivatePlayer(KPlayer *player); + + /** + * Set the maximal number of players. After this is + * reached no more players can be added. You must be ADMIN to call this (@see + * isAdmin). + * @param maxnumber maximal number of players + */ + void setMaxPlayers(uint maxnumber); + + /** + * What is the maximal number of players? + * @return maximal number of players + */ + int maxPlayers() const; + + /** + * Set the minimal number of players. A game can not be started + * with less player resp. is paused when already running. You must be ADMIN + * to call this (see @ref isAdmin)! + * @param minnumber minimal number of players + */ + void setMinPlayers(uint minnumber); + + /** + * What is the minimal number of players? + * @return minimal number of players + */ + uint minPlayers() const; + + /** + * Returns how many players are plugged into the game + * @return number of players + */ + uint playerCount() const; + + /** + * @deprecated + * Use @ref KGameSequence::nextPlayer instead + **/ + virtual KPlayer * nextPlayer(KPlayer *last,bool exclusive=true); + + // Input events + /** + * Called by KPlayer to send a player input to the + * KMessageServer. + **/ + virtual bool sendPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0); + + /** + * Called when a player input arrives from KMessageServer. + * + * Calls prepareNext (using QTimer::singleShot) if gameOver() + * returns 0. This function should normally not be used outside KGame. + * It could be made non-virtual,protected in a later version. At the + * moment it is a virtual function to give you more control over KGame. + * + * For documentation see playerInput. + **/ + virtual bool systemPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0); + + /** + * This virtual function is called if the KGame needs to create a new player. + * This happens only over a network and with load/save. Doing nothing + * will create a default KPlayer. If you want to have your own player + * you have to create one with the given rtti here. + * Note: If your game uses a player class derived from KPlayer you MUST + * override this function and create your player here. Otherwise the + * game will crash. + * Example: + * \code + * KPlayer *MyGame::createPlayer(int rtti,int io,bool isvirtual) + * { + * KPlayer *player=new MyPlayer; + * if (!isvirtual) // network player ? + * { + * // Define something like this to add the IO modules + * createIO(player,(KGameIO::IOMode)io); + * } + * return player; + * } + * \endcode + * + * @param rtti is the type of the player (0 means default KPlayer) + * @param io is the 'or'ed rtti of the KGameIO's + * @param isvirtual true if player is virtual + */ + virtual KPlayer *createPlayer(int rtti,int io,bool isvirtual); + + // load/save + /** + * Load a saved game, from file OR network. This function has + * to be overwritten or you need to connect to the load signal + * if you have game data other than KGameProperty. + * For file load you should reset() the game before any load attempt + * to make sure you load into an clear state. + * + * @param stream a data stream where you can stream the game from + * @param reset - shall the game be reset before loading + * + * @return true? + */ + virtual bool load(QDataStream &stream,bool reset=true); + + /** + * Same as above function but with different parameters + * + * @param filename - the filename of the file to be opened + * @param reset - shall the game be reset before loading + * + * @return true? + **/ + virtual bool load(QString filename,bool reset=true); + + /** + * Save a game to a file OR to network. Otherwise the same as + * the load function + * + * @param stream a data stream to load the game from + * @param saveplayers If true then all players wil be saved too + * + * @return true? + */ + virtual bool save(QDataStream &stream,bool saveplayers=true); + + /** + * Same as above function but with different parameters + * + * @param filename the filename of the file to be saved + * @param saveplayers If true then all players wil be saved too + * + * @return true? + **/ + virtual bool save(QString filename,bool saveplayers=true); + + /** + * Resets the game, i.e. puts it into a state where everything + * can be started from, e.g. a load game + * Right now it does only need to delete all players + * + * @return true on success + */ + virtual bool reset(); + + + // Game sequence + /** + * returns the game status, ie running,pause,ended,... + * + * @return game status + */ + int gameStatus() const; + + /** + * sets the game status + * + * @param status the new status + */ + void setGameStatus(int status); + + /** + * docu: see KPlayer + **/ + bool addProperty(KGamePropertyBase* data); + + /** + * This is called by KPlayer::sendProperty only! Internal function! + **/ + bool sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId); + + /** + * This function allows to find the pointer to a player + * property when you know it's id + */ + KGamePropertyBase* findProperty(int id) const; + + /** + * Changes the consistency policy of a property. The + * GamePolicy is one of PolicyClean (default), PolicyDirty or PolicyLocal. + * + * It is up to you to decide how you want to work. + **/ + void setPolicy(GamePolicy p,bool recursive=true); + + /** + * @return The default policy of the property + **/ + GamePolicy policy() const; + + /** + * See KGameNetwork::sendMessage + * + * Send a network message msg with a given message ID msgid to all players of + * a given group (see KPlayer::group) + * @param msg the message which will be send. See messages.txt for contents + * @param msgid an id for this message + * @param sender the id of the sender + * @param group the group of the receivers + * @return true if worked + */ + bool sendGroupMessage(const QByteArray& msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(int msg, int msgid, Q_UINT32 sender, const QString& group); + bool sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group); + + /** + * This will either forward an incoming message to a specified player + * (see KPlayer::networkTransmission) or + * handle the message directly (e.g. if msgif==IdRemovePlayer it will remove + * the (in the stream) specified player). If both is not possible (i.e. the + * message is user specified data) the signal signalNetworkData is + * emitted. + * + * This emits signalMessageUpdate <em>before</em> doing anything with + * the message. You can use this signal when you want to be notified about + * an update/change. + * @param msgid Specifies the kind of the message. See messages.txt for + * further information + * @param stream The message that is being sent + * @param receiver The is of the player this message is for. 0 For broadcast. + * @param sender + * @param clientID the client from which we received the transmission - hardly used + **/ + virtual void networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 clientID); + + /** + * Returns a pointer to the KGame property handler + **/ + KGamePropertyHandler* dataHandler() const; + +protected slots: + /** + * Called by KGamePropertyHandler only! Internal function! + **/ + void sendProperty(int msgid, QDataStream& stream, bool* sent); + + /** + * Called by KGamePropertyHandler only! Internal function! + **/ + void emitSignal(KGamePropertyBase *me); + + /** + * @deprecated + * Use KGameSequence::prepareNext() instead + **/ + virtual void prepareNext(); + + + /** + * Calls negotiateNetworkGame() + * See KGameNetwork::signalClientConnected + **/ + void slotClientConnected(Q_UINT32 clientId); + + /** + * This slot is called whenever the connection to a client is lost (ie the + * signal KGameNetwork::signalClientDisconnected is emitted) and will remove + * the players from that client. + * @param clientId The client the connection has been lost to + * @param broken (ignore this - not used) + **/ + void slotClientDisconnected(Q_UINT32 clientId,bool broken); + + /** + * This slot is called whenever the connection to the server is lost (ie the + * signal KGameNetwork::signalConnectionBroken is emitted) and will + * switch to local game mode + **/ + void slotServerDisconnected(); + +signals: + /** + * When a client disconnects from the game usually all players from that + * client are removed. But if you use completely the KGame structure you + * probably don't want this. You just want to replace the KGameIO of the + * (human) player by a computer KGameIO. So this player continues game but + * is from this point on controlled by the computer. + * + * You achieve this by connecting to this signal. It is emitted as soon as a + * client disconnects on <em>all</em> other clients. Make sure to add a new + * KGameIO only once! you might want to use @ref isAdmin for this. If you + * added a new KGameIO set *remove=false otherwise the player is completely + * removed. + * @param player The player that is about to be removed. Add your new + * KGameIO here - but only on <em>one</em> client! + * @param remove Set this to FALSE if you don't want this player to be + * removed completely. + **/ + void signalReplacePlayerIO(KPlayer* player, bool* remove); + + /** + * The game will be loaded from the given stream. Load from here + * the data which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * This signal is emitted <em>before</em> the players are loaded by + * KGame. See also signalLoad + * + * You must load <em>exactly</em> the same data from the stream that you have saved + * in signalSavePrePlayers. Otherwise player loading will not work + * anymore. + * + * @param stream the load stream + */ + void signalLoadPrePlayers(QDataStream &stream); + + /** + * The game will be loaded from the given stream. Load from here + * the data which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * @param stream the load stream + */ + void signalLoad(QDataStream &stream); + + /** + * The game will be saved to the given stream. Fill this with data + * which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * This signal is emitted <em>before</em> the players are saved by + * KGame. See also signalSave + * + * If you can choose between signalSavePrePlayers and signalSave then + * better use signalSave + * + * @param stream the save stream + **/ + void signalSavePrePlayers(QDataStream &stream); + + /** + * The game will be saved to the given stream. Fill this with data + * which is NOT a game or player property. + * It is not necessary to use this signal for a full property game. + * + * @param stream the save stream + */ + void signalSave(QDataStream &stream); + + /** + * Is emmited if a game with a different version cookie is loaded. + * Normally this should result in an error. But maybe you do support + * loading of older game versions. Here would be a good place to do a + * conversion. + * + * @param stream - the load stream + * @param network - true if this is a network connect. False for load game + * @param cookie - the saved cookie. It differs from KGame::cookie() + * @param result - set this to true if you managed to load the game + */ + void signalLoadError(QDataStream &stream,bool network,int cookie, bool &result); + + /** + * We got an user defined update message. This is usually done + * by a sendData in a inherited KGame Object which defines its + * own methods and has to syncronise them over the network. + * Reaction to this is usually a call to a KGame function. + */ + void signalNetworkData(int msgid,const QByteArray& buffer, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * We got an network message. this can be used to notify us that something + * changed. What changed can be seen in the message id. Whether this is + * the best possible method to do this is unclear... + */ + void signalMessageUpdate(int msgid,Q_UINT32 receiver,Q_UINT32 sender); + + /** + * a player left the game because of a broken connection or so! + * + * Note that when this signal is emitted the player is not part of @ref + * playerList anymore but the pointer is still valid. You should do some + * final cleanups here since the player is usually deleted after the signal + * is emitted. + * + * @param player the player who left the game + */ + void signalPlayerLeftGame(KPlayer *player); + + /** + * a player joined the game + * + * @param player the player who joined the game + */ + void signalPlayerJoinedGame(KPlayer *player); + + + /** + * This signal is emmited if a player property changes its value and + * the property is set to notify this change + */ + void signalPropertyChanged(KGamePropertyBase *property, KGame *me); + + /** + * Is emitted after a call to gameOver() returns a non zero + * return code. This code is forwarded to this signal as 'status'. + * + * @param status the return code of gameOver() + * @param current the player who did the last move + * @param me a pointer to the KGame object + */ + void signalGameOver(int status, KPlayer *current, KGame *me); + + /** + * Is emmited after a client is successfully connected to the game. + * The client id is the id of the new game client. An easy way to + * check whether that's us is + * \code + * if (clientid==gameid()) .. // we joined + * else ... // someone joined the game + * \endcode + * @param clientid - The id of the new client + * @param me - our game pointer + */ + void signalClientJoinedGame(Q_UINT32 clientid,KGame *me); + + /** + * This signal is emitted after a network partner left the + * game (either by a broken connection or voluntarily). + * All changes to the network players have already be done. + * If there are not enough players left, the game might have + * been paused. To check this you get the old gamestatus + * before the disconnection as argument here. The id of the + * client who left the game allows to distinguish who left the + * game. If it is 0, the server disconnected and you were a client + * which has been switched back to local play. + * You can use this signal to, e.g. set some menues back to local + * player when they were network before. + * + * @param clientID - 0:server left, otherwise the client who left + * @param oldgamestatus - the gamestatus before the loss + * @param me - our game pointer + **/ + void signalClientLeftGame(int clientID,int oldgamestatus,KGame *me); + + +protected: + /** + * A player input occurred. This is the most important function + * as the given message will contain the current move made by + * the given player. + * Note that you HAVE to overwrite this function. Otherwise your + * game makes no sense at all. + * Generally you have to return TRUE in this function. Only then + * the game sequence is proceeded by calling @ref playerInputFinished + * which in turn will check for game over or the next player + * However, if you have a delayed move, because you e.g. move a + * card or a piece you want to return FALSE to pause the game sequence + * and then manually call @ref playerInputFinished to resume it. + * Example: + * \code + * bool MyClass::playerInput(QDataStream &msg,KPlayer *player) + * { + * Q_INT32 move; + * msg >> move; + * kdDebug() << " Player " << player->id() << " moved to " << move << + * endl; + * return true; + * } + * \endcode + * + * @param msg the move message + * @param player the player who did the move + * @return true - input ready, false: input manual + */ + virtual bool playerInput(QDataStream &msg,KPlayer *player)=0; + + + /** + * Called after the player input is processed by the game. Here the + * checks for game over and nextPlayer (in the case of turn base games) + * are processed. + * Call this manually if you have a delayed move, i.e. your playerInput + * function returns FALSE. If it returns true you need not do anything + * here. + * + * @return the current player + * + **/ + KPlayer *playerInputFinished(KPlayer *player); + + + /** + * This virtual function can be overwritten for your own player management. + * It is called when a new game connects to an existing network game or + * to the network master. In case you do not want all players of both games + * to be present in the new network game, you can deactivate players here. + * This is of particular importance if you have a game with fixed number of + * player like e.g. chess. A network connect needs to disable one player of + * each game to make sense. + * + * Not overwriting this function will activate a default behaviour which + * will deactivate players until the @ref maxPlayers() numebr is reached + * according to the KPlayer::networkPriority() value. Players with a low + * value will be kicked out first. With equal priority players of the new + * client will leave first. This means, not setting this value and not + * overwriting this function will never allow a chess game to add client + * players!!! + * On the other hand setting one player of each game to a networkPriorty of + * say 10, already does most of the work for you. + * + * The parameters of this function are the playerlist of the network game, + * which is @ref playerList(). The second argument is the player list of + * the new client who wants to join and the third argument serves as return + * parameter. All <em>player ID's</em> which are written into this list + * will be <em>removed</em> from the created game. You do this by an + * \code + * inactivate.append(player->id()); + * \endcode + * + * @param oldplayer - the list of the network players + * @param newplayer - the list of the client players + * @param inactivate - the value list of ids to be deactivated + * + **/ + virtual void newPlayersJoin(KGamePlayerList *oldplayer, + KGamePlayerList *newplayer, + QValueList<int> &inactivate) { + Q_UNUSED( oldplayer ); + Q_UNUSED( newplayer ); + Q_UNUSED( inactivate ); + } + + /** + * Save the player list to a stream. Used for network game and load/save. + * Can be overwritten if you know what you are doing + * + * @param stream is the stream to save the player ot + * @param list the optional list is the player list to be saved, default is playerList() + * + **/ + void savePlayers(QDataStream &stream,KGamePlayerList *list=0); + + /** + * Prepare a player for being added. Put all data about a player into the + * stream so that it can be sent to the KGameCommunicationServer using + * addPlayer (e.g.) + * + * This function ensures that the code for adding a player is the same in + * addPlayer as well as in negotiateNetworkGame + * @param stream is the stream to add the player + * @param player The player to add + **/ + void savePlayer(QDataStream& stream,KPlayer* player); + + /** + * Load the player list from a stream. Used for network game and load/save. + * Can be overwritten if you know what you are doing + * + * @param stream is the stream to save the player to + * @param isvirtual will set the virtual flag true/false + * + **/ + KPlayer *loadPlayer(QDataStream& stream,bool isvirtual=false); + + + /** + * inactivates player. Use @ref inactivatePlayer instead! + */ + bool systemInactivatePlayer(KPlayer *player); + + /** + * activates player. Use @ref activatePlayer instead! + */ + bool systemActivatePlayer(KPlayer *player); + + /** + * Adds a player to the game + * + * Use @ref addPlayer to send @ref KGameMessage::IdAddPlayer. As soon as + * this Id is received this function is called, where the player (see @ref + * KPlayer::rtti) is added as well as its properties (see @ref KPlayer::save + * and @ref KPlayer::load) + * + * This method calls the overloaded @ref systemAddPlayer with the created + * player as argument. That method will really add the player. + * If you need to do some changes to your newly added player just connect to + * @ref signalPlayerJoinedGame + */ + + /** + * Finally adds a player to the game and therefore to the list. + **/ + void systemAddPlayer(KPlayer* newplayer); + + /** + * Removes a player from the game + * + * Use removePlayer to send KGameMessage::IdRemovePlayer. As soon + * as this Id is received systemRemovePlayer is called and the player is + * removed directly. + **/ + void systemRemovePlayer(KPlayer* player,bool deleteit); + + /** + * This member function will transmit e.g. all players to that client, as well as + * all properties of these players (at least if they have been added by + * @ref KPlayer::addProperty) so that the client will finally have the same + * status as the master. You want to overwrite this function if you expand + * KGame by any properties which have to be known by all clients. + * + * Only the ADMIN is allowed to call this. + * @param clientID The ID of the message client which has connected + **/ + virtual void negotiateNetworkGame(Q_UINT32 clientID); + + /** + * syncronise the random numbers with all network clients + * not used by KGame - if it should be kept then as public method + */ + void syncRandom(); + + void deletePlayers(); + void deleteInactivePlayers(); + + /** + * @deprecated + * Use @ref KGameSequence instead. + * + * @param player the player who made the last move + * @return anything else but 0 is considered as game over + */ + virtual int checkGameOver(KPlayer *player); + + /** + * Load a saved game, from file OR network. Internal. + * Warning: loadgame must not rely that all players all already + * activated. Actually the network will activate a player AFTER + * the loadgame only. This is not true anymore. But be careful + * anyway. + * + * @param stream a data stream where you can stream the game from + * @param network is it a network call -> make players virtual + * @param reset shall the game be reset before loading + * + * @return true? + */ + virtual bool loadgame(QDataStream &stream, bool network, bool reset); + + /** + * Save a game, to file OR network. Internal. + * + * @param stream a data stream where you can stream the game from + * @param network is it a call from the network or from a file (unused but informative) + * @param saveplayers shall the players be saved too (should be TRUE) + * + * @return true? + */ + virtual bool savegame(QDataStream &stream, bool network,bool saveplayers); + +private: + //AB: this is to hide the "receiver" parameter from the user. It shouldn't be + //used if possible (except for init). + /** + * This is an overloaded function. Id differs from the public one only in + * its parameters: + * + * @param receiver The Client that will receive the message. You will hardly + * ever need this. It it internally used to initialize a newly connected + * client. + **/ + //void addPlayer(KPlayer* newplayer, Q_UINT32 receiver); + + /** + * Just the same as the public one except receiver: + * @param receiver 0 for broadcast, otherwise the receiver. Should only be + * used in special circumstances and not outside KGame. + **/ + bool removePlayer(KPlayer * player, Q_UINT32 receiver); + + /** + * Helping function - game negotiation + **/ + void setupGame(Q_UINT32 sender); + + /** + * Helping function - game negotiation + **/ + void setupGameContinue(QDataStream& msg, Q_UINT32 sender); + + /** + * Removes a player from all lists, removes the @ref KGame pointer from the + * @ref KPlayer and deletes the player. Used by (e.g.) @ref + * systemRemovePlayer + * @return True if the player has been removed, false if the current is not + * found + **/ + bool systemRemove(KPlayer* player,bool deleteit); + + +private: + KGamePrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgamechat.cpp b/libkdegames/kgame/kgamechat.cpp new file mode 100644 index 00000000..16ec7c18 --- /dev/null +++ b/libkdegames/kgame/kgamechat.cpp @@ -0,0 +1,341 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 "kgamechat.h" +#include "kgamechat.moc" + +#include "kgame.h" +#include "kplayer.h" +#include "kgameproperty.h" +#include "kgamemessage.h" + +#include <klocale.h> +#include <kdebug.h> + +#include <qmap.h> +#include <qintdict.h> + +//FIXME: +#define FIRST_ID 2 // first id, that is free of use, aka not defined above + +class KGameChatPrivate +{ +public: + KGameChatPrivate() + { + mFromPlayer = 0; + mGame = 0; + + mToMyGroup = -1; + } + + KGame* mGame; + KPlayer* mFromPlayer; + int mMessageId; + + + QIntDict<KPlayer> mIndex2Player; + + QMap<int, int> mSendId2PlayerId; + int mToMyGroup; // just as the above - but for the group, not for players +}; + +KGameChat::KGameChat(KGame* g, int msgid, QWidget* parent) : KChatBase(parent) +{ + init(g, msgid); +} + +KGameChat::KGameChat(KGame* g, int msgid, KPlayer* fromPlayer, QWidget* parent) : KChatBase(parent) +{ + init(g, msgid); + setFromPlayer(fromPlayer); +} + +KGameChat::KGameChat(QWidget* parent) : KChatBase(parent) +{ + init(0, -1); +} + +KGameChat::~KGameChat() +{ + kdDebug(11001) << k_funcinfo << endl; + delete d; +} + +void KGameChat::init(KGame* g, int msgId) +{ + kdDebug(11001) << k_funcinfo << endl; + d = new KGameChatPrivate; + setMessageId(msgId); + + setKGame(g); +} + +void KGameChat::addMessage(int fromId, const QString& text) +{ + if (!d->mGame) { + kdWarning(11001) << "no KGame object has been set" << endl; + addMessage(i18n("Player %1").arg(fromId), text); + } else { + KPlayer* p = d->mGame->findPlayer(fromId); + if (p) { + kdDebug(11001) << "adding message of player " << p->name() << "id=" << fromId << endl; + addMessage(p->name(), text); + } else { + kdWarning(11001) << "Could not find player id " << fromId << endl; + addMessage(i18n("Unknown"), text); + } + } +} + +void KGameChat::returnPressed(const QString& text) +{ + if (!d->mFromPlayer) { + kdWarning(11001) << k_funcinfo << ": You must set a player first!" << endl; + return; + } + if (!d->mGame) { + kdWarning(11001) << k_funcinfo << ": You must set a game first!" << endl; + return; + } + + kdDebug(11001) << "from: " << d->mFromPlayer->id() << "==" << d->mFromPlayer->name() << endl; + + int id = sendingEntry(); + + if (isToGroupMessage(id)) { + // note: there is currently no support for other groups than the players + // group! It might be useful to send to other groups, too + QString group = d->mFromPlayer->group(); + kdDebug(11001) << "send to group " << group << endl; + int sender = d->mFromPlayer->id(); + d->mGame->sendGroupMessage(text, messageId(), sender, group); + + //TODO + //AB: this message is never received!! we need to connect to + //KPlayer::networkData!!! + //TODO + + } else { + int toPlayer = 0; + if (!isSendToAllMessage(id) && isToPlayerMessage(id)) { + toPlayer = playerId(id); + if (toPlayer == -1) { + kdError(11001) << k_funcinfo << ": don't know that player " + << "- internal ERROR" << endl; + } + } + int receiver = toPlayer; + int sender = d->mFromPlayer->id(); + d->mGame->sendMessage(text, messageId(), receiver, sender); + } +} + +void KGameChat::setMessageId(int msgid) +{ d->mMessageId = msgid; } + +int KGameChat::messageId() const +{ return d->mMessageId; } + +bool KGameChat::isSendToAllMessage(int id) const +{ return (id == KChatBase::SendToAll); } + +bool KGameChat::isToGroupMessage(int id) const +{ return (id == d->mToMyGroup); } + +bool KGameChat::isToPlayerMessage(int id) const +{ +return d->mSendId2PlayerId.contains(id); } + +QString KGameChat::sendToPlayerEntry(const QString& name) const +{ return i18n("Send to %1").arg(name); } + +int KGameChat::playerId(int id) const +{ + if (!isToPlayerMessage(id)) { + return -1; + } + + return d->mSendId2PlayerId[id]; +} + +int KGameChat::sendingId(int playerId) const +{ + QMap<int, int>::Iterator it; + for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) { + if (it.data() == playerId) { + return it.key(); + } + } + return -1; +} + +const QString& KGameChat::fromName() const +{ return d->mFromPlayer ? d->mFromPlayer->name() : QString::null; } + +bool KGameChat::hasPlayer(int id) const +{ + return (sendingId(id) != -1); +} + +void KGameChat::setFromPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + removeSendingEntry(d->mToMyGroup); + d->mFromPlayer = 0; + return; + } + if (d->mFromPlayer) { + changeSendingEntry(p->group(), d->mToMyGroup); + } else { + if (d->mToMyGroup != -1) { + kdWarning(11001) << "send to my group exists already - removing" << endl; + removeSendingEntry(d->mToMyGroup); + } + d->mToMyGroup = nextId(); + addSendingEntry(i18n("Send to My Group (\"%1\")").arg(p->group()), d->mToMyGroup); + } + d->mFromPlayer = p; + kdDebug(11001) << k_funcinfo << " player=" << p << endl; +} + + +void KGameChat::setKGame(KGame* g) +{ + if (d->mGame) { + slotUnsetKGame(); + } + kdDebug(11001) << k_funcinfo << " game=" << g << endl; + d->mGame = g; + + if (d->mGame) { + connect(d->mGame, SIGNAL(signalPlayerJoinedGame(KPlayer*)), + this, SLOT(slotAddPlayer(KPlayer*))); + connect(d->mGame, SIGNAL(signalPlayerLeftGame(KPlayer*)), + this, SLOT(slotRemovePlayer(KPlayer*))); + connect(d->mGame, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, Q_UINT32)), + this, SLOT(slotReceiveMessage(int, const QByteArray&, Q_UINT32, Q_UINT32))); + connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame())); + + QPtrList<KPlayer> playerList = *d->mGame->playerList(); + for (int unsigned i = 0; i < playerList.count(); i++) { + slotAddPlayer(playerList.at(i)); + } + } +} + +KGame* KGameChat::game() const +{ + return d->mGame; +} + +KPlayer* KGameChat::fromPlayer() const +{ + return d->mFromPlayer; +} + +void KGameChat::slotUnsetKGame() +{ +//TODO: test this method! + + if (!d->mGame) { + return; + } + disconnect(d->mGame, 0, this, 0); + removeSendingEntry(d->mToMyGroup); + QMap<int, int>::Iterator it; + for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) { + removeSendingEntry(it.data()); + } +} + +void KGameChat::slotAddPlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": cannot add NULL player" << endl; + return; + } + if (hasPlayer(p->id())) { + kdError(11001) << k_funcinfo << ": player was added before" << endl; + return; + } + + int sendingId = nextId(); + addSendingEntry(comboBoxItem(p->name()), sendingId); + d->mSendId2PlayerId.insert(sendingId, p->id()); + connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)), + this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*))); + connect(p, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, KPlayer*)), + this, SLOT(slotReceivePrivateMessage(int, const QByteArray&, Q_UINT32, KPlayer*))); +} + +void KGameChat::slotRemovePlayer(KPlayer* p) +{ + if (!p) { + kdError(11001) << k_funcinfo << ": NULL player" << endl; + return; + } + if (!hasPlayer(p->id())) { + kdError(11001) << k_funcinfo << ": cannot remove non-existent player" << endl; + return; + } + + int id = sendingId(p->id()); + removeSendingEntry(id); + p->disconnect(this); + d->mSendId2PlayerId.remove(id); +} + +void KGameChat::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player) +{ + if (prop->id() == KGamePropertyBase::IdName) { +// kdDebug(11001) << "new Name" << endl; + changeSendingEntry(player->name(), sendingId(player->id())); +/* + mCombo->changeItem(comboBoxItem(player->name()), index); + */ + } else if (prop->id() == KGamePropertyBase::IdGroup) { + //TODO + } +} + +void KGameChat::slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me) +{ + if (!me || me != fromPlayer()) { + kdDebug() << k_funcinfo << "nope - not for us!" << endl; + return; + } + slotReceiveMessage(msgid, buffer, me->id(), sender); +} + +void KGameChat::slotReceiveMessage(int msgid, const QByteArray& buffer, Q_UINT32 , Q_UINT32 sender) +{ + QDataStream msg(buffer, IO_ReadOnly); + if (msgid != messageId()) { + return; + } + + QString text; + msg >> text; + + addMessage(sender, text); +} + diff --git a/libkdegames/kgame/kgamechat.h b/libkdegames/kgame/kgamechat.h new file mode 100644 index 00000000..6f7ea65d --- /dev/null +++ b/libkdegames/kgame/kgamechat.h @@ -0,0 +1,223 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-2002 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 __KGAMECHAT_H__ +#define __KGAMECHAT_H__ + +#include <qstring.h> + +#include "kchatbase.h" +#include <kdemacros.h> +class KPlayer; +class KGame; +class KGamePropertyBase; + +class KGameChatPrivate; + +/** + * @short A Chat widget for KGame-based games + * + * Call @ref setFromPlayer() first - this will be used as the "from" part of + * every message you will send. Otherwise it won't work! You can also use the + * fromPlayer parameter in the constructor though... + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KGameChat : public KChatBase +{ + Q_OBJECT +public: + /** + * Construct a @ref KGame chat widget on @p game that used @p msgid for + * the chat message. The @p fromPlayer is the local player (see @ref + * setFromPlayer). + **/ + KGameChat(KGame* game, int msgid, KPlayer* fromPlayer, QWidget * parent); + + /** + * @overload + * To make use of this widget you need to call @ref setFromPlayer + * manually. + **/ + KGameChat(KGame* game, int msgId, QWidget* parent); + + /** + * @overload + * This constructs a widget that is not usable. You must call at least + * setGame, setFromPlayer and setMessageId manually. + * @since 3.2 + **/ + KGameChat(QWidget* parent); + + virtual ~KGameChat(); + + enum SendingIds { + SendToGroup = 1 + }; + + /** + * This sets the fromPlayer to @p player. The fromPlayer is the + * player that will appear as "from" when you send messages through this + * widget. + * @param player The player of this widget + **/ + void setFromPlayer(KPlayer* player); + + KPlayer* fromPlayer() const; + + /** + * Set the @ref KGame object for this chat widget. All messages will be + * sent through this object. You don't have to implement any send + * functions, just call this function, call @ref setFromPlayer and be + * done :-) + * @param g The @ref KGame object the messages will be sent through + **/ + void setKGame(KGame* g); + + KGame* game() const; + + /** + * @return The id of the messages produced by KGameChat. The id will be + * used in @ref KGame as parameter msgid in the method @ref KGame::sendMessage + **/ + int messageId() const; + + /** + * Change the message id of the chat widget. It is recommended that you + * don't use this but prefer the constructor instead, but in certain + * situations (such as using this widget in Qt designer) it may be + * useful to change the message id. + * + * See also @ref messageId + * @since 3.2 + **/ + void setMessageId(int msgid); + + /** + * reimplemented from @ref KChatBase + * @return @ref KPlayer::name() for the player set by @ref setFromPlayer + **/ + virtual const QString& fromName() const; + + +public slots: + virtual void addMessage(const QString& fromName, const QString& text) { KChatBase::addMessage(fromName, text);} + virtual void addMessage(int fromId, const QString& text); + + void slotReceiveMessage(int, const QByteArray&, Q_UINT32 receiver, Q_UINT32 sender); + +protected: + /** + * @param id The ID of the sending entry, as returned by @ref + * KChatBase::sendingEntry + * @return True if the entry "send to all" was selected, otherwise false + **/ + bool isSendToAllMessage(int id) const; + + /** + * Used to indicate whether a message shall be sent to a group of + * players. Note that this was not yet implemented when this doc was + * written so this description might be wrong. (FIXME) + * @param id The ID of the sending entry, as returned by @ref + * KChatBase::sendingEntry + * @return True if the message is meant to be sent to a group (see @ref + * KPlayer::group), e.g. if "send to my group" was selected. + **/ + bool isToGroupMessage(int id) const; + + + /** + * Used to indicate whether the message shall be sent to a single player + * only. Note that you can also call @ref isSendToAllMessage and @ref + * isToGroupMessage - if both return false it must be a player message. + * This behaviour might be changed later - so don't depend on it. + * + * See also toPlayerId + * @param id The ID of the sending entry, as returned by + * KChatBase::sendingEntry + * @return True if the message shall be sent to a special player, + * otherwise false. + **/ + bool isToPlayerMessage(int id) const; + + /** + * @param id The ID of the sending entry, as returned by + * KChatBase::sendingEntry + * @return The ID of the player (see KPlayer::id) the sending entry + * belongs to. Note that the parameter id is an id as returned by ref + * KChatBase::sendingEntry and the id this method returns is a + * KPlayer ID. If isToPlayerMessage returns false this method + * returns -1 + **/ + int playerId(int id) const; + + /** + * @param playerId The ID of the KPlayer object + * @return The ID of the sending entry (see KChatBase) or -1 if + * the player id was not found. + **/ + int sendingId(int playerId) const; + + /** + * @return True if the player with this ID was added before (see + * slotAddPlayer) + **/ + bool hasPlayer(int id) const; + + /** + * @param name The name of the added player + * @return A string that will be added as sending entry in @ref + * KChatBase. By default this is "send to name" where name is the name + * that you specify. See also KChatBase::addSendingEntry + **/ + virtual QString sendToPlayerEntry(const QString& name) const; + + +protected slots: + /** + * Unsets a KGame object that has been set using setKGame + * before. You don't have to call this - this is usually done + * automatically. + **/ + void slotUnsetKGame(); + + + void slotPropertyChanged(KGamePropertyBase*, KPlayer*); + void slotAddPlayer(KPlayer*); + void slotRemovePlayer(KPlayer*); + + /** + * Called when KPlayer::signalNetworkData is emitted. The message + * gets forwarded to slotReceiveMessage if @p me equals + * fromPlayer. + **/ + void slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me); + +protected: + virtual void returnPressed(const QString& text); + +private: + void init(KGame* g, int msgid); + +private: + KGameChatPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgameerror.cpp b/libkdegames/kgame/kgameerror.cpp new file mode 100644 index 00000000..93f40f93 --- /dev/null +++ b/libkdegames/kgame/kgameerror.cpp @@ -0,0 +1,80 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgameerror.h" +#include "kgamemessage.h" + +#include <klocale.h> + +QByteArray KGameError::errVersion(int remoteVersion) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + s << (Q_INT32)KGameMessage::version(); + s << (Q_INT32)remoteVersion; + return b; +} + +QByteArray KGameError::errCookie(int localCookie, int remoteCookie) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + s << (Q_INT32)localCookie; + s << (Q_INT32)remoteCookie; + return b; +} + +QString KGameError::errorText(int errorCode, const QByteArray& message) +{ + QDataStream s(message, IO_ReadOnly); + return errorText(errorCode, s); +} + +QString KGameError::errorText(int errorCode, QDataStream& s) +{ + QString text; + switch (errorCode) { + case Cookie: + { + Q_INT32 cookie1; + Q_INT32 cookie2; + s >> cookie1; + s >> cookie2; + text = i18n("Cookie mismatch!\nExpected Cookie: %1\nReceived Cookie: %2").arg(cookie1).arg(cookie2); + break; + } + case Version: + { + Q_INT32 version1; + Q_INT32 version2; + s >> version1; + s >> version2; + text = i18n("KGame Version mismatch!\nExpected Version: %1\nReceived Version: %2\n").arg(version1).arg(version2); + break; + } + default: + text = i18n("Unknown error code %1").arg(errorCode); + } + return text; +} + diff --git a/libkdegames/kgame/kgameerror.h b/libkdegames/kgame/kgameerror.h new file mode 100644 index 00000000..2916e891 --- /dev/null +++ b/libkdegames/kgame/kgameerror.h @@ -0,0 +1,59 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEERROR_H_ +#define __KGAMEERROR_H_ + +#include <qstring.h> + + +class KGameError +{ +public: + KGameError() { } + ~KGameError() { } + + enum ErrorCodes { + Cookie = 0, // Cookie mismatch + Version = 1 // Version mismatch + }; + + /** + * Generate an error message with Erorr Code = ErrCookie + **/ + static QByteArray errCookie(int localCookie, int remoteCookie); + static QByteArray errVersion(int remoteVersion); + + /** + * Create an erorr text using a QDataStream (QByteArray) which was + * created using @ref KGameError. This is the opposite function to all + * the errXYZ() function (e.g. @ref errVersion). + * You want to use this to generate the message that shall be + * displayed to the user. + * @return an error message + **/ + static QString errorText(int errorCode, QDataStream& message); + static QString errorText(int errorCode, const QByteArray& message); + +}; + +#endif diff --git a/libkdegames/kgame/kgameio.cpp b/libkdegames/kgame/kgameio.cpp new file mode 100644 index 00000000..9b3a55e8 --- /dev/null +++ b/libkdegames/kgame/kgameio.cpp @@ -0,0 +1,539 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgameio.h" +#include "kgameio.moc" +#include "kgame.h" +#include "kplayer.h" +#include "kgamemessage.h" +#include "kmessageio.h" + +#include <kdebug.h> + +#include <qwidget.h> +#include <qbuffer.h> +#include <qtimer.h> + +#include <stdlib.h> + +// ----------------------- Generic IO ------------------------- +KGameIO::KGameIO() : QObject(0,0) +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl; + mPlayer = 0; +} + +KGameIO::KGameIO(KPlayer* player) : QObject(0,0) +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl; + mPlayer = 0; + if (player) + { + player->addGameIO(this); + } +} + +KGameIO::~KGameIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + // unregister ourselves + if (player()) + { + player()->removeGameIO(this, false); + } +} + +void KGameIO::initIO(KPlayer *p) +{ + setPlayer(p); +} + +void KGameIO::notifyTurn(bool b) +{ + if (!player()) + { + kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl; + return; + } + bool sendit=false; + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + emit signalPrepareTurn(stream, b, this, &sendit); + if (sendit) + { + QDataStream ostream(buffer,IO_ReadOnly); + Q_UINT32 sender = player()->id(); // force correct sender + kdDebug(11001) << "Prepare turn sendInput" << endl; + sendInput(ostream, true, sender); + } +} + +KGame* KGameIO::game() const +{ + if (!player()) + { + return 0; + } + return player()->game(); +} + +bool KGameIO::sendInput(QDataStream& s, bool transmit, Q_UINT32 sender) +{ + if (!player()) + { + return false; + } + return player()->forwardInput(s, transmit, sender); +} + +void KGameIO::Debug() +{ + kdDebug(11001) << "------------------- KGAMEINPUT --------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "rtti : " << rtti() << endl; + kdDebug(11001) << "Player: " << player() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + + +// ----------------------- Key IO --------------------------- +KGameKeyIO::KGameKeyIO(QWidget *parent) + : KGameIO() +{ + if (parent) + { + kdDebug(11001) << "Key Event filter installed" << endl; + parent->installEventFilter(this); + } +} + +KGameKeyIO::~KGameKeyIO() +{ + if (parent()) + { + parent()->removeEventFilter(this); + } +} + +int KGameKeyIO::rtti() const { return KeyIO; } + +bool KGameKeyIO::eventFilter( QObject *o, QEvent *e ) +{ + if (!player()) + { + return false; + } + + // key press/release + if ( e->type() == QEvent::KeyPress || + e->type() == QEvent::KeyRelease ) + { + QKeyEvent *k = (QKeyEvent*)e; + // kdDebug(11001) << "KGameKeyIO " << this << " key press/release " << k->key() << endl ; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + bool eatevent=false; + emit signalKeyEvent(this,stream,k,&eatevent); + QDataStream msg(buffer,IO_ReadOnly); + + if (eatevent && sendInput(msg)) + { + return eatevent; + } + return false; // do not eat otherwise + } + return QObject::eventFilter( o, e ); // standard event processing +} + + +// ----------------------- Mouse IO --------------------------- +KGameMouseIO::KGameMouseIO(QWidget *parent,bool trackmouse) + : KGameIO() +{ + if (parent) + { + kdDebug(11001) << "Mouse Event filter installed tracking=" << trackmouse << endl; + parent->installEventFilter(this); + parent->setMouseTracking(trackmouse); + } +} + +KGameMouseIO::~KGameMouseIO() +{ + if (parent()) + { + parent()->removeEventFilter(this); + } +} + +int KGameMouseIO::rtti() const +{ + return MouseIO; +} + +void KGameMouseIO::setMouseTracking(bool b) +{ + if (parent()) + { + ((QWidget*)parent())->setMouseTracking(b); + } +} + +bool KGameMouseIO::eventFilter( QObject *o, QEvent *e ) +{ + if (!player()) + { + return false; + } +// kdDebug(11001) << "KGameMouseIO " << this << endl ; + + // mouse action + if ( e->type() == QEvent::MouseButtonPress || + e->type() == QEvent::MouseButtonRelease || + e->type() == QEvent::MouseButtonDblClick || + e->type() == QEvent::Wheel || + e->type() == QEvent::MouseMove + ) + { + QMouseEvent *k = (QMouseEvent*)e; + // kdDebug(11001) << "KGameMouseIO " << this << endl ; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + bool eatevent=false; + emit signalMouseEvent(this,stream,k,&eatevent); +// kdDebug(11001) << "################# eatevent=" << eatevent << endl; + QDataStream msg(buffer,IO_ReadOnly); + if (eatevent && sendInput(msg)) + { + return eatevent; + } + return false; // do not eat otherwise + } + return QObject::eventFilter( o, e ); // standard event processing +} + + +// ----------------------- KGameProcesPrivate --------------------------- +class KGameProcessIO::KGameProcessIOPrivate +{ +public: + KGameProcessIOPrivate() + { + //mMessageServer = 0; + //mMessageClient = 0; + mProcessIO=0; + } + //KMessageServer *mMessageServer; + //KMessageClient *mMessageClient; + KMessageProcess *mProcessIO; +}; + +// ----------------------- Process IO --------------------------- +KGameProcessIO::KGameProcessIO(const QString& name) + : KGameIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)=" << sizeof(KGameProcessIO) << endl; + d = new KGameProcessIOPrivate; + + //kdDebug(11001) << "================= KMEssageServer ==================== " << endl; + //d->mMessageServer=new KMessageServer(0,this); + //kdDebug(11001) << "================= KMEssageClient ==================== " << endl; + //d->mMessageClient=new KMessageClient(this); + kdDebug(11001) << "================= KMEssageProcessIO ==================== " << endl; + d->mProcessIO=new KMessageProcess(this,name); + kdDebug(11001) << "================= KMEssage Add client ==================== " << endl; + //d->mMessageServer->addClient(d->mProcessIO); + //kdDebug(11001) << "================= KMEssage SetSErver ==================== " << endl; + //d->mMessageClient->setServer(d->mMessageServer); + kdDebug(11001) << "================= KMEssage: Connect ==================== " << endl; + //connect(d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), + // this, SLOT(clientMessage(const QByteArray&, Q_UINT32))); + //connect(d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &)), + // this, SLOT(clientMessage(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &))); + connect(d->mProcessIO, SIGNAL(received(const QByteArray&)), + this, SLOT(receivedMessage(const QByteArray&))); + //kdDebug(11001) << "Our client is id="<<d->mMessageClient->id() << endl; +} + +KGameProcessIO::~KGameProcessIO() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + kdDebug(11001) << "player="<<player() << endl; + if (player()) + { + player()->removeGameIO(this,false); + } + if (d->mProcessIO) + { + delete d->mProcessIO; + d->mProcessIO=0; + } + delete d; +} + +int KGameProcessIO::rtti() const +{ + return ProcessIO; +} + +void KGameProcessIO::initIO(KPlayer *p) +{ + KGameIO::initIO(p); + // Send 'hello' to process + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + Q_INT16 id = p->userId(); + stream << id; + + bool sendit=true; + if (p) + { + emit signalIOAdded(this,stream,p,&sendit); + if (sendit ) + { + Q_UINT32 sender = p->id(); + kdDebug(11001) << "Sending IOAdded to process player !!!!!!!!!!!!!! " << endl; + sendSystemMessage(stream, KGameMessage::IdIOAdded, 0, sender); + } + } +} + +void KGameProcessIO::notifyTurn(bool b) +{ + if (!player()) + { + kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl; + return; + } + bool sendit=true; + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << (Q_INT8)b; + emit signalPrepareTurn(stream,b,this,&sendit); + if (sendit) + { + Q_UINT32 sender=player()->id(); + kdDebug(11001) << "Sending Turn to process player !!!!!!!!!!!!!! " << endl; + sendSystemMessage(stream, KGameMessage::IdTurn, 0, sender); + } +} + +void KGameProcessIO::sendSystemMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + sendAllMessages(stream, msgid, receiver, sender, false); +} + +void KGameProcessIO::sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + sendAllMessages(stream, msgid, receiver, sender, true); +} + +void KGameProcessIO::sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg) +{ + kdDebug(11001) << "==============> KGameProcessIO::sendMessage (usermsg="<<usermsg<<")" << endl; + // if (!player()) return ; + //if (!player()->isActive()) return ; + + if (usermsg) + { + msgid+=KGameMessage::IdUser; + } + + kdDebug(11001) << "=============* ProcessIO (" << msgid << "," << receiver << "," << sender << ") ===========" << endl; + + QByteArray buffer; + QDataStream ostream(buffer,IO_WriteOnly); + QBuffer *device=(QBuffer *)stream.device(); + QByteArray data=device->buffer();; + + KGameMessage::createHeader(ostream,sender,receiver,msgid); + // ostream.writeRawBytes(data.data()+device->at(),data.size()-device->at()); + ostream.writeRawBytes(data.data(),data.size()); + kdDebug(11001) << " Adding user data from pos="<< device->at() <<" amount= " << data.size() << " byte " << endl; + //if (d->mMessageClient) d->mMessageClient->sendBroadcast(buffer); + if (d->mProcessIO) + { + d->mProcessIO->send(buffer); + } +} + +//void KGameProcessIO::clientMessage(const QByteArray& receiveBuffer, Q_UINT32 clientID, const QValueList <Q_UINT32> &recv) +void KGameProcessIO::receivedMessage(const QByteArray& receiveBuffer) +{ + QDataStream stream(receiveBuffer,IO_ReadOnly); + int msgid; + Q_UINT32 sender; + Q_UINT32 receiver; + KGameMessage::extractHeader(stream,sender,receiver,msgid); + + kdDebug(11001) << "************* Got process message sender =" << sender + << " receiver=" << receiver << " msgid=" << msgid << endl; + + + // Cut out the header part...to not confuse network code + QBuffer *buf=(QBuffer *)stream.device(); + QByteArray newbuffer; + newbuffer.setRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at()); + QDataStream ostream(newbuffer,IO_ReadOnly); + kdDebug(11001) << "Newbuffer size=" << newbuffer.size() << endl; + + + +// This is a dummy message which allows us the process to talk with its owner + if (msgid==KGameMessage::IdProcessQuery) + { + emit signalProcessQuery(ostream,this); + } + else if (player()) + { + sender = player()->id(); // force correct sender + if (msgid==KGameMessage::IdPlayerInput) + { + sendInput(ostream,true,sender); + } + else + { + player()->forwardMessage(ostream,msgid,receiver,sender); + } + } + else + { + kdDebug(11001) << k_funcinfo << ": Got message from process but no player defined!" << endl; + } + newbuffer.resetRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at()); +} + + +// ----------------------- Computer IO -------------------------- +class KGameComputerIO::KGameComputerIOPrivate +{ +//TODO: maybe these should be KGameProperties!! +public: + KGameComputerIOPrivate() + { + mAdvanceCounter = 0; + mReactionPeriod = 0; + + mPauseCounter = 0; + + mAdvanceTimer = 0; + } + int mAdvanceCounter; + int mReactionPeriod; + + int mPauseCounter; + + QTimer* mAdvanceTimer; +}; + +KGameComputerIO::KGameComputerIO() : KGameIO() +{ + init(); +} + +KGameComputerIO::KGameComputerIO(KPlayer* p) : KGameIO(p) +{ + init(); +} + +void KGameComputerIO::init() +{ + d = new KGameComputerIOPrivate; +} + +KGameComputerIO::~KGameComputerIO() +{ + if (d->mAdvanceTimer) + { + delete d->mAdvanceTimer; + } + delete d; +} + +int KGameComputerIO::rtti() const +{ + return ComputerIO; +} + +void KGameComputerIO::setReactionPeriod(int calls) +{ + d->mReactionPeriod = calls; +} + +int KGameComputerIO::reactionPeriod() const +{ + return d->mReactionPeriod; +} + +void KGameComputerIO::setAdvancePeriod(int ms) +{ + stopAdvancePeriod(); + d->mAdvanceTimer = new QTimer(this); + connect(d->mAdvanceTimer, SIGNAL(timeout()), this, SLOT(advance())); + d->mAdvanceTimer->start(ms); +} + +void KGameComputerIO::stopAdvancePeriod() +{ + if (d->mAdvanceTimer) + { + d->mAdvanceTimer->stop(); + delete d->mAdvanceTimer; + } +} + +void KGameComputerIO::pause(int calls) +{ + d->mPauseCounter = calls; +} + +void KGameComputerIO::unpause() +{ + pause(0); +} + +void KGameComputerIO::advance() +{ + if (d->mPauseCounter > 0) + { + d->mPauseCounter--; + return; + } + else if (d->mPauseCounter < 0) + { + return; + } + d->mAdvanceCounter++; + if (d->mAdvanceCounter >= d->mReactionPeriod) + { + d->mAdvanceCounter = 0; + reaction(); + } +} + +void KGameComputerIO::reaction() +{ + emit signalReaction(); +} + + diff --git a/libkdegames/kgame/kgameio.h b/libkdegames/kgame/kgameio.h new file mode 100644 index 00000000..4d7e0f5b --- /dev/null +++ b/libkdegames/kgame/kgameio.h @@ -0,0 +1,566 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEIO_H__ +#define __KGAMEIO_H__ + +#include <qstring.h> +#include <qobject.h> +#include <kdemacros.h> +class KPlayer; +class KGame; +class KProcess; + +/** + * \short Base class for IO devices for games + * + * This is the master class for + * creating IO game devices. You cannot use it directly. + * Either take one of the classes derived from it or + * you have to create your own IO class derived from it (more probably). + * + * The idea behind this class is to provide a common interface + * for input devices into your game. By programming a KGameIO + * device you need not distinguish the actual IO in the game + * anymore. All work is done by the IO's. This allows very + * easy reuse in other games as well. + * A further advantage of using the IO's is that you can exchange + * the control of a player at runtime. E.g. you switch a player + * to be controlled by the computer or vice versa. + * + * To achieve this you have to make all of your player inputs through a + * KGameIO. You will usually call KGameIO::sendInput to do so. + * + * @author Martin Heni <martin@heni-online.de> + */ +class KDE_EXPORT KGameIO : public QObject +{ + Q_OBJECT + +public: + /** + * Constructs a KGameIO object + */ + KGameIO(); + KGameIO(KPlayer*); + virtual ~KGameIO(); + + /** + * Gives debug output of the game status + */ + void Debug(); + + /** + * Identifies the KGameIO via the rtti function + */ + enum IOMode {GenericIO=1,KeyIO=2,MouseIO=4,ProcessIO=8,ComputerIO=16}; + /** + * Run time idendification. Predefined values are from IOMode + * You MUST overwrite this in derived classes! + * + * @return rtti value + */ + virtual int rtti() const = 0; // Computer, network, local, ... + + /** + * This function returns the player who owns this IO + * + * @return the player this IO device is plugged into + */ + KPlayer *player() const {return mPlayer;} + + /** + * Equivalent to player()->game() + * @return the @ref KGame object of this player + **/ + KGame* game() const; + + /** + * Sets the player to which this IO belongs to. This + * is done automatically when adding a device to a + * player + * + * @param p the player + */ + void setPlayer(KPlayer *p) {mPlayer=p;} + + /** + * Init this device by setting the player and e.g. sending an + * init message to the device. This initialisation message is + * very useful for computer players as you can transmit the + * game status to them and only update this status in the setTurn + * commands. + * + * Called by @ref KPlayer::addGameIO only! + */ + virtual void initIO(KPlayer *p); + + /** + * Notifies the IO device that the player's setTurn had been called + * Called by KPlayer + * + * This emits @ref signalPrepareTurn and sends the turn if the send + * parameter is set to true. + * + * @param b turn is true/false + */ + virtual void notifyTurn(bool b); + + /** + * Send an input message using @ref KPlayer::forwardInput + **/ + bool sendInput(QDataStream& stream, bool transmit = true, Q_UINT32 sender = 0); + +signals: + /** + * Signal generated when @ref KPlayer::myTurn changes. This can either be + * when you get the turn status or when you lose it. + * + * The datastream has to be filled with a move. If you set (or leave) the + * send parameter to FALSE then nothing happens: the datastream will be + * ignored. If you set it to TRUE @ref sendInput is used to + * send the move. + * + * Often you want to ignore this signal (leave send=FALSE) and send the + * message later. This is usually the case for a human player as he probably + * doesn't react immediately. But you can still use this e.g. to notify the + * player about the turn change. + * + * Example: + * \code + * void GameWindow::slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool * ) + * { + * KPlayer *player=input->player(); + * if (!player->myTurn()) return ; + * if (!b) return ; // only do something on setTurn(true) + * stream << 1 << 2 << 3; // Some data for the process + * } + * \endcode + * + * @param io the KGameIO object itself + * @param stream the stream into which the move will be written + * @param turn the argument of setTurn + * @param send set this to true to send the generated move using @ref + * sendInput + **/ + void signalPrepareTurn(QDataStream & stream, bool turn, KGameIO *io, bool * send); + + +private: + KPlayer *mPlayer; +}; + +/** + * The KGameKeyIO class. It is used to process keyboard input + * from a widget and create moves for the player it belongs to. + * @author Martin Heni <martin@heni-online.de> + */ +class KDE_EXPORT KGameKeyIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Create a keyboard input devices. All keyboards + * inputs of the given widgets are passed through a signal + * handler signalKeyEvent and can be used to generate + * a valid move for the player. + * Note the widget you pass to the constructor must be + * the main window of your application, e.g. view->parentWidget() + * as QT does not forward your keyevents otherwise. This means + * that this might be a different widget comapred to the one you + * use for mouse inputs! + * Example: + * \code + * KGameKeyIO *input; + * input=new KGameKeyIO(myWidget); + * connect(input,SIGNAL(signalKeyEvent(KGameIO *,QDataStream &,QKeyEvent *,bool *)), + * this,SLOT(slotKeyInput(KGameIO *,QDataStream &,QKeyEvent *,bool *))); + * \endcode + * + * @param parent The parents widget whose keyboard events * should be grabbed + */ + KGameKeyIO(QWidget *parent); + virtual ~KGameKeyIO(); + + /** + * The idendification of the IO + * + * @return KeyIO + */ + virtual int rtti() const; + +signals: + /** + * Signal handler for keyboard events. This function is called + * on every keyboard event. If appropriate it can generate a + * move for the player the device belongs to. If this is done + * and the event is eaten eatevent needs to be set to true. + * What move you generate (i.e. what you write to the stream) + * is totally up to you as it will not be evaluated but forwared + * to the player's/game's input move function + * Example: + * \code + * KPlayer *player=input->player(); // Get the player + * Q_INT32 key=e->key(); + * stream << key; + * eatevent=true; + * \endcode + * + * @param io the IO device we belong to + * @param stream the stream where we write our move into + * @param m The QKeyEvent we can evaluate + * @param eatevent set this to true if we processed the event + */ + void signalKeyEvent(KGameIO *io,QDataStream &stream,QKeyEvent *m,bool *eatevent); + +protected: + /** + * Internal method to process the events + */ + bool eventFilter( QObject *o, QEvent *e ); +}; + +/** + * The KGameMouseIO class. It is used to process mouse input + * from a widget and create moves for the player it belongs to. + * @author Martin Heni <martin@heni-online.de> + */ +class KDE_EXPORT KGameMouseIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a mouse IO device. It captures all mouse + * event of the given widget and forwards them to the + * signal handler signalMouseEvent. + * Example: + * \code + * KGameMouseIO *input; + * input=new KGameMouseIO(mView); + * connect(input,SIGNAL(signalMouseEvent(KGameIO *,QDataStream &,QMouseEvent *,bool *)), + * this,SLOT(slotMouseInput(KGameIO *,QDataStream &,QMouseEvent *,bool *))); + * \endcode + * + * @param parent The widget whose events should be captured + * @param trackmouse enables mouse tracking (gives mouse move events) + */ + KGameMouseIO(QWidget *parent,bool trackmouse=false); + virtual ~KGameMouseIO(); + + /** + * Manually activate or deactivate mouse tracking + * + * @param b true = tracking on + */ + void setMouseTracking(bool b); + /** + * The idendification of the IO + * + * @return MouseIO + */ + virtual int rtti() const; + +signals: + /** + * Signal handler for mouse events. This function is called + * on every mouse event. If appropriate it can generate a + * move for the player the device belongs to. If this is done + * and the event is eaten eatevent needs to be set to true. + * @see signalKeyEvent + * Example: + * \code + * KPlayer *player=input->player(); // Get the player + * Q_INT32 button=e->button(); + * stream << button; + * eatevent=true; + * \endcode + * + * @param io the IO device we belong to + * @param stream the stream where we write our move into + * @param m The QMouseEvent we can evaluate + * @param eatevent set this to true if we processed the event + */ + void signalMouseEvent(KGameIO *io,QDataStream &stream,QMouseEvent *m,bool *eatevent); + +protected: + /** + * Internal event filter + */ + bool eventFilter( QObject *o, QEvent *e ); + +}; + + +/** + * The KGameProcessIO class. It is used to create a computer player + * via a separate process and communicate transparetly with it. + * Its counterpart is the @ref KGameProcess class which needs + * to be used by the computer player. See its documentation + * for the definition of the computer player. + * @author Martin Heni <martin@heni-online.de> + */ +class KDE_EXPORT KGameProcessIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a computer player via a separate process. The process + * name is given as fully qualified filename. + * Example: + * \code + * KGameProcessIO *input; + * input=new KGameProcessIO(executable_file); + * connect(input,SIGNAL(signalPrepareTurn(QDataStream &,bool,KGameIO *,bool *)), + * this,SLOT(slotPrepareTurn(QDataStream &,bool,KGameIO *,bool *))); + * connect(input,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)), + * this,SLOT(slotProcessQuery(QDataStream &,KGameProcessIO *))); + * \endcode + * + * @param name the filename of the process to start + */ + KGameProcessIO(const QString& name); + + /** + * Deletes the process input devices + */ + virtual ~KGameProcessIO(); + + /** + * The idendification of the IO + * + * @return ProcessIO + */ + int rtti() const; + + /** + * Send a message to the process. This is analogous to the sendMessage + * commands of KGame. It will result in a signal of the computer player + * on which you can react in the process player. + * + * @param stream - the actual data + * @param msgid - the id of the message + * @param receiver - not used + * @param sender - who send the message + */ + void sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * Send a system message to the process. This is analogous to the sendMessage + * commands of KGame. It will result in a signal of the computer player + * on which you can react in the process player. + * + * @param stream - the actual data + * @param msgid - the id of the message + * @param receiver - not used + * @param sender - who send the message + */ + void sendSystemMessage(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender); + + /** + * Init this device by setting the player and e.g. sending an + * init message to the device. Calling this function will emit + * the IOAdded signal on which you can react and initilise the + * computer player. + * This function is called automatically when adding the IO to + * a player. + */ + void initIO(KPlayer *p); + + /** + * Notifies the IO device that the player's setTurn had been called + * Called by KPlayer. You can react on the @ref signalPrepareTurn to + * prepare a message for the process, i.e. either update it on + * the changes made to the game since the last turn or the initIO + * has been called or transmit your gamestatus now. + * + * @param turn is true/false + */ + virtual void notifyTurn(bool turn); + + protected: + /** + * Internal ~ombined function for all message handling + **/ + void sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg); + + protected slots: + /** + * Internal message handler to receive data from the process + */ + void receivedMessage(const QByteArray& receiveBuffer); + + +signals: + /** + * A computer query message is received. This is a 'dummy' + * message sent by the process if it needs to communicate + * with us. It is not forwarded over the network. + * Reacting to this message allows you to 'answer' questions + * of the process, e.g. sending addition data which the process + * needs to calculate a move. + * + * Example: + * \code + * void GameWindow::slotProcessQuery(QDataStream &stream,KGameProcessIO *reply) + * { + * int no; + * stream >> no; // We assume the process sends us an integer question numner + * if (no==1) // but YOU have to do this in the process player + * { + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * reply->sendSystemMessage(out,4242,0,0); // lets reply something... + * } + * } + * \endcode + */ + void signalProcessQuery(QDataStream &stream,KGameProcessIO *me); + + /** + * Signal generated when the computer player is added. + * You can use this to communicated with the process and + * e.g. send initialisation information to the process. + * + * @param game the KGameIO object itself + * @param stream the stream into which the move will be written + * @param p the player itself + * @param send set this to false if no move should be generated + */ + void signalIOAdded(KGameIO *game,QDataStream &stream,KPlayer *p,bool *send); + + +protected: + +private: + class KGameProcessIOPrivate; + KGameProcessIOPrivate* d; +}; + +/** + * \brief KGameIO variant for real-time games + * + * The KGameComputerIO class. It is used to create a LOCAL computer player + * and communicate transparently with it. + * Question: Is this needed or is it overwritten anyway for a real game? + * + * You most probably don't want to use this if you want to design a turn based + * game/player. You'll rather use @ref KGameIO directly, i.e. subclass it + * yourself. You just need to use @ref KGameIO::signalPrepareTurn and/or @ref + * KGameIO::notifyTurn there. + * + * This is rather meant to be of use in real time games. + * + * @author <b_mann@gmx.de> + */ +class KDE_EXPORT KGameComputerIO : public KGameIO +{ + Q_OBJECT + +public: + /** + * Creates a LOCAL computer player + * + */ + KGameComputerIO(); + KGameComputerIO(KPlayer* player); + ~KGameComputerIO(); + + int rtti() const; + + /** + * The number of advance calls until the player (or rather: the IO) + * does something (default: 1). + **/ + void setReactionPeriod(int advanceCalls); + int reactionPeriod() const; + + /** + * Start a QTimer which calls advance every @p ms milli seconds. + **/ + void setAdvancePeriod(int ms); + + void stopAdvancePeriod(); + + /** + * Ignore calls number of advance calls. if calls is -1 then all + * following advance calls are ignored until unpause is called. + * + * This simply prevents the internal advance counter to be increased. + * + * You may want to use this to emulate a "thinking" computer player. Note + * that this means if you increase the advance period (see + * setAdvancePeriod), i.e. if you change the speed of your game, your + * computer player thinks "faster". + * @param calls Number of advance calls to be ignored + **/ + void pause(int calls = -1); + + /** + * Equivalent to pause(0). Immediately continue to increase the internal + * advance counter. + **/ + void unpause(); + +public slots: + /** + * Works kind of similar to QCanvas::advance. Increase the internal + * advance counter. If @p reactionPeriod is reached the counter is set back to + * 0 and @ref signalReaction is emitted. This is when the player is meant + * to do something (move its units or so). + * + * This is very useful if you use QCanvas as you can use this in your + * QCanvas::advance call. The advantage is that if you change the speed + * of the game (i.e. change QCanvas::setAdvancePeriod) the computer + * player gets slower as well. + * + * If you don't use QCanvas you can use setAdvancePeriod to get + * the same result. Alternatively you can just use a QTimer. + * + **/ + virtual void advance(); + +signals: + /** + * This signal is emitted when your computer player is meant to do + * something, or better is meant to be allowed to do something. + **/ + void signalReaction(); + +protected: + /** + * Default implementation simply emits signalReaction + **/ + virtual void reaction(); + +private: + void init(); + +private: + class KGameComputerIOPrivate; + KGameComputerIOPrivate* d; +}; + + +#endif diff --git a/libkdegames/kgame/kgamemessage.cpp b/libkdegames/kgame/kgamemessage.cpp new file mode 100644 index 00000000..6464d407 --- /dev/null +++ b/libkdegames/kgame/kgamemessage.cpp @@ -0,0 +1,156 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgamemessage.h" + +#include <klocale.h> + +#define MESSAGE_VERSION 2 + +Q_UINT32 KGameMessage::createPlayerId(int oldplayerid,Q_UINT32 gameid) +{ + int p; + p = oldplayerid & 0x3ff; // remove game id + p |= (gameid << 10); + return p; +} + +int KGameMessage::rawPlayerId(Q_UINT32 playerid) +{ + return playerid & 0x03ff; +} + +Q_UINT32 KGameMessage::rawGameId(Q_UINT32 playerid) +{ + return (playerid & 0xfc00) >> 10; +} + +bool KGameMessage::isPlayer(Q_UINT32 msgid) +{ + if (msgid & 0xfc00) { + return true; + } else { + return false; + } +} + +bool KGameMessage::isGame(Q_UINT32 msgid) +{ + return !isPlayer(msgid); +} + + +void KGameMessage::createHeader(QDataStream &msg,Q_UINT32 sender,Q_UINT32 receiver,int msgid) +{ + msg << (Q_INT16)sender << (Q_INT16)receiver << (Q_INT16)msgid; +} + +void KGameMessage::extractHeader(QDataStream &msg,Q_UINT32 &sender,Q_UINT32 &receiver,int &msgid) +{ + Q_INT16 d3,d4,d5; + msg >> d3 >> d4 >> d5; + sender=d3;receiver=d4;msgid=d5; +} + +void KGameMessage::createPropertyHeader(QDataStream &msg,int id) +{ + msg << (Q_INT16)id; +} + +void KGameMessage::extractPropertyHeader(QDataStream &msg,int &id) +{ + Q_INT16 d1; + msg >> d1; + id=d1; +} + +void KGameMessage::createPropertyCommand(QDataStream &msg,int cmdid,int pid,int cmd) +{ + createPropertyHeader(msg,cmdid); + msg << (Q_INT16)pid ; + msg << (Q_INT8)cmd ; +} + +void KGameMessage::extractPropertyCommand(QDataStream &msg,int &pid,int &cmd) +{ + Q_INT16 d1; + Q_INT8 d2; + msg >> d1 >> d2; + pid=d1; + cmd=d2; +} + +int KGameMessage::version() +{ + return MESSAGE_VERSION; +} + +QString KGameMessage::messageId2Text(int msgid) +{ +// this should contain all KGameMessage::GameMessageIds +// feel free to add missing ones, to remove obsolete one and even feel free to +// let it be ;-) + switch (msgid) { + case KGameMessage::IdSetupGame: + return i18n("Setup Game"); + case KGameMessage::IdSetupGameContinue: + return i18n("Setup Game Continue"); + case KGameMessage::IdGameLoad: + return i18n("Load Game"); + case KGameMessage::IdGameConnected: + return i18n("Client game connected"); + case KGameMessage::IdGameSetupDone: + return i18n("Game setup done"); + case KGameMessage::IdSyncRandom: + return i18n("Synchronize Random"); + case KGameMessage::IdDisconnect: + return i18n("Disconnect"); + case KGameMessage::IdPlayerProperty: + return i18n("Player Property"); + case KGameMessage::IdGameProperty: + return i18n("Game Property"); + case KGameMessage::IdAddPlayer: + return i18n("Add Player"); + case KGameMessage::IdRemovePlayer: + return i18n("Remove Player"); + case KGameMessage::IdActivatePlayer: + return i18n("Activate Player"); + case KGameMessage::IdInactivatePlayer: + return i18n("Inactivate Player"); + case KGameMessage::IdTurn: + return i18n("Id Turn"); + case KGameMessage::IdError: + return i18n("Error Message"); + case KGameMessage::IdPlayerInput: + return i18n("Player Input"); + case KGameMessage::IdIOAdded: + return i18n("An IO was added"); + case KGameMessage::IdProcessQuery: + return i18n("Process Query"); + case KGameMessage::IdPlayerId: + return i18n("Player ID"); + case KGameMessage::IdUser: // IdUser must be unknown for use, too! + default: + return QString::null; + } +} diff --git a/libkdegames/kgame/kgamemessage.h b/libkdegames/kgame/kgamemessage.h new file mode 100644 index 00000000..4394b4fa --- /dev/null +++ b/libkdegames/kgame/kgamemessage.h @@ -0,0 +1,173 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEMSG_H_ +#define __KGAMEMSG_H_ + +#include <qdatastream.h> +#include <kdemacros.h> + +class KDE_EXPORT KGameMessage +{ + public: + /** + * Creates a fully qualified player ID which contains the original + * player id in the lower bits and the game number in the higher bits. + * Do not rely on the exact bit positions as they are internal. + * + * See also @ref rawPlayerId and @ref rawGameId which are the inverse + * operations + * + * @param playerid the player id - can include a gameid (will get removed) + * @param gameid The game id (<64). 0 For broadcast. + * @return the new player id + */ + static Q_UINT32 createPlayerId(int player, Q_UINT32 game); + + /** + * Returns the raw playerid, that is, a id which does not + * contain the game number encoded in it. See also @ref createPlayerId which + * is the inverse operation. + * + * @param the player id + * @return the raw player id + **/ + static int rawPlayerId(Q_UINT32 playerid); + + /** + * Returns the raw game id, that is, the game id the player + * belongs to. Se also @ref createPlayerId which is the inverse operation. + * + * @param the player id + * @return the raw game id + **/ + static Q_UINT32 rawGameId(Q_UINT32 playerid); + + /** + * Checks whether a message receiver/sender is a player + * + * @param id The ID of the sender/receiver + * @return true/false + */ + static bool isPlayer(Q_UINT32 id); + + /** + * Checks whether the sender/receiver of a message is a game + * + * @param id The ID of the sender/receiver + * @return true/false + */ + static bool isGame(Q_UINT32 id); + + /** + * Creates a message header given cookie,sender,receiver,... + * + * Also puts "hidden" header into the stream which are used by KGameClient + * (message length and magic cookie). If you don't need them remove them + * with @ref dropExternalHeader + */ + static void createHeader(QDataStream &msg, Q_UINT32 sender, Q_UINT32 receiver, int msgid); + + /** + * Retrieves the information like cookie,sender,receiver,... from a message header + * + * Note that it could be necessary to call @ref dropExternalHeader first + */ + static void extractHeader(QDataStream &msg,Q_UINT32 &sender, Q_UINT32 &receiver, int &msgid); + + /** + * Creates a property header given the property id + */ + static void createPropertyHeader(QDataStream &msg, int id); + + /** + * Retrieves the property id from a property message header + */ + static void extractPropertyHeader(QDataStream &msg, int &id); + + /** + * Creates a property header given the property id + */ + static void createPropertyCommand(QDataStream &msg, int cmdid, int pid, int cmd); + + /** + * Retrieves the property id from a property message header + */ + static void extractPropertyCommand(QDataStream &msg, int &pid, int &cmd); + + /** + * @return Version of the network library + */ + static int version(); + + /** + * This function takes a @ref GameMessageIds as argument and returns a + * suitable string for it. This string can't be used to identify a message + * (as it is i18n'ed) but it can make debugging more easy. See also @ref + * KGameDebugDialog. + * @return Either a i18n'ed string (the name of the id) or QString::null if + * the msgid is unknown + **/ + static QString messageId2Text(int msgid); + + + /** + * Message Ids used inside @ref KGame. + * + * You can use your own custom message Id by adding @p IdUser to it. + **/ +// please document every new id with a short comment + enum GameMessageIds { +// game init, game load, disconnect, ... + IdSetupGame=1, // sent to a newly connected player + IdSetupGameContinue=2, // continue the setup + IdGameLoad=3, // load/save the game to the client + IdGameConnected=4, // Client successfully connected to master + IdSyncRandom=5, // new random seed set - sync games + IdDisconnect=6, // KGame object disconnects from game + IdGameSetupDone=7, // New game client is now operational + +// properties + IdPlayerProperty=20, // a player property changed + IdGameProperty=21, // a game property changed + +// player management + IdAddPlayer=30, // add a player + IdRemovePlayer=31, // the player will be removed + IdActivatePlayer=32, // Activate a player + IdInactivatePlayer=33, // Inactivate a player + IdTurn=34, // Turn to be prepared + +// to-be-categorized + IdError=100, // an error occurred + IdPlayerInput=101, // a player input occurred + IdIOAdded=102, // KGameIO got added to a player...init this IO + +// special ids for computer player + IdProcessQuery=220, // Process queries data (process only) + IdPlayerId=221, // PlayerId got changed (process only) + + IdUser=256 // a user specified message + }; +}; + +#endif diff --git a/libkdegames/kgame/kgamenetwork.cpp b/libkdegames/kgame/kgamenetwork.cpp new file mode 100644 index 00000000..9eccb868 --- /dev/null +++ b/libkdegames/kgame/kgamenetwork.cpp @@ -0,0 +1,516 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgamenetwork.h" +#include "kgamenetwork.moc" +#include "kgamemessage.h" +#include "kgameerror.h" + +#include "kmessageserver.h" +#include "kmessageclient.h" +#include "kmessageio.h" +#include <dnssd/publicservice.h> + +#include <kdebug.h> + +#include <qbuffer.h> + + +class KGameNetworkPrivate +{ +public: + KGameNetworkPrivate() + { + mMessageClient = 0; + mMessageServer = 0; + mDisconnectId = 0; + mService = 0; + } + +public: + KMessageClient* mMessageClient; + KMessageServer* mMessageServer; + Q_UINT32 mDisconnectId; // Stores gameId() over a disconnect process + DNSSD::PublicService* mService; + QString mType; + QString mName; + + int mCookie; +}; + +// ------------------- NETWORK GAME ------------------------ +KGameNetwork::KGameNetwork(int c, QObject* parent) : QObject(parent, 0) +{ + d = new KGameNetworkPrivate; + d->mCookie = (Q_INT16)c; + + // Init the game as a local game, i.e. + // create your own KMessageServer and a KMessageClient connected to it. + setMaster(); + + kdDebug(11001) << k_funcinfo << "this=" << this <<", cookie=" << cookie() << " sizeof(this)="<<sizeof(KGameNetwork) << endl; +} + +KGameNetwork::~KGameNetwork() +{ + kdDebug(11001) << k_funcinfo << "this=" << this << endl; +// Debug(); + delete d->mService; + delete d; +} + +// ----------------------------- status methods +bool KGameNetwork::isNetwork() const +{ return isOfferingConnections() || d->mMessageClient->isNetwork();} + +Q_UINT32 KGameNetwork::gameId() const +{ + //return d->mMessageClient->id() ; + // Return stored id in the case of disconnect. In any other + // case the disconnect id is 0 + if (d->mMessageClient->id()!=0 ) { + return d->mMessageClient->id() ; + } else { + return d->mDisconnectId; + } +} + +int KGameNetwork::cookie() const +{ return d->mCookie; } + +bool KGameNetwork::isMaster() const +{ return (d->mMessageServer != 0); } + +bool KGameNetwork::isAdmin() const +{ return (d->mMessageClient->isAdmin()); } + +KMessageClient* KGameNetwork::messageClient() const +{ return d->mMessageClient; } + +KMessageServer* KGameNetwork::messageServer() const +{ return d->mMessageServer; } + +// ----------------------- network init +void KGameNetwork::setMaster() +{ + if (!d->mMessageServer) { + d->mMessageServer = new KMessageServer (cookie(), this); + } else { + kdWarning(11001) << k_funcinfo << "Server already running!!" << endl; + } + if (!d->mMessageClient) { + d->mMessageClient = new KMessageClient (this); + connect (d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), + this, SLOT(receiveNetworkTransmission(const QByteArray&, Q_UINT32))); + connect (d->mMessageClient, SIGNAL(connectionBroken()), + this, SIGNAL(signalConnectionBroken())); + connect (d->mMessageClient, SIGNAL(aboutToDisconnect(Q_UINT32)), + this, SLOT(aboutToLoseConnection(Q_UINT32))); + connect (d->mMessageClient, SIGNAL(connectionBroken()), + this, SLOT(slotResetConnection())); + + connect (d->mMessageClient, SIGNAL(adminStatusChanged(bool)), + this, SLOT(slotAdminStatusChanged(bool))); + connect (d->mMessageClient, SIGNAL(eventClientConnected(Q_UINT32)), + this, SIGNAL(signalClientConnected(Q_UINT32))); + connect (d->mMessageClient, SIGNAL(eventClientDisconnected(Q_UINT32, bool)), + this, SIGNAL(signalClientDisconnected(Q_UINT32, bool))); + + // broacast and direct messages are treated equally on receive. + connect (d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList<Q_UINT32>&)), + d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32))); + + } else { + // should be no problem but still has to be tested + kdDebug(11001) << k_funcinfo << "Client already exists!" << endl; + } + d->mMessageClient->setServer(d->mMessageServer); +} + +void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name) +{ + kdDebug() << k_funcinfo << type << ":" << name << endl; + d->mType = type; + d->mName = name; + tryPublish(); +} + +void KGameNetwork::tryPublish() +{ + if (d->mType.isNull() || !isOfferingConnections()) return; + if (!d->mService) d->mService = new DNSSD::PublicService(d->mName,d->mType,port()); + else { + if (d->mType!=d->mService->type()) d->mService->setType(d->mType); + if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName); + } + if (!d->mService->isPublished()) d->mService->publishAsync(); +} + +void KGameNetwork::tryStopPublishing() +{ + if (d->mService) d->mService->stop(); +} + +bool KGameNetwork::offerConnections(Q_UINT16 port) +{ + kdDebug (11001) << k_funcinfo << "on port " << port << endl; + if (!isMaster()) { + setMaster(); + } + + // Make sure this is 0 + d->mDisconnectId = 0; + + // FIXME: This debug message can be removed when the program is working correct. + if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) { + kdDebug (11001) << k_funcinfo << "Already running as server! Changing the port now!" << endl; + } + + tryStopPublishing(); + kdDebug (11001) << k_funcinfo << "before Server->initNetwork" << endl; + if (!d->mMessageServer->initNetwork (port)) { + kdError (11001) << k_funcinfo << "Unable to bind to port " << port << "!" << endl; + // no need to delete - we just cannot listen to the port +// delete d->mMessageServer; +// d->mMessageServer = 0; +// d->mMessageClient->setServer((KMessageServer*)0); + return false; + } + kdDebug (11001) << k_funcinfo << "after Server->initNetwork" << endl; + tryPublish(); + return true; +} + +bool KGameNetwork::connectToServer (const QString& host, Q_UINT16 port) +{ + if (host.isEmpty()) { + kdError(11001) << k_funcinfo << "No hostname given" << endl; + return false; + } + + // Make sure this is 0 + d->mDisconnectId = 0; + +// if (!d->mMessageServer) { +// // FIXME: What shall we do here? Probably must stop a running game. +// kdWarning (11001) << k_funcinfo << "We are already connected to another server!" << endl; +/// } + + if (d->mMessageServer) { + // FIXME: What shall we do here? Probably must stop a running game. + kdWarning(11001) << "we are server but we are trying to connect to another server! " + << "make sure that all clients connect to that server! " + << "quitting the local server now..." << endl; + stopServerConnection(); + d->mMessageClient->setServer((KMessageIO*)0); + delete d->mMessageServer; + d->mMessageServer = 0; + } + + kdDebug(11001) << " about to set server" << endl; + d->mMessageClient->setServer(host, port); + emit signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now! + + // OK: We say that we already have connected, but this isn't so yet! + // If the connection cannot be established, it will look as being disconnected + // again ("slotConnectionLost" is called). + // Shall we differ between these? + kdDebug(11001) << "connected to " << host << ":" << port << endl; + return true; +} + +Q_UINT16 KGameNetwork::port() const +{ + if (isNetwork()) { + if (isOfferingConnections()) { + return d->mMessageServer->serverPort(); + } else { + return d->mMessageClient->peerPort(); + } + } + return 0; +} + +QString KGameNetwork::hostName() const +{ + return d->mMessageClient->peerName(); +} + +bool KGameNetwork::stopServerConnection() +{ + // We still are the Master, we just don't accept further connections! + tryStopPublishing(); + if (d->mMessageServer) { + d->mMessageServer->stopNetwork(); + return true; + } + return false; +} + +bool KGameNetwork::isOfferingConnections() const +{ return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); } + +void KGameNetwork::disconnect() +{ + // TODO MH + kdDebug(11001) << k_funcinfo << endl; + stopServerConnection(); + if (d->mMessageServer) { + QValueList <Q_UINT32> list=d->mMessageServer->clientIDs(); + QValueList<Q_UINT32>::Iterator it; + for( it = list.begin(); it != list.end(); ++it ) + { + kdDebug(11001) << "Client id=" << (*it) << endl; + KMessageIO *client=d->mMessageServer->findClient(*it); + if (!client) + { + continue; + } + kdDebug(11001) << " rtti=" << client->rtti() << endl; + if (client->rtti()==2) + { + kdDebug(11001) << "DIRECT IO " << endl; + } + else + { + d->mMessageServer->removeClient(client,false); + } + } + } + else + { + kdDebug(11001) << k_funcinfo << "before client->disconnect() id="<<gameId()<< endl; + //d->mMessageClient->setServer((KMessageIO*)0); + kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl; + d->mMessageClient->disconnect(); + + kdDebug(11001) << "++++++--------------------------------------------+++++"<<endl; + } + //setMaster(); + /* + if (d->mMessageServer) { + //delete d->mMessageServer; + //d->mMessageServer=0; + server=true; + kdDebug(11001) << " server true" << endl; + d->mMessageServer->deleteClients(); + kdDebug(11001) << " server deleteClients" << endl; + } + */ + kdDebug(11001) << k_funcinfo << "DONE" << endl; +} + +void KGameNetwork::aboutToLoseConnection(Q_UINT32 clientID) +{ + kdDebug(11001) << "Storing client id of connection "<<clientID<<endl; + d->mDisconnectId = clientID; +} + +void KGameNetwork::slotResetConnection() +{ + kdDebug(11001) << "Resseting client disconnect id"<<endl; + d->mDisconnectId = 0; +} + +void KGameNetwork::electAdmin(Q_UINT32 clientID) +{ + if (!isAdmin()) { + kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl; + return; + } + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << static_cast<Q_UINT32>( KMessageServer::REQ_ADMIN_CHANGE ); + stream << clientID; + d->mMessageClient->sendServerMessage(buffer); +} + +void KGameNetwork::setMaxClients(int max) +{ + if (!isAdmin()) { + kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl; + return; + } + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << static_cast<Q_UINT32>( KMessageServer::REQ_MAX_NUM_CLIENTS ); + stream << (Q_INT32)max; + d->mMessageClient->sendServerMessage(buffer); +} + +void KGameNetwork::lock() +{ + if (messageClient()) { + messageClient()->lock(); + } +} + +void KGameNetwork::unlock() +{ + if (messageClient()) { + messageClient()->unlock(); + } +} + +// --------------------- send messages --------------------------- + +bool KGameNetwork::sendSystemMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << data; + return sendSystemMessage(buffer,msgid,receiver,sender); +} + +bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer, IO_WriteOnly); + stream << msg; + return sendSystemMessage(buffer, msgid, receiver, sender); +} + +bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); } + +bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + if (!sender) { + sender = gameId(); + } + + Q_UINT32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId() + int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id() + + KGameMessage::createHeader(stream, sender, receiver, msgid); + stream.writeRawBytes(data.data(), data.size()); + + /* + kdDebug(11001) << "transmitGameClientMessage msgid=" << msgid << " recv=" + << receiver << " sender=" << sender << " Buffersize=" + << buffer.size() << endl; + */ + + if (!d->mMessageClient) { + // No client created, this should never happen! + // Having a local game means we have our own + // KMessageServer and we are the only client. + kdWarning (11001) << k_funcinfo << "We don't have a client! Should never happen!" << endl; + return false; + } + + if (receiverClient == 0 || receiverPlayer != 0) + { + // if receiverClient == 0 this is a broadcast message. if it is != 0 but + // receiverPlayer is also != 0 we have to send broadcast anyway, because the + // KPlayer object on all clients needs to receive the message. + d->mMessageClient->sendBroadcast(buffer); + } + else + { + d->mMessageClient->sendForward(buffer, receiverClient); + } + return true; +} + +bool KGameNetwork::sendMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); } + +bool KGameNetwork::sendMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); } + +bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } + +bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender) +{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); } + +void KGameNetwork::sendError(int error,const QByteArray& message, Q_UINT32 receiver, Q_UINT32 sender) +{ + QByteArray buffer; + QDataStream stream(buffer,IO_WriteOnly); + stream << (Q_INT32) error; + stream.writeRawBytes(message.data(), message.size()); + sendSystemMessage(stream,KGameMessage::IdError,receiver,sender); +} + + +// ----------------- receive messages from the network +void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, Q_UINT32 clientID) +{ + QDataStream stream(receiveBuffer, IO_ReadOnly); + int msgid; + Q_UINT32 sender; // the id of the KGame/KPlayer who sent the message + Q_UINT32 receiver; // the id of the KGame/KPlayer the message is for + KGameMessage::extractHeader(stream, sender, receiver, msgid); +// kdDebug(11001) << k_funcinfo << "id=" << msgid << " sender=" << sender << " recv=" << receiver << endl; + + // No broadcast : receiver==0 + // No player isPlayer(receiver) + // Different game gameId()!=receiver + if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) ) + { + // receiver=0 is broadcast or player message + kdDebug(11001) << k_funcinfo << "Message not meant for us " + << gameId() << "!=" << receiver << " rawid=" + << KGameMessage::rawGameId(receiver) << endl; + return; + } + else if (msgid==KGameMessage::IdError) + { + QString text; + Q_INT32 error; + stream >> error; + kdDebug(11001) << k_funcinfo << "Got IdError " << error << endl; + text = KGameError::errorText(error, stream); + kdDebug(11001) << "Error text: " << text.latin1() << endl; + emit signalNetworkErrorMessage((int)error,text); + } + else + { + networkTransmission(stream, msgid, receiver, sender, clientID); + } +} + +// -------------- slots for the signals of the client +void KGameNetwork::slotAdminStatusChanged(bool isAdmin) +{ + emit signalAdminStatusChanged(isAdmin); + +// TODO: I'm pretty sure there are a lot of things that should be done here... +} + +void KGameNetwork::Debug() +{ + kdDebug(11001) << "------------------- KNETWORKGAME -------------------------" << endl; + kdDebug(11001) << "gameId " << gameId() << endl; + kdDebug(11001) << "gameMaster " << isMaster() << endl; + kdDebug(11001) << "gameAdmin " << isAdmin() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgamenetwork.h b/libkdegames/kgame/kgamenetwork.h new file mode 100644 index 00000000..6ff5cf94 --- /dev/null +++ b/libkdegames/kgame/kgamenetwork.h @@ -0,0 +1,431 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMENETWORK_H_ +#define __KGAMENETWORK_H_ + +#include <qstring.h> +#include <qobject.h> +#include <kdemacros.h> +class KGameIO; +class KMessageClient; +class KMessageServer; + +class KGameNetworkPrivate; + +/** + * The KGameNetwork class is the KGame class with network + * support. All other features are the same but they are + * now network transparent. It is not used directly but + * only via a KGame object. So you do not really have + * to bother with this object. + * + * @short The main KDE game object + * @author Martin Heni <martin@heni-online.de> + * @version $Id$ + */ +class KDE_EXPORT KGameNetwork : public QObject +{ + Q_OBJECT + +public: + /** + * Create a KGameNetwork object + */ + KGameNetwork(int cookie=42,QObject* parent=0); + virtual ~KGameNetwork(); + + /** + * Gives debug output of the game status + **/ + virtual void Debug(); + + /** + * @return TRUE if this is a network game - i.e. you are either MASTER or + * connected to a remote MASTER. + **/ + bool isNetwork() const; + + /** + * Is this the game MASTER (i.e. has started theKMessageServer). A + * game has always exactly one MASTER. This is either a KGame object (i.e. a + * Client) or an own MessageServer-process. A KGame object that has the + * MASTER status is always admin. + * + * You probably don't want to use this. It is a mostly internal method which + * will probably become protected. Better use isAdmin + * + * @see isAdmin + * @return Whether this client has started the KMessageServer + **/ + bool isMaster() const; + + /** + * The admin of a game is the one who initializes newly connected clients + * using negotiateNetworkGame and is allowed to configure the game. + * E.g. only the admin is allowed to use KGame::setMaxPlayers. + * + * If one KGame object in the game is MASTER then this client is the admin + * as well. isMaster and isAdmin differ only if the KMessageServer + * is running in an own process. + * @return Whether this client (KGame object) is the admin + **/ + bool isAdmin() const; + + /** + * The unique ID of this game + * + * @return int id + **/ + Q_UINT32 gameId() const; + + /** + * Inits a network game as network MASTER. Note that if the + * KMessageServer is not yet started it will be started here (see + * setMaster). Any existing connection will be disconnected. + * + * If you already offer connections the port is changed. + * + * @param port The port on which the service is offered + * @return true if it worked + **/ + bool offerConnections (Q_UINT16 port); + + /** + * Announces game MASTER on network using DNS-SD. Clients then can discover it using + * DNSSD::ServiceBrowser (or KGameConnectWidget) instead of manually entering + * IP address. + * @param type service type (something like _kwin4._tcp). + * It should be unique for application. + * @param name game name that will be displayed by clients. If not + * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name. + * @since 3.4 + **/ + void setDiscoveryInfo(const QString& type, const QString& name=QString::null); + + /** + * Inits a network game as a network CLIENT + * + * @param host the host to which we want to connect + * @param port the port we want to connect to + * + * @return true if connected + **/ + bool connectToServer(const QString& host, Q_UINT16 port); + + /** + * @since 3.2 + * @return The port we are listening to if offerConnections was called + * or the port we are connected to if connectToServer was called. + * Otherwise 0. + **/ + Q_UINT16 port() const; + + /** + * @since 3.2 + * @return The name of the host that we are currently connected to is + * isNetwork is TRUE and we are not the MASTER, i.e. if connectToServer + * was called. Otherwise this will return "localhost". + **/ + QString hostName() const; + + /** + * Stops offering server connections - only for game MASTER + * @return true + **/ + bool stopServerConnection(); + + /** + * Changes the maximal connection number of the KMessageServer to max. + * -1 Means infinite connections are possible. Note that existing + * connections are not affected, so even if you set this to 0 in a running + * game no client is being disconnected. You can call this only if you are + * the ADMIN! + * + * @see KMessageServer::setMaxClients + * @param max The maximal number of connections possible. + **/ + void setMaxClients(int max); + + //AB: is this now internal only? Can we make it protected (maybe with + //friends)? sendSystemMessage AND sendMessage is very confusing to the + //user. + /** + * Sends a network message msg with a given msg id msgid to all clients. + * Use this to communicate with KGame (e.g. to add a player ot to configure + * the game - usually not necessary). + * + * For your own messages use sendMessage instead! This is mostly + * internal! + * + * @param buffer the message which will be send. See messages.txt for contents + * @param msgid an id for this message. See + * KGameMessage::GameMessageIds + * @param receiver the KGame / KPlayer this message is for. + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You + * probably want to leave this 0, then KGameNetwork will create the correct + * value for you. You might want to use this if you send a message from a + * specific player. + * @return true if worked + */ + // AB: TODO: doc on how "receiver" and "sender" should be created! + bool sendSystemMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * @overload + **/ + bool sendSystemMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * Sends a network message + * @param error The error code + * @param message The error message - use KGameError + * @param receiver the KGame / KPlayer this message is for. 0 For + * all + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You probably want to leave this 0, then KGameNetwork will create + * the correct value for you. You might want to use this if you send a + * message from a specific player. + **/ + void sendError(int error, const QByteArray& message, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * Are we still offer offering server connections - only for game MASTER + * @return true/false + **/ + bool isOfferingConnections() const; + + /** + * Application cookie. this idendifies the game application. It + * help to distinguish between e.g. KPoker and KWin4 + * @return the application cookie + **/ + int cookie() const; + + /** + * Send a network message msg with a given message ID msgid to all clients. + * You want to use this to send a message to the clients. + * + * Note that a message is always sent to ALL clients! This is necessary so + * that all clients always have the same data and can easily be changed from + * network to non-network without restarting the game. If you want a + * specific KGame / KPlayer to react to the message use the + * receiver and sender parameters. See KGameMessage::calsMessageId + * + * SendMessage differs from sendSystemMessage only by the msgid parameter. + * sendSystemMessage is thought as a KGame only mehtod while + * sendMessage is for public use. The msgid parameter will be + * +=KGameMessage::IdUser and in KGame::signalNetworkData msgid will + * be -= KGameMessage::IdUser again, so that one can easily distinguish + * between system and user messages. + * + * Use sendSystemMessage to comunicate with KGame (e.g. by adding a + * player) and sendMessage for your own user message. + * + * Note: a player should send messages through a KGameIO! + * + * @param buffer the message which will be send. See messages.txt for contents + * @param msgid an id for this message. See KGameMessage::GameMessageIds + * @param receiver the KGame / KPlayer this message is for. + * @param sender The KGame / KPlayer this message is from (i.e. + * you). You + * probably want to leave this 0, then KGameNetwork will create the correct + * value for you. You might want to use this if you send a message from a + * specific player. + * @return true if worked + **/ + // AB: TODO: doc on how "receiver" and "sender" should be created! + bool sendMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + /** + * This is an overloaded member function, provided for convenience. + **/ + bool sendMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0); + + + /** + * Called by ReceiveNetworkTransmission(). Will be overwritten by + * KGame and handle the incoming message. + **/ + virtual void networkTransmission(QDataStream&, int, Q_UINT32, Q_UINT32, Q_UINT32 clientID) = 0; + + + /** + * Disconnect the current connection and establish a new local one. + **/ + void disconnect(); + + + /** + * If you are the ADMIN of the game you can give the ADMIN status away to + * another client. Use this e.g. if you want to quit the game or if you want + * another client to administrate the game (note that disconnect calls + * this automatically). + * @param clientID the ID of the new ADMIN (note: this is the _client_ID + * which has nothing to do with the player IDs. See KMessageServer) + **/ + void electAdmin(Q_UINT32 clientID); + + /** + * Don't use this unless you really know what youre doing! You might + * experience some strange behaviour if you send your messages directly + * through the KMessageClient! + * + * @return a pointer to the KMessageClient used internally to send the + * messages. You should rather use one of the send functions! + **/ + KMessageClient* messageClient() const; + + /** + * Don't use this unless you really know what you are doing! You might + * experience some strange behaviour if you use the message server directly! + * + * @return a pointer to the message server if this is the MASTER KGame + * object. Note that it might be possible that no KGame object contains + * the KMessageServer at all! It might even run stand alone! + **/ + KMessageServer* messageServer() const; + + /** + * You should call this before doing thigs like, e.g. qApp->processEvents(). + * Don't forget to call unlock once you are done! + * + * @see KMessageClient::lock + **/ + virtual void lock(); + + /** + * @see KMessageClient::unlock + **/ + virtual void unlock(); + +signals: + /** + * A network error occurred + * @param error the error code + * @param text the error text + */ + void signalNetworkErrorMessage(int error, QString text); + + /** + * Our connection to the KMessageServer has broken. + * See KMessageClient::connectionBroken + **/ + void signalConnectionBroken(); + + /** + * This signal is emitted whenever the KMessageServer sends us a message that a + * new client connected. KGame uses this to call KGame::negotiateNetworkGame + * for the newly connected client if we are admin (see isAdmin) + * + * @see KMessageClient::eventClientConnected + * + * @param clientID the ID of the newly connected client + **/ + void signalClientConnected(Q_UINT32 clientID); + + /** + * This signal is emitted whenever the KMessageServer sends us a message + * that a connection to a client was detached. The second parameter can be used + * to distinguish between network errors or removing on purpose. + * + * @see KMessageClient::eventClientDisconnected + * + * @param clientID the client that has disconnected + * @param broken true if the connection was lost because of a network error, false + * if the connection was closed by the message server admin. + */ + void signalClientDisconnected(Q_UINT32 clientID, bool broken); + + /** + * This client gets or loses the admin status. + * @see KMessageClient::adminStatusChanged + * @param isAdmin True if this client gets the ADMIN status otherwise FALSE + **/ + void signalAdminStatusChanged(bool isAdmin); + +protected: + /** + * @internal + * Start a KMessageServer object and use it as the MASTER of the game. + * Note that you must not call this if there is already another master + * running! + **/ + void setMaster(); + +protected slots: + /** + * Called by KMessageClient::broadcastReceived() and will check if the + * message format is valid. If it is not, it will generate an error (see + * signalNetworkVersionError and signalNetworkErorrMessage). + * If it is valid, the pure virtual method networkTransmission() is called. + * (This one is overwritten in KGame.) + **/ + void receiveNetworkTransmission(const QByteArray& a, Q_UINT32 clientID); + + /** + * This KGame object receives or loses the admin status. + * @param isAdmin Whether we are admin or not + **/ + void slotAdminStatusChanged(bool isAdmin); + + /** + * Called when the network connection is about to terminate. Is used + * to store the network parameter like the game id + */ + void aboutToLoseConnection(Q_UINT32 id); + + /** + * Called when the network connection is terminated. Used to clean + * up the disconnect parameter + */ + void slotResetConnection(); + + +private: + void tryPublish(); + void tryStopPublishing(); + KGameNetworkPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgameprocess.cpp b/libkdegames/kgame/kgameprocess.cpp new file mode 100644 index 00000000..96efe0ce --- /dev/null +++ b/libkdegames/kgame/kgameprocess.cpp @@ -0,0 +1,158 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgameprocess.h" +#include "kplayer.h" +#include "kgame.h" +#include "kgamemessage.h" +#include "kmessageio.h" + +#include <krandomsequence.h> + +#include <qbuffer.h> +#include <qdatastream.h> +#include <qcstring.h> + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#define READ_BUFFER_SIZE 1024 + +// ----------------------- Process Child --------------------------- + +KGameProcess::KGameProcess() : QObject(0,0) +{ + mTerminate=false; + // Check whether a player is set. If not create one! + rFile.open(IO_ReadOnly|IO_Raw,stdin); + wFile.open(IO_WriteOnly|IO_Raw,stdout); + mMessageIO=new KMessageFilePipe(this,&rFile,&wFile); +// mMessageClient=new KMessageClient(this); +// mMessageClient->setServer(mMessageIO); +// connect (mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)), +// this, SLOT(receivedMessage(const QByteArray&, Q_UINT32))); + connect (mMessageIO, SIGNAL(received(const QByteArray&)), + this, SLOT(receivedMessage(const QByteArray&))); + fprintf(stderr,"KGameProcess::constructor %p %p\n",&rFile,&wFile); + + mRandom = new KRandomSequence; + mRandom->setSeed(0); +} +KGameProcess::~KGameProcess() +{ + delete mRandom; + //delete mMessageClient; + //delete mMessageServer; + delete mMessageIO; + rFile.close(); + wFile.close(); + fprintf(stderr,"KGameProcess::destructor\n"); +} + + +bool KGameProcess::exec(int argc, char *argv[]) +{ + // Get id and cookie, ... from command line + processArgs(argc,argv); + do + { + mMessageIO->exec(); + } while(!mTerminate); + return true; +} + +// You have to do this to create a message +// QByteArray buffer; +// QDataStream wstream(buffer,IO_WriteOnly); +// then stream data into the stream and call this function +void KGameProcess::sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver) +{ + fprintf(stderr,"KGameProcess::sendMessage id=%d recv=%d",msgid,receiver); + QByteArray a; + QDataStream outstream(a,IO_WriteOnly); + + QBuffer *device=(QBuffer *)stream.device(); + QByteArray data=device->buffer();; + + KGameMessage::createHeader(outstream,0,receiver,msgid); + outstream.writeRawBytes(data.data(),data.size()); + + //if (mMessageClient) mMessageClient->sendBroadcast(a); + // TODO: The fixed received 2 will cause problems. But how to address the + // proper one? +// if (mMessageClient) mMessageClient->sendForward(a,2); + if (mMessageIO) mMessageIO->send(a); +} + +void KGameProcess::sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver) +{ + sendSystemMessage(stream,msgid+KGameMessage::IdUser,receiver); +} + +void KGameProcess::processArgs(int argc, char *argv[]) +{ + int v=0; + if (argc>2) + { + v=atoi(argv[2]); + //kdDebug(11001) << "cookie (unused) " << v << endl; + } + if (argc>1) + { + v=atoi(argv[1]); + //kdDebug(11001) << "id (unused) " << v << endl; + } + fprintf(stderr,"processArgs \n"); + fflush(stderr); +} + +void KGameProcess::receivedMessage(const QByteArray& receiveBuffer) +{ + QDataStream stream(receiveBuffer, IO_ReadOnly); + int msgid; + Q_UINT32 sender; + Q_UINT32 receiver; + KGameMessage::extractHeader(stream, sender, receiver, msgid); + fprintf(stderr,"------ receiveNetworkTransmission(): id=%d sender=%d,recv=%d\n",msgid,sender,receiver); + switch(msgid) + { + case KGameMessage::IdTurn: + Q_INT8 b; + stream >> b; + emit signalTurn(stream,(bool)b); + break; + case KGameMessage::IdIOAdded: + Q_INT16 id; + stream >> id; + emit signalInit(stream,(int)id); + break; + default: + emit signalCommand(stream,msgid-KGameMessage::IdUser,receiver,sender); + break; + } +} + +#include "kgameprocess.moc" diff --git a/libkdegames/kgame/kgameprocess.h b/libkdegames/kgame/kgameprocess.h new file mode 100644 index 00000000..a8db4fcd --- /dev/null +++ b/libkdegames/kgame/kgameprocess.h @@ -0,0 +1,242 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEPROCESS_H_ +#define __KGAMEPROCESS_H_ + +#include <qstring.h> +#include <qobject.h> +#include <qfile.h> + +#include "kgameproperty.h" +#include <krandomsequence.h> +#include <kdemacros.h> +class KPlayer; +class KMessageFilePipe; + +/** + * This is the process class used on the computer player + * side to communicate with its counterpart KProcessIO class. + * Using these two classes will give fully transparent communication + * via QDataStreams. + */ +class KDE_EXPORT KGameProcess: public QObject +{ + Q_OBJECT + + public: + /** + * Creates a KGameProcess class. Done only in the computer + * player. To activate the communication you have to call + * the exec function of this class which will listen + * to the communication and emit signals to notify you of + * any incoming messages. + * Note: This function will only return after you set + * setTerminate(true) in one of the received signals. + * So you can not do any computer calculation after the exec function. + * Instead you react on the signals which are emitted after a + * message is received and perform the calculations there! + * Example: + * \code + * int main(int argc ,char * argv[]) + * { + * KGameProcess proc; + * connect(&proc,SIGNAL(signalCommand(QDataStream &,int ,int ,int )), + * this,SLOT(slotCommand(QDataStream & ,int ,int ,int ))); + * connect(&proc,SIGNAL(signalInit(QDataStream &,int)), + * this,SLOT(slotInit(QDataStream & ,int ))); + * connect(&proc,SIGNAL(signalTurn(QDataStream &,bool )), + * this,SLOT(slotTurn(QDataStream & ,bool ))); + * return proc.exec(argc,argv); + * } + * \endcode + */ + KGameProcess(); + /** + * Destruct the process + */ + ~KGameProcess(); + + /** + * Enters the event loop of the computer process. Does only + * return on setTerminate(true)! + */ + bool exec(int argc, char *argv[]); + + /** + * Should the computer process leave its exec function? + * Activated if you setTerminate(true); + * + * @return true/false + */ + bool terminate() const {return mTerminate;} + + /** + * Set this to true if the computer process should end, ie + * leave its exec function. + * + * @param b true for exit the exec function + */ + void setTerminate(bool b) {mTerminate=b;} + + /** + * Sends a message to the corresponding KGameIO + * device. Works like the sendSystemMessage but + * for user id's + * + * @param stream the QDataStream containing the message + * @param msgid the message id for the message + * @param receiver unused + */ + void sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0); + + /** + * Sends a system message to the corresonding KGameIO device. + * This will normally be either a performed move or a query + * (IdProcessQuery). The query option is a way to communicate + * with the KGameIO at the other side and e.g. retrieve some + * game relevant data from here. + * Exmaple for a query: + * \code + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * int msgid=KGameMessage::IdProcessQuery; + * out << (int)1; + * proc.sendSystemMessage(out,msgid,0); + * \endcode + * + * @param stream the QDataStream containing the message + * @param msgid the message id for the message + * @param receiver unused + */ + void sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0); + + /** + * Returns a pointer to a KRandomSequence. You can generate + * random numbers via e.g. + * \code + * random()->getLong(100); + * \endcode + * + * @return KRandomSequence pointer + */ + KRandomSequence *random() {return mRandom;} + + protected: + /** + * processes the command line argumens to set up the computer player + * Pass the argumens exactely as given by main() + */ + void processArgs(int argc, char *argv[]); + + protected slots: + /** + * A message is received via the interprocess connection. The + * appropriate signals are called. + */ + void receivedMessage(const QByteArray& receiveBuffer); + + signals: + /** + * The generic communication signal. You have to connect to this + * signal to generate a valid computer response onto arbitrary messages. + * All signals but IdIOAdded and IdTurn end up here! + * Example: + * \code + * void Computer::slotCommand(int &msgid,QDataStream &in,QDataStream &out) + * { + * Q_INT32 data,move; + * in >> data; + * // compute move ... + * move=data*2; + * out << move; + * } + * \endcode + * + * @param inputStream the incoming data stream + * @param msgid the message id of the message which got transmitted to the computer + * @param receiver the id of the receiver + * @param sender the id of the sender + */ + void signalCommand(QDataStream &inputStream,int msgid,int receiver,int sender); + + /** + * This signal is emmited if the computer player should perform a turn. + * Calculations can be made here and the move can then be send back with + * sendSystemMessage with the message id KGameMessage::IdPlayerInput. + * These must provide a move which complies to your other move syntax as + * e.g. produces by keyboard or mouse input. + * Additonal data which have been written into the stream from the + * ProcessIO's signal signalPrepareTurn can be retrieved from the + * stream here. + * Example: + * \code + * void slotTurn(QDataStream &in,bool turn) + * { + * int id; + * int recv; + * QByteArray buffer; + * QDataStream out(buffer,IO_WriteOnly); + * if (turn) + * { + * // Create a move - the format is yours to decide + * // It arrives exactly as this in the kgame inputMove function!! + * Q_INT8 x1,y1,pl; + * pl=-1; + * x1=proc.random()->getLong(8); + * y1=proc.random()->getLong(8); + * // Stream it + * out << pl << x1 << y1; + * id=KGameMessage::IdPlayerInput; + * proc.sendSystemMessage(out,id,0); + * } + * } + * \endcode + * + * @param stream The datastream which contains user data + * @param turn True or false whether the turn is activated or deactivated + * + */ + void signalTurn(QDataStream &stream,bool turn); + + /** + * This signal is emmited when the process is initialized, i.e. added + * to a KPlayer. Initial initialisation can be performed here be reacting + * to the KProcessIO signal signalIOAdded and retrieving the data here + * from the stream. + * It works just as the signalTurn() but is only send when the player is + * added to the game, i.e. it needs some initialization data + * + * @param stream The datastream which contains user data + * @param userid The userId of the player. (Careful to rely on it yet) + */ + void signalInit(QDataStream &stream,int userid); + + protected: + bool mTerminate; + KMessageFilePipe *mMessageIO; + private: + QFile rFile; + QFile wFile; + KRandomSequence* mRandom; +}; +#endif diff --git a/libkdegames/kgame/kgameproperty.cpp b/libkdegames/kgame/kgameproperty.cpp new file mode 100644 index 00000000..68a33bcb --- /dev/null +++ b/libkdegames/kgame/kgameproperty.cpp @@ -0,0 +1,211 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" +#include "kgamemessage.h" +#include "kplayer.h" +#include "kgame.h" + +#define KPLAYERHANDLER_LOAD_COOKIE 6239 + +KGamePropertyBase::KGamePropertyBase(int id, KGame* parent) +{ + init(); + registerData(id, parent); +} + +KGamePropertyBase::KGamePropertyBase(int id, KPlayer* parent) +{ + init(); + registerData(id, parent); +} + +KGamePropertyBase::KGamePropertyBase(int id, KGamePropertyHandler* owner) +{ + init(); + registerData(id, owner); +} + +KGamePropertyBase::KGamePropertyBase() +{ + init(); +} + +KGamePropertyBase::~KGamePropertyBase() +{ + unregisterData(); +} + +void KGamePropertyBase::init() +{ + mOwner = 0; + setDirty(false); + + // this is very useful and used by e.g. KGameDialog so + // it is activated by default. Big games may profit by deactivating it to get + // a better performance. + setEmittingSignal(true); + + setOptimized(false); + + //setReadOnly(false); + mFlags.bits.locked = false ; // setLocked(false); is NOT possible as it checks whether isLocked() allows to change the status + + // local is default + setPolicy(PolicyLocal); +} + +int KGamePropertyBase::registerData(int id, KGame* owner, QString name) +{ return registerData(id, owner->dataHandler(), name); } + +int KGamePropertyBase::registerData(int id, KPlayer* owner, QString name) +{ return registerData(id, owner->dataHandler(), name); } + +int KGamePropertyBase::registerData( KGamePropertyHandler* owner,PropertyPolicy p, QString name) +{ return registerData(-1, owner,p, name); } + +int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner, QString name) +{ return registerData(id, owner,PolicyUndefined, name); } + +int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name) +{ +// we don't support changing the id + if (!owner) { + kdWarning(11001) << k_funcinfo << "Resetting owner=0. Sure you want to do this?" << endl; + mOwner=0; + return -1; + } + if (!mOwner) { + if (id==-1) { + id=owner->uniquePropertyId(); + } + mId = id; + mOwner = owner; + mOwner->addProperty(this, name); + if (p!=PolicyUndefined) { + setPolicy(p); + } else { + setPolicy(mOwner->policy()); + } + } + return mId; +} + +void KGamePropertyBase::unregisterData() +{ + if (!mOwner) { + return; + } + mOwner->removeProperty(this); + mOwner = 0; +} + +bool KGamePropertyBase::sendProperty() +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyHeader(s, id()); + save(s); + if (mOwner) { + return mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << "Cannot send because there is no receiver defined" << endl; + return false; + } +} + +bool KGamePropertyBase::sendProperty(const QByteArray& data) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyHeader(s, id()); + s.writeRawBytes(data.data(), data.size()); + if (mOwner) { + return mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl; + return false; + } +} + +bool KGamePropertyBase::lock() +{ + if (isLocked()) { + return false; + } + setLock(true); + return true; +} + +bool KGamePropertyBase::unlock(bool force) +{ + if (isLocked() && !force) { + return false; + } + setLock(false); + return true; +} + +void KGamePropertyBase::setLock(bool l) +{ + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s, IdCommand, id(), CmdLock); + + s << (Q_INT8)l; + if (mOwner) { + mOwner->sendProperty(s); + } else { + kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl; + return ; + } +} + +void KGamePropertyBase::emitSignal() +{ + //kdDebug(11001) << k_funcinfo << ": mOwnerP="<< mOwner << " id=" << id() << endl; + if (mOwner ) { + mOwner->emitSignal(this); + } else { + kdError(11001) << k_funcinfo << ":id="<<id()<<" Cannot emitSignal because there is no handler set" << endl; + } +} + +void KGamePropertyBase::command(QDataStream& s, int cmd, bool isSender) +{ + switch (cmd) { + case CmdLock: + { + if (!isSender) { + Q_INT8 locked; + s >> locked; + mFlags.bits.locked = (bool)locked ; + break; + } + } + default: // probably in derived classes + break; + } +} + diff --git a/libkdegames/kgame/kgameproperty.h b/libkdegames/kgame/kgameproperty.h new file mode 100644 index 00000000..c6915606 --- /dev/null +++ b/libkdegames/kgame/kgameproperty.h @@ -0,0 +1,848 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 __KGAMEPROPERTY_H_ +#define __KGAMEPROPERTY_H_ + +#include <qdatastream.h> + +#include <kdebug.h> +#include <typeinfo> +#include <kdemacros.h> +class KGame; +class KPlayer; +class KGamePropertyHandler; +using namespace std; + +/** + * @short Base class of KGameProperty + * + * The KGamePropertyBase class is the base class of KGameProperty. See + * KGameProperty for further information. + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KDE_EXPORT KGamePropertyBase +{ +public: + enum PropertyDataIds { // these belong to KPlayer/KGame! + //KPlayer + IdGroup=1, + IdUserId=2, + IdAsyncInput=3, + IdTurn=4, + IdName=5, + + //KGame + IdGameStatus=6, + IdMaxPlayer=7, + IdMinPlayer=8, + + // Input Grabbing + IdGrabInput=16, + IdReleaseInput=17, + + IdCommand, // Reserved for internal use + IdUser=256, + + IdAutomatic=0x7000 // Id's from here on are automatically given (16bit) + }; + + /** + * Commands for advanced properties (Q_INT8) + **/ + enum PropertyCommandIds + { + // General + CmdLock=1, + + // Array + CmdAt=51, + CmdResize=52, + CmdFill=53, + CmdSort=54, + // List (could be the same id's actually) + CmdInsert=61, + CmdAppend=62, + CmdRemove=63, + CmdClear=64 + }; + + /** + * The policy of the property. This can be PolicyClean (setValue uses + * send), PolicyDirty (setValue uses changeValue) or + * PolicyLocal (setValue uses setLocal). + * + * A "clean" policy means that the property is always the same on every + * client. This is achieved by calling send which actually changes + * the value only when the message from the MessageServer is received. + * + * A "dirty" policy means that as soon as setValue is called the + * property is changed immediately. And additionally sent over network. + * This can sometimes lead to bugs as the other clients do not + * immediately have the same value. For more information see + * changeValue. + * + * PolicyLocal means that a KGameProperty behaves like ever + * "normal" variable. Whenever setValue is called (e.g. using "=") + * the value of the property is changes immediately without sending it + * over network. You might want to use this if you are sure that all + * clients set the property at the same time. + **/ + enum PropertyPolicy + { + PolicyUndefined = 0, + PolicyClean = 1, + PolicyDirty = 2, + PolicyLocal = 3 + }; + + + /** + * Constructs a KGamePropertyBase object and calls registerData. + * @param id The id of this property. MUST be UNIQUE! Used to send and + * receive changes in the property of the playere automatically via + * network. + * @param owner The owner of the object. Must be a KGamePropertyHandler which manages + * the changes made to this object, i.e. which will send the new data + **/ + KGamePropertyBase(int id, KGamePropertyHandler* owner); + + KGamePropertyBase(int id, KGame* parent); + KGamePropertyBase(int id, KPlayer* parent); + + /** + * Creates a KGamePropertyBase object without an owner. Remember to call + * registerData! + **/ + KGamePropertyBase(); + + virtual ~KGamePropertyBase(); + + /** + * Changes the consistency policy of a property. The + * PropertyPolicy is one of PolicyClean (defaulz), PolicyDirty or PolicyLocal. + * + * It is up to you to decide how you want to work. + **/ + void setPolicy(PropertyPolicy p) { mFlags.bits.policy = p; } + + /** + * @return The default policy of the property + **/ + PropertyPolicy policy() const { return (PropertyPolicy)mFlags.bits.policy; } + + /** + * Sets this property to emit a signal on value changed. + * As the proerties do not inehrit QObject for optimisation + * this signal is emited via the KPlayer or KGame object + **/ + void setEmittingSignal(bool p) { mFlags.bits.emitsignal=p; } + + /** + * See also setEmittingSignal + * @return Whether this property emits a signal on value change + **/ + bool isEmittingSignal() const { return mFlags.bits.emitsignal; } + + /** + * Sets this property to try to optimize signal and network handling + * by not sending it out when the property value is not changed. + **/ + void setOptimized(bool p) { mFlags.bits.optimize = p ; } + + /** + * See also setOptimize + * @return Whether the property optimizes access (signals,network traffic) + **/ + bool isOptimized() const { return mFlags.bits.optimize; } + + /** + * @return Whether this property is "dirty". See also setDirty + **/ + bool isDirty() const { return mFlags.bits.dirty; } + + /** + * A locked property can only be changed by the player who has set the + * lock. See also setLocked + * @return Whether this property is currently locked. + **/ + bool isLocked() const { return mFlags.bits.locked; } + + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * + * @return returns false if the property can not be locked, i.e. it is already locked + * + **/ + bool lock(); + + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * + * @return returns false if the property can not be locked, i.e. it is already locked + * + **/ + bool unlock(bool force=false); + + /** + * This will read the value of this property from the stream. You MUST + * overwrite this method in order to use this class + * @param s The stream to read from + **/ + virtual void load(QDataStream& s) = 0; + + /** + * Write the value into a stream. MUST be overwritten + **/ + virtual void save(QDataStream& s) = 0; + + /** + * send a command to advanced properties like arrays + * @param stream The stream containing the data of the comand + * @param msgid The ID of the command - see PropertyCommandIds + * @param isSender whether this client is also the sender of the command + **/ + virtual void command(QDataStream &stream, int msgid, bool isSender=false); + + /** + * @return The id of this property + **/ + int id() const { return mId; } + + /** + * @return a type_info of the data this property contains. This is used + * e.g. by KGameDebugDialog + **/ + virtual const type_info* typeinfo() { return &typeid(this); } + + /** + * You have to register a KGamePropertyBase before you can use it. + * + * You MUST call this before you can use KGamePropertyBase! + * + * @param id the id of this KGamePropertyBase object. The id MUST be + * unique, i.e. you cannot have two properties with the same id for one + * player, although (currently) nothing prevents you from doing so. But + * you will get strange results! + * + * @param owner The owner of this data. This will send the data + * using KPropertyHandler::sendProperty whenever you call send + * + * @param p If not 0 you can set the policy of the property here + * + * @param name if not 0 you can assign a name to this property + * + **/ + int registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KGamePropertyHandler* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KGame* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + **/ + int registerData(int id, KPlayer* owner, QString name=0); + + /** + * This is an overloaded member function, provided for convenience. + * It differs from the above function only in what argument(s) it accepts. + * In particular you can use this function to create properties which + * will have an automatic id assigned. The new id is returned. + **/ + int registerData(KGamePropertyHandler* owner,PropertyPolicy p=PolicyUndefined, QString name=0); + + void unregisterData(); + + +protected: + /** + * A locked property can only be changed by the player who has set the + * lock. + * + * You can only call this if isLocked is false. A message is sent + * over network so that the property is locked for all players except + * you. + * Usually you use lock and unlock to access this property + * + **/ + void setLock(bool l); + + /** + * Sets the "dirty" flag of the property. If a property is "dirty" i.e. + * KGameProperty::setLocal has been called there is no guarantee + * that all clients share the same value. You have to ensure this + * yourself e.g. by calling KGameProperty::setLocal on every + * client. You can also ignore the dirty flag and continue working withe + * the property depending on your situation. + **/ + void setDirty(bool d) { mFlags.bits.dirty = d ; } + + /** + * Forward the data to the owner of this property which then sends it + * over network. save is used to store the data into a stream so + * you have to make sure that function is working properly if you + * implement your own property! + * + * Note: this sends the <em>current</em> property! + * + * Might be obsolete - KGamePropertyArray still uses it. Is this a bug + * or correct? + **/ + bool sendProperty(); + + /** + * Forward the data to the owner of this property which then sends it + * over network. save is used to store the data into a stream so + * you have to make sure that function is working properly if you + * implement your own property! + * + * This function is used by send to send the data over network. + * This does <em>not</em> send the current value but the explicitly + * given value. + * + * @return TRUE if the message could be sent successfully, otherwise + * FALSE + **/ + bool sendProperty(const QByteArray& b); + + /** + * Causes the parent object to emit a signal on value change + **/ + void emitSignal(); + +protected: + KGamePropertyHandler* mOwner; + + // Having this as a union of the bitfield and the char + // allows us to stream this quantity easily (if we need to) + // At the moment it is not yet transmitted + union Flags { + char flag; + struct { + // unsigned char dosave : 1; // do save this property + // unsigned char delaytransmit : 1; // do not send immediately on + // change but a KPlayer:QTimer + // sends it later on - fast + // changing variables + unsigned char emitsignal : 1; // KPlayer notifies on variable change (true) + //unsigned char readonly : 1; // whether the property can be changed (false) + unsigned char optimize : 1; // whether the property tries to optimize send/emit (false) + unsigned char dirty: 1; // whether the property dirty (setLocal() was used) + unsigned char policy : 2; // whether the property is always consistent (see PropertyPolicy) + unsigned char locked: 1; // whether the property is locked (true) + } bits; + } mFlags; + +private: + friend class KGamePropertyHandler; + void init(); + +private: + int mId; + +}; + +/** + * @short A class for network transparent games + * + * Note: The entire API documentation is obsolete! + * + * The class KGameProperty can store any form of data and will transmit it via + * network whenver you call send. This makes network transparent games + * very easy. You first have to register the data to a KGamePropertyHandler + * using KGamePropertyBase::registerData (which is called by the + * constructor). For the KGamePropertyHandler you can use + * KGame::dataHandler or KPlayer::dataHandler but you can also create your + * own data handler. + * + * There are several concepts you can follow when writing network games. These + * concepts differ completely from the way how data is transferred so you should + * decide which one to use. You can also mix these concepts for a single + * property but we do not recommend this. The concepts: + * <ul> + * <li> Always Consistent (clean) + * <li> Not Always Consistent (dirty) + * <li> A Mixture (very dirty) + * </ul> + * I repeat: we do <em>not</em> recommend the third option ("a mixture"). Unless + * you have a good reason for this you will probably introduce some hard to find + * (and to fix) bugs. + * + * @section Always consistent (clean): + * + * This "policy" is default. Whenever you create a KGameProperty it is always + * consistent. This means that consistency is the most important thing for the + * property. This is achieved by using send to change the value of the + * property. send needs a running KMessageServer and therefore + * <em>MUST</em> be plugged into a KGamePropertyHandler using either + * registerData or the constructor. The parent of the dataHandler must be able + * to send messages (see above: the message server must be running). If you use + * send to change the value of a property you won't see the effect + * immediately: The new value is first transferred to the message server which + * queues the message. As soon as <em>all</em> messages in the message server + * which are before the changed property have been transferred the message + * server delivers the new value of the KGameProperty to all clients. A + * QTimer::singleShot is used to queue the messages inside the + * KMessageServer. + * + * This means that if you do the following: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.initData(0); + * myProperty = 10; + * int value = myProperty.value(); + * \endcode + * then "value" will be "0". initData is used to initialize the property + * (e.g. when the KMessageServer is not yet running or can not yet be + * reached). This is because "myProperty = 10" or "myProperty.send(10)" send a + * message to the KMessageServer which uses QTimer::singleShot to + * queue the message. The game first has to go back into the event loop where + * the message is received. The KGamePropertyHandler receives the new value + * sets the property. So if you need the new value you need to store it in a + * different variable (see setLocal which creates one for you until the + * message is received). The KGamePropertyHandler emits a signal (unless + * you called setEmitSignal with false) when the new value is received: + * KGamePropertyHandler::signalPropertyChanged. You can use this to react + * to a changed property. + * + * This may look quite confusing but it has a <em>big</em> advantage: all + * KGameProperty objects are ensured to have the same value on all clients in + * the game at every time. This way you will save you a lot of trouble as + * debugging can be very difficult if the value of a property changes + * immediately on client A but only after one or two additianal messages + * (function calls, status changes, ...) on client B. + * + * The only disadvantage of this (clean) concept is that you cannot use a + * changed variable immediately but have to wait for the KMessageServer to + * change it. You probably want to use + * KGamePropertyHandler::signalPropertyChanged for this. + * + * @section Not Always Consistent (dirty): + * + * There are a lot of people who don't want to use the (sometimes quite complex) + * "clean" way. You can use setAlwaysConsistent to change the default + * behaviour of the KGameProperty. If a property is not always consistent + * it will use changeValue to send the property. changeValue also uses + * send to send the new value over network but it also uses + * setLocal to create a local copy of the property. This copy is created + * dynamically and is deleted again as soon as the next message from the network + * is received. To use the example above again: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.setAlwaysConsistent(false); + * myProperty.initData(0); + * myProperty = 10; + * int value = myProperty.value(); + * \endcode + * Now this example will "work" so value now is 10. Additionally the + * KMessageServer receives a message from the local client (just as explained + * above in "Always Consistent"). As soon as the message returns to the local + * client again the local value is deleted, as the "network value" has the same + * value as the local one. So you won't lose the ability to use the always + * consistent "clean" value of the property if you use the "dirty" way. Just use + * networkValue to access the value which is consistent among all clients. + * + * The advantage of this concept is clear: you can use a KGameProperty as + * every other variable as the changes value takes immediate effect. + * Additionally you can be sure that the value is transferred to all clients. + * You will usually not experience serious bugs just because you use the "dirty" + * way. Several events have to happen at once to get these "strange errors" + * which result in inconsistent properties (like "game running" on client A but + * "game ended/paused" on client B). But note that there is a very good reason + * for the existence of these different concepts of KGameProperty. I have + * myself experienced such a "strange error" and it took me several days to find + * the reason until I could fix it. So I personally recommend the "clean" way. + * On the other hand if you want to port a non-network game to a network game + * you will probably start with "dirty" properties as it is you will not have to + * change that much code... + * + * @section A Mixture (very dirty): + * + * You can also mix the concepts above. Note that we really don't recommend + * this. With a mixture I mean something like this: + * \code + * KGamePropertyInt myProperty(id, dataHandler()); + * myProperty.setAlwaysConsistent(false); + * myProperty.initData(0); + * myProperty = 10; + * myProperty.setAlwaysConsistent(true); + * myProperty = 20; + * \endcode + * (totally senseless example, btw) I.e. I am speaking of mixing both concepts + * for a single property. Things like + * \code + * KGamePropertyInt myProperty1(id1, dataHandler()); + * KGamePropertyInt myProperty2(id2, dataHandler()); + * myProperty1.initData(0); + * myProperty2.initData(0); + * myProperty1.setAlwaysConsistent(false); + * myProperty2.setAlwaysConsistent(true); + * myProperty1 = 10; + * myProperty2 = 20; + * \endcode + * are ok. But mixing the concepts for a single property will make it nearly + * impossible to you to debug your game. + * + * So the right thing to do(tm) is to decide in the constructor whether you want + * a "clean" or "dirty" property. + * + * Even if you have decided for one of the concepts you still can manually + * follow another concept than the "policy" of your property. So if you use an + * always consistent KGameProperty you still can manually call + * changeValue as if it was not always consistent. Note that although this is + * also kind of a "mixture" as described above this is very useful sometimes. In + * contrast to the "mixture" above you don't have the problem that you don't + * exactly know which concept you are currently following because you used the + * function of the other concept only once. + * + * @section Custom classes: + * + * If you want to use a custum class with KGameProperty you have to implement the + * operators << and >> for QDataStream: + * \code + * class Card + * { + * public: + * int type; + * int suite; + * }; + * QDataStream& operator<<(QDataStream& stream, Card& card) + * { + * Q_INT16 type = card.type; + * Q_INT16 suite = card.suite; + * s << type; + * s << suite; + * return s; + * } + * QDataStream& operator>>(QDataStream& stream, Card& card) + * { + * Q_INT16 type; + * Q_INT16 suite; + * s >> type; + * s >> suite; + * card.type = (int)type; + * card.suite = (int)suite; + * return s; + * } + * + * class Player : KPlayer + * { + * [...] + * KGameProperty<Card> mCards; + * }; + * \endcode + * + * Note: unlike most QT classes KGameProperty objects are *not* deleted + * automatically! So if you create an object using e.g. KGameProperty<int>* data = + * new KGameProperty(id, dataHandler()) you have to put a delete data into your + * destructor! + * + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +template<class type> +class KGameProperty : public KGamePropertyBase +{ +public: + /** + * Constructs a KGameProperty object. A KGameProperty object will transmit + * any changes to the KMessageServer and then to all clients in the + * game (including the one that has sent the new value) + * @param id The id of this property. <em>MUST be UNIQUE</em>! Used to send and + * receive changes in the property of the playere automatically via + * network. + * @param owner The parent of the object. Must be a KGame which manages + * the changes made to this object, i.e. which will send the new data. + * Note that in contrast to most KDE/QT classes KGameProperty objects + * are <em>not</em> deleted automatically! + **/ +// TODO: ID: Very ugly - better use something like parent()->propertyId() or so which assigns a free id automatically. + KGameProperty(int id, KGamePropertyHandler* owner) : KGamePropertyBase(id, owner) { init(); } + + /** + * This constructor does nothing. You have to call + * KGamePropertyBase::registerData + * yourself before using the KGameProperty object. + **/ + KGameProperty() : KGamePropertyBase() { init(); } + + virtual ~KGameProperty() {} + + /** + * Set the value depending on the current policy (see + * setConsistent). By default KGameProperty just uses send to set + * the value of a property. This behaviour can be changed by using + * setConsistent. + * @param v The new value of the property + **/ + void setValue(type v) + { + switch (policy()) { + case PolicyClean: + send(v); + break; + case PolicyDirty: + changeValue(v); + break; + case PolicyLocal: + setLocal(v); + break; + default: // NEVER! + kdError(11001) << "Undefined Policy in property " << id() << endl; + return; + } + } + + + /** + * This function sends a new value over network. + * + * Note that the value DOES NOT change when you call this function. This + * function saves the value into a QDataStream and calls + * sendProperty where it gets forwarded to the owner and finally the + * value is sent over network. The KMessageServer now sends the + * value to ALL clients - even the one who called this function. As soon + * as the value from the message server is received load is called + * and _then_ the value of the KGameProperty has been set. + * + * This ensures that a KGameProperty has _always_ the same value on + * _every_ client in the network. Note that this means you can NOT do + * something like + * \code + * myProperty.send(1); + * doSomething(myProperty); + * \endcode + * as myProperty has not yet been set when doSomething is being called. + * + * You are informed about a value change by a singal from the parent of + * the property which can be deactivated by setEmittingSignal because of + * performance (you probably don't have to deactivate it - except you + * want to write a real-time game like Command&Conquer with a lot of + * acitvity). See emitSignal + * + * Note that if there is no KMessageServer accessible - before + * the property has been registered to the KGamePropertyHandler (as + * it is the case e.g. before a KPlayer has been plugged into the + * KGame object) the property is *not* sent but set *locally* (see + * setLocal)! + * + * @param v The new value of the property + * @return whether the property could be sent successfully + * @see setValue setLocal changeValue value + **/ + bool send(type v) + { + if (isOptimized() && mData == v) { + return true; + } + if (isLocked()) { + return false; + } + QByteArray b; + QDataStream stream(b, IO_WriteOnly); + stream << v; + if (!sendProperty(b)) { + setLocal(v); + return false; + } + return true; + } + + /** + * This function sets the value of the property directly, i.e. it + * doesn't send it to the network. + * + * Int contrast to @see you change _only_ the local value when using + * this function. You do _not_ change the value of any other client. You + * probably don't want to use this if you are using a dedicated server + * (which is the only "client" which is allowed to change a value) but + * rather want to use send(). + * + * But if you use your clients as servers (i.e. all clients receive a + * players turn and then calculate the reaction of the game theirselves) + * then you probably want to use setLocal as you can do things like + * \code + * myProperty.setLocal(1); + * doSomething(myProperty); + * \endcode + * on every client. + * + * If you want to set the value locally AND send it over network you + * want to call changeValue! + * + * You can also use setPolicy to set the default policy to + * PolicyLocal. + * + * @see setValue send changeValue value + **/ + bool setLocal(type v) + { + if (isOptimized() && mData == v) { + return false; + } + if (isLocked()) { + return false; + } + mData = v; + setDirty(true); + if (isEmittingSignal()) { + emitSignal(); + } + return true; + } + + /** + * This function does both, change the local value and change the + * network value. The value is sent over network first, then changed + * locally. + * + * This function is a convenience function and just calls send + * followed by setLocal + * + * Note that emitSignal is also called twice: once after + * setLocal and once when the value from send is received + * + * @see send setLocal setValue value + **/ + void changeValue(type v) + { + send(v); + setLocal(v); + } + + /** + * Saves the object to a stream. + * @param stream The stream to save to + **/ + virtual void save(QDataStream &stream) + { + stream << mData; + } + + /** + * @return The local value (see setLocal) if it is existing, + * otherwise the network value which is always consistent on every + * client. + **/ + const type& value() const + { + return mData; + } + + /** + * Reads from a stream and assigns the read value to this object. + * + * This function is called automatically when a new value is received + * over network (i.e. it has been sent using send on this or any + * other client) or when a game is loaded (and maybe on some other + * events). + * + * Also calls emitSignal if isEmittingSignal is TRUE. + * @param s The stream to read from + **/ + virtual void load(QDataStream& s) + { + s >> mData; + setDirty(false); + if (isEmittingSignal()) { + emitSignal(); + } + } + + /** + * This calls setValue to change the value of the property. Note + * that depending on the policy (see setAlwaysConsistent) the + * returned value might be different from the assigned value!! + * + * So if you use setPolicy(PolicyClean): + * \code + * int a, b = 10; + * myProperty = b; + * a = myProperty.value(); + * \endcode + * Here a and b would differ! + * The value is actually set as soon as it is received from the + * KMessageServer which forwards it to ALL clients in the network. + * + * If you use a clean policy (see setPolicy) then + * the returned value is the assigned value + **/ + const type& operator=(const type& t) + { + setValue(t); + return value(); + } + + /** + * This copies the data of property to the KGameProperty object. + * + * Equivalent to setValue(property.value()); + **/ + const type& operator=(const KGameProperty& property) + { + setValue(property.value()); + return value(); + } + + /** + * Yeah, you can do it! + * \code + * int a = myGamePropertyInt; + * \endcode + * If you don't see it: you don't have to use integerData.value() + **/ + operator type() const { return value(); } + + virtual const type_info* typeinfo() { return &typeid(type); } + +private: + void init() { } + +private: + type mData; +}; + + +typedef KGameProperty<int> KGamePropertyInt; +typedef KGameProperty<unsigned int> KGamePropertyUInt; +typedef KGameProperty<QString> KGamePropertyQString; +typedef KGameProperty<Q_INT8> KGamePropertyBool; + +#endif diff --git a/libkdegames/kgame/kgamepropertyarray.h b/libkdegames/kgame/kgamepropertyarray.h new file mode 100644 index 00000000..f91bd75c --- /dev/null +++ b/libkdegames/kgame/kgamepropertyarray.h @@ -0,0 +1,309 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KGAMEPROPERTYARRAY_H_ +#define __KGAMEPROPERTYARRAY_H_ + +#include <qdatastream.h> +#include <kdebug.h> + +#include "kgamemessage.h" +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" + + +template<class type> +class KGamePropertyArray : public QMemArray<type>, public KGamePropertyBase +{ +public: + KGamePropertyArray() :QMemArray<type>(), KGamePropertyBase() + { + //kdDebug(11001) << "KGamePropertyArray init" << endl; + } + + KGamePropertyArray( int size ) + { + resize(size); + } + + KGamePropertyArray( const KGamePropertyArray<type> &a ) : QMemArray<type>(a) + { + } + + bool resize( uint size ) + { + if (size!=QMemArray<type>::size()) + { + bool a=true; + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdResize); + s << size ; + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); +// a=QMemArray<type>::resize(size);// FIXME: return value! + } + return a; + } + else return true; + } + + void setAt(uint i,type data) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAt); + s << i ; + s << data; + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); + } + //kdDebug(11001) << "KGamePropertyArray setAt send COMMAND for id="<<id() << " type=" << 1 << " at(" << i<<")="<<data << endl; + } + + type at( uint i ) const + { + return QMemArray<type>::at(i); + } + + type operator[]( int i ) const + { + return QMemArray<type>::at(i); + } + + KGamePropertyArray<type> &operator=(const KGamePropertyArray<type> &a) + { + return assign(a); + } + + bool truncate( uint pos ) + { + return resize(pos); + } + + bool fill( const type &data, int size = -1 ) + { + bool r=true; + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdFill); + s << data; + s << size ; + if (policy()==PolicyClean || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); +// r=QMemArray<type>::fill(data,size);//FIXME: return value! + } + return r; + } + + KGamePropertyArray<type>& assign( const KGamePropertyArray<type>& a ) + { +// note: send() has been replaced by sendProperty so it might be broken now! + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendProperty(); + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + QMemArray<type>::assign(a); + } + return *this; + } + KGamePropertyArray<type>& assign( const type *a, uint n ) + { + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendProperty(); + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + QMemArray<type>::assign(a,n); + } + return *this; + } + KGamePropertyArray<type>& duplicate( const KGamePropertyArray<type>& a ) + { + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendProperty(); + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + QMemArray<type>::duplicate(a); + } + return *this; + } + KGamePropertyArray<type>& duplicate( const type *a, uint n ) + { + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendProperty(); + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + QMemArray<type>::duplicate(a,n); + } + return *this; + } + KGamePropertyArray<type>& setRawData( const type *a, uint n ) + { + if (policy()==PolicyClean || policy()==PolicyDirty) + { + sendProperty(); + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + QMemArray<type>::setRawData(a,n); + } + return *this; + } + void sort() + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdSort); + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy()==PolicyLocal || policy()==PolicyDirty) + { + extractProperty(b); + } + } + + void load(QDataStream& s) + { + //kdDebug(11001) << "KGamePropertyArray load " << id() << endl; + type data; + for (unsigned int i=0; i<QMemArray<type>::size(); i++) + { + s >> data; + QMemArray<type>::at(i)=data; + } + if (isEmittingSignal()) + { + emitSignal(); + } + } + void save(QDataStream &s) + { + //kdDebug(11001) << "KGamePropertyArray save "<<id() << endl; + for (unsigned int i=0; i<QMemArray<type>::size(); i++) + { + s << at(i); + } + } + + void command(QDataStream &s,int cmd,bool) + { + KGamePropertyBase::command(s, cmd); + //kdDebug(11001) << "Array id="<<id()<<" got command ("<<cmd<<") !!!" <<endl; + switch(cmd) + { + case CmdAt: + { + uint i; + type data; + s >> i >> data; + QMemArray<type>::at(i)=data; + //kdDebug(11001) << "CmdAt:id="<<id()<<" i="<<i<<" data="<<data <<endl; + if (isEmittingSignal()) + { + emitSignal(); + } + break; + } + case CmdResize: + { + uint size; + s >> size; + //kdDebug(11001) << "CmdResize:id="<<id()<<" oldsize="<<QMemArray<type>::size()<<" newsize="<<size <<endl; + if (QMemArray<type>::size() != size) + { + QMemArray<type>::resize(size); + } + break; + } + case CmdFill: + { + int size; + type data; + s >> data >> size; + //kdDebug(11001) << "CmdFill:id="<<id()<<"size="<<size <<endl; + QMemArray<type>::fill(data,size); + if (isEmittingSignal()) + { + emitSignal(); + } + break; + } + case CmdSort: + { + //kdDebug(11001) << "CmdSort:id="<<id()<<endl; + QMemArray<type>::sort(); + break; + } + default: + kdError(11001) << "Error in KPropertyArray::command: Unknown command " << cmd << endl; + break; + } + } +protected: + void extractProperty(const QByteArray& b) + { + QDataStream s(b, IO_ReadOnly); + int cmd; + int propId; + KGameMessage::extractPropertyHeader(s, propId); + KGameMessage::extractPropertyCommand(s, propId, cmd); + command(s, cmd, true); + } + +}; + +#endif diff --git a/libkdegames/kgame/kgamepropertyhandler.cpp b/libkdegames/kgame/kgamepropertyhandler.cpp new file mode 100644 index 00000000..75b3e7e2 --- /dev/null +++ b/libkdegames/kgame/kgamepropertyhandler.cpp @@ -0,0 +1,407 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgamepropertyhandler.h" +#include "kgameproperty.h" +#include "kgamemessage.h" + +#include <qmap.h> +#include <qptrqueue.h> + +#include <klocale.h> +#include <typeinfo> + +#define KPLAYERHANDLER_LOAD_COOKIE 6239 + +//---------------------- KGamePropertyHandler ----------------------------------- +class KGamePropertyHandlerPrivate +{ +public: + KGamePropertyHandlerPrivate() + { + } + + QMap<int, QString> mNameMap; + QIntDict<KGamePropertyBase> mIdDict; + int mUniqueId; + int mId; + KGamePropertyBase::PropertyPolicy mDefaultPolicy; + bool mDefaultUserspace; + int mIndirectEmit; + QPtrQueue<KGamePropertyBase> mSignalQueue; +}; + +KGamePropertyHandler::KGamePropertyHandler(int id, const QObject* receiver, const char * sendf, const char *emitf, QObject* parent) : QObject(parent) +{ + init(); + registerHandler(id,receiver,sendf,emitf); +} + +KGamePropertyHandler::KGamePropertyHandler(QObject* parent) : QObject(parent) +{ + init(); +} + +KGamePropertyHandler::~KGamePropertyHandler() +{ + clear(); + delete d; +} + +void KGamePropertyHandler::init() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this << endl; + d = new KGamePropertyHandlerPrivate; // for future use - is BC important to us? + d->mId = 0; + d->mUniqueId=KGamePropertyBase::IdAutomatic; + d->mDefaultPolicy=KGamePropertyBase::PolicyLocal; + d->mDefaultUserspace=true; + d->mIndirectEmit=0; +} + + +int KGamePropertyHandler::id() const +{ + return d->mId; +} + +void KGamePropertyHandler::setId(int id) +{ + d->mId = id; +} + +void KGamePropertyHandler::registerHandler(int id,const QObject * receiver, const char * sendf, const char *emitf) +{ + setId(id); + if (receiver && sendf) { + kdDebug(11001) << "Connecting SLOT " << sendf << endl; + connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool*)), receiver, sendf); + } + if (receiver && emitf) { + kdDebug(11001) << "Connecting SLOT " << emitf << endl; + connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf); + } +} + +bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender) +{ +// kdDebug(11001) << k_funcinfo << ": id=" << id << " mId=" << d->mId << endl; + if (id != d->mId) { + return false; // Is the message meant for us? + } + KGamePropertyBase* p; + int propertyId; + KGameMessage::extractPropertyHeader(stream, propertyId); +// kdDebug(11001) << k_funcinfo << ": Got property " << propertyId << endl; + if (propertyId==KGamePropertyBase::IdCommand) { + int cmd; + KGameMessage::extractPropertyCommand(stream, propertyId, cmd); +//kdDebug(11001) << k_funcinfo << ": Got COMMAND for id= "<<propertyId <<endl; + p = d->mIdDict.find(propertyId); + if (p) { + if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) { + p->command(stream, cmd, isSender); + } + } else { + kdError(11001) << k_funcinfo << ": (cmd): property " << propertyId << " not found" << endl; + } + return true; + } + p = d->mIdDict.find(propertyId); + if (p) { + //kdDebug(11001) << k_funcinfo << ": Loading " << propertyId << endl; + if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) { + p->load(stream); + } + } else { + kdError(11001) << k_funcinfo << ": property " << propertyId << " not found" << endl; + } + return true; +} + +bool KGamePropertyHandler::removeProperty(KGamePropertyBase* data) +{ + if (!data) { + return false; + } + d->mNameMap.erase(data->id()); + return d->mIdDict.remove(data->id()); +} + +bool KGamePropertyHandler::addProperty(KGamePropertyBase* data, QString name) +{ + //kdDebug(11001) << k_funcinfo << ": " << data->id() << endl; + if (d->mIdDict.find(data->id())) { + // this id already exists + kdError(11001) << " -> cannot add property " << data->id() << endl; + return false; + } else { + d->mIdDict.insert(data->id(), data); + // if here is a check for "is_debug" or so we can add the strings only in debug mode + // and save memory!! + if (!name.isNull()) { + d->mNameMap[data->id()] = name; + //kdDebug(11001) << k_funcinfo << ": nid="<< (data->id()) << " inserted in Map name=" << d->mNameMap[data->id()] <<endl; + //kdDebug(11001) << "Typeid=" << typeid(data).name() << endl; + //kdDebug(11001) << "Typeid call=" << data->typeinfo()->name() << endl; + } + } + return true; +} + +QString KGamePropertyHandler::propertyName(int id) const +{ + QString s; + if (d->mIdDict.find(id)) { + if (d->mNameMap.contains(id)) { + s = i18n("%1 (%2)").arg(d->mNameMap[id]).arg(id); + } else { + s = i18n("Unnamed - ID: %1").arg(id); + } + } else { + // Should _never_ happen + s = i18n("%1 unregistered").arg(id); + } + return s; +} + +bool KGamePropertyHandler::load(QDataStream &stream) +{ + // Prevent direct emmiting until all is loaded + lockDirectEmit(); + uint count,i; + stream >> count; + kdDebug(11001) << k_funcinfo << ": " << count << " KGameProperty objects " << endl; + for (i = 0; i < count; i++) { + processMessage(stream, id(),false); + } + Q_INT16 cookie; + stream >> cookie; + if (cookie == KPLAYERHANDLER_LOAD_COOKIE) { + kdDebug(11001) << " KGamePropertyHandler loaded propertly"<<endl; + } else { + kdError(11001) << "KGamePropertyHandler loading error. probably format error"<<endl; + } + // Allow direct emmiting (if no other lock still holds) + unlockDirectEmit(); + return true; +} + +bool KGamePropertyHandler::save(QDataStream &stream) +{ + kdDebug(11001) << k_funcinfo << ": " << d->mIdDict.count() << " KGameProperty objects " << endl; + stream << (uint)d->mIdDict.count(); + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + KGamePropertyBase *base=it.current(); + if (base) { + KGameMessage::createPropertyHeader(stream, base->id()); + base->save(stream); + } + ++it; + } + stream << (Q_INT16)KPLAYERHANDLER_LOAD_COOKIE; + return true; +} + +KGamePropertyBase::PropertyPolicy KGamePropertyHandler::policy() +{ +// kdDebug(11001) << k_funcinfo << ": " << d->mDefaultPolicy << endl; + return d->mDefaultPolicy; +} +void KGamePropertyHandler::setPolicy(KGamePropertyBase::PropertyPolicy p,bool userspace) +{ + // kdDebug(11001) << k_funcinfo << ": " << p << endl; + d->mDefaultPolicy=p; + d->mDefaultUserspace=userspace; + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + if (!userspace || it.current()->id()>=KGamePropertyBase::IdUser) { + it.current()->setPolicy((KGamePropertyBase::PropertyPolicy)p); + } + ++it; + } +} + +void KGamePropertyHandler::unlockProperties() +{ + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + it.current()->unlock(); + ++it; + } +} + +void KGamePropertyHandler::lockProperties() +{ + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + it.current()->lock(); + ++it; + } +} + +int KGamePropertyHandler::uniquePropertyId() +{ + return d->mUniqueId++; +} + +void KGamePropertyHandler::flush() +{ + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + if (it.current()->isDirty()) { + it.current()->sendProperty(); + } + ++it; + } +} + +/* Fire all property signal changed which are collected in + * the queque + **/ +void KGamePropertyHandler::lockDirectEmit() +{ + d->mIndirectEmit++; +} + +void KGamePropertyHandler::unlockDirectEmit() +{ + // If the flag is <=0 we emit the queued signals + d->mIndirectEmit--; + if (d->mIndirectEmit<=0) + { + KGamePropertyBase *prop; + while((prop=d->mSignalQueue.dequeue()) != 0) + { + // kdDebug(11001) << "emmiting signal for " << prop->id() << endl; + emit signalPropertyChanged(prop); + } + } +} + +void KGamePropertyHandler::emitSignal(KGamePropertyBase *prop) +{ + // If the indirect flag is set (load and network transmit) + // we cannot emit the signals directly as it can happend that + // a sigal causes an access to a property which is e.g. not + // yet loaded or received + + if (d->mIndirectEmit>0) + { + // Queque the signal + d->mSignalQueue.enqueue(prop); + } + else + { + // directly emit + emit signalPropertyChanged(prop); + } +} + +bool KGamePropertyHandler::sendProperty(QDataStream &s) +{ + bool sent = false; + emit signalSendMessage(id(), s, &sent); + return sent; +} + +KGamePropertyBase *KGamePropertyHandler::find(int id) +{ + return d->mIdDict.find(id); +} + +void KGamePropertyHandler::clear() +{ + kdDebug(11001) << k_funcinfo << id() << endl; + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.toFirst()) { + KGamePropertyBase* p = it.toFirst(); + p->unregisterData(); + if (d->mIdDict.find(p->id())) { + // shouldn't happen - but if mOwner in KGamePropertyBase is NULL + // this might be possible + removeProperty(p); + } + } +} + +QIntDict<KGamePropertyBase>& KGamePropertyHandler::dict() const +{ + return d->mIdDict; +} + +QString KGamePropertyHandler::propertyValue(KGamePropertyBase* prop) +{ + if (!prop) { + return i18n("NULL pointer"); + } + + int id = prop->id(); + QString name = propertyName(id); + QString value; + + const type_info* t = prop->typeinfo(); + if (*t == typeid(int)) { + value = QString::number(((KGamePropertyInt*)prop)->value()); + } else if (*t == typeid(unsigned int)) { + value = QString::number(((KGamePropertyUInt *)prop)->value()); + } else if (*t == typeid(long int)) { + value = QString::number(((KGameProperty<long int> *)prop)->value()); + } else if (*t == typeid(unsigned long int)) { + value = QString::number(((KGameProperty<unsigned long int> *)prop)->value()); + } else if (*t == typeid(QString)) { + value = ((KGamePropertyQString*)prop)->value(); + } else if (*t == typeid(Q_INT8)) { + value = ((KGamePropertyBool*)prop)->value() ? i18n("True") : i18n("False"); + } else { + emit signalRequestValue(prop, value); + } + + if (value.isNull()) { + value = i18n("Unknown"); + } + return value; +} + +void KGamePropertyHandler::Debug() +{ + kdDebug(11001) << "-----------------------------------------------------------" << endl; + kdDebug(11001) << "KGamePropertyHandler:: Debug this=" << this << endl; + + kdDebug(11001) << " Registered properties: (Policy,Lock,Emit,Optimized, Dirty)" << endl; + QIntDictIterator<KGamePropertyBase> it(d->mIdDict); + while (it.current()) { + KGamePropertyBase *p=it.current(); + kdDebug(11001) << " "<< p->id() << ": p=" << p->policy() + << " l="<<p->isLocked() + << " e="<<p->isEmittingSignal() + << " o=" << p->isOptimized() + << " d="<<p->isDirty() + << endl; + ++it; + } + kdDebug(11001) << "-----------------------------------------------------------" << endl; +} + +#include "kgamepropertyhandler.moc" diff --git a/libkdegames/kgame/kgamepropertyhandler.h b/libkdegames/kgame/kgamepropertyhandler.h new file mode 100644 index 00000000..6147c071 --- /dev/null +++ b/libkdegames/kgame/kgamepropertyhandler.h @@ -0,0 +1,353 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + + 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 __KGAMEPROPERTYHANDLER_H_ +#define __KGAMEPROPERTYHANDLER_H_ + +#include <qobject.h> +#include <qintdict.h> + +#include "kgameproperty.h" +#include <kdemacros.h> + +class QDataStream; +class KGame; +class KPlayer; +//class KGamePropertyBase; + +class KGamePropertyHandlerPrivate; // wow - what a name ;-) + +/** + * @short A collection class for KGameProperty objects + * + * The KGamePropertyHandler class is some kind of a collection class for + * KGameProperty. You usually don't have to create one yourself, as both + * KPlayer and KGame provide a handler. In most cases you do not even have + * to care about the KGamePropertHandler. KGame and KPlayer implement + * all features of KGamePropertyHandler so you will rather use it there. + * + * You have to use the KGamePropertyHandler as parent for all KGameProperty + * objects but you can also use KPlayer or KGame as parent - then + * KPlayer::dataHandler or KGame::dataHandler will be used. + * + * Every KGamePropertyHandler must have - just like every KGameProperty - + * a unique ID. This ID is provided either in the constructor or in + * registerHandler. The ID is used to assign an incoming message (e.g. a changed + * property) to the correct handler. Inside the handler the property ID is used + * to change the correct property. + * + * The constructor or registerHandler takes 3 addittional arguments: a + * receiver and two slots. The first slot is connected to + * signalSendMessage, the second to signalPropertyChanged. You must provide + * these in order to use the KGamePropertyHandler. + * + * The most important function of KGamePropertyHandler is processMessage + * which assigns an incoming value to the correct property. + * + * A KGamePropertyHandler is also used - indirectly using emitSignal - to + * emit a signal when the value of a property changes. This is done this way + * because a KGameProperty does not inherit QObject because of memory + * advantages. Many games can have dozens or even hundreds of KGameProperty + * objects so every additional variable in KGameProperty would be + * multiplied. + * + **/ +class KDE_EXPORT KGamePropertyHandler : public QObject +{ + Q_OBJECT + +public: + /** + * Construct an unregistered KGamePropertyHandler + * + * You have to call registerHandler before you can use this + * handler! + **/ + KGamePropertyHandler(QObject* parent = 0); + + /** + * Construct a registered handler. + * + * @see registerHandler + **/ + KGamePropertyHandler(int id, const QObject* receiver, const char* sendf, const char* emitf, QObject* parent = 0); + ~KGamePropertyHandler(); + + /** + * Register the handler with a parent. This is to use + * if the constructor without arguments has been chosen. + * Otherwise you need not call this. + * + * @param id The id of the message to listen for + * @param receiver The object that will receive the signals of + * KGamePropertyHandler + * @param send A slot that is being connected to signalSendMessage + * @param emit A slot that is being connected to signalPropertyChanged + **/ + void registerHandler(int id, const QObject *receiver, const char * send, const char *emit); + + /** + * Main message process function. This has to be called by + * the parent's message event handler. If the id of the message + * agrees with the id of the handler, the message is extracted + * and processed. Otherwise false is returned. + * Example: + * \code + * if (mProperties.processMessage(stream,msgid,sender==gameId())) return ; + * \endcode + * + * @param stream The data stream containing the message + * @param id the message id of the message + * @param isSender Whether the receiver is also the sender + * @return true on message processed otherwise false + **/ + bool processMessage(QDataStream &stream, int id, bool isSender ); + + /** + * @return the id of the handler + **/ + int id() const; + + /** + * Adds a KGameProperty property to the handler + * @param data the property + * @param name A description of the property, which will be returned by + * propertyName. This is used for debugging, e.g. in KGameDebugDialog + * @return true on success + **/ + bool addProperty(KGamePropertyBase *data, QString name=0); + + /** + * Removes a property from the handler + * @param data the property + * @return true on success + **/ + bool removeProperty(KGamePropertyBase *data); + + /** + * returns a unique property ID starting called usually with a base of + * KGamePropertyBase::IdAutomatic. This is used internally by + * the property base to assign automtic id's. Not much need to + * call this yourself. + **/ + int uniquePropertyId(); + + + /** + * Loads properties from the datastream + * + * @param stream the datastream to load from + * @return true on success otherwise false + **/ + virtual bool load(QDataStream &stream); + + /** + * Saves properties into the datastream + * + * @param stream the datastream to save to + * @return true on success otherwise false + **/ + virtual bool save(QDataStream &stream); + + /** + * called by a property to send itself into the + * datastream. This call is simply forwarded to + * the parent object + **/ + bool sendProperty(QDataStream &s); + + void sendLocked(bool l); + + /** + * called by a property to emit a signal + * This call is simply forwarded to + * the parent object + **/ + void emitSignal(KGamePropertyBase *data); + + /** + * @param id The ID of the property + * @return A name of the property which can be used for debugging. Don't + * depend on this function! It it possible not to provide a name or to + * provide the same name for multiple properties! + **/ + QString propertyName(int id) const; + + /** + * @param id The ID of the property. See KGamePropertyBase::id + * @return The KGameProperty this ID is assigned to + **/ + KGamePropertyBase *find(int id); + + /** + * Clear the KGamePropertyHandler. Note that the properties are + * <em>not</em> deleted so if you created your KGameProperty + * objects dynamically like + * \code + * KGamePropertyInt* myProperty = new KGamePropertyInt(id, dataHandler()); + * \endcode + * you also have to delete it: + * \code + * dataHandler()->clear(); + * delete myProperty; + * \endcode + **/ + void clear(); + + /** + * Use id as new ID for this KGamePropertyHandler. This is used + * internally only. + **/ + void setId(int id);//AB: TODO: make this protected in KGamePropertyHandler!! + + /** + * Calls KGamePropertyBase::setReadOnly(false) for all properties of this + * player. See also lockProperties + **/ + void unlockProperties(); + + /** + * Set the policy for all kgame variables which are currently registerd in + * the KGame proeprty handler. See KGamePropertyBase::setPolicy + * + * @param p is the new policy for all properties of this handler + * @param userspace if userspace is true (default) only user properties are changed. + * Otherwise the system properties are also changed. + **/ + void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true); + + /** + * Called by the KGame or KPlayer object or the handler itself to delay + * emmiting of signals. Lockign keeps a counter and unlock is only achieved + * when every lock is canceld by an unlock. + * While this is set signals are quequed and only emmited after this + * is reset. Its deeper meaning is to prevent inconsistencies in a game + * load or network transfer where a emit could access a property not + * yet loaded or transmitted. Calling this by yourself you better know + * what your are doing. + **/ + void lockDirectEmit(); + + /** + * Removes the lock from the emitting of property signals. Corresponds to + * the lockIndirectEmits + **/ + void unlockDirectEmit(); + + /** + * Returns the default policy for this property handler. All properties + * registered newly, will have this property. + **/ + KGamePropertyBase::PropertyPolicy policy(); + + /** + * Calls KGamePropertyBase::setReadOnly(true) for all properties of this + * handler + * + * Use with care! This will even lock the core properties, like name, + * group and myTurn!! + * + * @see unlockProperties + **/ + void lockProperties(); + + /** + * Sends all properties which are marked dirty over the network. This will + * make a forced synchornisation of the properties and mark them all not dirty. + **/ + void flush(); + + /** + * Reference to the internal dictionary + **/ + QIntDict<KGamePropertyBase> &dict() const; + + /** + * In several situations you just want to have a QString of a + * KGameProperty object. This is the case in the + * KGameDebugDialog where the value of all properties is displayed. This + * function will provide you with such a QString for all the types + * used inside of all KGame classes. If you have a non-standard + * property (probably a self defined class or something like this) you + * also need to connect to signalRequestValue to make this function + * useful. + * @param property Return the value of this KGameProperty + * @return The value of a KGameProperty + **/ + QString propertyValue(KGamePropertyBase* property); + + + /** + * Writes some debug output to the console. + **/ + void Debug(); + + +signals: + /** + * This is emitted by a property. KGamePropertyBase::emitSignal + * calls emitSignal which emits this signal. + * + * This signal is emitted whenever the property is changed. Note that + * you can switch off this behaviour using + * KGamePropertyBase::setEmittingSignal in favor of performance. Note + * that you won't experience any performance loss using signals unless + * you use dozens or hundreds of properties which change very often. + **/ + void signalPropertyChanged(KGamePropertyBase *); + + /** + * This signal is emitted when a property needs to be sent. Only the + * parent has to react to this. + * @param msgid The id of the handler + * @param sent set this to true if the property was sent successfully - + * otherwise don't touch + **/ + void signalSendMessage(int msgid, QDataStream &, bool* sent); // AB shall we change bool* into bool& again? + + /** + * If you call propertyValue with a non-standard KGameProperty + * it is possible that the value cannot automatically be converted into a + * QString. Then this signal is emitted and asks you to provide the + * correct value. You probably want to use something like this to achieve + * this: + * \code + * #include <typeinfo> + * void slotRequestValue(KGamePropertyBase* p, QString& value) + * { + * if (*(p->typeinfo()) == typeid(MyType) { + * value = QString(((KGameProperty<MyType>*)p)->value()); + * } + * } + * \endcode + * + * @param property The KGamePropertyBase the value is requested for + * @param value The value of this property. You have to set this. + **/ + void signalRequestValue(KGamePropertyBase* property, QString& value); + +private: + void init(); + +private: + KGamePropertyHandlerPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/kgamepropertylist.h b/libkdegames/kgame/kgamepropertylist.h new file mode 100644 index 00000000..3a304e70 --- /dev/null +++ b/libkdegames/kgame/kgamepropertylist.h @@ -0,0 +1,258 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KGAMEPROPERTYLIST_H_ +#define __KGAMEPROPERTYLIST_H_ + +#include <qvaluelist.h> + +#include <kdebug.h> + +#include "kgamemessage.h" +#include "kgameproperty.h" +#include "kgamepropertyhandler.h" + +// AB: also see README.LIB! + +template<class type> +class KGamePropertyList : public QValueList<type>, public KGamePropertyBase +{ +public: + /** + * Typedefs + */ + typedef QValueListIterator<type> Iterator; + typedef QValueListConstIterator<type> ConstIterator; + + KGamePropertyList() :QValueList<type>(), KGamePropertyBase() + { + } + + KGamePropertyList( const KGamePropertyList<type> &a ) : QValueList<type>(a) + { + } + + uint findIterator(Iterator me) + { + Iterator it; + uint cnt=0; + for( it = this->begin(); it != this->end(); ++it ) + { + if (me==it) + { + return cnt; + } + cnt++; + } + return this->count(); + } + + Iterator insert( Iterator it, const type& d ) + { + it=QValueList<type>::insert(it,d); + + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdInsert); + int i=findIterator(it); + s << i; + s << d; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + return it; + } + + void prepend( const type& d) { insert(this->begin(),d); } + + void append( const type& d ) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAppend); + s << d; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + } + + Iterator erase( Iterator it ) + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdRemove); + int i=findIterator(it); + s << i; + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + //TODO: return value - is it correct for PolicyLocal|PolicyDirty? +// return QValueList<type>::remove(it); + return it; + } + + Iterator remove( Iterator it ) + { + return erase(it); + } + + void remove( const type& d ) + { + Iterator it=find(d); + remove(it); + } + + void clear() + { + QByteArray b; + QDataStream s(b, IO_WriteOnly); + KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdClear); + if (policy() == PolicyClean || policy() == PolicyDirty) + { + if (mOwner) + { + mOwner->sendProperty(s); + } + } + if (policy() == PolicyDirty || policy() == PolicyLocal) + { + extractProperty(b); + } + } + + void load(QDataStream& s) + { + kdDebug(11001) << "KGamePropertyList load " << id() << endl; + QValueList<type>::clear(); + uint size; + type data; + s >> size; + + for (unsigned int i=0;i<size;i++) + { + s >> data; + QValueList<type>::append(data); + } + if (isEmittingSignal()) emitSignal(); + } + + void save(QDataStream &s) + { + kdDebug(11001) << "KGamePropertyList save "<<id() << endl; + type data; + uint size=this->count(); + s << size; + Iterator it; + for( it = this->begin(); it != this->end(); ++it ) + { + data=*it; + s << data; + } + } + + void command(QDataStream &s,int cmd,bool) + { + KGamePropertyBase::command(s, cmd); + kdDebug(11001) << "---> LIST id="<<id()<<" got command ("<<cmd<<") !!!" <<endl; + Iterator it; + switch(cmd) + { + case CmdInsert: + { + uint i; + type data; + s >> i >> data; + it=this->at(i); + QValueList<type>::insert(it,data); +// kdDebug(11001) << "CmdInsert:id="<<id()<<" i="<<i<<" data="<<data <<endl; + if (isEmittingSignal()) emitSignal(); + break; + } + case CmdAppend: + { + type data; + s >> data; + QValueList<type>::append(data); +// kdDebug(11001) << "CmdAppend:id=" << id() << " data=" << data << endl; + if (isEmittingSignal()) emitSignal(); + break; + } + case CmdRemove: + { + uint i; + s >> i; + it=this->at(i); + QValueList<type>::remove(it); + kdDebug(11001) << "CmdRemove:id="<<id()<<" i="<<i <<endl; + if (isEmittingSignal()) emitSignal(); + break; + } + case CmdClear: + { + QValueList<type>::clear(); + kdDebug(11001) << "CmdClear:id="<<id()<<endl; + if (isEmittingSignal()) emitSignal(); + break; + } + default: + kdDebug(11001) << "Error in KPropertyList::command: Unknown command " << cmd << endl; + } + } + +protected: + void extractProperty(const QByteArray& b) + // this is called for Policy[Dirty|Local] after putting the stuff into the + // stream + { + QDataStream s(b, IO_ReadOnly); + int cmd; + int propId; + KGameMessage::extractPropertyHeader(s, propId); + KGameMessage::extractPropertyCommand(s, propId, cmd); + command(s, cmd, true); + } + +}; + +#endif diff --git a/libkdegames/kgame/kgamesequence.cpp b/libkdegames/kgame/kgamesequence.cpp new file mode 100644 index 00000000..984a9315 --- /dev/null +++ b/libkdegames/kgame/kgamesequence.cpp @@ -0,0 +1,125 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ + +#include "kgamesequence.h" +#include "kgamesequence.moc" + +#include "kplayer.h" +#include "kgame.h" + +KGameSequence::KGameSequence() : QObject() +{ + mGame = 0; + mCurrentPlayer = 0; +} + +KGameSequence::~KGameSequence() +{ +} + +void KGameSequence::setGame(KGame* game) +{ + mGame = game; +} + +void KGameSequence::setCurrentPlayer(KPlayer* player) +{ + mCurrentPlayer = player; +} + +KPlayer *KGameSequence::nextPlayer(KPlayer *last,bool exclusive) +{ + kdDebug(11001) << "=================== NEXT PLAYER =========================="<<endl; + if (!game()) + { + kdError() << k_funcinfo << "NULL game object" << endl; + return 0; + } + unsigned int minId,nextId,lastId; + KPlayer *nextplayer, *minplayer; + if (last) + { + lastId = last->id(); + } + else + { + lastId = 0; + } + + kdDebug(11001) << "nextPlayer: lastId="<<lastId<<endl; + + // remove when this has been checked + minId = 0x7fff; // we just need a very large number...properly MAX_UINT or so would be ok... + nextId = minId; + nextplayer = 0; + minplayer = 0; + + KPlayer *player; + for (player = game()->playerList()->first(); player != 0; player=game()->playerList()->next() ) + { + // Find the first player for a cycle + if (player->id() < minId) + { + minId=player->id(); + minplayer=player; + } + if (player==last) + { + continue; + } + // Find the next player which is bigger than the current one + if (player->id() > lastId && player->id() < nextId) + { + nextId=player->id(); + nextplayer=player; + } + } + + // Cycle to the beginning + if (!nextplayer) + { + nextplayer=minplayer; + } + + kdDebug(11001) << k_funcinfo << " ##### lastId=" << lastId << " exclusive=" + << exclusive << " minId=" << minId << " nextid=" << nextId + << " count=" << game()->playerList()->count() << endl; + if (nextplayer) + { + nextplayer->setTurn(true,exclusive); + } + else + { + return 0; + } + return nextplayer; +} + +// Per default we do not do anything +int KGameSequence::checkGameOver(KPlayer*) +{ + return 0; +} +/* + * vim: et sw=2 + */ diff --git a/libkdegames/kgame/kgamesequence.h b/libkdegames/kgame/kgamesequence.h new file mode 100644 index 00000000..4d26ceee --- /dev/null +++ b/libkdegames/kgame/kgamesequence.h @@ -0,0 +1,87 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMESEQUENCE_H_ +#define __KGAMESEQUENCE_H_ + +#include <qobject.h> + +class KPlayer; +class KGame; + +/** + * This class takes care of round or move management as well of the gameover + * condition. It is especially used for round based games. For these games @ref + * nextPlayer and @ref checkGameOver are the most important methods. + * + * You can subclass KGameSequence and use @ref KGame::setGameSequence to use + * your own rules. Note that @ref KGame will take ownership and therefore will + * delete the object on destruction. + * @short Round/move management class + * @author Andreas Beckermann <b_mann@gmx.de> + **/ +class KGameSequence : public QObject +{ + Q_OBJECT +public: + KGameSequence(); + virtual ~KGameSequence(); + + /** + * Select the next player in a turn based game. In an asynchronous game this + * function has no meaning. Overwrite this function for your own game sequence. + * Per default it selects the next player in the playerList + */ + virtual KPlayer* nextPlayer(KPlayer *last, bool exclusive = true); + + virtual void setCurrentPlayer(KPlayer* p); + + /** + * @return The @ref KGame object this sequence is for, or NULL if none. + **/ + KGame* game() const { return mGame; } + + KPlayer* currentPlayer() const { return mCurrentPlayer; } + + /** + * Set the @ref KGame object for this sequence. This is called + * automatically by @ref KGame::setGameSequence and you should not call + * it. + **/ + void setGame(KGame* game); + + /** + * Check whether the game is over. The default implementation always + * returns 0. + * + * @param player the player who made the last move + * @return anything else but 0 is considered as game over + **/ + virtual int checkGameOver(KPlayer *player); + +private: + KGame* mGame; + KPlayer* mCurrentPlayer; +}; + +#endif + diff --git a/libkdegames/kgame/kgameversion.h b/libkdegames/kgame/kgameversion.h new file mode 100644 index 00000000..c3147525 --- /dev/null +++ b/libkdegames/kgame/kgameversion.h @@ -0,0 +1,54 @@ +/* + This file is part of the KDE games library + Copyright (C) 2003 Andreas Beckermann (b_mann@gmx.de) + Copyright (C) 2003 Martin Heni (martin@heni-online.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEVERSION_H__ +#define __KGAMEVERSION_H__ + +/** + * In this file you find a couple of defines that indicate whether a specific + * feature or function is present in this version of the KGame library. + * + * You don't need this for KDE CVS, but for games that live outside of KDE CVS + * it may be very helpful and a lot easier than writing configure scripts for + * this task. + * + * All defines are prefixed with KGAME_ to avoid conflicts. + **/ + +// KGame::savegame() didn't exist in KDE 3.0 +#define KGAME_HAVE_KGAME_SAVEGAME 1 + +// KGameNetwork::port(), KMessageIO::peerPort() and friends were added in KDE 3.2 +#define KGAME_HAVE_KGAME_PORT 1 + +// KGameNetwork::hostName(), KMessageIO::peerName() and friends were added in KDE 3.2 +#define KGAME_HAVE_KGAME_HOSTNAME 1 + +// KGameSequence class was added in KDE 3.2 +#define KGAME_HAVE_KGAMESEQUENCE 1 + +// KGame::addPlayer() needs to assign an ID to new players, otherwise network is +// broken. this is done in KDE 3.2. +#define KGAME_HAVE_FIXED_ADDPLAYER_ID 1 + +#endif + diff --git a/libkdegames/kgame/kmessageclient.cpp b/libkdegames/kgame/kmessageclient.cpp new file mode 100644 index 00000000..ed9cc966 --- /dev/null +++ b/libkdegames/kgame/kmessageclient.cpp @@ -0,0 +1,373 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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 <kdebug.h> +#include <stdio.h> + +#include <qbuffer.h> +#include <qtimer.h> + +#include "kmessageio.h" +#include "kmessageserver.h" + +#include "kmessageclient.h" + +class KMessageClientPrivate +{ +public: + KMessageClientPrivate () + : adminID (0), connection (0) + {} + + ~KMessageClientPrivate () + { + delete connection; + } + + Q_UINT32 adminID; + QValueList <Q_UINT32> clientList; + KMessageIO *connection; + + bool isLocked; + QValueList <QByteArray> delayedMessages; +}; + +KMessageClient::KMessageClient (QObject *parent, const char *name) + : QObject (parent, name) +{ + d = new KMessageClientPrivate (); + d->isLocked = false; +} + +KMessageClient::~KMessageClient () +{ + d->delayedMessages.clear(); + delete d; +} + +// -- setServer stuff + +void KMessageClient::setServer (const QString &host, Q_UINT16 port) +{ + setServer (new KMessageSocket (host, port)); +} + +void KMessageClient::setServer (KMessageServer *server) +{ + KMessageDirect *serverIO = new KMessageDirect (); + setServer (new KMessageDirect (serverIO)); + server->addClient (serverIO); +} + +void KMessageClient::setServer (KMessageIO *connection) +{ + if (d->connection) + { + delete d->connection; + kdDebug (11001) << k_funcinfo << ": We are changing the server!" << endl; + } + + d->connection = connection; + if (connection ) + { + connect (connection, SIGNAL (received(const QByteArray &)), + this, SLOT (processIncomingMessage(const QByteArray &))); + connect (connection, SIGNAL (connectionBroken()), + this, SLOT (removeBrokenConnection ())); + } +} + +// -- id stuff + +Q_UINT32 KMessageClient::id () const +{ + return (d->connection) ? d->connection->id () : 0; +} + +bool KMessageClient::isAdmin () const +{ + return id() != 0 && id() == adminId(); +} + +Q_UINT32 KMessageClient::adminId () const +{ + return d->adminID; +} + +const QValueList <Q_UINT32> &KMessageClient::clientList() const +{ + return d->clientList; +} + +bool KMessageClient::isConnected () const +{ + return d->connection && d->connection->isConnected(); +} + +bool KMessageClient::isNetwork () const +{ + return isConnected() ? d->connection->isNetwork() : false; +} + +Q_UINT16 KMessageClient::peerPort () const +{ + return d->connection ? d->connection->peerPort() : 0; +} + +QString KMessageClient::peerName () const +{ + return d->connection ? d->connection->peerName() : QString::fromLatin1("localhost"); +} + +// --------------------- Sending messages + +void KMessageClient::sendServerMessage (const QByteArray &msg) +{ + if (!d->connection) + { + kdWarning (11001) << k_funcinfo << ": We have no connection yet!" << endl; + return; + } + d->connection->send (msg); +} + +void KMessageClient::sendBroadcast (const QByteArray &msg) +{ + QByteArray sendBuffer; + QBuffer buffer (sendBuffer); + buffer.open (IO_WriteOnly); + QDataStream stream (&buffer); + + stream << static_cast<Q_UINT32> ( KMessageServer::REQ_BROADCAST ); + buffer.QIODevice::writeBlock (msg); + sendServerMessage (sendBuffer); +} + +void KMessageClient::sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients) +{ + QByteArray sendBuffer; + QBuffer buffer (sendBuffer); + buffer.open (IO_WriteOnly); + QDataStream stream (&buffer); + + stream << static_cast<Q_UINT32>( KMessageServer::REQ_FORWARD ) << clients; + buffer.QIODevice::writeBlock (msg); + sendServerMessage (sendBuffer); +} + +void KMessageClient::sendForward (const QByteArray &msg, Q_UINT32 client) +{ + sendForward (msg, QValueList <Q_UINT32> () << client); +} + + +// --------------------- Receiving and processing messages + +void KMessageClient::processIncomingMessage (const QByteArray &msg) +{ + if (d->isLocked) + { + d->delayedMessages.append(msg); + return; + } + if (d->delayedMessages.count() > 0) + { + d->delayedMessages.append (msg); + QByteArray first = d->delayedMessages.front(); + d->delayedMessages.pop_front(); + processMessage (first); + } + else + { + processMessage(msg); + } +} + +void KMessageClient::processMessage (const QByteArray &msg) +{ + if (d->isLocked) + { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage + d->delayedMessages.append(msg); + return; + } + QBuffer in_buffer (msg); + in_buffer.open (IO_ReadOnly); + QDataStream in_stream (&in_buffer); + + + bool unknown = false; + + Q_UINT32 messageID; + in_stream >> messageID; + switch (messageID) + { + case KMessageServer::MSG_BROADCAST: + { + Q_UINT32 clientID; + in_stream >> clientID; + emit broadcastReceived (in_buffer.readAll(), clientID); + } + break; + + case KMessageServer::MSG_FORWARD: + { + Q_UINT32 clientID; + QValueList <Q_UINT32> receivers; + in_stream >> clientID >> receivers; + emit forwardReceived (in_buffer.readAll(), clientID, receivers); + } + break; + + case KMessageServer::ANS_CLIENT_ID: + { + bool old_admin = isAdmin(); + Q_UINT32 clientID; + in_stream >> clientID; + d->connection->setId (clientID); + if (old_admin != isAdmin()) + emit adminStatusChanged (isAdmin()); + } + break; + + case KMessageServer::ANS_ADMIN_ID: + { + bool old_admin = isAdmin(); + in_stream >> d->adminID; + if (old_admin != isAdmin()) + emit adminStatusChanged (isAdmin()); + } + break; + + case KMessageServer::ANS_CLIENT_LIST: + { + in_stream >> d->clientList; + } + break; + + case KMessageServer::EVNT_CLIENT_CONNECTED: + { + Q_UINT32 id; + in_stream >> id; + + if (d->clientList.contains (id)) + kdWarning (11001) << k_funcinfo << ": Adding a client that already existed!" << endl; + else + d->clientList.append (id); + + emit eventClientConnected (id); + } + break; + + case KMessageServer::EVNT_CLIENT_DISCONNECTED: + { + Q_UINT32 id; + Q_INT8 broken; + in_stream >> id >> broken; + + if (!d->clientList.contains (id)) + kdWarning (11001) << k_funcinfo << ": Removing a client that doesn't exist!" << endl; + else + d->clientList.remove (id); + + emit eventClientDisconnected (id, bool (broken)); + } + break; + + default: + unknown = true; + } + + if (!unknown && !in_buffer.atEnd()) + kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl; + + emit serverMessageReceived (msg, unknown); + + if (unknown) + kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl; +} + +void KMessageClient::processFirstMessage() +{ + if (d->isLocked) + { + return; + } + if (d->delayedMessages.count() == 0) + { + kdDebug(11001) << k_funcinfo << ": no messages delayed" << endl; + return; + } + QByteArray first = d->delayedMessages.front(); + d->delayedMessages.pop_front(); + processMessage (first); +} + +void KMessageClient::removeBrokenConnection () +{ + kdDebug (11001) << k_funcinfo << ": timer single shot for removeBrokenConnection"<<this << endl; + // MH We cannot directly delete the socket. otherwise QSocket crashes + QTimer::singleShot( 0, this, SLOT(removeBrokenConnection2()) ); + return; +} + + +void KMessageClient::removeBrokenConnection2 () +{ + kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object"<<this << endl; + + emit aboutToDisconnect(id()); + delete d->connection; + d->connection = 0; + d->adminID = 0; + emit connectionBroken(); + kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object DONE" << endl; +} + +void KMessageClient::disconnect () +{ + kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object" << endl; + + emit aboutToDisconnect(id()); + delete d->connection; + d->connection = 0; + d->adminID = 0; + emit connectionBroken(); + kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object DONE" << endl; +} + +void KMessageClient::lock () +{ + d->isLocked = true; +} + +void KMessageClient::unlock () +{ + d->isLocked = false; + for (unsigned int i = 0; i < d->delayedMessages.count(); i++) + { + QTimer::singleShot(0, this, SLOT(processFirstMessage())); + } +} + +unsigned int KMessageClient::delayedMessageCount() const +{ + return d->delayedMessages.count(); +} + +#include "kmessageclient.moc" diff --git a/libkdegames/kgame/kmessageclient.h b/libkdegames/kgame/kmessageclient.h new file mode 100644 index 00000000..90364c8d --- /dev/null +++ b/libkdegames/kgame/kmessageclient.h @@ -0,0 +1,422 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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 __KMESSAGECLIENT_H__ +#define __KMESSAGECLIENT_H__ + +#include <qobject.h> +#include <qstring.h> +#include <qvaluelist.h> + +class KMessageIO; +class KMessageServer; +class KMessageClientPrivate; + +/** + @short A client to connect to a KMessageServer + + This class implements a client that can connect to a KMessageServer object. + It can be used to exchange messages between clients. + + Usually you will connect the signals broadcastReceived and forwardReceived to + some specific slots. In these slot methods you can analyse the messages that are + sent to you from other clients. + + To send messages to other clients, use the methods sendBroadcast() (to send to all + clients) or sendForward() (to send to a list of selected clients). + + If you want to communicate with the KMessageServer object directly (on a more low + level base), use the method sendServerMessage to send a command to the server and + connect to the signal serverMessageReceived to see all the incoming messages. + In that case the messages must be of the format specified in KMessageServer. + @author Burkhard Lehner <Burkhard.Lehner@gmx.de> +*/ +class KMessageClient : public QObject +{ + Q_OBJECT + +public: + + /** + Constructor. + Creates an unconnected KMessageClient object. Use setServer() later to connect to a + KMessageServer object. + */ + KMessageClient (QObject *parent = 0, const char *name = 0); + + /** + Destructor. + Disconnects from the server, if any connection was established. + */ + ~KMessageClient (); + + /** + @return The client ID of this client. Every client that is connected to a KMessageServer + has a unique ID number. + + NOTE: As long as the object is not yet connected to the server, and as long as the server + hasn't sent the client ID, this method returns 0. + */ + Q_UINT32 id () const; + + /** + @return Whether or not this client is the server admin. + One of the clients connected to the server is the admin and can administrate the server + (set maximum number of clients, remove clients, ...). + + If you use admin commands without being the admin, these commands are simply ignored by + the server. + + NOTE: As long as you are not connected to a server, this method returns false. + */ + bool isAdmin () const; + + /** + @return The ID of the admin client on the message server. + */ + Q_UINT32 adminId() const; + + /** + @return The list of the IDs of all the message clients connected to the message server. + */ + const QValueList <Q_UINT32> &clientList() const; + + /** + Connects the client to (another) server. + + Tries to connect via a TCP/IP socket to a KMessageServer object + on the given host, listening on the specified port. + + If we were already connected, the old connection is closed. + @param host The name of the host to connect to. Must be either a hostname which can + be resolved to an IP or just an IP + @param port The port to connect to + */ + void setServer (const QString &host, Q_UINT16 port); + + /** + Connects the client to (another) server. + + Connects to the given server, using KMessageDirect. + (The server object has to be in the same process.) + + If we were already connected, the old connection is closed. + @param server The KMessageServer to connect to + */ + void setServer (KMessageServer *server); + + /** + * Corresponds to setServer(0); but also emits the connectionBroken signal + **/ + void disconnect(); + + /** + Connects the client to (another) server. + + To use this method, you have to create a KMessageIO object with new (indeed you must + create an instance of a subclass of KMessageIO, e.g. KMessageSocket or KMessageDirect). + This object must already be connected to the new server. + + Calling this method disconnects any earlier connection, and uses the new KMessageIO + object instead. This object gets owned by the KMessageClient object, so don't delete + or manipulate it afterwards. + + With this method it is possible to change the server on the fly. But be careful that + there are no important messages from the old server not yet delivered. + + NOTE: It is very likely that we will have another client ID on the new server. The + value returned by clientID may be a little outdated until the new server tells us + our new ID. + + NOTE: The two other setServer methods are for convenience. If you use them, you don't + have to create a KMessageIO object yourself. + */ + virtual void setServer (KMessageIO *connection); + + /** + @return True, if a connection to a KMessageServer has been started, and if the + connection is ready for transferring data. (It will return false e.g. as long as + a socket connection hasn't been established, and it will also return false after + a socket connection is broken.) + */ + bool isConnected () const; + + /** + @return TRUE if isConnected() is true AND this is not a local (like + KMessageDirect) connection. + */ + bool isNetwork () const; + + /** + @return 0 if isConnected() is FALSE, otherwise the port number this client is + connected to. See also KMessageIO::peerPort and QSocket::peerPort. + @since 3.2 + */ + Q_UINT16 peerPort () const; + + /** + @since 3.2 + @return "localhost" if isConnected() is FALSE, otherwise the hostname this client is + connected to. See also KMessageIO::peerName() and QSocket::peerName(). + */ + QString peerName() const; + + /** + Sends a message to the KMessageServer. If we are not yet connected to one, nothing + happens. + + Use this method to send a low level command to the server. It has to be in the + format specified in KMessageServer. + + If you want to send messages to other clients, you should use sendBroadcast() + and sendForward(). + @param msg The message to be sent to the server. Must be in the format specified in KMessageServer. + */ + void sendServerMessage (const QByteArray &msg); + + /** + Sends a message to all the clients connected to the server, including ourself. + The message consists of an arbitrary block of data with arbitrary length. + + All the clients will receive an exact copy of this block of data, which will be + processed in their processBroadcast() method. + @param msg The message to be sent to the clients + */ + //AB: processBroadcast doesn't exist!! is processIncomingMessage meant? + void sendBroadcast (const QByteArray &msg); + + /** + Sends a message to all the clients in a list. + The message consists of an arbitrary block of data with arbitrary length. + + All clients will receive an exact copy of this block of data, which will be + processed in their processForward() method. + + If the list contains client IDs that are not defined, they are ignored. If + it contains an ID several times, that client will receive the message several + times. + + To send a message to the admin of the KMessageServer, you can use 0 as clientID, + instead of using the real client ID. + @param msg The message to be sent to the clients + @param clients A list of clients the message should be sent to + */ + //AB: processForward doesn't exist!! is processIncomingMessage meant? + void sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients); + + /** + Sends a message to a single client. This is a convenieance method. It calls + sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients) + with a list containing only one client ID. + + To send a message to the admin of the KMessageServer, you can use 0 as clientID, + instead of using the real client ID. + @param msg The message to be sent to the client + @param client The id of the client the message shall be sent to + */ + void sendForward (const QByteArray &msg, Q_UINT32 client); + + /** + Once this function is called no message will be received anymore. + processIncomingMessage() gets delayed until unlock() is called. + + Note that all messages are still received, but their delivery (like + broadcastReceived()) get delayed only. + */ + void lock(); + + /** + Deliver every message that was delayed by lock() and actually deliver + all messages that get received from now on. + */ + void unlock(); + + /** + @return The number of messages that got delayed since lock() was called + */ + unsigned int delayedMessageCount() const; + +signals: + /** + This signal is emitted when the client receives a broadcast message from the + KMessageServer, sent by another client. Connect to this signal to analyse the + received message and do the right reaction. + + senderID contains the ID of the client that sent the broadcast message. You can + use this e.g. to send a reply message to only that client. Or you can use it + to ignore broadcast messages that were sent by yourself: + + \code + void myObject::myBroadcastSlot (const QByteArray &msg, Q_UINT32 senderID) + { + if (senderID == ((KMessageClient *)sender())->id()) + return; + ... + } + \endcode + @param msg The message that has been sent to us + @param senderID The ID of the client which sent the message + */ + void broadcastReceived (const QByteArray &msg, Q_UINT32 senderID); + + /** + This signal is emitted when the client receives a forward message from the + KMessageServer, sent by another client. Connect to this signal to analyse the + received message and do the right reaction. + + senderID contains the ID of the client that sent the broadcast message. You can + use this e.g. to send a reply message to only that client. + + receivers contains the list of the clients that got the message. (If this list + only contains one number, this will be your client ID, and it was exclusivly + sent to you.) + + If you don't want to distinguish between broadcast and forward messages and + treat them the same, you can connect forwardReceived signal to the + broadcastReceived signal. (Yes, that's possible! You can connect a Qt signal to + a Qt signal, and the second one can have less parameters.) + + \code + KMessageClient *client = new KMessageClient (); + connect (client, SIGNAL (forwardReceived (const QByteArray &, Q_UINT32, const QValueList <Q_UINT32>&)), + client, SIGNAL (broadcastReceived (const QByteArray &, Q_UINT32))); + \endcode + + Then connect the broadcast signal to your slot that analyzes the message. + @param msg The message that has been sent to us + @param senderID The ID of the client which sent the message + @param receivers All clients which receive this message + */ + void forwardReceived (const QByteArray &msg, Q_UINT32 senderID, const QValueList <Q_UINT32> &receivers); + + /** + This signal is emitted when the connection to the KMessageServer is broken. + Reasons for this can be: a network error, a server breakdown, or you were just kicked + from the server. + + When this signal is sent, the connection is already lost and the client is unconnected. + You can connect to another server by calling setServer() afterwards. But keep in mind that + some important messages might have vanished. + */ + void connectionBroken (); + + /** + This signal is emitted right before the client disconnects. It can be used + to this store the id of the client which is about to be lost. + */ + void aboutToDisconnect(Q_UINT32 id); + + /** + This signal is emitted when this client becomes the admin client or when it loses + the admin client status. Connect to this signal if you have to do any initialization + or cleanup. + @param isAdmin Whether we are now admin or not + */ + void adminStatusChanged (bool isAdmin); + + /** + This signal is emitted when another client has connected + to the server. Connect to this method if that clients needs initialization. + This should usually only be done in one client, e.g. the admin client. + @param clientID The ID of the client that has newly connectd. + */ + void eventClientConnected (Q_UINT32 clientID); + + /** + This signal is emitted when the server has lost the + connection to one of the clients (This could be because of a bad internet connection + or because the client disconnected on purpose). + @param clientID The ID of the client that has disconnected + @param broken true if it was disconnected because of a network error + */ + void eventClientDisconnected (Q_UINT32 clientID, bool broken); + + /** + This signal is emitted on every message that came from the server. You can connect to this + signal to see the messages directly. They are in the format specified in KMessageServer. + + @param msg The message that has been sent to us + @param unknown True when KMessageClient didn't recognize the message, i.e. it contained an unknown + message ID. If you want to add additional message types to the client, connect to this signal, + and if unknown is true, analyse the message by yourself. If you recognized the message, + set unknown to false (Otherwise a debug message will be printed). + */ + //AB: maybe add a setNoEmit() so that the other signals can be deactivated? + //Could be a performance benefit (note: KMessageClient is a time critical + //class!!!) + void serverMessageReceived (const QByteArray &msg, bool &unknown); + +protected: + /** + This slot is called from processIncomingMessage or + processFirstMessage, depending on whether the client is locked or a delayed + message is still here (see lock) + + It processes the message and analyses it. If it is a broadcast or a forward message from + another client, it emits the signal processBroadcast or processForward accordingly. + + If you want to treat additional server messages, you can overwrite this method. Don't + forget to call processIncomingMessage of your superclass! + + At the moment, the following server messages are interpreted: + + MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST + @param msg The incoming message + */ + + virtual void processMessage (const QByteArray& msg); + +protected slots: + /** + This slot is called from the signal KMessageIO::received whenever a message from the + KMessageServer arrives. + + It processes the message and analyses it. If it is a broadcast or a forward message from + another client, it emits the signal processBroadcast or processForward accordingly. + + If you want to treat additional server messages, you can overwrite this method. Don't + forget to call processIncomingMessage() of your superclass! + + At the moment, the following server messages are interpreted: + + MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST + @param msg The incoming message + */ + virtual void processIncomingMessage (const QByteArray &msg); + + /** + Called from unlock() (using QTimer::singleShot) until all delayed + messages are delivered. + */ + void processFirstMessage(); + + /** + This slot is called from the signal KMessageIO::connectionBroken. + + It deletes the internal KMessageIO object, and resets the client to default + values. To connect again to another server, use setServer. + */ + virtual void removeBrokenConnection (); + void removeBrokenConnection2 (); + +private: + KMessageClientPrivate *d; +}; + +#endif diff --git a/libkdegames/kgame/kmessageio.cpp b/libkdegames/kgame/kmessageio.cpp new file mode 100644 index 00000000..f3353277 --- /dev/null +++ b/libkdegames/kgame/kmessageio.cpp @@ -0,0 +1,482 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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. +*/ + +/* + KMessageIO class and subclasses KMessageSocket and KMessageDirect +*/ + +#include "kmessageio.h" +#include <qsocket.h> +#include <kdebug.h> +#include <kprocess.h> +#include <qfile.h> + +// ----------------------- KMessageIO ------------------------- + +KMessageIO::KMessageIO (QObject *parent, const char *name) + : QObject (parent, name), m_id (0) +{} + +KMessageIO::~KMessageIO () +{} + +void KMessageIO::setId (Q_UINT32 id) +{ + m_id = id; +} + +Q_UINT32 KMessageIO::id () +{ + return m_id; +} + +// ----------------------KMessageSocket ----------------------- + +KMessageSocket::KMessageSocket (QString host, Q_UINT16 port, QObject *parent, +const char *name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->connectToHost (host, port); + initSocket (); +} + +KMessageSocket::KMessageSocket (QHostAddress host, Q_UINT16 port, QObject +*parent, const char *name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->connectToHost (host.toString(), port); + initSocket (); +} + +KMessageSocket::KMessageSocket (QSocket *socket, QObject *parent, const char +*name) + : KMessageIO (parent, name) +{ + mSocket = socket; + initSocket (); +} + +KMessageSocket::KMessageSocket (int socketFD, QObject *parent, const char +*name) + : KMessageIO (parent, name) +{ + mSocket = new QSocket (); + mSocket->setSocket (socketFD); + initSocket (); +} + +KMessageSocket::~KMessageSocket () +{ + delete mSocket; +} + +bool KMessageSocket::isConnected () const +{ + return mSocket->state() == QSocket::Connection; +} + +void KMessageSocket::send (const QByteArray &msg) +{ + QDataStream str (mSocket); + str << Q_UINT8 ('M'); // magic number for begin of message + str.writeBytes (msg.data(), msg.size()); // writes the length (as Q_UINT32) and the data +} + +void KMessageSocket::processNewData () +{ + if (isRecursive) + return; + isRecursive = true; + + QDataStream str (mSocket); + while (mSocket->bytesAvailable() > 0) + { + if (mAwaitingHeader) + { + // Header = magic number + packet length = 5 bytes + if (mSocket->bytesAvailable() < 5) + { + isRecursive = false; + return; + } + + // Read the magic number first. If something unexpected is found, + // start over again, ignoring the data that was read up to then. + + Q_UINT8 v; + str >> v; + if (v != 'M') + { + kdWarning(11001) << k_funcinfo << ": Received unexpected data, magic number wrong!" << endl; + continue; + } + + str >> mNextBlockLength; + mAwaitingHeader = false; + } + else + { + // Data not completely read => wait for more + if (mSocket->bytesAvailable() < (Q_ULONG) mNextBlockLength) + { + isRecursive = false; + return; + } + + QByteArray msg (mNextBlockLength); + str.readRawBytes (msg.data(), mNextBlockLength); + + // send the received message + emit received (msg); + + // Waiting for the header of the next message + mAwaitingHeader = true; + } + } + + isRecursive = false; +} + +void KMessageSocket::initSocket () +{ + connect (mSocket, SIGNAL (error(int)), SIGNAL (connectionBroken())); + connect (mSocket, SIGNAL (connectionClosed()), SIGNAL (connectionBroken())); + connect (mSocket, SIGNAL (readyRead()), SLOT (processNewData())); + mAwaitingHeader = true; + mNextBlockLength = 0; + isRecursive = false; +} + +Q_UINT16 KMessageSocket::peerPort () const +{ + return mSocket->peerPort(); +} + +QString KMessageSocket::peerName () const +{ + return mSocket->peerName(); +} + +// ----------------------KMessageDirect ----------------------- + +KMessageDirect::KMessageDirect (KMessageDirect *partner, QObject *parent, +const char *name) + : KMessageIO (parent, name), mPartner (0) +{ + // 0 as first parameter leaves the object unconnected + if (!partner) + return; + + // Check if the other object is already connected + if (partner && partner->mPartner) + { + kdWarning(11001) << k_funcinfo << ": Object is already connected!" << endl; + return; + } + + // Connect from us to that object + mPartner = partner; + + // Connect the other object to us + partner->mPartner = this; +} + +KMessageDirect::~KMessageDirect () +{ + if (mPartner) + { + mPartner->mPartner = 0; + emit mPartner->connectionBroken(); + } +} + +bool KMessageDirect::isConnected () const +{ + return mPartner != 0; +} + +void KMessageDirect::send (const QByteArray &msg) +{ + if (mPartner) + emit mPartner->received (msg); + else + kdError(11001) << k_funcinfo << ": Not yet connected!" << endl; +} + + +// ----------------------- KMessageProcess --------------------------- + +KMessageProcess::~KMessageProcess() +{ + kdDebug(11001) << "@@@KMessageProcess::Delete process" << endl; + if (mProcess) + { + mProcess->kill(); + delete mProcess; + mProcess=0; + // Remove not send buffers + mQueue.setAutoDelete(true); + mQueue.clear(); + // Maybe todo: delete mSendBuffer + } +} +KMessageProcess::KMessageProcess(QObject *parent, QString file) : KMessageIO(parent,0) +{ + // Start process + kdDebug(11001) << "@@@KMessageProcess::Start process" << endl; + mProcessName=file; + mProcess=new KProcess; + int id=0; + *mProcess << mProcessName << QString("%1").arg(id); + kdDebug(11001) << "@@@KMessageProcess::Init:Id= " << id << endl; + kdDebug(11001) << "@@@KMessgeProcess::Init:Processname: " << mProcessName << endl; + connect(mProcess, SIGNAL(receivedStdout(KProcess *, char *, int )), + this, SLOT(slotReceivedStdout(KProcess *, char * , int ))); + connect(mProcess, SIGNAL(receivedStderr(KProcess *, char *, int )), + this, SLOT(slotReceivedStderr(KProcess *, char * , int ))); + connect(mProcess, SIGNAL(processExited(KProcess *)), + this, SLOT(slotProcessExited(KProcess *))); + connect(mProcess, SIGNAL(wroteStdin(KProcess *)), + this, SLOT(slotWroteStdin(KProcess *))); + mProcess->start(KProcess::NotifyOnExit,KProcess::All); + mSendBuffer=0; + mReceiveCount=0; + mReceiveBuffer.resize(1024); +} +bool KMessageProcess::isConnected() const +{ + kdDebug(11001) << "@@@KMessageProcess::Is conencted" << endl; + if (!mProcess) return false; + return mProcess->isRunning(); +} +void KMessageProcess::send(const QByteArray &msg) +{ + kdDebug(11001) << "@@@KMessageProcess:: SEND("<<msg.size()<<") to process" << endl; + unsigned int size=msg.size()+2*sizeof(long); + + char *tmpbuffer=new char[size]; + long *p1=(long *)tmpbuffer; + long *p2=p1+1; + kdDebug(11001) << "p1="<<p1 << "p2="<< p2 << endl; + memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size()); + *p1=0x4242aeae; + *p2=size; + + QByteArray *buffer=new QByteArray(); + buffer->assign(tmpbuffer,size); + // buffer->duplicate(msg); + mQueue.enqueue(buffer); + writeToProcess(); +} +void KMessageProcess::writeToProcess() +{ + // Previous send ok and item in queue + if (mSendBuffer || mQueue.isEmpty()) return ; + mSendBuffer=mQueue.dequeue(); + if (!mSendBuffer) return ; + + // write it out to the process + // kdDebug(11001) << " @@@@@@ writeToProcess::SEND to process " << mSendBuffer->size() << " BYTE " << endl; + // char *p=mSendBuffer->data(); + // for (int i=0;i<16;i++) printf("%02x ",(unsigned char)(*(p+i)));printf("\n"); + mProcess->writeStdin(mSendBuffer->data(),mSendBuffer->size()); + +} +void KMessageProcess::slotWroteStdin(KProcess * ) +{ + kdDebug(11001) << k_funcinfo << endl; + if (mSendBuffer) + { + delete mSendBuffer; + mSendBuffer=0; + } + writeToProcess(); +} + +void KMessageProcess::slotReceivedStderr(KProcess * proc, char *buffer, int buflen) +{ + int pid=0; + int len; + char *p; + char *pos; +// kdDebug(11001)<<"############# Got stderr " << buflen << " bytes" << endl; + + if (!buffer || buflen==0) return ; + if (proc) pid=proc->pid(); + + + pos=buffer; + do + { + p=(char *)memchr(pos,'\n',buflen); + if (!p) len=buflen; + else len=p-pos; + + QByteArray a; + a.setRawData(pos,len); + QString s(a); + kdDebug(11001) << "PID" <<pid<< ":" << s << endl; + a.resetRawData(pos,len); + if (p) pos=p+1; + buflen-=len+1; + }while(buflen>0); +} + + +void KMessageProcess::slotReceivedStdout(KProcess * , char *buffer, int buflen) +{ + kdDebug(11001) << "$$$$$$ " << k_funcinfo << ": Received " << buflen << " bytes over inter process communication" << endl; + + // TODO Make a plausibility check on buflen to avoid memory overflow + while (mReceiveCount+buflen>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024); + memcpy(mReceiveBuffer.data()+mReceiveCount,buffer,buflen); + mReceiveCount+=buflen; + + // Possbile message + while (mReceiveCount>2*sizeof(long)) + { + long *p1=(long *)mReceiveBuffer.data(); + long *p2=p1+1; + unsigned int len; + if (*p1!=0x4242aeae) + { + kdDebug(11001) << k_funcinfo << ": Cookie error...transmission failure...serious problem..." << endl; +// for (int i=0;i<mReceiveCount;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n"); + } + len=(int)(*p2); + if (len<2*sizeof(long)) + { + kdDebug(11001) << k_funcinfo << ": Message size error" << endl; + break; + } + if (len<=mReceiveCount) + { + kdDebug(11001) << k_funcinfo << ": Got message with len " << len << endl; + + QByteArray msg; + // msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + emit received(msg); + // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + // Shift buffer + if (len<mReceiveCount) + { + memmove(mReceiveBuffer.data(),mReceiveBuffer.data()+len,mReceiveCount-len); + } + mReceiveCount-=len; + } + else break; + } +} + +void KMessageProcess::slotProcessExited(KProcess * /*p*/) +{ + kdDebug(11001) << "Process exited (slot)" << endl; + emit connectionBroken(); + delete mProcess; + mProcess=0; +} + + +// ----------------------- KMessageFilePipe --------------------------- +KMessageFilePipe::KMessageFilePipe(QObject *parent,QFile *readfile,QFile *writefile) : KMessageIO(parent,0) +{ + mReadFile=readfile; + mWriteFile=writefile; + mReceiveCount=0; + mReceiveBuffer.resize(1024); +} + +KMessageFilePipe::~KMessageFilePipe() +{ +} + +bool KMessageFilePipe::isConnected () const +{ + return (mReadFile!=0)&&(mWriteFile!=0); +} + +void KMessageFilePipe::send(const QByteArray &msg) +{ + unsigned int size=msg.size()+2*sizeof(long); + + char *tmpbuffer=new char[size]; + long *p1=(long *)tmpbuffer; + long *p2=p1+1; + memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size()); + *p1=0x4242aeae; + *p2=size; + + QByteArray buffer; + buffer.assign(tmpbuffer,size); + mWriteFile->writeBlock(buffer); + mWriteFile->flush(); + /* + fprintf(stderr,"+++ KMessageFilePipe:: SEND(%d to parent) realsize=%d\n",msg.size(),buffer.size()); + for (int i=0;i<buffer.size();i++) fprintf(stderr,"%02x ",buffer[i]);fprintf(stderr,"\n"); + fflush(stderr); + */ +} + +void KMessageFilePipe::exec() +{ + + // According to BL: Blocking read is ok + // while(mReadFile->atEnd()) { usleep(100); } + + int ch=mReadFile->getch(); + + while (mReceiveCount>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024); + mReceiveBuffer[mReceiveCount]=(char)ch; + mReceiveCount++; + + // Change for message + if (mReceiveCount>=2*sizeof(long)) + { + long *p1=(long *)mReceiveBuffer.data(); + long *p2=p1+1; + unsigned int len; + if (*p1!=0x4242aeae) + { + fprintf(stderr,"KMessageFilePipe::exec:: Cookie error...transmission failure...serious problem...\n"); +// for (int i=0;i<16;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n"); + } + len=(int)(*p2); + if (len==mReceiveCount) + { + //fprintf(stderr,"KMessageFilePipe::exec:: Got Message with len %d\n",len); + + QByteArray msg; + //msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + emit received(msg); + //msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long)); + mReceiveCount=0; + } + } + + + return ; + + +} + +#include "kmessageio.moc" diff --git a/libkdegames/kgame/kmessageio.h b/libkdegames/kgame/kmessageio.h new file mode 100644 index 00000000..37cf35cd --- /dev/null +++ b/libkdegames/kgame/kmessageio.h @@ -0,0 +1,416 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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. +*/ + +/* + KMessageIO class and subclasses KMessageSocket and KMessageDirect +*/ + +#ifndef _KMESSAGEIO_H_ +#define _KMESSAGEIO_H_ + +#include <qcstring.h> +#include <qhostaddress.h> +#include <qobject.h> +#include <qstring.h> +#include <qptrqueue.h> +#include <qfile.h> +#include <kdebug.h> + +class QSocket; +class KProcess; +//class QFile; + + +/** + This abstract base class represents one end of a message connections + between two clients. Each client has one object of a subclass of + KMessageIO. Calling /e send() on one object will emit the signal + /e received() on the other object, and vice versa. + + For each type of connection (TCP/IP socket, COM port, direct connection + within the same class) a subclass of KMessageIO must be defined that + implements the pure virtual methods /e send() and /e isConnected(), + and sends the signals. (See /e KMessageSocket for an example implementation.) + + Two subclasses are already included: /e KMessageSocket (connection using + TCP/IP sockets) and /e KMessageDirect (connection using method calls, both + sides must be within the same process). +*/ + +class KMessageIO : public QObject +{ + Q_OBJECT + +public: + /** + * The usual QObject constructor, does nothing else. + **/ + KMessageIO (QObject *parent = 0, const char *name = 0); + + /** + * The usual destructor, does nothing special. + **/ + ~KMessageIO (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 0;} + + /** + * @return Whether this KMessageIO is a network IO or not. + **/ + //virtual bool isNetwork () const = 0; + virtual bool isNetwork () const + { + kdError(11001) << "Calling PURE virtual isNetwork...BAD" << endl; + return false; + } + + /** + This method returns the status of the object, whether it is already + (or still) connected to another KMessageIO object or not. + + This is a pure virtual method, so it has to be implemented in a subclass + of KMessageIO. + */ + //virtual bool isConnected () const = 0; + virtual bool isConnected () const + { + kdError(11001) << "Calling PURE virtual isConencted...BAD" << endl; + return false; + } + + /** + Sets the ID number of this object. This number can for example be used to + distinguish several objects in a server. + + NOTE: Sometimes it is useful to let two connected KMessageIO objects + have the same ID number. You have to do so yourself, KMessageIO doesn't + change this value on its own! + */ + void setId (Q_UINT32 id); + + /** + Queries the ID of this object. + */ + Q_UINT32 id (); + + /** + @since 3.2 + @return 0 in the default implementation. Reimplemented in @ref KMessageSocket. + */ + virtual Q_UINT16 peerPort () const { return 0; } + + /** + @since 3.2 + @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket + */ + virtual QString peerName () const { return QString::fromLatin1("localhost"); } + + +signals: + /** + This signal is emitted when /e send() on the connected KMessageIO + object is called. The parameter contains the same data array in /e msg + as was used in /e send(). + */ + void received (const QByteArray &msg); + + /** + This signal is emitted when the connection is closed. This can be caused + by a hardware error (e.g. broken internet connection) or because the other + object was killed. + + Note: Sometimes a broken connection can be undetected for a long time, + or may never be detected at all. So don't rely on this signal! + */ + void connectionBroken (); + +public slots: + + /** + This slot sends the data block in /e msg to the connected object, that will + emit /e received(). + + For a concrete class, you have to subclass /e KMessageIO and overwrite this + method. In the subclass, implement this method as an ordinary method, not + as a slot! (Otherwise another slot would be defined. It would work, but uses + more memory and time.) See /e KMessageSocket for an example implementation. + */ + virtual void send (const QByteArray &msg) = 0; + +protected: + Q_UINT32 m_id; +}; + + +/** + This class implements the message communication using a TCP/IP socket. The + object can connect to a server socket, or can use an already connected socket. +*/ + +class KMessageSocket : public KMessageIO +{ + Q_OBJECT + +public: + /** + Connects to a server socket on /e host with /e port. host can be an + numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org") + IP address. You can immediately use the /e sendSystem() and + /e sendBroadcast() methods. The messages are stored and sent to the + receiver after the connection is established. + + If the connection could not be established (e.g. unknown host or no server + socket at this port), the signal /e connectionBroken is emitted. + */ + KMessageSocket (QString host, Q_UINT16 port, QObject *parent = 0, + const char *name = 0); + + /** + Connects to a server socket on /e host with /e port. You can immediately + use the /e sendSystem() and /e sendBroadcast() methods. The messages are + stored and sent to the receiver after the connection is established. + + If the connection could not be established (e.g. unknown host or no server + socket at this port), the signal /e connectionBroken is emitted. + */ + KMessageSocket (QHostAddress host, Q_UINT16 port, QObject *parent = 0, + const char *name = 0); + + /** + Uses /e socket to do the communication. + + The socket should already be connected, or at least be in /e connecting + state. + + Note: The /e socket object is then owned by the /e KMessageSocket object. + So don't use it otherwise any more and don't delete it. It is deleted + together with this KMessageSocket object. (Use 0 as parent for the QSocket + object t ensure it is not deleted.) + */ + KMessageSocket (QSocket *socket, QObject *parent = 0, const char *name = 0); + + /** + Uses the socket specified by the socket descriptor socketFD to do the + communication. The socket must already be connected. + + This constructor can be used with a QServerSocket within the (pure + virtual) method /e newConnection. + + Note: The socket is then owned by the /e KMessageSocket object. So don't + manipulate the socket afterwards, especially don't close it. The socket is + automatically closed when KMessageSocket is deleted. + */ + KMessageSocket (int socketFD, QObject *parent = 0, const char *name = 0); + + /** + Destructor, closes the socket. + */ + ~KMessageSocket (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 1;} + + /** + @since 3.2 + @return The port that this object is connected to. See QSocket::peerPort + */ + virtual Q_UINT16 peerPort () const; + + /** + @since 3.2 + @return The hostname this object is connected to. See QSocket::peerName. + */ + virtual QString peerName () const; + + /** + @return TRUE as this is a network IO. + */ + bool isNetwork() const { return true; } + + /** + Returns true if the socket is in state /e connected. + */ + bool isConnected () const; + + /** + Overwritten slot method from KMessageIO. + + Note: It is not declared as a slot method, since the slot is already + defined in KMessageIO as a virtual method. + */ + void send (const QByteArray &msg); + +protected slots: + virtual void processNewData (); + +protected: + void initSocket (); + QSocket *mSocket; + bool mAwaitingHeader; + Q_UINT32 mNextBlockLength; + + bool isRecursive; // workaround for "bug" in QSocket, Qt 2.2.3 or older +}; + + +/** + This class implements the message communication using function calls + directly. It can only be used when both sides of the message pipe are + within the same process. The communication is very fast. + + To establish a communication, you have to create two instances of + KMessageDirect, the first one with no parameters in the constructor, + the second one with the first as parameter: + + /code + KMessageDirect *peer1, *peer2; + peer1 = new KMessageDirect (); // unconnected + peer2 = new KMessageDirect (peer1); // connect with peer1 + /endcode + + The connection is only closed when one of the instances is deleted. +*/ + +class KMessageDirect : public KMessageIO +{ + Q_OBJECT + +public: + /** + Creates an object and connects it to the object given in the first + parameter. Use 0 as first parameter to create an unconnected object, + that is later connected. + + If that object is already connected, the object remains unconnected. + */ + KMessageDirect (KMessageDirect *partner = 0, QObject *parent = 0, const char +*name = 0); + + /** + Destructor, closes the connection. + */ + ~KMessageDirect (); + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 2;} + + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + Returns true, if the object is connected to another instance. + + If you use the first constructor, the object is unconnected unless another + object is created with this one as parameter. + + The connection can only be closed by deleting one of the objects. + */ + bool isConnected () const; + + /** + Overwritten slot method from KMessageIO. + + Note: It is not declared as a slot method, since the slot is already + defined in KMessageIO as a virtual method. + */ + void send (const QByteArray &msg); + +protected: + KMessageDirect *mPartner; +}; + +class KMessageProcess : public KMessageIO +{ + Q_OBJECT + + public: + KMessageProcess(QObject *parent, QString file); + ~KMessageProcess(); + bool isConnected() const; + void send (const QByteArray &msg); + void writeToProcess(); + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 3;} + + + + public slots: + void slotReceivedStdout(KProcess *proc, char *buffer, int buflen); + void slotReceivedStderr(KProcess *proc, char *buffer, int buflen); + void slotProcessExited(KProcess *p); + void slotWroteStdin(KProcess *p); + + private: + QString mProcessName; + KProcess *mProcess; + QPtrQueue <QByteArray> mQueue; + QByteArray *mSendBuffer; + QByteArray mReceiveBuffer; + unsigned int mReceiveCount; +}; + +class KMessageFilePipe : public KMessageIO +{ + Q_OBJECT + + public: + KMessageFilePipe(QObject *parent,QFile *readFile,QFile *writeFile); + ~KMessageFilePipe(); + bool isConnected() const; + void send (const QByteArray &msg); + void exec(); + + /** + @return FALSE as this is no network IO. + */ + bool isNetwork() const { return false; } + + /** + * The runtime idendifcation + */ + virtual int rtti() const {return 4;} + + + + private: + QFile *mReadFile; + QFile *mWriteFile; + QByteArray mReceiveBuffer; + unsigned int mReceiveCount; +}; + +#endif diff --git a/libkdegames/kgame/kmessageserver.cpp b/libkdegames/kgame/kmessageserver.cpp new file mode 100644 index 00000000..e857ea31 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.cpp @@ -0,0 +1,515 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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 <qiodevice.h> +#include <qbuffer.h> +#include <qptrlist.h> +#include <qptrqueue.h> +#include <qtimer.h> +#include <qvaluelist.h> + +#include <kdebug.h> + +#include "kmessageio.h" +#include "kmessageserver.h" + +// --------------- internal class KMessageServerSocket + +KMessageServerSocket::KMessageServerSocket (Q_UINT16 port, QObject *parent) + : QServerSocket (port, 0, parent) +{ +} + +KMessageServerSocket::~KMessageServerSocket () +{ +} + +void KMessageServerSocket::newConnection (int socket) +{ + emit newClientConnected (new KMessageSocket (socket)); +} + +// ---------------- class for storing an incoming message + +class MessageBuffer +{ + public: + MessageBuffer (Q_UINT32 clientID, const QByteArray &messageData) + : id (clientID), data (messageData) { } + ~MessageBuffer () {} + Q_UINT32 id; + QByteArray data; +}; + +// ---------------- KMessageServer's private class + +class KMessageServerPrivate +{ +public: + KMessageServerPrivate() + : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (0) + { + mClientList.setAutoDelete (true); + mMessageQueue.setAutoDelete (true); + } + + int mMaxClients; + int mGameId; + Q_UINT16 mCookie; + Q_UINT32 mUniqueClientNumber; + Q_UINT32 mAdminID; + + KMessageServerSocket* mServerSocket; + + QPtrList <KMessageIO> mClientList; + QPtrQueue <MessageBuffer> mMessageQueue; + QTimer mTimer; + bool mIsRecursive; +}; + + +// ------------------ KMessageServer + +KMessageServer::KMessageServer (Q_UINT16 cookie,QObject* parent) + : QObject(parent, 0) +{ + d = new KMessageServerPrivate; + d->mIsRecursive=false; + d->mCookie=cookie; + connect (&(d->mTimer), SIGNAL (timeout()), + this, SLOT (processOneMessage())); + kdDebug(11001) << "CREATE(KMessageServer=" + << this + << ") cookie=" + << d->mCookie + << " sizeof(this)=" + << sizeof(KMessageServer) + << endl; +} + +KMessageServer::~KMessageServer() +{ + kdDebug(11001) << k_funcinfo << "this=" << this << endl; + Debug(); + stopNetwork(); + deleteClients(); + delete d; + kdDebug(11001) << k_funcinfo << " done" << endl; +} + +//------------------------------------- TCP/IP server stuff + +bool KMessageServer::initNetwork (Q_UINT16 port) +{ + kdDebug(11001) << k_funcinfo << endl; + + if (d->mServerSocket) + { + kdDebug (11001) << k_funcinfo << ": We were already offering connections!" << endl; + delete d->mServerSocket; + } + + d->mServerSocket = new KMessageServerSocket (port); + d->mIsRecursive = false; + + if (!d->mServerSocket || !d->mServerSocket->ok()) + { + kdError(11001) << k_funcinfo << ": Serversocket::ok() == false" << endl; + delete d->mServerSocket; + d->mServerSocket=0; + return false; + } + + kdDebug (11001) << k_funcinfo << ": Now listening to port " + << d->mServerSocket->port() << endl; + connect (d->mServerSocket, SIGNAL (newClientConnected (KMessageIO*)), + this, SLOT (addClient (KMessageIO*))); + return true; +} + +Q_UINT16 KMessageServer::serverPort () const +{ + if (d->mServerSocket) + return d->mServerSocket->port(); + else + return 0; +} + +void KMessageServer::stopNetwork() +{ + if (d->mServerSocket) + { + delete d->mServerSocket; + d->mServerSocket = 0; + } +} + +bool KMessageServer::isOfferingConnections() const +{ + return d->mServerSocket != 0; +} + +//----------------------------------------------- adding / removing clients + +void KMessageServer::addClient (KMessageIO* client) +{ + QByteArray msg; + + // maximum number of clients reached? + if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount()) + { + kdError (11001) << k_funcinfo << ": Maximum number of clients reached!" << endl; + return; + } + + // give it a unique ID + client->setId (uniqueClientNumber()); + kdDebug (11001) << k_funcinfo << ": " << client->id() << endl; + + // connect its signals + connect (client, SIGNAL (connectionBroken()), + this, SLOT (removeBrokenClient())); + connect (client, SIGNAL (received (const QByteArray &)), + this, SLOT (getReceivedMessage (const QByteArray &))); + + // Tell everyone about the new guest + // Note: The new client doesn't get this message! + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_CONNECTED) << client->id(); + broadcastMessage (msg); + + // add to our list + d->mClientList.append (client); + + // tell it its ID + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_ID) << client->id(); + client->send (msg); + + // Give it the complete list of client IDs + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + client->send (msg); + + + if (clientCount() == 1) + { + // if it is the first client, it becomes the admin + setAdmin (client->id()); + } + else + { + // otherwise tell it who is the admin + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID(); + client->send (msg); + } + + emit clientConnected (client); +} + +void KMessageServer::removeClient (KMessageIO* client, bool broken) +{ + Q_UINT32 clientID = client->id(); + if (!d->mClientList.removeRef (client)) + { + kdError(11001) << k_funcinfo << ": Deleting client that wasn't added before!" << endl; + return; + } + + // tell everyone about the removed client + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (Q_INT8)broken; + broadcastMessage (msg); + + // If it was the admin, select a new admin. + if (clientID == adminID()) + { + if (!d->mClientList.isEmpty()) + setAdmin (d->mClientList.first()->id()); + else + setAdmin (0); + } +} + +void KMessageServer::deleteClients() +{ + d->mClientList.clear(); + d->mAdminID = 0; +} + +void KMessageServer::removeBrokenClient () +{ + if (!sender()->inherits ("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": sender of the signal was not a KMessageIO object!" << endl; + return; + } + + KMessageIO *client = (KMessageIO *) sender(); + + emit connectionLost (client); + removeClient (client, true); +} + + +void KMessageServer::setMaxClients(int c) +{ + d->mMaxClients = c; +} + +int KMessageServer::maxClients() const +{ + return d->mMaxClients; +} + +int KMessageServer::clientCount() const +{ + return d->mClientList.count(); +} + +QValueList <Q_UINT32> KMessageServer::clientIDs () const +{ + QValueList <Q_UINT32> list; + for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter) + list.append ((*iter)->id()); + return list; +} + +KMessageIO* KMessageServer::findClient (Q_UINT32 no) const +{ + if (no == 0) + no = d->mAdminID; + + QPtrListIterator <KMessageIO> iter (d->mClientList); + while (*iter) + { + if ((*iter)->id() == no) + return (*iter); + ++iter; + } + return 0; +} + +Q_UINT32 KMessageServer::adminID () const +{ + return d->mAdminID; +} + +void KMessageServer::setAdmin (Q_UINT32 adminID) +{ + // Trying to set the the client that is already admin => nothing to do + if (adminID == d->mAdminID) + return; + + if (adminID > 0 && findClient (adminID) == 0) + { + kdWarning (11001) << "Trying to set a new admin that doesn't exist!" << endl; + return; + } + + d->mAdminID = adminID; + + QByteArray msg; + QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID; + + // Tell everyone about the new master + broadcastMessage (msg); +} + + +//------------------------------------------- ID stuff + +Q_UINT32 KMessageServer::uniqueClientNumber() const +{ + return d->mUniqueClientNumber++; +} + +// --------------------- Messages --------------------------- + +void KMessageServer::broadcastMessage (const QByteArray &msg) +{ + for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter) + (*iter)->send (msg); +} + +void KMessageServer::sendMessage (Q_UINT32 id, const QByteArray &msg) +{ + KMessageIO *client = findClient (id); + if (client) + client->send (msg); +} + +void KMessageServer::sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg) +{ + for (QValueListConstIterator <Q_UINT32> iter = ids.begin(); iter != ids.end(); ++iter) + sendMessage (*iter, msg); +} + +void KMessageServer::getReceivedMessage (const QByteArray &msg) +{ + if (!sender() || !sender()->inherits("KMessageIO")) + { + kdError (11001) << k_funcinfo << ": slot was not called from KMessageIO!" << endl; + return; + } + //kdDebug(11001) << k_funcinfo << ": size=" << msg.size() << endl; + KMessageIO *client = (KMessageIO *) sender(); + Q_UINT32 clientID = client->id(); + + //QByteArray *ta=new QByteArray; + //ta->duplicate(msg); + //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta)); + + + d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg)); + if (!d->mTimer.isActive()) + d->mTimer.start(0); // AB: should be , TRUE i guess +} + +void KMessageServer::processOneMessage () +{ + // This shouldn't happen, since the timer should be stopped before. But only to be sure! + if (d->mMessageQueue.isEmpty()) + { + d->mTimer.stop(); + return; + } + if (d->mIsRecursive) + { + return; + } + d->mIsRecursive = true; + + MessageBuffer *msg_buf = d->mMessageQueue.head(); + + Q_UINT32 clientID = msg_buf->id; + QBuffer in_buffer (msg_buf->data); + in_buffer.open (IO_ReadOnly); + QDataStream in_stream (&in_buffer); + + QByteArray out_msg; + QBuffer out_buffer (out_msg); + out_buffer.open (IO_WriteOnly); + QDataStream out_stream (&out_buffer); + + bool unknown = false; + + QByteArray ttt=in_buffer.buffer(); + Q_UINT32 messageID; + in_stream >> messageID; + //kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl; + switch (messageID) + { + case REQ_BROADCAST: + out_stream << Q_UINT32 (MSG_BROADCAST) << clientID; + // FIXME, compiler bug? + // this should be okay, since QBuffer is subclass of QIODevice! : + // out_buffer.writeBlock (in_buffer.readAll()); + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + broadcastMessage (out_msg); + break; + + case REQ_FORWARD: + { + QValueList <Q_UINT32> clients; + in_stream >> clients; + out_stream << Q_UINT32 (MSG_FORWARD) << clientID << clients; + // see above! + out_buffer.QIODevice::writeBlock (in_buffer.readAll()); + sendMessage (clients, out_msg); + } + break; + + case REQ_CLIENT_ID: + out_stream << Q_UINT32 (ANS_CLIENT_ID) << clientID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_ID: + out_stream << Q_UINT32 (ANS_ADMIN_ID) << d->mAdminID; + sendMessage (clientID, out_msg); + break; + + case REQ_ADMIN_CHANGE: + if (clientID == d->mAdminID) + { + Q_UINT32 newAdmin; + in_stream >> newAdmin; + setAdmin (newAdmin); + } + break; + + case REQ_REMOVE_CLIENT: + if (clientID == d->mAdminID) + { + QValueList <Q_UINT32> client_list; + in_stream >> client_list; + for (QValueListIterator <Q_UINT32> iter = client_list.begin(); iter != client_list.end(); ++iter) + { + KMessageIO *client = findClient (*iter); + if (client) + removeClient (client, false); + else + kdWarning (11001) << k_funcinfo << ": removing non-existing clientID" << endl; + } + } + break; + + case REQ_MAX_NUM_CLIENTS: + if (clientID == d->mAdminID) + { + Q_INT32 maximum_clients; + in_stream >> maximum_clients; + setMaxClients (maximum_clients); + } + break; + + case REQ_CLIENT_LIST: + { + out_stream << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs(); + sendMessage (clientID, out_msg); + } + break; + + default: + unknown = true; + } + + // check if all the data has been used + if (!unknown && !in_buffer.atEnd()) + kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl; + + emit messageReceived (msg_buf->data, clientID, unknown); + + if (unknown) + kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl; + + // remove the message, since we are ready with it + d->mMessageQueue.remove(); + if (d->mMessageQueue.isEmpty()) + d->mTimer.stop(); + d->mIsRecursive = false; +} + +void KMessageServer::Debug() +{ + kdDebug(11001) << "------------------ KMESSAGESERVER -----------------------" << endl; + kdDebug(11001) << "MaxClients : " << maxClients() << endl; + kdDebug(11001) << "NoOfClients : " << clientCount() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +#include "kmessageserver.moc" diff --git a/libkdegames/kgame/kmessageserver.h b/libkdegames/kgame/kmessageserver.h new file mode 100644 index 00000000..0049a2e7 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.h @@ -0,0 +1,492 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Burkhard Lehner (Burkhard.Lehner@gmx.de) + + 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 __KMESSAGESERVER_H__ +#define __KMESSAGESERVER_H__ + +#include <qobject.h> +#include <qserversocket.h> +#include <qstring.h> +#include <qvaluelist.h> + +class KMessageIO; +class KMessageServerPrivate; + +/** + @short A server for message sending and broadcasting, using TCP/IP connections. + + An object of this class listens for incoming connections via TCP/IP sockets and + creates KMessageSocket objects for every established connection. It receives + messages from the "clients", analyses them and processes an appropriate + reaction. + + You can also use other KMessageIO objects with KMessageServer, not only + TCP/IP socket based ones. Use addClient to connect via an object of any + KMessageIO subclass. (For clients within the same process, you can e.g. use + KMessageDirect.) This object already has to be connected. + + The messages are always packages of an arbitrary length. The format of the messages + is given below. All the data is stored and received with QDataStream, to be + platform independant. + + Setting up a KMessageServer can be done like this: + + \code + KMessageServer *server = new KMessageServer (); + server->initNetwork (TCP/IP-Portnumber); + \endcode + + Usually that is everything you will do. There are a lot of public methods to + administrate the object (maximum number of clients, finding clients, removing + clients, setting the admin client, ...), but this functionality can also + be done by messages from the clients. So you can administrate the object completely + on remote. + + If you want to extend the Server for your own needs (e.g. additional message types), + you can either create a subclass and overwrite the method processOneMessage. + (But don't forget to call the method of the superclass!) Or you can connect to + the signal messageReceived, and analyse the messages there. + + Every client has a unique ID, so that messages can be sent to another dedicated + client or a list of clients. + + One of the clients (the admin) has a special administration right. Some of the + administration messages can only be used with him. The admin can give the admin + status to another client. You can send a message to the admin by using clientID 0. + This is always interpreted as the admin client, independant of its real clientID. + + Here is a list of the messages the KMessageServer understands: + << means, the value is inserted into the QByteArray using QDataStream. The + messageIDs (REQ_BROADCAST, ...) are of type Q_UINT32. + + - QByteArray << static_cast<Q_UINT32>( REQ_BROADCAST ) << raw_data + + When the server receives this message, it sends the following message to + ALL connected clients (a broadcast), where the raw_data is left unchanged: + QByteArray << static_cast <Q_UINT32>( MSG_BROADCAST ) << clientID << raw_data + Q_UINT32 clientID; // the ID of the client that sent the broadcast request + + - QByteArray << static_cast<Q_UINT32>( REQ_FORWARD ) << client_list << raw_data + QValueList <Q_UINT32> client_list; // list of receivers + + When the server receives this message, it sends the following message to + the clients in client_list: + QByteArray << static_cast<Q_UINT32>( MSG_FORWARD ) << senderID << client_list << raw_data + Q_UINT32 senderID; // the sender of the forward request + QValueList <Q_UINT32> client_list; // a copy of the receiver list + + Note: Every client receives the message as many times as he is in the client_list. + Note: Since the client_list is sent to all the clients, every client can see who else + got the message. If you want to prevent this, send a single REQ_FORWARD + message for every receiver. + + - QByteArray << static_cast<Q_UINT32>( REQ_CLIENT_ID ) + + When the server receives this message, it sends the following message to + the asking client: + QByteArray << static_cast<Q_UINT32>( ANS_CLIENT_ID ) << clientID + Q_UINT32 clientID; // The ID of the client who asked for it + + Note: This answer is also automatically sent to a new connected client, so that he + can store his ID. The ID of a client doesn't change during his lifetime, and is + unique for this KMessageServer. + + - QByteArray << static_cast<Q_UINT32>( REQ_ADMIN_ID ) + + When the server receives this message, it sends the following message to + the asking client: + QByteArray << ANS_ADMIN_ID << adminID + Q_UINT32 adminID; // The ID of the admin + + Note: This answer is also automatically sent to a new connected client, so that he + can see if he is the admin or not. It will also be sent to all connected clients + when a new admin is set (see REQ_ADMIN_CHANGE). + + - QByteArray << static_cast<Q_UINT32>( REQ_ADMIN_CHANGE ) << new_admin + Q_UINT32 new_admin; // the ID of the new admin, or 0 for no admin + + When the server receives this message, it sets the admin to the new ID. If no client + with that ID exists, nothing happens. With new_admin == 0 no client is a admin. + ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE! + + Note: The server sends a ANS_ADMIN_ID message to every connected client. + + - QByteArray << static_cast<Q_UINT32>( REQ_REMOVE_CLIENT ) << client_list + QValueList <Q_UINT32> client_list; // The list of clients to be removed + + When the server receives this message, it removes the clients with the ids stored in + client_list, disconnecting the connection to them. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If one of the clients is the admin himself, he will also be deleted. + Another client (if any left) will become the new admin. + + - QByteArray << static_cast<Q_UINT32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients + Q_INT32 maximum_clients; // The maximum of clients connected, or infinite if -1 + + When the server receives this message, it limits the number of clients to the number given, + or sets it unlimited for maximum_clients == -1. + ONLY THE ADMIN CAN USE THIS MESSAGE! + + Note: If there are already more clients, they are not affected. It only prevents new Clients + to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT) + + - QByteArray << static_cast<Q_UINT32>( REQ_CLIENT_LIST ) + + When the server receives this message, it answers by sending a list of IDs of all the clients + that are connected at the moment. So it sends the following message to the asking client: + QByteArray << static_cast<Q_UINT32>( ANS_CLIENT_LIST ) << clientList + QValueList <Q_UINT32> clientList; // The IDs of the connected clients + + Note: This message is also sent to every new connected client, so that he knows the other + clients. + + There are two more messages that are sent from the server to the every client automatically + when a new client connects or a connection to a client is lost: + + QByteArray << static_cast<Q_UINT32>( EVNT_CLIENT_CONNECTED ) << clientID; + Q_UINT32 clientID; // the ID of the new connected client + + QByteArray << static_cast<Q_UINT32>( EVNT_CLIENT_DISCONNECTED ) << clientID; + Q_UINT32 clientID; // the ID of the client that lost the connection + Q_UINT8 broken; // 1 if the network connection was closed, 0 if it was disconnected + // on purpose + + + @author Andreas Beckermann <b_mann@gmx.de>, Burkhard Lehner <Burkhard.Lehner@gmx.de> + @version $Id$ +*/ +class KMessageServer : public QObject +{ + Q_OBJECT + +public: + /** + MessageIDs for messages from a client to the message server. + */ + enum { + REQ_BROADCAST = 1, + REQ_FORWARD, + REQ_CLIENT_ID, + REQ_ADMIN_ID, + REQ_ADMIN_CHANGE, + REQ_REMOVE_CLIENT, + REQ_MAX_NUM_CLIENTS, + REQ_CLIENT_LIST, + REQ_MAX_REQ = 0xffff }; + + /** + * MessageIDs for messages from the message server to a client. + **/ + enum { + MSG_BROADCAST = 101, + MSG_FORWARD, + ANS_CLIENT_ID, + ANS_ADMIN_ID, + ANS_CLIENT_LIST, + EVNT_CLIENT_CONNECTED, + EVNT_CLIENT_DISCONNECTED, + EVNT_MAX_EVNT = 0xffff + }; + + /** + * Create a KGameNetwork object + **/ + KMessageServer(Q_UINT16 cookie = 42, QObject* parent = 0); + + ~KMessageServer(); + + /** + * Gives debug output of the game status + **/ + virtual void Debug(); + +//---------------------------------- TCP/IP server stuff + + /** + * Starts the Communication server to listen for incoming TCP/IP connections. + * + * @param port The port on which the service is offered, or 0 to let the + * system pick a free port + * @return true if it worked + */ + bool initNetwork (Q_UINT16 port = 0); + + /** + * Returns the TCP/IP port number we are listening to for incoming connections. + * (This has to be known by other clients so that they can connect to us. It's + * especially necessary if you used 0 as port number in initNetwork(). + * @return the port number + **/ + Q_UINT16 serverPort () const; + + /** + * Stops listening for connections. The already running connections are + * not affected. + * To listen for connections again call initNetwork again. + **/ + void stopNetwork(); + + /** + * Are we still offer offering server connections? + * @return true, if we are still listening to connections requests + **/ + bool isOfferingConnections() const; + +//---------------------------------- adding / removing clients + +public slots: + /** + * Adds a new @ref KMessageIO object to the communication server. This "client" + * gets a unique ID. + * + * This slot method is automatically called for any incoming TCP/IP + * connection. You can use it to add other types of connections, e.g. + * local connections (KMessageDirect) to the server manually. + * + * NOTE: The @ref KMessageIO object gets owned by the KMessageServer, + * so don't delete or manipulate it afterwards. It is automatically deleted + * when the connection is broken or the communication server is deleted. + * So, add a @ref KMessageIO object to just ONE KMessageServer. + **/ + void addClient (KMessageIO *); + + /** + * Removes the KMessageIO object from the client list and deletes it. + * This destroys the connection, if it already was up. + * Does NOT emit connectionLost. + * Sends an info message to the other clients, that contains the ID of + * the removed client and the value of the parameter broken. + * + * @param io the object to delete and to remove from the client list + * @param broken true if the client has lost connection + * Mostly used internally. You will probably not need this. + **/ + void removeClient (KMessageIO *io, bool broken); + + /** + Deletes all connections to the clients. + */ + void deleteClients(); + +private slots: + /** + * Removes the sender object of the signal that called this slot. It is + * automatically connected to @ref KMessageIO::connectionBroken. + * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object. + * Don't call it directly! + **/ + void removeBrokenClient (); + +public: + /** + * sets the maximum number of clients which can connect. + * If this number is reached, no more clients can be added. + * Setting this number to -1 means unlimited number of clients. + * + * NOTE: Existing connections are not affected. + * So, clientCount > maxClients is possible, if there were already + * more clients than allowed before reducing this value. + * + * @param maxnumber the number of clients + **/ + void setMaxClients(int maxnumber); + + /** + * returns the maximum number of clients + * + * @return the number of clients + **/ + int maxClients() const; + + /** + * returns the current number of connected clients. + * + * @return the number of clients + **/ + int clientCount() const; + + /** + * returns a list of the unique IDs of all clients. + **/ + QValueList <Q_UINT32> clientIDs() const; + + /** + * Find the @ref KMessageIO object to the given client number. + * @param no the client number to look for, or 0 to look for the admin + * @return address of the client, or 0 if no client with that number exists + **/ + KMessageIO *findClient (Q_UINT32 no) const; + + /** + * Returns the clientID of the admin, if there is a admin, 0 otherwise. + * + * NOTE: Most often you don't need to know that id, since you can + * use clientID 0 to specify the admin. + **/ + Q_UINT32 adminID() const; + + /** + * Sets the admin to a new client with the given ID. + * The old admin (if existed) and the new admin will get the ANS_ADMIN message. + * If you use 0 as new adminID, no client will be admin. + **/ + void setAdmin (Q_UINT32 adminID); + + +//------------------------------ ID stuff + + /* + * The unique ID of this game + * + * @return int id + **/ +// int gameId() const; + + /* + * Application cookie. this idendifies the game application. It + * help to distinguish between e.g. KPoker and KWin4 + * + * @return the application cookie + **/ +// int cookie() const; + +//------------------------------ Message stuff + +public: + /** + * Sends a message to all connected clients. + * The message is NOT translated in any way. This method calls + * @ref KMessageIO::send for every client added. + **/ + virtual void broadcastMessage (const QByteArray &msg); + + /** + * Sends a message to a single client with the given ID. + * The message is NOT translated in any way. + * If no client with the given id exists, nothing is done. + * This is just a convenience method. You could also call + * @ref findClient (id)->send(msg) manually, but this method checks for + * errors. + **/ + virtual void sendMessage (Q_UINT32 id, const QByteArray &msg); + + /** + * Sends a message to a list of clients. Their ID is given in ids. If + * a client id is given more than once in the list, the message is also + * sent several times to that client. + * This is just a convenience method. You could also iterate over the + * list of IDs. + **/ + virtual void sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg); + +protected slots: + /** + * This slot receives all the messages from the @ref KMessageIO::received signals. + * It stores the messages in a queue. The messages are later taken out of the queue + * by @ref getReceivedMessage. + * + * NOTE: It is important that this slot may only be called from the signal + * @ref KMessageIO::received, since the sender() object is used to find out + * the client that sent the message! + **/ + virtual void getReceivedMessage (const QByteArray &msg); + + /** + * This slot is called whenever there are elements in the message queue. This queue + * is filled by @ref getReceivedMessage. + * This slot takes one message out of the queue and analyses processes it, + * if it recognizes it. (See message types in the description of the class.) + * After that, the signal @ref messageReceived is emitted. Connect to that signal if + * you want to process other types of messages. + **/ + virtual void processOneMessage (); + +//---------------------------- Signals + +signals: + /** + * A new client connected to the game + * @param client the client object that connected + **/ + void clientConnected (KMessageIO *client); + + /** + * A network connection got broken. Note that the client will automatically get deleted + * after this signal is emitted. The signal is not emitted when the client was removed + * regularly. + * + * @param client the client which left the game + **/ + void connectionLost (KMessageIO *client); + + /** + * This signal is always emitted when a message from a client is received. + * + * You can use this signal to extend the communication server without subclassing. + * Just connect to this signal and analyse the message, if unknown is true. + * If you recognize a message and process it, set unknown to false, otherwise + * a warning message is printed. + * + * @param data the message data + * @param clientID the ID of the KMessageIO object that received the message + * @param unknown true, if the message type is not known by the KMessageServer + **/ + void messageReceived (const QByteArray &data, Q_UINT32 clientID, bool &unknown); + +protected: + /** + * @return A unique number which can be used as the id of a @ref KMessageIO. It is + * incremented after every call so if you need the id twice you have to save + * it anywhere. It's currently used to initialize newly connected clints only. + **/ + Q_UINT32 uniqueClientNumber() const; + +private: + KMessageServerPrivate* d; +}; + + +/** + Internal class of KMessageServer. Creates a server socket and waits for + connections. + + NOTE: This has to be here in the header file, because it is a subclass from + QObject and has to go through the moc. + + @short An internal class for KServerSocket + @author Burkhard Lehner <Burkhard.Lehner@gmx.de> +*/ +class KMessageServerSocket : public QServerSocket +{ + Q_OBJECT + +public: + KMessageServerSocket (Q_UINT16 port, QObject *parent = 0); + ~KMessageServerSocket (); + + void newConnection (int socket); + +signals: + void newClientConnected (KMessageIO *client); +}; + + + +#endif diff --git a/libkdegames/kgame/kmessageserver.png b/libkdegames/kgame/kmessageserver.png Binary files differnew file mode 100644 index 00000000..07fba6c5 --- /dev/null +++ b/libkdegames/kgame/kmessageserver.png diff --git a/libkdegames/kgame/kplayer.cpp b/libkdegames/kgame/kplayer.cpp new file mode 100644 index 00000000..0f8ea184 --- /dev/null +++ b/libkdegames/kgame/kplayer.cpp @@ -0,0 +1,446 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + + +#include "kgame.h" +#include "kgameio.h" +#include "kplayer.h" +#include "kgamemessage.h" +#include "kgamepropertyhandler.h" + +#include <kdebug.h> +#include <klocale.h> + +#include <qbuffer.h> + +#include <stdio.h> +#include <assert.h> + +#define KPLAYER_LOAD_COOKIE 7285 + +class KPlayerPrivate +{ +public: + KPlayerPrivate() + { + mNetworkPlayer = 0; + } + + Q_UINT32 mId; + bool mVirtual; // virtual player + int mPriority; // tag for replacement + + KPlayer* mNetworkPlayer; // replacement player + + KGamePropertyHandler mProperties; + +// Playerdata + KGamePropertyQString mName; + KGamePropertyQString mGroup; +}; + +KPlayer::KPlayer() : QObject(0,0) +{ + init(); +} + +KPlayer::KPlayer(KGame* game) : QObject(0, 0) +{ + init(); + game->addPlayer(this); +} + +void KPlayer::init() +{ +// note that NO KGame object exists here! so we cannot use KGameProperty::send! + kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)="<<sizeof(KPlayer) << endl; + kdDebug(11001) << "sizeof(m_Group)="<<sizeof(d->mGroup)<<endl; + d = new KPlayerPrivate; + + d->mProperties.registerHandler(KGameMessage::IdPlayerProperty, + this,SLOT(sendProperty(int, QDataStream&, bool*)), + SLOT(emitSignal(KGamePropertyBase *))); + d->mVirtual=false; + mActive=true; + mGame=0; + d->mId=0; // "0" is always an invalid ID! + d->mPriority=0; + // I guess we cannot translate the group otherwise no + // international conenctions are possible + + mUserId.registerData(KGamePropertyBase::IdUserId, this, i18n("UserId")); + mUserId.setLocal(0); + d->mGroup.registerData(KGamePropertyBase::IdGroup, this, i18n("Group")); + d->mGroup.setLocal(i18n("default")); + d->mName.registerData(KGamePropertyBase::IdName, this, i18n("Name")); + d->mName.setLocal(i18n("default")); + + mAsyncInput.registerData(KGamePropertyBase::IdAsyncInput, this, i18n("AsyncInput")); + mAsyncInput.setLocal(false); + mMyTurn.registerData(KGamePropertyBase::IdTurn, this, i18n("myTurn")); + mMyTurn.setLocal(false); + mMyTurn.setEmittingSignal(true); + mMyTurn.setOptimized(false); +} + +KPlayer::~KPlayer() +{ + kdDebug(11001) << k_funcinfo << ": this=" << this <<", id=" << this->id() << endl; + + // Delete IODevices + KGameIO *input; + while((input=mInputList.first())) + { + delete input; + } + if (game()) + { + game()->playerDeleted(this); + } + +// note: mProperties does not use autoDelete or so - user must delete objects +// himself + d->mProperties.clear(); + delete d; +// kdDebug(11001) << k_funcinfo << " done" << endl; +} + +bool KPlayer::forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver,Q_UINT32 sender) +{ + if (!isActive()) + { + return false; + } + if (!game()) + { + return false; + } + kdDebug(11001) << k_funcinfo << ": to game sender="<<sender<<"" << "recv="<<receiver <<"msgid="<<msgid << endl; + return game()->sendSystemMessage(msg,msgid,receiver,sender); +} + +bool KPlayer::forwardInput(QDataStream &msg,bool transmit,Q_UINT32 sender) +{ + if (!isActive()) + { + return false; + } + if (!game()) + { + return false; + } + + kdDebug(11001) << k_funcinfo << ": to game playerInput(sender="<<sender<<")" << endl; + if (!asyncInput() && !myTurn()) + { + kdDebug(11001) << k_funcinfo << ": rejected cause it is not our turn" << endl; + return false; + } + + // AB: I hope I remember the usage correctly: + // this function is called twice (on sender side) - once with transmit = true + // where it sends the input to the comserver and once with transmit = false + // where it really looks at the input + if (transmit) + { + kdDebug(11001) << "indirect playerInput" << endl; + return game()->sendPlayerInput(msg,this,sender); + } + else + { + kdDebug(11001) << "direct playerInput" << endl; + return game()->systemPlayerInput(msg,this,sender); + } +} + +void KPlayer::setId(Q_UINT32 newid) +{ + // Needs to be after the sendProcess + d->mId=newid; +} + + +void KPlayer::setGroup(const QString& group) +{ d->mGroup = group; } + +const QString& KPlayer::group() const +{ return d->mGroup.value(); } + +void KPlayer::setName(const QString& name) +{ d->mName = name; } + +const QString& KPlayer::name() const +{ return d->mName.value(); } + +Q_UINT32 KPlayer::id() const +{ return d->mId; } + +KGamePropertyHandler * KPlayer::dataHandler() +{ return &d->mProperties; } + +void KPlayer::setVirtual(bool v) +{ d->mVirtual = v; } + +bool KPlayer::isVirtual() const +{ return d->mVirtual;} + +void KPlayer::setNetworkPlayer(KPlayer* p) +{ d->mNetworkPlayer = p; } + +KPlayer* KPlayer::networkPlayer() const +{ return d->mNetworkPlayer; } + +int KPlayer::networkPriority() const +{ return d->mPriority; } + +void KPlayer::setNetworkPriority(int p) +{ d->mPriority = p; } + +bool KPlayer::addGameIO(KGameIO *input) +{ + if (!input) + { + return false; + } + mInputList.append(input); + input->initIO(this); // set player and init device + return true; +} + +// input=0, remove all +bool KPlayer::removeGameIO(KGameIO *targetinput,bool deleteit) +{ + kdDebug(11001) << k_funcinfo << ": " << targetinput << " delete=" << deleteit<< endl; + bool result=true; + if (!targetinput) // delete all + { + KGameIO *input; + while((input=mInputList.first())) + { + if (input) removeGameIO(input,deleteit); + } + } + else + { +// kdDebug(11001) << "remove IO " << targetinput << endl; + if (deleteit) + { + delete targetinput; + } + else + { + targetinput->setPlayer(0); + result=mInputList.remove(targetinput); + } + } + return result; +} + +KGameIO * KPlayer::findRttiIO(int rtti) const +{ + QPtrListIterator<KGameIO> it(mInputList); + while (it.current()) + { + if (it.current()->rtti() == rtti) + { + return it.current(); + } + ++it; + } + return 0; +} + +int KPlayer::calcIOValue() +{ + int value=0; + QPtrListIterator<KGameIO> it(mInputList); + while (it.current()) + { + value|=it.current()->rtti(); + ++it; + } + return value; +} + +bool KPlayer::setTurn(bool b, bool exclusive) +{ + kdDebug(11001) << k_funcinfo << ": " << id() << " (" << this << ") to " << b << endl; + if (!isActive()) + { + return false; + } + + // if we get to do an exclusive turn all other players are disallowed + if (exclusive && b && game()) + { + KPlayer *player; + KGame::KGamePlayerList *list=game()->playerList(); + for ( player=list->first(); player != 0; player=list->next() ) + { + if (player==this) + { + continue; + } + player->setTurn(false,false); + } + } + + // Return if nothing changed + mMyTurn = b; + + return true; +} + +bool KPlayer::load(QDataStream &stream) +{ + Q_INT32 id,priority; + stream >> id >> priority; + setId(id); + setNetworkPriority(priority); + + // Load Player Data + //FIXME: maybe set all properties setEmitSignal(false) before? + d->mProperties.load(stream); + + Q_INT16 cookie; + stream >> cookie; + if (cookie==KPLAYER_LOAD_COOKIE) + { + kdDebug(11001) << " Player loaded propertly"<<endl; + } + else + { + kdError(11001) << " Player loading error. probably format error"<<endl; + } + + // emit signalLoad(stream); + return true; +} + +bool KPlayer::save(QDataStream &stream) +{ + stream << (Q_INT32)id() << (Q_INT32)networkPriority(); + + d->mProperties.save(stream); + + stream << (Q_INT16)KPLAYER_LOAD_COOKIE; + + //emit signalSave(stream); + return true; +} + + +void KPlayer::networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender) +{ + //kdDebug(11001) << k_funcinfo ": msgid=" << msgid << " sender=" << sender << " we are=" << id() << endl; + // PlayerProperties processed + bool issender; + if (game()) + { + issender=sender==game()->gameId(); + } + else + { + issender=true; + } + if (d->mProperties.processMessage(stream,msgid,issender)) + { + return ; + } + switch(msgid) + { + case KGameMessage::IdPlayerInput: + { + kdDebug(11001) << k_funcinfo << ": Got player move " + << "KPlayer (virtual) forwards it to the game object" << endl; + forwardInput(stream,false); + } + break; + default: + emit signalNetworkData(msgid - KGameMessage::IdUser, + ((QBuffer*)stream.device())->readAll(),sender,this); + kdDebug(11001) << k_funcinfo << ": " + << "User data msgid " << msgid << endl; + break; + } + +} + +KGamePropertyBase* KPlayer::findProperty(int id) const +{ + return d->mProperties.find(id); +} + +bool KPlayer::addProperty(KGamePropertyBase* data) +{ + return d->mProperties.addProperty(data); +} + +void KPlayer::sendProperty(int msgid, QDataStream& stream, bool* sent) +{ + if (game()) + { + bool s = game()->sendPlayerProperty(msgid, stream, id()); + if (s) + { + *sent = true; + } + } +} + +void KPlayer::emitSignal(KGamePropertyBase *me) +{ + // Notify KGameIO (Process) for a new turn + if (me->id()==KGamePropertyBase::IdTurn) + { + //kdDebug(11001) << k_funcinfo << ": for KGamePropertyBase::IdTurn " << endl; + QPtrListIterator<KGameIO> it(mInputList); + while (it.current()) + { + it.current()->notifyTurn(mMyTurn.value()); + ++it; + } + } + emit signalPropertyChanged(me,this); +} + +// --------------------- DEBUG -------------------- +void KPlayer::Debug() +{ + kdDebug(11001) << "------------------- KPLAYER -----------------------" << endl; + kdDebug(11001) << "this: " << this << endl; + kdDebug(11001) << "rtti: " << rtti() << endl; + kdDebug(11001) << "id : " << id() << endl; + kdDebug(11001) << "Name : " << name() << endl; + kdDebug(11001) << "Group: " << group() << endl; + kdDebug(11001) << "Async: " << asyncInput() << endl; + kdDebug(11001) << "myTurn: " << myTurn() << endl; + kdDebug(11001) << "Virtual: " << isVirtual() << endl; + kdDebug(11001) << "Active: " << isActive() << endl; + kdDebug(11001) << "Priority:" << networkPriority() << endl; + kdDebug(11001) << "Game : " << game() << endl; + kdDebug(11001) << "#IOs: " << mInputList.count() << endl; + kdDebug(11001) << "---------------------------------------------------" << endl; +} + +#include "kplayer.moc" diff --git a/libkdegames/kgame/kplayer.h b/libkdegames/kgame/kplayer.h new file mode 100644 index 00000000..0e511ac3 --- /dev/null +++ b/libkdegames/kgame/kplayer.h @@ -0,0 +1,471 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Martin Heni (martin@heni-online.de) + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 __KPLAYER_H_ +#define __KPLAYER_H_ + +#include <qstring.h> +#include <qobject.h> +#include <qptrlist.h> + +#include "kgameproperty.h" +#include <kdemacros.h> + +class KGame; +class KGameIO; +class KGamePropertyBase; +class KGamePropertyHandler; + +class KPlayerPrivate; + +/** + * @short Base class for a game player + * + * The KPlayer class is the central player object. It holds + * information about the player and is responsible for any + * input the player does. For this arbitrary many KGameIO + * modules can be plugged into it. Main features are: + * - Handling of IO devices + * - load/save (mostly handled by KGamePropertyHandler) + * - Turn handling (turn based, asynchronous) + * + * A KPlayer depends on a KGame object. Call KGame::addPlayer() to plug + * a KPlayer into a KGame object. Note that you cannot do much with a + * KPlayer object before it has been plugged into a KGame. This is because + * most properties of KPlayer are KGameProperty which need to send messages + * through a KGame object to be changed. + * + * A KGameIO represents the input methods of a player and you should make all + * player inputs through it. So call something like playerInput->move(4); + * instead which should call KGameIO::sendInput() to actually move. This way + * you gain a *very* big advantage: you can exchange a KGameIO whenever you + * want! You can e.g. remove the KGameIO of a local (human) player and just + * replace it by a computerIO on the fly! So from that point on all playerInputs + * are done by the computerIO instead of the human player. You also can replace + * all network players by computer players when the network connection is broken + * or a player wants to quit. + * So remember: use KGameIO whenever possible! A KPlayer should just + * contain all data of the player (KGameIO must not!) and several common + * functions which are shared by all of your KGameIOs. + * + */ +class KDE_EXPORT KPlayer : public QObject +{ + Q_OBJECT + +public: + typedef QPtrList<KGameIO> KGameIOList; + + // KPlayer(KGame *,KGameIO * input=0); + /** + * Create a new player object. It will be automatically + * deleted if the game it belongs to is deleted. + */ + KPlayer(); + + /** + * Create a new player object. It will be automatically + * deleted if the game it belongs to is deleted. This constructor + * automatically adds the player to the game using KGame::addPlayer() + */ + KPlayer(KGame* game); + + virtual ~KPlayer(); + + /** + * The idendification of the player. Overwrite this in + * classes inherting KPlayer to run time identify them. + * + * @return 0 for default KPlayer. + */ + virtual int rtti() const {return 0;} + + /** + * Gives debug output of the game status + */ + void Debug(); + + // properties + /** + * Returns a list of input devices + * + * @return list of devices + */ + KGameIOList *ioList() {return &mInputList;} + + /** + * sets the game the player belongs to. This + * is usually automatically done when adding a + * player + * + * @param game the game + */ + void setGame(KGame *game) {mGame=game;} + + /** + * Query to which game the player belongs to + * + * @return the game + */ + KGame *game() const {return mGame;} + + /** + * Set whether this player can make turns/input + * all the time (true) or only when it is its + * turn (false) as it is used in turn based games + * + * @param a async=true turn based=false + */ + void setAsyncInput(bool a) {mAsyncInput = a;} + + /** + * Query whether this player does asynchronous + * input + * + * @return true/false + */ + bool asyncInput() const {return mAsyncInput.value();} + + /** + * Is this player a virtual player, ie is it + * created by mirroring a real player from another + * network game. This mirroring is done autmatically + * as soon as a network connection is build and it affects + * all players regardless what type + * + * @return true/false + */ + bool isVirtual() const; + + /** + * @internal + * Sets whether this player is virtual. This is internally + * called + * + * @param v virtual true/false + */ + void setVirtual(bool v); + + /** + * Is this player an active player. An player is usually + * inactivated if it is replaced by a network connection. + * But this could also be called manually + * + * @return true/false + */ + bool isActive() const {return mActive;} + + /** + * Set an player as active (true) or inactive (false) + * + * @param v true=active, false=inactive + */ + void setActive(bool v) {mActive=v;} + + /** + * Returns the id of the player + * + * @return the player id + */ + Q_UINT32 id() const; + + /* Set the players id. This is done automatically by + * the game object when adding a new player! + * + * @param i the player id + */ + void setId(Q_UINT32 i); + + /** + * Returns the user defined id of the player + * This value can be used arbitrary by you to + * have some user idendification for your player, + * e.g. 0 for a white chess player, 1 for a black + * one. This value is more reliable than the player + * id whcih can even change when you make a network + * connection. + * + * @return the user defined player id + */ + int userId() const {return mUserId.value();} + + /* Set the user defined players id. + * + * @param i the user defined player id + */ + void setUserId(int i) {mUserId = i;} + + /** + * Returns whether this player can be replaced by a network + * connection player. The name of this function can be + * improved ;-) If you do not overwrite the function to + * select what players shall play in a network the KGame + * does an automatic selection based on the networkPriority + * This is not a terrible important function at the moment. + * + * @return true/false + */ + int networkPriority() const; + + /** + * Set whether this player can be replaced by a network + * player. There are to possible games. The first type + * of game has arbitrary many players. As soon as a network + * players connects the game runs with more players (not tagged + * situation). The other type is e.g. games like chess which + * require a constant player number. In a network game situation + * you would tag one or both players of all participants. As + * soon as the connect the tagged player will then be replaced + * by the network partner and it is then controlled over the network. + * On connection loss the old situation is automatically restored. + * + * The name of this function can be improved;-) + * + * @param b should this player be tagged + */ + void setNetworkPriority(int b); + + /** + * Returns the player which got inactivated to allow + * this player to be set up via network. Mostly internal + * function + */ + KPlayer *networkPlayer() const; + + /** + * Sets this network player replacement. Internal stuff + */ + void setNetworkPlayer(KPlayer *p); + + // A name and group the player belongs to + /** + * A group the player belongs to. This + * Can be set arbitrary by you. + */ + void setGroup(const QString& group); + + /** + * Query the group the player belongs to. + */ + virtual const QString& group() const; + + /** + * Sets the name of the player. + * This can be chosen arbitrary. + * @param name The player's name + */ + void setName(const QString& name); + + /** + * @return The name of the player. + */ + virtual const QString& name() const; + + + // set devices + /** + * Adds an IO device for the player. Possible KGameIO devices + * can either be taken from the existing ones or be self written. + * Existing are e.g. Keyboard, Mouse, Computerplayer + * + * @param input the inut device + * @return true if ok + */ + bool addGameIO(KGameIO *input); + + /** + * remove (and delete) a game IO device + * + * The remove IO(s) is/are deleted by default. If + * you do not want this set the parameter deleteit to false + * + * @param input the device to be removed or 0 for all devices + * @param deleteit true (default) to delete the device otherwisse just remove it + * @return true on ok + */ + bool removeGameIO(KGameIO *input=0,bool deleteit=true); + + /** + * Finds the KGameIO devies with the given rtti code. + * E.g. find the mouse or network device + * + * @param rtti the rtti code to be searched for + * @return the KGameIO device + */ + KGameIO *findRttiIO(int rtti) const; + + /** + * Checks whether this player has a IO device of the + * given rtti type + * + * @param rtti the rtti typed to be checked for + * @return true if it exists + */ + bool hasRtti(int rtti) const {return findRttiIO(rtti)!=0;} + + // Message exchange + /** + * Forwards input to the game object..internal use only + * + * This method is used by KGameIO::sendInput(). Use that function + * instead to send player inputs! + * + * This function forwards a player input (see KGameIO classes) to the + * game object, see KGame, either to KGame::sendPlayerInput() (if + * transmit=true, ie the message has just been created) or to + * KGame::playerInput() (if player=false, ie the message *was* sent through + * KGame::sendPlayerInput). + */ + virtual bool forwardInput(QDataStream &msg,bool transmit=true, Q_UINT32 sender=0); + + /** + * Forwards Message to the game object..internal use only + */ + virtual bool forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver=0,Q_UINT32 sender=0); + + // Game logic + /** + * is it my turn to go + * + * @return true/false + */ + bool myTurn() const {return mMyTurn.value();} + + /** + * Sets whether this player is the next to turn. + * If exclusive is given all other players are set + * to setTurn(false) and only this player can move + * + * @param b true/false + * @param exclusive true (default)/ false + * @return should be void + */ + bool setTurn(bool b,bool exclusive=true); + + + // load/save + /** + * Load a saved player, from file OR network. By default all + * KGameProperty objects in the dataHandler of this player are loaded + * and saved when using load or save. If you need to save/load more + * you have to replace this function (and save). You will probably + * still want to call the default implementation additionally! + * + * @param stream a data stream where you can stream the player from + * + * @return true? + */ + virtual bool load(QDataStream &stream); + + /** + * Save a player to a file OR to network. See also load + * + * @param stream a data stream to load the player from + * + * @return true? + */ + virtual bool save(QDataStream &stream); + + /** + * Receives a message + * @param msgid The kind of the message. See messages.txt for further + * information + * @param stream The message itself + * @param sender + **/ + void networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender); + + /** + * Searches for a property of the player given its id. + * @param id The id of the property + * @return The property with the specified id + **/ + KGamePropertyBase* findProperty(int id) const; + + /** + * Adds a property to a player. You would add all + * your player specific game data as KGameProperty and + * they are automatically saved and exchanged over network. + * + * @param data The property to be added. Must have an unique id! + * @return false if the given id is not valid (ie another property owns + * the id) or true if the property could be added successfully + **/ + bool addProperty(KGamePropertyBase* data); + + /** + * Calculates a checksum over the IO devices. Can be used to + * restore the IO handlers. The value returned is the 'or'ed + * value of the KGameIO rtti's. + * this is itnernally used for saving and restorign a player. + */ + int calcIOValue(); + + /** + * @return the property handler + */ + KGamePropertyHandler* dataHandler(); + +signals: + /** + * The player object got a message which was targeted + * at it but has no default method to process it. This + * means probably a user message. Connecting to this signal + * allowed to process it. + */ + void signalNetworkData(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer *me); + + /** + * This signal is emmited if a player property changes its value and + * the property is set to notify this change. This is an + * important signal as you should base the actions on a reaction + * to this property changes. + */ + void signalPropertyChanged(KGamePropertyBase *property,KPlayer *me); + +protected slots: + /** + * Called by KGameProperty only! Internal function! + **/ + void sendProperty(int msgid, QDataStream& stream, bool* sent); + /** + * Called by KGameProperty only! Internal function! + **/ + void emitSignal(KGamePropertyBase *me); + + +private: + void init(); + +private: + KGame *mGame; + bool mActive; // active player + KGameIOList mInputList; + + // GameProperty // AB: I think we can't move them to KPlayerPrivate - inline + // makes sense here + KGamePropertyBool mAsyncInput; // async input allowed + KGamePropertyBool mMyTurn; // Is it my turn to play (only useful if not async)? + KGamePropertyInt mUserId; // a user defined id + + KPlayerPrivate* d; +}; + +#endif diff --git a/libkdegames/kgame/libkdegames.html b/libkdegames/kgame/libkdegames.html new file mode 100644 index 00000000..66206c47 --- /dev/null +++ b/libkdegames/kgame/libkdegames.html @@ -0,0 +1,187 @@ +<html> + <head> + <title>Documentation for libkdegames</title> + <meta content=""> + <style></style> + </head> + <body> + <H1>Documentation for the classes in libkdegames</H1> +<!--------------------------------------------------------------------------------> + <H3>Design Principles</H3> + The library <em>kdegames</em> contains a collection of classes that can be used + to develop games using the KDE environment very easily. There are a few + principles that were used when developing the library:<P> + + <UL> + <LI><b>usable for a big variety of games</b><br> + The class <em>KGame</em> provides many features that are needed in many games. + It can be used for board games, card games, maze games, simulation games, strategy games and + many more.<br> + It does not (yet) include special features for realtime games, but can be used there, too. + <LI><b>features one-player and multi-player games</b></br> + A game developed with this library can easily support any number of simultaneous players. + So use it for one-player games (like Tetris or KSame), for two-player games (like TicTacToe + or chess) or for games with an arbitrary number of players. + <LI><b>computer players can easily be developed</b><br> + The class <em>KPlayer</em> represents an abstract player in a game. This can be a + human player that gets the input from the mouse or the keyboard. Or it can be a computer + player that makes moves by random or with artificial intelligence. All this can be achieved + subclassing KPlayer. + <LI><b>support for network games transparently</b><br> + The class <em>KGame</em> contains lots of features for network game support. Developing + a network game using a TCP/IP connection is very easy this way. But the default + case is still the local game. So the user should not need to connect to the internet + or to select a game server to play a game. + <LI><b>support for turn based and for asynchronous games</b><br> + You can use this library for turn based games, when only one player can make a move at a time + (like most bord games or card games), but also for asynchronous games, when every player can + make a move any time (like many action games). + </UL> +<!--------------------------------------------------------------------------------> + <H3>The central game class: <em>KGame</em></H3> + + When you want to develop your own KDE game using the KDE games library, you most likely want + to use the <em>KGame</em> class. There are two possible ways to extend it to your own needs: + Create a subclass and overwrite the virtual methods, or simply create an instance of KGame + and connect to the appropriate signals.<P> + + <<more code about KGame, an easy example>> + +<!--------------------------------------------------------------------------------> + <H3>Network games and related classes</H3> + + One of the main principles in the design of <em>KGame</em> was to make network games possible + with a minimum of effort for the game developer.<P> + + A network game is a game with usually several players, that are on different computers. These + computers are usually connected to the internet, and all the moves a player does are exchanged + over this network.<P> + + The exchange of moves and other information is done using the class <em>KMessageServer</em>. + An object of this class is a server that waits for connections. Someone who wants to take part + in the game has to connect to this server - usually using an internet socket connection. He does + this by creating a <em>KMessageClient</em> object. This object connects to the message server.<P> + + The transfer of data is realised by subclasses of the abstract class <em>KMessageIO</em>. One object + of this class is created on the client side, one on the server side. Different types of networks can + be supported by creating new subclasses of KMessageIO. There are already two subclasses of KMessageIO: + <em>KMessageSocket</em> uses a internet socket connection to transfer the data from the message client + to the message server or vice versa. <em>KMessageDirect</em> can be used if both the message server and + the message client are within the same process. The data blocks are copied directly to the other side, + so the transfer is faster and needs no network bandwidth.<P> + + A typical network game situation could look like this:<P> + + <IMG SRC="kmessageserver.png"><P> + + Here, three KGame object (called message clients) are connected to the KMessageServer object. One + is in the same process, so it uses KMessageDirect. The other two use KMessageSocket, so an internet + socket connection is used. KGame doesn't talk directly to the message server, but uses a KMessageClient + object instead. One of the KMessageClient objects is the admin of the message server. This client has some + priviledges. It may e.g. kill the connection to other message clients, or limit the number of clients + that may connect.<P> + + The KGame objects are by default all equal. So the usual approach will be that every KGame object will + store the complete status of the game, and any change or move will be broadcasted to the other KGame + objects, so that they all change the status in identical ways. Sometimes it may be necessary (or just + easier to implement) that one KGame object is a <em>game server</em> (i.e. he is repsonsible for everything, + he coordinates the complete game and stores the game status), whereas the other KGame objects are + only dumb stubs that are used to contact the <em>game server</em>. You can implement both approaches + using the message server structure. If you need to elect the KGame object that shall be + the game server, you may e.g. use the one that has the KMessageClient that is the admin of the message + server. (Of course this is only a suggestion, you can use other approaches.)<P> + + The main principle when developing the message server/client structure was, that the message server + doesn't have <em>any</em> idea of the game and its rules that is played. The message server only forwards + messages from one message client to the others without interpreting or manipulating the data. So always + keep in mind that the message server is <em>not</em> a game server! It does not store any data about + the game status. It is only a server for network connections and message broadcasting, <em>not</em> + for game purposes. The reason for this principle is, that <em>any</em> game can be played using a + KMessageServer on any computer. The computer being the message server doesn't need to know anything + about the game that is played on it. So you don't have to install new versions of the game there. Only + the clients need to be game specific.<P> + + Usually you don't need to create <em>KMessageServer</em> or <em>KMessageClient</em> objects in your game, + since <em>KGame</em> does this for you. There are three different scenarios fo network games that are + all supported in <em>KGame</em>:<P> + + <b>Scenario 1: local game</b><P> + + The local game should always be the default state a game should be in. To avoid having this scenario + as a special case, <em>KGame</em> automatically creates a KMessageServer object and a KMessageClient + object. So every change and every move is sent to the message server and is returned to the KGame + object before it is processed. Since the connection between the message client and the message server + uses KMessageDirect the data transfer is very fast and wont hurt in most cases.<P> + + <IMG SRC="scenario0.png"><P> + + This is the default situation right after creating the <em>KGame</em> object.<P> + + <b>Scenario 2: network game, started by one player</b><P> + + If one user is bored of playing alone, he can open his game for connections from the outside world. + He listens to a TCP/IP socket port (i.e. a number between 0 and 65535). Other players can create + KGame objects of their own and connect to this port. They need to know the IP address of that computer + and the port number. This situation will have this structure: + + <IMG SRC="scenario1.png"><P> + + The first player has to do something like:<P> + + <PRE> + KGame *myGame = new KGame (); + // wait for connections on port 12345 + myGame->offerConnections (12345); + </PRE> + + And the other players have to do something like:<P> + + <PRE> + KGame *myGame = new KGame (); + // connect to the message server + myGame->connectToServer ("theServer.theDomain.com", 12345); + </PRE> + + This automatically removes the message server in these KGame objects and connects to the given + one instead.<P> + + <b>Scenario 3: network game, using a stand alone message server</b><P> + + Sometimes it is not possible to let the message server run on one of the players computer. If e.g. all + the players have their computer in a local network that uses masquerading to contact the internet, + other computers cannot connect to them since the computer doesn't have a IP address to the outside + world. Then the only way to play a network game is to have a standalone KMessageServer object on + another server computer (somthing like "games.kde.org" e.g.). Since the KMessageServer isn't game + specific at all, every game can be played using it. There doesn't have to be any special software + installed on that server computer, only the program creating a KMessageServer object.<P> + + This scenario has some more advantages: The message server can be a well known meeting point to + start a game. This way one could play games against other players you never knew before. Furthermore + the game is stopped brutally when the program that contains the message server in scenario 2 is + quitted. (Migration of message servers is not yet implemented, but may be in the future.) Using a + stand alone message server, the players may enter and leave the game as they want. + + <IMG SRC="scenario2.png"><P> + + To create this scenario, a special KMessageServer program has to be started on the computer + that shall be the stand alone message server:<P> + + <PRE> + % kmessageserver -port=12345 + </PRE> + + The other games that want to connect have to do this (supposed the stand alone message server + has the IP address "games.kde.org"):<P> + + <PRE> + KGame *myGame = new KGame (); + // connect to the message server + myGame->connectToServer ("games.kde.org", 12345); + </PRE> + + + + +<!--------------------------------------------------------------------------------> + </body> +</html>
\ No newline at end of file diff --git a/libkdegames/kgame/messages.txt b/libkdegames/kgame/messages.txt new file mode 100644 index 00000000..151196d5 --- /dev/null +++ b/libkdegames/kgame/messages.txt @@ -0,0 +1,93 @@ +Message formats of libkdegames: +------------------------------- + +There are two different communication layers, using their own protocols: + + - the message layer (KMessageIO, KMessageServer, KMessageClient) + + This is used to send messages from one client (i.e. KGame object) + to an other one, to a group of other clients, or to all the clients + connected to the KMessageServer. The messages are arbitrary blocks + of data, of an arbitrary length. + This layer is an underlying protocol that isn't game specific at all. + You shouldn't need to know the message format of the packets. If you + want to extend the protocol, have a look into KMessageServer API + reference for a complete list of message types. + + - the game layer (KGame, KGameIO, KPlayer) + + This layer uses the message layer to send its specific message packets + between the objects of the game. + This layer contains the game specific messages (e.g. addPlayer, setupGame). + The rest of this file describes this layer. + + +Game Layer Messages: +-------------------- + + Application Cookie 16 Bit + Version 8 Bit + MsgId 16 Bit + SenderId 16 Bit + ReceiverId 16 Bit + Userdata + +The format of the messages is used internally and there is usually no reason why +you have to know what it is used for. But as usually != always here are some +comments on the format. Note that KGame is under development and the content of +this file could be obsolete. Please result the sourcecode for up-to-date +information. +Application Cookie is used to identify the application. This prevents a +chess game from talking to a poker game. +Version is the version of KNetworkGame, sender and receiver must be of the same +version. + library note: this could be a limitation, as KGame should be backward + compatible. Maybe change to version must be >= KNETWORKGAME or something less + restrictive +MsgId specifies the kind of the message data (see below). +SenderId is the id of the KGame/KPlayer object which sent the message, it is +coded like this: the lower 10 bits specify a player and the upper bit +represent the game id shifted by 10 bits. So we get +Id=playerId | (gameId<<10); +ReceiverId is the id of the receiver of the message. It can be either +a player, a game or a broadcast. For a broadcast the Id is set to 0 +in the other cases the coding is as with the senderId +Userdata is the data of the user (wow ;-)) + + +MsgId UserData +--------------------------------------------------------- +IdMessage user defined + +IdSetupGame Q_INT32 isServer + Q_INT32 maxPlayers + Q_INT32 newid (id for the new game) + Q_INT32 cntR (virtual player nunmber) + Q_INT32 cntT (tagged player number) + TODO: Changed + +IdContinueSetup: TODO + +IdSendPlayer Q_INT32 omit how many tagged players for replacement + TODO: Changed + +IdGameSave Save(msg)->Load(msg) + +IdAddPlayer rtti + gameid() of the owner + player->Save(msg) -> player->Load(msg) + +IdRemovePlayer Q_INT16 playerid + +IdError Q_INT32 errorcode + QString errortext + +IdGameStatus Q_INT32 status + +IdPlayerProperty Q_INT16 propertyId + user defined -> the property + +IdGameProperty Q_INT16 propertyId + user defined -> the property + +IdPlayerInput user defined diff --git a/libkdegames/kgame/scenario0.png b/libkdegames/kgame/scenario0.png Binary files differnew file mode 100644 index 00000000..4de99b52 --- /dev/null +++ b/libkdegames/kgame/scenario0.png diff --git a/libkdegames/kgame/scenario1.png b/libkdegames/kgame/scenario1.png Binary files differnew file mode 100644 index 00000000..74af4f6f --- /dev/null +++ b/libkdegames/kgame/scenario1.png diff --git a/libkdegames/kgame/scenario2.png b/libkdegames/kgame/scenario2.png Binary files differnew file mode 100644 index 00000000..14ea0a3c --- /dev/null +++ b/libkdegames/kgame/scenario2.png diff --git a/libkdegames/kgamelcd.cpp b/libkdegames/kgamelcd.cpp new file mode 100644 index 00000000..65c436a5 --- /dev/null +++ b/libkdegames/kgamelcd.cpp @@ -0,0 +1,250 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 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 "kgamelcd.h" +#include "kgamelcd.moc" + +#include <qlayout.h> +#include <qlabel.h> +#include <qtimer.h> + +#include <kglobal.h> + + +//----------------------------------------------------------------------------- +KGameLCD::KGameLCD(uint nbDigits, QWidget *parent, const char *name) + : QLCDNumber(nbDigits, parent, name), _htime(800) +{ + const QPalette &p = palette(); + _fgColor = p.color(QPalette::Active, QColorGroup::Foreground); + _hlColor = p.color(QPalette::Active, QColorGroup::HighlightedText); + + _timer = new QTimer(this); + connect(_timer, SIGNAL(timeout()), SLOT(timeout())); + + setFrameStyle(Panel | Plain); + setSegmentStyle(Flat); + + displayInt(0); +} + +KGameLCD::~KGameLCD() +{} + +void KGameLCD::setDefaultBackgroundColor(const QColor &color) +{ + QPalette p = palette(); + p.setColor(QColorGroup::Background, color); + setPalette(p); +} + +void KGameLCD::setDefaultColor(const QColor &color) +{ + _fgColor = color; + QPalette p = palette(); + p.setColor(QColorGroup::Foreground, color); + setPalette(p); +} + +void KGameLCD::setHighlightColor(const QColor &color) +{ + _hlColor = color; +} + +void KGameLCD::setLeadingString(const QString &s) +{ + _lead = s; + displayInt(0); +} + +void KGameLCD::setHighlightTime(uint time) +{ + _htime = time; +} + +void KGameLCD::resetColor() +{ + setColor(QColor()); +} + +void KGameLCD::setColor(const QColor &color) +{ + const QColor &c = (color.isValid() ? color : _fgColor); + QPalette p = palette(); + p.setColor(QColorGroup::Foreground, c); + setPalette(p); +} + +void KGameLCD::displayInt(int v) +{ + int n = numDigits() - _lead.length(); + display(_lead + QString::number(v).rightJustify(n)); +} + +void KGameLCD::highlight() +{ + highlight(true); + _timer->start(_htime, true); +} + +void KGameLCD::highlight(bool light) +{ + if (light) setColor(_hlColor); + else resetColor(); +} + +//----------------------------------------------------------------------------- +KGameLCDClock::KGameLCDClock(QWidget *parent, const char *name) +: KGameLCD(5, parent, name) +{ + _timerClock = new QTimer(this); + connect(_timerClock, SIGNAL(timeout()), SLOT(timeoutClock())); +} + +KGameLCDClock::~KGameLCDClock() +{} + +void KGameLCDClock::timeoutClock() +{ + // waiting an hour does not restart timer + if ( _min==59 && _sec==59 ) return; + _sec++; + if (_sec==60) { + _min++; + _sec = 0; + } + showTime(); +} + +QString KGameLCDClock::pretty() const +{ + QString sec = QString::number(_sec).rightJustify(2, '0', true); + QString min = QString::number(_min).rightJustify(2, '0', true); + return min + ':' + sec; +} + +void KGameLCDClock::showTime() +{ + display(pretty()); +} + +void KGameLCDClock::reset() +{ + _timerClock->stop(); + _sec = 0; + _min = 0; + showTime(); +} + +void KGameLCDClock::start() +{ + _timerClock->start(1000); // 1 second +} + +void KGameLCDClock::stop() +{ + _timerClock->stop(); +} + +uint KGameLCDClock::seconds() const +{ + return _min*60 + _sec; +} + +void KGameLCDClock::setTime(uint sec) +{ + Q_ASSERT( sec<3600 ); + _sec = sec % 60; + _min = sec / 60; + showTime(); +} + +void KGameLCDClock::setTime(const QString &s) +{ + Q_ASSERT( s.length()==5 && s[2]==':' ); + uint min = kMin(s.section(':', 0, 0).toUInt(), uint(59)); + uint sec = kMin(s.section(':', 1, 1).toUInt(), uint(59)); + setTime(sec + min*60); +} + + +//----------------------------------------------------------------------------- +class KGameLCDList::KGameLCDListPrivate +{ +public: + QValueVector<QLabel *> _leadings; +}; + +KGameLCDList::KGameLCDList(const QString &title, QWidget *parent, + const char *name) + : QWidget(parent, name) +{ + init(title); +} + +KGameLCDList::KGameLCDList(QWidget *parent, const char *name) + : QWidget(parent, name) +{ + init(QString::null); +} + +KGameLCDList::~KGameLCDList() +{ + delete d; +} + +void KGameLCDList::init(const QString &title) +{ + d = new KGameLCDListPrivate; + + QGridLayout *top = new QGridLayout(this, 1, 2, 5); + top->setColStretch(1, 1); + + _title = new QLabel(title, this); + _title->setAlignment(AlignCenter); + top->addMultiCellWidget(_title, 0, 0, 0, 1, AlignCenter); +} + +void KGameLCDList::append(QLCDNumber *lcd) +{ + append(QString::null, lcd); +} + +void KGameLCDList::append(const QString &leading, QLCDNumber *lcd) +{ + uint i = size(); + QLabel *label = 0; + if ( !leading.isEmpty() ) { + label = new QLabel(leading, this); + static_cast<QGridLayout *>(layout())->addWidget(label, i+1, 0); + } + d->_leadings.push_back(label); + _lcds.push_back(lcd); + static_cast<QGridLayout *>(layout())->addWidget(lcd, i+1, 1); +} + +void KGameLCDList::clear() +{ + for (uint i=0; i<_lcds.size(); i++) { + delete d->_leadings[i]; + delete _lcds[i]; + } + d->_leadings.clear(); + _lcds.clear(); +} diff --git a/libkdegames/kgamelcd.h b/libkdegames/kgamelcd.h new file mode 100644 index 00000000..3e6ad33c --- /dev/null +++ b/libkdegames/kgamelcd.h @@ -0,0 +1,249 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001,2002,2003 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 __KGAMELCD_H +#define __KGAMELCD_H + +#include <qlcdnumber.h> +#include <qvaluevector.h> +#include <kdemacros.h> + +class QLabel; +class QTimer; + +//----------------------------------------------------------------------------- +/** + * This class is a visually enhanced @ref QLCDNumber: + * <ul> + * <li> It can show an additional string before the integer being + * displayed.</li> + * <li> Its foreground and background colors can easily be modified. </li> + * <li> It can be highlighted for a short time. </li> + * </ul> + * + * @since 3.2 + */ +class KDE_EXPORT KGameLCD : public QLCDNumber +{ + Q_OBJECT +public: + KGameLCD(uint nbDigits, QWidget *parent = 0, const char *name = 0); + + ~KGameLCD(); + + /** + * Set the default background color. + */ + void setDefaultBackgroundColor(const QColor &color); + + /** + * Set the default foreground color. + */ + void setDefaultColor(const QColor &color); + + /** + * Set highlight color. + */ + void setHighlightColor(const QColor &color); + + /** + * Set the string that will be displayed before the integer number to be + * displayed. By default this string is null. + */ + void setLeadingString(const QString &s); + + /** + * Set the highlight duration in milliseconds. The default value is + * 800 milliseconds. + */ + void setHighlightTime(uint time); + + /** + * Reset the foreground color to the default one. + */ + void resetColor(); + + /** + * Set the foreground color. + */ + void setColor(const QColor &color); + +public slots: + /** + * Highlight the LCD with the QColorGourp::HighlightedText color + * for a small time (setHighlightTime). + */ + void highlight(); + + /** + * Display the given integer with the (optionnal) leading string. + * + * Note: we cannot use display(int) since QLCDNumber::display is + * not virtual... And you cannot use QLCDNumber::intValue() to retrieve + * the given value. + */ + void displayInt(int value); + +private slots: + void timeout() { highlight(false); } + +private: + QColor _fgColor, _hlColor; + QString _lead; + uint _htime; + QTimer *_timer; + + class KGameLCDPrivate; + KGameLCDPrivate *d; + + void highlight(bool light); + +}; + +//----------------------------------------------------------------------------- +/** + * This class is a digital clock widget. It has a maximum duration of + * 3599 seconds (one hour) and it gets updated every second. + * + * @since 3.2 + */ +class KDE_EXPORT KGameLCDClock : public KGameLCD +{ + Q_OBJECT +public: + KGameLCDClock(QWidget *parent = 0, const char *name = 0); + + ~KGameLCDClock(); + + /** + * @return the total number of seconds elapsed. + */ + uint seconds() const; + + /** + * @return the time as a string to be displayed: "mm:ss". + */ + QString pretty() const; + + /** + * Set the time. + */ + void setTime(uint seconds); + + /** + * Set the time (format should be "mm:ss"). + */ + void setTime(const QString &s); + +public slots: + /** + * Stop the clock and reset it to zero. + */ + virtual void reset(); + + /** + * Stop the clock but do not reset it to zero. + */ + virtual void stop(); + + /** + * Start the clock from the current time. + */ + virtual void start(); + +protected slots: + virtual void timeoutClock(); + +private: + QTimer *_timerClock; + uint _sec, _min; + + class KGameLCDClockPrivate; + KGameLCDClockPrivate *d; + + void showTime(); +}; + +//----------------------------------------------------------------------------- +/** + * This widget holds a list of @ref QLCDNumber arranged in a vertical layout. + * It also shows a label at the top of the list. + * + * @since 3.2 + */ +class KDE_EXPORT KGameLCDList : public QWidget +{ + Q_OBJECT +public: + /** + * Constructor. + * + * @param title is the content of the top label. + * @param parent passed to the QWidget constructor + * @param name passed to the QWidget constructor + */ + KGameLCDList(const QString &title, + QWidget *parent = 0, const char *name = 0); + KGameLCDList(QWidget *parent = 0, const char *name = 0); + + ~KGameLCDList(); + + /** + * Append a QLCDNumber at the bottom of the list. + * The QLCDNumber should have the KGameLCDList as parent. + */ + void append(QLCDNumber *lcd); + + /** + * Append a QLCDNumber at the bottom of the list. + * The QLCDNumber should have the KGameLCDList as parent. + */ + void append(const QString &leading, QLCDNumber *lcd); + + /** + * Delete all @ref QLCDNumber and clear the list. + */ + void clear(); + + /** + * @return the title label. + */ + QLabel *title() const { return _title; } + + /** + * @return the QLCDNumber at index @param i + */ + QLCDNumber *lcd(uint i) const { return _lcds[i]; } + + /** + * @return the number of QLCDNumber in the list. + */ + uint size() const { return _lcds.size(); } + +private: + QLabel *_title; + QValueVector<QLCDNumber *> _lcds; + + class KGameLCDListPrivate; + KGameLCDListPrivate *d; + + void init(const QString &title); +}; + +#endif diff --git a/libkdegames/kgamemisc.cpp b/libkdegames/kgamemisc.cpp new file mode 100644 index 00000000..bfedd11c --- /dev/null +++ b/libkdegames/kgamemisc.cpp @@ -0,0 +1,62 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ + +#include <qstringlist.h> + +#include <krandomsequence.h> +#include <klocale.h> + +#include "kgamemisc.h" + +class KGameMiscPrivate +{ +public: + KGameMiscPrivate() + { + } + +}; + +KGameMisc::KGameMisc() +{ +// not yet used +// d = new KGamePrivate; +} + +KGameMisc::~KGameMisc() +{ + // don't forget to delete it as soon as it is used! +// delete d; +} + +QString KGameMisc::randomName()// do we need i18n? I think yes +{ + QStringList names = QStringList::split( QChar(' '), + i18n( "A list of language typical names ( for games ), separated by spaces", + "Adam Alex Andreas Andrew Bart Ben Bernd Bill " + "Chris Chuck Daniel Don Duncan Ed Emily Eric " + "Gary Greg Harry Ian Jean Jeff Jan Kai Keith Ken " + "Kirk Marc Mike Neil Paul Rik Robert Sam Sean " + "Thomas Tim Walter" ) ); + KRandomSequence random; + return *names.at( random.getLong( names.count() ) ); +} diff --git a/libkdegames/kgamemisc.h b/libkdegames/kgamemisc.h new file mode 100644 index 00000000..526bb0ae --- /dev/null +++ b/libkdegames/kgamemisc.h @@ -0,0 +1,45 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +/* + $Id$ +*/ +#ifndef __KGAMEMISC_H__ +#define __KGAMEMISC_H__ + +#include <qstring.h> +#include <kdemacros.h> +class KGameMiscPrivate; +/** + * This class contains several (usually static) functions I really did not know + * a class for. If you know a class for any of these member s please drop one of + * the above copyright holders a mail (or just kde-games-devel@kde.org) + **/ +class KDE_EXPORT KGameMisc +{ +public: + KGameMisc(); + ~KGameMisc(); + + static QString randomName(); + +private: + KGameMiscPrivate* d; +}; + +#endif diff --git a/libkdegames/kgameprogress.cpp b/libkdegames/kgameprogress.cpp new file mode 100644 index 00000000..861dd454 --- /dev/null +++ b/libkdegames/kgameprogress.cpp @@ -0,0 +1,345 @@ +/* This file is part of the KDE libraries + Copyright (C) 1996 Martynas Kunigelis + + 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. +*/ +/** + * KGameProgress -- a progress indicator widget for KDE. + */ + +#include <qpainter.h> +#include <qpixmap.h> +#include <qstring.h> +#include <qregexp.h> +#include <qstyle.h> + +#include "kgameprogress.h" + +#include <kapplication.h> + +KGameProgress::KGameProgress(QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(0, 100, 1, 10, 0), + orient(Horizontal) +{ + initialize(); +} + +KGameProgress::KGameProgress(Orientation orientation, QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(0, 100, 1, 10, 0), + orient(orientation) +{ + initialize(); +} + +KGameProgress::KGameProgress(int minValue, int maxValue, int value, + Orientation orientation, QWidget *parent, const char *name) + : QFrame(parent, name), + QRangeControl(minValue, maxValue, 1, 10, value), + orient(orientation) +{ + initialize(); +} + +KGameProgress::~KGameProgress() +{ + delete bar_pixmap; +} + +void KGameProgress::advance(int offset) +{ + setValue(value() + offset); +} + +void KGameProgress::initialize() +{ + format_ = "%p%"; + use_supplied_bar_color = false; + bar_pixmap = 0; + bar_style = Solid; + text_enabled = TRUE; + setBackgroundMode( PaletteBackground ); + connect(kapp, SIGNAL(appearanceChanged()), this, SLOT(paletteChange())); + paletteChange(); +} + +void KGameProgress::paletteChange() +{ + QPalette p = kapp->palette(); + const QColorGroup &colorGroup = p.active(); + if (!use_supplied_bar_color) + bar_color = colorGroup.highlight(); + bar_text_color = colorGroup.highlightedText(); + text_color = colorGroup.text(); + setPalette(p); + + adjustStyle(); +} + + +void KGameProgress::setBarPixmap(const QPixmap &pixmap) +{ + if (pixmap.isNull()) + return; + if (bar_pixmap) + delete bar_pixmap; + + bar_pixmap = new QPixmap(pixmap); +} + +void KGameProgress::setBarColor(const QColor &color) +{ + bar_color = color; + use_supplied_bar_color = true; + if (bar_pixmap) { + delete bar_pixmap; + bar_pixmap = 0; + } +} + +void KGameProgress::setBarStyle(BarStyle style) +{ + if (bar_style != style) { + bar_style = style; + update(); + } +} + +void KGameProgress::setOrientation(Orientation orientation) +{ + if (orient != orientation) { + orient = orientation; + update(); + } +} + +void KGameProgress::setValue(int value) +{ + QRangeControl::setValue(value); +} + +void KGameProgress::setTextEnabled(bool enable) +{ + text_enabled = enable; +} + +const QColor & KGameProgress::barColor() const +{ + return bar_color; +} + +const QPixmap * KGameProgress::barPixmap() const +{ + return bar_pixmap; +} + +bool KGameProgress::textEnabled() const +{ + return text_enabled; +} + +QSize KGameProgress::sizeHint() const +{ + QSize s( size() ); + + if(orientation() == KGameProgress::Vertical) { + s.setWidth(24); + } else { + s.setHeight(24); + } + + return s; +} + +QSize KGameProgress::minimumSizeHint() const +{ + return sizeHint(); +} + +QSizePolicy KGameProgress::sizePolicy() const +{ + if ( orientation()==KGameProgress::Vertical ) + return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + else + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); +} + +KGameProgress::Orientation KGameProgress::orientation() const +{ + return orient; +} + +KGameProgress::BarStyle KGameProgress::barStyle() const +{ + return bar_style; +} + +int KGameProgress::recalcValue(int range) +{ + int abs_value = value() - minValue(); + int abs_range = maxValue() - minValue(); + return abs_range ? range * abs_value / abs_range : 0; +} + +void KGameProgress::valueChange() +{ + repaint(contentsRect(), FALSE); + emit percentageChanged(recalcValue(100)); +} + +void KGameProgress::rangeChange() +{ + repaint(contentsRect(), FALSE); + emit percentageChanged(recalcValue(100)); +} + +void KGameProgress::styleChange(QStyle&) +{ + adjustStyle(); +} + +void KGameProgress::adjustStyle() +{ + switch (style().styleHint(QStyle::SH_GUIStyle)) { + case WindowsStyle: + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + break; + case MotifStyle: + default: + setFrameStyle(QFrame::Panel | QFrame::Sunken); + setLineWidth( 2 ); + break; + } + update(); +} + +void KGameProgress::paletteChange( const QPalette &p ) +{ + // This never gets called for global color changes + // because we call setPalette() ourselves. + QFrame::paletteChange(p); +} + +void KGameProgress::drawText(QPainter *p) +{ + QRect r(contentsRect()); + //QColor c(bar_color.rgb() ^ backgroundColor().rgb()); + + // Rik: Replace the tags '%p', '%v' and '%m' with the current percentage, + // the current value and the maximum value respectively. + QString s(format_); + + s.replace(QRegExp(QString::fromLatin1("%p")), QString::number(recalcValue(100))); + s.replace(QRegExp(QString::fromLatin1("%v")), QString::number(value())); + s.replace(QRegExp(QString::fromLatin1("%m")), QString::number(maxValue())); + + p->setPen(text_color); + QFont font = p->font(); + font.setBold(true); + p->setFont(font); + //p->setRasterOp(XorROP); + p->drawText(r, AlignCenter, s); + p->setClipRegion( fr ); + p->setPen(bar_text_color); + p->drawText(r, AlignCenter, s); +} + +void KGameProgress::drawContents(QPainter *p) +{ + QRect cr = contentsRect(), er = cr; + fr = cr; + QBrush fb(bar_color), eb(backgroundColor()); + + if (bar_pixmap) + fb.setPixmap(*bar_pixmap); + + if (backgroundPixmap()) + eb.setPixmap(*backgroundPixmap()); + + switch (bar_style) { + case Solid: + if (orient == Horizontal) { + fr.setWidth(recalcValue(cr.width())); + er.setLeft(fr.right() + 1); + } else { + fr.setTop(cr.bottom() - recalcValue(cr.height())); + er.setBottom(fr.top() - 1); + } + + p->setBrushOrigin(cr.topLeft()); + p->fillRect(fr, fb); + + p->fillRect(er, eb); + + break; + + case Blocked: + const int margin = 2; + int max, num, dx, dy; + if (orient == Horizontal) { + fr.setHeight(cr.height() - 2 * margin); + fr.setWidth((int)(0.67 * fr.height())); + fr.moveTopLeft(QPoint(cr.left() + margin, cr.top() + margin)); + dx = fr.width() + margin; + dy = 0; + max = (cr.width() - margin) / (fr.width() + margin) + 1; + num = recalcValue(max); + } else { + fr.setWidth(cr.width() - 2 * margin); + fr.setHeight((int)(0.67 * fr.width())); + fr.moveBottomLeft(QPoint(cr.left() + margin, cr.bottom() - margin)); + dx = 0; + dy = - (fr.height() + margin); + max = (cr.height() - margin) / (fr.height() + margin) + 1; + num = recalcValue(max); + } + p->setClipRect(cr.x() + margin, cr.y() + margin, + cr.width() - margin, cr.height() - margin); + for (int i = 0; i < num; i++) { + p->setBrushOrigin(fr.topLeft()); + p->fillRect(fr, fb); + fr.moveBy(dx, dy); + } + + if (num != max) { + if (orient == Horizontal) + er.setLeft(fr.right() + 1); + else + er.setBottom(fr.bottom() + 1); + if (!er.isNull()) { + p->setBrushOrigin(cr.topLeft()); + p->fillRect(er, eb); + } + } + + break; + } + + if (text_enabled && bar_style != Blocked) + drawText(p); +} + +void KGameProgress::setFormat(const QString & format) +{ + format_ = format; +} + +QString KGameProgress::format() const +{ + return format_; +} + +#include "kgameprogress.moc" diff --git a/libkdegames/kgameprogress.h b/libkdegames/kgameprogress.h new file mode 100644 index 00000000..d6a353ac --- /dev/null +++ b/libkdegames/kgameprogress.h @@ -0,0 +1,255 @@ +/* This file is part of the KDE libraries + Copyright (C) 1996 Martynas Kunigelis + + 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. +*/ +/***************************************************************************** +* * +* KGameProgress -- progress indicator widget for KDE by Martynas Kunigelis * +* * +*****************************************************************************/ + +#ifndef _KPROGRES_H +#define _KPROGRES_H "$Id$" + +#include <qframe.h> +#include <qrangecontrol.h> +#include <kdemacros.h> +/** + * @short A progress indicator widget. + * + * KGameProgress is derived from QFrame and QRangeControl, so + * you can use all the methods from those classes. The only difference + * is that setValue() is now made a slot, so you can connect + * stuff to it. + * + * None of the constructors take line step and page step as arguments, + * so by default they're set to 1 and 10 respectively. + * + * The Blocked style ignores the textEnabled() setting and displays + * no text, since it looks truly ugly (and for other reasons). Signal + * percentageChanged() is emitted whenever the value changes so you + * can set up a different widget to display the current percentage complete + * and connect the signal to it. + * + * @author Martynas Kunigelis + * @version $Id$ + */ +class KDE_EXPORT KGameProgress : public QFrame, public QRangeControl +{ + Q_OBJECT + Q_ENUMS( BarStyle ) + Q_PROPERTY( int value READ value WRITE setValue) + Q_PROPERTY( BarStyle barStyle READ barStyle WRITE setBarStyle ) + Q_PROPERTY( QColor barColor READ barColor WRITE setBarColor ) + Q_PROPERTY( QPixmap barPixmap READ barPixmap WRITE setBarPixmap ) + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( bool textEnabled READ textEnabled WRITE setTextEnabled ) + +public: + /** + * Possible values for bar style. + * + * @p Solid means one continuous progress bar, @p Blocked means a + * progress bar made up of several blocks. + */ + enum BarStyle { Solid, Blocked }; + + /** + * Construct a horizontal progress bar. + */ + KGameProgress(QWidget *parent=0, const char *name=0); + + /** + * Construct a progress bar with orientation @p orient. + */ + KGameProgress(Orientation orient, QWidget *parent=0, const char *name=0); + + /** + * Construct a progress bar with minimum, maximum and initial values. + */ + KGameProgress(int minValue, int maxValue, int value, Orientation, + QWidget *parent=0, const char *name=0); + + /** + * Destruct the progress bar. + */ + ~KGameProgress(); + + /** + * Set the progress bar style. + * + * Allowed values are @p Solid and @p Blocked. + */ + void setBarStyle(BarStyle style); + + /** + * Set the color of the progress bar. + */ + void setBarColor(const QColor &); + + /** + * Set a pixmap to be shown in the progress bar. + */ + void setBarPixmap(const QPixmap &); + + /** + * Set the orientation of the progress bar. + * + * Allowed values are @p Horizontal and @p Vertical. + */ + void setOrientation(Orientation); + + /** + * If this is set to @p true, the progress text will be displayed. + * + */ + void setTextEnabled(bool); + + /** + * Retrieve the bar style. + * + * @see setBarStyle() + */ + BarStyle barStyle() const; + + /** + * Retrieve the bar color. + * @see setBarColor() + */ + const QColor &barColor() const; + + /** + * Retrieve the bar pixmap. + * + * @see setBarPixmap() + */ + const QPixmap *barPixmap() const; + + /** + * Retrive the current status + * + * @see setValue() + */ + int value() const { return QRangeControl::value(); } + /** + * Retrive the orientation of the progress bar. + * + * @see setOrientation() + */ + Orientation orientation() const; + + /** + * Returns @p true if progress text will be displayed, + * @p false otherwise. + * + * @see setFormat() + */ + bool textEnabled() const; + + /** + */ + virtual QSize sizeHint() const; + + /** + */ + virtual QSize minimumSizeHint() const; + + /** + */ + virtual QSizePolicy sizePolicy() const; + + /** + * Retrieve the current format for printing status text. + * @see setFormat() + */ + QString format() const; + +public slots: + + /** + * Set the format of the text to use to display status. + * + * The default format is "%p%" (which looks like "42%".) + * + * @param format %p is replaced by percentage done, %v is replaced by actual + * value, %m is replaced by the maximum value. + */ + void setFormat(const QString & format); + + /** + * Set the current value of the progress bar to @p value. + * + * This must be a number in the range 0..100. + */ + void setValue(int value); + + /** + * Advance the progress bar by @p prog. + * + * This method is + * provided for convenience and is equivalent with + * setValue(value()+prog). + */ + void advance(int prog); + +signals: + /** + * Emitted when the state of the progress bar changes. + */ + void percentageChanged(int); + +protected: + /** + */ + void valueChange(); + /** + */ + void rangeChange(); + /** + */ + void styleChange( QStyle& ); + /** + */ + void paletteChange( const QPalette & ); + /** + */ + void drawContents( QPainter * ); + +private slots: + void paletteChange(); + +private: + QPixmap *bar_pixmap; + bool use_supplied_bar_color; + QColor bar_color; + QColor bar_text_color; + QColor text_color; + QRect fr; + BarStyle bar_style; + Orientation orient; + bool text_enabled; + QString format_; + void initialize(); + int recalcValue(int); + void drawText(QPainter *); + void adjustStyle(); + + class KGameProgressPrivate; + KGameProgressPrivate *d; +}; + + +#endif diff --git a/libkdegames/kgrid2d.h b/libkdegames/kgrid2d.h new file mode 100644 index 00000000..f9dfd8dd --- /dev/null +++ b/libkdegames/kgrid2d.h @@ -0,0 +1,520 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001-02 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 __KGRID2D_H_ +#define __KGRID2D_H_ + +#include <math.h> + +#include <qpair.h> +#include <qvaluelist.h> +#include <qvaluevector.h> + +#include <kglobal.h> + + +//----------------------------------------------------------------------------- +namespace KGrid2D +{ + /** + * This type represents coordinates on a bidimensionnal grid. + * @since 3.2 + */ + typedef QPair<int, int> Coord; + + /** + * This type represents a list of @ref Coord. + * @since 3.2 + */ + typedef QValueList<Coord> CoordList; +} + +inline KGrid2D::Coord +operator +(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(c1.first + c2.first, c1.second + c2.second); +} + +inline KGrid2D::Coord +operator -(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(c1.first - c2.first, c1.second - c2.second); +} + +/** + * @return the maximum of both coordinates. + * @since 3.2 + */ +inline KGrid2D::Coord +maximum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(kMax(c1.first, c2.first), kMax(c1.second, c2.second)); +} +/** + * @return the minimum of both coordinates. + * @since 3.2 + */ +inline KGrid2D::Coord +minimum(const KGrid2D::Coord &c1, const KGrid2D::Coord &c2) { + return KGrid2D::Coord(kMin(c1.first, c2.first), kMin(c1.second, c2.second)); +} + +inline QTextStream &operator <<(QTextStream &s, const KGrid2D::Coord &c) { + return s << '(' << c.second << ", " << c.first << ')'; +} + +inline QTextStream &operator <<(QTextStream &s, const KGrid2D::CoordList &list) +{ + for(KGrid2D::CoordList::const_iterator i=list.begin(); i!=list.end(); ++i) + s << *i; + return s; +} + +//----------------------------------------------------------------------------- +namespace KGrid2D +{ +/** + * This template class represents a generic bidimensionnal grid. Each node + * contains an element of the template type. + * + * @since 3.2 + */ +template <class Type> +class Generic +{ + public: + /** + * Constructor. + */ + Generic(uint width = 0, uint height = 0) { + resize(width, height); + } + + virtual ~Generic() {} + + /** + * Resize the grid. + */ + void resize(uint width, uint height) { + _width = width; + _height = height; + _vector.resize(width*height); + } + + /** + * Fill the nodes with the given value. + */ + void fill(const Type &value) { + for (uint i=0; i<_vector.count(); i++) _vector[i] = value; + } + + /** + * @return the width. + */ + uint width() const { return _width; } + /** + * @return the height. + */ + uint height() const { return _height; } + /** + * @return the number of nodes (ie width*height). + */ + uint size() const { return _width*_height; } + + /** + * @return the linear index for the given coordinate. + */ + uint index(const Coord &c) const { + return c.first + c.second*_width; + } + + /** + * @return the coordinate corresponding to the linear index. + */ + Coord coord(uint index) const { + return Coord(index % _width, index / _width); + } + + /** + * @return the value at the given coordinate. + */ + const Type &at(const Coord &c) const { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + Type &at(const Coord &c) { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + const Type &operator [](const Coord &c) const { return _vector[index(c)]; } + /** + * @return the value at the given coordinate. + */ + Type &operator [](const Coord &c) { return _vector[index(c)]; } + + /** + * @return the value at the given linear index. + */ + const Type &at(uint index) const { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + Type &at(uint index) { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + const Type &operator [](uint index) const { return _vector[index]; } + /** + * @return the value at the given linear index. + */ + Type &operator [](uint index) { return _vector[index]; } + + /** + * @return if the given coordinate is inside the grid. + */ + bool inside(const Coord &c) const { + return ( c.first>=0 && c.first<(int)_width + && c.second>=0 && c.second<(int)_height ); + } + + /** + * Bound the given coordinate with the grid dimensions. + */ + void bound(Coord &c) const { + c.first = kMax(kMin(c.first, (int)_width-1), 0); + c.second = kMax(kMin(c.second, (int)_height-1), 0); + } + + protected: + uint _width, _height; + QValueVector<Type> _vector; +}; +} + +template <class Type> +QDataStream &operator <<(QDataStream &s, const KGrid2D::Generic<Type> &m) { + s << (Q_UINT32)m.width() << (Q_UINT32)m.height(); + for (uint i=0; i<m.size(); i++) s << m[i]; + return s; +} + +template <class Type> +QDataStream &operator >>(QDataStream &s, KGrid2D::Generic<Type> &m) { + Q_UINT32 w, h; + s >> w >> h; + m.resize(w, h); + for (uint i=0; i<m.size(); i++) s >> m[i]; + return s; +} + + +namespace KGrid2D +{ + +//----------------------------------------------------------------------------- +/** + * This class contains static methods to manipulate coordinates for a + * square bidimensionnal grid. + * + * @since 3.2 + */ +class SquareBase +{ + public: + /** + * Identify the eight neighbours. + */ + enum Neighbour { Left=0, Right, Up, Down, LeftUp, LeftDown, + RightUp, RightDown, Nb_Neighbour }; + + /** + * @return the trigonometric angle in radians for the given neighbour. + */ + static double angle(Neighbour n) { + switch (n) { + case Left: return M_PI; + case Right: return 0; + case Up: return M_PI_2; + case Down: return -M_PI_2; + case LeftUp: return 3.0*M_PI_4; + case LeftDown: return -3.0*M_PI_4; + case RightUp: return M_PI_4; + case RightDown: return -M_PI_4; + case Nb_Neighbour: Q_ASSERT(false); + } + return 0; + } + + /** + * @return the opposed neighbour. + */ + static Neighbour opposed(Neighbour n) { + switch (n) { + case Left: return Right; + case Right: return Left; + case Up: return Down; + case Down: return Up; + case LeftUp: return RightDown; + case LeftDown: return RightUp; + case RightUp: return LeftDown; + case RightDown: return LeftUp; + case Nb_Neighbour: Q_ASSERT(false); + } + return Nb_Neighbour; + } + + /** + * @return true if the neighbour is a direct one (ie is one of the four + * nearest). + */ + static bool isDirect(Neighbour n) { return n<LeftUp; } + + /** + * @return the neighbour for the given coordinate. + */ + static Coord neighbour(const Coord &c, Neighbour n) { + switch (n) { + case Left: return c + Coord(-1, 0); + case Right: return c + Coord( 1, 0); + case Up: return c + Coord( 0, -1); + case Down: return c + Coord( 0, 1); + case LeftUp: return c + Coord(-1, -1); + case LeftDown: return c + Coord(-1, 1); + case RightUp: return c + Coord( 1, -1); + case RightDown: return c + Coord( 1, 1); + case Nb_Neighbour: Q_ASSERT(false); + } + return c; + } +}; + +/** + * This template is a @ref Generic implementation for a square bidimensionnal + * grid (@ref SquareBase). + * + * @since 3.2 + */ +template <class T> +class Square : public Generic<T>, public SquareBase +{ + public: + /** + * Constructor. + */ + Square(uint width = 0, uint height = 0) + : Generic<T>(width, height) {} + + /** + * @return the neighbours of coordinate @param c + * to the given set of coordinates + * @param c the coordinate to use as the reference point + * @param insideOnly only add coordinates that are inside the grid. + * @param directOnly only add the four nearest neighbours. + */ + CoordList neighbours(const Coord &c, bool insideOnly = true, + bool directOnly = false) const { + CoordList neighbours; + for (uint i=0; i<(directOnly ? LeftUp : Nb_Neighbour); i++) { + Coord n = neighbour(c, (Neighbour)i); + if ( insideOnly && !Generic<T>::inside(n) ) continue; + neighbours.append(n); + } + return neighbours; + } + + /** + * @return the "projection" of the given coordinate on the grid edges. + * + * @param c the coordinate to use as the reference point + * @param n the direction of projection. + */ + Coord toEdge(const Coord &c, Neighbour n) const { + switch (n) { + case Left: return Coord(0, c.second); + case Right: return Coord(Generic<T>::width()-1, c.second); + case Up: return Coord(c.first, 0); + case Down: return Coord(c.first, Generic<T>::height()-1); + case LeftUp: return Coord(0, 0); + case LeftDown: return Coord(0, Generic<T>::height()-1); + case RightUp: return Coord(Generic<T>::width()-1, 0); + case RightDown: return Coord(Generic<T>::width()-1, Generic<T>::height()-1); + case Nb_Neighbour: Q_ASSERT(false); + } + return c; + } +}; + +//----------------------------------------------------------------------------- +/** + * This class contains static methods to manipulate coordinates on an + * hexagonal grid where hexagons form horizontal lines: + * <pre> + * (0,0) (0,1) (0,2) + * (1,0) (1,1) (1,2) + * (2,0) (2,1) (2,2) + * </pre> + * + * @since 3.2 + */ +class HexagonalBase +{ + public: + /** + * Identify the six neighbours. + */ + enum Neighbour { Left = 0, Right, LeftUp, LeftDown, + RightUp, RightDown, Nb_Neighbour }; + + /** + * @return the trigonometric angle in radians for the given neighbour. + */ + static double angle(Neighbour n) { + switch (n) { + case Left: return M_PI; + case Right: return 0; + case LeftUp: return 2.0*M_PI/3; + case LeftDown: return -2.0*M_PI/3; + case RightUp: return M_PI/3; + case RightDown: return -M_PI/3; + case Nb_Neighbour: Q_ASSERT(false); + } + return 0; + } + + /** + * @return the opposed neighbour. + */ + static Neighbour opposed(Neighbour n) { + switch (n) { + case Left: return Right; + case Right: return Left; + case LeftUp: return RightDown; + case LeftDown: return RightUp; + case RightUp: return LeftDown; + case RightDown: return LeftUp; + case Nb_Neighbour: Q_ASSERT(false); + } + return Nb_Neighbour; + } + + /** + * @return the neighbour of the given coordinate. + */ + static Coord neighbour(const Coord &c, Neighbour n) { + bool oddRow = c.second%2; + switch (n) { + case Left: return c + Coord(-1, 0); + case Right: return c + Coord( 1, 0); + case LeftUp: return c + (oddRow ? Coord( 0, -1) : Coord(-1, -1)); + case LeftDown: return c + (oddRow ? Coord( 0, 1) : Coord(-1, 1)); + case RightUp: return c + (oddRow ? Coord( 1, -1) : Coord( 0, -1)); + case RightDown: return c + (oddRow ? Coord( 1, 1) : Coord( 0, 1)); + case Nb_Neighbour: Q_ASSERT(false); + } + return c; + } + + /** + * @return the distance between the two coordinates in term of hexagons. + */ + static uint distance(const Coord &c1, const Coord &c2) { + return kAbs(c1.first - c2.first) + kAbs(c1.second - c2.second) + + (c1.first==c2.first || c1.second==c2.second ? 0 : -1); + } +}; + +/** + * This template implements a hexagonal grid + * where hexagons form horizontal lines: + * <pre> + * (0,0) (0,1) (0,2) + * (1,0) (1,1) (1,2) + * (2,0) (2,1) (2,2) + * </pre> + * + * @ since 3.2 + */ +template <class Type> +class Hexagonal : public Generic<Type>, public HexagonalBase +{ + public: + /** + * Constructor. + */ + Hexagonal(uint width = 0, uint height = 0) + : Generic<Type>(width, height) {} + + /** + * @return the neighbours of coordinate @param c + * to the given set of coordinates + * @param c the coordiante to use as the reference point + * @param insideOnly only add coordinates that are inside the grid. + */ + CoordList neighbours(const Coord &c, bool insideOnly = true) const { + CoordList neighbours; + for (uint i=0; i<Nb_Neighbour; i++) { + Coord n = neighbour(c, (Neighbour)i); + if ( insideOnly && !Generic<Type>::inside(n) ) continue; + neighbours.append(n); + } + return neighbours; + } + + + /** + * @return the neighbours at distance @param distance of coordinate + * @param c the coordinate to use as the reference point + * @param distance distance to the neighbour (1 means at contact). + * @param insideOnly only add coordinates that are inside the grid. + * @param all returns all neighbours at distance equal and less than + * @param distance (the original coordinate is not included). + */ + CoordList neighbours(const Coord &c, uint distance, bool all, + bool insideOnly = true) const { + // brute force algorithm -- you're welcome to make it more efficient :) + CoordList ring; + if ( distance==0 ) return ring; + ring = neighbours(c, insideOnly); + if ( distance==1 ) return ring; + CoordList center; + center.append(c); + for (uint i=1; i<distance; i++) { + CoordList newRing; + CoordList::const_iterator it; + for (it=ring.begin(); it!=ring.end(); ++it) { + CoordList n = neighbours(*it, insideOnly); + CoordList::const_iterator it2; + for (it2=n.begin(); it2!=n.end(); ++it2) + if ( center.find(*it2)==center.end() + && ring.find(*it2)==ring.end() + && newRing.find(*it2)==newRing.end() ) + newRing.append(*it2); + center.append(*it); + } + ring = newRing; + } + if ( !all ) return ring; + CoordList::const_iterator it; + for (it=ring.begin(); it!=ring.end(); ++it) + center.append(*it); + center.remove(c); + return center; + } +}; + +} // namespace + +#endif diff --git a/libkdegames/kstdgameaction.cpp b/libkdegames/kstdgameaction.cpp new file mode 100644 index 00000000..53b8f16c --- /dev/null +++ b/libkdegames/kstdgameaction.cpp @@ -0,0 +1,209 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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 "kstdgameaction.h" + +#include <klocale.h> +#include <kaction.h> +#include <kstdaccel.h> +#include <kconfig.h> +#include <kdebug.h> + + +KStdGameAction::KStdGameAction() +{} + +KStdGameAction::~KStdGameAction() +{} + +KAction *KStdGameAction::action(StdGameAction act_enum, const QObject *recvr, + const char *slot, KActionCollection *parent, + const char *name) +{ + return create( act_enum, name, recvr, slot, parent ); +} + +const char* KStdGameAction::stdName(StdGameAction act_enum) +{ + return name(act_enum); +} + +struct KStdGameActionInfo +{ + KStdGameAction::StdGameAction id; + KStdAccel::StdAccel globalAccel; // if we reuse a global accel + int shortcut; // specific shortcut (NH: should be configurable) + const char* psName; + const char* psLabel; + const char* psWhatsThis; + const char* psIconName; +}; + +const KStdGameActionInfo g_rgActionInfo[] = { +// "game" menu + { KStdGameAction::New, KStdAccel::New, 0, "game_new", I18N_NOOP2("new game", "&New"), 0, "filenew" }, + { KStdGameAction::Load, KStdAccel::Open, 0, "game_load", I18N_NOOP("&Load..."), 0, "fileopen" }, + { KStdGameAction::LoadRecent, KStdAccel::AccelNone, 0, "game_load_recent", I18N_NOOP("Load &Recent"), 0, 0 }, + { KStdGameAction::Restart, KStdAccel::Reload, 0, "game_restart", I18N_NOOP("Restart &Game"), 0, "reload" }, + { KStdGameAction::Save, KStdAccel::Save, 0, "game_save", I18N_NOOP("&Save"), 0, "filesave" }, + { KStdGameAction::SaveAs, KStdAccel::AccelNone, 0, "game_save_as", I18N_NOOP("Save &As..."), 0, "filesaveas" }, + { KStdGameAction::End, KStdAccel::End, 0, "game_end", I18N_NOOP("&End Game"), 0, "fileclose" }, + { KStdGameAction::Pause, KStdAccel::AccelNone, Qt::Key_P, "game_pause", I18N_NOOP("Pa&use"), 0, "player_pause" }, + { KStdGameAction::Highscores, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_H, "game_highscores", I18N_NOOP("Show &Highscores"), 0, "highscore" }, + { KStdGameAction::Print, KStdAccel::Print, 0, "game_print", I18N_NOOP("&Print..."), 0, "fileprint" }, + { KStdGameAction::Quit, KStdAccel::Quit, 0, "game_quit", I18N_NOOP("&Quit"), 0, "exit" }, +// "move" menu + { KStdGameAction::Repeat, KStdAccel::AccelNone, 0, "move_repeat", I18N_NOOP("Repeat"), 0, 0 }, + { KStdGameAction::Undo, KStdAccel::Undo, 0, "move_undo", I18N_NOOP("Und&o"), 0, "undo" }, + { KStdGameAction::Redo, KStdAccel::Redo, 0, "move_redo", I18N_NOOP("Re&do"), 0, "redo" }, + { KStdGameAction::Roll, KStdAccel::AccelNone, Qt::CTRL+Qt::Key_R, "move_roll", I18N_NOOP("&Roll Dice"), 0, "roll" }, + { KStdGameAction::EndTurn, KStdAccel::AccelNone, 0, "move_end_turn", I18N_NOOP("End Turn"), 0, "endturn" }, + { KStdGameAction::Hint, KStdAccel::AccelNone, Qt::Key_H, "move_hint", I18N_NOOP("&Hint"), 0, "idea" }, + { KStdGameAction::Demo, KStdAccel::AccelNone, Qt::Key_D, "move_demo", I18N_NOOP("&Demo"), 0, "1rightarrow" }, + { KStdGameAction::Solve, KStdAccel::AccelNone, 0, "move_solve", I18N_NOOP("&Solve"), 0, "wizard" }, +// "settings" menu + { KStdGameAction::ChooseGameType, KStdAccel::AccelNone, 0, "options_choose_game_type", I18N_NOOP("Choose Game &Type"), 0, 0 }, + { KStdGameAction::Carddecks, KStdAccel::AccelNone, 0, "options_configure_carddecks", I18N_NOOP("Configure &Carddecks..."), 0, 0 }, + { KStdGameAction::ConfigureHighscores, KStdAccel::AccelNone, 0, "options_configure_highscores", I18N_NOOP("Configure &Highscores..."), 0, 0 }, + + { KStdGameAction::ActionNone, KStdAccel::AccelNone, 0, 0, 0, 0, 0 } +}; + +static const KStdGameActionInfo* infoPtr( KStdGameAction::StdGameAction id ) +{ + for (uint i = 0; g_rgActionInfo[i].id!=KStdGameAction::ActionNone; i++) { + if( g_rgActionInfo[i].id == id ) + return &g_rgActionInfo[i]; + } + return 0; +} + + +KAction* KStdGameAction::create(StdGameAction id, const char *name, + const QObject *recvr, const char *slot, + KActionCollection* parent ) +{ + KAction* pAction = 0; + const KStdGameActionInfo* pInfo = infoPtr( id ); + kdDebug(125) << "KStdGameAction::create( " << id << "=" << (pInfo ? pInfo->psName : (const char*)0) << ", " << parent << ", " << name << " )" << endl; + if( pInfo ) { + QString sLabel = i18n(pInfo->psLabel); + KShortcut cut = (pInfo->globalAccel==KStdAccel::AccelNone + ? KShortcut(pInfo->shortcut) + : KStdAccel::shortcut(pInfo->globalAccel)); + const char *n = name ? name : pInfo->psName; + switch( id ) { + case LoadRecent: + pAction = + new KRecentFilesAction(sLabel, cut, recvr, slot, parent, n); + break; + case Pause: + case Demo: + pAction = new KToggleAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + case ChooseGameType: + pAction = new KSelectAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + default: + pAction = new KAction( sLabel, pInfo->psIconName, cut, + recvr, slot, parent, n); + break; + } + } + return pAction; +} + +const char* KStdGameAction::name( StdGameAction id ) +{ + const KStdGameActionInfo* pInfo = infoPtr( id ); + return (pInfo) ? pInfo->psName : 0; +} + +KAction *KStdGameAction::gameNew(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(New, name, recvr, slot, parent); } +KAction *KStdGameAction::load(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Load, name, recvr, slot, parent); } +KRecentFilesAction *KStdGameAction::loadRecent(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return static_cast<KRecentFilesAction *>(KStdGameAction::create(LoadRecent, name, recvr, slot, parent)); } +KAction *KStdGameAction::save(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Save, name, recvr, slot, parent); } +KAction *KStdGameAction::saveAs(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(SaveAs, name, recvr, slot, parent); } +KAction *KStdGameAction::end(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(End, name, recvr, slot, parent); } +KToggleAction *KStdGameAction::pause(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return static_cast<KToggleAction *>(KStdGameAction::create(Pause, name, recvr, slot, parent)); } +KAction *KStdGameAction::highscores(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Highscores, name, recvr, slot, parent); } +KAction *KStdGameAction::print(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Print, name, recvr, slot, parent); } +KAction *KStdGameAction::quit(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Quit, name, recvr, slot, parent); } + +KAction *KStdGameAction::repeat(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Repeat, name, recvr, slot, parent); } +KAction *KStdGameAction::undo(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Undo, name, recvr, slot, parent); } + +KAction *KStdGameAction::redo(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Redo, name, recvr, slot, parent); } + +KAction *KStdGameAction::roll(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Roll, name, recvr, slot, parent); } +KAction *KStdGameAction::endTurn(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(EndTurn, name, recvr, slot, parent); } + +KAction *KStdGameAction::carddecks(const QObject *recvr, const char *slot, + KActionCollection *parent, const char *name ) +{ return KStdGameAction::create(Carddecks, name, recvr, slot, parent); } +KAction *KStdGameAction::configureHighscores(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(ConfigureHighscores, name, recvr, slot, parent); } +KAction *KStdGameAction::hint(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Hint, name, recvr, slot, parent); } +KToggleAction *KStdGameAction::demo(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return static_cast<KToggleAction *>(KStdGameAction::create(Demo, name, recvr, slot, parent)); } +KAction *KStdGameAction::solve(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Solve, name, recvr, slot, parent); } +KSelectAction *KStdGameAction::chooseGameType(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return static_cast<KSelectAction *>(KStdGameAction::create(ChooseGameType, name, recvr, slot, parent)); } +KAction *KStdGameAction::restart(const QObject*recvr, const char *slot, + KActionCollection *parent, const char *name) +{ return KStdGameAction::create(Restart, name, recvr, slot, parent); } diff --git a/libkdegames/kstdgameaction.h b/libkdegames/kstdgameaction.h new file mode 100644 index 00000000..a38082af --- /dev/null +++ b/libkdegames/kstdgameaction.h @@ -0,0 +1,261 @@ +/* + This file is part of the KDE games library + Copyright (C) 2001 Andreas Beckermann (b_mann@gmx.de) + + 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. +*/ +// this class was shamelessy stolen from kdelibs/kdeui/kstdction.[cpp|h] and +// after that just edited for our needs +#ifndef KSTDGAMEACTION_H +#define KSTDGAMEACTION_H + +class KAction; +class KToggleAction; +class QObject; +class KActionCollection; +class KRecentFilesAction; +class KSelectAction; +#include <kdemacros.h> + +//----------------------------------------------------------------------------- +/** + * Replacement for KStdAction for KDE Games + * + * This class is an extension to the usual KStdAction class which provides + * easy access to often used KDE actions + * + * Games often use different menu entries than other programs, e.g. games use + * the menu "game" instead of "file". This class provides the entries which + * differ from the usual KStdAction entries. + * + * @see KStdAction + * + * @author Andreas Beckermann <b_mann@gmx.de> + */ +// #### KDE4: transform in namespace +class KDE_EXPORT KStdGameAction +{ +public: + /** + * The standard menubar and toolbar actions. + **/ + enum StdGameAction { + // Game menu + New=1, Load, LoadRecent, Save, SaveAs, End, Pause, Highscores, + Print, Quit, + // Move menu + Repeat, Undo, Redo, Roll, EndTurn, + // Settings menu + Carddecks, + ChooseGameType, // @since 3.2 + ConfigureHighscores, // @since 3.2 + + Restart, // @since 3.2 + Hint, // @since 3.2 + Demo, // @since 3.2 + Solve, // @since 3.2 + ActionNone // @since 3.2 + }; + + KStdGameAction(); + ~KStdGameAction(); + + /** + * Creates an action corresponding to the + * KStdAction::StdAction enum. + * @since 3.2 + */ + static KAction* create( StdGameAction id, const char *name, + const QObject *recvr, const char *slot, + KActionCollection* parent ); + + /** + * @since 3.2 + */ + static KAction* create( StdGameAction id, + const QObject *recvr, const char *slot, + KActionCollection* parent ) + { return create( id, 0, recvr, slot, parent ); } + + + /** + * Retrieve the action corresponding to the + * KStdGameAction::StdGameAction enum. + * @deprecated + */ + static KAction *action(StdGameAction act_enum, const QObject *recvr = 0, + const char *slot = 0, KActionCollection *parent = 0, + const char *name = 0L ); + + /** + * This will return the internal name of a given standard action. + * @since 3.2 + */ + static const char* name( StdGameAction id ); + + /** + * This will return the internal name of a given standard action. + * @deprecated + */ + static const char* stdName(StdGameAction act_enum); + + /** + * Start a new game + **/ + static KAction *gameNew(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Load a previousely saved game + */ + static KAction *load(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Load a recently loaded game. + */ + static KRecentFilesAction *loadRecent(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Save the current game. + */ + static KAction *save(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Save the current game under a different filename. + */ + static KAction *saveAs(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Pause the game + **/ + static KToggleAction *pause(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Show the highscores. + */ + static KAction *highscores(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + /** + * End the current game, but do not quit the program. Think of a "close" + * entry. + */ + static KAction *end(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Print the current screen? Game? Whatever - hardly used in games but there + * is at least one example (ktuberling) + */ + static KAction *print(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Quit the game. + */ + static KAction *quit(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + + /** + * Repeat the last move. + **/ + static KAction *repeat(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Undo the last move + **/ + static KAction *undo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Redo the last move (which has been undone) + **/ + static KAction *redo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Roll die or dice + **/ + static KAction *roll(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * End the current turn (not the game). Usually to let the next player + * start + **/ + static KAction *endTurn(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + + /** + * Display configure carddecks dialog. + */ + static KAction *carddecks(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Display configure highscores dialog. + * @since 3.2 + */ + static KAction *configureHighscores(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Give an advice/hint. + * @since 3.2 + */ + static KAction *hint(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Show a demo. + * @since 3.2 + */ + static KToggleAction *demo(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Solve the game. + * @since 3.2 + */ + static KAction *solve(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Choose game type. + * @since 3.2 + */ + static KSelectAction *chooseGameType(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + + /** + * Restart game. + * @since 3.2 + */ + static KAction *restart(const QObject *recvr = 0, const char *slot = 0, + KActionCollection *parent = 0, const char *name = 0L ); + +}; + +#endif diff --git a/libkdegames/pics/Makefile.am b/libkdegames/pics/Makefile.am new file mode 100644 index 00000000..8f94956a --- /dev/null +++ b/libkdegames/pics/Makefile.am @@ -0,0 +1,4 @@ +picsdir = $(kde_datadir)/kdegames/pics +pics_DATA = star.png + +KDE_ICON = action-roll action-highscore action-endturn diff --git a/libkdegames/pics/cr16-action-endturn.png b/libkdegames/pics/cr16-action-endturn.png Binary files differnew file mode 100644 index 00000000..00051f46 --- /dev/null +++ b/libkdegames/pics/cr16-action-endturn.png diff --git a/libkdegames/pics/cr16-action-highscore.png b/libkdegames/pics/cr16-action-highscore.png Binary files differnew file mode 100644 index 00000000..8eb54762 --- /dev/null +++ b/libkdegames/pics/cr16-action-highscore.png diff --git a/libkdegames/pics/cr16-action-roll.png b/libkdegames/pics/cr16-action-roll.png Binary files differnew file mode 100644 index 00000000..e41d572a --- /dev/null +++ b/libkdegames/pics/cr16-action-roll.png diff --git a/libkdegames/pics/cr22-action-roll.png b/libkdegames/pics/cr22-action-roll.png Binary files differnew file mode 100644 index 00000000..bdf85a8b --- /dev/null +++ b/libkdegames/pics/cr22-action-roll.png diff --git a/libkdegames/pics/cr32-action-endturn.png b/libkdegames/pics/cr32-action-endturn.png Binary files differnew file mode 100644 index 00000000..fc6d82bc --- /dev/null +++ b/libkdegames/pics/cr32-action-endturn.png diff --git a/libkdegames/pics/cr32-action-highscore.png b/libkdegames/pics/cr32-action-highscore.png Binary files differnew file mode 100644 index 00000000..ad797080 --- /dev/null +++ b/libkdegames/pics/cr32-action-highscore.png diff --git a/libkdegames/pics/cr32-action-roll.png b/libkdegames/pics/cr32-action-roll.png Binary files differnew file mode 100644 index 00000000..8452f4a8 --- /dev/null +++ b/libkdegames/pics/cr32-action-roll.png diff --git a/libkdegames/pics/star.png b/libkdegames/pics/star.png Binary files differnew file mode 100644 index 00000000..ea88d160 --- /dev/null +++ b/libkdegames/pics/star.png |