diff options
Diffstat (limited to 'kgoldrunner/src')
28 files changed, 9844 insertions, 0 deletions
diff --git a/kgoldrunner/src/KGoldrunner.desktop b/kgoldrunner/src/KGoldrunner.desktop new file mode 100644 index 00000000..6e22384a --- /dev/null +++ b/kgoldrunner/src/KGoldrunner.desktop @@ -0,0 +1,122 @@ +# KDE Config File +[Desktop Entry] +Name=KGoldrunner +Name[ar]=لعبة البحث عن الذهب (KGoldrunner) +Name[be]=Залатая ліхаманка +Name[bn]=কে-গোল্ডরানার +Name[fr]=KGoldRunner +Name[hi]=के-गोल्डरनर +Name[hr]=KPotraga za zlatom +Name[ne]=केडीई गोल्ड रनर +Name[pa]=ਕੇ-ਗੋਲਡ ਰਨਰ +Name[sv]=Kgoldrunner +Name[ta]=கேதங்கம் ஓடுபவர் +Name[tg]=KҶӯяндаи тилло +Name[uk]=Золотошукач +Name[zh_TW]=KGoldrunner 撿金塊 +GenericName=Action & Puzzle Solving Game +GenericName[be]=Актыўная гульня з галаваломкамі +GenericName[bg]=Бегач за злато +GenericName[bn]=অ্যাকশন আর ধাঁধাঁ সমাধানের খেলা +GenericName[bs]=Igra akcije i rješavanja zagonetki +GenericName[ca]=Joc d'acció i resolució de trencaclosques +GenericName[cs]=Řešení hádanek a akce +GenericName[cy]=Gêm Gynhyrfus o Ddatrys Posau +GenericName[da]=Handlings & mysteriums løsningsspil +GenericName[de]=Spiel zum Rätsellösen +GenericName[el]=Παιχνίδι δράσης και επίλυσης παζλ +GenericName[eo]=Agada kaj enigmosolva ludo +GenericName[es]=Juego de acción y resolución de rompecabezas +GenericName[et]=Põnev mõistatuste lahendamise mäng +GenericName[eu]=Ekintza eta puzzleen ebazpen jokoa +GenericName[fa]=بازی کنشی و حل معما +GenericName[fi]=Toiminta- & palikkapeli +GenericName[fr]=Jeu de résolution de puzzle et d'actions +GenericName[he]=משחק אקשן ופתרון חידות +GenericName[hr]=Igra akcije i rješavanja zagonetki +GenericName[hu]=Ügyességi és logikai +GenericName[is]=Leysa þrautir og læti +GenericName[it]=Gioco di azione e rompicapo +GenericName[ja]=アクションと謎解きゲーム +GenericName[km]=សកម្មភាព Puzzle ដោះស្រាយស្បែង +GenericName[ko]=액션 및 퍼즐 풀기 게임 +GenericName[lt]=Veiskmo ir dėlionių žaidimas +GenericName[lv]=Darbības un mīklu atrisināšanas spēle +GenericName[mk]=Игра со акција и решавање сложувалка +GenericName[nb]=Handlingsfylt grublespill +GenericName[nds]=Akschoon- un Radelsspeel +GenericName[ne]=काम र पजल समाधान गर्ने खेल +GenericName[nl]=Actie- en puzzelspel +GenericName[nn]=Handlingsfylt grublespel +GenericName[pl]=Układanka zręcznościowa +GenericName[pt]=Jogo de Acção e Puzzles +GenericName[pt_BR]=Jogo de ação e quebra-cabeça +GenericName[ru]=Золотоискатель +GenericName[sk]=Akčná a skladačková hra +GenericName[sl]=Igra akcije in reševanja ugank +GenericName[sr]=Акциона игра са решавањем загонетки +GenericName[sr@Latn]=Akciona igra sa rešavanjem zagonetki +GenericName[sv]=Action och pussellösningsspel +GenericName[ta]=செயல் & புதிர் விடுவிக்கும் விளையாட்டு +GenericName[uk]=Тактичка гра-головоломка +GenericName[zh_CN]=动作与解迷游戏 +GenericName[zh_TW]=動作與解謎遊戲 +Type=Application +Exec=kgoldrunner +Icon=kgoldrunner +DocPath=kgoldrunner/index.html +Comment=A game of action and puzzle-solving +Comment[ar]=لعبة حركة وحلَ ألغاز +Comment[be]=Актыўная гульня з галаваломкамі +Comment[bg]=Екшън игра с решаване на главоблъсканици +Comment[bn]=ধাঁধাঁ ও অ্যাকশন মেশানো এক দারুণ খেলা +Comment[bs]=Akciona igra sa rješavanjem zagonetke +Comment[ca]=Un joc d'acció i solució de trencaclosques +Comment[cs]=Akční hra s řešením hádanek +Comment[cy]=Gêm gynhyrfus o ddatrys posau +Comment[da]=Et spil med handling og problemløsning +Comment[de]=Aktionsreiches Spiel für Rätsellöser +Comment[el]=Ένα παιχνίδι δράσης και λύσης γρίφων +Comment[es]=Un juego de acción y resolución de rompecabezas +Comment[et]=Põnev mõistatuste lahendamise mäng +Comment[eu]=Ekintzarako eta puzzleen ebazpenerako jokoa +Comment[fa]=یک بازی کنشی و حل معما +Comment[fi]=Toiminta- ja pulmanratkaisupeli +Comment[fr]=Jeu d'action et de résolution d'énigmes +Comment[gl]=Un xogo de acción e resolución de puzzles +Comment[he]=משחק אקשן ופתרון חידות +Comment[hi]=पहेलियाँ हल करने और सक्रियता का एक खेल +Comment[hr]=Igra akcije i rješavanja zagonetki +Comment[hu]=Ügyességi játék rejtvényekkel tarkítva +Comment[is]=Leikur sem reynir á hraða og kænsku +Comment[it]=Un gioco d'azione e un rompicapo +Comment[ja]=アクションと謎解きゲーム +Comment[km]=ល្បែងនៃសកម្មភាព និងការដោះស្រាយ puzzle +Comment[ko]=액션 및 퍼즐 풀기 게임 +Comment[lt]=Veiksmo ir galvosūkių žaidimas +Comment[lv]=Darbības un loģikas spēle +Comment[mk]=Игра со акција и решавање на загатки +Comment[nb]=Et handlingsfylt spill med innlagte gåter +Comment[nds]=Een Speel mit veel Akschoon un Radels +Comment[ne]=काम र पजल समाधान गर्ने खेल +Comment[nl]=Een spel met veel actie en puzzels +Comment[nn]=Eit handlingsfylt spel med innlagde gåter +Comment[pl]=Gra akcji i rozwiązywania układanek +Comment[pt]=Um jogo de acção e resolução de quebra-cabeças +Comment[pt_BR]=Um jogo de ação e quebra-cabeça +Comment[ro]=Un joc de acţiune şi rezolvare de puzzle +Comment[ru]=Решай головоломки на ходу +Comment[sk]=Kombinovaná akčná a logická hra +Comment[sl]=Igra akcije in reševanja ugank +Comment[sr]=Игра акције и решавања загонетки +Comment[sr@Latn]=Igra akcije i rešavanja zagonetki +Comment[sv]=Ett spel med action och pussellösning +Comment[ta]=செயல்பாடு மற்றும் புதிர் விடுவிக்கும் விளையாட்டு +Comment[tg]=Бозии амалиёт ва ҳалли муаммоҳо +Comment[tr]=Aksiyon ve bulmaca çözümlerini barındıran bir oyun +Comment[uk]=Вирішення головоломок на ходу +Comment[zh_CN]=动作与解迷游戏 +Comment[zh_TW]= 一個動作和解謎的遊戲 +Comment[zu]=Umdlalo womnyakazo kanye neyokuxazulula i puzzle +Terminal=false +Categories=Qt;KDE;Game;ArcadeGame; diff --git a/kgoldrunner/src/Makefile.am b/kgoldrunner/src/Makefile.am new file mode 100644 index 00000000..b243765b --- /dev/null +++ b/kgoldrunner/src/Makefile.am @@ -0,0 +1,42 @@ +## Makefile.am for kgoldrunner + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = kgoldrunner + +# set the include path for X, qt and KDE +INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes) + +# the library search path. +kgoldrunner_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +# the libraries to link against. +kgoldrunner_LDADD = $(LIB_KDEGAMES) $(LIB_KFILE) $(LIB_KDEPRINT) +kgoldrunner_DEPENDENCIES = $(LIB_KDEGAMES_DEP) + +# which sources should be compiled for kgoldrunner +kgoldrunner_SOURCES = kgoldrunner.cpp kgrcanvas.cpp kgrfigure.cpp kgrgame.cpp kgrobject.cpp main.cpp kgrdialog.cpp + +# these are the headers for your project +noinst_HEADERS = enemy1.xpm enemy2.xpm hero.xpm kgoldrunner.h kgrcanvas.h kgrconsts.h kgrdialog.h kgrfigure.h kgrgame.h kgrobject.h kgraphics.h + +# client stuff + + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kgoldrunner.pot + +KDE_ICON = kgoldrunner + +# this is where the kdelnk file will go +xdg_apps_DATA = KGoldrunner.desktop + +# this is where the XML-GUI resource file goes +rcdir = $(kde_datadir)/kgoldrunner +rc_DATA = kgoldrunnerui.rc + +AM_CXXFLAGS = -DQT3 + diff --git a/kgoldrunner/src/data_messages.cpp b/kgoldrunner/src/data_messages.cpp new file mode 100644 index 00000000..838db2a8 --- /dev/null +++ b/kgoldrunner/src/data_messages.cpp @@ -0,0 +1,608 @@ +/* This is a dummy *.cpp program that contains strings extracted from + KGoldrunner game data files. It is not intended to be compiled or + executed. Its only function is to hold the strings in a place where + they will be re-formatted into a .pot file for the translators. +*/ + +#include <klocale.h> + +dont_call_me() { + +i18n("TRANSLATORS: Please see the notes in the data_messages.cpp file."); + +// NOTES: Strings in this file originate from KGoldrunner DATA files, +// as installed in $KDEDIR/share/apps/kgoldrunner/system. They are +// names and descriptions of games and names and hints for levels. The +// C++ comments indicate which data file the strings came from. You +// may sometimes need to look at the corresponding game or level graphics +// to work out a good translation (e.g. Initiation 25, Mongolian Horde). + +// To ease the burden on translators, this file does not include all the +// data messages. For example, the hints on championship games are not +// included. A player that good should not need a hint and maybe has no +// need of a translation ... :-) The most urgent translations would be +// names and hints for the Tutorial games, then the game names and +// descriptions (as appearing in the main dialog box) and then the other +// level names and hints. + +// From levels/tute001.grl (in levels.tar). +i18n("Hi !!"); +i18n("Hi ! Welcome to KGoldrunner ! The idea of the game is to pick up all the gold nuggets, then climb to the top of the playing area and move up to the next level. A hidden ladder will appear as you collect the last nugget." +"\n" +"\nThe hero (the green figure) is your deputy. To collect the nuggets, just point the mouse where you want him to go. At first gravity takes over and he falls ..." +); + +// From levels/tute002.grl (in levels.tar). +i18n("Navigation"); +i18n("This is an exercise in moving around. Follow the track of gold nuggets until the ladder pops up at the right. The hero can only follow the mouse along simple paths (like _ | L or U), so be careful not to get too far ahead of him." +"\n" +"\nDANGER: Try not to fall off the ladder or bar into the concrete pit at the bottom right. If you do get trapped there, the only way out is to kill the hero (press key Q for quit) and start the level again." +); + +// From levels/tute003.grl (in levels.tar). +i18n("Digging"); +i18n("Now you have to dig to get the gold! Just use the left and right mouse buttons to dig left or right of the hero's position. The hero can then jump into and through the hole he has dug. He can also dig several holes in a row and run sideways through the dug holes. Be careful though. After a while the holes close up and you can get trapped and killed." +"\n" +"\nIn the third box down, you have to dig two holes, jump in and quickly dig one more, to get through two layers. On the right, you have to dig three, then two then one to get through. There are also two little puzzles to work out along the way. Good luck!" +"\n" +"\nBy the way, you can dig through brick, but not concrete." +); + +// From levels/tute004.grl (in levels.tar). +i18n("You Have ENEMIES !!!"); +i18n("Well, it's been nice and easy up to now, but the game would be no fun without enemies. They are after the gold too: worse still they are after you! You die if they catch you, but maybe you will have a few lives left and can start again." +"\n" +"\nYou can handle enemies by running away, digging a hole or luring them into part of the playing area where they get stranded." +"\n" +"\nIf an enemy falls into a hole, he gives up any gold he is carrying, then gets stuck in the hole for a time and climbs out. If the hole closes while he is in it, he dies and reappears somewhere else on the screen. You can deliberately kill enemies by digging several holes in a row." +"\n" +"\nMore importantly, you can run over an enemy's head. You must do that right at the start of this level. Dig a hole, trap the enemy, wait for him to fall all the way in, then run over him, with the other enemy in hot pursuit..." +); + +// From levels/tute005.grl (in levels.tar). +i18n("Bars"); +i18n("You can move horizontally along bars (or poles), but if you move down you will let go and fall .... Also, note that you can collect gold by falling onto it." +); + +// From levels/tute006.grl (in levels.tar). +i18n("False Bricks"); +i18n("Some of the bricks in this level are not what they seem. If you walk onto them you fall through. The enemies fall through them too." +); + +// From levels/tute007.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/tutea001.grl (in levels.tar). +i18n("Bars and Ladders"); +i18n("There's nowhere to dig, so you must dodge the enemies and avoid falling to the concrete at the wrong time. Try to keep the enemies together." +"\n" +"\nIf an enemy has a gold outline, he is holding a nugget. He might drop it as he runs over the concrete or maybe at the top of a ladder ...... patience, patience !!" +); + +// From levels/tutea002.grl (in levels.tar). +i18n("To kill ....."); +i18n("You cannot get up to the gold, so you must get the enemies to bring it down to you. But how do you get them to keep going back?" +"\n" +"\nIf you are feeling stressed, you can hit the P or Esc key and take a break. Also, you can use the Settings menu to slow down the action." +); + +// From levels/tutea003.grl (in levels.tar). +i18n("... Or not to kill?"); +i18n("It is best not to kill the enemy. Try it and you will find out why ... Heh, heh, heh !! ... ;-)" +"\n" +"\nIf you do kill him unintentionally, before you have collected the gold at the top left, you can still finish the level by digging away the side of the pit he is in." +); + +// From levels/tutea004.grl (in levels.tar). +i18n("Traps"); +i18n("Some of the bricks here are not what they seem. They are known as false bricks, fall-through bricks or traps. If you try to walk on them, you fall through. If the enemies walk on them, they can descend on you without warning. Sometimes you have to fall through a brick to get some gold." +"\n" +"\nAt the start, jump into the concrete pit, dig and drop through. That will get one enemy tied up for a while .... :-)" +); + +// From levels/tutea005.grl (in levels.tar). +i18n("Bye ......."); +i18n("This is just a nice easy level to finish up with. Have fun with the other games in KGoldrunner." +"\n" +"\nNote that there is a game Editor in which you and your friends can make up levels and challenge each other. There is also a KGoldrunner Handbook in the Help Menu, which contains more detail than this Tutorial." +"\n" +"\nIt's been a pleasure showing you around. Bye !!!" +); + +// From levels/level000.grl (in levels.tar). + +// From levels/level001.grl (in levels.tar). + +// From levels/level002.grl (in levels.tar). + +// From levels/level003.grl (in levels.tar). + +// From levels/level004.grl (in levels.tar). + +// From levels/level005.grl (in levels.tar). + +// From levels/level006.grl (in levels.tar). + +// From levels/level007.grl (in levels.tar). + +// From levels/level008.grl (in levels.tar). + +// From levels/level009.grl (in levels.tar). + +// From levels/level010.grl (in levels.tar). + +// From levels/level011.grl (in levels.tar). + +// From levels/level012.grl (in levels.tar). + +// From levels/level013.grl (in levels.tar). + +// From levels/level014.grl (in levels.tar). + +// From levels/level015.grl (in levels.tar). + +// From levels/level016.grl (in levels.tar). + +// From levels/level017.grl (in levels.tar). + +// From levels/level018.grl (in levels.tar). + +// From levels/plws001.grl (in levels.tar). +i18n("Don't Panic"); + +// From levels/plws002.grl (in levels.tar). +i18n("Lust for Gold"); +i18n("When you kill the enemies you can trap them permanently in the pit at top right." +); + +// From levels/plws003.grl (in levels.tar). +i18n("Ladders? Trust me !"); + +// From levels/plws004.grl (in levels.tar). +i18n("Drop In and Say Hello"); + +// From levels/plws005.grl (in levels.tar). +i18n("The Mask"); + +// From levels/plws006.grl (in levels.tar). +i18n("Check for Traps"); + +// From levels/plws007.grl (in levels.tar). +i18n("Take It Easy !"); + +// From levels/plws008.grl (in levels.tar). +i18n("Fall on a Fortune"); + +// From levels/plws009.grl (in levels.tar). +i18n("The Lattice"); + +// From levels/plws010.grl (in levels.tar). +i18n("Shower of Gold"); + +// From levels/plws011.grl (in levels.tar). +i18n("The Foundry"); + +// From levels/plws012.grl (in levels.tar). +i18n("Soft Landings"); + +// From levels/plws013.grl (in levels.tar). +i18n("Unlucky for Some"); + +// From levels/plws014.grl (in levels.tar). +i18n("The Balance"); + +// From levels/plws015.grl (in levels.tar). +i18n("Gold Bars"); + +// From levels/plws016.grl (in levels.tar). +i18n("Hard Row to Hoe"); + +// From levels/plws017.grl (in levels.tar). +i18n("Golden Maze"); + +// From levels/plws018.grl (in levels.tar). +i18n("Delayed Trap"); + +// From levels/plws019.grl (in levels.tar). +i18n("Nowhere to Hide"); + +// From levels/plws020.grl (in levels.tar). +i18n("Watch the Centre"); + +// From levels/plws021.grl (in levels.tar). +i18n("Where to Dig?"); + +// From levels/plws022.grl (in levels.tar). +i18n("Easy Stages"); + +// From levels/plws023.grl (in levels.tar). +i18n("Gold Mesh"); + +// From levels/plws024.grl (in levels.tar). +i18n("Acrobat"); + +// From levels/plws025.grl (in levels.tar). +i18n("Mongolian Horde"); + +// From levels/plws026.grl (in levels.tar). +i18n("Rocky Terrain"); + +// From levels/plws027.grl (in levels.tar). +i18n("Down the Chimney"); + +// From levels/plws028.grl (in levels.tar). +i18n("Space Invader"); + +// From levels/plws029.grl (in levels.tar). +i18n("Winding Road"); + +// From levels/plws030.grl (in levels.tar). +i18n("Light My Fire"); + +// From levels/plws031.grl (in levels.tar). +i18n("Cockroach"); + +// From levels/plws032.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plws033.grl (in levels.tar). +i18n("Speedy"); + +// From levels/plws034.grl (in levels.tar). +i18n("Dig Deep"); + +// From levels/plws035.grl (in levels.tar). +i18n("Zig Zag"); + +// From levels/plws036.grl (in levels.tar). +i18n("Free Fall"); + +// From levels/plws037.grl (in levels.tar). +i18n("Forgotten Gold"); + +// From levels/plws038.grl (in levels.tar). +i18n("Two of Diamonds"); + +// From levels/plws039.grl (in levels.tar). +i18n("Suicide Jump"); + +// From levels/plws040.grl (in levels.tar). +i18n("Easy Access"); + +// From levels/plws041.grl (in levels.tar). +i18n("Gold Braid"); + +// From levels/plws042.grl (in levels.tar). +i18n("Cat's Eyes"); + +// From levels/plws043.grl (in levels.tar). +i18n("Keep 'em Coming"); + +// From levels/plws044.grl (in levels.tar). +i18n("The Funnel"); + +// From levels/plws045.grl (in levels.tar). +i18n("Lattice Maze"); + +// From levels/plws046.grl (in levels.tar). +i18n("Hard Work for Poor Pay"); + +// From levels/plws047.grl (in levels.tar). +i18n("Forked Ladders"); + +// From levels/plws048.grl (in levels.tar). +i18n("Snowing Gold"); + +// From levels/plws049.grl (in levels.tar). +i18n("Left or Right?"); + +// From levels/plws050.grl (in levels.tar). +i18n("Houndstooth"); + +// From levels/plws051.grl (in levels.tar). +i18n("Five Levels"); + +// From levels/plws052.grl (in levels.tar). +i18n("Pitfalls"); + +// From levels/plws053.grl (in levels.tar). +i18n("Get IN There !!"); + +// From levels/plws054.grl (in levels.tar). +i18n("A Steady Climb"); + +// From levels/plws055.grl (in levels.tar). +i18n("Fall-through Lattice"); + +// From levels/plws056.grl (in levels.tar). +i18n("Get me OUT of Here !!"); + +// From levels/plws057.grl (in levels.tar). +i18n("Empty Cellar"); + +// From levels/plws058.grl (in levels.tar). +i18n("The Rose"); + +// From levels/plws059.grl (in levels.tar). +i18n("Lotus Puzzle"); + +// From levels/plws060.grl (in levels.tar). +i18n("Long Drop"); + +// From levels/plws061.grl (in levels.tar). +i18n("Party On !!!"); + +// From levels/plws062.grl (in levels.tar). +i18n("Cross-stitch"); + +// From levels/plws063.grl (in levels.tar). +i18n("Can't Get Up There"); + +// From levels/plws064.grl (in levels.tar). +i18n("They're Everywhere !!!"); + +// From levels/plws065.grl (in levels.tar). +i18n("Rooftops"); + +// From levels/plws066.grl (in levels.tar). +i18n("Tricky Traps"); + +// From levels/plws067.grl (in levels.tar). +i18n("Make Them Work for You"); + +// From levels/plws068.grl (in levels.tar). +i18n("Get Going !!"); + +// From levels/plws069.grl (in levels.tar). +i18n("Three Chimneys"); + +// From levels/plws070.grl (in levels.tar). +i18n("The Archway"); + +// From levels/plws071.grl (in levels.tar). +i18n("Starwave"); + +// From levels/plws072.grl (in levels.tar). +i18n("Amazing Finish"); + +// From levels/plws073.grl (in levels.tar). +i18n("Overcrowding"); + +// From levels/plws074.grl (in levels.tar). +i18n("Pillars"); + +// From levels/plws075.grl (in levels.tar). +i18n("Hopeful Descent"); + +// From levels/plws076.grl (in levels.tar). +i18n("The Rack"); + +// From levels/plws077.grl (in levels.tar). +i18n("Twists and Turns"); + +// From levels/plws078.grl (in levels.tar). +i18n("The Saucer"); + +// From levels/plws079.grl (in levels.tar). +i18n("The Dotted Line"); + +// From levels/plws080.grl (in levels.tar). +i18n("Don't Look Down (1)"); + +// From levels/plws081.grl (in levels.tar). +i18n("Getting Started"); + +// From levels/plws082.grl (in levels.tar). +i18n("Digging Hassle"); + +// From levels/plws083.grl (in levels.tar). +i18n("Easy Middle"); + +// From levels/plws084.grl (in levels.tar). +i18n("Don't Look Down (2)"); + +// From levels/plws085.grl (in levels.tar). +i18n("Which Way?"); + +// From levels/plws086.grl (in levels.tar). +i18n("Don't Look Down (3)"); + +// From levels/plws087.grl (in levels.tar). +i18n("Drop ???"); + +// From levels/plws088.grl (in levels.tar). +i18n("Help !!!"); + +// From levels/plws089.grl (in levels.tar). +i18n("Yorick's Skull"); + +// From levels/plws090.grl (in levels.tar). +i18n("No Mercy"); + +// From levels/plws091.grl (in levels.tar). +i18n("Gold Sandwich"); + +// From levels/plws092.grl (in levels.tar). +i18n("Golden Curtain"); + +// From levels/plws093.grl (in levels.tar). +i18n("Are you spider or fly?"); + +// From levels/plws094.grl (in levels.tar). +i18n("Funny?"); + +// From levels/plws095.grl (in levels.tar). +i18n("Hard Landings"); + +// From levels/plws096.grl (in levels.tar). +i18n("Golden Tower"); + +// From levels/plws097.grl (in levels.tar). +// i18n("Das Boot"); // Not translated: it's the name of a German movie. +i18n("This level is named after the famous German submarine war film and dedicated to Marco Krüger of Berlin, the original author of KGoldrunner." +"\n" +"\nJust one small hint .... if you stand on the right hand end of the boat you can get the enemy to fall towards you .... the rest is up to you !!!!" +); + +// From levels/plws098.grl (in levels.tar). +i18n("Quick ! RUN !!!"); + +// From levels/plws099.grl (in levels.tar). +i18n("Surprise Ending"); + +// From levels/plws100.grl (in levels.tar). +i18n("Diagonal Disaster"); + +// From levels/wad001.grl (in levels.tar). +i18n("Easy Start"); + +// From levels/wad002.grl (in levels.tar). +i18n("Mobile Bricks"); + +// From levels/wad003.grl (in levels.tar). +i18n("The Big Haul"); + +// From levels/wad004.grl (in levels.tar). +i18n("Quick Off The Mark"); + +// From levels/wad005.grl (in levels.tar). +i18n("Who Needs Enemies?"); + +// From levels/wad006.grl (in levels.tar). +i18n("Asymmetrical"); + +// From levels/wad007.grl (in levels.tar). +i18n("Goldrunner Prophecy"); + +// From levels/wad008.grl (in levels.tar). +i18n("The Rosette"); + +// From levels/wad009.grl (in levels.tar). +i18n("He's Got the Gold"); + +// From levels/wad010.grl (in levels.tar). +i18n("Towers of Gold"); + +// From levels/wad011.grl (in levels.tar). +i18n("The Box"); + +// From levels/wad012.grl (in levels.tar). +i18n("Delayed Drop"); + +// From levels/wad013.grl (in levels.tar). +i18n("Maze of Ladders"); + +// From levels/wad014.grl (in levels.tar). +i18n("Ride 'em Down"); + +// From levels/wad015.grl (in levels.tar). +i18n("Hair's Breadth Timing"); + +// From levels/wad016.grl (in levels.tar). +i18n("The Three Musketeers"); + +// From levels/wad017.grl (in levels.tar). +i18n("Rat Trap"); + +// From levels/wad018.grl (in levels.tar). +i18n("Head Case"); + +// From levels/wad019.grl (in levels.tar). +i18n("Under the Stairs"); + +// From levels/wad020.grl (in levels.tar). +i18n("Bertie Beetle"); + +// From levels/wad021.grl (in levels.tar). +i18n("Short Circuit"); + +// From levels/wad022.grl (in levels.tar). +i18n("Synchronised Running"); + +// From levels/plwv001.grl (in levels.tar). +i18n("Impossible?"); + +// From levels/plwv002.grl (in levels.tar). +i18n("The Runaround"); + +// From levels/plwv003.grl (in levels.tar). +i18n("Short Cut?"); + +// From levels/plwv004.grl (in levels.tar). +i18n("Sky Walker"); + +// From levels/plwv005.grl (in levels.tar). +i18n("The Vault"); + +// From levels/plwv006.grl (in levels.tar). +i18n("Patchwork Quilt"); + +// From levels/plwv007.grl (in levels.tar). +i18n("Do You Need Him?"); + +// From levels/plwv008.grl (in levels.tar). +i18n("Stuck in Storage"); + +// From levels/plwv009.grl (in levels.tar). +i18n("So Far for So Little"); + +// From levels/plwv010.grl (in levels.tar). +i18n("Pharaoh's Tomb"); + +// From levels/plwv011.grl (in levels.tar). +i18n("Entangled"); + +// From levels/plwv012.grl (in levels.tar). +i18n("Flying Tower"); + +// From levels/plwv013.grl (in levels.tar). +i18n("Pot Hole"); + +// From levels/plwv014.grl (in levels.tar). +i18n("Sticky Ladders"); + +// From levels/plwv015.grl (in levels.tar). +i18n("The Laboratory"); + +// From levels/plwv016.grl (in levels.tar). +i18n("Pete likes Ladders"); + +// From levels/plwv017.grl (in levels.tar). +i18n("Where's the Roof?"); + +// From levels/plwv018.grl (in levels.tar). +i18n("Ninja Style"); + +// From levels/plwv019.grl (in levels.tar). +i18n("Cooperation?"); + +// From levels/plwv020.grl (in levels.tar). +i18n("Triple Trap"); + +// The following messages are titles and descriptions of games, +// which have been extracted from the file gamedata/games.dat. + +i18n("Initiation"); +i18n("These 100 levels make an excellent introductory game, as well as a good opportunity for experts to build up high scores. They were composed by Peter Wadham and use traditional playing rules.\n\nThe last few levels are very hard, but if you are looking for even more of a challenge, have a go at 'Vengeance of Peter W' .... ;-) ...."); + +i18n("Challenge"); +i18n("These tricky little levels were composed by Peter, Simon, Genevieve and their father Ian Wadham. They use traditional playing rules. Enjoy! .... ;-) ...."); + +i18n("Vengeance of Peter W"); +i18n("Gooood luck !!\nMwarrhh hwwarrrr haarrrr !!!"); + +i18n("KGoldrunner"); +i18n("These levels were composed by Marco Krüger, the original author of the KGoldrunner program, and some of his friends and contributors. They use KGoldrunner rules. The enemies run fast and have an aggressive search strategy. Enjoy! .... :-) ...."); + +i18n("Tutorial"); +i18n("This tutorial is a collection of easy levels that teaches you the rules of KGoldrunner and helps you develop the skills you need to get started. Each level has a brief explanation, then you play .....\n\nWhen you move on to play more advanced levels, you will find that KGoldrunner combines action, strategy and puzzle solving --- all in one game."); + +i18n("Advanced Tutorial"); +i18n("This tutorial is preparation for some of the things you might find in the middle levels of the 'Initiation' game. Enjoy ...."); +} diff --git a/kgoldrunner/src/enemy1.xpm b/kgoldrunner/src/enemy1.xpm new file mode 100644 index 00000000..a494edb7 --- /dev/null +++ b/kgoldrunner/src/enemy1.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static const char * enemy1_xpm[] = { +"320 16 6 1", +" c None", +". c #00FFFF", +"+ c #008080", +"@ c #0000FF", +"# c #000080", +"$ c #000000", +" .++ .++ ", +" .+@ +## +## +## .+@ .+@ +## .@@ +## .@@ +## .++ .+@ .+@ +@# ++# ", +" .@@# +## +## +### .@@ .@@ +## ++@@ +## .@@ +### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" +++## .++@@ +++## .++@@ ++++# .++@@ ++++# .++@@ ++@@#.++@.@@@ .@@ ++@@ .++@++@@ .@@ .@@@.++@++@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# ++@@++@@ ++# +++# .@@ .@@ +++# ++# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ ++@@.@@++@# .@++++ ++@@++@+@@# .@@++@ ++@+@@+.@@# .++@@@ ++@+@@+.@@# .+@.@@ +++@+@@ ++ ++ .@@+++# .@@# .@@@++@# ++@#.@@@ ++@@ ", +" ++@@## +++.@@@# ++@@## .@@@# +#+@@# +@@@@#++ +#+@@# ++@@@ ++@@@+@@# .@.@@@ ++@@@@@@# .@@@@@ ++@+@@@@# +@@+@@ ++@+@@@@# +@@.@@ ++@@@+++ .+.@@@@+ .@@@+++@@+@@# ++@@@@#+++@@@ ", +" +++#+@@# .@@@@@@@@@++@ ++++@@@@@## ++@ +++@@@@@@ ++@@++++ .+++@@@@@@@@@ .++ +++@@@@@@## .@@@@@@@# ++@@@@ ..+@ ++@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"+++@@@@@@@+ ++@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@+#.+++ .@@@@@@@++# +@@@+@@@@@+@@ .@@+@@@@@@#@@@@ +++@@@@@+@@@ .@@@# .@@+++ .@@@# .@@@++ ++@@@ +++@@@ ++@@@ ++.@@@ .@@@@@.# ++.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@+## .@@ .@@@@@@@ .@@++@@++@@@+@@@+@@@@@@##+@@ ##++@@@@## .@@++@@@ .@@ .@@@+.@++@@@ .@@@# ++@@@@@ .@@@# ++@@@@@ ++@@@ .@@@@@@ ++@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# ++@@@ ", +".@@ .@@@+@@@# .@@@@ ### ++@@@ +++@@@@$+## #@@@@+@@@ .@@ .@@@@ .@@@# +## +## .@@@@@# ++@@+@@@@ .@.### ++@@@@@@@ .@@### .+@@+@@@# +##.@@@ .+@@+@@@# +##.@@@# .@@@@@@@ .@@@@@@@ +++@@@@ .@@@@## ", +" .@@@++## .@@@+ .@@@@ .@@@+ ++++@@@@ ++@@@@ .@@@@ .@@@@ .@@@ ++@@ ++@@ .@@@ ++@@ ++@@ .@@# .@@@ .@@@ .@@# .@@@ .@@@ .@@.@@@@ .@@@.@@@ ++@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ +++@@@@@ .++@ +@@@@@# +++@@@@@ .@@@@@@@@ .@@@@@++ ++@@@@+ .++@ .@@@@@#++ ++@@ .@@@ .@@@ ++@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ ++@@ .@@# .@@ .@@# .@@ .@@ ++@@ ", +" .@@@ ++@@ .@@@@@+@@@@ +@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ ++@@# .@@ .@+@@ ++@@# +@@ .+.@@ +@@ ++@@# .@.+@ +@@ ++@@# .@.+@ ### .@@# ++@@ +## ++@@# .@@ .@@ ++@@# ", +" ++@@ .@@# +@####+#+@@@ #@@@@ .@@# +@####.@@@ ++@@ .@@# .@@@#+####@@ ++@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ ++@@ .@@ ++@@ .@@ ++@@ .@@# +@@# .@@ .@@ ++@@ ", +"++@@# +## +@@# ++@@# .@@@ ### ++@@# +@@# ++@@# .@@@ .@@# ++@@ .@@ .@@ ", +".@@# ++@@ ++@@@ ++@@ .@@# .@@@# +@@@ +@@# +@# ++# "}; diff --git a/kgoldrunner/src/enemy2.xpm b/kgoldrunner/src/enemy2.xpm new file mode 100644 index 00000000..7de334d0 --- /dev/null +++ b/kgoldrunner/src/enemy2.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * enemy2_xpm[] = { +"320 16 7 1", +" c None", +". c #FFFF00", +"+ c #808080", +"@ c #0000FF", +"# c #808000", +"$ c #000080", +"% c #000000", +" .++ .++ ", +" .+@ #$$ #$$ #$$ .+@ .+@ #$$ .@@ #$$ .@@ #$$ .++ .+@ .+@ #@$ #+$ ", +" .@@$ #$$ #$$ #$$$ .@@ .@@ #$$ #+@@ #$$ .@@ #$$$ .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" ###$$ .++@@ ###$$ .++@@ ####$ .++@@ ####$ .++@@ #+@@$.++@.@@@ .@@ #+@@ .++@#+@@ .@@ .@@@.++@#+@@$ .@@ .@@$.++@ .@@$ .@@ .@@++@@$ #+@@++@@ ##$ ###$ .@@ .@@ ###$ ##$ ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ #+@@.@@++@$ .@++++ #+@@++@+@@$ .@@++@ #+@+@@+.@@$ .++@@@ #+@+@@+.@@$ .+@.@@ #++@+@@ ## ## .@@+++$ .@@$ .@@@#+@$ #+@$.@@@ #+@@ ", +" #+@@$$ ###.@@@$ #+@@$$ .@@@$ #$+@@$ #@@@@$## #$+@@$ #+@@@ #+@@@+@@$ .@.@@@ #+@@@@@@$ .@@@@@ #+@+@@@@$ +@@+@@ #+@+@@@@$ +@@.@@ #+@@@+++ .+.@@@@# .@@@###@@+@@$ #+@@@@$##+@@@ ", +" ###$+@@$ .@@@@@@@@@#+@ ##++@@@@@$$ #+@ #++@@@@@@ #+@@#### .+#+@@@@@@@@@ .+# ##+@@@@@@$$ .@@@@@@@$ #+@@@@ ..+@ #+@@@@ .@@+ .@@@@$ .++@ .@@@@$ .@@@ .@@@+@@ .@@@@@@ $@@@@@@@@@ .@@@@@@@@$ ", +"#++@@@@@@@+ #+@@ .@+@@@@@+@@@$ .@@@$+@@@@@+@@@ .@@+@@@+@@#$.++# .@@@@@@@++$ #@@@+@@@@@+@@ .@@+@@@@@@$@@@@ ##+@@@@@+@@@ .@@@$ .@@+## .@@@$ .@@@## #+@@@ ##+@@@ #+@@@ ##.@@@ .@@@@@.$ #+.@@@@@ $+@@@@ .@@@@$ ", +".@@$$+@@@@@+@@@$ $$+@@@@#$$ .@@ .@@@@@@@ .@@++@@#+@@@#@@@+@@@@@@$$+@@ $$#+@@@@$$ .@@++@@@ .@@ .@@@#.@++@@@ .@@@$ #+@@@@@ .@@@$ #+@@@@@ #+@@@ .@@@@@@ #+@@@ .@@.@@@$ .+.@@@@@ .@@@@@@@ .@@@$ #+@@@ ", +".@@ .@@@+@@@$ .@@@@ $$$ #+@@@ #++@@@@%#$$ $@@@@+@@@ .@@ .@@@@ .@@@$ #$$ #$$%.@@@@@$ #+@@+@@@@ .@.$$$ #+@@@@@@@ .@@$$$ .+@@+@@@$ #$$.@@@ .+@@+@@@$ #$$.@@@$ .@@@@@@@ .@@@@@@@ ##+@@@@ .@@@@$$ ", +" .@@@+#$$ .@@@+ .@@@@ .@@@+ ###+@@@@ #+@@@@ .@@@@ .@@@@ .@@@ #+@@ #+@@ .@@@ #+@@ #+@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@@ .@@.@@@@ .@@@.@@@ #+@@@@@@@@ .@@@++@@@$ ", +" .@@@@@@@@ ##+@@@@@ .++@ #@@@@@$ ##+@@@@@ .@@@@@@@@ .@@@@@## #+@@@@# .++@ .@@@@@$## #+@@ .@@@ .@@@ #+@@ .@@@ .@@@ .@@@ .@@$ .@@@ .@@@ .@@$ .@@@ .@@ #+@@ .@@$ .@@ .@@$ .@@ .@@ #+@@ ", +" .@@@ #+@@ .@@@@@+@@@@ #@@@@@@@+@@@ .@@@@@+@@@ .@@$ .@@@ .@@@+@@@@@@ .@@@+@@+@@@$ .@@@@@@@@@ #+@@$ .@@ .@+@@ #+@@$ +@@ .+.@@ +@@ #+@@$ .@.+@ +@@ #+@@$ .@.+@ $$$ .@@$ #+@@ #$$ #+@@$ .@@ .@@ #+@@$ ", +" #+@@ .@@$ +@$$$$#$+@@@ $@@@@ .@@$ +@$$$$.@@@ #+@@ .@@$ .@@@$#$$$$@@ #+@@ @@@@$ .@@@$$$$@@ .@@$ .@@ .@@$ .@@ #+@@ .@@ #+@@ .@@ #+@@ .@@$ +@@$ .@@ .@@ #+@@ ", +"#+@@$ #$$ #@@$ #+@@$ .@@@ $$$ #+@@$ #@@$ #+@@$ .@@@ .@@$ #+@@ .@@ .@@ ", +".@@$ #+@@ #+@@@ #+@@ .@@$ .@@@$ #@@@ +@@$ #@$ #+$ "}; diff --git a/kgoldrunner/src/hero.xpm b/kgoldrunner/src/hero.xpm new file mode 100644 index 00000000..6cfa2e2c --- /dev/null +++ b/kgoldrunner/src/hero.xpm @@ -0,0 +1,26 @@ +/* XPM */ +static const char * hero_xpm[] = { +"320 16 7 1", +" c None", +". c #00FF00", +"+ c #00C000", +"@ c #008000", +"# c #004000", +"$ c #000000", +"% c #303030", +" .++ .++ ", +" .+@ @## @## @## .+@ .+@ @## .@@ @## .@@ @## .++ .+@ .+@ @@# @+# ", +" .@@# @## @## @### .@@ .@@ @## @+@@ @## .@@ @### .@@ .@@.++@@ .+++@.@@ .@@ .@@ ", +" @@@## .++@@ @@@## .++@@ @@@@# .++@@ @@@@# .++@@ @+@@#.++@.@@@ .@@ @+@@ .++@@+@@ .@@ .@@@.++@@+@@# .@@ .@@#.++@ .@@# .@@ .@@++@@# @+@@++@@ @@# @@@# .@@ .@@ @@@# @@# ", +" .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ .@@@@ @+@@.@@++@# .@++++ @+@@++@+@@# .@@++@ @+@+@@+.@@# .++@@@ @+@+@@+.@@# .+@.@@ @++@+@@ @@ @@ .@@+++# .@@# .@@@@+@# @+@#.@@@ @+@@ ", +" @+@@## @@@.@@@# @+@@## .@@@# @#+@@# @@@@@#@@ @#+@@# @+@@@ @+@@@+@@# .@.@@@ @+@@@@@@# .@@@@@ @+@+@@@@# +@@+@@ @+@+@@@@# +@@.@@ @+@@@+++ .+.@@@@@ .@@@@@@@@+@@# @+@@@@#@@+@@@ ", +" @@@#+@@# .@@@@@@@@@@+@ @@++@@@@@## @+@ @++@@@@@@ @+@@@@@@ .+@+@@@@@@@@@ .+@ @@+@@@@@@## .@@@@@@@# @+@@@@ ..+@ @+@@@@ .@@+ .@@@@# .++@ .@@@@# .@@@ .@@@+@@ .@@@@@@ #@@@@@@@@@ .@@@@@@@@# ", +"@++@@@@@@@+ @+@@ .@+@@@@@+@@@# .@@@#+@@@@@+@@@ .@@+@@@+@@@#.++@ .@@@@@@@++# @@@@+@@@@@+@@ .@@+@@@@@@#@@@@ @@+@@@@@+@@@ .@@@# .@@+@@ .@@@# .@@@@@ @+@@@ @++@@@ @+@@@ @..@@@ .@@@@@.# @+.@@@@@ #+@@@@ .@@@@# ", +".@@##+@@@@@+@@@# ##+@@@@@## .@@ .@@@@@@@ .@@++@@@+@@@@@@@+@@@@@@##+@@ ##@+@@@@## .@@++@@@ .@@ .@@@@.@++@@@ .@@@# @+@@@@@ .@@@# @+@@@@@ @+@@@ .@@@+@@ @+@@@ .@@.@@@# .+.@@@@@ .@@@@@@@ .@@@# @+@@@ ", +".@@ .@@@+@@@# .@@@@ ### @+@@@ @++@@@@$@## #@@@@+@@@ .@@ .@@@@ .@@@# @## @##$.@@@@@# @+@@+@@@@ .@.### @+@@@@@@@ .@@### .+@@+@@@# @##.@@@ .+@@+@@@# @# .@@@# .@@@@@@@ .@@@@@@@ @@+@@@@ .@@@@## ", +" .@@@+@## .@@@+ .@@@@ .@@@+ @@@+@@@@ @+@@@@ .@@@@ .@@@@ .@@@ @+@@ @+@@ .@@@ @+@@ @+@@ .@@% .@@@ .@@@ .@@% .@@@ .@@@ .@@.@@@@ .@@@.@@@ @+@@@@@@@@ .@@@++@@@# ", +" .@@@@@@@@ @@+@@@@@ .++@ @@@@@@# @@+@@@@@ .@@@@@@@@ .@@@@@@@ @+@@@@@ .++@ .@@@@@#@@ @+@@ .@@@ .@@@ @+@@ .@@@ .@@@ .@@@ .@@# .@@@ .@@@ .@@# .@@@ .@@ @+@@ .@@# .@@ .@@# .@@ .@@ @+@@ ", +" .@@@ @+@@ .@@@@@+@@@@ @@@@@@@@+@@@ .@@@@@+@@@ .@@# .@@@ .@@@+@@@@@@ .@@@+@@+@@@# .@@@@@@@@@ @+@@# .@@ .@+@@ @+@@# +@@ .+.@@ +@@ @+@@# .@.+@ +@@ @+@@# .@.+@ ### .@@# @+@@ @## @+@@# .@@ .@@ @+@@# ", +" @+@@ .@@# +@#%##@#+@@@ #@@@@ .@@# +@#%##.@@@ @+@@ .@@# .@@@#@####@@ @+@@ @@@@# .@@@####@@ .@@# .@@ .@@# .@@ @+@@ .@@ @+@@ .@@ @+@@ .@@# +@@# .@@ .@@ @+@@ ", +"@+@@# @## @@@# @+@@# .@@@ ### @+@@# @@@% @+@@# .@@@ .@@# @+@@ .@@ .@@ ", +".@@# @+@@ @+@@@ @+@@ .@@# .@@@# @@@@ +@@# @@# @+# "}; diff --git a/kgoldrunner/src/hi128-app-kgoldrunner.png b/kgoldrunner/src/hi128-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..d47375a8 --- /dev/null +++ b/kgoldrunner/src/hi128-app-kgoldrunner.png diff --git a/kgoldrunner/src/hi16-app-kgoldrunner.png b/kgoldrunner/src/hi16-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..ae7d42a6 --- /dev/null +++ b/kgoldrunner/src/hi16-app-kgoldrunner.png diff --git a/kgoldrunner/src/hi22-app-kgoldrunner.png b/kgoldrunner/src/hi22-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..460d7cdd --- /dev/null +++ b/kgoldrunner/src/hi22-app-kgoldrunner.png diff --git a/kgoldrunner/src/hi32-app-kgoldrunner.png b/kgoldrunner/src/hi32-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..1b72581a --- /dev/null +++ b/kgoldrunner/src/hi32-app-kgoldrunner.png diff --git a/kgoldrunner/src/hi48-app-kgoldrunner.png b/kgoldrunner/src/hi48-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..6ae5f636 --- /dev/null +++ b/kgoldrunner/src/hi48-app-kgoldrunner.png diff --git a/kgoldrunner/src/hi64-app-kgoldrunner.png b/kgoldrunner/src/hi64-app-kgoldrunner.png Binary files differnew file mode 100644 index 00000000..35172e71 --- /dev/null +++ b/kgoldrunner/src/hi64-app-kgoldrunner.png diff --git a/kgoldrunner/src/kgoldrunner.cpp b/kgoldrunner/src/kgoldrunner.cpp new file mode 100644 index 00000000..6e8cfb3c --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.cpp @@ -0,0 +1,1097 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krger <ianw2@optusnet.com.au> + */ + +#include <kprinter.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> + +#include <kglobal.h> +#include <klocale.h> +#include <kiconloader.h> +#include <kmenubar.h> +#include <kstatusbar.h> +#include <kkeydialog.h> +#include <kaccel.h> +#include <kio/netaccess.h> +#include <kfiledialog.h> +#include <kconfig.h> + +#include <kedittoolbar.h> +#include <ktoolbar.h> + +#include <kstdaccel.h> +#include <kaction.h> +#include <kstdaction.h> +#include <kstdgameaction.h> + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" +#include "kgrgame.h" +#include "kgoldrunner.h" + +KGoldrunner::KGoldrunner() + : KMainWindow (0, "KGoldrunner"), + view (new KGrCanvas (this)) +{ +/******************************************************************************/ +/************* FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE *************/ +/******************************************************************************/ + + // Avoid "saveOK()" check if an error-exit occurs during the file checks. + startupOK = TRUE; + + // Get directory paths for the system levels, user levels and manual. + if (! getDirectories()) { + fprintf (stderr, "getDirectories() FAILED\n"); + startupOK = FALSE; + return; // If games directory not found, abort. + } + + // This message is to help diagnose distribution or installation problems. + printf + ("The games data and handbook should be in the following locations:\n"); + printf ("System games: %s\nUser data: %s\nHandbook: %s\n", + systemDataDir.myStr(), userDataDir.myStr(), systemHTMLDir.myStr()); + +/******************************************************************************/ +/************************ SET PLAYFIELD AND GAME DATA ***********************/ +/******************************************************************************/ + + game = new KGrGame (view, systemDataDir, userDataDir); + + // Initialise the collections of levels (i.e. the list of games). + if (! game->initCollections()) { + startupOK = FALSE; + return; // If no game files, abort. + } + + hero = game->getHero(); // Get a pointer to the hero. + +/******************************************************************************/ +/************************* SET UP THE USER INTERFACE ************************/ +/******************************************************************************/ + + // Get catalog for translation + KGlobal::locale()->insertCatalogue("libkdegames"); + + // Tell the KMainWindow that this is the main widget + setCentralWidget (view); + + // Set up our actions (menu, toolbar and keystrokes) ... + setupActions(); + + // and a status bar. + initStatusBar(); + + // Connect the game actions to the menu and toolbar displays. + connect(game, SIGNAL (setEditMenu (bool)), SLOT (setEditMenu (bool))); + connect(game, SIGNAL (markRuleType (char)), SLOT (markRuleType (char))); + connect(game, SIGNAL (hintAvailable(bool)), SLOT (adjustHintAction(bool))); + connect(game, SIGNAL (defaultEditObj()), SLOT (defaultEditObj())); + + // Apply the saved mainwindow settings, if any, and ask the mainwindow + // to automatically save settings if changed: window size, toolbar + // position, icon size, etc. + setAutoSaveSettings(); + +#ifdef QT3 + // Base size of playing-area and widgets on the monitor resolution. + int dw = KApplication::desktop()->width(); + if (dw > 800) { // More than 800x600. + view->changeSize (+1); // Scale 1.25:1. + } + if (dw > 1024) { // More than 1024x768. + view->changeSize (+1); + view->changeSize (+1); // Scale 1.75:1. + setUsesBigPixmaps (TRUE); // Use big toolbar buttons. + } + view->setBaseScale(); // Set scale for level-names. +#endif + setFixedSize (view->size()); + + makeEditToolbar(); // Uses pixmaps from "view". + editToolbar->hide(); + setDockEnabled (DockBottom, FALSE); + setDockEnabled (DockLeft, FALSE); + setDockEnabled (DockRight, FALSE); + + // Make it impossible to turn off the editor toolbar. + // Accidentally hiding it would make editing impossible. + setDockMenuEnabled (FALSE); + + // Set mouse control of the hero as the default. + game->setMouseMode (TRUE); + + // Paint the main widget (title, menu, status bar, blank playfield). + show(); + + // Force the main widget to appear before the "Start Game" dialog does. + qApp->processEvents(); + + // Call the "Start Game" function and pop up the "Start Game" dialog. + game->startLevelOne(); +} + +KGoldrunner::~KGoldrunner() +{ + delete editToolbar; +} + +void KGoldrunner::setupActions() +{ + /**************************************************************************/ + /****************************** GAME MENU ******************************/ + /**************************************************************************/ + + // New Game... + // Load Saved Game... + // Play Any Level... + // Play Next Level... + // Tutorial + // -------------------------- + + KAction * newAction = KStdGameAction:: + gameNew ( + game, + SLOT(startLevelOne()), actionCollection()); + newAction-> setText (i18n("&New Game...")); + KAction * loadGame = KStdGameAction:: + load ( + game, SLOT(loadGame()), actionCollection()); + loadGame-> setText (i18n("&Load Saved Game...")); + (void) new KAction ( + i18n("&Play Any Level..."), + 0, + game, SLOT(startAnyLevel()), actionCollection(), + "play_any"); + (void) new KAction ( + i18n("Play &Next Level..."), + 0, + game, + SLOT(startNextLevel()), actionCollection(), + "play_next"); + + // Save Game... + // Save Edits... (extra copy) + // -------------------------- + + saveGame = KStdGameAction:: + save ( + game, SLOT(saveGame()), actionCollection()); + saveGame-> setText (i18n("&Save Game...")); + saveGame-> setShortcut (Key_S); // Alternate key. + + // Pause + // Show High Scores + // Get a Hint + // Kill the Hero + // -------------------------- + + myPause = KStdGameAction:: + pause ( + this, SLOT(stopStart()), actionCollection()); + myPause-> setShortcut (Key_Escape); // Alternate key. + highScore = KStdGameAction:: + highscores ( + game, SLOT(showHighScores()), actionCollection()); + hintAction = new KAction ( + i18n("&Get Hint"), "ktip", + 0, + game, SLOT(showHint()), actionCollection(), + "get_hint"); + killHero = new KAction ( + i18n("&Kill Hero"), + Key_Q, + game, SLOT(herosDead()), actionCollection(), + "kill_hero"); + + // Quit + // -------------------------- + + (void) KStdGameAction:: + quit ( + this, SLOT(close()), actionCollection()); + + /**************************************************************************/ + /*************************** GAME EDITOR MENU **************************/ + /**************************************************************************/ + + // Create a Level + // Edit Any Level... + // Edit Next Level... + // -------------------------- + + (void) new KAction ( + i18n("&Create Level"), + 0, + game, SLOT(createLevel()), actionCollection(), + "create"); + (void) new KAction ( + i18n("&Edit Any Level..."), + 0, + game, SLOT(updateLevel()), actionCollection(), + "edit_any"); + (void) new KAction ( + i18n("Edit &Next Level..."), + 0, + game, SLOT(updateNext()), actionCollection(), + "edit_next"); + + // Save Edits... + // Move Level... + // Delete Level... + // -------------------------- + + saveEdits = new KAction ( + i18n("&Save Edits..."), + 0, + game, SLOT(saveLevelFile()), actionCollection(), + "save_edits"); + saveEdits->setEnabled (FALSE); // Nothing to save, yet. + + (void) new KAction ( + i18n("&Move Level..."), + 0, + game, SLOT(moveLevelFile()), actionCollection(), + "move_level"); + (void) new KAction ( + i18n("&Delete Level..."), + 0, + game, + SLOT(deleteLevelFile()), actionCollection(), + "delete_level"); + + // Create a Game + // Edit Game Info... + // -------------------------- + + (void) new KAction ( + i18n("Create Game..."), + 0, + this, SLOT(createGame()), actionCollection(), + "create_game"); + (void) new KAction ( + i18n("Edit Game Info..."), + 0, + this, + SLOT(editGameInfo()), actionCollection(), + "edit_game"); + + /**************************************************************************/ + /*************************** LANDSCAPES MENU ***************************/ + /**************************************************************************/ + + // Default shortcut keys are set by "kgoldrunnerui.rc". + + setKGoldrunner = new KRadioAction ( + "K&Goldrunner", + 0, // Default Shift+G + this, SLOT(lsKGoldrunner()), actionCollection(), + "kgoldrunner"); + setAppleII = new KRadioAction ( + "&Apple II", + 0, // Default Shift+A + this, SLOT(lsApple2()), actionCollection(), + "apple_2"); + setIceCave = new KRadioAction ( + i18n("&Ice Cave"), + 0, // Default Shift+I + this, SLOT(lsIceCave()), actionCollection(), + "ice_cave"); + setMidnight = new KRadioAction ( + i18n("&Midnight"), + 0, // Default Shift+M + this, SLOT(lsMidnight()), actionCollection(), + "midnight"); + setKDEKool = new KRadioAction ( + i18n("&KDE Kool"), + 0, // Default Shift+K + this, SLOT(lsKDEKool()), actionCollection(), + "kde_kool"); + + setKGoldrunner-> setExclusiveGroup ("landscapes"); + setAppleII-> setExclusiveGroup ("landscapes"); + setIceCave-> setExclusiveGroup ("landscapes"); + setMidnight-> setExclusiveGroup ("landscapes"); + setKDEKool-> setExclusiveGroup ("landscapes"); + setKGoldrunner-> setChecked (TRUE); + + /**************************************************************************/ + /**************************** SETTINGS MENU ****************************/ + /**************************************************************************/ + + // Mouse Controls Hero + // Keyboard Controls Hero + // -------------------------- + + setMouse = new KRadioAction ( + i18n("&Mouse Controls Hero"), + 0, + this, + SLOT(setMouseMode()), actionCollection(), + "mouse_mode"); + setKeyboard = new KRadioAction ( + i18n("&Keyboard Controls Hero"), + 0, + this, + SLOT(setKeyBoardMode()), actionCollection(), + "keyboard_mode"); + + setMouse-> setExclusiveGroup ("control"); + setKeyboard-> setExclusiveGroup ("control"); + setMouse-> setChecked (TRUE); + + // Normal Speed + // Beginner Speed + // Champion Speed + // Increase Speed + // Decrease Speed + // -------------------------- + + KRadioAction * nSpeed = new KRadioAction ( + i18n("Normal Speed"), + 0, + this, SLOT(normalSpeed()), actionCollection(), + "normal_speed"); + KRadioAction * bSpeed = new KRadioAction ( + i18n("Beginner Speed"), + 0, + this, SLOT(beginSpeed()), actionCollection(), + "beginner_speed"); + KRadioAction * cSpeed = new KRadioAction ( + i18n("Champion Speed"), + 0, + this, SLOT(champSpeed()), actionCollection(), + "champion_speed"); + (void) new KAction ( // Repeatable action. + i18n("Increase Speed"), + Key_Plus, + this, SLOT(incSpeed()), actionCollection(), + "increase_speed"); + (void) new KAction ( // Repeatable action. + i18n("Decrease Speed"), + Key_Minus, + this, SLOT(decSpeed()), actionCollection(), + "decrease_speed"); + + nSpeed-> setExclusiveGroup ("speed"); + bSpeed-> setExclusiveGroup ("speed"); + cSpeed-> setExclusiveGroup ("speed"); + nSpeed-> setChecked (TRUE); + + // Traditional Rules + // KGoldrunner Rules + // -------------------------- + + tradRules = new KRadioAction ( + i18n("&Traditional Rules"), + 0, + this, SLOT(setTradRules()), actionCollection(), + "trad_rules"); + kgrRules = new KRadioAction ( + i18n("K&Goldrunner Rules"), + 0, + this, SLOT(setKGrRules()), actionCollection(), + "kgr_rules"); + + tradRules-> setExclusiveGroup ("rules"); + kgrRules-> setExclusiveGroup ("rules"); + tradRules-> setChecked (TRUE); + + // Larger Playing Area + // Smaller Playing Area + // -------------------------- + + (void) new KAction ( + i18n("Larger Playing Area"), + 0, + this, SLOT(makeLarger()), actionCollection(), + "larger_area"); + (void) new KAction ( + i18n("Smaller Playing Area"), + 0, + this, SLOT(makeSmaller()), actionCollection(), + "smaller_area"); + + // Configure Shortcuts... + // Configure Toolbars... + // -------------------------- + + KStdAction::keyBindings ( + this, SLOT(optionsConfigureKeys()), + actionCollection()); + // KStdAction::configureToolbars ( + // this, SLOT(optionsConfigureToolbars()), + // actionCollection()); + + /**************************************************************************/ + /************************** KEYSTROKE ACTIONS **************************/ + /**************************************************************************/ + + // Two-handed KB controls and alternate one-handed controls for the hero. + + (void) new KAction (i18n("Move Up"), Key_Up, + this, SLOT(goUp()), actionCollection(), "move_up"); + (void) new KAction (i18n("Move Right"), Key_Right, + this, SLOT(goR()), actionCollection(), "move_right"); + (void) new KAction (i18n("Move Down"), Key_Down, + this, SLOT(goDown()), actionCollection(), "move_down"); + (void) new KAction (i18n("Move Left"), Key_Left, + this, SLOT(goL()), actionCollection(), "move_left"); + (void) new KAction (i18n("Stop"), Key_Space, + this, SLOT(stop()), actionCollection(), "stop"); + (void) new KAction (i18n("Dig Right"), Key_C, + this, SLOT(digR()), actionCollection(), "dig_right"); + (void) new KAction (i18n("Dig Left"), Key_Z, + this, SLOT(digL()), actionCollection(), "dig_left"); + + // Alternate one-handed controls. Set up in "kgoldrunnerui.rc". + + // Key_I, "move_up" + // Key_L, "move_right" + // Key_K, "move_down" + // Key_J, "move_left" + // Key_Space, "stop" (as above) + // Key_O, "dig_right" + // Key_U, "dig_left" + +#ifdef KGR_DEBUG + // Authors' debugging aids. + + (void) new KAction (i18n("Step"), Key_Period, + game, SLOT(doStep()), actionCollection(), "do_step"); + (void) new KAction (i18n("Test Bug Fix"), Key_B, + game, SLOT(bugFix()), actionCollection(), "bug_fix"); + (void) new KAction (i18n("Show Positions"), Key_D, + game, SLOT(showFigurePositions()), actionCollection(), "step"); + (void) new KAction (i18n("Start Logging"), Key_G, + game, SLOT(startLogging()), actionCollection(), "logging"); + (void) new KAction (i18n("Show Hero"), Key_H, + game, SLOT(showHeroState()), actionCollection(), "show_hero"); + (void) new KAction (i18n("Show Object"), Key_Question, + game, SLOT(showObjectState()), actionCollection(), "show_obj"); + (void) new KAction (i18n("Show Enemy") + "0", Key_0, + this, SLOT(showEnemy0()), actionCollection(), "show_enemy_0"); + (void) new KAction (i18n("Show Enemy") + "1", Key_1, + this, SLOT(showEnemy1()), actionCollection(), "show_enemy_1"); + (void) new KAction (i18n("Show Enemy") + "2", Key_2, + this, SLOT(showEnemy2()), actionCollection(), "show_enemy_2"); + (void) new KAction (i18n("Show Enemy") + "3", Key_3, + this, SLOT(showEnemy3()), actionCollection(), "show_enemy_3"); + (void) new KAction (i18n("Show Enemy") + "4", Key_4, + this, SLOT(showEnemy4()), actionCollection(), "show_enemy_4"); + (void) new KAction (i18n("Show Enemy") + "5", Key_5, + this, SLOT(showEnemy5()), actionCollection(), "show_enemy_5"); + (void) new KAction (i18n("Show Enemy") + "6", Key_6, + this, SLOT(showEnemy6()), actionCollection(), "show_enemy_6"); +#endif + + /**************************************************************************/ + /************************** NOW SET IT ALL UP **************************/ + /**************************************************************************/ + + createGUI(); +} + +/******************************************************************************/ +/********************** SLOTS FOR STATUS BAR UPDATES ************************/ +/******************************************************************************/ + +void KGoldrunner::initStatusBar() +{ + QString s = statusBar()->fontInfo().family(); // Set bold font. + int i = statusBar()->fontInfo().pointSize(); + statusBar()->setFont (QFont (s, i, QFont::Bold)); + + statusBar()->setSizeGripEnabled (FALSE); // Use Settings menu ... + + statusBar()->insertItem ("", ID_LIVES); + statusBar()->insertItem ("", ID_SCORE); + statusBar()->insertItem ("", ID_LEVEL); + statusBar()->insertItem ("", ID_HINTAVL); + statusBar()->insertItem ("", ID_MSG, QSizePolicy::Horizontally); + + showLives (5); // Start with 5 lives. + showScore (0); + showLevel (0); + adjustHintAction (FALSE); + + // Set the PAUSE/RESUME key-names into the status bar message. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (FALSE); + + statusBar()->setItemFixed (ID_LIVES, -1); // Fix current sizes. + statusBar()->setItemFixed (ID_SCORE, -1); + statusBar()->setItemFixed (ID_LEVEL, -1); + + connect(game, SIGNAL (showLives (long)), SLOT (showLives (long))); + connect(game, SIGNAL (showScore (long)), SLOT (showScore (long))); + connect(game, SIGNAL (showLevel (int)), SLOT (showLevel (int))); + connect(game, SIGNAL (gameFreeze (bool)), SLOT (gameFreeze (bool))); +} + +void KGoldrunner::showLives (long newLives) +{ + QString tmp; + tmp.setNum (newLives); + if (newLives < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Lives: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LIVES); +} + +void KGoldrunner::showScore (long newScore) +{ + QString tmp; + tmp.setNum (newScore); + if (newScore < 10000) + tmp = tmp.rightJustify (5, '0'); + tmp.insert (0, i18n(" Score: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_SCORE); +} + +void KGoldrunner::showLevel (int newLevelNo) +{ + QString tmp; + tmp.setNum (newLevelNo); + if (newLevelNo < 100) + tmp = tmp.rightJustify (3, '0'); + tmp.insert (0, i18n(" Level: ")); + tmp.append (" "); + statusBar()->changeItem (tmp, ID_LEVEL); +} + +void KGoldrunner::gameFreeze (bool on_off) +{ + if (on_off) + statusBar()->changeItem + (i18n("Press \"%1\" to RESUME").arg(pauseKeys), ID_MSG); + else + statusBar()->changeItem + (i18n("Press \"%1\" to PAUSE").arg(pauseKeys), ID_MSG); +} + +void KGoldrunner::adjustHintAction (bool hintAvailable) +{ + hintAction->setEnabled (hintAvailable); + + if (hintAvailable) { + statusBar()->changeItem (i18n(" Has hint "), ID_HINTAVL); + } + else { + statusBar()->changeItem (i18n(" No hint "), ID_HINTAVL); + } +} + +void KGoldrunner::markRuleType (char ruleType) +{ + if (ruleType == 'T') + tradRules->activate(); + else + kgrRules->activate(); +} + +void KGoldrunner::setEditMenu (bool on_off) +{ + saveEdits->setEnabled (on_off); + + saveGame->setEnabled (! on_off); + hintAction->setEnabled (! on_off); + killHero->setEnabled (! on_off); + highScore->setEnabled (! on_off); + + if (on_off){ + editToolbar->show(); + } + else { + editToolbar->hide(); + } +} + +/******************************************************************************/ +/******************* SLOTS FOR MENU AND KEYBOARD ACTIONS *******************/ +/******************************************************************************/ + +// Slot to halt (pause) or restart the game play. + +void KGoldrunner::stopStart() +{ + if (! (KGrObject::frozen)) { + game->freeze(); + } + else { + game->unfreeze(); + } +} + +// Local slots to create or edit game information. + +void KGoldrunner::createGame() {game->editCollection (SL_CR_GAME);} +void KGoldrunner::editGameInfo() {game->editCollection (SL_UPD_GAME);} + +// Local slots to set the landscape (colour scheme). + +void KGoldrunner::lsKGoldrunner() {view->changeLandscape ("KGoldrunner");} +void KGoldrunner::lsApple2() {view->changeLandscape ("Apple II");} +void KGoldrunner::lsIceCave() {view->changeLandscape ("Ice Cave");} +void KGoldrunner::lsMidnight() {view->changeLandscape ("Midnight");} +void KGoldrunner::lsKDEKool() {view->changeLandscape ("KDE Kool");} + +// Local slots to set mouse or keyboard control of the hero. + +void KGoldrunner::setMouseMode() {game->setMouseMode (TRUE);} +void KGoldrunner::setKeyBoardMode() {game->setMouseMode (FALSE);} + +// Local slots to set game speed. + +void KGoldrunner::normalSpeed() {hero->setSpeed (NSPEED);} +void KGoldrunner::beginSpeed() {hero->setSpeed (BEGINSPEED);} +void KGoldrunner::champSpeed() {hero->setSpeed (CHAMPSPEED);} +void KGoldrunner::incSpeed() {hero->setSpeed (+1);} +void KGoldrunner::decSpeed() {hero->setSpeed (-1);} + +// Slots to set Traditional or KGoldrunner rules. + +void KGoldrunner::setTradRules() +{ + KGrFigure::variableTiming = TRUE; + KGrFigure::alwaysCollectNugget = TRUE; + KGrFigure::runThruHole = TRUE; + KGrFigure::reappearAtTop = TRUE; + KGrFigure::searchStrategy = LOW; +} + +void KGoldrunner::setKGrRules() +{ + KGrFigure::variableTiming = FALSE; + KGrFigure::alwaysCollectNugget = FALSE; + KGrFigure::runThruHole = FALSE; + KGrFigure::reappearAtTop = FALSE; + KGrFigure::searchStrategy = MEDIUM; +} + +// Local slots to make playing area larger or smaller. + +void KGoldrunner::makeLarger() +{ + if (view->changeSize (+1)) + setFixedSize (view->size()); +} + +void KGoldrunner::makeSmaller() +{ + if (view->changeSize (-1)) + setFixedSize (view->size()); +} + +// Local slots for hero control keys. + +void KGoldrunner::goUp() {setKey (KB_UP);} +void KGoldrunner::goR() {setKey (KB_RIGHT);} +void KGoldrunner::goDown() {setKey (KB_DOWN);} +void KGoldrunner::goL() {setKey (KB_LEFT);} +void KGoldrunner::stop() {setKey (KB_STOP);} +void KGoldrunner::digR() {setKey (KB_DIGRIGHT);} +void KGoldrunner::digL() {setKey (KB_DIGLEFT);} + +// Local slots for authors' debugging aids. + +void KGoldrunner::showEnemy0() {game->showEnemyState (0);} +void KGoldrunner::showEnemy1() {game->showEnemyState (1);} +void KGoldrunner::showEnemy2() {game->showEnemyState (2);} +void KGoldrunner::showEnemy3() {game->showEnemyState (3);} +void KGoldrunner::showEnemy4() {game->showEnemyState (4);} +void KGoldrunner::showEnemy5() {game->showEnemyState (5);} +void KGoldrunner::showEnemy6() {game->showEnemyState (6);} + +void KGoldrunner::saveProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. Anything you write here will be available + // later when this app is restored. + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::saveProperties.\n"); + // config->writeEntry("qqq", qqq); +} + +void KGoldrunner::readProperties(KConfig *config) +{ + // The 'config' object points to the session managed + // config file. This function is automatically called whenever + // the app is being restored. Read in here whatever you wrote + // in 'saveProperties' + + config->setGroup ("Game"); // Prevents a compiler warning. + printf ("I am in KGoldrunner::readProperties.\n"); + // QString qqq = config->readEntry("qqq"); +} + +void KGoldrunner::optionsShowToolbar() +{ + // this is all very cut and paste code for showing/hiding the + // toolbar + // if (m_toolbarAction->isChecked()) + // toolBar()->show(); + // else + // toolBar()->hide(); +} + +void KGoldrunner::optionsShowStatusbar() +{ + // this is all very cut and paste code for showing/hiding the + // statusbar + // if (m_statusbarAction->isChecked()) + // statusBar()->show(); + // else + // statusBar()->hide(); +} + +void KGoldrunner::optionsConfigureKeys() +{ + KKeyDialog::configure(actionCollection()); + + // Update the PAUSE/RESUME message in the status bar. + pauseKeys = myPause->shortcut().toString(); + pauseKeys = pauseKeys.replace (';', "\" " + i18n("or") + " \""); + gameFreeze (KGrObject::frozen); // Refresh the status bar text. +} + +void KGoldrunner::optionsConfigureToolbars() +{ + // use the standard toolbar editor +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + saveMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + saveMainWindowSettings(KGlobal::config()); +# endif +#else + saveMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::newToolbarConfig() +{ + // this slot is called when user clicks "Ok" or "Apply" in the toolbar editor. + // recreate our GUI, and re-apply the settings (e.g. "text under icons", etc.) + createGUI(); + +#if defined(KDE_MAKE_VERSION) +# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,0) + applyMainWindowSettings(KGlobal::config(), autoSaveGroup()); +# else + applyMainWindowSettings(KGlobal::config()); +# endif +#else + applyMainWindowSettings(KGlobal::config()); +#endif +} + +void KGoldrunner::optionsPreferences() +{ + // popup some sort of preference dialog, here + // KGoldrunnerPreferences dlg; + // if (dlg.exec()) + // { + // redo your settings + // } +} + +void KGoldrunner::changeStatusbar(const QString& text) +{ + // display the text on the statusbar + statusBar()->message(text); +} + +void KGoldrunner::changeCaption(const QString& text) +{ + // display the text on the caption + setCaption(text); +} + +bool KGoldrunner::getDirectories() +{ + bool result = TRUE; + + // WHERE THINGS ARE: In the KDE 3 environment (Release 3.1.1), application + // documentation and data files are in a directory structure given by + // $KDEDIRS (e.g. "/usr/local/kde" or "/opt/kde3/"). Application user data + // files are in a directory structure given by $KDEHOME ("$HOME/.kde"). + // Within those two structures, the three sub-directories will typically be + // "share/doc/HTML/en/kgoldrunner/", "share/apps/kgoldrunner/system/" and + // "share/apps/kgoldrunner/user/". Note that it is necessary to have + // an extra path level ("system" or "user") after "kgoldrunner", otherwise + // all the KGoldrunner files have similar path names (after "apps") and + // KDE always locates directories in $KDEHOME and never the released games. + + // The directory strings are set by KDE at run time and might change in + // later releases, so use them with caution and only if something gets lost. + + KStandardDirs * dirs = new KStandardDirs(); + +#ifdef QT3 + QString myDir = "kgoldrunner"; +#else + QString myDir = "kgoldrun"; +#endif + + // Find the KGoldrunner Users' Guide, English version (en). + systemHTMLDir = dirs->findResourceDir ("html", "en/" + myDir + "/"); + if (systemHTMLDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find documentation sub-folder 'en/%1/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("html"))); + // result = FALSE; // Don't abort if the doc is missing. + } + else + systemHTMLDir.append ("en/" + myDir + "/"); + + // Find the system collections in a directory of the required KDE type. + systemDataDir = dirs->findResourceDir ("data", myDir + "/system/"); + if (systemDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find system games sub-folder '%1/system/' " + "in area '%2' of the KDE folder ($KDEDIRS).") + .arg(myDir).arg(dirs->kde_default ("data"))); + result = FALSE; // ABORT if the games data is missing. + } + else + systemDataDir.append (myDir + "/system/"); + + // Locate and optionally create directories for user collections and levels. + bool create = TRUE; + userDataDir = dirs->saveLocation ("data", myDir + "/user/", create); + if (userDataDir.length() <= 0) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create user games sub-folder '%1/user/' " + "in area '%2' of the KDE user area ($KDEHOME).") + .arg(myDir).arg(dirs->kde_default ("data"))); + // result = FALSE; // Don't abort if user area is missing. + } + else { + create = dirs->makeDir (userDataDir + "levels/"); + if (! create) { + KGrMessage::information (this, i18n("Get Folders"), + i18n("Cannot find or create 'levels/' folder in " + "sub-folder '%1/user/' in the KDE user area ($KDEHOME).").arg(myDir)); + // result = FALSE; // Don't abort if user area is missing. + } + } + + return (result); +} + +// This method is invoked when top-level window is closed, whether by selecting +// "Quit" from the menu or by clicking the "X" at the top right of the window. + +bool KGoldrunner::queryClose () +{ + // Last chance to save: user has clicked "X" widget or menu-Quit. + bool cannotContinue = TRUE; + game->saveOK (cannotContinue); + return (TRUE); +} + +void KGoldrunner::setKey (KBAction movement) +{ + if (game->inEditMode()) return; + + // Using keyboard control can automatically disable mouse control. + if (game->inMouseMode()) { + // Halt the game while a message is displayed. + game->setMessageFreeze (TRUE); + + switch (KGrMessage::warning (this, i18n("Switch to Keyboard Mode"), + i18n("You have pressed a key that can be used to move the " + "Hero. Do you want to switch automatically to keyboard " + "control? Mouse control is easier to use in the long term " + "- like riding a bike rather than walking!"), + i18n("Switch to &Keyboard Mode"), i18n("Stay in &Mouse Mode"))) + { + case 0: game->setMouseMode (FALSE); // Set internal mouse mode OFF. + setMouse->setChecked (FALSE); // Adjust the Settings menu. + setKeyboard->setChecked (TRUE); + break; + case 1: break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + game->setMessageFreeze (FALSE); + + if (game->inMouseMode()) + return; // Stay in Mouse Mode. + } + + if ( game->getLevel() != 0 ) + { + if (! hero->started ) // Start when first movement + game->startPlaying(); // key is pressed ... + game->heroAction (movement); + } +} + +/******************************************************************************/ +/********************** MAKE A TOOLBAR FOR THE EDITOR **********************/ +/******************************************************************************/ + +#include <qwmatrix.h> +void KGoldrunner::makeEditToolbar() +{ + // Set up the pixmaps for the editable objects. + QPixmap pixmap; + QImage image; + + QPixmap brickbg = view->getPixmap (BRICK); + QPixmap fbrickbg = view->getPixmap (FBRICK); + + QPixmap freebg = view->getPixmap (FREE); + QPixmap nuggetbg = view->getPixmap (NUGGET); + QPixmap polebg = view->getPixmap (POLE); + QPixmap betonbg = view->getPixmap (BETON); + QPixmap ladderbg = view->getPixmap (LADDER); + QPixmap hladderbg = view->getPixmap (HLADDER); + QPixmap edherobg = view->getPixmap (HERO); + QPixmap edenemybg = view->getPixmap (ENEMY); + + if (usesBigPixmaps()) { // Scale up the pixmaps (to give cleaner looking + // icons than leaving it for QToolButton to do). + QWMatrix w; + w = w.scale (2.0, 2.0); + + // The pixmaps shown on the buttons used to remain small and incorrectly + // painted, in KDE, in spite of the 2x (32x32) scaling. "insertButton" + // calls QIconSet, to generate a set of icons from each pixmapx, then + // seems to select the small size to paint on the button. The following + // line forces all icons, large and small, to be size 32x32 in advance. + QIconSet::setIconSize (QIconSet::Small, QSize (32, 32)); + + brickbg = brickbg.xForm (w); + fbrickbg = fbrickbg.xForm (w); + + freebg = freebg.xForm (w); + nuggetbg = nuggetbg.xForm (w); + polebg = polebg.xForm (w); + betonbg = betonbg.xForm (w); + ladderbg = ladderbg.xForm (w); + hladderbg = hladderbg.xForm (w); + edherobg = edherobg.xForm (w); + edenemybg = edenemybg.xForm (w); + } + + editToolbar = new KToolBar (this, Qt::DockTop, TRUE, "Editor", TRUE); + + // Choose a colour that enhances visibility of the KGoldrunner pixmaps. + // editToolbar->setPalette (QPalette (QColor (150, 150, 230))); + + // editToolbar->setHorizontallyStretchable (TRUE); // Not effective in KDE. + + // All those separators are just to get reasonable visual spacing of the + // pixmaps in KDE. "setHorizontallyStretchable(TRUE)" does it in Qt. + + editToolbar->insertSeparator(); + + editToolbar->insertButton ("filenew", 0, SIGNAL(clicked()), game, + SLOT(createLevel()), TRUE, i18n("&Create a Level")); + editToolbar->insertButton ("fileopen", 1, SIGNAL(clicked()), game, + SLOT(updateLevel()), TRUE, i18n("&Edit Any Level...")); + editToolbar->insertButton ("filesave", 2, SIGNAL(clicked()), game, + SLOT(saveLevelFile()),TRUE, i18n("&Save Edits...")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton ("ktip", 3, SIGNAL(clicked()), game, + SLOT(editNameAndHint()),TRUE,i18n("Edit Name/Hint")); + + editToolbar->insertSeparator(); + editToolbar->insertSeparator(); + + editToolbar->insertButton (freebg, (int)FREE, SIGNAL(clicked()), this, + SLOT(freeSlot()), TRUE, i18n("Empty space")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edherobg, (int)HERO, SIGNAL(clicked()), this, + SLOT (edheroSlot()), TRUE, i18n("Hero")); + editToolbar->insertSeparator(); + editToolbar->insertButton (edenemybg, (int)ENEMY, SIGNAL(clicked()), this, + SLOT (edenemySlot()), TRUE, i18n("Enemy")); + editToolbar->insertSeparator(); + editToolbar->insertButton (brickbg, (int)BRICK, SIGNAL(clicked()), this, + SLOT (brickSlot()), TRUE, i18n("Brick (can dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (betonbg, (int)BETON, SIGNAL(clicked()), this, + SLOT (betonSlot()), TRUE, i18n("Concrete (cannot dig)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (fbrickbg, (int)FBRICK, SIGNAL(clicked()), this, + SLOT (fbrickSlot()), TRUE, i18n("Trap (can fall through)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (ladderbg, (int)LADDER, SIGNAL(clicked()), this, + SLOT (ladderSlot()), TRUE, i18n("Ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (hladderbg, (int)HLADDER, SIGNAL(clicked()), this, + SLOT (hladderSlot()), TRUE, i18n("Hidden ladder")); + editToolbar->insertSeparator(); + editToolbar->insertButton (polebg, (int)POLE, SIGNAL(clicked()), this, + SLOT (poleSlot()), TRUE, i18n("Pole (or bar)")); + editToolbar->insertSeparator(); + editToolbar->insertButton (nuggetbg, (int)NUGGET, SIGNAL(clicked()), this, + SLOT (nuggetSlot()), TRUE, i18n("Gold nugget")); + + editToolbar->setToggle ((int) FREE, TRUE); + editToolbar->setToggle ((int) HERO, TRUE); + editToolbar->setToggle ((int) ENEMY, TRUE); + editToolbar->setToggle ((int) BRICK, TRUE); + editToolbar->setToggle ((int) BETON, TRUE); + editToolbar->setToggle ((int) FBRICK, TRUE); + editToolbar->setToggle ((int) LADDER, TRUE); + editToolbar->setToggle ((int) HLADDER, TRUE); + editToolbar->setToggle ((int) POLE, TRUE); + editToolbar->setToggle ((int) NUGGET, TRUE); + + pressedButton = (int) BRICK; + editToolbar->setButton (pressedButton, TRUE); +} + +/******************************************************************************/ +/********************* EDIT-BUTTON SLOTS **********************************/ +/******************************************************************************/ + +void KGoldrunner::freeSlot() + { game->setEditObj (FREE); setButton ((int) FREE); } +void KGoldrunner::edheroSlot() + { game->setEditObj (HERO); setButton ((int) HERO); } +void KGoldrunner::edenemySlot() + { game->setEditObj (ENEMY); setButton ((int) ENEMY); } +void KGoldrunner::brickSlot() + { game->setEditObj (BRICK); setButton ((int) BRICK); } +void KGoldrunner::betonSlot() + { game->setEditObj (BETON); setButton ((int) BETON); } +void KGoldrunner::fbrickSlot() + { game->setEditObj (FBRICK); setButton ((int) FBRICK); } +void KGoldrunner::ladderSlot() + { game->setEditObj (LADDER); setButton ((int) LADDER); } +void KGoldrunner::hladderSlot() + { game->setEditObj (HLADDER); setButton ((int) HLADDER); } +void KGoldrunner::poleSlot() + { game->setEditObj (POLE); setButton ((int) POLE); } +void KGoldrunner::nuggetSlot() + { game->setEditObj (NUGGET); setButton ((int) NUGGET); } +void KGoldrunner::defaultEditObj() + { setButton ((int) BRICK); } + +void KGoldrunner::setButton (int btn) +{ + editToolbar->setButton (pressedButton, FALSE); + pressedButton = btn; + editToolbar->setButton (pressedButton, TRUE); +} + +#include "kgoldrunner.moc" diff --git a/kgoldrunner/src/kgoldrunner.h b/kgoldrunner/src/kgoldrunner.h new file mode 100644 index 00000000..57123596 --- /dev/null +++ b/kgoldrunner/src/kgoldrunner.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krger <ianw2@optusnet.com.au> + */ + +#ifndef _KGOLDRUNNER_H_ +#define _KGOLDRUNNER_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +// Status bar +const int ID_LIVES = 0; // Field IDs in KDE status bar. +const int ID_SCORE = 1; +const int ID_LEVEL = 2; +const int ID_HINTAVL = 3; +const int ID_MSG = 4; + +const int L_LIVES = 15; // Lengths of fields. +const int L_SCORE = 17; +const int L_LEVEL = 15; + +#include <kapplication.h> +#include <kmainwindow.h> +#include <kstandarddirs.h> +#include <kaction.h> + +class KGrGame; +class KGrCanvas; +class KGrHero; + +/** + * This class serves as the main window for KGoldrunner. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author $AUTHOR <$EMAIL> + * @version $APP_VERSION + */ +class KGoldrunner : public KMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KGoldrunner(); + + /** + * Default Destructor + */ + virtual ~KGoldrunner(); + + bool startedOK() {return (startupOK);} + +protected: + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties. + */ + void readProperties(KConfig *); + + bool queryClose (); // To save edits before closing. + +private slots: + // Slot to pause or restart the game. + void stopStart(); + + // Local slots to create or edit game information. + void createGame(); + void editGameInfo(); + + // Local slots to set the landscape (colour scheme). + void lsKGoldrunner(); + void lsApple2(); + void lsIceCave(); + void lsMidnight(); + void lsKDEKool(); + + // Local slots to set mouse/keyboard control of the hero. + void setMouseMode(); + void setKeyBoardMode(); + + // Local slots to set game speed. + void normalSpeed(); + void beginSpeed(); + void champSpeed(); + void incSpeed(); + void decSpeed(); + + // Slots to set Traditional or KGoldrunner rules. + void setTradRules(); + void setKGrRules(); + + // Local slots to make playing area larger or smaller. + void makeLarger(); + void makeSmaller(); + + // Local slots for hero control keys. + void goUp(); + void goR(); + void goDown(); + void goL(); + void stop(); + void digR(); + void digL(); + + void setKey (KBAction movement); + + // Local slots for authors' debugging aids. + void showEnemy0(); + void showEnemy1(); + void showEnemy2(); + void showEnemy3(); + void showEnemy4(); + void showEnemy5(); + void showEnemy6(); + + void optionsShowToolbar(); + void optionsShowStatusbar(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + void optionsPreferences(); + void newToolbarConfig(); + + void changeStatusbar(const QString& text); + void changeCaption(const QString& text); + + void showLevel (int); // Show the current level number. + void showLives (long); // Show how many lives are remaining. + void showScore (long); // Show the player's score. + void gameFreeze (bool); // Status feedback on freeze/unfreeze. + + void adjustHintAction (bool); // Enable/disable "Hint" action. + void markRuleType (char ruleType); // Check game's rule type in the menu. + void setEditMenu (bool on_off); // Enable/disable "Save Edits" action. + +private: + void setupAccel(); + void setupActions(); + void initStatusBar(); + void makeEditToolbar(); + void setButton (int btn); + +private: + bool startupOK; + + KGrCanvas * view; + KGrGame * game; + + bool getDirectories(); // Get directory paths, as below. + QString systemHTMLDir; // Where the manual is stored. + QString systemDataDir; // Where the system levels are stored. + QString userDataDir; // Where the user levels are stored. + + KAction * saveGame; // Save game, level, lives and score. + + KAction * myPause; // Pause or resume the game. + QString pauseKeys; // Keystroke names to put in status bar. + + KAction * hintAction; // Display a hint, if available. + KAction * killHero; // Kill hero (disabled during edits). + KAction * highScore; // High scores (disabled during edits). + + KAction * saveEdits; // Save a level that has been edited. + + KRadioAction * setKGoldrunner; // Show default "KGoldrunner" landscape. + KRadioAction * setAppleII; // Show "Apple II" landscape. + KRadioAction * setIceCave; // Show "Ice Cave" landscape. + KRadioAction * setMidnight; // Show "Midnight" landscape. + KRadioAction * setKDEKool; // Show "KDE Kool" landscape. + + KRadioAction * setMouse; // Show mouse/keyboard mode on menu. + KRadioAction * setKeyboard; // Show mouse/keyboard mode on menu. + + KRadioAction * tradRules; // Set Traditional rules. + KRadioAction * kgrRules; // Set KGoldrunner rules. + + KGrHero * hero; // Pointer to the hero. + + // KToggleAction * m_toolbarAction; + // KToggleAction * m_statusbarAction; + + KToolBar * editToolbar; // Toolbar for creating/editing levels. + int pressedButton; // ID of currently set toolbar button. + +private slots: + void freeSlot(); // Set editObj to Free Space. + void edheroSlot(); // Set editObj to Hero. + void edenemySlot(); // Set editObj to Enemy. + void brickSlot(); // Set editObj to Brick. + void betonSlot(); // Set editObj to Concrete. + void fbrickSlot(); // Set editObj to Fall-through Brick. + void ladderSlot(); // Set editObj to Ladder. + void hladderSlot(); // Set editObj to Hidden Ladder. + void poleSlot(); // Set editObj to Pole (or Bar). + void nuggetSlot(); // Set editObj to Gold Nugget. + void defaultEditObj(); // Set editObj to default (brick). +}; + +#endif // _KGOLDRUNNER_H_ diff --git a/kgoldrunner/src/kgoldrunnerui.rc b/kgoldrunner/src/kgoldrunnerui.rc new file mode 100644 index 00000000..9b8ce1fe --- /dev/null +++ b/kgoldrunner/src/kgoldrunnerui.rc @@ -0,0 +1,75 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui version="4" name="KGoldrunner" > +<MenuBar> + <Menu name="game" > + <Action append="new_merge" name="play_any" /> + <Action append="new_merge" name="play_next" /> + <Action append="new_merge" name="tutorial" /> + <Action append="save_merge" name="save_edits" /> + <Action append="misc_merge" name="get_hint" /> + <Action append="misc_merge" name="kill_hero" /> + </Menu> + <Menu name="editor" > <text>&Editor</text> + <Action name="create" /> + <Action name="edit_any" /> + <Action name="edit_next" /> + <Separator/> + <Action name="save_edits" /> + <Action name="move_level" /> + <Action name="delete_level" /> + <Separator/> + <Action name="create_game" /> + <Action name="edit_game" /> + </Menu> + <Menu name="landscapes" > <text>&Landscapes</text> + <Action name="kgoldrunner" /> + <Action name="apple_2" /> + <Action name="ice_cave" /> + <Action name="midnight" /> + <Action name="kde_kool" /> + </Menu> + <Menu name="settings" > + <Action append="save_merge" name="mouse_mode" /> + <Action append="save_merge" name="keyboard_mode" /> + <Separator append="save_merge" /> + <Action append="save_merge" name="normal_speed" /> + <Action append="save_merge" name="beginner_speed" /> + <Action append="save_merge" name="champion_speed" /> + <Action append="save_merge" name="increase_speed" /> + <Action append="save_merge" name="decrease_speed" /> + <Separator append="save_merge" /> + <Action append="save_merge" name="trad_rules" /> + <Action append="save_merge" name="kgr_rules" /> + <Separator append="save_merge" /> + <Action append="save_merge" name="larger_area" /> + <Action append="save_merge" name="smaller_area" /> + </Menu> + <Menu name="help" > + <Action name="tutorial" /> + <Action name="get_hint" /> + </Menu> +</MenuBar> + +<ActionProperties> + <Action shortcut="P;Escape" name="game_pause" /> + <Action shortcut="S" name="game_save" /> + <Action shortcut="Ctrl+S" name="save_edits" /> + + <Action shortcut="Shift+G" name="kgoldrunner" /> + <Action shortcut="Shift+A" name="apple_2" /> + <Action shortcut="Shift+I" name="ice_cave" /> + <Action shortcut="Shift+M" name="midnight" /> + <Action shortcut="Shift+K" name="kde_kool" /> + + <Action shortcut="Up;I" name="move_up" /> + <Action shortcut="Right;L" name="move_right" /> + <Action shortcut="Down;K" name="move_down" /> + <Action shortcut="Left;J" name="move_left" /> + <Action shortcut="Space" name="stop" /> + <Action shortcut="Z;U" name="dig_left" /> + <Action shortcut="C;O" name="dig_right" /> + + <Action shortcut="+" name="increase_speed" /> + <Action shortcut="-" name="decrease_speed" /> +</ActionProperties> +</kpartgui> diff --git a/kgoldrunner/src/kgraphics.h b/kgoldrunner/src/kgraphics.h new file mode 100644 index 00000000..81f004ad --- /dev/null +++ b/kgoldrunner/src/kgraphics.h @@ -0,0 +1,357 @@ +/*************************************************************************** + kgrgraphics.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRAPHICS_H +#define KGRAPHICS_H + +// List of colour schemes. +static const char * colourScheme [] = { + "KGoldrunner", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #c8b0a0", /* Background */ + "; c #b8a090", /* Background mortar */ + "o c #ff0000", /* Solid light */ + "x c #c05040", /* Solid */ + "s c #b00020", /* Solid dark */ + "+ c #500000", /* Mortar */ + ": c #b47444", /* Ladder light */ + "# c #845424", /* Ladder dark */ + "a c #ffffff", /* Pole or bar */ + "Apple II", + "#8a8acb", /* Border - Apple II blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #001020", /* Background */ + "; c #001020", /* Background mortar */ + "o c #8a8acb", /* Solid light */ + "x c #8a8acb", /* Solid */ + "s c #8a8acb", /* Solid dark */ + "+ c #001020", /* Mortar */ + ": c #dddddd", /* Ladder light */ + "# c #dddddd", /* Ladder dark */ + "a c #dddddd", /* Pole or bar */ + "Ice Cave", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #efefff", /* Background */ + "; c #d0dfef", /* Background mortar */ + "o c #ffffff", /* Solid light */ + "x c #d0f0ff", /* Solid */ + "s c #b0d8f0", /* Solid dark */ + "+ c #a8c8ff", /* Mortar */ + ": c #ffffff", /* Ladder light */ + "# c #f9d26a", /* Ladder dark */ + "a c #40a0ff", /* Pole or bar */ + "Midnight", + "#5a5a9b", /* Border - periwinkle blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #000040", /* Background */ + "; c #000020", /* Background mortar */ + "o c #880000", /* Solid light */ + "x c #702820", /* Solid */ + "s c #680010", /* Solid dark */ + "+ c #200000", /* Mortar */ + ": c #563622", /* Ladder light */ + "# c #422a12", /* Ladder dark */ + "a c #666666", /* Pole or bar */ + "KDE Kool", + "#aabaf0", /* Border - pale blue */ + "#ffffff", /* Title-text - pure-white. */ + ". c #eef7ff", /* Background */ + "; c #eef7ff", /* Background mortar */ + "o c #ecfdfe", /* Solid light */ + "x c #c1dafe", /* Solid */ + "s c #c1dafe", /* Solid dark */ + "+ c #9a9afe", /* Mortar */ + ": c #f9d26a", /* Ladder light */ + "# c #c19a68", /* Ladder dark */ + "a c #af7516", /* Pole or bar */ + "" /* TERMINATOR */ +}; + +/* XPM - Background brick or square (free space) */ +static const char * hgbrick_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Editor's icon for the hero */ +static const char * edithero_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"c c #008000", +"a c #00c000", +"b c #00ff00", +"d c #808080", +"...........;....", +"...........;....", +"..........a;....", +"........baacc...", +"........bcccc...", +".......bccca....", +"....caacccccc...", +";;;;bccacccaccc.", +"...;bccaacccaccc", +"...;caacccc.....", +"...;..dbccca....", +"...;.ccacccca...", +"...bcccccaccc...", +"...ac....bccc...", +"...;.....bccc...", +";;;;;;;;;caccc;;"}; + +/* XPM - Editor's icon for an enemy */ +static const char * editenemy_xpm []={ +"16 16 13 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"d c #000080", +"c c #0000ff", +"b c #008080", +"a c #00ffff", +"...........;....", +"...........;....", +"...........;....", +"........abbcc...", +"........acccc...", +".......acccd....", +"....bbbcccccc...", +";;;;accbcccbccbd", +"....accbbccbbccc", +"....bbbcccc.bdd.", +"....;..acccb....", +"....;bbbccccc...", +"...acccccbccc...", +"...bcddddaccc...", +"....;....accc...", +";;;;;;;;;bbccc;;"}; + +/* XPM - Ladder */ +static const char * ladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#::::::::::::##", +":############:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":#::::::::::::##", +":############:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Hidden ladder (for Editor only) */ +static const char * hladder_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +":##........;.:##", +":##........;.:##", +":##........;.:##", +":#:::::::..;.:##", +":########..;.:##", +":##........;.:##", +":##........;.:##", +":##;;;;;;;;;;:##", +":##.;........:##", +":##.;........:##", +":##.;........:##", +":##.;..:::::::##", +":##.;..######:##", +":##.;........:##", +":##.;........:##", +":##;;;;;;;;;;:##"}; + +/* XPM - Nugget */ +static const char * nugget_xpm []={ +"16 16 12 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"a c #c0b000", +"c c #e08000", +"b c #ffff00", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +"......bba..;....", +";;;;bbbbbcca;;;;", +"...bbbababacc...", +"..abbbababaac...", +"..bbbbbababaa...", +"..bbbbbbabaac...", +"..abbababacca...", +"...ababacacc....", +"....acacacc;....", +";;;;;;aaa;;;;;;;"}; + +/* XPM - Pole or bar */ +static const char * pole_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"...........;....", +"...........;....", +"aaaaaaaaaaaaaaaa", +"...........;....", +"...........;....", +"...........;....", +"...........;....", +";;;;;;;;;;;;;;;;", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +"....;...........", +";;;;;;;;;;;;;;;;"}; + +/* XPM - Concrete */ +static const char * beton_xpm []={ +"16 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sxssxssxssxsssss", +"sxxsxxsxxsxsxxsx", +"xsxsxsxssxxssxxs", +"xssxsxxsxssxsxss", +"sxxsxssxsxsxxsxs", +"xxsxxxssxsxsxxsx", +"sxsxssxxsxxssxsx", +"xsxsxssxsxsxsxss", +"xssxxsxssxssxsxs", +"xsxsxssxssxxxsxx", +"sxxssxxsxxsxsxss", +"sxsxsssxsxsxsxxs", +"sxssxxssxsxssxss", +"ssxssxsxsxsxsxxs", +"xsxxsxssxsxxsxss", +"++++++++++++++++"}; + +/* XPM - Bricks (10 pics - from whole brick to background [hole] and back) */ +static const char * bricks_xpm []={ +"160 16 9 1", +". c #c8b0a0", /* Background */ +"; c #b8a090", /* Background mortar */ +"o c #ff0000", /* Solid light */ +"x c #c05040", /* Solid */ +"s c #b00020", /* Solid dark */ +"+ c #500000", /* Mortar */ +": c #b47444", /* Ladder light */ +"# c #845424", /* Ladder dark */ +"a c #ffffff", /* Pole or bar */ +"sosossosos++osssssx;x;.x;;++s++sx;+;x;.x;;;;;+++x;x+x+.x;;;;;;.;;;;;;;.;;;;;;+.;...........;....osos+;.x+++;ssossososossos++osssososososso++ossssossossoso++osss", +"sxsxsxsxsx++oxsxsx+..;...x;++;+x+....;...++;;+++.....;+.+x;;;..;.....;...x;;+..;...........;....xs;++;...x;;+++xsx+;;+;+;+++o+xxxsxsx;sxsx++oxsx+xsxsxxxsx++osxs", +"xsxsxsxxsx++osxsxxsxs;+x;.;+osxs+;+;++;x;.;;x;+;+x;.x.;x;.;;++++;x;.x.;x;.;;x;.;...........;....+;+;x+;x;.+;+;sxs+;++++++;++o++xsxxx+++sxx++osxssxsxssxsxx++oxsx", +"xsxssxsxsx++oxxsxxssxsx+x+++osxsxsxs;..+;x;;++;s+++.;....x+;;.;+..;.;.+..x;;;.;;...........;....s++.;....x+;;+;xsxs++.+++++;s++xsssx++xsx++;osxxxsxxxxsxss++oxss", +"xsxxxsxxsx++osxssxsxsxsxsx++osxss+++x+++++;;x++;+.x.x;.+.;+;x+x;;.x.x;.;.;;;x.x;...........;....;.x.++.;.;;;++x.s;+xx++;.;+;o;+sxxxsx+.;.+;;s+sxsxssxsxx+x++osxs", +"xsxsx+sxss++oxsxxxxxsxsxss++osxxsx+++;+;+;+;++sxs;.+..x.;x;;.+++.;.;..x.++;;.;.x...........;....+;...+x.;x;;.+.xxs+;.+x.+;+;o+;ssxs++.x.+;+;osxxsxx++;s+++++oxsx", +"ssxxsxxxsx++oxssxssxxsxxxs++oxsxxs+;+++++++;oxss;+;.;;;;++;;++;s;x+.;;;+.;;;;.;+...........;....;x....+..;;;+.;;s;++;;+;.+;;s++xsxxs;;;;.+;;oxsxsx+sxsx;.;++oxsx", +"+++++++++++++++++++++++++++++++++++++++;+++;++++;;;;;+;;;;;;++++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+;;;;;;;;;;++;;++;;;;;;;;;;;++++++++;;;;;;;++++++++++;;;;;+++++", +"sss++ossssossssssss++ossssosssososo++osssssossso+;;+;s;+;x++++so;...;x;++x;x+.x+....;...........x....x+x;x;x;.x;s+++;x;x;x;x;++ssss++++x;x++++++sso++osx;xs+ssso", +"xsx++osxsx+xsxxsxsx++oxsxxsxsxsxxsx++oxsxsxsxxsxsxx+;+++...+;+;s++x+;;......;+......;.............x+;;......+...x+;+;+......++;sxsx++s;..+;+;+;sxsx++ox.+s++xsxs", +"sxs++oxsxsxsxssx+sx++osxs+xsxxx+sxs++osx+xs+ss;sxs;+;++;++.;+++x.;;;;x++;++x+;+;....;...........;+;;;x..x..x;.++s+++;+;;;;;s;+xsxxs+++++;+sx++xsxxs++osxx+sxsxxx", +"xsx++oxssxsxsxssxss++oxsxsxxsssxsxx++oxsssxxsxssxsx++os;+xs+xsxsx.++;+++++++++++....;...........++x+;.....;.+.;+x+;++os..s;+++++xsx++ox++;s+xxsxxsx++oxs++s++xss", +"xsx++oxsxxxsxxxsxxx++oxsxsx+xsxxxss++osxxxssxsxxsxx++oxssxsxsxxs;+;;;;+;+;++;+x+....;...........;+;;;+.x;.+;+..+s;+++ox+;;++;+++sxx++os;++;xssxssxx++osxsxsxssxx", +"xsx++ox+xssxssxsxsx++oxxssxsxxssxxx++osxxxxsxxsxssx++osxsxxsxssxsx;;;+++++x+++++....;...........xs++;.+.++;s+.+;sxs++o++sx+x++;;ssx++oxsxs+sxsxxsxs++oxsxsxxxxsx", +"sxs++osssxxsxxsxxsx++osxsxsxssxssxs++oxssxsxsxsxxxs++osxxsxsxsxssx+;;+;;;+++++++....;...........s;s+;.x..x.;+;sxx+x++o+;++++++sxsxs++ossxs;sxsxsxxs++oxssxssxssx", +"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++;;;;;;++;+++;;;;;;;;;;;;;;;;+;++;;;;;;;;;+++++++++++++++++++++++++++++++++++++++++++++++++++"}; +#endif // KGRAPHICS_H diff --git a/kgoldrunner/src/kgrcanvas.cpp b/kgoldrunner/src/kgrcanvas.cpp new file mode 100644 index 00000000..6104939e --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.cpp @@ -0,0 +1,561 @@ +/*************************************************************************** + kgrcanvas.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" + +#include "kgrdialog.h" +#include "kgrcanvas.h" +#include "kgrgame.h" + +// Graphics files for moving figures and background. +#include "hero.xpm" +#include "enemy1.xpm" +#include "enemy2.xpm" +#include "kgraphics.h" + +class KGoldrunner; + +KGrCanvas::KGrCanvas (QWidget * parent, const char *name) + : QCanvasView (0, parent, name) +{ + setBackgroundMode (NoBackground); + m = new QCursor (); // For handling the mouse. + + scaleStep = STEP; // Initial scale is 1:1. + baseScale = scaleStep; + baseFontSize = fontInfo().pointSize(); + + border = 4; // Allow 2 tile-widths on each side for border. + cw = 4*STEP; // Playfield cell width (= four steps). + bw = border*cw/2; // Total border width (= two cells). + lw = cw/8; // Line width (for edge of border). + mw = bw - lw; // Border main-part width. + + initView(); // Set up the graphics, etc. +} + +KGrCanvas::~KGrCanvas() +{ +} + +void KGrCanvas::changeLandscape (const QString & name) +{ + for (int i = 0; strcmp (colourScheme [i], "") != 0; i++) { + if (colourScheme [i] == name) { + + // Change XPM colours and re-draw the tile-pictures used by QCanvas. + changeColours (& colourScheme [i]); + makeTiles(); + + // Set all cells to same tile-numbers as before, but new colours. + int tileNo [FIELDWIDTH] [FIELDHEIGHT]; + int offset = border / 2; + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + tileNo[x][y] = field->tile (x + offset, y + offset); + } + } + + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); // Sets all tile-numbers to 0. + + for (int x = 0; x < FIELDWIDTH; x++) { + for (int y = 0; y < FIELDHEIGHT; y++) { + field->setTile (x + offset, y + offset, tileNo[x][y]); + } + } + + borderB->setBrush (QBrush (borderColor)); + borderL->setBrush (QBrush (borderColor)); + borderR->setBrush (QBrush (borderColor)); + + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Repaint the playing area. + updateCanvas(); + return; + } + } +} + +bool KGrCanvas::changeSize (int d) +{ +#ifdef QT3 + if ((d < 0) && (scaleStep <= STEP)) { + // Note: Smaller scales lose detail (e.g. the joints in brickwork). + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any smaller.")); + return FALSE; + } + + if ((d >= 0) && (scaleStep >= 2 * STEP)) { + // Note: Larger scales go off the edge of the monitor. + KGrMessage::information (this, i18n("Change Size"), + i18n("Sorry, you cannot make the play area any larger.")); + return FALSE; + } + + QWMatrix wm = worldMatrix(); + double wmScale = 1.0; + + // Set the scale back to 1:1 and calculate the new scale factor. + wm.reset(); + scaleStep = (d < 0) ? (scaleStep - 1) : (scaleStep + 1); + + // If scale > 1:1, scale up to the new factor (e.g. 1.25:1, 1.5:1, etc.) + if (scaleStep > STEP) { + wmScale = (wmScale * scaleStep) / STEP; + wm.scale (wmScale, wmScale); + } + setWorldMatrix (wm); + + // Force the title size and position to be re-calculated. + QString t = title->text(); + makeTitle (); + setTitle (t); + + // Fit the QCanvasView and its frame to the canvas. + int frame = frameWidth()*2; + setFixedSize ((FIELDWIDTH + 4) * 4 * scaleStep + frame, + (FIELDHEIGHT + 4) * 4 * scaleStep + frame); + return TRUE; + +#else + KGrMessage::information (this, i18n( "Change Size" ), + i18n( "Sorry, you cannot change the size of the playing area. " + "That function requires Qt Library version 3 or later." )); + return FALSE; +#endif +} + +void KGrCanvas::updateCanvas() +{ + field->update(); +} + +void KGrCanvas::paintCell (int x, int y, char type, int offset) +{ + int tileNumber = 0; + + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + tileNumber = tileNumber + offset; // Offsets 1-9 are for digging sequence. + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + x++; y++; + field->setTile (x, y, tileNumber); // Paint cell with required pixmap. +} + +void KGrCanvas::setBaseScale () +{ + // Synchronise the desktop font size with the initial canvas scale. + baseScale = scaleStep; + QString t = title->text(); + makeTitle (); + setTitle (t); +} + +void KGrCanvas::setTitle (QString newTitle) +{ + title->setText (newTitle); +} + +void KGrCanvas::makeTitle () +{ + // This uses a calculated QLabel and QFont size because a QCanvasText + // object does not always display scaled-up fonts cleanly (in Qt 3.1.1). + + if (title != 0) + title->close (TRUE); // Close and delete previous title. + + title = new QLabel ("", this); + title->setFixedWidth (((FIELDWIDTH * cw + 2 * bw) * scaleStep) / STEP); + title->setFixedHeight ((mw * scaleStep) / STEP); + title->move (0, 0); + title->setPaletteBackgroundColor (borderColor); + title->setPaletteForegroundColor (textColor); + title->setFont (QFont (fontInfo().family(), + (baseFontSize * scaleStep) / baseScale, QFont::Bold)); + title->setAlignment (Qt::AlignCenter); + title->raise(); + title->show(); +} + +void KGrCanvas::contentsMousePressEvent (QMouseEvent * m) { + emit mouseClick (m->button ()); +} + +void KGrCanvas::contentsMouseReleaseEvent (QMouseEvent * m) { + emit mouseLetGo (m->button ()); +} + +QPoint KGrCanvas::getMousePos () +{ + int i, j; + int fw = frameWidth(); + int cell = 4 * scaleStep; + QPoint p = mapFromGlobal (m->pos()); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i = ((p.x() - fw) / cell) - 1; j = ((p.y() - fw) / cell) - 1; + + return (QPoint (i, j)); +} + +void KGrCanvas::setMousePos (int i, int j) +{ + int fw = frameWidth(); + int cell = 4 * scaleStep; + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + //m->setPos (mapToGlobal (QPoint (i * 4 * STEP + 8, j * 4 * STEP + 8))); + //m->setPos (mapToGlobal (QPoint (i * 5 * STEP + 10, j * 5 * STEP + 10))); + m->setPos (mapToGlobal ( + QPoint (i * cell + fw + cell / 2, j * cell + fw + cell / 2))); +} + +void KGrCanvas::makeHeroSprite (int i, int j, int startFrame) +{ + heroSprite = new QCanvasSprite (heroArray, field); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + heroSprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + heroSprite->setZ (1); + heroSprite->setVisible (TRUE); +} + +void KGrCanvas::setHeroVisible (bool newState) +{ + heroSprite->setVisible (newState); // Show or hide the hero. +} + +void KGrCanvas::makeEnemySprite (int i, int j, int startFrame) +{ + QCanvasSprite * enemySprite = new QCanvasSprite (enemyArray, field); + + enemySprites->append (enemySprite); + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + i++; j++; + enemySprite->move (i * 4 * STEP, j * 4 * STEP, startFrame); + enemySprite->setZ (2); + enemySprite->show(); +} + +void KGrCanvas::moveHero (int x, int y, int frame) +{ + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + heroSprite->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::moveEnemy (int id, int x, int y, int frame, int nuggets) +{ + if (nuggets != 0) { // If enemy is carrying gold, + frame = frame + goldEnemy; // show him with gold outline. + } + + // In KGoldrunner, the top-left visible cell is [1,1] --- in QCanvas [2,2]. + enemySprites->at(id)->move (x + 4 * STEP, y + 4 * STEP, frame); + updateCanvas(); +} + +void KGrCanvas::deleteEnemySprites() +{ + enemySprites->clear(); +} + +QPixmap KGrCanvas::getPixmap (char type) +{ + QPixmap pic (bgw, bgh, bgd); + QPainter p (& pic); + int tileNumber; + + // Get a pixmap from the tile-array for use on an edit-button. + switch (type) { + case FREE: tileNumber = freebg; break; // Free space. + case NUGGET: tileNumber = nuggetbg; break; // Nugget. + case POLE: tileNumber = polebg; break; // Pole or bar. + case LADDER: tileNumber = ladderbg; break; // Ladder. + case HLADDER: tileNumber = hladderbg; break;// Hidden ladder (for editing). + case HERO: tileNumber = edherobg; break; // Static hero (for editing). + case ENEMY: tileNumber = edenemybg; break;// Static enemy (for editing). + case BETON: tileNumber = betonbg; break; // Concrete. + case BRICK: tileNumber = brickbg; break; // Solid brick. + case FBRICK: tileNumber = fbrickbg; break; // False brick (for editing). + default: tileNumber = freebg; break; + } + + // Copy a tile of width bgw and height bgh from the tile-array. + p.drawPixmap (0, 0, bgPix, tileNumber * bgw, 0, bgw, bgh); + p.end(); + + return (pic); +} + +void KGrCanvas::initView() +{ + changeColours (& colourScheme [0]); // Set "KGoldrunner" landscape. + + // Set up the pixmaps for the editable objects. + freebg = 0; // Free space. + nuggetbg = 1; // Nugget. + polebg = 2; // Pole or bar. + ladderbg = 3; // Ladder. + hladderbg = 4; // Hidden ladder (for editing). + edherobg = 5; // Static hero (for editing). + edenemybg = 6; // Static enemy (for editing). + betonbg = 7; // Concrete. + + // The bricks have 10 pixmaps (showing various stages of digging). + brickbg = 8; // Solid brick - 1st pixmap. + fbrickbg = 15; // False brick - 8th pixmap (for editing). + + QPixmap pixmap; + QImage image; + + pixmap = QPixmap (hgbrick_xpm); + + bgw = pixmap.width(); // Save dimensions for "getPixmap". + bgh = pixmap.height(); + bgd = pixmap.depth(); + + // Assemble the background and editing pixmaps into a strip (18 pixmaps). + bgPix = QPixmap ((brickbg + 10) * bgw, bgh, bgd); + + makeTiles(); // Fill the strip with 18 tiles. + + // Define the canvas as an array of tiles. Default tile is 0 (free space). + int frame = frameWidth()*2; + field = new QCanvas ((FIELDWIDTH+border) * bgw, (FIELDHEIGHT+border) * bgh); + field->setTiles (bgPix, (FIELDWIDTH+border), (FIELDHEIGHT+border), + bgw, bgh); + + // Embed the canvas in the view and make it occupy the whole of the view. + setCanvas (field); + setVScrollBarMode (QScrollView::AlwaysOff); + setHScrollBarMode (QScrollView::AlwaysOff); + setFixedSize (field->width() + frame, field->height() + frame); + + ////////////////////////////////////////////////////////////////////////// + // The pixmaps for hero and enemies are arranged in strips of 20: walk // + // right (4), walk left (4), climb right along bar (4), climb left (4), // + // climb up ladder (2) and fall (2) --- total 20. // + ////////////////////////////////////////////////////////////////////////// + + // Convert the pixmap strip for hero animation into a QCanvasPixmapArray. + pixmap = QPixmap (hero_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + QPixmap pm; + QValueList<QPixmap> pmList; + + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + heroArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + QPixmap * pm; + QPoint * pt; + QList<QPixmap> pmList; + QList<QPoint> ptList; + + pt = new QPoint (0, 0); // "Hot spot" not used in KGoldrunner. + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + heroArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + // Convert pixmap strips for enemy animations into a QCanvasPixmapArray. + // First convert the pixmaps for enemies with no gold ... + pixmap = QPixmap (enemy1_xpm); + image = pixmap.convertToImage (); + + pmList.clear(); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } +#else + ptList.clear(); + + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } +#endif + + // ... then convert the gold-carrying enemies. + pixmap = QPixmap (enemy2_xpm); + image = pixmap.convertToImage (); + +#ifdef QT3 + for (int i = 0; i < 20; i++) { + pm.convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + } + + enemyArray = new QCanvasPixmapArray (pmList); // Hot spots all (0,0). +#else + for (int i = 0; i < 20; i++) { + pm = new QPixmap (); + pm->convertFromImage (image.copy (i * 16, 0, 16, 16)); + pmList.append (pm); + ptList.append (pt); + } + + enemyArray = new QCanvasPixmapArray (pmList, ptList); +#endif + + goldEnemy = 20; // Offset of gold-carrying frames. + + // Draw the border around the playing area (z = 0). + makeBorder(); + + // Create a title item, in off-white colour, on top of the border. + title = 0; + makeTitle(); + + // Create an empty list of enemy sprites. +#ifdef QT3 + enemySprites = new QPtrList<QCanvasSprite> (); +#else + enemySprites = new QList<QCanvasSprite> (); +#endif + enemySprites->setAutoDelete(TRUE); +} + +void KGrCanvas::makeTiles () +{ + QPainter p (& bgPix); + + // First draw the single pixmaps (8 tiles) ... + p.drawPixmap (freebg * bgw, 0, QPixmap (hgbrick_xpm)); // Free space. + p.drawPixmap (nuggetbg * bgw, 0, QPixmap (nugget_xpm)); // Nugget. + p.drawPixmap (polebg * bgw, 0, QPixmap (pole_xpm)); // Pole or bar. + p.drawPixmap (ladderbg * bgw, 0, QPixmap (ladder_xpm)); // Ladder. + p.drawPixmap (hladderbg * bgw, 0, QPixmap (hladder_xpm)); // Hidden laddr. + p.drawPixmap (edherobg * bgw, 0, QPixmap (edithero_xpm)); // Static hero. + p.drawPixmap (edenemybg * bgw, 0, QPixmap (editenemy_xpm)); // Static enemy. + p.drawPixmap (betonbg * bgw, 0, QPixmap (beton_xpm)); // Concrete. + + // ... then add the 10 brick pixmaps. + p.drawPixmap (brickbg * bgw, 0, QPixmap (bricks_xpm)); // Bricks. + + p.end(); +} + +void KGrCanvas::makeBorder () +{ + // Draw main part of border, in the order: top, bottom, left, right. + // Allow some overlap to prevent slits appearing when using "changeSize". + colour = borderColor; + + // The first rectangle is actually a QLabel drawn by "makeTitle()". + // borderT = drawRectangle (11, 0, 0, FIELDWIDTH*cw + 2*bw, mw); + borderB = drawRectangle (11, 0, FIELDHEIGHT*cw + bw + lw, + FIELDWIDTH*cw + 2*bw, mw); + borderL = drawRectangle (12, 0, bw - lw - 1, mw, FIELDHEIGHT*cw + 2*lw + 2); + borderR = drawRectangle (12, FIELDWIDTH*cw + bw + lw, bw - lw - 1, + mw, FIELDHEIGHT*cw + 2*lw + 2); + + // Draw inside edges of border, in the same way. + colour = QColor (black); + drawRectangle (10, bw-lw, bw-lw-1, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw-lw, FIELDHEIGHT*cw + bw, FIELDWIDTH*cw + 2*lw, lw+1); + drawRectangle (10, bw - lw, bw, lw, FIELDHEIGHT*cw); + drawRectangle (10, FIELDWIDTH*cw + bw, bw, lw, FIELDHEIGHT*cw); +} + +QCanvasRectangle * KGrCanvas::drawRectangle (int z, int x, int y, int w, int h) +{ + QCanvasRectangle * r = new QCanvasRectangle (x, y, w, h, field); + + r->setBrush (QBrush (colour)); + r->setPen (QPen (NoPen)); + r->setZ (z); + r->show(); + + return (r); +} + +void KGrCanvas::changeColours (const char * colours []) +{ + recolourObject (hgbrick_xpm, colours); + recolourObject (nugget_xpm, colours); + recolourObject (pole_xpm, colours); + recolourObject (ladder_xpm, colours); + recolourObject (hladder_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (edithero_xpm, colours); + recolourObject (editenemy_xpm, colours); + recolourObject (beton_xpm, colours); + recolourObject (bricks_xpm, colours); + + borderColor = QColor (colours [1]); + textColor = QColor (colours [2]); + + KGrThumbNail::backgroundColor = QColor (QString(colours [3]).right(7)); + KGrThumbNail::brickColor = QColor (QString(colours [6]).right(7)); + KGrThumbNail::ladderColor = QColor (QString(colours [9]).right(7)); + KGrThumbNail::poleColor = QColor (QString(colours [11]).right(7)); +} + +void KGrCanvas::recolourObject (const char * object [], const char * colours []) +{ + int i; + for (i = 0; i < 9; i++) { + object [i+1] = colours [i+3]; + } +} + +#include "kgrcanvas.moc" diff --git a/kgoldrunner/src/kgrcanvas.h b/kgoldrunner/src/kgrcanvas.h new file mode 100644 index 00000000..02b89bed --- /dev/null +++ b/kgoldrunner/src/kgrcanvas.h @@ -0,0 +1,111 @@ +/*************************************************************************** + kgrcanvas.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGRCANVAS_H +#define KGRCANVAS_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <qcursor.h> +#include <qcanvas.h> +#include <qlabel.h> +#include <qimage.h> +#include <qpainter.h> + +class KGrCanvas : public QCanvasView +{ + Q_OBJECT +public: + KGrCanvas (QWidget * parent = 0, const char *name = 0); + virtual ~KGrCanvas(); + + void changeLandscape (const QString & name); + + QPoint getMousePos (); + void setMousePos (int, int); + + bool changeSize (int); + void setBaseScale (); + + void updateCanvas (); + void paintCell (int, int, char, int offset = 0); + void setTitle (QString); + + void makeHeroSprite (int, int, int); + void setHeroVisible (bool); + void moveHero (int, int, int); + + void makeEnemySprite (int, int, int); + void moveEnemy (int, int, int, int, int); + void deleteEnemySprites(); + + QPixmap getPixmap (char type); + +signals: + void mouseClick (int); + void mouseLetGo (int); + +protected: + void contentsMousePressEvent (QMouseEvent *); + void contentsMouseReleaseEvent (QMouseEvent *); + +private: + QCursor * m; + + QCanvas * field; + QCanvasView * fieldView; + int scaleStep; // Current scale-factor of canvas. + int baseScale; // Starting scale-factor of canvas. + int baseFontSize; + + int border; // Number of tiles allowed for border. + int cw, bw, lw, mw; // Dimensions (in pixels) of the border. + QColor borderColor, textColor; // Border colours. + QLabel * title; // Title and top part of border. + QCanvasRectangle * borderB; // Bottom part of border. + QCanvasRectangle * borderL; // Left-hand part of border. + QCanvasRectangle * borderR; // Right-hand part of border. + + int freebg, nuggetbg, polebg, ladderbg, hladderbg; + int edherobg, edenemybg, betonbg, brickbg, fbrickbg; + int bgw, bgh, bgd; + QPixmap bgPix; + + QCanvasPixmapArray * heroArray; + QCanvasPixmapArray * enemyArray; + int goldEnemy; + + QCanvasSprite * heroSprite; +#ifdef QT3 + QPtrList<QCanvasSprite> * enemySprites; +#else + QList<QCanvasSprite> * enemySprites; +#endif + + void initView(); + void makeTiles(); + void makeBorder(); + void makeTitle(); + QColor colour; + QCanvasRectangle * drawRectangle (int, int, int, int, int); + void changeColours (const char * colours []); + void recolourObject (const char * object [], const char * colours []); +}; + +#endif // KGRCANVAS_H diff --git a/kgoldrunner/src/kgrconsts.h b/kgoldrunner/src/kgrconsts.h new file mode 100644 index 00000000..2006648a --- /dev/null +++ b/kgoldrunner/src/kgrconsts.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * kgrconsts.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRCONSTS_H +#define KGRCONSTS_H + +enum Owner {SYSTEM, USER}; + +const char FREE = ' '; +const char ENEMY = 'E'; +const char HERO = 'R'; +const char BETON = 'X'; +const char BRICK = 'M'; +const char FBRICK = 'F'; +const char HLADDER = 'Z'; +const char LADDER = 'H'; +const char NUGGET = 'N'; +const char POLE = 'T'; +const char HOLE = 'O'; +const char USEDHOLE= 'U'; + +const char CANWALKLEFT = 0x1; +const char CANWALKRIGHT = 0x2; +const char CANWALKUP = 0x4; +const char CANWALKDOWN = 0x8; +const char VISITED = 0x10; + +const char FIELDWIDTH = 28; +const char FIELDHEIGHT = 20; + +const char VERTIKAL = 0; +const char HORIZONTAL = 1; + +/* Action times ... */ +#define NSPEED 12 +#define MAXSPEED NSPEED * 2 +#define MINSPEED NSPEED / 4 + +#define BEGINSPEED NSPEED / 2 +#define NOVICESPEED (3 * NSPEED) / 4 +#define CHAMPSPEED (3 * NSPEED) / 2 + +typedef struct { + int hwalk; + int hfall; + int ewalk; + int efall; + int ecaptive; + int hole; +} Timing; + +const int DIGDELAY = 200; + +const int STEP = 4; + +const double DROPNUGGETDELAY = 70.0; // Enemy holds gold for avg. 12.5 cells. + +enum Position {RIGHTWALK1,RIGHTWALK2,RIGHTWALK3,RIGHTWALK4, + LEFTWALK1,LEFTWALK2,LEFTWALK3,LEFTWALK4, + RIGHTCLIMB1,RIGHTCLIMB2,RIGHTCLIMB3,RIGHTCLIMB4, + LEFTCLIMB1,LEFTCLIMB2,LEFTCLIMB3,LEFTCLIMB4, + CLIMB1,CLIMB2, + FALL1,FALL2}; +enum Status {STANDING,FALLING,WALKING,CLIMBING,CAPTIVE}; +enum Direction {RIGHT,LEFT,UP,DOWN,STAND}; +enum SearchStrategy {LOW,MEDIUM,HIGH}; + +// Keyboard action codes +enum KBAction {KB_UP, KB_DOWN, KB_LEFT, KB_RIGHT, + KB_DIGLEFT, KB_DIGRIGHT, KB_STOP}; + +// Action codes when selecting a level or game for play or editing. +enum SelectAction {SL_START, SL_ANY, SL_CREATE, SL_UPDATE, SL_SAVE, + SL_MOVE, SL_DELETE, SL_CR_GAME, SL_UPD_GAME}; + +#endif // KGRCONSTS_H diff --git a/kgoldrunner/src/kgrdialog.cpp b/kgoldrunner/src/kgrdialog.cpp new file mode 100644 index 00000000..61ca2f29 --- /dev/null +++ b/kgoldrunner/src/kgrdialog.cpp @@ -0,0 +1,974 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrcanvas.h" +#include "kgrgame.h" +#include "kgrdialog.h" + +#ifndef KGR_PORTABLE +#include <kglobalsettings.h> +#endif + +/******************************************************************************/ +/***************** DIALOG BOX TO SELECT A GAME AND LEVEL *****************/ +/******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList<KGrCollection> & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrSLDialog::KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList<KGrCollection> & gamesList, KGrGame * theGame, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Game"), + KDialogBase::Ok | KDialogBase::Cancel | KDialogBase::Help, + KDialogBase::Ok, parent, name) +#endif +{ + slAction = action; + defaultLevel = requestedLevel; + defaultGame = collnIndex; + collections = gamesList; + game = theGame; + collection = collections.at(defaultGame); + slParent = parent; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + collnL = new QLabel (i18n("List of games:"), dad); + mainLayout->addWidget (collnL); + colln = new QListBox (dad); + mainLayout->addWidget (colln); + + QHBox * gameInfo = new QHBox (dad); + mainLayout->addWidget (gameInfo); + gameInfo->setSpacing (spacing); + collnN = new QLabel ("", gameInfo); // Name of selected collection. + QFont f = collnN->font(); + f.setBold (TRUE); + collnN->setFont (f); + collnA = new QPushButton (i18n("More Info"), gameInfo); + + collnD = new QLabel ("", dad); // Description of collection. + mainLayout->addWidget (collnD); + + QFrame * separator = new QFrame (dad); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + if ((action == SL_START) || (action == SL_UPD_GAME)) { + dad-> setCaption (i18n("Select Game")); + QLabel * startMsg = new QLabel + ("<b>" + i18n("Level 1 of the selected game is:") + "</b>", dad); + mainLayout->addWidget (startMsg); + } + else { + dad-> setCaption (i18n("Select Game/Level")); + QLabel * selectLev = new QLabel (i18n("Select level:"), dad); + mainLayout->addWidget (selectLev); + } + + QGridLayout * grid = new QGridLayout (3, 2, -1); + mainLayout->addLayout (grid); + + // Initial range 1->150, small step 1, big step 10 and default value 1. + number = new QScrollBar (1, 150, 1, 10, 1, + QScrollBar::Horizontal, dad); + grid->addWidget (number, 1, 1); + + QHBox * numberPair = new QHBox (dad); + grid->addWidget (numberPair, 2, 1); + numberPair->setSpacing (spacing); + numberL = new QLabel (i18n("Level number:"), numberPair); + display = new QLineEdit (numberPair); + + levelNH = new QPushButton (i18n("Edit Level Name && Hint"), dad); + mainLayout->addWidget (levelNH); + + slName = new QLabel ("", dad); + grid->addWidget (slName, 3, 1); + thumbNail = new KGrThumbNail (dad); + grid->addMultiCellWidget (thumbNail, 1, 3, 2, 2); + + // Set thumbnail cell size to about 1/5 of game cell size. + int cellSize = parent->width() / (5 * (FIELDWIDTH + 4)); + thumbNail-> setFixedWidth ((FIELDWIDTH * cellSize) + 2); + thumbNail-> setFixedHeight ((FIELDHEIGHT * cellSize) + 2); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (this); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + HELP = new QPushButton (i18n("Help"), buttons); + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); + + OK-> setAccel (Key_Return); + HELP-> setAccel (Key_F1); + CANCEL-> setAccel (Key_Escape); +#endif + + // Set the default for the level-number in the scrollbar. + number-> setTracking (TRUE); + number->setValue (requestedLevel); + + slSetCollections (defaultGame); + + // Vary the dialog according to the action. + QString OKText = ""; + switch (slAction) { + case SL_START: // Must start at level 1, but can choose a collection. + OKText = i18n("Start Game"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + case SL_ANY: // Can start playing at any level in any collection. + OKText = i18n("Play Level"); + break; + case SL_UPDATE: // Can use any level in any collection as edit input. + OKText = i18n("Edit Level"); + break; + case SL_CREATE: // Can save a new level only in a USER collection. + OKText = i18n("Save New"); + break; + case SL_SAVE: // Can save an edited level only in a USER collection. + OKText = i18n("Save Change"); + break; + case SL_DELETE: // Can delete a level only in a USER collection. + OKText = i18n("Delete Level"); + break; + case SL_MOVE: // Can move a level only into a USER collection. + OKText = i18n("Move To..."); + break; + case SL_UPD_GAME: // Can only edit USER collection details. + OKText = i18n("Edit Game Info"); + number->setValue (1); + number->setEnabled(FALSE); + display->setEnabled(FALSE); + number->hide(); + numberL->hide(); + display->hide(); + break; + + default: break; // Keep the default settings. + } + if (!OKText.isEmpty()) { +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + } + + // Set value in the line-edit box. + slShowLevel (number->value()); + + if (display->isEnabled()) { + display->setFocus(); // Set the keyboard input on. + display->selectAll(); + display->setCursorPosition (0); + } + + // Paint a thumbnail sketch of the level. + thumbNail->setFrameStyle (QFrame::Box | QFrame::Plain); + thumbNail->setLineWidth (1); + slPaintLevel(); + thumbNail->show(); + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slColln (int))); + connect (collnA, SIGNAL (clicked ()), this, SLOT (slAboutColln ())); + + connect (display, SIGNAL (textChanged (const QString &)), + this, SLOT (slUpdate (const QString &))); + + connect (number, SIGNAL (valueChanged(int)), this, SLOT(slShowLevel(int))); + + // Only enable name and hint dialog here if saving a new or edited level. + // At other times the name and hint have not been loaded or initialised yet. + if ((slAction == SL_CREATE) || (slAction == SL_SAVE)) { + connect (levelNH, SIGNAL (clicked()), game, SLOT (editNameAndHint())); + } + else { + levelNH->setEnabled (FALSE); + levelNH->hide(); + } + + connect (colln, SIGNAL (highlighted (int)), this, SLOT (slPaintLevel ())); + connect (number, SIGNAL (sliderReleased()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevLine()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (nextPage()), this, SLOT (slPaintLevel())); + connect (number, SIGNAL (prevPage()), this, SLOT (slPaintLevel())); + +#ifdef KGR_PORTABLE + // Set the exits from this dialog box. + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); + connect (HELP, SIGNAL (clicked ()), this, SLOT (slotHelp ())); +#endif +} + +KGrSLDialog::~KGrSLDialog() +{ +} + +/******************************************************************************/ +/***************** LOAD THE LIST OF GAMES (COLLECTIONS) *****************/ +/******************************************************************************/ + +void KGrSLDialog::slSetCollections (int cIndex) +{ + int i; + int imax = collections.count(); + + // Set values in the combo box that holds collection names. + colln->clear(); + slCollnIndex = -1; + + for (i = 0; i < imax; i++) { + colln->insertItem (collections.at(i)->name, -1); + if (slCollnIndex < 0) { + slCollnIndex = i; // There is at least one collection. + } + } + + if (slCollnIndex < 0) { + return; // There are no collections (unlikely). + } + // Mark the currently selected collection (or default 0). + colln->setCurrentItem (cIndex); + colln->setSelected (cIndex, TRUE); + + // Fetch and display information on the selected collection. + slColln (cIndex); +} + +/******************************************************************************/ +/***************** SLOTS USED BY LEVEL SELECTION DIALOG *****************/ +/******************************************************************************/ + +void KGrSLDialog::slColln (int i) +{ + if (slCollnIndex < 0) { + // Ignore the "highlighted" signal caused by inserting in an empty box. + return; + } + + // User "highlighted" a new collection (with one click) ... + colln->setSelected (i, TRUE); // One click = selected. + slCollnIndex = i; + int n = slCollnIndex; // Collection selected. + int N = defaultGame; // Current collection. + if (collections.at(n)->nLevels > 0) + number->setMaxValue (collections.at(n)->nLevels); + else + number->setMaxValue (1); // Avoid range errors. + + // Set a default level number for the selected collection. + switch (slAction) { + case SL_ANY: + case SL_UPDATE: + case SL_DELETE: + case SL_UPD_GAME: + // If selecting the current collection, use the current level number. + if (n == N) + number->setValue (defaultLevel); + else + number->setValue (1); // Else use level 1. + break; + case SL_CREATE: + case SL_SAVE: + case SL_MOVE: + if ((n == N) && (slAction != SL_CREATE)) { + // Saving/moving level in current collection: use current number. + number->setValue (defaultLevel); + } + else { + // Saving new/edited level or relocating a level: use "nLevels + 1". + number->setMaxValue (collections.at(n)->nLevels + 1); + number->setValue (number->maxValue()); + } + break; + default: + number->setValue (1); // Default is level 1. + break; + } + + slShowLevel (number->value()); + +#ifndef KGR_PORTABLE + int levCnt = collections.at(n)->nLevels; + if (collections.at(n)->settings == 'K') + collnD->setText (i18n("1 level, uses KGoldrunner rules.", + "%n levels, uses KGoldrunner rules.", levCnt)); + else + collnD->setText (i18n("1 level, uses Traditional rules.", + "%n levels, uses Traditional rules.", levCnt)); +#else + QString levCnt; + levCnt = levCnt.setNum (collections.at(n)->nLevels); + if (collections.at(n)->settings == 'K') + collnD->setText (levCnt + i18n(" levels, uses KGoldrunner rules.")); + else + collnD->setText (levCnt + i18n(" levels, uses Traditional rules.")); +#endif + collnN->setText (collections.at(n)->name); +} + +void KGrSLDialog::slAboutColln () +{ + // User clicked the "About" button ... + int n = slCollnIndex; + QString title = i18n("About \"%1\"").arg(collections.at(n)->name); + + if (collections.at(n)->about.length() > 0) { + // Convert game description to ASCII and UTF-8 codes, then translate it. + KGrMessage::wrapped (slParent, title, + i18n((const char *) collections.at(n)->about.utf8())); + } + else { + KGrMessage::wrapped (slParent, title, + i18n("Sorry, there is no further information about this game.")); + } +} + +void KGrSLDialog::slShowLevel (int i) +{ + // Display the level number as the slider is moved. + QString tmp; + tmp.setNum(i); + tmp = tmp.rightJustify(3,'0'); + display->setText(tmp); +} + +void KGrSLDialog::slUpdate (const QString & text) +{ + // Move the slider when a valid level number is entered. + QString s = text; + bool ok = FALSE; + int n = s.toInt (&ok); + if (ok) { + number->setValue (n); + slPaintLevel(); + } + else + KGrMessage::information (this, i18n("Select Level"), + i18n("This level number is not valid. It can not be used.")); +} + +void KGrSLDialog::slPaintLevel () +{ + // Repaint the thumbnail sketch of the level whenever the level changes. + int n = slCollnIndex; + if (n < 0) { + return; // Owner has no collections. + } + QString filePath = game->getFilePath + (collections.at(n)->owner, collections.at(n), number->value()); + thumbNail->setFilePath (filePath, slName); + thumbNail->repaint(); // Will call "drawContents (p)". +} + +void KGrSLDialog::slotHelp () +{ + // Help for "Select Game and Level" dialog box. + QString s = + i18n("The main button at the bottom echoes the " + "menu action you selected. Click it after choosing " + "a game and level - or use \"Cancel\"."); + + if (slAction == SL_START) { + s += i18n("\n\nIf this is your first time in KGoldrunner, select the " + "tutorial game or click \"Cancel\" and click that item in " + "the Game or Help menu. The tutorial game gives you hints " + "as you go.\n\n" + "Otherwise, just click on the name of a game (in the list box), " + "then, to start at level 001, click on the main button at the " + "bottom. Play begins when you move the mouse or press a key."); + } + else { + switch (slAction) { + case SL_UPDATE: + s += i18n("\n\nYou can select System levels for editing (or " + "copying), but you must save the result in a game you have " + "created. Use the mouse as a paintbrush and the editor " + "toolbar buttons as a palette. Use the 'Empty Space' button " + "to erase."); + break; + case SL_CREATE: + s += i18n("\n\nYou can add a name and hint to your new level here, " + "but you must save the level you have created into one of " + "your own games. By default your new level will go at the " + "end of your game, but you can also select a level number and " + "save into the middle of your game."); + break; + case SL_SAVE: + s += i18n("\n\nYou can create or edit a name and hint here, before " + "saving. If you change the game or level, you can do a copy " + "or \"Save As\", but you must always save into one of your " + "own games. If you save a level into the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_DELETE: + s += i18n("\n\nYou can only delete levels from one of your own " + "games. If you delete a level from the middle of a series, " + "the other levels are automatically re-numbered."); + break; + case SL_MOVE: + s += i18n("\n\nTo move (re-number) a level, you must first select " + "it by using \"Edit Any Level...\", then you can use " + "\"Move Level...\" to assign it a new number or even a different " + "game. Other levels are automatically re-numbered as " + "required. You can only move levels within your own games."); + break; + case SL_UPD_GAME: + s += i18n("\n\nWhen editing game info you only need to choose a " + "game, then you can go to a dialog where you edit the " + "details of the game."); + break; + default: + break; + } + s += i18n("\n\nClick on the list box to choose a game. " + "Below the list box you can see \"More Info\" about the " + "selected game, how many levels there are and what " + "rules the enemies follow (see the Settings menu).\n\n" + "You select " + "a level number by typing it or using the scroll bar. As " + "you vary the game or level, the thumbnail area shows a " + "preview of your choice."); + } + + KGrMessage::wrapped (slParent, i18n("Help: Select Game & Level"), s); +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrNHDialog::KGrNHDialog(const QString & levelName, const QString & levelHint, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Name & Hint"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * nameL = new QLabel (i18n("Name of level:"), dad); + mainLayout->addWidget (nameL); + nhName = new QLineEdit (dad); + mainLayout->addWidget (nhName); + + QLabel * mleL = new QLabel (i18n("Hint for level:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + dad-> setCaption (i18n("Edit Name & Hint")); +#endif + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + + // Configure the text box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (9); +#endif + + nhName-> setText (levelName); + mle-> setText (levelHint); + +#ifdef KGR_PORTABLE + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + CANCEL-> setAccel (Key_Escape); + + connect (OK, SIGNAL (clicked ()), dad, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), dad, SLOT (reject ())); +#endif +} + +KGrNHDialog::~KGrNHDialog() +{ +} + +/******************************************************************************* +*************** DIALOG BOX TO CREATE OR EDIT A GAME (COLLECTION) *************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList<KGrCollection> & gamesList, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrECDialog::KGrECDialog (int action, int collnIndex, + QPtrList<KGrCollection> & gamesList, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Edit Game Info"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ + collections = gamesList; + defaultGame = collnIndex; + +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QHBox * nameBox = new QHBox (dad); + mainLayout->addWidget (nameBox); + nameBox->setSpacing (spacing); + nameL = new QLabel (i18n("Name of game:"), nameBox); + ecName = new QLineEdit (nameBox); + + QHBox * prefixBox = new QHBox (dad); + mainLayout->addWidget (prefixBox); + prefixBox->setSpacing (spacing); + prefixL = new QLabel (i18n("File name prefix:"), prefixBox); + ecPrefix = new QLineEdit (prefixBox); + + ecGrp = new QButtonGroup (1, QButtonGroup::Horizontal, 0, dad); + mainLayout->addWidget (ecGrp); + ecTradB = new QRadioButton (i18n("Traditional rules"), ecGrp); + ecKGrB = new QRadioButton (i18n("KGoldrunner rules"), ecGrp); + + nLevL = new QLabel (i18n( "0 levels" ), dad); + mainLayout->addWidget (nLevL); + + mleL = new QLabel (i18n("About this game:"), dad); + mainLayout->addWidget (mleL); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + mle = new QTextEdit (dad); + mle-> setTextFormat (PlainText); +#else + mle = new QMultiLineEdit (dad); +#endif + mainLayout->addWidget (mle); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + mainLayout->addWidget (buttons); + buttons->setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + OK = new QPushButton (i18n("&OK"), buttons); + CANCEL = new QPushButton (i18n("&Cancel"), buttons); + + QPoint p = parent->mapToGlobal (QPoint (0,0)); + + // Base the geometry of the dialog box on the playing area. + int cell = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*cell, p.y()+2*cell); + dad-> setMinimumSize ((FIELDWIDTH*cell/2), (FIELDHEIGHT-1)*cell); +#endif + + if (action == SL_CR_GAME) { + setCaption (i18n("Create Game")); + } + else { + setCaption (i18n("Edit Game Info")); + } + + QString OKText = ""; + if (action == SL_UPD_GAME) { // Edit existing collection. + ecName-> setText (collections.at(defaultGame)->name); + ecPrefix-> setText (collections.at(defaultGame)->prefix); + if (collections.at(defaultGame)->nLevels > 0) { + // Collection already has some levels, so cannot change the prefix. + ecPrefix-> setEnabled (FALSE); + } + QString s; +#ifndef KGR_PORTABLE + nLevL-> setText (i18n("1 level", "%n levels", + collections.at(defaultGame)->nLevels)); +#else + nLevL-> setText (i18n("%1 levels") + .arg(collections.at(defaultGame)->nLevels)); +#endif + OKText = i18n("Save Changes"); + } + else { // Create a collection. + ecName-> setText (""); + ecPrefix-> setText (""); + nLevL-> setText (i18n("0 levels")); + OKText = i18n("Save New"); + } +#ifdef KGR_PORTABLE + OK->setText (OKText); +#else + setButtonOK (OKText); +#endif + + if ((action == SL_CR_GAME) || + (collections.at(defaultGame)->settings == 'T')) { + ecSetRules ('T'); // Traditional settings. + } + else { + ecSetRules ('K'); // KGoldrunner settings. + } + + // Configure the edit box. + mle-> setAlignment (AlignLeft); +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (8); +#endif + + if ((action == SL_UPD_GAME) && + (collections.at(defaultGame)->about.length() > 0)) { + // Display and edit the game description in its original language. + mle-> setText (collections.at(defaultGame)->about); + } + else { + mle-> setText (""); + } + + connect (ecKGrB, SIGNAL (clicked ()), this, SLOT (ecSetKGr ())); + connect (ecTradB, SIGNAL (clicked ()), this, SLOT (ecSetTrad ())); + +#ifdef KGR_PORTABLE + OK-> setGeometry (10, 145 + mle->height(), 100, 25); + // OK-> setAccel (Key_Return); // No! We need it in "mle" box. + + CANCEL-> setGeometry (190, 145 + mle->height(), 100, 25); + CANCEL-> setAccel (Key_Escape); + + dad-> resize (300, 175 + mle->height()); + + connect (OK, SIGNAL (clicked ()), this, SLOT (accept())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject())); +#endif +} + +KGrECDialog::~KGrECDialog() +{ +} + +void KGrECDialog::ecSetRules (const char settings) +{ + ecKGrB-> setChecked (FALSE); + ecTradB-> setChecked (FALSE); + if (settings == 'K') + ecKGrB-> setChecked (TRUE); + else + ecTradB-> setChecked (TRUE); +} + +void KGrECDialog::ecSetKGr () {ecSetRules ('K');} // Radio button slots. +void KGrECDialog::ecSetTrad () {ecSetRules ('T');} + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#ifdef KGR_PORTABLE +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList<KGrCollection> & collections, + QWidget * parent, const char * name) + : QDialog (parent, name, TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title) +#else +KGrLGDialog::KGrLGDialog (QFile * savedGames, + QPtrList<KGrCollection> & collections, + QWidget * parent, const char * name) + : KDialogBase (KDialogBase::Plain, i18n("Select Saved Game"), + KDialogBase::Ok | KDialogBase::Cancel, + KDialogBase::Ok, parent, name) +#endif +{ +#ifdef KGR_PORTABLE + int margin = 10; + int spacing = 10; + QWidget * dad = this; +#else + int margin = marginHint(); + int spacing = spacingHint(); + QWidget * dad = plainPage(); +#endif + + QVBoxLayout * mainLayout = new QVBoxLayout (dad, margin, spacing); + + QLabel * lgHeader = new QLabel ( + i18n("Game Level/Lives/Score " + "Day Date Time "), dad); + + lgList = new QListBox (dad); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f.setFixedPitch (TRUE); + lgList-> setFont (f); + f.setBold (TRUE); + lgHeader-> setFont (f); + + mainLayout-> addWidget (lgHeader); + mainLayout-> addWidget (lgList); + +#ifdef KGR_PORTABLE + QHBox * buttons = new QHBox (dad); + buttons-> setSpacing (spacing); + // Buttons are for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), buttons); + QPushButton * CANCEL = new QPushButton (i18n("&Cancel"), buttons); + mainLayout-> addWidget (buttons); + + dad-> setCaption (i18n("Select Saved Game")); + + // Base the geometry of the list box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + dad-> move (p.x()+2*c, p.y()+2*c); + lgList-> setMinimumHeight ((FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (4*c); + CANCEL-> setMaximumWidth (4*c); + + OK-> setAccel (Key_Return); + CANCEL-> setAccel (Key_Escape); +#endif + + lgHighlight = -1; + + QTextStream gameText (savedGames); + QString s = ""; + QString pr = ""; + int i; + int imax = collections.count(); + + // Read the saved games into the list box. + while (! gameText.endData()) { + s = gameText.readLine(); // Read in one saved game. + pr = s.left (s.find (" ", 0, FALSE)); // Get the collection prefix. + for (i = 0; i < imax; i++) { // Get the collection name. + if (collections.at(i)->prefix == pr) { + s = s.insert (0, + collections.at(i)->name.leftJustify (20, ' ', TRUE) + " "); + break; + } + } + lgList-> insertItem (s); + } + savedGames->close(); + + // Mark row 0 (the most recently saved game) as the default selection. + lgList-> setCurrentItem (0); + lgList-> setSelected (0, TRUE); + lgHighlight = 0; + + connect (lgList, SIGNAL (highlighted (int)), this, SLOT (lgSelect (int))); +#ifdef KGR_PORTABLE + connect (OK, SIGNAL (clicked ()), this, SLOT (accept ())); + connect (CANCEL, SIGNAL (clicked ()), this, SLOT (reject ())); +#endif +} + +void KGrLGDialog::lgSelect (int n) +{ + lgHighlight = n; +} + +/******************************************************************************* +*********************** CENTRALISED MESSAGE FUNCTIONS ************************ +*******************************************************************************/ + +void KGrMessage::information (QWidget * parent, const QString &caption, const QString &text) +{ +#ifdef KGR_PORTABLE + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + QMessageBox::information (parent, caption, + "<qt>" + text + "</qt>"); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + KMessageBox::information (parent, text, caption); +#endif +} + +int KGrMessage::warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2) +{ + int ans = 0; +#ifdef KGR_PORTABLE + // Display a box with 2 or 3 buttons, depending on if label2 is empty or not. + // Force Qt to do word-wrapping (but it ignores "\n" line-breaks). + ans = QMessageBox::warning (parent, caption, + "<qt>" + text + "</qt>", + label0, label1, label2, + 0, (label2.isEmpty()) ? 1 : 2); +#else + // KDE does word-wrapping and will observe "\n" line-breaks. + if (label2.isEmpty()) { + // Display a box with 2 buttons. + ans = KMessageBox::questionYesNo (parent, text, caption, + label0, label1); + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } + else { + // Display a box with 3 buttons. + ans = KMessageBox::questionYesNoCancel (parent, text, caption, + label0, label1); + if (ans == KMessageBox::Cancel) + ans = 2; + else + ans = (ans == KMessageBox::Yes) ? 0 : 1; + } +#endif + return (ans); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrMessage::wrapped (QWidget * parent, QString title, QString contents) +{ +#ifndef KGR_PORTABLE + KMessageBox::information (parent, contents, title); +#else + QDialog * mm = new QDialog (parent, "wrappedMessage", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (mm, margin, spacing); + + // Make text background grey not white (i.e. same as widget background). + QPalette pl = mm->palette(); +#ifdef QT3 + pl.setColor (QColorGroup::Base, mm->paletteBackgroundColor()); +#else + pl.setColor (QColorGroup::Base, mm->backgroundColor()); +#endif + mm-> setPalette (pl); + + // Set up a widget to hold the wrapped text, using \n for paragraph breaks. +#ifdef QT3 + QTextEdit * mle = new QTextEdit (mm); + mle-> setTextFormat (PlainText); +#else + QMultiLineEdit * mle = new QMultiLineEdit (mm); +#endif + mainLayout->addWidget (mle); + + // Button is for Qt-only portability. NOT COMPILED in KDE environment. + QPushButton * OK = new QPushButton (i18n("&OK"), mm); + mainLayout->addWidget (OK, Qt::AlignHCenter); + + mm-> setCaption (title); + + // Base the geometry of the text box on the playing area. + QPoint p = parent->mapToGlobal (QPoint (0,0)); + int c = parent->width() / (FIELDWIDTH + 4); + mm-> move (p.x()+4*c, p.y()+4*c); + mle-> setMinimumSize ((FIELDWIDTH*c/2), (FIELDHEIGHT/2)*c); + OK-> setMaximumWidth (3*c); + + mle-> setFrameStyle (QFrame::NoFrame); + mle-> setAlignment (AlignLeft); + mle-> setReadOnly (TRUE); + mle-> setText (contents); + +#ifndef QT3 + mle-> setWordWrap (QMultiLineEdit::WidgetWidth); + mle-> setFixedVisibleLines (10); + if (mle-> numLines() < 10) { + mle-> setFixedVisibleLines (mle->numLines()); + } +#endif + + OK-> setAccel (Key_Return); + connect (OK, SIGNAL (clicked ()), mm, SLOT (accept ())); + + mm-> exec (); + + delete mm; +#endif // KGR_PORTABLE +} + +#include "kgrdialog.moc" diff --git a/kgoldrunner/src/kgrdialog.h b/kgoldrunner/src/kgrdialog.h new file mode 100644 index 00000000..2e2e847c --- /dev/null +++ b/kgoldrunner/src/kgrdialog.h @@ -0,0 +1,218 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRDIALOG_QT_H +#define KGRDIALOG_QT_H + +// If portable version, use QDialog and QMessageBox. +// If KDE version, use KDialogBase and KMessageBox. + +#ifdef KGR_PORTABLE +#include <qdialog.h> +#define KGR_DIALOG QDialog +#include <qmessagebox.h> + +#else +#include <klocale.h> +#include <kdialogbase.h> +#define KGR_DIALOG KDialogBase +#include <kmessagebox.h> +#endif + +#include <qlayout.h> + +#include <qlistbox.h> +#include <qscrollbar.h> +#include <qlineedit.h> +#include <qhbox.h> +#include <qpushbutton.h> +#include <qbuttongroup.h> +#include <qradiobutton.h> +#ifdef QT3 +#include <qtextedit.h> +#else +#include <qmultilineedit.h> +#endif + +#include <qptrlist.h> + +/** +@author Ian Wadham and Marco Krger +*/ + +class KGrCanvas; +class KGrGame; +class KGrCollection; +class KGrThumbNail; + +/******************************************************************************/ +/******************* DIALOG TO SELECT A GAME AND LEVEL *******************/ +/******************************************************************************/ + +class KGrSLDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrSLDialog (int action, int requestedLevel, int collnIndex, + QPtrList<KGrCollection> & gamesList, KGrGame * theGame, + QWidget * parent = 0, const char *name = 0); + ~KGrSLDialog(); + + int selectedLevel() {return (number->value());} + int selectedGame() {return (slCollnIndex);} + +private slots: + void slSetCollections (int cIndex); + void slColln (int i); + void slAboutColln (); + void slShowLevel (int i); + void slUpdate (const QString & text); + void slPaintLevel (); + void slotHelp (); // Will replace KDE slotHelp(). + +private: + int slAction; + QPtrList<KGrCollection> collections; // List of games. + int defaultLevel; + int defaultGame; + int slCollnIndex; + KGrGame * game; + KGrCollection * collection; + QWidget * slParent; + + QLabel * collnL; + QListBox * colln; + QLabel * collnN; + QLabel * collnD; + QPushButton * collnA; + + QLabel * numberL; + QLineEdit * display; + QScrollBar * number; + QPushButton * levelNH; + QLabel * slName; + KGrThumbNail * thumbNail; + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * HELP; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG BOX TO CREATE/EDIT A LEVEL NAME AND HINT **************** +*******************************************************************************/ + +class KGrNHDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrNHDialog (const QString & levelName, const QString & levelHint, + QWidget * parent = 0, const char * name = 0); + ~KGrNHDialog(); + + QString getName() {return (nhName->text());} + QString getHint() {return (mle->text());} + +private: + QLineEdit * nhName; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif +}; + +/******************************************************************************* +***************** DIALOG TO CREATE OR EDIT A GAME (COLLECTION) ***************** +*******************************************************************************/ + +class KGrECDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrECDialog (int action, int collnIndex, + QPtrList<KGrCollection> & gamesList, + QWidget *parent = 0, const char *name = 0); + ~KGrECDialog(); + + QString getName() {return (ecName->text());} + QString getPrefix() {return (ecPrefix->text());} + bool isTrad() {return (ecTradB->isChecked());} + QString getAboutText() {return (mle->text());} + +private slots: + void ecSetRules (const char settings); + void ecSetKGr(); // Radio button slots. + void ecSetTrad(); + +private: + QPtrList<KGrCollection> collections; // List of existing games. + int defaultGame; + + QLabel * nameL; + QLineEdit * ecName; + QLabel * prefixL; + QLineEdit * ecPrefix; + QButtonGroup * ecGrp; + QRadioButton * ecKGrB; + QRadioButton * ecTradB; + QLabel * nLevL; + + QLabel * mleL; +#ifdef QT3 + QTextEdit * mle; +#else + QMultiLineEdit * mle; +#endif + +#ifdef KGR_PORTABLE + QPushButton * OK; + QPushButton * CANCEL; +#endif +}; + +/******************************************************************************* +*************** DIALOG TO SELECT A SAVED GAME TO BE RE-LOADED **************** +*******************************************************************************/ + +#include <qfile.h> +#include <qtextstream.h> + +class KGrLGDialog : public KGR_DIALOG // KGR_PORTABLE sets QDialog/KDialogBase +{ +Q_OBJECT +public: + KGrLGDialog (QFile * savedGames, QPtrList<KGrCollection> & collections, + QWidget * parent, const char * name); + QString getCurrentText() {return (lgList->currentText());} + +private slots: + void lgSelect (int n); + +private: + QListBox * lgList; + int lgHighlight; +}; + +/******************************************************************************* +****************** PORTABLE MESSAGE FUNCTIONS (Qt Version) ******************* +*******************************************************************************/ + +class KGrMessage : public QDialog +{ +public: + static void information (QWidget * parent, const QString &caption, const QString &text); + static int warning (QWidget * parent, QString caption, QString text, + QString label0, QString label1, QString label2 = ""); + static void wrapped (QWidget * parent, QString caption, QString text); +}; + +#endif diff --git a/kgoldrunner/src/kgrfigure.cpp b/kgoldrunner/src/kgrfigure.cpp new file mode 100644 index 00000000..08fc91d4 --- /dev/null +++ b/kgoldrunner/src/kgrfigure.cpp @@ -0,0 +1,1803 @@ +/*************************************************************************** + * kgrfigure.cpp - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * email : See menu "Help, About KGoldrunner" * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrcanvas.h" + +#include "kgrfigure.h" + +#include <stdio.h> + +KGrFigure :: KGrFigure (int px, int py) +{ + x = mem_x = px; + y = mem_y = py; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = px*16; + absy = py*16; + + nuggets = 0; + status = STANDING; + + walkTimer = new QTimer (this); + fallTimer = new QTimer (this); +} + +// Initialise the global settings flags. +bool KGrFigure::variableTiming = TRUE; +bool KGrFigure::alwaysCollectNugget = TRUE; +bool KGrFigure::runThruHole = TRUE; +bool KGrFigure::reappearAtTop = TRUE; +SearchStrategy KGrFigure::searchStrategy = LOW; + +int KGrFigure::herox = 0; +int KGrFigure::heroy = 0; + +// Initialise the global game-speed factors. +int KGrFigure::speed = NSPEED; +int KGrBrick::speed = NSPEED; + +// Initialise constants for fixed (KGoldrunner) and variable (Traditional) +// timing. Each row contains timings for hero walk and fall, enemy walk and +// fall, enemy captured in hole and dug brick. + +Timing KGrFigure::fixedTiming = {45, 50, 55, 100, 500, 40}; // KGr original. + +Timing KGrFigure::varTiming[6] = { // Traditional. + {40, 58, 78, 88, 170, 23}, // No enemies. + {50, 68, 78, 88, 170, 32}, // 1 enemy. + {57, 67, 114, 128, 270, 37}, // 2 enemies. + {60, 70, 134, 136, 330, 40}, // 3 enemies. + {63, 76, 165, 150, 400, 46}, // 4 enemies. + {70, 80, 189, 165, 460, 51} // >4 enemies. +}; + +int KGrBrick::HOLETIME = 0; + +int KGrFigure::getx() +{ + return absx; +} + +int KGrFigure::gety() +{ + return absy; +} + +Status KGrFigure::getStatus() +{ + return status; +} + +void KGrFigure::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + nuggets = 0; + status = STANDING; +} + +void KGrFigure:: setNuggets(int n) +{ + nuggets = n; +} + + +bool KGrFigure::canWalkRight() +{ + return (((*playfield)[x+1][y]->whatIam() != BRICK) && + ((*playfield)[x+1][y]->whatIam() != BETON) && + ((*playfield)[x+1][y]->whatIam() != FBRICK)); +} + +bool KGrFigure::canWalkLeft() +{ + return (((*playfield)[x-1][y]->whatIam() != BRICK) && + ((*playfield)[x-1][y]->whatIam() != BETON) && + ((*playfield)[x-1][y]->whatIam() != FBRICK)); + } + +bool KGrFigure::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + ((*playfield)[x][y]->whatIam() == LADDER)); +} + +bool KGrFigure::canWalkDown() +{ + return (((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON) && + // v0.3 FIX - Let figure step down into FBRICK from a ladder. + // ((*playfield)[x][y+1]->whatIam() != FBRICK)&& + (((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +bool KGrFigure::canStand() +{ + return (((*playfield)[x][y+1]->whatIam() == BRICK) || + ((*playfield)[x][y+1]->whatIam() == BETON) || + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)|| + ((*playfield)[x][y+1]->whatIam() == LADDER)|| + ((*playfield)[x][y]->whatIam() == LADDER)|| + standOnEnemy()); + } + +bool KGrFigure::hangAtPole() +{ + return ((*playfield)[x][y]->whatIam() == POLE); +} + +void KGrFigure::walkUp(int WALKDELAY) +{ + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step up, if possible. + if (canWalkUp()) { + rely -= STEP; + absy -= STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move up to next cell, if possible. + if (canWalkUp()) { + y--; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Wait for caller to set next direction. + status = STANDING; + } +} + +void KGrFigure::walkDown(int WALKDELAY, int FALLDELAY) +{ + if (hangAtPole() || (! canStand())) { + // On bar or no firm ground underneath: so must fall. + initFall (FALL2, FALLDELAY); + } + else { + actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1; + if (walkCounter++ < 4) { + // Not end of 4-step cycle: move one step down, if possible. + if (canWalkDown()) { + rely += STEP; + absy += STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-step cycle: move down to next cell, if possible. + if (canWalkDown()) { + y++; + } + // Always reset position, in case we are stuck partly into a brick. + rely = 0; + absy = y*16; + + // Must be able to halt at a pole when going down. + if (! (canStand() || hangAtPole())) + initFall(FALL2, FALLDELAY); // kein Halt....urgs + else + // Wait for caller to set next direction. + status = STANDING; + } + } +} + +void KGrFigure::walkLeft (int WALKDELAY, int FALLDELAY) +{ + // If counter != 0, the figure is walking, otherwise he is turning around. + if (walkCounter++ != 0) { + // Change to the next pixmap in the animation. + if ((++actualPixmap%4) != 0) { + // Not end of 4-pixmap cycle: move one step left, if possible. + if (canWalkLeft()) { + relx -= STEP; + absx -=STEP; + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // End of 4-pixmap cycle: start again, in next cell if possible. + actualPixmap -= 4; + if (canWalkLeft()) { + x--; + } + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + // If cannot stand or hang, start fall, else await next assignment. + if (! (canStand() || hangAtPole())) + initFall (FALL1, FALLDELAY); + else + status = STANDING; // Caller should set next direction. + } + } + else { + status = STANDING; // The figure is turning around. + } +} + +void KGrFigure::walkRight(int WALKDELAY, int FALLDELAY) +{ + if (walkCounter++) { // wenn 0, soll sich Figur nur umdrehen + if (++actualPixmap % 4) { // wenn true, dann ist kein vollstndiger Schritt gemacht + if (canWalkRight()) { + relx += STEP; + absx += STEP; // nur vorwrts gehen, wenn es auch mglich ist + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + actualPixmap -= 4; // Schritt war vollendet + if (canWalkRight()) { + x++; + } //gehe entgltig nach rechts + // Always reset position, in case we are stuck partly into a brick. + relx = 0; + absx = x*16; + + if (!(canStand()||hangAtPole())) // kein Halt mehr...arrrgghhh + initFall (FALL2, FALLDELAY); + else + status = STANDING; // Figur hat Halt + } + } + else { + status = STANDING; // Figur sollte sich nur Umdrehen. + } +} + +void KGrFigure::initFall(int apm, int FALLDELAY) +{ + status = FALLING; + actualPixmap = apm; + walkCounter=1; + walkTimer->stop(); + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); +} + +void KGrFigure::showFigure () +{ +} + +void KGrFigure::setPlayfield (KGrObject * (*p)[30][22]) +{ + playfield = p; +} + +KGrFigure :: ~KGrFigure () +{ +} + +KGrHero :: KGrHero (KGrCanvas * view, int x, int y) + :KGrFigure (x, y) +{ + heroView = view; + status = STANDING; + actualPixmap = FALL1; + + herox = x; + heroy = y; + + started = FALSE; + mouseMode = TRUE; + walkCounter = 1; + + walkFrozen = FALSE; + fallFrozen = FALSE; + + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrHero::WALKDELAY = 0; +int KGrHero::FALLDELAY = 0; + +/* Es ist Notwendig der eigentlichen Timerfunktion diese + Startwalk vorzuschalten, um bei einem evtl. notwendigem + Richtungswechsel der Figur diese Bewegung mit einzufgen */ +void KGrHero::startWalk () +{ + switch (nextDir) { + case UP: + if ((*playfield)[x][y]->whatIam () == LADDER) + {walkCounter = 1; + direction = UP;} + break; + case RIGHT: + if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + if (direction != RIGHT) + walkCounter = 0; + else + walkCounter = 1; + direction = RIGHT; + break; + case DOWN: + if (((*playfield)[x][y]->whatIam () == LADDER)|| + ((*playfield)[x][y+1]->whatIam () == LADDER)) + {walkCounter = 1; + direction = DOWN;} + else // wenn figur an Stange haengt und nichts unter ihr, + if (hangAtPole() && (!canStand())) // dann soll sie fallen + { status = STANDING; + actualPixmap = (direction==RIGHT)?19:18; + walkCounter=1; + direction=STAND; + walkTimer->stop(); + } + break; + case LEFT: + if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + if (direction != LEFT) + walkCounter = 0; + else + walkCounter = 1; + direction = LEFT; + break; + default : + direction = STAND; + status = FALLING; + break; + } + nextDir = STAND; + if (status != FALLING)//immer ausfhren, ausser beim fallen + { status = WALKING; // da sonst der FALLINGstatus wieder + showFigure (); // geaendert wird und der falsche Timer anspringt. + } +} // END KGrHero::startWalk + +void KGrHero::setKey(Direction key) +{ + // Keyboard control of hero: direction is fixed until next key is pressed. + // Sets a simulated mouse-pointer above, below, left, right or on the hero. + mouseMode = FALSE; + stopped = FALSE; + switch (key) { + case UP: mousex = x; mousey = 0; break; + case DOWN: mousex = x; mousey = FIELDHEIGHT + 1; break; + case LEFT: mousex = 0; mousey = y; break; + case RIGHT: mousex = FIELDWIDTH + 1; mousey = y; break; + case STAND: stopped = TRUE; mousex = x; mousey = y; break; + } +} + +void KGrHero::setDirection(int i, int j) +{ + // Mouse control of hero: direction is updated continually on a timer. + mouseMode = TRUE; + stopped = FALSE; + mousex = i; + mousey = j; +} + +void KGrHero::setNextDir() +{ + int dx, dy; + + if (! mouseMode) { + // Keyboard control of hero: adjust simulated mouse-pointer. + if (stopped) { + mousex = x; + mousey = y; + } + if ((mousey < 1) || (mousey > FIELDHEIGHT)) { + mousex = x; // Stay directly above/below the hero. + } + else if ((mousex < 1) || (mousex > FIELDWIDTH)) { + mousey = y; // Stay directly left/right of the hero. + } + } + + dx = mousex - x; dy = mousey - y; + + if ((dy == 0) && (y == 1) && (nuggets <= 0)) { + nextDir = UP; + } + else if ((dy > 0) && + (canWalkDown() || + standOnEnemy() || + (hangAtPole() && ((*playfield)[x][y+1]->whatIam() != BRICK) && + ((*playfield)[x][y+1]->whatIam() != BETON)))) { + nextDir = DOWN; + } + else if ((dy < 0) && canWalkUp ()) { + nextDir = UP; + } + else if (dx > 0) { + nextDir = RIGHT; + } + else if (dx < 0) { + nextDir = LEFT; + } + else if (dx == 0) { + nextDir = STAND; + } +} + +void KGrHero::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } +} + +void KGrHero::showState(char option) +{ + printf("(%02d,%02d) - Hero ", x, y); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d next dirn %d\n", direction, nextDir); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + printf ("\n"); + break; + } +} + +void KGrHero::init(int a,int b) +{ + walkTimer->stop(); + fallTimer->stop(); + walkCounter = 1; + started = FALSE; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx = 16*x; + absy = 16*y; + + nuggets = 0; + + if (herox < 1) { // If first call to init, ... + heroView->makeHeroSprite (x, y, actualPixmap); + } + herox = x; + heroy = y; + + actualPixmap = FALL2; + heroView->moveHero (absx, absy, actualPixmap); +} + +void KGrHero::start() +{ + started = TRUE; + walkFrozen = FALSE; + fallFrozen = FALSE; + + if (!(canStand()||hangAtPole())) { // Held muss wohl fallen... + status = FALLING; + fallTimeDone(); + } + else { + status = STANDING; + walkTimeDone(); + } +} + +void KGrHero::setSpeed (int gamespeed) +{ + if (gamespeed >= 0) { + if (gamespeed < MINSPEED) + speed++; // Increase speed. + else + speed = gamespeed; // Set selected speed. + if (speed > MAXSPEED) + speed = MAXSPEED; // Set upper limit. + } + else { + speed--; // Reduce speed. + if (speed < MINSPEED) + speed = MINSPEED; // Set lower limit. + } + + KGrBrick::speed = speed; // Make a copy for bricks. +} + +void KGrHero::walkTimeDone () +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + if ((*playfield)[x][y]->whatIam() == BRICK) { + emit caughtHero(); // Brick closed over hero. + return; + } + + if ((y==1)&&(nuggets<=0)) { // If on top row and all nuggets collected, + emit leaveLevel(); // the hero has won and can go to next level. + return; + } + + if (status == STANDING) + setNextDir(); + if ((status == STANDING) && (nextDir != STAND)) { + if ((standOnEnemy()) && (nextDir == DOWN)) { + emit caughtHero(); // Hero is going to step down into an enemy. + return; + } + startWalk(); + } + if (status != STANDING) { + switch (direction) { + case UP: walkUp (WALKDELAY); break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default : + // The following code is strange. It makes the hero fall off a pole. + // It works because of other strange code in "startWalk(), case DOWN:". + if (!canStand()||hangAtPole()) // falling + initFall(FALL1, FALLDELAY); + else status = STANDING; + break; + } + herox=x;heroy=y; // Koordinatenvariablen neu + // wenn Held genau ein Feld weitergelaufen ist, + if ((relx==0)&&(rely==0)) // dann setzte statische + { + collectNugget(); // und nehme evtl. Nugget + } + showFigure(); // Is this REDUNDANT now? See showFigure() below. + ////////////////////////////////////////////////// + } + if (status == STANDING) + if (!canStand()&&!hangAtPole()) + initFall(FALL1, FALLDELAY); + else + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + + // This additional showFigure() is to update the hero position after it is + // altered by the hero-enemy deadlock fix in standOnEnemy(). Messy, but ... + //////////////////////////////////////////////////////////////////////////// + showFigure(); + if(isInEnemy()) { + walkTimer->stop(); + emit caughtHero(); + } +} + +void KGrHero::fallTimeDone() +{ + if (! started) return; // Ignore signals from earlier play. + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if (!standOnEnemy()) { + if (walkCounter++ < 4) { // Held fllt vier Positionen + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + rely+=STEP; + absy+=STEP; + } + else { //Held ist ein Feld weitergefallen + // Verschiebung der Figur zum 0-Punkt des Objekts (Brick etc...) + heroy = ++y; + rely = 0; + absy = y*16; // wird Null und Figur eins runter + collectNugget(); // gesetzt. Zeit evtl. Nugget zu nehmen + if (! (canStand()||hangAtPole())) { // Held muss wohl weiterfallen. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter = 1; + } + else { // Held hat Boden (oder Feind) unter den + status = STANDING; // Fssen oder hngt an Stange -> steh! + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + } + } + showFigure(); + } + else { + if (rely == 0) { + // If at the bottom of a cell, try to walk or just stand still. + status = STANDING; + direction = (actualPixmap == 19) ? RIGHT : LEFT; + if ((*playfield)[x][y]->whatIam() == POLE) + actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1; + // else + // Reduce jerkiness when descending over a falling enemy. + // actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1; + walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + // Else, freeze hero until enemy moves out of the way. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + } + } + if (isInEnemy() && (! standOnEnemy())) + emit caughtHero(); +} + + +void KGrHero::showFigure () { + + heroView->moveHero (absx, absy, actualPixmap); + + // Merke alte Werte zum lschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +void KGrHero::dig(){ + if (direction == LEFT) + digLeft(); + else + if (direction == RIGHT) + digRight(); +} + +void KGrHero::digLeft(){ + int i = 1; // If stationary or moving up/down, dig at x-1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 2; // If walking left, dig at x-2 and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 0; // If walking right, dig at x and stop at x+1. + } + if (((*playfield)[x-i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x-i][y]->whatIam() == HLADDER)|| + ((*playfield)[x-i][y]->whatIam() == FREE)|| + ((*playfield)[x-i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x-i][y+1])->dig(); +} + +void KGrHero::digRight(){ + int i = 1; // If stationary or moving up/down, dig at x+1. + if (status == STANDING) + setNextDir(); + if ((status == WALKING) || + ((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) { + if ((direction == LEFT) && canWalkLeft()) + i = 0; // If walking left, dig at x and stop at x-1. + else if ((direction == RIGHT) && canWalkRight()) + i = 2; // If walking right, dig at x+2 and stop at x+1. + } + if (((*playfield)[x+i][y+1]->whatIam() == BRICK)&& + (((*playfield)[x+i][y]->whatIam() == HLADDER)|| + ((*playfield)[x+i][y]->whatIam() == FREE)|| + ((*playfield)[x+i][y]->whatIam() == HOLE))) + ((KGrBrick*)(*playfield)[x+i][y+1])->dig(); +} + +#ifdef QT3 +void KGrHero::setEnemyList(QPtrList<KGrEnemy> *e) +#else +void KGrHero::setEnemyList(QList<KGrEnemy> *e) +#endif +{ + enemies = e; +} + +bool KGrHero::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 0) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if hero's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + if (((absy + 12) == enemy->gety()) && + (enemy->getStatus() != FALLING)) { + absy = absy - rely; // Bounce back from overlap, to avoid + rely = 0; // hero-enemy mid-cycle deadlock. + walkCounter = 1; + } + return true; + } + } + } + return false; +} + +void KGrHero::collectNugget(){ + + if ((*playfield)[x][y]->whatIam() == NUGGET) + { + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + if (!(--nuggets)) + emit haveAllNuggets(); // sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen + emit gotNugget(250); // sendet der Application ein Nugget um Score zu erhhen + + } +} + +void KGrHero::loseNugget() { + + // Enemy trapped or dead and could not drop nugget (NO SCORE for this). + if (! (--nuggets)) + emit haveAllNuggets(); // Sendet der Application dass alle Nuggets + // gesammelt sind, um versteckte Leitern zu zeigen. +} + +bool KGrHero::isInEnemy(){ + + int c=0; + int range=enemies->count(); + if (range) + for (KGrEnemy *enemy=enemies->at(c);c<range; ) + {enemy = enemies->at(c++); + if (isInside(enemy->getx(),enemy->gety())|| + isInside(enemy->getx()-15,enemy->gety())|| + isInside(enemy->getx(),enemy->gety()-15)) + return true;} + return false; +} + +bool KGrHero::isInside(int enemyx, int enemyy){ + + return ((absx >= enemyx)&& + (absx <= enemyx+15)&& + (absy >= enemyy)&& + (absy <= enemyy+15)); +} + + +KGrHero :: ~KGrHero (){ + + delete walkTimer; + delete fallTimer; +} + + +KGrEnemy :: KGrEnemy (KGrCanvas * view, int x, int y) + : KGrFigure (x, y) +{ + enemyView = view; + actualPixmap = FALL1; + nuggets = 0; + enemyView->makeEnemySprite (x, y, actualPixmap); + + walkCounter = 1; + captiveCounter = 0; + + searchStatus = HORIZONTAL; + + birthX=x; + birthY=y; + + walkFrozen = FALSE; + fallFrozen = FALSE; + captiveFrozen = FALSE; + + captiveTimer = new QTimer (this); + connect (captiveTimer,SIGNAL(timeout()),SLOT(captiveTimeDone())); + connect (walkTimer, SIGNAL (timeout ()), SLOT (walkTimeDone ())); + connect (fallTimer, SIGNAL (timeout ()), SLOT (fallTimeDone ())); +} + +int KGrEnemy::WALKDELAY = 0; +int KGrEnemy::FALLDELAY = 0; +int KGrEnemy::CAPTIVEDELAY = 0; + +void KGrEnemy::doStep() { + if (walkFrozen) { + walkFrozen = FALSE; + walkTimeDone(); + } + if (fallFrozen) { + fallFrozen = FALSE; + fallTimeDone(); + } + if (captiveFrozen) { + captiveFrozen = FALSE; + captiveTimeDone(); + } +} + +void KGrEnemy::showState(char option) +{ + printf("(%02d,%02d) - Enemy [%d]", x, y, enemyId); + switch (option) { + case 'p': printf ("\n"); break; + case 's': printf (" nuggets %02d status %d walk-ctr %d ", + nuggets, status, walkCounter); + printf ("dirn %d search %d capt-ctr %d\n", + direction, searchStatus, captiveCounter); + printf (" rel (%02d,%02d) abs (%03d,%03d)", + relx, rely, absx, absy); + printf (" pix %02d", actualPixmap); + printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely); + if (walkFrozen) printf (" wBlock"); + if (fallFrozen) printf (" fBlock"); + if (captiveFrozen) printf (" cBlock"); + printf ("\n"); + break; + } +} + +void KGrEnemy::init(int a,int b) +{ + walkTimer->stop(); // alles stoppen bevor die Werte neu gesetzt + fallTimer->stop(); // werden, da es sonst zu ungewollten Effekten + captiveTimer->stop(); // kommen kann + walkCounter = 1; + captiveCounter = 0; + + x = mem_x = a; + y = mem_y = b; + relx = mem_relx = 0; + rely = mem_rely = 0; + + absx=16*x; + absy=16*y; + + actualPixmap = 19; + + status = STANDING; +} + +void KGrEnemy::walkTimeDone () +{ + if (KGrObject::frozen) {walkFrozen = TRUE; return; } + + // Check we are alive BEFORE checking for friends being in the way. + // Maybe a friend overhead is blocking our escape from a brick. + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "walkTimeDone" when an enemy dies. + } + + if (! bumpingFriend()) { + switch (direction) { + case UP: walkUp (WALKDELAY); + if ((rely == 0) && + ((*playfield)[x][y+1]->whatIam() == USEDHOLE)) + // Enemy kletterte grad aus einem Loch hinaus + // -> gib es frei! + ((KGrBrick *)(*playfield)[x][y+1])->unUseHole(); + break; + case DOWN: walkDown (WALKDELAY, FALLDELAY); break; + case RIGHT: walkRight (WALKDELAY, FALLDELAY); break; + case LEFT: walkLeft (WALKDELAY, FALLDELAY); break; + default: // Switch search direction in KGoldrunner search (only). + searchStatus = (searchStatus==VERTIKAL) ? + HORIZONTAL : VERTIKAL; + + // In KGoldrunner rules, if a hole opens under an enemy + // who is standing and waiting to move, he should fall. + if (!(canStand()||hangAtPole())) { + initFall (actualPixmap, FALLDELAY); + } + else { + status = STANDING; + } + + break; + } + // wenn die Figur genau ein Feld gelaufen ist + if (status == STANDING) { // dann suche den Helden + direction = searchbestway(x,y,herox,heroy); // und + if (walkCounter >= 4) { + if (! nuggets) + collectNugget(); + else + dropNugget(); + } + status = WALKING; // initialisiere die Zhlervariablen und + walkCounter = 1; // den Timer um den Held weiter + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); // zu jagen + startWalk (); + } + } + else { + // A friend is in the way. Try a new direction, but not if leaving a hole. + Direction dirn; + + // In KGoldrunner rules, change the search strategy, + // to avoid enemy-enemy deadlock. + searchStatus = (searchStatus==VERTIKAL) ? HORIZONTAL : VERTIKAL; + + dirn = searchbestway (x, y, herox, heroy); + if ((dirn != direction) && ((*playfield)[x][y]->whatIam() != USEDHOLE)) { + direction = dirn; + status = WALKING; + walkCounter = 1; + relx = 0; absx = 16 * x; + rely = 0; absy = 16 * y; + startWalk (); + } + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + showFigure(); +} + +void KGrEnemy::fallTimeDone () +{ + if (KGrObject::frozen) {fallFrozen = TRUE; return; } + + if ((*playfield)[x][y+1]->whatIam() == HOLE) { // wenn Enemy ins Loch fllt + ((KGrBrick*)(*playfield)[x][y+1])->useHole(); // reserviere das Loch, damit + // kein anderer es benutzt und + if (nuggets) { // er muss Gold vorher fallen lassen + nuggets=0; + switch ((*playfield)[x][y]->whatIam()) { + case FREE: + case HLADDER: + ((KGrFree *)(*playfield)[x][y])->setNugget(true); break; + default: + emit lostNugget(); break; // Cannot drop the nugget here. + } + } + emit trapped (75); // Enemy trapped: score 75. + } + else if (walkCounter <= 1) { + // Enemies collect nuggets when falling. + if (!nuggets) { + collectNugget(); + } + } + + if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick + dieAndReappear(); // sein, dann stirbt er wohl + return; // Must leave "fallTimeDone" when an enemy dies. + } + + if (standOnEnemy()) { // Don't fall into a friend. + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + return; + } + + if (walkCounter++ < 4){ + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + { rely+=STEP; absy+=STEP;} + } + else { + rely = 0; y ++; absy=16*y; + if ((*playfield)[x][y]->whatIam() == USEDHOLE) { + captiveCounter = 0; + status = CAPTIVE; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + } + else if (!(canStand()||hangAtPole())) { + fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE); + walkCounter=1; + } + else { + status = STANDING; + if (hangAtPole()) + actualPixmap=(direction ==RIGHT)?8:12; + } + } + if (status == STANDING) { + status = WALKING; + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + startWalk (); + if (!nuggets) + collectNugget(); + else + dropNugget(); + } + showFigure(); +} + +void KGrEnemy::captiveTimeDone() +{ + if (KGrObject::frozen) {captiveFrozen = TRUE; return; } + if ((*playfield)[x][y]->whatIam()==BRICK) + dieAndReappear(); + else + if (captiveCounter > 6){ + status = WALKING; + walkCounter = 1; + direction = UP; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + captiveCounter = 0; + } else { + captiveCounter ++; + captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE); + showFigure(); + } +} + +void KGrEnemy::startSearching() +{ + // Called from "KGoldrunner::startPlaying" and "KGrEnemy::dieAndReappear". + init(x,y); + + if (canStand()||((*playfield)[x][y+1]->whatIam()==USEDHOLE)) { + status = WALKING; + walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); + } + else { + status = FALLING; + fallTimer->start ((FALLDELAY * NSPEED) / speed, TRUE); + } + + walkCounter = 1; + direction = searchbestway(x,y,herox,heroy); + startWalk(); +} + +void KGrEnemy::collectNugget() +{ + if (((*playfield)[x][y]->whatIam() == NUGGET) && (nuggets == 0) && + (alwaysCollectNugget || ((int)(5.0*rand()/RAND_MAX+1.0) > 4))){ + ((KGrFree *)(*playfield)[x][y])->setNugget(false); + nuggets=1; + } +} + +void KGrEnemy::dropNugget() +{ + if (((int)(DROPNUGGETDELAY*rand()/RAND_MAX+1.0) > DROPNUGGETDELAY-5) && + ((*playfield)[x][y]->whatIam() == FREE)) { + ((KGrFree *)(*playfield)[x][y])->setNugget(true); + nuggets=0; + } +} + +void KGrEnemy::showFigure () +{ + enemyView->moveEnemy (enemyId, absx, absy, actualPixmap, nuggets); + + // Merke alte Werte zum lschen der Figur + mem_x = x; + mem_y = y; + mem_relx = relx; + mem_rely = rely; +} + +bool KGrEnemy::canWalkUp() +{ + return (((*playfield)[x][y-1]->whatIam() != BRICK) && + ((*playfield)[x][y-1]->whatIam() != BETON) && + ((*playfield)[x][y-1]->whatIam() != FBRICK) && + (((*playfield)[x][y]->whatIam() == USEDHOLE) || + ((*playfield)[x][y]->whatIam() == LADDER))); +} + +void KGrEnemy::startWalk () +{ + switch (direction) { + case UP: break; + case RIGHT: if (hangAtPole()) + actualPixmap = RIGHTCLIMB1; + else + actualPixmap = RIGHTWALK1; + break; + case DOWN: break; + case LEFT: if (hangAtPole()) + actualPixmap = LEFTCLIMB1; + else + actualPixmap = LEFTWALK1; + break; + default: break; + } +} + +void KGrEnemy::dieAndReappear() +{ + bool looking; + int i; + + if (nuggets > 0) { + nuggets = 0; // Enemy died and could not drop nugget. + emit lostNugget(); // NO SCORE for lost nugget. + } + emit killed (75); // Killed an enemy: score 75. + + if (reappearAtTop) { + // Follow Traditional rules: enemies reappear at top. + looking = TRUE; + y = 2; + // Randomly look for a free spot in row 2. Limit the number of tries. + for (i = 1; ((i <= 3) && looking); i++) { + x = (int)((FIELDWIDTH * (float) rand()) / RAND_MAX) + 1; + switch ((*playfield)[x][2]->whatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + // If unsuccessful, choose the first free spot in row 3 or below. + while ((y<FIELDHEIGHT) && looking) { + y++; + x = 0; + while ((x<FIELDWIDTH) && looking) { + x++; + switch ((*playfield)[x][y]->whatIam()) { + case FREE: + case HLADDER: + looking = FALSE; + break; + default: + break; + } + } + } + } + else { + // Follow KGoldrunner rules: enemies reappear where they started. + x = birthX; + y = birthY; + } + + // Enemy reappears and starts searching for the hero. + startSearching(); +} + +Direction KGrEnemy::searchbestway(int ew,int eh,int hw,int hh) +{ + Direction dirn; + + if ((*playfield)[x][y]->whatIam() == USEDHOLE) // Could not get out of + return UP; // hole (eg. brick above + // closed): keep trying. + + if (!canStand() && // Cannot stand, + !hangAtPole() && // not on pole, not + !standOnEnemy() && // walking on friend and + !((*playfield)[x][y+1]->whatIam() == HOLE)) // not just out of hole, + return DOWN; // so must fall. + + switch (searchStrategy) { + + // Traditional search strategy. + case LOW: + dirn = STAND; + if (eh == hh) { // Hero on same row. + dirn = lowGetHero (ew, eh, hw); + } + if (dirn != STAND) return (dirn); // Can go towards him. + + if (eh >= hh) { // Hero above enemy. + dirn = lowSearchUp (ew, eh, hh); // Find a way up. + } + else { // Hero below enemy. + dirn = lowSearchDown (ew, eh, hh); // Find way down to him. + if (dirn == STAND) { + dirn = lowSearchUp (ew, eh, hh); // No go: try up. + } + } + + if (dirn == STAND) { // When all else fails, + dirn = lowSearchDown (ew, eh, eh - 1); // find way below hero. + } + return dirn; + break; + + // KGoldrunner search strategy. + case MEDIUM: + case HIGH: + if(searchStatus==VERTIKAL){ + if (eh > hh) + return searchupway(ew,eh); + if (eh < hh) + return searchdownway(ew,eh); + return STAND; + } else { + if (ew > hw) + return searchleftway(ew,eh); + if (ew < hw) + return searchrightway(ew,eh); + return STAND; + } + break; + } + return STAND; +} + +/////////////////////////////////////////////// +// Methods for medium-level search strategy. // +/////////////////////////////////////////////// + +Direction KGrEnemy :: searchdownway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKDOWN) + return DOWN; + else while((i>=0)||(j<=FIELDWIDTH)){ + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKDOWN) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKDOWN) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + return STAND; +} + +Direction KGrEnemy :: searchupway(int ew,int eh){ +int i,j; + i=j=ew; + if ( (*playfield)[ew][eh]->searchValue & CANWALKUP) + return UP; + else while((i>=0)||(j<=FIELDWIDTH)){// search for the first way up + if (i>=0) + if ( (*playfield)[i][eh]->searchValue & CANWALKUP) + return LEFT; + else + if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) || + (runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE)))) + i=-1; + if (j<=FIELDWIDTH) + if ( (*playfield)[j][eh]->searchValue & CANWALKUP) + return RIGHT; + else + if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE)))) + j=FIELDWIDTH+1; + } + // BUG FIX - Ian W., 30/4/01 - Don't leave an enemy standing in mid air. + if (!canStand()) return DOWN; else return STAND; +} + +Direction KGrEnemy :: searchleftway(int ew,int eh){ +int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKLEFT) || /* kann figur nach links laufen ?*/ + (runThruHole && ( (*playfield)[ew-1][eh]->whatIam() == HOLE))) + return LEFT; + else while((i>=0)||(j<=FIELDHEIGHT)){ /* in den grenzen ?*/ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKLEFT) || /* ein weg nach links- oben gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][i]->whatIam() == HOLE))) + return UP; /* geh nach oben */ + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) /* sonst ...*/ + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKLEFT) || /* ein weg nach links- unten gefunden ?*/ + (runThruHole && ( (*playfield)[ew-1][j]->whatIam() == HOLE))) + return DOWN; /* geh nach unten */ + else + if (!( (*playfield)[ew][j++]->searchValue&CANWALKDOWN)) /* sonst ... */ + j=FIELDHEIGHT+1; + } + return STAND; /* default */ +} + +Direction KGrEnemy :: searchrightway(int ew,int eh) +{ + int i,j; + i=j=eh; + if ( ((*playfield)[ew][eh]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][eh]->whatIam() == HOLE))) + return RIGHT; + else while((i>=0)||(j<=FIELDHEIGHT)){ + if (i>=0) + if ( ((*playfield)[ew][i]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][i]->whatIam() == HOLE))) + return UP; + else + if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) + i=-1; + if (j<=FIELDHEIGHT) + if ( ((*playfield)[ew][j]->searchValue & CANWALKRIGHT) || + (runThruHole && ( (*playfield)[ew+1][j]->whatIam() == HOLE))) + return DOWN; + else + if (!( (*playfield)[ew][j++]->searchValue & CANWALKDOWN)) + j=FIELDHEIGHT+1; + } + return STAND; +} + +//////////////////////////////////////////// +// Methods for low-level search strategy. // +//////////////////////////////////////////// + +Direction KGrEnemy::lowSearchUp (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs; + + deltah = eh - hh; // Get distance up to hero's level. + + // Search for the best ladder right here or on the left. + i = ew; ilen = 0; ipos = -1; + while (i >= 1) { + rungs = distanceUp (i, eh, deltah); + if (rungs > ilen) { + ilen = rungs; // This the best yet. + ipos = i; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best ladder on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + rungs = distanceUp (j, eh, deltah); + if (rungs > jlen) { + jlen = rungs; // This the best yet. + jpos = j; + } + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // No ladder found. + return STAND; + + // Choose a ladder to go to. + if (ilen != jlen) { // If the ladders are not the same + // length, choose the longer one. + if (ilen > jlen) { + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else + return LEFT; + } + else + return RIGHT; + } + else { // Both ladders are the same length. + + if (ipos == ew) // If already on the best ladder, go up. + return UP; + else if (ilen == deltah) { // If both reach the hero's level, + if ((ew - ipos) <= (jpos - ew)) // choose the closest. + return LEFT; + else + return RIGHT; + } + else return LEFT; // Else choose the left ladder. + } +} + +Direction KGrEnemy::lowSearchDown (int ew, int eh, int hh) +{ + int i, ilen, ipos, j, jlen, jpos, deltah, rungs, path; + + deltah = hh - eh; // Get distance down to hero's level. + + // Search for the best way down, right here or on the left. + ilen = 0; ipos = -1; + i = (willNotFall (ew, eh)) ? ew : -1; + rungs = distanceDown (ew, eh, deltah); + if (rungs > 0) { + ilen = rungs; ipos = ew; + } + + while (i >= 1) { + rungs = distanceDown (i - 1, eh, deltah); + if (((rungs > 0) && (ilen == 0)) || + ((deltah > 0) && (rungs > ilen)) || + ((deltah <= 0) && (rungs < ilen) && (rungs != 0))) { + ilen = rungs; // This the best way yet. + ipos = i - 1; + } + if (searchOK (-1, i, eh)) + i--; // Look further to the left. + else + i = -1; // Cannot go any further to the left. + } + + // Search for the best way down, on the right. + j = ew; jlen = 0; jpos = -1; + while (j < FIELDWIDTH) { + rungs = distanceDown (j + 1, eh, deltah); + if (((rungs > 0) && (jlen == 0)) || + ((deltah > 0) && (rungs > jlen)) || + ((deltah <= 0) && (rungs < jlen) && (rungs != 0))) { + jlen = rungs; // This the best way yet. + jpos = j + 1; + } + if (searchOK (+1, j, eh)) { + j++; // Look further to the right. + } + else + j = FIELDWIDTH+1; // Cannot go any further to the right. + } + + if ((ilen == 0) && (jlen == 0)) // Found no way down. + return STAND; + + // Choose a way down to follow. + if (ilen == 0) + path = jpos; + else if (jlen == 0) + path = ipos; + else if (ilen != jlen) { // If the ways down are not same length, + // choose closest to hero's level. + if (deltah > 0) { + if (jlen > ilen) + path = jpos; + else + path = ipos; + } + else { + if (jlen > ilen) + path = ipos; + else + path = jpos; + } + } + else { // Both ways down are the same length. + if ((deltah > 0) && // If both reach the hero's level, + (ilen == deltah)) { // choose the closest. + if ((ew - ipos) <= (jpos - ew)) + path = ipos; + else + path = jpos; + } + else + path = ipos; // Else, go left or down. + } + + if (path == ew) + return DOWN; + else if (path < ew) + return LEFT; + else + return RIGHT; +} + +Direction KGrEnemy::lowGetHero (int ew, int eh, int hw) +{ + int i, inc, returnValue; + + inc = (ew > hw) ? -1 : +1; + i = ew; + while (i != hw) { + returnValue = canWalkLR (inc, i, eh); + if (returnValue > 0) + i = i + inc; // Can run further towards the hero. + else if (returnValue < 0) + break; // Will run into a wall regardless. + else + return STAND; // Won't run over a hole. + } + + if (i < ew) return LEFT; + else if (i > ew) return RIGHT; + else return STAND; +} + +int KGrEnemy::distanceUp (int x, int y, int deltah) +{ + int rungs = 0; + + // If there is a ladder at (x.y), return its length, else return zero. + while ( (*playfield)[x][y - rungs]->whatIam() == LADDER) { + rungs++; + if (rungs >= deltah) // To hero's level is enough. + break; + } + return rungs; +} + +int KGrEnemy::distanceDown (int x, int y, int deltah) +{ + // When deltah > 0, we want an exit sideways at the hero's level or above. + // When deltah <= 0, we can go down any distance (as a last resort). + + int rungs = -1; + int exitRung = 0; + bool canGoThru = TRUE; + char objType; + + // If there is a way down at (x,y), return its length, else return zero. + // Because rungs == -1, we first check that level y is not blocked here. + while (canGoThru) { + objType = (*playfield)[x][y + rungs + 1]->whatIam(); + switch (objType) { + case BRICK: + case BETON: + case HOLE: // Enemy cannot go DOWN through a hole. + case USEDHOLE: + if ((deltah > 0) && (rungs <= deltah)) + exitRung = rungs; + if ((objType == HOLE) && (rungs < 0)) + rungs = 0; // Enemy can go SIDEWAYS through a hole. + else + canGoThru = FALSE; // Cannot go through here. + break; + case LADDER: + case POLE: // Can go through or stop. + rungs++; // Add to path length. + if ((deltah > 0) && (rungs >= 0)) { + // If at or above hero's level, check for an exit from ladder. + if ((rungs - 1) <= deltah) { + // Maybe can stand on top of ladder and exit L or R. + if ((objType == LADDER) && (searchOK (-1, x, y+rungs-1) || + searchOK (+1, x, y+rungs-1))) + exitRung = rungs - 1; + // Maybe can exit L or R from ladder body or pole. + if ((rungs <= deltah) && (searchOK (-1, x, y+rungs) || + searchOK (+1, x, y+rungs))) + exitRung = rungs; + } + else + canGoThru = FALSE; // Should stop at hero's level. + } + break; + default: + rungs++; // Can go through. Add to path length. + break; + } + } + if (rungs == 1) { + for (KGrEnemy *enemy=enemies->first();enemy!=0;enemy=enemies->next()) { + if((x*16==enemy->getx()) && (y*16+16==enemy->gety())) + rungs = 0; // Pit is blocked. Find another way. + } + } + if (rungs <= 0) + return 0; // There is no way down. + else if (deltah > 0) + return exitRung; // We want to take an exit, if any. + else + return rungs; // We can go down all the way. +} + +bool KGrEnemy::searchOK (int direction, int x, int y) +{ + // Check whether it is OK to search left or right. + if (canWalkLR (direction, x, y) > 0) { + // Can go into that cell, but check for a fall. + if (willNotFall (x+direction, y)) + return TRUE; + } + return FALSE; // Cannot go into and through that cell. +} + +int KGrEnemy::canWalkLR (int direction, int x, int y) +{ + if (willNotFall (x, y)) { + switch ((*playfield)[x+direction][y]->whatIam()) { + case BETON: + case BRICK: + case USEDHOLE: + return -1; break; // Will be halted in current cell. + default: + // NB. FREE, LADDER, HLADDER, NUGGET and POLE are OK of course, + // but enemies can also walk left/right through a HOLE and + // THINK they can walk left/right through a FBRICK. + + return +1; break; // Can walk into next cell. + } + } + else + return 0; // Will fall before getting there. +} + +bool KGrEnemy::willNotFall (int x, int y) +{ + int c, cmax; + KGrEnemy *enemy; + + // Check the ceiling. + switch ( (*playfield)[x][y]->whatIam()) { + case LADDER: + case POLE: + return TRUE; break; // OK, can hang on ladder or pole. + default: + break; + } + + // Check the floor. + switch ( (*playfield)[x][y+1]->whatIam()) { + + // Cases where the enemy knows he will fall. + case FREE: + case HLADDER: + case FBRICK: + + // N.B. The enemy THINKS he can run over a NUGGET, a buried POLE or a HOLE. + // The last of these cases allows the hero to trap the enemy, of course. + + // Note that there are several Traditional levels that require an enemy to + // be trapped permanently in a pit containing a nugget, as he runs towards + // you. It is also possible to use a buried POLE in the same way. + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->getx()==16*x) && (enemy->gety()==16*(y+1))) + return TRUE; // Standing on a friend. + } + return FALSE; break; // Will fall: there is no floor. + + default: + return TRUE; break; // OK, will not fall. + } +} + +#ifdef QT3 +void KGrEnemy::setEnemyList(QPtrList<KGrEnemy> *e) +#else +void KGrEnemy::setEnemyList(QList<KGrEnemy> *e) +#endif +{ + enemies = e; +} + +bool KGrEnemy::standOnEnemy() +{ + int c = 0; + int range = enemies->count(); + if (range > 1) { + for (KGrEnemy * enemy = enemies->at (c); c < range; ) { + enemy = enemies->at(c++); + // Test if enemy's foot is at or just below enemy's head (tolerance + // of 4 pixels) and the two figures overlap in the X direction. + if ((((absy + 16) == enemy->gety()) || + ((absy + 12) == enemy->gety())) && + (((absx - 16) < enemy->getx()) && + ((absx + 16) > enemy->getx()))) { + return true; + } + } + } + return false; +} + +bool KGrEnemy::bumpingFriend() +{ +// Enemies that are falling are treated as being invisible (i.e. a walking +// enemy will walk through them or they will stop on that enemy's head). +// +// If two enemies are moving in opposite directions, they are allowed to come +// within two cell widths of each other (8 steps). Then one must stop before +// entering the next cell and let the other one go into it. If both are about +// to enter a new cell, the one on the right or above gives way to the one on +// the left or below (implemented by letting the latter approach to 7 steps +// apart before detecting an impending collision, by which time the first +// enemy should have stopped and given way). +// +// In all other cases, enemies are allowed to approach to 4 steps apart (i.e. +// bumping a friend), before being forced to stop and wait. If they somehow +// get closer than 4 steps (i.e. overlapping), the lower ID enemy is allowed +// to move out while the higher ID enemy waits. This can happen if one enemy +// falls into another or is reborn where another enemy is located. + + int c, cmax; + KGrEnemy *enemy; + int dx, dy; + + cmax = enemies->count(); + for (c = 0; c < cmax; c++) { + enemy = enemies->at(c); + if ((enemy->enemyId != enemyId) && (enemy->status != FALLING)) { + dx = enemy->getx() - absx; + dy = enemy->gety() - absy; + switch (direction) { + case LEFT: + if ((dx >= -32) && (dx < 16) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == RIGHT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx >= -16) { + if ((dx > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case RIGHT: + if ((dx > -16) && (dx < 32) && (dy > -16) && (dy < 16)) { + if ((enemy->direction == LEFT) && + (enemy->status == WALKING) && (absx%16==0)) { + return TRUE; + } + else if (dx <= 16) { + if ((dx < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one in front. + } + } + break; + case UP: + if ((dy >= -32) && (dy < 16) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == DOWN) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy >= -16) { + if ((dy > -16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one above. + } + } + break; + case DOWN: + if ((dy > -16) && (dy < 32) && (dx > -16) && (dx < 16)) { + if ((enemy->direction == UP) && + (enemy->status == WALKING) && (absy%16==0)) { + return TRUE; + } + else if (dy <= 16) { + if ((dy < 16) && (enemyId < enemy->enemyId)) + return FALSE; + else + return TRUE; // Wait for the one below. + } + } + break; + default: + break; + } + } + } + return FALSE; +} + +KGrEnemy :: ~KGrEnemy () +{ + delete captiveTimer; + delete walkTimer; + delete fallTimer; +} + +#include "kgrfigure.moc" diff --git a/kgoldrunner/src/kgrfigure.h b/kgoldrunner/src/kgrfigure.h new file mode 100644 index 00000000..e6ee0f2e --- /dev/null +++ b/kgoldrunner/src/kgrfigure.h @@ -0,0 +1,231 @@ +/*************************************************************************** + * kgrfigure.h - description * + * ------------------- * + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * email : See menu "Help, About KGoldrunner" * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef KGRFIGURE_H +#define KGRFIGURE_H + +// Obsolete - #include <iostream.h> +#include <iostream> + +#include <qimage.h> +#ifdef QT3 +#include <qptrlist.h> +#else +#include <qlist.h> +#endif +#include <qpainter.h> +#include <qpixmap.h> +#include <qtimer.h> +#include <qwidget.h> +#include <stdlib.h> // fr Zufallsfunktionen + +class KGrCanvas; +class KGrObject; +class KGrEnemy; + +class KGrFigure : public QObject +{ + Q_OBJECT +public: + KGrFigure (int, int); + virtual ~KGrFigure(); + + // STATIC GLOBAL FLAGS. + static bool variableTiming; // More enemies imply less speed. + static bool alwaysCollectNugget; // Enemies always collect nuggets. + static bool runThruHole; // Enemy can run L/R through dug hole. + static bool reappearAtTop; // Enemies are reborn at top of screen. + static SearchStrategy searchStrategy; // Low, medium or high difficulty. + + static Timing fixedTiming; // Original set of 6 KGr timing values. + + static Timing varTiming [6]; // Optional 6 sets of timing values, + // dependent on number of enemies. + int getx(); + int gety(); + Status getStatus(); + + int getnuggets(); + void setNuggets(int n); + void setPlayfield(KGrObject *(*p)[30][22]); + void showFigure(); //zeigt Figur + virtual void init(int,int); + void eraseOldFigure(); + +protected: + // STATIC GLOBAL VARIABLES. + static int herox; + static int heroy; + + static int speed; // Adjustable game-speed factor. + + int x, y; + int absx, absy; + int relx, rely; // Figur wird um relx,rely Pixel verschoben + int mem_x,mem_y,mem_relx,mem_rely; + int walkCounter; + int nuggets; + int actualPixmap; // ArrayPos der zu zeichnenden Pixmap + QTimer *walkTimer; + QTimer *fallTimer; + + KGrObject *(*playfield)[30][22]; + Status status; + Direction direction; + bool canWalkRight(); + bool canWalkLeft(); + virtual bool canWalkUp(); + bool canWalkDown(); + bool canStand(); + bool hangAtPole(); + virtual bool standOnEnemy()=0; + void walkUp(int); + void walkDown(int, int); + void walkRight(int, int); + void walkLeft(int, int); + void initFall(int, int); + + bool walkFrozen; + bool fallFrozen; +}; + +class KGrHero : public KGrFigure +{ + Q_OBJECT +public: + KGrHero(KGrCanvas *, int , int); + virtual ~KGrHero(); + bool started; + void showFigure(); + void dig(); + void digLeft(); + void digRight(); + void startWalk(); +#ifdef QT3 + void setEnemyList(QPtrList<KGrEnemy> *); +#else + void setEnemyList(QList<KGrEnemy> *); +#endif + void init(int,int); + void setKey(Direction); + void setDirection(int, int); + void start(); + void loseNugget(); + static int WALKDELAY; + static int FALLDELAY; + void setSpeed(int); + void doStep(); + void showState (char); + +private: +#ifdef QT3 + QPtrList<KGrEnemy> *enemies; +#else + QList<KGrEnemy> *enemies; +#endif + KGrCanvas * heroView; + bool standOnEnemy(); + bool isInEnemy(); + bool isInside(int, int); + + Direction nextDir; + void collectNugget(); + + bool mouseMode; + bool stopped; + int mousex; + int mousey; + void setNextDir(); + +public slots: + void walkTimeDone(); + void fallTimeDone(); + +signals: + void gotNugget(int); + void haveAllNuggets(); + void leaveLevel(); + void caughtHero(); +}; + +class KGrEnemy : public KGrFigure +{ + Q_OBJECT +public: + KGrEnemy (KGrCanvas *, int , int); + virtual ~KGrEnemy(); + void showFigure(); + void startSearching(); +#ifdef QT3 + void setEnemyList(QPtrList<KGrEnemy> *); +#else + void setEnemyList(QList<KGrEnemy> *); +#endif + virtual void init(int,int); + static int WALKDELAY; + static int FALLDELAY; + static int CAPTIVEDELAY; + int enemyId; + void doStep(); + void showState (char); + +private: + KGrCanvas * enemyView; + int birthX, birthY; + int searchStatus; + int captiveCounter; + QTimer *captiveTimer; + bool canWalkUp(); +#ifdef QT3 + QPtrList<KGrEnemy> *enemies; +#else + QList<KGrEnemy> *enemies; +#endif + bool standOnEnemy(); + bool bumpingFriend(); + + void startWalk(); + void dieAndReappear(); + Direction searchbestway(int,int,int,int); + Direction searchdownway(int,int); + Direction searchupway(int,int); + Direction searchleftway(int,int); + Direction searchrightway(int,int); + + Direction lowSearchUp(int,int,int); + Direction lowSearchDown(int,int,int); + Direction lowGetHero(int,int,int); + + int distanceUp (int, int, int); + int distanceDown (int, int, int); + bool searchOK (int, int, int); + int canWalkLR (int, int, int); + bool willNotFall (int, int); + + void collectNugget(); + void dropNugget(); + + bool captiveFrozen; + +public slots: + void walkTimeDone(); + void fallTimeDone(); + void captiveTimeDone(); + +signals: + void lostNugget(); + void trapped(int); + void killed(int); +}; + +#endif // KGRFIGURE_H diff --git a/kgoldrunner/src/kgrgame.cpp b/kgoldrunner/src/kgrgame.cpp new file mode 100644 index 00000000..93164d88 --- /dev/null +++ b/kgoldrunner/src/kgrgame.cpp @@ -0,0 +1,2613 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifdef KGR_PORTABLE +// If compiling for portability, redefine KDE's i18n. +#define i18n tr +#endif + +#include "kgrconsts.h" +#include "kgrobject.h" +#include "kgrfigure.h" +#include "kgrcanvas.h" +#include "kgrdialog.h" + +#include "kgrgame.h" + +// Obsolete - #include <iostream.h> +#include <iostream> +#include <stdlib.h> +#include <ctype.h> + +#include <kpushbutton.h> +#include <kstdguiitem.h> + +#ifndef KGR_PORTABLE +#include <kglobalsettings.h> +#endif + +/******************************************************************************/ +/*********************** KGOLDRUNNER GAME CLASS *************************/ +/******************************************************************************/ + +KGrGame::KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir) +{ + view = theView; + systemDataDir = theSystemDir; + userDataDir = theUserDir; + + // Set the game-editor OFF, but available. + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + shouldSave = FALSE; + + enemies.setAutoDelete(TRUE); + + hero = new KGrHero (view, 0, 0); // The hero is born ... Yay !!! + hero->setPlayfield (&playfield); + + setBlankLevel (TRUE); // Fill the playfield with blank walls. + + enemy = NULL; + newLevel = TRUE; // Next level will be a new one. + loading = TRUE; // Stop input until it is loaded. + + modalFreeze = FALSE; + messageFreeze = FALSE; + + connect (hero, SIGNAL (gotNugget(int)), SLOT (incScore(int))); + connect (hero, SIGNAL (caughtHero()), SLOT (herosDead())); + connect (hero, SIGNAL (haveAllNuggets()), SLOT (showHiddenLadders())); + connect (hero, SIGNAL (leaveLevel()), SLOT (goUpOneLevel())); + + dyingTimer = new QTimer (this); + connect (dyingTimer, SIGNAL (timeout()), SLOT (finalBreath())); + + // Get the mouse position every 40 msec. It is used to steer the hero. + mouseSampler = new QTimer (this); + connect (mouseSampler, SIGNAL(timeout()), SLOT (readMousePos ())); + mouseSampler->start (40, FALSE); + + srand(1); // initialisiere Random-Generator +} + +KGrGame::~KGrGame() +{ +} + +/******************************************************************************/ +/************************* GAME SELECTION PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::startLevelOne() +{ + startLevel (SL_START, 1); +} + +void KGrGame::startAnyLevel() +{ + startLevel (SL_ANY, level); +} + +void KGrGame::startNextLevel() +{ + startLevel (SL_ANY, level + 1); +} + +void KGrGame::startLevel (int startingAt, int requestedLevel) +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + // Use dialog box to select game and level: startingAt = ID_FIRST or ID_ANY. + int selectedLevel = selectLevel (startingAt, requestedLevel); + if (selectedLevel > 0) { // If OK, start the selected game and level. + newGame (selectedLevel, selectedGame); + } else { + level = 0; + } +} + +/******************************************************************************/ +/************************ MAIN GAME EVENT PROCEDURES ************************/ +/******************************************************************************/ + +void KGrGame::incScore (int n) +{ + score = score + n; // SCORING: trap enemy 75, kill enemy 75, + emit showScore (score); // collect gold 250, complete the level 1500. +} + +void KGrGame::herosDead() +{ + if ((level < 1) || (lives <= 0)) + return; // Game over: we are in the "ENDE" screen. + + // Lose a life. + if (--lives > 0) { + // Still some life left, so PAUSE and then re-start the level. + emit showLives (lives); + KGrObject::frozen = TRUE; // Freeze the animation and let + dyingTimer->start (1500, TRUE); // the player see what happened. + } + else { + // Game over: display the "ENDE" screen. + emit showLives (lives); + freeze(); + QString gameOver = "<NOBR><B>" + i18n("GAME OVER !!!") + "</B></NOBR>"; + KGrMessage::information (view, collection->name, gameOver); + checkHighScore(); // Check if there is a high score for this game. + + enemyCount = 0; + enemies.clear(); // Stop the enemies catching the hero again ... + view->deleteEnemySprites(); + unfreeze(); // ... NOW we can unfreeze. + newLevel = TRUE; + level = 0; + loadLevel (level); // Display the "ENDE" screen. + newLevel = FALSE; + } +} + +void KGrGame::finalBreath() +{ + // Fix bug 95202: Avoid re-starting if the player selected + // edit mode before the 1.5 seconds were up. + if (! editMode) { + enemyCount = 0; // Hero is dead: re-start the level. + loadLevel (level); + } + KGrObject::frozen = FALSE; // Unfreeze the game, but don't move yet. +} + +void KGrGame::showHiddenLadders() +{ + int i,j; + for (i=1;i<21;i++) + for (j=1;j<29;j++) + if (playfield[j][i]->whatIam()==HLADDER) + ((KGrHladder *)playfield[j][i])->showLadder(); + view->updateCanvas(); + initSearchMatrix(); +} + +void KGrGame::goUpOneLevel() +{ + lives++; // Level completed: gain another life. + emit showLives (lives); + incScore (1500); + + if (level >= collection->nLevels) { + freeze(); + KGrMessage::information (view, collection->name, + i18n("<b>CONGRATULATIONS !!!!</b>" + "<p>You have conquered the last level in the %1 game !!</p>") + .arg("<b>\"" + collection->name + "\"</b>")); + checkHighScore(); // Check if there is a high score for this game. + + unfreeze(); + level = 0; // Game completed: display the "ENDE" screen. + } + else { + level++; // Go up one level. + emit showLevel (level); + } + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::loseNugget() +{ + hero->loseNugget(); // Enemy trapped/dead and holding a nugget. +} + +KGrHero * KGrGame::getHero() +{ + return (hero); // Return a pointer to the hero. +} + +int KGrGame::getLevel() // Return the current game-level. +{ + return (level); +} + +bool KGrGame::inMouseMode() +{ + return (mouseMode); // Return TRUE if game is under mouse control. +} + +bool KGrGame::inEditMode() +{ + return (editMode); // Return TRUE if the game-editor is active. +} + +bool KGrGame::isLoading() +{ + return (loading); // Return TRUE if a level is being loaded. +} + +void KGrGame::setMouseMode (bool on_off) +{ + mouseMode = on_off; // Set Mouse OR keyboard control. +} + +void KGrGame::freeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (TRUE); // Do visual feedback in the GUI. + } + KGrObject::frozen = TRUE; // Halt the game, by blocking all timer events. +} + +void KGrGame::unfreeze() +{ + if ((! modalFreeze) && (! messageFreeze)) { + emit gameFreeze (FALSE);// Do visual feedback in the GUI. + } + KGrObject::frozen = FALSE; // Restart the game. Because frozen == FALSE, + restart(); // the game goes on running after the next step. +} + +void KGrGame::setMessageFreeze (bool on_off) +{ + if (on_off) { // Freeze the game action during a message. + messageFreeze = FALSE; + if (! KGrObject::frozen) { + messageFreeze = TRUE; + freeze(); + } + } + else { // Unfreeze the game action after a message. + if (messageFreeze) { + unfreeze(); + messageFreeze = FALSE; + } + } +} + +void KGrGame::setBlankLevel(bool playable) +{ + for (int j=0;j<20;j++) + for (int i=0;i<28;i++) { + if (playable) { + //playfield[i+1][j+1] = new KGrFree (freebg, nuggetbg, false, view); + playfield[i+1][j+1] = new KGrFree (FREE,i+1,j+1,view); + } + else { + //playfield[i+1][j+1] = new KGrEditable (freebg, view); + playfield[i+1][j+1] = new KGrEditable (FREE); + view->paintCell (i+1, j+1, FREE); + } + editObjArray[i+1][j+1] = FREE; + } + for (int j=0;j<30;j++) { + //playfield[j][0]=new KGrBeton(QPixmap ()); + playfield[j][0]=new KGrObject (BETON); + editObjArray[j][0] = BETON; + //playfield[j][21]=new KGrBeton(QPixmap ()); + playfield[j][21]=new KGrObject (BETON); + editObjArray[j][21] = BETON; + } + for (int i=0;i<22;i++) { + //playfield[0][i]=new KGrBeton(QPixmap ()); + playfield[0][i]=new KGrObject (BETON); + editObjArray[0][i] = BETON; + //playfield[29][i]=new KGrBeton(QPixmap ()); + playfield[29][i]=new KGrObject (BETON); + editObjArray[29][i] = BETON; + } + //for (int j=0;j<22;j++) + //for (int i=0;i<30;i++) { + //playfield[i][j]->move(16+i*16,16+j*16); + //} +} + +void KGrGame::newGame (const int lev, const int gameIndex) +{ + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; // "loadLevel (level)" will reset it. + + if (editMode) { + emit setEditMenu (FALSE); // Disable edit menu items and toolbar. + + editMode = FALSE; + paintEditObj = FALSE; + editObj = BRICK; + + view->setHeroVisible (TRUE); + } + + newLevel = TRUE; + level = lev; + collnIndex = gameIndex; + collection = collections.at (collnIndex); + owner = collection->owner; + + lives = 5; // Start with 5 lives. + score = 0; + startScore = 0; + + emit showLives (lives); + emit showScore (score); + emit showLevel (level); + + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; +} + +void KGrGame::startTutorial() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + int i, index; + int imax = collections.count(); + bool found = FALSE; + + index = 0; + for (i = 0; i < imax; i++) { + index = i; // Index within owner. + if (collections.at(i)->prefix == "tute") { + found = TRUE; + break; + } + } + if (found) { + // Start the tutorial. + collection = collections.at (index); + owner = collection->owner; + emit markRuleType (collection->settings); + collnIndex = index; + level = 1; + newGame (level, collnIndex); + } + else { + KGrMessage::information (view, i18n("Start Tutorial"), + i18n("Cannot find the tutorial game (file-prefix %1) in " + "the %2 files.") + .arg("'tute'").arg("'games.dat'")); + } +} + +void KGrGame::showHint() +{ + // Put out a hint for this level. + QString caption = i18n("Hint"); + + if (levelHint.length() > 0) + myMessage (view, caption, levelHint); + else + myMessage (view, caption, + i18n("Sorry, there is no hint for this level.")); +} + +int KGrGame::loadLevel (int levelNo) +{ + int i,j; + QFile openlevel; + + if (! openLevelFile (levelNo, openlevel)) { + return 0; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + nuggets = 0; + enemyCount=0; + startScore = score; // What we will save, if asked. + + // lade den Level + for (j=1;j<21;j++) + for (i=1;i<29;i++) { + changeObject(openlevel.getch(),i,j); + } + + // Absorb a newline character, then read in the level name and hint (if any). + int c = openlevel.getch(); + levelName = ""; + levelHint = ""; + QCString levelNameC = ""; + QCString levelHintC = ""; + i = 1; + while ((c = openlevel.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + openlevel.close(); + + // If there is a name, recode any UTF-8 substrings and translate it right now. + if (levelNameC.length() > 0) + levelName = i18n((const char *) levelNameC); + + // Indicate on the menus whether there is a hint for this level. + int len = levelHintC.length(); + emit hintAvailable (len > 0); + + // If there is a hint, remove the final newline and translate it right now. + if (len > 0) + levelHint = i18n((const char *) levelHintC.left(len-1)); + + // Disconnect edit-mode slots from signals from "view". + disconnect (view, SIGNAL (mouseClick(int)), 0, 0); + disconnect (view, SIGNAL (mouseLetGo(int)), 0, 0); + + if (newLevel) { + hero->setEnemyList (&enemies); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) + enemy->setEnemyList(&enemies); + } + + hero->setNuggets(nuggets); + setTimings(); + + // Set direction-flags to use during enemy searches. + initSearchMatrix(); + + // Re-draw the playfield frame, level title and figures. + view->setTitle (getTitle()); + view->updateCanvas(); + + // Check if this is a tutorial collection and we are not on the "ENDE" screen. + if ((collection->prefix.left(4) == "tute") && (levelNo != 0)) { + // At the start of a tutorial, put out an introduction. + if (levelNo == 1) + myMessage (view, collection->name, + i18n((const char *) collection->about.utf8())); + + // Put out an explanation of this level. + myMessage (view, getTitle(), levelHint); + } + + // Put the mouse pointer on the hero. + if (mouseMode) + view->setMousePos (startI, startJ); + + // Connect play-mode slot to signal from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doDig(int))); + + // Re-enable player input. + loading = FALSE; + + return 1; +} + +bool KGrGame::openLevelFile (int levelNo, QFile & openlevel) +{ + QString filePath; + QString msg; + + filePath = getFilePath (owner, collection, levelNo); + + openlevel.setName (filePath); + + // gucken ob und welcher Level existiert + + if (! openlevel.exists()) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot find file '%1'. Please make sure '%2' has been " + "run in the '%3' folder.") + .arg(filePath).arg("tar xf levels.tar").arg(systemDataDir.myStr())); + return (FALSE); + } + + // ffne Level zum lesen + if (! openlevel.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Level"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + return (TRUE); +} + +void KGrGame::changeObject (unsigned char kind, int i, int j) +{ + delete playfield[i][j]; + switch(kind) { + case FREE: createObject(new KGrFree (FREE,i,j,view),FREE,i,j);break; + case LADDER: createObject(new KGrObject (LADDER),LADDER,i,j);break; + case HLADDER: createObject(new KGrHladder (HLADDER,i,j,view),FREE,i,j);break; + case BRICK: createObject(new KGrBrick (BRICK,i,j,view),BRICK,i,j);break; + case BETON: createObject(new KGrObject (BETON),BETON,i,j);break; + case FBRICK: createObject(new KGrObject (FBRICK),BRICK,i,j);break; + case POLE: createObject(new KGrObject (POLE),POLE,i,j);break; + case NUGGET: createObject(new KGrFree (NUGGET,i,j,view),NUGGET,i,j); + nuggets++;break; + case HERO: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + hero->init(i,j); + startI = i; startJ = j; + hero->started = FALSE; + hero->showFigure(); + break; + case ENEMY: createObject(new KGrFree (FREE,i,j,view),FREE,i,j); + if (newLevel){ + // Starting a level for the first time. + enemy = new KGrEnemy (view, i, j); + enemy->setPlayfield(&playfield); + enemy->enemyId = enemyCount++; + enemies.append(enemy); + connect(enemy, SIGNAL(lostNugget()), SLOT(loseNugget())); + connect(enemy, SIGNAL(trapped(int)), SLOT(incScore(int))); + connect(enemy, SIGNAL(killed(int)), SLOT(incScore(int))); + } else { + // Starting a level again after losing. + enemy=enemies.at(enemyCount); + enemy->enemyId=enemyCount++; + enemy->setNuggets(0); + enemy->init(i,j); // Re-initialise the enemy's state information. + } + enemy->showFigure(); + break; + default : createObject(new KGrBrick(BRICK,i,j,view),BRICK,i,j);break; + } +} + +void KGrGame::createObject (KGrObject *o, char picType, int x, int y) +{ + playfield[x][y] = o; + view->paintCell (x, y, picType); // Pic maybe not same as object. +} + +void KGrGame::setTimings () +{ + Timing * timing; + int c = -1; + + if (KGrFigure::variableTiming) { + c = enemies.count(); // Timing based on enemy count. + c = (c > 5) ? 5 : c; + timing = &(KGrFigure::varTiming[c]); + } + else { + timing = &(KGrFigure::fixedTiming); // Fixed timing. + } + + KGrHero::WALKDELAY = timing->hwalk; + KGrHero::FALLDELAY = timing->hfall; + KGrEnemy::WALKDELAY = timing->ewalk; + KGrEnemy::FALLDELAY = timing->efall; + KGrEnemy::CAPTIVEDELAY = timing->ecaptive; + KGrBrick::HOLETIME = timing->hole; +} + +void KGrGame::initSearchMatrix() +{ + // Called at start of level and also when hidden ladders appear. + int i,j; + + for (i=1;i<21;i++){ + for (j=1;j<29;j++) + { + // If on ladder, can walk L, R, U or D. + if (playfield[j][i]->whatIam()==LADDER) + playfield[j][i]->searchValue = CANWALKLEFT + CANWALKRIGHT + + CANWALKUP + CANWALKDOWN; + else + // If on solid ground, can walk L or R. + if ((playfield[j][i+1]->whatIam()==BRICK)|| + (playfield[j][i+1]->whatIam()==HOLE)|| + (playfield[j][i+1]->whatIam()==USEDHOLE)|| + (playfield[j][i+1]->whatIam()==BETON)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT; + else + // If on pole or top of ladder, can walk L, R or D. + if ((playfield[j][i]->whatIam()==POLE)|| + (playfield[j][i+1]->whatIam()==LADDER)) + playfield[j][i]->searchValue=CANWALKLEFT+CANWALKRIGHT+CANWALKDOWN; + else + // Otherwise, gravity takes over ... + playfield[j][i]->searchValue=CANWALKDOWN; + + // Clear corresponding bits if there are solids to L, R, U or D. + if(playfield[j][i-1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKUP; + if(playfield[j-1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKLEFT; + if(playfield[j+1][i]->blocker) + playfield[j][i]->searchValue &= ~CANWALKRIGHT; + if(playfield[j][i+1]->blocker) + playfield[j][i]->searchValue &= ~CANWALKDOWN; + } + } +} + +void KGrGame::startPlaying () { + if (! hero->started) { + // Start the enemies and the hero. + for (--enemyCount; enemyCount>=0; --enemyCount) { + enemy=enemies.at(enemyCount); + enemy->startSearching(); + } + hero->start(); + } +} + +QString KGrGame::getFilePath (Owner o, KGrCollection * colln, int lev) +{ + QString filePath; + + if (lev == 0) { + // End of game: show the "ENDE" screen. + o = SYSTEM; + filePath = "level000.grl"; + } + else { + filePath.setNum (lev); // Convert INT -> QString. + filePath = filePath.rightJustify (3,'0'); // Add 0-2 zeros at left. + filePath.append (".grl"); // Add KGoldrunner level-suffix. + filePath.prepend (colln->prefix); // Add collection file-prefix. + } + + filePath.prepend (((o == SYSTEM)? systemDataDir : userDataDir) + "levels/"); + + return (filePath); +} + +QString KGrGame::getTitle() +{ + QString levelTitle; + if (level == 0) { + // Generate a special title: end of game or creating a new level. + if (! editMode) + levelTitle = "E N D --- F I N --- E N D E"; + else + levelTitle = i18n("New Level"); + } + else { + // Generate title string "Collection-name - NNN - Level-name". + levelTitle.setNum (level); + levelTitle = levelTitle.rightJustify (3,'0'); + levelTitle = collection->name + " - " + levelTitle; + if (levelName.length() > 0) { + levelTitle = levelTitle + " - " + levelName; + } + } + return (levelTitle); +} + +void KGrGame::readMousePos() +{ + QPoint p; + int i, j; + + // If loading a level for play or editing, ignore mouse-position input. + if (loading) return; + + // If game control is currently by keyboard, ignore the mouse. + if ((! mouseMode) && (! editMode)) return; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + if (editMode) { + // Editing - check if we are in paint mode and have moved the mouse. + if (paintEditObj && ((i != oldI) || (j != oldJ))) { + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + } + } + else { + // Playing - if the level has started, control the hero. + if (KGrObject::frozen) return; // If game is stopped, do nothing. + + hero->setDirection (i, j); + + // Start playing when the mouse moves off the hero. + if ((! hero->started) && ((i != startI) || (j != startJ))) { + startPlaying(); + } + } +} + +void KGrGame::doDig (int button) { + + // If game control is currently by keyboard, ignore the mouse. + if (editMode) return; + if (! mouseMode) return; + + // If loading a level for play or editing, ignore mouse-button input. + if ((! loading) && (! KGrObject::frozen)) { + if (! hero->started) { + startPlaying(); // If first player-input, start playing. + } + switch (button) { + case LeftButton: hero->digLeft (); break; + case RightButton: hero->digRight (); break; + default: break; + } + } +} + +void KGrGame::heroAction (KBAction movement) +{ + switch (movement) { + case KB_UP: hero->setKey (UP); break; + case KB_DOWN: hero->setKey (DOWN); break; + case KB_LEFT: hero->setKey (LEFT); break; + case KB_RIGHT: hero->setKey (RIGHT); break; + case KB_STOP: hero->setKey (STAND); break; + case KB_DIGLEFT: hero->setKey (STAND); hero->digLeft (); break; + case KB_DIGRIGHT: hero->setKey (STAND); hero->digRight (); break; + } +} + +/******************************************************************************/ +/************************** SAVE AND RE-LOAD GAMES **************************/ +/******************************************************************************/ + +void KGrGame::saveGame() // Save game ID, score and level. +{ + if (editMode) {myMessage (view, i18n("Save Game"), + i18n("Sorry, you cannot save your game play while you are editing. " + "Please try menu item %1.").arg("\"" + i18n("&Save Edits...") + "\"")); + return; + } + if (hero->started) {myMessage (view, i18n("Save Game"), + i18n("Please note: for reasons of simplicity, your saved game " + "position and score will be as they were at the start of this " + "level, not as they are now.")); + } + + QDate today = QDate::currentDate(); + QTime now = QTime::currentTime(); + QString saved; + QString day; +#ifdef QT3 + day = today.shortDayName(today.dayOfWeek()); +#else + day = today.dayName(today.dayOfWeek()); +#endif + saved = saved.sprintf + ("%-6s %03d %03ld %7ld %s %04d-%02d-%02d %02d:%02d\n", + collection->prefix.myStr(), level, lives, startScore, + day.myStr(), + today.year(), today.month(), today.day(), + now.hour(), now.minute()); + + QFile file1 (userDataDir + "savegame.dat"); + QFile file2 (userDataDir + "savegame.tmp"); + + if (! file2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "savegame.tmp")); + return; + } + QTextStream text2 (&file2); + text2 << saved; + + if (file1.exists()) { + if (! file1.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Save Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + QTextStream text1 (&file1); + int n = 30; // Limit the file to the last 30 saves. + while ((! text1.endData()) && (--n > 0)) { + saved = text1.readLine() + "\n"; + text2 << saved; + } + file1.close(); + } + + file2.close(); + + QDir dir; + dir.rename (file2.name(), file1.name(), TRUE); + KGrMessage::information (view, i18n("Save Game"), + i18n("Your game has been saved.")); +} + +void KGrGame::loadGame() // Re-load game, score and level. +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + QFile savedGames (userDataDir + "savegame.dat"); + if (! savedGames.exists()) { + // Use myMessage() because it stops the game while the message appears. + myMessage (view, i18n("Load Game"), + i18n("Sorry, there are no saved games.")); + return; + } + + if (! savedGames.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot open file '%1' for read-only.") + .arg(userDataDir + "savegame.dat")); + return; + } + + // Halt the game during the loadGame() dialog. + modalFreeze = FALSE; + if (!KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + QString s; + + KGrLGDialog * lg = new KGrLGDialog (&savedGames, collections, + view, "loadDialog"); + + if (lg->exec() == QDialog::Accepted) { + s = lg->getCurrentText(); + } + + bool found = FALSE; + QString pr; + int lev; + int i; + int imax = collections.count(); + + if (! s.isNull()) { + pr = s.mid (21, 7); // Get the collection prefix. + pr = pr.left (pr.find (" ", 0, FALSE)); + + for (i = 0; i < imax; i++) { // Find the collection. + if (collections.at(i)->prefix == pr) { + collection = collections.at(i); + collnIndex = i; + owner = collections.at(i)->owner; + found = TRUE; + break; + } + } + if (found) { + // Set the rules for the selected game. + emit markRuleType (collection->settings); + lev = s.mid (28, 3).toInt(); + newGame (lev, collnIndex); // Re-start the selected game. + lives = s.mid (32, 3).toLong(); // Update the lives. + emit showLives (lives); + score = s.mid (36, 7).toLong(); // Update the score. + emit showScore (score); + } + else { + KGrMessage::information (view, i18n("Load Game"), + i18n("Cannot find the game with prefix '%1'.").arg(pr)); + } + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete lg; +} + +/******************************************************************************/ +/************************** HIGH-SCORE PROCEDURES ***************************/ +/******************************************************************************/ + +void KGrGame::checkHighScore() +{ + bool prevHigh = TRUE; + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + QString thisUser = i18n("Unknown"); + int highCount = 0; + + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") + return; + + if (score <= 0) + return; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + prevHigh = FALSE; + } + } + + // If a previous high score file exists, check the current score against it. + if (prevHigh) { + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + // Read previous users, levels and scores from the high score file. + s1.setDevice (&high1); + bool found = FALSE; + highCount = 0; + while (! s1.endData()) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + delete prevUser; + delete prevDate; + highCount++; + if (score > prevScore) { + found = TRUE; // We have a high score. + break; + } + } + + // Check if higher than one on file or fewer than 10 previous scores. + if ((! found) && (highCount >= 10)) { + return; // We did not have a high score. + } + } + + /* ************************************************************* */ + /* If we have come this far, we have a new high score to record. */ + /* ************************************************************* */ + + QFile high2 (userDataDir + "hi_" + collection->prefix + ".tmp"); + QDataStream s2; + + if (! high2.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Check for High Score"), + i18n("Cannot open file '%1' for output.") + .arg(userDataDir + "hi_" + collection->prefix + ".tmp")); + return; + } + + // Dialog to ask the user to enter their name. + QDialog * hsn = new QDialog (view, "hsNameDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hsn, margin, spacing); + + QLabel * hsnMessage = new QLabel ( + i18n("<b>Congratulations !!!</b> " + "You have achieved a high " + "score in this game. Please enter your name so that " + "it may be enshrined in the KGoldrunner Hall of Fame."), + hsn); + QLineEdit * hsnUser = new QLineEdit (hsn); + QPushButton * OK = new KPushButton (KStdGuiItem::ok(), hsn); + + mainLayout-> addWidget (hsnMessage); + mainLayout-> addWidget (hsnUser); + mainLayout-> addWidget (OK); + + hsn-> setCaption (i18n("Save High Score")); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hsn-> move (p.x() + 50, p.y() + 50); + + OK-> setAccel (Key_Return); + hsnUser-> setFocus(); // Set the keyboard input on. + + connect (hsnUser, SIGNAL (returnPressed ()), hsn, SLOT (accept ())); + connect (OK, SIGNAL (clicked ()), hsn, SLOT (accept ())); + + while (TRUE) { + hsn->exec(); + thisUser = hsnUser->text(); + if (thisUser.length() > 0) + break; + KGrMessage::information (view, i18n("Save High Score"), + i18n("You must enter something. Please try again.")); + } + + delete hsn; + + QDate today = QDate::currentDate(); + QString hsDate; +#ifdef QT3 + QString day = today.shortDayName(today.dayOfWeek()); +#else + QString day = today.dayName(today.dayOfWeek()); +#endif + hsDate = hsDate.sprintf + ("%s %04d-%02d-%02d", + day.myStr(), + today.year(), today.month(), today.day()); + + s2.setDevice (&high2); + + if (prevHigh) { + high1.reset(); + bool scoreRecorded = FALSE; + highCount = 0; + while ((! s1.endData()) && (highCount < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + if ((! scoreRecorded) && (score > prevScore)) { + highCount++; + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + scoreRecorded = TRUE; + } + if (highCount < 10) { + highCount++; + s2 << prevUser; + s2 << prevLevel; + s2 << prevScore; + s2 << prevDate; + } + delete prevUser; + delete prevDate; + } + if ((! scoreRecorded) && (highCount < 10)) { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + high1.close(); + } + else { + // Recode the user's name as UTF-8, in case it contains + // non-ASCII chars (e.g. "Krger" is encoded as "Krüger"). + s2 << (const char *) thisUser.utf8(); + s2 << (Q_INT16) level; + s2 << (Q_INT32) score; + s2 << hsDate.myStr(); + } + + high2.close(); + + QDir dir; + dir.rename (high2.name(), + userDataDir + "hi_" + collection->prefix + ".dat", TRUE); + KGrMessage::information (view, i18n("Save High Score"), + i18n("Your high score has been saved.")); + + showHighScores(); + return; +} + +void KGrGame::showHighScores() +{ + // Don't keep high scores for tutorial games. + if (collection->prefix.left(4) == "tute") { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, we do not keep high scores for tutorial games.")); + return; + } + + Q_INT16 prevLevel = 0; + Q_INT32 prevScore = 0; + int n = 0; + + // Look for user's high-score file or for a released high-score file. + QFile high1 (userDataDir + "hi_" + collection->prefix + ".dat"); + QDataStream s1; + + if (! high1.exists()) { + high1.setName (systemDataDir + "hi_" + collection->prefix + ".dat"); + if (! high1.exists()) { + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Sorry, there are no high scores for the %1 game yet.") + .arg("\"" + collection->name + "\"")); + return; + } + } + + if (! high1.open (IO_ReadOnly)) { + QString high1_name = high1.name(); + KGrMessage::information (view, i18n("Show High Scores"), + i18n("Cannot open file '%1' for read-only.").arg(high1_name)); + return; + } + + QDialog * hs = new QDialog (view, "hsDialog", TRUE, + WStyle_Customize | WStyle_NormalBorder | WStyle_Title); + + int margin = 10; + int spacing = 10; + QVBoxLayout * mainLayout = new QVBoxLayout (hs, margin, spacing); + + QLabel * hsHeader = new QLabel (i18n ( + "<center><h2>KGoldrunner Hall of Fame</h2></center><br>" + "<center><h3>\"%1\" Game</h3></center>") + .arg(collection->name), + hs); + QLabel * hsColHeader = new QLabel ( + i18n(" Name " + "Level Score Date"), hs); +#ifdef KGR_PORTABLE + QFont f ("courier", 12); +#else + QFont f = KGlobalSettings::fixedFont(); // KDE version. +#endif + f. setFixedPitch (TRUE); + f. setBold (TRUE); + hsColHeader-> setFont (f); + + QLabel * hsLine [10]; + + QHBox * buttons = new QHBox (hs); + buttons-> setSpacing (spacing); + QPushButton * OK = new KPushButton (KStdGuiItem::close(), buttons); + + mainLayout-> addWidget (hsHeader); + mainLayout-> addWidget (hsColHeader); + + hs-> setCaption (i18n("High Scores")); + + OK-> setAccel (Key_Return); + + // Set up the format for the high-score lines. + f. setBold (FALSE); + QString line; + const char * hsFormat = "%2d. %-30.30s %3d %7ld %s"; + + // Read and display the users, levels and scores from the high score file. + s1.setDevice (&high1); + n = 0; + while ((! s1.endData()) && (n < 10)) { + char * prevUser; + char * prevDate; + s1 >> prevUser; + s1 >> prevLevel; + s1 >> prevScore; + s1 >> prevDate; + + // QString::sprintf expects UTF-8 encoding in its string arguments, so + // prevUser has been saved on file as UTF-8 to allow non=ASCII chars + // in the user's name (e.g. "Krger" is encoded as "Krüger" in UTF-8). + + line = line.sprintf (hsFormat, + n+1, prevUser, prevLevel, prevScore, prevDate); + hsLine [n] = new QLabel (line, hs); + hsLine [n]->setFont (f); + mainLayout->addWidget (hsLine [n]); + + delete prevUser; + delete prevDate; + n++; + } + + QFrame * separator = new QFrame (hs); + separator->setFrameStyle (QFrame::HLine + QFrame::Sunken); + mainLayout->addWidget (separator); + + OK-> setMaximumWidth (100); + mainLayout-> addWidget (buttons); + + QPoint p = view->mapToGlobal (QPoint (0,0)); + hs-> move (p.x() + 50, p.y() + 50); + + // Start up the dialog box. + connect (OK, SIGNAL (clicked ()), hs, SLOT (accept ())); + hs-> exec(); + + delete hs; +} + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +void KGrGame::doStep() +{ + if (KGrObject::frozen) { // The game must have been halted. + restart(); // Do one step and halt again. + } +} + +void KGrGame::restart() +{ + bool temp; + int i,j; + + if (editMode) // Can't move figures when in Edit Mode. + return; + + temp = KGrObject::frozen; + + KGrObject::frozen = FALSE; // Temporarily restart the game, by re-running + // any timer events that have been blocked. + + readMousePos(); // Set hero's direction. + hero->doStep(); // Move the hero one step. + + j = enemies.count(); // Move each enemy one step. + for (i = 0; i < j; i++) { + enemy = enemies.at(i); // Need to use an index because called methods + enemy->doStep(); // change the "current()" of the "enemies" list. + } + + for (i=1; i<=28; i++) + for (j=1; j<=20; j++) { + if ((playfield[i][j]->whatIam() == HOLE) || + (playfield[i][j]->whatIam() == USEDHOLE) || + (playfield[i][j]->whatIam() == BRICK)) + ((KGrBrick *)playfield[i][j])->doStep(); + } + + KGrObject::frozen = temp; // If frozen was TRUE, halt again, which gives a + // single-step effect, otherwise go on running. +} + +void KGrGame::showFigurePositions() +{ + if (KGrObject::frozen) { + hero->showState('p'); + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + enemy->showState('p'); + } + } +} + +void KGrGame::showHeroState() +{ + if (KGrObject::frozen) { + hero->showState('s'); + } +} + +void KGrGame::showEnemyState(int enemyId) +{ + if (KGrObject::frozen) { + for (enemy=enemies.first();enemy != 0; enemy = enemies.next()) { + if (enemy->enemyId == enemyId) enemy->showState('s'); + } + } +} + +void KGrGame::showObjectState() +{ + QPoint p; + int i, j; + KGrObject * myObject; + + if (KGrObject::frozen) { + p = view->getMousePos (); + i = p.x(); j = p.y(); + myObject = playfield[i][j]; + switch (myObject->whatIam()) { + case BRICK: + case HOLE: + case USEDHOLE: + ((KGrBrick *)myObject)->showState(i, j); break; + default: myObject->showState(i, j); break; + } + } +} + +void KGrGame::bugFix() +{ + if (KGrObject::frozen) { // Toggle a bug fix on/off dynamically. + KGrObject::bugFixed = (KGrObject::bugFixed) ? FALSE : TRUE; + printf ("%s", (KGrObject::bugFixed) ? "\n" : ""); + printf (">>> Bug fix is %s\n", (KGrObject::bugFixed) ? "ON" : "OFF\n"); + } +} + +void KGrGame::startLogging() +{ + if (KGrObject::frozen) { // Toggle logging on/off dynamically. + KGrObject::logging = (KGrObject::logging) ? FALSE : TRUE; + printf ("%s", (KGrObject::logging) ? "\n" : ""); + printf (">>> Logging is %s\n", (KGrObject::logging) ? "ON" : "OFF\n"); + } +} + +/******************************************************************************/ +/************ GAME EDITOR FUNCTIONS ACTIVATED BY MENU OR TOOLBAR ************/ +/******************************************************************************/ + +void KGrGame::setEditObj (char newEditObj) +{ + editObj = newEditObj; +} + +void KGrGame::createLevel() +{ + int i, j; + + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Create Level"), + i18n("You cannot create and save a level " + "until you have created a game to hold " + "it. Try menu item \"Create Game\".")); + return; + } + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = 0; + initEdit(); + levelName = ""; + levelHint = ""; + + // Clear the playfield. + editObj = FREE; + for (i = 1; i <= FIELDWIDTH; i++) + for (j = 1; j <= FIELDHEIGHT; j++) { + insertEditObj (i, j); + editObjArray[i][j] = editObj; + } + + editObj = HERO; + insertEditObj (1, 1); + editObjArray[1][1] = editObj; + editObj = BRICK; + + showEditLevel(); + + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Re-enable player input. + loading = FALSE; + + view->updateCanvas(); // Show the edit area. + view->update(); // Show the level name. +} + +void KGrGame::updateLevel() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("You cannot edit and save a level until you " + "have created a game and a level. Try menu item \"Create Game\".")); + return; + } + + if (level < 0) level = 0; + int lev = selectLevel (SL_UPDATE, level); + if (lev == 0) + return; + + if (owner == SYSTEM) { + KGrMessage::information (view, i18n("Edit Level"), + i18n("It is OK to edit a system level, but you MUST save " + "the level in one of your own games. You're not just " + "taking a peek at the hidden ladders " + "and fall-through bricks, are you? :-)")); + } + + loadEditLevel (lev); +} + +void KGrGame::updateNext() +{ + if (! saveOK (FALSE)) { // Check unsaved work. + return; + } + level++; + updateLevel(); +} + +void KGrGame::loadEditLevel (int lev) +{ + int i, j; + QFile levelFile; + + if (! openLevelFile (lev, levelFile)) + return; + + // Ignore player input from keyboard or mouse while the screen is set up. + loading = TRUE; + + level = lev; + initEdit(); + + // Load the level. + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { + editObj = levelFile.getch (); + insertEditObj (i, j); + editObjArray[i][j] = editObj; + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + + // Read a newline character, then read in the level name and hint (if any). + int c = levelFile.getch(); + QCString levelHintC = ""; + QCString levelNameC = ""; + levelHint = ""; + levelName = ""; + i = 1; + while ((c = levelFile.getch()) != EOF) { + switch (i) { + case 1: if (c == '\n') // Level name is on one line. + i = 2; + else + levelNameC += (char) c; + break; + + case 2: levelHintC += (char) c; // Hint is on rest of file. + break; + } + } + + // Retain the original language of the name and hint when editing, + // but remove the final \n and convert non-ASCII, UTF-8 substrings + // to Unicode (eg. ü to ). + int len = levelHintC.length(); + if (len > 0) + levelHint = QString::fromUtf8((const char *) levelHintC.left(len-1)); + + len = levelNameC.length(); + if (len > 0) + levelName = QString::fromUtf8((const char *) levelNameC); + + editObj = BRICK; // Reset default object. + levelFile.close (); + + view->setTitle (getTitle()); // Show the level name. + view->updateCanvas(); // Show the edit area. + showEditLevel(); // Reconnect signals. + + // Re-enable player input. + loading = FALSE; +} + +void KGrGame::editNameAndHint() +{ + if (! editMode) + return; + + // Run a dialog box to create/edit the level name and hint. + KGrNHDialog * nh = new KGrNHDialog (levelName, levelHint, view, "NHDialog"); + + if (nh->exec() == QDialog::Accepted) { + levelName = nh->getName(); + levelHint = nh->getHint(); + shouldSave = TRUE; + } + + delete nh; +} + +bool KGrGame::saveLevelFile() +{ + bool isNew; + int action; + int selectedLevel = level; + + int i, j; + QString filePath; + + if (! editMode) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Inappropriate action: you are not editing a level.")); + return (FALSE); + } + + // Save the current collection index. + int N = collnIndex; + + if (selectedLevel == 0) { + // New level: choose a number. + action = SL_CREATE; + } + else { + // Existing level: confirm the number or choose a new number. + action = SL_SAVE; + } + + // Pop up dialog box, which could change the collection or level or both. + selectedLevel = selectLevel (action, selectedLevel); + if (selectedLevel == 0) + return (FALSE); + + // Get the new collection (if changed). + int n = collnIndex; + + // Set the name of the output file. + filePath = getFilePath (owner, collection, selectedLevel); + QFile levelFile (filePath); + + if ((action == SL_SAVE) && (n == N) && (selectedLevel == level)) { + // This is a normal edit: the old file is to be re-written. + isNew = FALSE; + } + else { + isNew = TRUE; + // Check if the file is to be inserted in or appended to the collection. + if (levelFile.exists()) { + switch (KGrMessage::warning (view, i18n("Save Level"), + i18n("Do you want to insert a level and " + "move existing levels up by one?"), + i18n("&Insert Level"), i18n("&Cancel"))) { + + case 0: if (! reNumberLevels (n, selectedLevel, + collections.at(n)->nLevels, +1)) { + return (FALSE); + } + break; + case 1: return (FALSE); + break; + } + } + } + + // Open the output file. + if (! levelFile.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the level. + for (j = 1; j < 21; j++) + for (i = 1; i < 29; i++) { + levelFile.putch (editObjArray[i][j]); + lastSaveArray[i][j] = editObjArray[i][j]; // Copy for "saveOK()". + } + levelFile.putch ('\n'); + + // Save the level name, changing non-ASCII chars to UTF-8 (eg. to ü). + QCString levelNameC = levelName.utf8(); + int len1 = levelNameC.length(); + if (len1 > 0) { + for (i = 0; i < len1; i++) + levelFile.putch (levelNameC[i]); + levelFile.putch ('\n'); // Add a newline. + } + + // Save the level hint, changing non-ASCII chars to UTF-8 (eg. to ü). + QCString levelHintC = levelHint.utf8(); + int len2 = levelHintC.length(); + char ch = '\0'; + + if (len2 > 0) { + if (len1 <= 0) + levelFile.putch ('\n'); // Leave blank line for name. + for (i = 0; i < len2; i++) { + ch = levelHintC[i]; + levelFile.putch (ch); // Copy the character. + } + if (ch != '\n') + levelFile.putch ('\n'); // Add a newline character. + } + + levelFile.close (); + shouldSave = FALSE; + + if (isNew) { + collections.at(n)->nLevels++; + saveCollections (owner); + } + + level = selectedLevel; + emit showLevel (level); + view->setTitle (getTitle()); // Display new title. + view->updateCanvas(); // Show the edit area. + return (TRUE); +} + +void KGrGame::moveLevelFile () +{ + if (level <= 0) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must first load a level to be moved. Use " + "the %1 or %2 menu.") + .arg("\"" + i18n("Game") + "\"") + .arg("\"" + i18n("Editor") + "\"")); + return; + } + + int action = SL_MOVE; + + int fromC = collnIndex; + int fromL = level; + int toC = fromC; + int toL = fromL; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You cannot move a level until you " + "have created a game and at least two levels. Try " + "menu item \"Create Game\".")); + return; + } + + if (collections.at(fromC)->owner != USER) { + KGrMessage::information (view, i18n("Move Level"), + i18n("Sorry, you cannot move a system level.")); + return; + } + + // Pop up dialog box to get the collection and level number to move to. + while ((toC == fromC) && (toL == fromL)) { + toL = selectLevel (action, toL); + if (toL == 0) + return; + + toC = collnIndex; + + if ((toC == fromC) && (toL == fromL)) { + KGrMessage::information (view, i18n("Move Level"), + i18n("You must change the level or the game or both.")); + } + } + + QDir dir; + QString filePath1; + QString filePath2; + + // Save the "fromN" file under a temporary name. + filePath1 = getFilePath (USER, collections.at(fromC), fromL); + filePath2 = filePath1; + filePath2 = filePath2.append (".tmp"); + dir.rename (filePath1, filePath2, TRUE); + + if (toC == fromC) { // Same collection. + if (toL < fromL) { // Decrease level. + // Move "toL" to "fromL - 1" up by 1. + if (! reNumberLevels (toC, toL, fromL-1, +1)) { + return; + } + } + else { // Increase level. + // Move "fromL + 1" to "toL" down by 1. + if (! reNumberLevels (toC, fromL+1, toL, -1)) { + return; + } + } + } + else { // Different collection. + // In "fromC", move "fromL + 1" to "nLevels" down and update "nLevels". + if (! reNumberLevels (fromC, fromL + 1, + collections.at(fromC)->nLevels, -1)) { + return; + } + collections.at(fromC)->nLevels--; + + // In "toC", move "toL + 1" to "nLevels" up and update "nLevels". + if (! reNumberLevels (toC, toL, collections.at(toC)->nLevels, +1)) { + return; + } + collections.at(toC)->nLevels++; + + saveCollections (USER); + } + + // Rename the saved "fromL" file to become "toL". + filePath1 = getFilePath (USER, collections.at(toC), toL); + dir.rename (filePath2, filePath1, TRUE); + + level = toL; + collection = collections.at(toC); + view->setTitle (getTitle()); // Re-write title. + view->updateCanvas(); // Re-display details of level. + emit showLevel (level); +} + +void KGrGame::deleteLevelFile () +{ + int action = SL_DELETE; + int lev = level; + + if (! ownerOK (USER)) { + KGrMessage::information (view, i18n("Delete Level"), + i18n("You cannot delete a level until you " + "have created a game and a level. Try " + "menu item \"Create Game\".")); + return; + } + + // Pop up dialog box to get the collection and level number. + lev = selectLevel (action, level); + if (lev == 0) + return; + + QString filePath; + + // Set the name of the file to be deleted. + int n = collnIndex; + filePath = getFilePath (USER, collections.at(n), lev); + QFile levelFile (filePath); + + // Delete the file for the selected collection and level. + if (levelFile.exists()) { + if (lev < collections.at(n)->nLevels) { + switch (KGrMessage::warning (view, i18n("Delete Level"), + i18n("Do you want to delete a level and " + "move higher levels down by one?"), + i18n("&Delete Level"), i18n("&Cancel"))) { + case 0: break; + case 1: return; break; + } + levelFile.remove (); + if (! reNumberLevels (n, lev + 1, collections.at(n)->nLevels, -1)) { + return; + } + } + else { + levelFile.remove (); + } + } + else { + KGrMessage::information (view, i18n("Delete Level"), + i18n("Cannot find file '%1' to be deleted.").arg(filePath)); + return; + } + + collections.at(n)->nLevels--; + saveCollections (USER); + if (lev <= collections.at(n)->nLevels) { + level = lev; + } + else { + level = collections.at(n)->nLevels; + } + + // Repaint the screen with the level that now has the selected number. + if (editMode && (level > 0)) { + loadEditLevel (level); // Load level in edit mode. + } + else if (level > 0) { + enemyCount = 0; // Load level in play mode. + enemies.clear(); + view->deleteEnemySprites(); + newLevel = TRUE;; + loadLevel (level); + newLevel = FALSE; + } + else { + createLevel(); // No levels left in collection. + } + emit showLevel (level); +} + +void KGrGame::editCollection (int action) +{ + int lev = level; + int n = -1; + + // If editing, choose a collection. + if (action == SL_UPD_GAME) { + lev = selectLevel (SL_UPD_GAME, level); + if (lev == 0) + return; + level = lev; + n = collnIndex; + } + + KGrECDialog * ec = new KGrECDialog (action, n, collections, + view, "editGameDialog"); + + while (ec->exec() == QDialog::Accepted) { // Loop until valid. + + // Validate the collection details. + QString ecName = ec->getName(); + int len = ecName.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a name for the game.")); + continue; + } + + QString ecPrefix = ec->getPrefix(); + if ((action == SL_CR_GAME) || (collections.at(n)->nLevels <= 0)) { + // The filename prefix could have been entered, so validate it. + len = ecPrefix.length(); + if (len == 0) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You must enter a filename prefix for the game.")); + continue; + } + if (len > 5) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should not " + "be more than 5 characters.")); + continue; + } + + bool allAlpha = TRUE; + for (int i = 0; i < len; i++) { + if (! isalpha(ecPrefix.myChar(i))) { + allAlpha = FALSE; + break; + } + } + if (! allAlpha) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix should be " + "all alphabetic characters.")); + continue; + } + + bool duplicatePrefix = FALSE; + KGrCollection * c; + int imax = collections.count(); + for (int i = 0; i < imax; i++) { + c = collections.at(i); + if ((c->prefix == ecPrefix) && (i != n)) { + duplicatePrefix = TRUE; + break; + } + } + + if (duplicatePrefix) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("The filename prefix '%1' is already in use.") + .arg(ecPrefix)); + continue; + } + } + + // Save the collection details. + char settings = 'K'; + if (ec->isTrad()) { + settings = 'T'; + } + if (action == SL_CR_GAME) { + collections.append (new KGrCollection (USER, + ecName, ecPrefix, settings, 0, ec->getAboutText())); + } + else { + collection->name = ecName; + collection->prefix = ecPrefix; + collection->settings = settings; + collection->about = ec->getAboutText(); + } + + saveCollections (USER); + break; // All done now. + } + + delete ec; +} + +/******************************************************************************/ +/********************* SUPPORTING GAME EDITOR FUNCTIONS *********************/ +/******************************************************************************/ + +bool KGrGame::saveOK (bool exiting) +{ + int i, j; + bool result; + QString option2 = i18n("&Go on editing"); + + result = TRUE; + + if (editMode) { + if (exiting) { // If window is closing, + option2 = ""; // can't go on editing. + } + for (j = 1; j <= FIELDHEIGHT; j++) + for (i = 1; i <= FIELDWIDTH; i++) { // Check cell changes. + if ((shouldSave) || (editObjArray[i][j] != lastSaveArray[i][j])) { + // If shouldSave == TRUE, level name or hint was edited. + switch (KGrMessage::warning (view, i18n("Editor"), + i18n("You have not saved your work. Do " + "you want to save it now?"), + i18n("&Save"), i18n("&Don't Save"), option2)) { + case 0: result = saveLevelFile(); break;// Save and continue. + case 1: shouldSave = FALSE; break; // Continue: don't save. + case 2: result = FALSE; break; // Go back to editing. + } + return (result); + } + } + } + return (result); +} + +void KGrGame::initEdit() +{ + if (! editMode) { + + editMode = TRUE; + emit setEditMenu (TRUE); // Enable edit menu items and toolbar. + + // We were previously in play mode: stop the hero running or falling. + hero->init (1, 1); + view->setHeroVisible (FALSE); + } + + paintEditObj = FALSE; + + // Set the default object and button. + editObj = BRICK; + emit defaultEditObj(); // Set default edit-toolbar button. + + oldI = 0; + oldJ = 0; + heroCount = 0; + enemyCount = 0; + enemies.clear(); + view->deleteEnemySprites(); + nuggets = 0; + + emit showLevel (level); + emit showLives (0); + emit showScore (0); + + deleteLevel(); + setBlankLevel(FALSE); // Fill playfield with Editable objects. + + view->setTitle (getTitle());// Show title of level. + view->updateCanvas(); // Show the edit area. + + shouldSave = FALSE; // Used to flag editing of name or hint. +} + +void KGrGame::deleteLevel() +{ + int i,j; + for (i = 1; i <= FIELDHEIGHT; i++) + for (j = 1; j <= FIELDWIDTH; j++) + delete playfield[j][i]; +} + +void KGrGame::insertEditObj (int i, int j) +{ + if ((i < 1) || (j < 1) || (i > FIELDWIDTH) || (j > FIELDHEIGHT)) + return; // Do nothing: mouse pointer is out of playfield. + + if (editObjArray[i][j] == HERO) { + // The hero is in this cell: remove him. + editObjArray[i][j] = FREE; + heroCount = 0; + } + + if (editObj == HERO) { + if (heroCount != 0) { + // Can only have one hero: remove him from his previous position. + for (int m = 1; m <= FIELDWIDTH; m++) + for (int n = 1; n <= FIELDHEIGHT; n++) { + if (editObjArray[m][n] == HERO) { + setEditableCell (m, n, FREE); + } + } + } + heroCount = 1; + } + + setEditableCell (i, j, editObj); +} + +void KGrGame::setEditableCell (int i, int j, char type) +{ + ((KGrEditable *) playfield[i][j])->setType (type); + view->paintCell (i, j, type); + editObjArray[i][j] = type; +} + +void KGrGame::showEditLevel() +{ + // Disconnect play-mode slots from signals from "view". + disconnect (view, SIGNAL(mouseClick(int)), 0, 0); + disconnect (view, SIGNAL(mouseLetGo(int)), 0, 0); + + // Connect edit-mode slots to signals from "view". + connect (view, SIGNAL(mouseClick(int)), SLOT(doEdit(int))); + connect (view, SIGNAL(mouseLetGo(int)), SLOT(endEdit(int))); +} + +bool KGrGame::reNumberLevels (int cIndex, int first, int last, int inc) +{ + int i, n, step; + QDir dir; + QString file1, file2; + + if (inc > 0) { + i = last; + n = first - 1; + step = -1; + } + else { + i = first; + n = last + 1; + step = +1; + } + + while (i != n) { + file1 = getFilePath (USER, collections.at(cIndex), i); + file2 = getFilePath (USER, collections.at(cIndex), i - step); + if (! dir.rename (file1, file2, TRUE)) { // Allow absolute paths. + KGrMessage::information (view, i18n("Save Level"), + i18n("Cannot rename file '%1' to '%2'.") + .arg(file1).arg(file2)); + return (FALSE); + } + i = i + step; + } + + return (TRUE); +} + +void KGrGame::setLevel (int lev) +{ + level = lev; + return; +} + +/******************************************************************************/ +/********************* EDIT ACTION SLOTS **********************************/ +/******************************************************************************/ + +void KGrGame::doEdit (int button) +{ + // Mouse button down: start making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = TRUE; + insertEditObj (i, j); + view->updateCanvas(); + oldI = i; + oldJ = j; + break; + default: + break; + } +} + +void KGrGame::endEdit (int button) +{ + // Mouse button released: finish making changes. + QPoint p; + int i, j; + + p = view->getMousePos (); + i = p.x(); j = p.y(); + + switch (button) { + case LeftButton: + case RightButton: + paintEditObj = FALSE; + if ((i != oldI) || (j != oldJ)) { + insertEditObj (i, j); + view->updateCanvas(); + } + break; + default: + break; + } +} + +/******************************************************************************/ +/********************** LEVEL SELECTION DIALOG BOX **********************/ +/******************************************************************************/ + +int KGrGame::selectLevel (int action, int requestedLevel) +{ + int selectedLevel = 0; // 0 = no selection (Cancel) or invalid. + + // Halt the game during the dialog. + modalFreeze = FALSE; + if (! KGrObject::frozen) { + modalFreeze = TRUE; + freeze(); + } + + // Create and run a modal dialog box to select a game and level. + KGrSLDialog * sl = new KGrSLDialog (action, requestedLevel, collnIndex, + collections, this, view, "levelDialog"); + while (sl->exec() == QDialog::Accepted) { + selectedGame = sl->selectedGame(); + selectedLevel = 0; // In case the selection is invalid. + if (collections.at(selectedGame)->owner == SYSTEM) { + switch (action) { + case SL_CREATE: // Can save only in a USER collection. + case SL_SAVE: + case SL_MOVE: + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only save or move " + "into one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_DELETE: // Can delete only in a USER collection. + KGrMessage::information (view, i18n("Select Level"), + i18n("Sorry, you can only delete a level " + "from one of your own games.")); + continue; // Re-run the dialog box. + break; + case SL_UPD_GAME: // Can edit info only in a USER collection. + KGrMessage::information (view, i18n("Edit Game Info"), + i18n("Sorry, you can only edit the game " + "information on your own games.")); + continue; // Re-run the dialog box. + break; + default: + break; + } + } + + selectedLevel = sl->selectedLevel(); + if ((selectedLevel > collections.at (selectedGame)->nLevels) && + (action != SL_CREATE) && (action != SL_SAVE) && + (action != SL_MOVE) && (action != SL_UPD_GAME)) { + KGrMessage::information (view, i18n("Select Level"), + i18n("There is no level %1 in %2, " + "so you cannot play or edit it.") + .arg(selectedLevel) + .arg("\"" + collections.at(selectedGame)->name + "\"")); + selectedLevel = 0; // Set an invalid selection. + continue; // Re-run the dialog box. + } + + // If "OK", set the results. + collection = collections.at (selectedGame); + owner = collection->owner; + collnIndex = selectedGame; + // Set default rules for selected game. + emit markRuleType (collection->settings); + break; + } + + // Unfreeze the game, but only if it was previously unfrozen. + if (modalFreeze) { + unfreeze(); + modalFreeze = FALSE; + } + + delete sl; + return (selectedLevel); // 0 = cancelled or invalid. +} + +bool KGrGame::ownerOK (Owner o) +{ + // Check that this owner has at least one collection. + KGrCollection * c; + bool OK = FALSE; + + for (c = collections.first(); c != 0; c = collections.next()) { + if (c->owner == o) { + OK = TRUE; + break; + } + } + + return (OK); +} + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +KGrThumbNail::KGrThumbNail (QWidget * parent, const char * name) + : QFrame (parent, name) +{ + // Let the parent do all the work. We need a class here so that + // QFrame::drawContents (QPainter *) can be re-implemented and + // the thumbnail can be automatically re-painted when required. +} + +QColor KGrThumbNail::backgroundColor = QColor ("#dddddd"); +QColor KGrThumbNail::brickColor = QColor ("#ff0000"); +QColor KGrThumbNail::ladderColor = QColor ("#ddcc00"); +QColor KGrThumbNail::poleColor = QColor ("#aa7700"); + +void KGrThumbNail::setFilePath (QString & fp, QLabel * sln) +{ + filePath = fp; // Keep safe copies of file + lName = sln; // path and level name field. +} + +void KGrThumbNail::drawContents (QPainter * p) // Activated via "paintEvent". +{ + QFile openFile; + QPen pen = p->pen(); + char obj = FREE; + int fw = 1; // Set frame width. + int n = width() / FIELDWIDTH; // Set thumbnail cell-size. + + pen.setColor (backgroundColor); + p->setPen (pen); + + openFile.setName (filePath); + if ((! openFile.exists()) || (! openFile.open (IO_ReadOnly))) { + // There is no file, so fill the thumbnail with "FREE" cells. + p->drawRect (QRect(fw, fw, FIELDWIDTH*n, FIELDHEIGHT*n)); + return; + } + + for (int j = 0; j < FIELDHEIGHT; j++) + for (int i = 0; i < FIELDWIDTH; i++) { + + obj = openFile.getch(); + + // Set the colour of each object. + switch (obj) { + case BRICK: + case BETON: + case FBRICK: + pen.setColor (brickColor); p->setPen (pen); break; + case LADDER: + pen.setColor (ladderColor); p->setPen (pen); break; + case POLE: + pen.setColor (poleColor); p->setPen (pen); break; + case HERO: + pen.setColor (green); p->setPen (pen); break; + case ENEMY: + pen.setColor (blue); p->setPen (pen); break; + default: + // Set the background for FREE, HLADDER and NUGGET. + pen.setColor (backgroundColor); p->setPen (pen); break; + } + + // Draw nxn pixels as n lines of length n. + p->drawLine (i*n+fw, j*n+fw, i*n+(n-1)+fw, j*n+fw); + if (obj == POLE) { + // For a pole, only the top line is drawn in white. + pen.setColor (backgroundColor); + p->setPen (pen); + } + for (int k = 1; k < n; k++) { + p->drawLine (i*n+fw, j*n+k+fw, i*n+(n-1)+fw, j*n+k+fw); + } + + // For a nugget, add just a vertical touch of yellow (2-3 pixels). + if (obj == NUGGET) { + int k = (n/2)+fw; + // pen.setColor (QColor("#ffff00")); + pen.setColor (ladderColor); + p->setPen (pen); + p->drawLine (i*n+k, j*n+k, i*n+k, j*n+(n-1)+fw); + p->drawLine (i*n+k+1, j*n+k, i*n+k+1, j*n+(n-1)+fw); + } + } + + // Absorb a newline character, then read in the level name (if any). + int c = openFile.getch(); + QCString s = ""; + while ((c = openFile.getch()) != EOF) { + if (c == '\n') // Level name is on one line. + break; + s += (char) c; + } + if (s.length() > 0) // If there is a name, translate it. + lName->setText (i18n((const char *) s)); + else + lName->setText (""); + + openFile.close(); +} + +/******************************************************************************/ +/************************* COLLECTIONS HANDLING ***************************/ +/******************************************************************************/ + +// NOTE: Macros "myStr" and "myChar", defined in "kgrgame.h", are used +// to smooth out differences between Qt 1 and Qt2 QString classes. + +bool KGrGame::initCollections () +{ + // Initialise the list of collections of levels (i.e. the list of games). + collections.setAutoDelete(TRUE); + owner = SYSTEM; // Use system levels initially. + if (! loadCollections (SYSTEM)) // Load system collections list. + return (FALSE); // If no collections, abort. + loadCollections (USER); // Load user collections list. + // If none, don't worry. + + mapCollections(); // Check ".grl" file integrity. + + // Set the default collection (first one in the SYSTEM "games.dat" file). + collnIndex = 0; + collection = collections.at (collnIndex); + level = 1; // Default start is at level 1. + + return (TRUE); +} + +void KGrGame::mapCollections() +{ + QDir d; + KGrCollection * colln; + QString d_path; + QString fileName1; + QString fileName2; + + // Find KGoldrunner level files, sorted by name (same as numerical order). + for (colln = collections.first(); colln != 0; colln = collections.next()) { + d.setPath ((colln->owner == SYSTEM) ? systemDataDir + "levels/" + : userDataDir + "levels/"); + d_path = d.path(); + if (! d.exists()) { + // There is no "levels" sub-directory: OK if game has no levels yet. + if (colln->nLevels > 0) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There is no folder '%1' to hold levels for" + " the '%2' game. Please make sure '%3' " + "has been run in the '%4' folder.") + .arg(d_path) + .arg(colln->name) + .arg("tar xf levels.tar") + .arg(systemDataDir)); + } + continue; + } + + const QFileInfoList * files = d.entryInfoList + (colln->prefix + "???.grl", QDir::Files, QDir::Name); + QFileInfoListIterator i (* files); + QFileInfo * file; + + if ((files->count() <= 0) && (colln->nLevels > 0)) { + KGrMessage::information (view, i18n("Check Games & Levels"), + i18n("There are no files '%1/%2???.grl' for the %3 game.") + .arg(d_path) + .arg(colln->prefix) + .arg("\"" + colln->name + "\"")); + continue; + } + + // If the prefix is "level", the first file is the "ENDE" screen. + int lev = (colln->prefix == "level") ? 0 : 1; + + while ((file = i.current())) { + // Get the name of the file found on disk. + fileName1 = file->fileName(); + + while (TRUE) { + // Work out what the file name should be, based on the level no. + fileName2.setNum (lev); // Convert to QString. + fileName2 = fileName2.rightJustify (3,'0'); // Add zeros. + fileName2.append (".grl"); // Add level-suffix. + fileName2.prepend (colln->prefix); // Add colln. prefix. + + if (lev > colln->nLevels) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is beyond the highest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else if (fileName1 == fileName2) { + lev++; + break; + } + else if (fileName1.myStr() < fileName2.myStr()) { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("File '%1' is before the lowest level for " + "the %2 game and cannot be played.") + .arg(fileName1) + .arg("\"" + colln->name + "\"")); + break; + } + else { + KGrMessage::information (view, + i18n("Check Games & Levels"), + i18n("Cannot find file '%1' for the %2 game.") + .arg(fileName2) + .arg("\"" + colln->name + "\"")); + lev++; + } + } + ++i; // Go to next file info entry. + } + } +} + +bool KGrGame::loadCollections (Owner o) +{ + QString filePath; + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + if (! c.exists()) { + // If the user has not yet created a collection, don't worry. + if (o == SYSTEM) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot find game info file '%1'.") + .arg(filePath)); + } + return (FALSE); + } + + if (! c.open (IO_ReadOnly)) { + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Cannot open file '%1' for read-only.").arg(filePath)); + return (FALSE); + } + + QCString line = ""; + QCString name = ""; + QString prefix = ""; + char settings = ' '; + int nLevels = -1; + + int ch = 0; + while (ch >= 0) { + ch = c.getch(); + if (((char) ch != '\n') && (ch >= 0)) { + // If not end-of-line and not end-of-file, add to the line. + if (ch == '\r') {line += '\n';} + else if (ch == '\\') {ch = c.getch(); line += '\n';} + else {line += (char) ch;} + } + else { + // If first character is a digit, we have a new collection. + if (isdigit(line[0])) { + if (nLevels >= 0) { + // If previous collection with no "about" exists, load it. + collections.append (new KGrCollection + (o, name, prefix, settings, nLevels, "")); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + // Decode the first (maybe the only) line in the new collection. + line = line.simplifyWhiteSpace(); + int i, j, len; + len = line.length(); + i = 0; j = line.find(' ',i); nLevels = line.left(j).toInt(); + i = j+1; j = line.find(' ',i); settings = line[i]; + i = j+1; j = line.find(' ',i); prefix = line.mid(i,j-i); + i = j+1; name = line.right(len-i); + } + // If first character is not a digit, the line should be an "about". + else if (nLevels >= 0) { + collections.append (new KGrCollection + (o, i18n((const char *) name), // Translate now. + prefix, settings, nLevels, + QString::fromUtf8((const char *) line))); + name = ""; prefix = ""; settings = ' '; nLevels = -1; + } + else if (ch >= 0) { + // Not EOF: it's an empty line or out-of-context "about" line. + KGrMessage::information (view, i18n("Load Game Info"), + i18n("Format error in game info file '%1'.") + .arg(filePath)); + c.close(); + return (FALSE); + } + line = ""; + } + } + + c.close(); + return (TRUE); +} + +bool KGrGame::saveCollections (Owner o) +{ + QString filePath; + + if (o != USER) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("You can only modify user games.")); + return (FALSE); + } + + filePath = ((o == SYSTEM)? systemDataDir : userDataDir) + "games.dat"; + + QFile c (filePath); + + // Open the output file. + if (! c.open (IO_WriteOnly)) { + KGrMessage::information (view, i18n("Save Game Info"), + i18n("Cannot open file '%1' for output.").arg(filePath)); + return (FALSE); + } + + // Save the collections. + KGrCollection * colln; + QCString line; + int i, len; + char ch; + + for (colln = collections.first(); colln != 0; colln = collections.next()) { + if (colln->owner == o) { + line.sprintf ("%03d %c %s %s\n", colln->nLevels, colln->settings, + colln->prefix.myStr(), + (const char *) colln->name.utf8()); + len = line.length(); + for (i = 0; i < len; i++) + c.putch (line[i]); + + len = colln->about.length(); + if (len > 0) { + QCString aboutC = colln->about.utf8(); + len = aboutC.length(); // Might be longer now. + for (i = 0; i < len; i++) { + ch = aboutC[i]; + if (ch != '\n') { + c.putch (ch); // Copy the character. + } + else { + c.putch ('\\'); // Change newline to \ and n. + c.putch ('n'); + } + } + c.putch ('\n'); // Add a real newline. + } + } + } + + c.close(); + return (TRUE); +} + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + +void KGrGame::myMessage (QWidget * parent, QString title, QString contents) +{ + // Halt the game while the message is displayed. + setMessageFreeze (TRUE); + + KGrMessage::wrapped (parent, title, contents); + + // Unfreeze the game, but only if it was previously unfrozen. + setMessageFreeze (FALSE); +} + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +KGrCollection::KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a) +{ + // Holds information about a collection of KGoldrunner levels (i.e. a game). + owner = o; name = n; prefix = p; settings = s; nLevels = nl; about = a; +} + +#include "kgrgame.moc" diff --git a/kgoldrunner/src/kgrgame.h b/kgoldrunner/src/kgrgame.h new file mode 100644 index 00000000..86144ef6 --- /dev/null +++ b/kgoldrunner/src/kgrgame.h @@ -0,0 +1,309 @@ +/*************************************************************************** + * Copyright (C) 2003 by Ian Wadham and Marco Krger * + * ianw2@optusnet.com.au * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef KGRGAME_H +#define KGRGAME_H + +// Macros to smooth out the differences between Qt 1 and Qt 2 classes. +// +// "myStr" converts a QString object to a C language "char*" character string. +// "myChar" extracts a C language character (type "char") from a QString object. +// "endData" checks for an end-of-file condition. +// +#define myStr latin1 +#define myChar(i) at((i)).latin1() +#define endData atEnd + +#include <qobject.h> + +#ifdef QT3 +#include <qptrlist.h> +#else +#include <qlist.h> +#endif +#include <qstring.h> + +#include <qdir.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qdatastream.h> + +#include <qcolor.h> + +/** +Sets up games and levels in KGoldrunner and controls the play. + +@author Ian Wadham +*/ + +class KGrObject; +class KGrHero; +class KGrEnemy; +class KGrCollection; + +class KGrGame : public QObject +{ +Q_OBJECT +public: + KGrGame (KGrCanvas * theView, QString theSystemDir, QString theUserDir); + ~KGrGame(); + + bool initCollections(); + KGrHero * getHero(); + + int getLevel(); + + void startPlaying(); + + bool inMouseMode(); // True if the game is in mouse mode. + bool inEditMode(); // True if the game is in editor mode. + bool isLoading(); // True if a level is being loaded. + + bool saveOK (bool exiting); // Check if edits were saved. + + QString getTitle(); // Collection - Level NNN, Name. + + void setEditObj (char newEditObj); // Set object for editor to paint. + + QString getFilePath (Owner o, KGrCollection * colln, int lev); + +public slots: + void startLevelOne(); // Start any game from level 1. + void startAnyLevel(); // Start any game from any level. + void startNextLevel(); // Start next level of current game. + + void setMouseMode (bool on_off); // Set mouse OR keyboard control. + void startLevel (int startingAt, int requestedLevel); + void newGame (const int lev, const int gameIndex); + void startTutorial(); // Start tutorial game. + void showHint(); // Show hint for current level. + + void showHighScores(); // Show high scores for current game. + + void incScore(int); // Update the score. + void herosDead(); // Hero was caught or he quit (key Q). + void showHiddenLadders(); // Show hidden ladders (nuggets gone). + void goUpOneLevel(); // Hero completed the level. + void loseNugget(); // Nugget destroyed (not collected). + void heroAction (KBAction movement);// Move hero under keyboard control. + + void saveGame(); // Save game ID, score and level. + void loadGame(); // Re-load game, score and level. + +signals: + void showScore (long); // For main window to show the score. + void showLives (long); // For main window to show lives left. + void showLevel (int); // For main window to show the level. + + void hintAvailable (bool); // For main window to adjust menu text. + + void setEditMenu (bool); // Enable/Disable edit menu items. + void defaultEditObj(); // Set default edit-toolbar button. + + void markRuleType (char); // Mark KGoldrunner/Traditional rules. + void gameFreeze (bool); // Do visual feedback in the GUI. + +private slots: + void finalBreath (); // Hero is dead: re-start the level. + void readMousePos (); // Timed reading of mouse position. + void doDig (int button); // Dig when under mouse-button control. + +private: + void setBlankLevel (bool playable); + int loadLevel (int levelNo); + bool openLevelFile (int levelNo, QFile & openlevel); + void changeObject (unsigned char kind, int i, int j); + void createObject (KGrObject *o, char picType, int x, int y); + void setTimings (); + void initSearchMatrix(); + + void checkHighScore(); // Check if high score for current game. + + int selectLevel (int action, int requestedLevel); + int selectedGame; + + void restart(); // Kickstart the game action. + +/******************************************************************************/ +/************************** PLAYFIELD AND GAME DATA *************************/ +/******************************************************************************/ + +private: + KGrCanvas * view; // Where the game is displayed. + QString systemDataDir; // System games are stored here. + QString userDataDir; // User games are stored here. + + KGrObject * playfield[30][22]; // Array of playfield objects. + char editObjArray[30][22]; // Character-code equivalent. + char lastSaveArray[30][22]; // Copy for use in "saveOK()". + + int level; // Current play/edit level. + QString levelName; // Level name (optional). + QString levelHint; // Level hint (optional). + + long lives; // Lives remaining. + long score; // Current score. + long startScore; // Score at start of level. + + KGrHero * hero; // The HERO figure !! Yay !!! + int startI, startJ; // The hero's starting position. + +#ifdef QT3 + QPtrList<KGrEnemy> enemies; // The list of enemies. +#else + QList<KGrEnemy> enemies; // The list of enemies. +#endif + int enemyCount; // How many enemies. + KGrEnemy * enemy; // One of the enemies. + + int nuggets; // How many gold nuggets. + + bool newLevel; // Next level will be a new one. + bool loading; // Stop input until it's loaded. + + bool modalFreeze; // Stop game during dialog. + bool messageFreeze; // Stop game during message. + + QTimer * mouseSampler; // Timer for mouse tracking. + QTimer * dyingTimer; // For pause when the hero dies. + + int lgHighlight; // Row selected in "loadGame()". + +/******************************************************************************/ +/************************** AUTHORS' DEBUGGING AIDS **************************/ +/******************************************************************************/ + +public slots: + void doStep(); // Do one animation step. + void showFigurePositions(); // Show everybody's co-ordinates. + void showHeroState(); // Show hero's co-ordinates and state. + void showEnemyState (int); // Show enemy's co-ordinates and state. + void showObjectState(); // Show an object's state. + void bugFix(); // Turn a bug fix on/off dynamically. + void startLogging(); // Turn logging on/off. + +/******************************************************************************/ +/******************** GAME EDITOR PROPERTIES AND METHODS ********************/ +/******************************************************************************/ + +public slots: // Slots connected to the Menu and Edit Toolbar. + void createLevel(); // Set up a blank level-display for edit. + void updateLevel(); // Update an existing level. + void updateNext(); // Update the current level + 1. + void editNameAndHint(); // Run a dialog to edit the level name and hint. + bool saveLevelFile(); // Save the edited level in a text file (.grl). + void moveLevelFile(); // Move level to another collection or number. + void deleteLevelFile(); // Delete a level file. + + void editCollection (int action); + + void setLevel (int lev); // Set level to be edited. + + void freeze(); // Stop the gameplay action. + void unfreeze(); // Restart the gameplay action. + void setMessageFreeze (bool); + +private: + bool mouseMode; // Flag to set up keyboard OR mouse control. + bool editMode; // Flag to change keyboard and mouse functions. + char editObj; // Type of object to be painted by the mouse. + bool paintEditObj; // Sets painting on/off (toggled by clicking). + int oldI, oldJ; // Last mouse position painted. + int editLevel; // Level to be edited (= 0 for new level). + int heroCount; // Can enter at most one hero. + bool shouldSave; // True if name or hint was edited. + +private: + void loadEditLevel (int); // Load and display an existing level for edit. + void initEdit(); + void deleteLevel(); + void insertEditObj (int, int); + void setEditableCell (int, int, char); + void showEditLevel(); + bool reNumberLevels (int, int, int, int); + bool ownerOK (Owner o); + + // Pixmaps for repainting objects as they are edited. + QPixmap digpix[10]; + QPixmap brickbg, fbrickbg; + QPixmap freebg, nuggetbg, polebg, betonbg, ladderbg, hladderbg; + QPixmap edherobg, edenemybg; + +private slots: + void doEdit(int); // For mouse-click when in edit-mode. + void endEdit(int); // For mouse-release when in edit-mode. + +/******************************************************************************/ +/******************** COLLECTION PROPERTIES AND METHODS *******************/ +/******************************************************************************/ + +private: + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +#ifdef QT3 + QPtrList<KGrCollection> collections; // List of ALL collections. +#else + QList<KGrCollection> collections; // List of ALL collections. +#endif + + KGrCollection * collection; // Collection currently in use. + Owner owner; // Collection owner. + int collnIndex; // Index in collections list. + + void mapCollections(); + bool loadCollections (Owner); + bool saveCollections (Owner); + +/******************************************************************************/ +/********************** WORD-WRAPPED MESSAGE BOX ************************/ +/******************************************************************************/ + + void myMessage (QWidget * parent, QString title, QString contents); +}; + +/******************************************************************************/ +/********************** CLASS TO DISPLAY THUMBNAIL ***********************/ +/******************************************************************************/ + +class KGrThumbNail : public QFrame +{ +public: + KGrThumbNail (QWidget *parent = 0, const char *name = 0); + void setFilePath (QString &, QLabel *); // Set filepath and name field. + + static QColor backgroundColor; + static QColor brickColor; + static QColor ladderColor; + static QColor poleColor; + +protected: + void drawContents (QPainter *); // Draw a preview of a level. + QString filePath; + QLabel * lName; +}; + +/******************************************************************************/ +/*********************** COLLECTION DATA CLASS **************************/ +/******************************************************************************/ + +// Note that a collection of KGoldrunner levels is the same thing as a "game". +class KGrCollection +{ +public: + KGrCollection (Owner o, const QString & n, const QString & p, + const char s, int nl, const QString & a); + Owner owner; // Collection owner: "System" or "User". + QString name; // Collection name. + QString prefix; // Collection's filename prefix. + char settings; // Collection rules: KGoldrunner or Traditional. + int nLevels; // Number of levels in the collection. + QString about; // Optional text about the collection. +}; + +#endif diff --git a/kgoldrunner/src/kgrobject.cpp b/kgoldrunner/src/kgrobject.cpp new file mode 100644 index 00000000..948fd540 --- /dev/null +++ b/kgoldrunner/src/kgrobject.cpp @@ -0,0 +1,196 @@ +/*************************************************************************** + kgrobject.cpp - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kgrconsts.h" +#include "kgrcanvas.h" + +#include "kgrobject.h" + +#include <stdio.h> + +KGrObject::KGrObject (char objType) +{ + iamA = objType; + searchValue = 0; + blocker = FALSE; + if ((objType == BRICK) || (objType == BETON) || (objType == FBRICK)) { + blocker = TRUE; + } + xpos = 0; + ypos = 0; + objectView = NULL; +} + +bool KGrObject::frozen = FALSE; // Initialise game as running, not halted. +bool KGrObject::bugFixed = FALSE;// Initialise game with dynamic bug-fix OFF. +bool KGrObject::logging = FALSE;// Initialise game with log printing OFF. + +char KGrObject::whatIam () +{ + return iamA; +} + +void KGrObject::showState (int i, int j) +{ + printf("(%02d,%02d) - Object [%c] search %d", i, j, iamA, searchValue); + if (blocker) printf(" blocker"); + printf("\n"); +} + +KGrObject :: ~KGrObject () +{ +} + +KGrEditable::KGrEditable (char editType) : KGrObject (editType) +{ +} + +void KGrEditable::setType (char editType) +{ + iamA = editType; +} + +KGrEditable::~KGrEditable () +{ +} + +KGrFree::KGrFree (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + theRealMe = FREE; // Remember what we are, even "iamA == NUGGET". +} + +void KGrFree::setNugget(bool nug) +{ + // This code must work over a hidden ladder as well as a free cell. + if (! nug) { + iamA = theRealMe; + objectView->paintCell (xpos, ypos, FREE); + } + else { + iamA = NUGGET; + objectView->paintCell (xpos, ypos, NUGGET); + } +} + +KGrFree :: ~KGrFree () +{ +} + +/* +++++++++++++++ BRICK ++++++++++++++++ */ + +KGrBrick::KGrBrick (char objType, int i, int j, KGrCanvas * view) + : KGrObject (objType) +{ + xpos = i; + ypos = j; + objectView = view; + dig_counter = 0; + holeFrozen = FALSE; + iamA = BRICK; + timer = new QTimer (this); + connect (timer, SIGNAL (timeout ()), SLOT (timeDone ())); +} + +void KGrBrick::dig (void) +{ + dig_counter = 1; + hole_counter = HOLETIME; + iamA = HOLE; + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); +} + +void KGrBrick::doStep() { + if (holeFrozen) { + holeFrozen = FALSE; + timeDone(); + } +} + +void KGrBrick::showState (int i, int j) +{ + printf ("(%02d,%02d) - Brick [%c] search %d dig-counter %d", + i, j, iamA, searchValue, dig_counter); + if (blocker) + printf (" blocker"); + printf ("\n"); +} + +void KGrBrick::timeDone () +{ + if (KGrObject::frozen) {holeFrozen = TRUE; return;} + + // When the hole is complete, we need a longer delay. + if (dig_counter == 5) { + hole_counter--; + if (hole_counter > 0) { + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + return; + } + } + if (dig_counter < 9) { + dig_counter++; + timer->start ((DIGDELAY * NSPEED) / speed, TRUE); + if (dig_counter >= 8) + iamA = BRICK; + } + else + dig_counter = 0; + + // Brick pix:- 0 normal, 1-4 crumbling, 5 hole complete, 6-9 re-growing. + objectView->paintCell (xpos, ypos, BRICK, dig_counter); + objectView->updateCanvas(); +} + +void KGrBrick::useHole() { + if (iamA == HOLE) + iamA = USEDHOLE; +} + +void KGrBrick::unUseHole() { + if (iamA == USEDHOLE) + iamA = HOLE; +} + +KGrBrick :: ~KGrBrick () +{ + delete timer; +} + +KGrHladder::KGrHladder (char objType, int i, int j, KGrCanvas * view) + : KGrFree (objType, i, j, view) + // Must inherit "setNugget()" from "KGrFree". +{ + theRealMe = HLADDER; // But remember we are a hidden ladder ... +} + +void KGrHladder::showLadder() +{ + iamA = LADDER; + objectView->paintCell (xpos, ypos, LADDER); +} + +KGrHladder :: ~KGrHladder () +{ +} + +#include "kgrobject.moc" diff --git a/kgoldrunner/src/kgrobject.h b/kgoldrunner/src/kgrobject.h new file mode 100644 index 00000000..a48eba88 --- /dev/null +++ b/kgoldrunner/src/kgrobject.h @@ -0,0 +1,107 @@ +/*************************************************************************** + kgrobject.h - description + ------------------- + begin : Wed Jan 23 2002 + copyright : (C) 2002 by Marco Krger and Ian Wadham + email : See menu "Help, About KGoldrunner" + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KGROBJECT_H +#define KGROBJECT_H + +// Obsolete - #include <iostream.h> +#include <iostream> + +#include <qtimer.h> +#include <stdlib.h> // for random + +class KGrCanvas; + +class KGrObject : public QObject +{ + Q_OBJECT +public: + KGrObject (char objType); + virtual ~KGrObject(); + + // STATIC GLOBAL FLAGS. + static bool frozen; // Game play halted (use ESCAPE key). + static bool bugFixed; // Dynamic bug fix turned on (key B, if halted). + static bool logging; // Log printing turned on. + + char whatIam(); + int searchValue; + bool blocker; // Beton or Brick -> TRUE + void showState (int, int); + +protected: + KGrCanvas * objectView; + int xpos; + int ypos; + char iamA; +}; + +class KGrEditable : public KGrObject +{ + Q_OBJECT +public: + KGrEditable (char editType); + virtual ~KGrEditable (); + void setType (char); +}; + +class KGrFree : public KGrObject +{ Q_OBJECT +public: + KGrFree (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrFree(); + void setNugget(bool); + +protected: + char theRealMe; // Set to FREE or HLADDER, even when "iamA == NUGGET". +}; + +class KGrBrick : public KGrObject +{ + Q_OBJECT +public: + KGrBrick (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrBrick(); + void dig(void); + void useHole(); + void unUseHole(); + static int speed; // Digging & repair speed (copy of KGrFigure::speed). + static int HOLETIME; // Number of timing cycles for a hole to remain open. + void doStep(); + void showState (int, int); + +protected slots: + void timeDone(void); + +private: + int dig_counter; + int hole_counter; + bool holeFrozen; + QTimer *timer; +}; + +class KGrHladder : public KGrFree +{ + Q_OBJECT +public: + // BUG FIX - Ian W., 21/6/01 - must inherit "setNugget()" from "KGrFree". + KGrHladder (char objType, int i, int j, KGrCanvas * view); + virtual ~KGrHladder(); + void showLadder(); +}; + +#endif // KGROBJECT_H diff --git a/kgoldrunner/src/main.cpp b/kgoldrunner/src/main.cpp new file mode 100644 index 00000000..63be6a71 --- /dev/null +++ b/kgoldrunner/src/main.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2003 Ian Wadham and Marco Krüger <ianw2@optusnet.com.au> + */ + +#include <kapplication.h> +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> + +#include "kgrconsts.h" +#include "kgoldrunner.h" + +static const char description[] = + I18N_NOOP("KGoldrunner is a game of action and puzzle solving"); + +static const char version[] = "2.0"; + +int main (int argc, char **argv) +{ + KAboutData about("kgoldrunner", I18N_NOOP("KGoldrunner" ), + version, description, + KAboutData::License_GPL, + "(C) 2003 Ian Wadham and Marco Krüger"); + about.addAuthor( "Ian Wadham", I18N_NOOP("Current author"), + "ianw2@optusnet.com.au" ); + about.addAuthor( "Marco Krüger", I18N_NOOP("Original author"), 0); + + KCmdLineArgs::init (argc, argv, &about); + + KApplication app; + + // Register as a DCOP client. + app.dcopClient()->registerAs (app.name(), false); + + // See if we are starting with session management. + if (app.isRestored()) + { + RESTORE(KGoldrunner); + return app.exec(); + } + else + { + KGoldrunner * widget = new KGoldrunner; + if (widget->startedOK()) { + widget->show(); + return app.exec(); + } + } +} |