defined high score properties for many ROMs

added option to limit number of chars for text input
added tooltips to High Scores dialogs and widgets
updated docs
This commit is contained in:
thrust26 2020-11-30 18:52:42 +01:00
parent 488b9cf077
commit 833ce27d37
13 changed files with 989 additions and 428 deletions

View File

@ -14,6 +14,8 @@
6.4 to 6.5 (December XX, 2020)
* Added high scores saving.
* Enhanced cut/copy/paste for text editing. (TODO: PromptWidget)
* Added undo and redo to text editing. (TODO: PromptWidget)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -41,6 +41,7 @@
<li><a href="#Hotkeys">Hotkeys</a></li>
<li><a href="#ControlMap">Controller Map</a></li>
<li><a href="#TimeMachine">Stella's 'Time Machine'</a></li>
<li><a href="#HighScores">High Scores Saving</a></li>
</ul>
</li>
<li><a href="#Advanced">Advanced Configuration</a><br>
@ -303,6 +304,7 @@
TIA sprites and collisions for each object separately</li>
<li>Full system state save/load functionality</li>
<li>Automatic save state creation ('Time Machine') which allows moving back and forth in the recorded timeline</li>
<li>High scores saving</li>
<li>Cross-platform UI including a built-in ROM launcher frontend</li>
<li>Built-in extensive debugger, including static analysis with the Distella disassembler
and dynamic analysis at runtime by tracking code/graphics/data sections, and
@ -593,7 +595,7 @@
'listdelay' option; see <b>User Interface Settings</b> to change this setting.</p>
<p>
<h3><b><u>Command Menu</u></b></h3>
<h3><a name ="CommandMenu"><b><u>Command Menu</u></b></a></h3>
<p>While playing a game, normally one would use the keyboard shortcuts for controlling the
'virtual' switches in Stella (ie, the commands associated with the
@ -649,13 +651,13 @@
</tr>
<tr>
<td>Enter/exit options mode</td>
<td>Enter/exit <a href="#Options"><b>options</b></a> mode</td>
<td>Tab/Escape</td>
<td>Tab/Escape</td>
</tr>
<tr>
<td>Enter/exit command mode</td>
<td>Enter/exit <a href="#CommandMenu"><b>command</b></a> mode</td>
<td>Backslash (\)</td>
<td>Backslash (\)</td>
</tr>
@ -1809,7 +1811,11 @@
<td>Shift-Control-Alt + s</td>
<td>Shift-Control-Cmd + s</td>
</tr>
<tr>
<td>Open the <a href="#HighScores"><b>High Scores</b></a> dialog.</td>
<td>Insert</td>
<td>Insert</td>
</tr>
<tr>
<td>Toggle 'Time Machine' mode</td>
<td>Alt + t</td>
@ -2272,6 +2278,58 @@
<a href="#Debugger"><b>Developer Options</b> - Time Machine</a></h2> tab.</p>
</blockquote></br>
<br>
<p><h2>
<a name="Highscores">High Scores</a></h2>
<blockquote>
<p>Stella allows saving high scores when the required definitions for a ROM
exist. For a number of popular games this has been done already. You are
welcome to help us with defining more games.</p>
<p><b>High Scores</b> dialog:</p>
<td><img src="graphics/highscores.png"></td>
<p>This dialog can be opened by pressing 'Insert' any time while a game is
played. It will read the current score from the game and allow the user
to add it as a new high score. Of course this makes most sense when a game
is over.
The dialog items are explained in the following two tables.</p>
<table border="1" cellpadding="4">
<tr><th>Item</th><th>Description</th></tr>
<tr><td>Top row</td><td>Displays the current game's name.</td></tr>
<tr><td>Variation</td><td>By default the current game's variation is
selected. By changing the variation, the high scores of other
variations can be reviewed.</td></tr>
<tr><td>High scores table</td><td>This table displays up to ten high scores
for the current game variation.
<ul>
<li>Besides 'Rank' and 'Score' an optional special value (e.g.
'Level', 'Wave' or 'Round') is displayed.</li>
<li>In the 'Name' column, the player's initials are displayed.
These can be entered when a new high score is added to the list.
</li>
<li>'Date' and 'Time' record the time when the high score was
added.</li>
<li>The buttons at the right allow deleting individual high
scores from the list.</li>
</ul>
</td></tr>
<tr><td>MD5</td><td>Displays the MD5 checksum of the current game. This can
be useful for comparing and verifying high scores.</td></tr>
<tr><td>Reset</td><td>Resets all high scores of the currently selected
variation.</td></tr>
<tr><td>Save</td><td>Saves the updated high scores and closes the dialog.
</td></tr>
<tr><td>Cancel</td><td>Closes the dialog without saving.</td></tr>
</table>
<p>For details how to configure high scores definintions for a game see
<a href="#HighScoreProps"><b>High Scores Properties</b></a></h2>.</p>
</blockquote></br>
<!-- ///////////////////////////////////////////////////////////////////////// -->
<br>
<p><h1>
@ -3277,7 +3335,7 @@
<p>All settings can be changed within the integrated Options UI while Stella is
running (unless otherwise noted; some settings require an application restart).
The Options menu can be accessed from the ROM launcher by clicking the
<b>Options</b> button, or in-game by pressing the 'Tab' key.</p>
<b>Options...</b> button, or in-game by pressing the 'Tab' key.</p>
<p><b>Options Menu</b> dialog:<br><br>
<img src="graphics/options.png">
@ -3748,6 +3806,8 @@
</td>
</tr>
</table>
</li>
<br><li><b>High scores</b>: This option displays the <a href="#HighScores">High Scores</a> dialog for the selected ROM.</li>
</li>
<br><li><b>Reload listing</b>: Selecting this performs a reload of the
current listing. It is an alternative to pressing the 'Control + r'
@ -4648,6 +4708,42 @@ Ms Pac-Man (Stella extended codes):
</table>
<h3><a name="HighScoreProps"><b>High Scores Properties</b></h3></a>
<p>
<img src="graphics/options_gameinfo_highscores.png">
</p>
<table CELLSPACING="10">
<tr>
<td VALIGN="TOP"><i>Cart.Highscore</i></td>
<td>Contains the high score definition data as JSON string.</br>
<p>It can be defined in the 'High Scores Properties' dialog as follows:</p>
<table cellpadding="2" border="1">
<tr><th>&nbsp;Item&nbsp;</th><th>Description</th></tr>
<tr><td>Enable High Scores</td><td>Check to enable high score definitions.</td></tr>
<tr><td>Variations</td><td>Defines the number of game variations.
If the number is set to 1, the following variation definitions are not required.</td></tr>
<tr><td>Address</td><td>Defines the address (in hex format) where the variation number is stored.</td></tr>
<tr><td>BCD</td><td>Defines whether the variation number is stored as decimal or BCD.</td></tr>
<tr><td>0-based</td><td>Defines whether the variation number is stored zero-based.</td></tr>
<tr><td>Digits</td><td>Select the number of score digits displayed.</td></tr>
<tr><td>0-digits</td><td>Select the number of trailing score digits which are fixed to 0.</td></tr>
<tr><td>BCD</td><td>Defines whether the score is stored as decimal or BCD.</td></tr>
<tr><td>Invert</td><td>Inverts the score ordering. Check if a lower score (e.g. a timer) is better.</td></tr>
<tr><td>Addresses</td><td>Defines the addresses (in hex format, highest byte first) where the score is stored.
The number of addresses required is defined by the number of digits and trailing, fixed 0-digits.</td></tr>
<tr><td>Special</td><td>Defines a short label (up to 5 chars) for the optional, game's special value (e.g. 'Level', 'Wave', 'Round'...).
If no label is defined, the following special definitions are not required.</td></tr>
<tr><td>Address</td><td>Defines the address (in hex format) where the special number is stored.</td></tr>
<tr><td>BCD</td><td>Defines whether the special number is stored as decimal or BCD.</td></tr>
<tr><td>0-based</td><td>Defines whether the special number is stored is stored zero-based.</td></tr>
<tr><td>Note</td><td>Allows defining some free text which explains the high scores properties.</td></tr>
</table>
<p>To find the required definition values, you can use Stella's built-in <a href="debugger.html">debugger</a>.</p>
<p>Note: To verify the definitions, the current values of the addresses and the resulting score are displayed.</p>
</td>
</tr>
</table>
<p>The buttons at the bottom of the dialogs work as follows:
<ul>
<li><b>Defaults</b>: Reset the properties to those built into Stella.</li>

File diff suppressed because it is too large Load Diff

View File

@ -388,6 +388,7 @@ void FrameBuffer::update(UpdateMode mode)
case EventHandlerState::HIGHSCORESMENU:
{
myOSystem.highscoresMenu().tick();
redraw |= myOSystem.highscoresMenu().needsRedraw();
if(redraw)
{
@ -395,6 +396,12 @@ void FrameBuffer::update(UpdateMode mode)
myTIASurface->render();
myOSystem.highscoresMenu().draw(forceRedraw);
}
else if(rerender)
{
clear();
myTIASurface->render();
myOSystem.highscoresMenu().render();
}
break; // EventHandlerState::HIGHSCORESMENU
}
@ -421,6 +428,12 @@ void FrameBuffer::update(UpdateMode mode)
myTIASurface->render();
myOSystem.timeMachine().draw(forceRedraw);
}
else if(rerender)
{
clear();
myTIASurface->render();
myOSystem.timeMachine().render();
}
break; // EventHandlerState::TIMEMACHINE
}

File diff suppressed because it is too large Load Diff

View File

@ -51,6 +51,8 @@ void EditableWidget::setText(const string& str, bool changed)
for(char c: str)
if(_filter(tolower(c)))
_editString.push_back(c);
if(_maxLen)
_editString = _editString.substr(0, _maxLen);
if(oldEditString != _editString)
setDirty();
@ -125,9 +127,12 @@ bool EditableWidget::tryInsertChar(char c, int pos)
if(_filter(tolower(c)))
{
killSelectedText();
myUndoHandler->doChar(); // aggregate single chars
_editString.insert(pos, 1, c);
return true;
if(!_maxLen || _editString.length() < _maxLen)
{
myUndoHandler->doChar(); // aggregate single chars
_editString.insert(pos, 1, c);
return true;
}
}
return false;
}

View File

@ -50,6 +50,7 @@ class EditableWidget : public Widget, public CommandSender
~EditableWidget() override = default;
virtual void setText(const string& str, bool changed = false);
void setMaxLen(int len) { _maxLen = len; }
const string& getText() const { return _editString; }
bool isEditable() const { return _editable; }
@ -68,7 +69,7 @@ class EditableWidget : public Widget, public CommandSender
void receivedFocusWidget() override;
void lostFocusWidget() override;
void tick() override;
bool wantsToolTip() const override;
bool wantsToolTip() const override;
virtual void startEditMode() { setFlags(Widget::FLAG_WANTS_RAWDATA); }
virtual void endEditMode() { clearFlags(Widget::FLAG_WANTS_RAWDATA); }
@ -110,6 +111,7 @@ class EditableWidget : public Widget, public CommandSender
private:
bool _editable{true};
string _editString;
int _maxLen{0};
unique_ptr<UndoHandler> myUndoHandler;
int _caretPos{0};

View File

@ -68,7 +68,7 @@ GameInfoDialog::GameInfoDialog(
WidgetArray wid;
// Set real dimensions
setSize(54 * fontWidth + HBORDER * 2,
setSize(56 * fontWidth + HBORDER * 2,
_th + VGAP * 3 + lineHeight + 8 * (lineHeight + VGAP) + 1 * (infoLineHeight + VGAP) +
ifont.getLineHeight() + VGAP + buttonHeight + VBORDER * 2,
max_w, max_h);
@ -496,7 +496,7 @@ void GameInfoDialog::addHighScoresTab()
myHighScores = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "Enable High Scores",
kHiScoresChanged);
xpos += 20; ypos += lineHeight + VGAP;
xpos += CheckboxWidget::prefixSize(_font); ypos += lineHeight + VGAP * 2;
/*myARMGame = new CheckboxWidget(myTab, _font, xpos, ypos + 1, "read ARM cartridge RAM",
kHiScoresChanged);
@ -505,7 +505,7 @@ void GameInfoDialog::addHighScoresTab()
pwidth = _font.getStringWidth("4"); // popup
int awidth = EditTextWidget::calcWidth(_font, HSM::MAX_ADDR_CHARS); // addresses
int awidth = EditTextWidget::calcWidth(_font, 4); // addresses
int vwidth = EditTextWidget::calcWidth(_font, 3); // values
int swidth = EditTextWidget::calcWidth(_font, HSM::MAX_SPECIAL_NAME); // special
int fwidth = EditTextWidget::calcWidth(_font, 3); // variants
@ -514,33 +514,40 @@ void GameInfoDialog::addHighScoresTab()
"Variations");
myVariations = new EditTextWidget(myTab, _font, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myVariations->setTextFilter(fVars);
myVariations->setMaxLen(3);
myVariations->setToolTip("Define the number of game variations.");
wid.push_back(myVariations);
myVarAddressLabel = new StaticTextWidget(myTab, _font, myVariations->getRight() + 16, ypos + 1,
"Address ");
myVarAddressLabel = new StaticTextWidget(myTab, _font, myVariations->getRight() + fontWidth * 2,
ypos + 1, "Address ");
myVarAddress = new EditTextWidget(myTab, _font, myVarAddressLabel->getRight(), ypos - 1, awidth,
lineHeight);
myVarAddress->setTextFilter(fAddr);
myVarAddress->setMaxLen(4);
myVarAddress->setToolTip("Define the address (in hex format) where the variation number "
"is stored.");
wid.push_back(myVarAddress);
myVarAddressVal = new EditTextWidget(myTab, _font, myVarAddress->getRight() + 2, ypos - 1,
vwidth, lineHeight);
myVarAddressVal->setEditable(false);
myVarsBCD = new CheckboxWidget(myTab, _font, myVarAddressVal->getRight() + 16, ypos + 1, "BCD",
kHiScoresChanged);
myVarsBCD = new CheckboxWidget(myTab, _font, myVarAddressVal->getRight() + fontWidth * 2,
ypos + 1, "BCD", kHiScoresChanged);
myVarsBCD->setToolTip("Check when the variation number is stored as BCD.");
wid.push_back(myVarsBCD);
myVarsZeroBased = new CheckboxWidget(myTab, _font, myVarsBCD->getRight() + 16, ypos + 1,
"0-based", kHiScoresChanged);
myVarsZeroBased = new CheckboxWidget(myTab, _font, myVarsBCD->getRight() + fontWidth * 2,
ypos + 1, "0-based", kHiScoresChanged);
myVarsZeroBased->setToolTip("Check when the variation number is stored zero-based.");
wid.push_back(myVarsZeroBased);
ypos += lineHeight + VGAP * 4;
ypos += lineHeight + VGAP * 3;
myScoreLabel = new StaticTextWidget(myTab, _font, xpos, ypos + 1, "Score");
xpos += 20; ypos += lineHeight + VGAP;
xpos += fontWidth * 2; ypos += lineHeight + VGAP;
vwidth = _font.getStringWidth("AB") + 3;
vwidth = EditTextWidget::calcWidth(_font, 2); // address values
items.clear();
for (uInt32 i = 1; i <= HSM::MAX_SCORE_DIGITS; ++i)
VarList::push_back(items, std::to_string(i), std::to_string(i));
@ -548,6 +555,7 @@ void GameInfoDialog::addHighScoresTab()
myScoreDigitsLabel = new StaticTextWidget(myTab, _font, xpos, ypos + 1, "Digits ");
myScoreDigits = new PopUpWidget(myTab, _font, myScoreDigitsLabel->getRight(), ypos, pwidth,
lineHeight, items, "", 0, kHiScoresChanged);
myScoreDigits->setToolTip("Select the number of score digits displayed.");
wid.push_back(myScoreDigits);
items.clear();
@ -560,14 +568,17 @@ void GameInfoDialog::addHighScoresTab()
myTrailingZeroes = new PopUpWidget(myTab, _font, myTrailingZeroesLabel->getRight(), ypos,
pwidth, lineHeight,
items, "", 0, kHiScoresChanged);
myTrailingZeroes->setToolTip("Select the number of trailing score digits which are fixed to 0.");
wid.push_back(myTrailingZeroes);
myScoreBCD = new CheckboxWidget(myTab, _font, myVarsBCD->getLeft(), ypos + 1, "BCD",
kHiScoresChanged);
myScoreBCD->setToolTip("Check when the score is stored as BCD.");
wid.push_back(myScoreBCD);
myScoreInvert = new CheckboxWidget(myTab, _font, myScoreBCD->getRight() + 16, ypos + 1,
"Invert");
myScoreInvert = new CheckboxWidget(myTab, _font, myScoreBCD->getRight() + fontWidth * 2,
ypos + 1, "Invert");
myScoreInvert->setToolTip("Check when a lower score (e.g. a timer) is better.");
wid.push_back(myScoreInvert);
uInt32 s_xpos = xpos;
@ -579,6 +590,9 @@ void GameInfoDialog::addHighScoresTab()
{
myScoreAddress[a] = new EditTextWidget(myTab, _font, s_xpos, ypos - 1, awidth, lineHeight);
myScoreAddress[a]->setTextFilter(fAddr);
myScoreAddress[a]->setMaxLen(4);
myScoreAddress[a]->setToolTip("Define the addresses (in hex format, highest byte first) "
"where the score is stored.");
wid.push_back(myScoreAddress[a]);
s_xpos += myScoreAddress[a]->getWidth() + 2;
@ -593,14 +607,18 @@ void GameInfoDialog::addHighScoresTab()
myCurrentScoreLabel = new StaticTextWidget(myTab, _font, xpos, ypos + 1, "Current ");
myCurrentScore = new StaticTextWidget(myTab, _font, myCurrentScoreLabel->getRight(), ypos + 1,
"12345678");
myCurrentScore->setToolTip("The score read using the current definitions.");
xpos -= 20; ypos += lineHeight + VGAP * 3;
xpos -= fontWidth * 2; ypos += lineHeight + VGAP * 3;
vwidth = _font.getStringWidth("123") + 4;
vwidth = EditTextWidget::calcWidth(_font, 3); // score values
mySpecialLabel = new StaticTextWidget(myTab, _font, xpos, ypos + 1, "Special");
mySpecialName = new EditTextWidget(myTab, _font, mySpecialLabel->getRight() + 19, ypos - 1,
swidth, lineHeight);
mySpecialName = new EditTextWidget(myTab, _font, mySpecialLabel->getRight() + fontWidth,
ypos - 1, swidth, lineHeight);
mySpecialName->setTextFilter(fText);
mySpecialName->setMaxLen(HSM::MAX_SPECIAL_NAME);
mySpecialName->setToolTip("Define a short label (up to 5 chars) for the optional,\ngame's "
"special value (e.g. 'Level', 'Wave', 'Round'" + ELLIPSIS + ")");
wid.push_back(mySpecialName);
mySpecialAddressLabel = new StaticTextWidget(myTab, _font, myVarAddressLabel->getLeft(),
@ -608,6 +626,9 @@ void GameInfoDialog::addHighScoresTab()
mySpecialAddress = new EditTextWidget(myTab, _font, mySpecialAddressLabel->getRight(),
ypos - 1, awidth, lineHeight);
mySpecialAddress->setTextFilter(fAddr);
mySpecialAddress->setMaxLen(4);
mySpecialAddress->setToolTip("Define the address (in hex format) where the special "
"number is stored.");
wid.push_back(mySpecialAddress);
mySpecialAddressVal = new EditTextWidget(myTab, _font, mySpecialAddress->getRight() + 2,
ypos - 1, vwidth, lineHeight);
@ -615,10 +636,12 @@ void GameInfoDialog::addHighScoresTab()
mySpecialBCD = new CheckboxWidget(myTab, _font, myVarsBCD->getLeft(), ypos + 1, "BCD",
kHiScoresChanged);
mySpecialBCD->setToolTip("Check when the special number is stored as BCD.");
wid.push_back(mySpecialBCD);
mySpecialZeroBased = new CheckboxWidget(myTab, _font, mySpecialBCD->getRight() + 16, ypos + 1,
"0-based", kHiScoresChanged);
mySpecialZeroBased = new CheckboxWidget(myTab, _font, mySpecialBCD->getRight() + fontWidth * 2,
ypos + 1, "0-based", kHiScoresChanged);
mySpecialZeroBased->setToolTip("Check when the special number is stored zero-based.");
wid.push_back(mySpecialZeroBased);
ypos += lineHeight + VGAP * 3;
@ -627,6 +650,7 @@ void GameInfoDialog::addHighScoresTab()
myHighScoreNotes = new EditTextWidget(myTab, _font, mySpecialName->getLeft(), ypos - 1,
_w - HBORDER - mySpecialName->getLeft() - 2 , lineHeight);
myHighScoreNotes->setTextFilter(fText);
myHighScoreNotes->setToolTip("Define some free text which explains the high scores properties.");
wid.push_back(myHighScoreNotes);
// Add items for tab 4

View File

@ -178,6 +178,7 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent,
myDeleteButtons[r] = new ButtonWidget(this, _font, xposDelete, ypos + 1, fontWidth * 2, fontHeight, "X",
kDeleteSingle);
myDeleteButtons[r]->setID(r);
myDeleteButtons[r]->setToolTip("Click to delete this high score.");
wid.push_back(myDeleteButtons[r]);
ypos += lineHeight + VGAP;
@ -199,6 +200,7 @@ HighScoresDialog::HighScoresDialog(OSystem& osystem, DialogContainer& parent,
_w - HBORDER * 2, lineHeight);
addDefaultsOKCancelBGroup(wid, _font, "Save", "Cancel", " Reset ");
_defaultWidget->setToolTip("Click to reset all high scores of this variation.");
addToFocusList(wid);
}
@ -482,7 +484,6 @@ void HighScoresDialog::deleteRank(int rank)
myHighScoreRank--;
myEditRank--;
myEditNameWidgets[myEditRank]->setText(myEditNameWidgets[myEditRank + 1]->getText());
setDirty();
}
myDirty = true;
}

View File

@ -179,5 +179,5 @@ void ToolTip::show(const string& tip)
void ToolTip::render()
{
if(myTipShown)
mySurface->render(); // , cerr << " render tooltip" << endl;
mySurface->render();
}

View File

@ -47,6 +47,7 @@ WhatsNewDialog::WhatsNewDialog(OSystem& osystem, DialogContainer& parent, const
add(ypos, "fixed navigation bug in Video & Audio settings dialog");
add(ypos, "fixed autofire bug for trackball controllers");
#else
add(ypos, "added high scores saving");
add(ypos, "enhanced cut/copy/paste for text editing");
add(ypos, "added undo and redo to text editing");
add(ypos, "added wildcard support to launcher dialog filter");