Commit c1394cca authored by Ian Wadham's avatar Ian Wadham

Fix the transitions between the various modes, via the GUI: play, demos,...

Fix the transitions between the various modes, via the GUI: play, demos, replays and editing.  Fix the recording and replay of keyboard-mode play.

svn path=/trunk/KDE/kdegames/kgoldrunner/; revision=968983
parent 5db793ee
This diff is collapsed.
......@@ -805,6 +805,8 @@ void KGoldrunner::setEditMenu (bool on_off)
hintAction->setEnabled (! on_off);
killHero->setEnabled (! on_off);
highScore->setEnabled (! on_off);
setAvail ("instant_replay", (! on_off));
setAvail ("game_pause", (! on_off));
if (on_off){
// Set the editToolbar icons to the current tile-size.
......
#include "kgrdebug.h"
/****************************************************************************
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
......@@ -66,10 +68,10 @@ void KGrEditor::setEditObj (char newEditObj)
editObj = newEditObj;
}
void KGrEditor::createLevel (int pGameIndex)
bool KGrEditor::createLevel (int pGameIndex)
{
if (! saveOK ()) { // Check unsaved work.
return;
return false;
}
if (! ownerOK (USER)) {
......@@ -77,7 +79,7 @@ void KGrEditor::createLevel (int pGameIndex)
i18n ("You cannot create and save a level "
"until you have created a game to hold "
"it. Try menu item \"Create Game\"."));
return;
return false;
}
int i, j;
......@@ -111,20 +113,21 @@ void KGrEditor::createLevel (int pGameIndex)
// Re-enable player input.
mouseDisabled = false;
return true;
}
void KGrEditor::updateLevel (int pGameIndex, int level)
bool KGrEditor::updateLevel (int pGameIndex, int level)
{
if (! saveOK ()) { // Check unsaved work.
return;
return false;
}
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;
return false;
}
gameIndex = pGameIndex;
......@@ -139,7 +142,7 @@ void KGrEditor::updateLevel (int pGameIndex, int level)
<< "level" << selectedLevel;
if (selectedLevel == 0) {
mouseDisabled = false;
return;
return false;
}
if (gameList.at(gameIndex)->owner == SYSTEM) {
......@@ -152,12 +155,14 @@ void KGrEditor::updateLevel (int pGameIndex, int level)
loadEditLevel (selectedLevel);
mouseDisabled = false;
return true;
}
void KGrEditor::loadEditLevel (int lev)
{
KGrLevelData d;
kDebug() << "gameIndex" << gameIndex;
// If system game or ENDE screen, choose system dir, else choose user dir.
const QString dir = ((gameList.at(gameIndex)->owner == SYSTEM) ||
(lev == 0)) ? systemDataDir : userDataDir;
......@@ -228,7 +233,7 @@ bool KGrEditor::saveLevelFile()
// Pop up dialog box, which could change the game or level or both.
int selectedLevel = selectLevel (action, editLevel, gameIndex);
if (selectedLevel == 0) {
return (false);
return false;
}
// Get the new game (if changed).
......@@ -253,10 +258,10 @@ bool KGrEditor::saveLevelFile()
case 0: if (! reNumberLevels (n, selectedLevel,
gameList.at (n)->nLevels, +1)) {
return (false);
return false;
}
break;
case 1: return (false);
case 1: return false;
break;
}
}
......@@ -266,7 +271,7 @@ bool KGrEditor::saveLevelFile()
if (! levelFile.open (QIODevice::WriteOnly)) {
KGrMessage::information (view, i18n ("Save Level"),
i18n ("Cannot open file '%1' for output.", filePath));
return (false);
return false;
}
// Save the level - row by row.
......@@ -314,17 +319,17 @@ bool KGrEditor::saveLevelFile()
editLevel = selectedLevel;
emit showLevel (editLevel);
view->setTitle (getTitle()); // Display new title.
return (true);
return true;
}
void KGrEditor::moveLevelFile (int pGameIndex, int level)
bool KGrEditor::moveLevelFile (int pGameIndex, int level)
{
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.",
i18n ("Game"), i18n ("Editor")));
return;
return false;
}
gameIndex = pGameIndex;
......@@ -340,20 +345,21 @@ void KGrEditor::moveLevelFile (int pGameIndex, int level)
i18n ("You cannot move a level until you "
"have created a game and at least two levels. Try "
"menu item \"Create Game\"."));
return;
return false;
}
if (gameList.at (fromC)->owner != USER) {
KGrMessage::information (view, i18n ("Move Level"),
i18n ("Sorry, you cannot move a system level."));
return;
return false;
}
// Pop up dialog box to get the game and level number to move to.
while ((toC == fromC) && (toL == fromL)) {
toL = selectLevel (action, toL, gameIndex);
if (toL == 0)
return;
if (toL == 0) {
return false;
}
toC = gameIndex;
......@@ -370,20 +376,21 @@ void KGrEditor::moveLevelFile (int pGameIndex, int level)
filePath1 = getLevelFilePath (gameList.at (fromC), fromL);
filePath2 = filePath1;
filePath2 = filePath2.append (".tmp");
if (! KGrGameIO::safeRename (filePath1, filePath2))
return;
if (! KGrGameIO::safeRename (filePath1, filePath2)) {
return false;
}
if (toC == fromC) { // Same game.
if (toL < fromL) { // Decrease level.
// Move "toL" to "fromL - 1" up by 1.
if (! reNumberLevels (toC, toL, fromL-1, +1)) {
return;
return false;
}
}
else { // Increase level.
// Move "fromL + 1" to "toL" down by 1.
if (! reNumberLevels (toC, fromL+1, toL, -1)) {
return;
return false;
}
}
}
......@@ -391,13 +398,13 @@ void KGrEditor::moveLevelFile (int pGameIndex, int level)
// In "fromC", move "fromL + 1" to "nLevels" down and update "nLevels".
if (! reNumberLevels (fromC, fromL + 1,
gameList.at (fromC)->nLevels, -1)) {
return;
return false;
}
gameList.at (fromC)->nLevels--;
// In "toC", move "toL + 1" to "nLevels" up and update "nLevels".
if (! reNumberLevels (toC, toL, gameList.at (toC)->nLevels, +1)) {
return;
return false;
}
gameList.at (toC)->nLevels++;
......@@ -411,10 +418,12 @@ void KGrEditor::moveLevelFile (int pGameIndex, int level)
editLevel = toL;
emit showLevel (editLevel);
view->setTitle (getTitle()); // Re-write title.
return true;
}
void KGrEditor::deleteLevelFile (int pGameIndex, int level)
bool KGrEditor::deleteLevelFile (int pGameIndex, int level)
{
// TODO - Coukd we delete a system level? There appears to be no check.
int action = SL_DELETE;
gameIndex = pGameIndex;
......@@ -423,13 +432,13 @@ void KGrEditor::deleteLevelFile (int pGameIndex, int level)
i18n ("You cannot delete a level until you "
"have created a game and a level. Try "
"menu item \"Create Game\"."));
return;
return false;
}
// Pop up dialog box to get the game and level number.
int selectedLevel = selectLevel (action, level, gameIndex);
if (selectedLevel == 0) {
return;
return false;
}
QString filePath;
......@@ -447,12 +456,12 @@ void KGrEditor::deleteLevelFile (int pGameIndex, int level)
"move higher levels down by one?"),
i18n ("&Delete Level"), i18n ("&Cancel"))) {
case 0: break;
case 1: return; break;
case 1: return false; break;
}
levelFile.remove();
if (! reNumberLevels (n, selectedLevel + 1,
gameList.at(n)->nLevels, -1)) {
return;
return false;
}
}
else {
......@@ -462,7 +471,7 @@ void KGrEditor::deleteLevelFile (int pGameIndex, int level)
else {
KGrMessage::information (view, i18n ("Delete Level"),
i18n ("Cannot find file '%1' to be deleted.", filePath));
return;
return false;
}
gameList.at (n)->nLevels--;
......@@ -482,9 +491,10 @@ void KGrEditor::deleteLevelFile (int pGameIndex, int level)
createLevel (gameIndex); // No levels left in game.
}
emit showLevel (editLevel);
return true;
}
void KGrEditor::editGame (int pGameIndex)
bool KGrEditor::editGame (int pGameIndex)
{
int n = -1;
int action = (pGameIndex < 0) ? SL_CR_GAME : SL_UPD_GAME;
......@@ -494,7 +504,7 @@ void KGrEditor::editGame (int pGameIndex)
if (gameIndex >= 0) {
int selectedLevel = selectLevel (SL_UPD_GAME, editLevel, gameIndex);
if (selectedLevel == 0) {
return;
return false;
}
editLevel = selectedLevel;
n = gameIndex;
......@@ -597,6 +607,7 @@ void KGrEditor::editGame (int pGameIndex)
}
delete ec;
return true;
}
/******************************************************************************/
......@@ -641,13 +652,13 @@ bool KGrEditor::saveOK ()
i18n ("&Go on editing")))
{
case 0:
result = saveLevelFile(); // Save and continue.
result = saveLevelFile(); // Save, do next action: or more edits.
break;
case 1:
shouldSave = false; // Continue: don't save.
shouldSave = false; // Do not save, but do next action.
break;
case 2:
result = false; // Go back to editing.
result = false; // Go back to editing.
break;
}
}
......@@ -675,7 +686,7 @@ void KGrEditor::initEdit()
void KGrEditor::insertEditObj (int i, int j, char obj)
{
kDebug() << i << j << obj;
dbk2 << i << j << obj;
if ((i < 1) || (j < 1) || (i > levelData.width) || (j > levelData.height)) {
return; // Do nothing: mouse pointer is out of playfield.
}
......
......@@ -81,8 +81,10 @@ public:
*
* @param pGameIndex The list-index of the game which will contain the new
* level: assumed for now, but can change at save time.
*
* @return If false, the action failed or was cancelled.
*/
void createLevel (int pGameIndex);
bool createLevel (int pGameIndex);
/**
* Load and display an existing level, ready for editing. This can be a
......@@ -92,8 +94,10 @@ public:
* to be edited: verified by a dialog and may change.
* @param pLevel The number of the level to be edited: verified by a
* dialog and may change.
*
* @return If false, the action failed or was cancelled.
*/
void updateLevel (int pGameIndex, int pLevel);
bool updateLevel (int pGameIndex, int pLevel);
/**
* Save an edited level in a text file (*.grl) in the user's area. The
......@@ -116,8 +120,10 @@ public:
* @param pLevel The number of the level to be moved: a dialog selects
* the number to move to. Other numbers may be changed,
* to preserve the sequential numbering of levels.
*
* @return If false, the action failed or was cancelled.
*/
void moveLevelFile (int pGameIndex, int pLevel);
bool moveLevelFile (int pGameIndex, int pLevel);
/**
* Delete a level from a game.
......@@ -126,8 +132,10 @@ public:
* to be deleted: verified by a dialog and may change.
* @param pLevel The number of the level to be deleted: verified by a
* dialog and may change.
*
* @return If false, the action failed or was cancelled.
*/
void deleteLevelFile (int pGameIndex, int pLevel);
bool deleteLevelFile (int pGameIndex, int pLevel);
/**
* Create a new game (a collection point for levels) or load the details
......@@ -136,8 +144,10 @@ public:
* @param pGameIndex The list-index of the game to be created or edited:
* 0 = create, >0 = edit (verified by a dialog and may
* change).
*
* @return If false, the action failed or was cancelled.
*/
void editGame (int pGameIndex);
bool editGame (int pGameIndex);
/**
* Run a dialog in which the name and hint of a level can be edited.
......
This diff is collapsed.
......@@ -74,7 +74,10 @@ public slots:
void incScore (const int n); // Update the score.
private:
void selectLevel (const SelectAction action, const int requestedLevel);
bool modeSwitch (const int action,
int & selectedGame, int & selectedLevel);
bool selectGame (const SelectAction slAction,
int & selectedGame, int & selectedLevel);
void toggleSoundsOnOff(); // Set sound enabled or disabled.
......@@ -82,9 +85,12 @@ private:
void setControlMode (const int mode);
void setTimeScale (const int action);
void newGame (const int lev, const int gameIndex);
void newGame (const int lev, const int gameIndex);
void runReplay (const int action,
const int selectedGame, const int selectedLevel);
bool startDemo (const Owner demoOwner, const QString & pPrefix,
const int levelNo);
void runNextDemoLevel();
void finishDemo();
private slots:
......@@ -109,8 +115,16 @@ private:
void herosDead(); // Hero was caught or he quit (key Q).
void levelCompleted(); // Hero completed the level.
void saveGame(); // Save game ID, score and level.
void loadGame(); // Re-load game, score and level.
// Save game ID, score and level.
void saveGame();
// Select a saved game, score and level.
bool selectSavedGame (int & selectedGame, int & selectedLevel);
// Load and run a saved game, score and level.
void loadGame (const int index, const int lev);
QString loadedData;
private slots:
void endLevel (const int result); // Hero completed the level or he died.
......@@ -151,8 +165,8 @@ private slots:
void quickStartQuit();
private:
bool playLevel (const QString & prefix, const int levelNo,
const bool newLevel);
bool playLevel (const Owner fileOwner, const QString & prefix,
const int levelNo, const bool newLevel);
void setupLevelPlayer();
void showTutorialMessages (int levelNo);
......@@ -168,8 +182,6 @@ private:
KGrLevelPlayer * levelPlayer; // Where the level is played.
KGrRecording * recording; // A recording of the play.
bool playback; // Play back or record?
GameAction demoType; // The type of replay or demo.
bool startupDemo; // Startup demo running?
KGrCanvas * view; // Where the game is displayed.
QString systemDataDir; // System games are stored here.
......@@ -184,10 +196,17 @@ private:
QString prefix; // Prefix for game or demo file.
int level; // Current play/edit/demo level.
int levelMax; // Last level no in game/demo.
int gameLevel; // Copy of play/edit level no.
QString levelName; // Level name (optional).
QString levelHint; // Level hint (optional).
QString demoPrefix; // File-prefix for demo levels.
QString mainDemoName; // File-prefix for Main Demo.
GameAction demoType; // The type of replay or demo.
bool startupDemo; // Startup demo running?
Owner playbackOwner; // Owner for current demo-file.
QString playbackPrefix; // File-prefix for current demo.
int playbackIndex; // Record-index for curr demo.
int playbackMax; // Max index for current demo.
long lives; // Lives remaining.
long score; // Current score.
......
......@@ -87,7 +87,7 @@ enum KBAction {KB_UP, KB_DOWN, KB_LEFT, KB_RIGHT,
// Action codes when selecting a level or game for play, editing or replay.
enum SelectAction {SL_START, SL_ANY, SL_CREATE, SL_UPDATE, SL_SAVE,
SL_MOVE, SL_DELETE, SL_CR_GAME, SL_UPD_GAME,
SL_REPLAY, SL_SOLVE};
SL_REPLAY, SL_SOLVE, SL_NONE};
/// Codes for the rules of the selected game and level.
const char TraditionalRules = 'T';
......@@ -147,10 +147,23 @@ public:
KGrLevelData levelData; ///< The level data, at time of recording.
long lives; ///< Number of lives at start of level.
long score; ///< Score at start of level.
int speed; ///< Speed of game during recording (normal=10).
int controlMode; ///< Control mode during recording (mouse, etc).
QByteArray content; ///< The encoded recording of play.
QByteArray draws; ///< The random numbers used during play.
};
// Offsets used to encode keystrokes, control modes and speeds in a recording.
// Allow space for 16 direction and digging codes, 16 control modes, 16 special
// actions and 30 speeds. We actually have (as at May 2009) 8 direction and
// digging codes, control modes from 2 to 4, one special action (code 7) and
// speeds ranging from 2 to 20.
#define DIRECTION_CODE 0x80
#define MODE_CODE 0x90
#define ACTION_CODE 0xa0
#define SPEED_CODE 0xe0
#define END_CODE 0xff
enum GameAction {NEW, NEXT_LEVEL, LOAD, SAVE_GAME, PAUSE, HIGH_SCORE,
KILL_HERO, HINT,
DEMO, SOLVE, INSTANT_REPLAY, REPLAY_LAST, REPLAY_ANY};
......@@ -171,8 +184,8 @@ typedef char DirectionFlag;
typedef char AccessFlag;
typedef char Flags;
enum Direction {STAND, RIGHT, LEFT, UP, DOWN, nDirections,
DIG_RIGHT = nDirections, DIG_LEFT};
enum Direction {STAND, RIGHT, LEFT, UP, DOWN, nDirections,
DIG_RIGHT = nDirections, DIG_LEFT, NO_DIRECTION};
const DirectionFlag dFlag [nDirections] = {
0x10, // Can stand.
......
......@@ -47,7 +47,8 @@ KGrLevelPlayer::KGrLevelPlayer (QObject * parent, KRandomSequence * pRandomGen)
playback (false),
targetI (1),
targetJ (1),
direction (STAND),
direction (NO_DIRECTION),
newDirection (NO_DIRECTION),
timer (0),
digCycleTime (200), // Milliseconds per dig-timing cycle (default).
digCycleCount (40), // Cycles while hole is fully open (default).
......@@ -135,12 +136,6 @@ void KGrLevelPlayer::init (KGrCanvas * view, const int mode,
recCount = 0;
randIndex = 0;
T = 0;
if (! playback) {
dbk << "Play is being RECORDED.";
}
else {
dbk << "Play is being REPRODUCED.";
}
view->setGoldEnemiesRule (rules->enemiesShowGold());
......@@ -278,6 +273,14 @@ void KGrLevelPlayer::init (KGrCanvas * view, const int mode,
connect (timer, SIGNAL (tick (bool, int)), this, SLOT (tick (bool, int)));
connect (this, SIGNAL (animation (bool)), view, SLOT (animate (bool)));
if (! playback) {
dbk << "Play is being RECORDED.";
recordInitialWaitTime (NO_DIRECTION, 1500);
}
else {
dbk << "Play is being REPRODUCED.";
}
}
void KGrLevelPlayer::startDigging (Direction diggingDirection)
......@@ -384,19 +387,13 @@ void KGrLevelPlayer::setTarget (int pointerI, int pointerJ)
// The pointer moved: fall into "case Playing:" and start playing.
else if (! playback) {
T = 0;
// Allow a pause for viewing when playback starts.
recording->content [recIndex++] = (uchar) targetI;
recording->content [recIndex++] = (uchar) targetJ;
recording->content [recIndex] = (uchar) 75;
recording->content [recIndex + 1] = (uchar) 0xff;
recCount = 75;
}
playState = Playing;
case Playing:
// The human player is playing now.
if (! playback) {
if ((pointerI == targetI) && (pointerJ == targetJ) && (recCount < 255)){
dbe2 "T %04d recIndex %03d REC: codes %d %d %d\n",
dbe2 "T %04d recIndex %03d REC: codes %d %d %d - recCount++\n",
T, recIndex - 2, (uchar)(recording->content.at (recIndex-2)),
(uchar)(recording->content.at (recIndex-1)),
(uchar)(recording->content.at (recIndex)));
......@@ -404,16 +401,16 @@ void KGrLevelPlayer::setTarget (int pointerI, int pointerJ)
recording->content [recIndex] = (uchar) recCount;
}
else {
dbe2 "T %04d recIndex %03d REC: codes %d %d %d\n",
dbe2 "T %04d recIndex %03d REC: codes %d %d %d - new or > 255\n",
T, recIndex - 2, (uchar)(recording->content.at (recIndex-2)),
(uchar)(recording->content.at (recIndex-1)),
(uchar)(recording->content.at (recIndex)));
recIndex++;
recCount = 1;
recording->content [recIndex++] = (uchar) pointerI;
recording->content [recIndex++] = (uchar) pointerJ;
recording->content [recIndex] = (uchar) 1;
recording->content [recIndex + 1] = (uchar) 0xff;
recCount = 1;
recording->content [recIndex] = (uchar) recCount;
recording->content [recIndex + 1] = (uchar) END_CODE;
dbe2 "T %04d recIndex %03d REC: codes %d %d %d - NEW TARGET\n",
T, recIndex - 2, pointerI, pointerJ,
(uchar)(recording->content.at (recIndex)));
......@@ -442,11 +439,11 @@ void KGrLevelPlayer::doDig (int button)
playState = Playing;
switch (button) {
case Qt::LeftButton:
recordByte = 0x80 + DIG_LEFT;
recordByte = DIRECTION_CODE + DIG_LEFT;
startDigging (DIG_LEFT);
break;
case Qt::RightButton:
recordByte = 0x80 + DIG_RIGHT;
recordByte = DIRECTION_CODE + DIG_RIGHT;
startDigging (DIG_RIGHT);
break;
default:
......@@ -454,64 +451,48 @@ void KGrLevelPlayer::doDig (int button)
}
if (recordByte != 0) {
// Record a digging action.
if (recIndex >= 2) {
// If not the hero's first move, interrupt the previous mouse-move.
recording->content [recIndex] =
recording->content [recIndex] - 1;
dbe2 "T %04d recIndex %03d REC: codes %d %d %d\n",
T, recIndex - 2,
(uchar)(recording->content.at (recIndex-2)),
(uchar)(recording->content.at (recIndex-1)),
(uchar)(recording->content.at (recIndex)));
recIndex++;
}
// Record the digging code.
dbe2 "T %04d recIndex %03d REC: dig code %d\n",
T, recIndex, recordByte);
recording->content [recIndex++] = recordByte;
// Continue recording the previous mouse-move.
recording->content [recIndex++] = targetI;
recording->content [recIndex++] = targetJ;
recording->content [recIndex] = 1;
recording->content [recIndex + 1] = 0xff;
recCount = 1;
recordDigAction (recordByte);
}
}
void KGrLevelPlayer::setDirectionByKey (Direction dirn)
{
// Any key ends playback mode.
if (playback) {
interruptPlayback();
return;
}
// Keystrokes control the hero.
if ((playState == NotReady) || (controlMode == MOUSE)) {
// Keystrokes control the hero. KGrGame should avoid calling this during
// playback, but better to be safe ...
if (playback || (playState == NotReady) || (controlMode == MOUSE)) {
return;
}
if ((dirn == DIG_LEFT) || (dirn == DIG_RIGHT)) {
// Control mode is KEYBOARD or LAPTOP (hybrid: pointer + dig-keys).
playState = Playing;
T = 0;
direction = STAND;
if (playState == Ready) {
playState = Playing;
T = 0;
}
if (controlMode == KEYBOARD) {
newDirection = STAND; // Stop a keyboard move when digging.
}
startDigging (dirn);
recordDigAction ((uchar) (DIRECTION_CODE + dirn));
}
else if (controlMode == KEYBOARD) {
playState = Playing;
T = 0;
direction = dirn;
if (playState == Ready) {
playState = Playing;
T = 0;