scummvm/gui/about.cpp
2024-01-01 00:04:27 +01:00

1490 lines
34 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/metaengine.h"
#include "base/plugins.h"
#include "base/version.h"
#include "common/events.h"
#include "common/system.h"
#include "common/translation.h"
#include "common/util.h"
#include "graphics/surface.h"
#include "graphics/fonts/amigafont.h"
#include "gui/about.h"
#include "gui/gui-manager.h"
#include "gui/ThemeEval.h"
namespace GUI {
class EE;
class EEHandler {
public:
bool handleKeyDown(Common::KeyState &state);
};
enum {
kScrollStartDelay = 1500,
kScrollMillisPerPixel = 60
};
// Every Line should start with a letter followed by a digit. Currently those can be
// (all subject to change)
// Letter:
// C, L, R -- set center/left/right alignment
// A -- ASCII text to replace the next (latin1) line
// Digit:
// 0 - 2 -- set a custom color:
// 0 normal text
// 1 highlighted text
// 2 disabled text
// TODO: Maybe add a tab/indent feature; that is, make it possible to specify
// an amount by which that line shall be indented (the indent of course would have
// to be considered while performing any word wrapping, too).
//
// TODO: Add different font sizes (for bigger headlines)
// TODO: Allow color change in the middle of a line...
static const char *const copyright_text[] = {
"",
"C0""Copyright (C) 2001-2024 The ScummVM Team",
"C0""https://www.scummvm.org",
"",
"C0""ScummVM is the legal property of its developers, whose names are too numerous to list here. Please refer to the COPYRIGHT file distributed with this binary.",
"",
};
static const char *const gpl_text[] = {
"",
"C0""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 3 of the License, or (at your option) any later version.",
"C0""",
"C0""This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.",
"",
"C0""You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.",
"",
};
#include "gui/credits.h"
AboutDialog::AboutDialog()
: Dialog(10, 20, 300, 174),
_scrollPos(0), _scrollTime(0), _willClose(false), _autoScroll(true) {
reflowLayout();
int i;
for (i = 0; i < 1; i++)
_lines.push_back(Common::U32String());
Common::String version("C0""ScummVM ");
version += gScummVMVersion;
addLine(version);
// I18N: built on <build date> with <compiler>
Common::U32String date = Common::U32String::format(_("(built on %s with %s)"), gScummVMBuildDate, gScummVMCompiler);
addLine(Common::U32String("C2") + date);
for (i = 0; i < ARRAYSIZE(copyright_text); i++)
addLine(Common::U32String(copyright_text[i]));
Common::U32String features("C1");
features += _("Features compiled in:");
addLine(features);
Common::String featureList("C0");
featureList += gScummVMFeatures;
addLine(featureList);
_lines.push_back(Common::U32String());
Common::U32String extensionSupportString[3] = { _("not supported"), _("disabled"), _("enabled") };
byte sse2Support = 0;
byte avx2Support = 0;
byte neonSupport = 0;
#ifdef SCUMMVM_SSE2
++sse2Support;
if (g_system->hasFeature(OSystem::kFeatureCpuSSE2))
++sse2Support;
#endif
#ifdef SCUMMVM_AVX2
++avx2Support;
if (g_system->hasFeature(OSystem::kFeatureCpuAVX2))
++avx2Support;
#endif
#ifdef SCUMMVM_NEON
++neonSupport;
if (g_system->hasFeature(OSystem::kFeatureCpuNEON))
++neonSupport;
#endif
Common::U32String extensionsInfo("C1");
// I18N: CPU extensions are sets of extra processor instructions used to speed up operations. See Intel AVX2, ARM NEON, etc.
extensionsInfo += _("CPU extensions support:");
addLine(extensionsInfo);
Common::U32String compiledExtensionsList("C0");
compiledExtensionsList += Common::U32String::format("SSE2(%S) AVX2(%S) NEON(%S)",
extensionSupportString[sse2Support].c_str(),
extensionSupportString[avx2Support].c_str(),
extensionSupportString[neonSupport].c_str());
addLine(compiledExtensionsList);
_lines.push_back(Common::U32String());
Common::U32String engines("C1");
engines += _("Available engines:");
addLine(engines);
const PluginList &plugins = EngineMan.getPlugins(PLUGIN_TYPE_ENGINE);
PluginList::const_iterator iter = plugins.begin();
for (; iter != plugins.end(); ++iter) {
Common::String str;
const Plugin *p = EngineMan.findPlugin((*iter)->getName());
if (!p) {
warning("Cannot find plugin for %s", (*iter)->getName());
continue;
}
str = "C0";
str += p->get<MetaEngineDetection>().getEngineName();
addLine(str);
str = "C2";
str += p->get<MetaEngineDetection>().getOriginalCopyright();
addLine(str);
//addLine("");
}
for (i = 0; i < ARRAYSIZE(gpl_text); i++)
addLine(Common::U32String(gpl_text[i]));
_lines.push_back(Common::U32String());
for (i = 0; i < ARRAYSIZE(credits); i++)
addLine(Common::U32String(credits[i], Common::kUtf8));
}
void AboutDialog::addLine(const Common::U32String &str) {
Common::U32String::const_iterator strBeginItr = str.begin();
if (*strBeginItr == 0) {
_lines.push_back(Common::U32String());
} else {
Common::U32String format(str.begin(), str.begin() + 2);
strBeginItr += 2;
Common::U32String renderStr(strBeginItr, str.end());
Common::U32StringArray wrappedLines;
g_gui.getFont().wordWrapText(renderStr, _w - 2 * _xOff, wrappedLines);
for (Common::U32StringArray::const_iterator i = wrappedLines.begin(); i != wrappedLines.end(); ++i) {
_lines.push_back(format + *i);
}
}
}
void AboutDialog::open() {
_scrollTime = g_system->getMillis() + kScrollStartDelay;
_scrollPos = 0;
_willClose = false;
Dialog::open();
}
void AboutDialog::close() {
Dialog::close();
}
void AboutDialog::drawDialog(DrawLayer layerToDraw) {
Dialog::drawDialog(layerToDraw);
setTextDrawableArea(Common::Rect(_x, _y, _x + _w, _y + _h));
// Draw text
// TODO: Add a "fade" effect for the top/bottom text lines
// TODO: Maybe prerender all of the text into another surface,
// and then simply compose that over the screen surface
// in the right way. Should be even faster...
const int firstLine = _scrollPos / _lineHeight;
const int lastLine = MIN((_scrollPos + _h) / _lineHeight + 1, (uint32)_lines.size());
int y = _y + _yOff - (_scrollPos % _lineHeight);
for (int line = firstLine; line < lastLine; line++) {
Common::U32String str = _lines[line];
Common::U32String::const_iterator strLineItrBegin = _lines[line].begin();
Common::U32String::const_iterator strLineItrEnd = _lines[line].end();
Graphics::TextAlign align = Graphics::kTextAlignCenter;
ThemeEngine::WidgetStateInfo state = ThemeEngine::kStateEnabled;
if (strLineItrBegin != strLineItrEnd) {
switch (*strLineItrBegin) {
case 'C':
align = Graphics::kTextAlignCenter;
break;
case 'L':
align = Graphics::kTextAlignLeft;
break;
case 'R':
align = Graphics::kTextAlignRight;
break;
default:
error("Unknown scroller opcode '%c'", str[0]);
break;
}
switch (*(strLineItrBegin + 1)) {
case '0':
state = ThemeEngine::kStateEnabled;
break;
case '1':
state = ThemeEngine::kStateHighlight;
break;
case '2':
state = ThemeEngine::kStateDisabled;
break;
case '3':
warning("Need state for color 3");
// color = g_gui._shadowcolor;
break;
case '4':
warning("Need state for color 4");
// color = g_gui._bgcolor;
break;
default:
error("Unknown color type '%c'", str[1]);
}
strLineItrBegin += 2;
}
// Trim leading whitespaces if center mode is on
if (align == Graphics::kTextAlignCenter)
while (strLineItrBegin != strLineItrEnd && *strLineItrBegin == ' ')
strLineItrBegin++;
Common::U32String renderStr(strLineItrBegin, strLineItrEnd);
if (!renderStr.empty())
g_gui.theme()->drawText(Common::Rect(_x + _xOff, y, _x + _w - _xOff, y + g_gui.theme()->getFontHeight()),
renderStr, state, align, ThemeEngine::kTextInversionNone, 0, false,
ThemeEngine::kFontStyleBold, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
y += _lineHeight;
}
}
void AboutDialog::handleTickle() {
const uint32 t = g_system->getMillis();
int scrollOffset = ((int)t - (int)_scrollTime) / kScrollMillisPerPixel;
if (_autoScroll && scrollOffset > 0) {
int modifiers = g_system->getEventManager()->getModifierState();
// Scroll faster when shift is pressed
if (modifiers & Common::KBD_SHIFT)
scrollOffset *= 4;
// Reverse scrolling when alt is pressed
if (modifiers & Common::KBD_ALT)
scrollOffset *= -1;
_scrollPos += scrollOffset;
_scrollTime = t;
if (_scrollPos < 0) {
_scrollPos = 0;
} else if ((uint32)_scrollPos > _lines.size() * _lineHeight) {
_scrollPos = 0;
_scrollTime += kScrollStartDelay;
}
drawDialog(kDrawLayerForeground);
}
}
void AboutDialog::handleMouseUp(int x, int y, int button, int clickCount) {
// Close upon any mouse click
close();
}
void AboutDialog::handleMouseWheel(int x, int y, int direction) {
const int stepping = 5 * _lineHeight * direction;
if (stepping == 0)
return;
_autoScroll = false;
int newScrollPos = _scrollPos + stepping;
if (newScrollPos < 0) {
_scrollPos = 0;
} else if ((uint32)newScrollPos < _lines.size() * _lineHeight) {
_scrollPos = newScrollPos;
}
drawDialog(kDrawLayerForeground);
}
void AboutDialog::handleKeyDown(Common::KeyState state) {
EEHandler eeHandler;
if (eeHandler.handleKeyDown(state)) {
reflowLayout();
return;
}
if (state.ascii)
_willClose = true;
}
void AboutDialog::handleKeyUp(Common::KeyState state) {
if (state.ascii && _willClose)
close();
}
void AboutDialog::reflowLayout() {
Dialog::reflowLayout();
int i;
const int screenW = g_system->getOverlayWidth();
const int screenH = g_system->getOverlayHeight();
_xOff = g_gui.xmlEval()->getVar("Globals.About.XOffset", 5);
_yOff = g_gui.xmlEval()->getVar("Globals.About.YOffset", 5);
int outerBorder = g_gui.xmlEval()->getVar("Globals.About.OuterBorder");
_w = screenW - 2 * outerBorder;
_h = screenH - 2 * outerBorder;
_lineHeight = g_gui.getFontHeight() + 3;
// Heuristic to compute 'optimal' dialog width
int maxW = _w - 2*_xOff;
_w = 0;
for (i = 0; i < ARRAYSIZE(credits); i++) {
int tmp = g_gui.getStringWidth(credits[i]) + 5;
if (_w < tmp && tmp <= maxW) {
_w = tmp;
}
}
_w += 2*_xOff;
// Center the dialog
_x = (screenW - _w) / 2;
_y = (screenH - _h) / 2;
}
////////////////////////////////
//// Here we go
////////////////////////////////
enum {
kDirUp, kDirLeft, kDirRight
};
enum {
kModeMenu, kModePlay, kModePause
};
enum { kPlComp, kPlHuman };
enum { kSndPoint, kSndHit, kSndLoss };
enum {
kSpL1, kSpL2, kSpL3,
kSpR1, kSpR2, kSpR3,
kSpB1, kSpB2, kSpB3, kSpB4,
kSp0, kSp1, kSp2, kSp3, kSp4,
kSp5, kSp6, kSp7, kSp8, kSp9,
kSpCode1,
kSpCode1h = kSpCode1 + 6,
kSpNum0 = kSpCode1h + 6,
kSpStar = kSpNum0 + 10,
kSpSpace,
kSpLast
};
class EE {
public:
EE();
~EE();
void run();
private:
Graphics::Surface _back;
int _hits;
bool _rmode; // animation after loosing
int _score[2];
bool _soundEnabled;
bool _shouldQuit;
int _rcnt; //last 20 cycles of round
int _winner; //who wins the point or serving
int _tvelx, _tvely;
int _x[2], _y[2], _px[2];
int _frame[2], _frameindex[2];
int _rebound[2];
int _bx, _by, _pbx, _pby, _tbx, _tby, _bvelx, _bvely;
int _serve, _server, _servevel, _compserve;
int _hitter, _starter, _sstage;
int _bytop;
int _rnd;
int _olx, _oly, _orx, _ory, _obx, _oby;
bool _air;
bool _oCoords;
int _mode;
int _keymove[2][3];
int _opts;
int _opt;
Graphics::AmigaFont _font;
Graphics::Surface _sp[kSpLast];
Graphics::PixelFormat _format;
int _windowX, _windowY, _windowW, _windowH;
float _scale;
bool _inited;
uint32 _colorBlack, _colorBlue, _colorOrange, _colorKey;
void cls(bool update = true);
void loadSounds();
void processKeyUp(Common::Event &e);
void processKeyDown(Common::Event &e);
void getPos();
void setwinner();
void resetpt();
void calcscore();
int destination(int pl, int destx, int tol);
bool moveball();
void docollisions();
void computer0();
void computer1();
void init();
void draw(int sn, int x, int y);
void doMenu(Common::Event &e);
void putshapes();
void game();
void playSound(int d);
void setupGraphics();
void genField();
};
bool EEHandler::handleKeyDown(Common::KeyState &state) {
if (state.ascii == 'v') {
EE *ee = new EE();
ee->run();
delete ee;
g_gui.redrawFull();
return true;
}
return false;
}
EE::EE() {
init();
_scale = 1.0;
_windowW = 320;
_windowH = 200;
_windowX = _windowY = 0;
_format = g_system->getOverlayFormat();
_colorBlack = _colorBlue = _colorOrange = _colorKey = 0;
}
EE::~EE() {
for (int i = 0; i < kSpLast; i++)
_sp[i].free();
}
void EE::cls(bool update) {
_back.fillRect(Common::Rect(0, 0, _windowW, _windowH), _colorBlack);
if (update)
g_system->copyRectToOverlay(_back.getPixels(), _back.pitch, _windowX, _windowY, _windowW, _windowH);
}
void EE::run() {
if (!_inited)
return;
_shouldQuit = false;
setupGraphics();
init();
while (!_shouldQuit) {
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RETURN_TO_LAUNCHER:
_shouldQuit = true;
break;
case Common::EVENT_SCREEN_CHANGED:
if (g_gui.checkScreenChange())
setupGraphics();
break;
case Common::EVENT_LBUTTONDOWN:
break;
case Common::EVENT_KEYDOWN:
processKeyDown(event);
break;
case Common::EVENT_KEYUP:
processKeyUp(event);
break;
default:
break;
}
}
game();
putshapes();
g_system->updateScreen();
g_system->delayMillis(10);
}
}
const int polecol[] = {0, 1, 2, 3, 3, 4, 6, 7, 9, 14};
const int jump[] = {-4, -4, -3, -3, -3, -3, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4};
void EE::loadSounds() {
#if 0
if ($.isEmptyObject(sounds)) {
for (i = 0; i < snNames.length; i++) {
s = snNames[i];
sounds[s] = new Audio();
sounds[s].src = 'sounds/' + s + '.wav';
}
}
#endif
}
void EE::processKeyUp(Common::Event &e) {
switch (e.kbd.keycode) {
case Common::KEYCODE_LEFT: // left
_keymove[1][kDirLeft] = 0;
break;
case Common::KEYCODE_RIGHT: // right
_keymove[1][kDirRight] = 0;
break;
case Common::KEYCODE_UP: // top
_keymove[1][kDirUp] = 0;
break;
case Common::KEYCODE_a: //a
_keymove[0][kDirLeft] = 0;
break;
case Common::KEYCODE_d: // d
_keymove[0][kDirRight] = 0;
break;
case Common::KEYCODE_w: // w
_keymove[0][kDirUp] = 0;
break;
default:
break;
}
}
void EE::processKeyDown(Common::Event &e) {
if (_mode == kModeMenu) {
doMenu(e);
return;
}
switch (e.kbd.keycode) {
case Common::KEYCODE_LEFT: // left
_keymove[1][kDirLeft] = -2;
break;
case Common::KEYCODE_RIGHT: // right
_keymove[1][kDirRight] = 2;
break;
case Common::KEYCODE_UP: // top
_keymove[1][kDirUp]= 1;
break;
case Common::KEYCODE_a: //a
_keymove[0][kDirLeft] = -2;
break;
case Common::KEYCODE_d: // d
_keymove[0][kDirRight] = 2;
break;
case Common::KEYCODE_w: // w
_keymove[0][kDirUp] = 1;
break;
case Common::KEYCODE_ESCAPE:
_mode = kModeMenu;
break;
case Common::KEYCODE_p: // p
_keymove[0][kDirRight] = _keymove[0][kDirLeft] = _keymove[0][kDirUp] = 0;
_mode = (_mode == kModePlay) ? kModePause : kModePlay;
break;
case Common::KEYCODE_r: // r
if (_mode == kModePlay)
init();
break;
case Common::KEYCODE_s: // s
if (!_soundEnabled)
loadSounds();
_soundEnabled = !_soundEnabled;
break;
default:
break;
}
}
void EE::getPos() {
for (int i = 0; i < 2; i++) {
if (_keymove[i][kDirUp] && _frameindex[i] == -1)
_frameindex[i] = 0;
int velx = _keymove[i][kDirLeft] + _keymove[i][kDirRight];
int tx = _x[i] + velx;
int bnd = 3 + i * 155;
if (velx > 0) {
if (tx < bnd + 119)
_x[i] = tx;
else
_x[i] = bnd + 119;
} else if (tx > bnd) {
_x[i] = tx;
} else {
_x[i] = bnd;
}
if (_frameindex[i] == -2) {
_y[i] = 173;
_frame[i] = 0;
_frameindex[i] = -1;
}
if (_frameindex[i] == -1) {
if (velx != 0) {
if (abs(_px[i] - _x[i]) > 4) {
_frame[i] ^= 1;
_px[i] = _x[i];
}
} else {
_frame[i] = 0;
}
} else {
_frame[i] = 2 + (_frameindex[i] > 18);
if (_frameindex[i] == 19)
_y[i] -= 4;
_y[i] += jump[_frameindex[i]++];
if (_frameindex[i] > 37)
_frameindex[i] = -2;
}
}
}
void EE::setwinner() {
if (_hits > 2) {
_winner = 1 - _hitter;
} else {
_winner = _tbx < 150 ? 1 : 0;
}
_tvely = abs(_bvely) >> 3;
_tvelx = abs(_bvelx) >> 3;
}
void EE::resetpt() {
_keymove[0][kDirUp] = 0;
_keymove[1][kDirUp] = 0;
getPos();
if (abs(_bvelx) > _tvelx) {
if (_bvelx < 0)
_bvelx = -_tvelx;
else
_bvelx = _tvelx;
}
if (abs(_bvely) > _tvely) {
if (_bvely < 0)
_bvely = -_tvely;
else
_bvely = _tvely;
}
docollisions();
moveball();
putshapes();
}
void EE::calcscore() {
if (_winner == _server) {
if (_soundEnabled && _mode != kModeMenu) {
playSound(kSndPoint);
}
_score[_winner]++;
if (_score[_winner] > 14 && _score[_winner] - _score[1 - _winner] > 1) {
init();
return;
}
} else {
if (_soundEnabled && _mode != kModeMenu) {
playSound(kSndLoss);
}
_server = _winner;
}
_bx = (_tbx = _pbx = (64 + _winner * 165)) << 6;
_by = (_tby = _pby = 135) << 6;
_bvelx = _bvely = _hits = _rebound[0] = _rebound[1] = 0;
_serve = _servevel = 1;
_hitter = 2;
_compserve = abs(_rnd) % 5;
if (_score[_server] == 14)
_compserve = 5;
_sstage = 0;
}
int EE::destination(int pl, int destx, int tol) {
int xp = _x[pl];
if (abs(xp - destx) < tol) {
_keymove[pl][kDirLeft] = 0;
_keymove[pl][kDirRight] = 0;
return 1;
}
if (xp < destx) {
_keymove[pl][kDirLeft] = 0;
_keymove[pl][kDirRight] = 2;
} else {
_keymove[pl][kDirLeft] = -2;
_keymove[pl][kDirRight] = 0;
}
return 0;
}
int reset = false;
bool EE::moveball() {
if (!reset) {
_bx = 4096; _by = 8640; _bvelx = 125; _bvely = -259;
reset = true;
}
int rbvelx = _bvelx;
int rbvely = _bvely;
if (rbvelx > 319) rbvelx = 319;
if (rbvelx < -319) rbvelx = -319;
if (rbvely > 319) rbvely = 319;
if (rbvely < -319) rbvely = -319;
_pbx = _tbx;
_pby = _tby;
_bx += rbvelx;
_by += rbvely;
if (_bx < 320) {
_bx = 320;
rbvelx = -rbvelx;
rbvelx -= rbvelx >> 4;
rbvely -= rbvely >> 4;
if (_hitter == 1) {
_hitter = 2;
_hits = 0;
}
}
if (_bx > 18112) {
_bx = 18112;
rbvelx = -rbvelx;
rbvelx -= rbvelx >> 4;
rbvely -= rbvely >> 4;
if (_hitter == 0) {
_hitter = 2;
_hits = 0;
}
}
if (_by < 832) {
_by = 832;
rbvely = -rbvely;
rbvelx -= rbvelx >> 4;
rbvely -= rbvely >> 4;
}
bool hitfloor;
if (_by > 11392) {
_by = 11392;
rbvely = -rbvely;
hitfloor = false;
} else {
hitfloor = true;
}
//if (rbvely > 0) // Checked with original, this is how it is
// rbvely += 1;
//else
rbvely += 1;
_tbx = _bx >> 6;
_tby = _by >> 6;
_bvelx = rbvelx;
_bvely = rbvely;
return hitfloor;
}
void EE::docollisions() {
for (int i = 0; i < 2; i++) {
int dx = _tbx - _x[i] - i * 7;
int dy = (_tby - _y[i]) >> 1;
int dist = (dx >> 2) * dx + dy * dy;
if (dist < 110) {
int rndoff = 8 - (_rnd & 15);
if (_frameindex[i] > -1)
_bvely = -abs(_bvely) + (jump[_frameindex[i]] << (3 << _servevel));
else
_bvely = -abs(_bvely);
_bvely += rndoff;
_bvelx += dx * abs(dx) + ((_keymove[i][kDirRight] + _keymove[i][kDirLeft]) << (4 + _servevel)) + rndoff;
if (!_rebound[i]) {
if (_mode != kModeMenu) {
if (_soundEnabled && _mode != kModeMenu) {
playSound(kSndHit);
}
}
_bytop = 200;
_serve = 0;
_rebound[i] = 1;
if (_hitter != i) {
_hitter = i;
_hits = 0;
} else {
_hits++;
}
}
} else if (_rebound[i]) {
_rebound[i] = _servevel = 0;
}
}
int i = 1;
if (_tby > 91) {
if (_pbx < 128 && _tbx > 127) {
_bvelx = -abs(_bvelx) >> 1;
_bx = 127 * 64;
i = 0;
} else if (_pbx > 159 && _tbx < 160) {
_bvelx = abs(_bvelx) >> 1;
_bx = 160 * 64;
i = 0;
}
}
if (i && _tby > 81 && _tbx > 127 && _tbx < 160) {
if (_tby > 91) {
if (_tbx < 148)
_bvelx = -abs(_bvelx);
else
_bvelx = abs(_bvelx);
} else if ((_tbx > 147 && 161 - _tbx >= polecol[91 - _tby]) ||
(_tbx < 148 && _tbx - 133 >= polecol[91 - _tby])) {
if (_bvely > 0) {
int dx = _tbx - 145;
if (dx < -5) _bvelx = -abs(_bvelx);
if (dx > 5) _bvelx = abs(_bvelx);
_bvely = -abs(_bvely);
}
if (abs(_bvelx) > 32) _bvelx = _bvelx >> 1;
if (abs(_bvely) > 32) _bvely = _bvely >> 1;
}
}
}
void EE::computer0() {
_keymove[0][kDirUp] = 0;
if (_tby < _bytop) _bytop = _tby;
int rndoff = 5 - _rnd % 10;
int dest = 0;
if (_serve && ((_server & 1) == 0)) {
switch (_compserve) {
case 0:
dest = destination(0, 55, 2);
break;
case 1:
dest = destination(0, 84, 2);
break;
case 2:
dest = destination(0, 80, 2);
break;
case 3:
if (_sstage == 0) {
if ((dest = destination(0, 44, 2)))
_sstage = 1;
} else {
destination(0, 58, 2);
dest = 1;
}
break;
case 4:
if (_sstage == 0) {
if ((dest = destination(0, 90, 2)))
_sstage = 1;
} else {
destination(0, 58, 2);
dest = 1;
}
break;
case 5:
if (_sstage == 0) {
if (destination(0, 3, 2))
_sstage = 1;
dest = 0;
} else {
destination(0, 8 + _sstage++, 1);
dest = 1;
}
break;
default:
break;
}
_keymove[0][kDirUp] = dest;
} else if (_bvely > 0 && _tbx < 140) {
int ystep, destx;
if (_bvely >> 6 == 0)
ystep = 0;
else
ystep = (140 - _tby) / (_bvely >> 6);
if (ystep < 1 || (_bvelx >> 6) == 0)
destx = _tbx;
else
destx = _tbx + (_bvelx >> 6) * ystep - 4;
int dx = _x[0] - _tbx;
if (abs(_bvelx) < 128 && _bytop < 75) {
if ((_tby < 158) ^ (_bvelx < 0))
destination(0, _tbx - 15, 3);
else
destination(0, _tbx + 15, 3);
} else {
if (_tby > 130) {
if (abs(dx) > 6 && abs(_bvelx) < 1024) {
destination(0, _tbx - ((_x[0] - 60) >> 3), 3);
} else {
destination(0, _tbx + rndoff + ((_x[0] - 60) >> 3), 10);
_keymove[0][kDirUp] = _x[0] < 105 && (_hitter != 0 || _hits < 2);
}
} else {
if (destx < 3)
destx = 6 - destx;
if (destx > 123)
destx = 246 - destx;
destination(0, destx + rndoff, 3);
}
}
} else
destination(0, 56, 8);
}
void EE::computer1() {
_keymove[1][kDirUp] = 0;
if (_tby < _bytop) _bytop = _tby;
int rndoff = 5 - _rnd % 10;
if (_serve && ((_server & 1) == 1)) {
int dest = 0;
switch (_compserve) {
case 0:
dest = destination(1, 232, 2);
break;
case 1:
dest = destination(1, 202, 2);
break;
case 2:
dest = destination(1, 208, 2);
break;
case 3:
if (_sstage == 0) {
if ((dest = destination(1, 250, 2)))
_sstage = 1;
} else {
destination(1, 220, 2);
dest = 1;
}
break;
case 4:
if (_sstage == 0) {
if ((dest = destination(1, 190, 2)))
_sstage = 1;
} else {
destination(1, 230, 2);
dest = 1;
}
break;
case 5:
if (_sstage == 0) {
if (destination(1, 277, 2))
_sstage = 1;
dest = 0;
} else {
destination(1, 272 - _sstage++, 1);
dest = 1;
}
break;
default:
break;
}
_keymove[1][kDirUp] = dest;
} else if (_bvely > 0 && _tbx > 125) {
int ystep, destx;
if (_bvely >> 6 == 0)
ystep = 0;
else
ystep = (140 - _tby) / (_bvely >> 6);
if (ystep < 1 || (_bvelx >> 6) == 0)
destx = _tbx;
else
destx = _tbx + (_bvelx >> 6) * ystep - 4;
int dx = _x[1] - _tbx;
if (abs(_bvelx) < 128 && _bytop < 75) {
if ((_tby < 158) ^ (_bvelx < 0))
destination(1, _tbx + 15, 3);
else
destination(1, _tbx - 15, 3);
} else {
if (_tby > 130) {
if (abs(dx) > 6 && abs(_bvelx) < 1024)
destination(1, _tbx + ((_x[1] - 218) >> 3), 3);
else {
destination(1, _tbx - rndoff - ((_x[1] - 218) >> 3), 10);
_keymove[1][kDirUp] = _x[1] > 175 && (_hitter != 1 || _hits < 2);
}
} else {
if (destx < 158)
destx = 316 - destx;
if (destx > 277)
destx = 554 - destx;
destination(1, destx - rndoff, 3);
}
}
} else
destination(1, 211, 8);
}
void EE::init() {
_rnd = 0;
_starter = _winner = _hits = 0;
_bvelx = _bvely = 0;
_tbx = 200; _tby = 20;
_bytop = 200;
_x[0] = 64; _x[1] = 226;
_air = false;
_rcnt = 0;
_tvelx = _tvely = 0;
for (int i = 0; i < 2; i++) {
_px[i] = _x[i];
_y[i] = 173;
_frameindex[i] = -1;
_rebound[i] = 0;
_score[i] = 0;
_keymove[i][kDirRight] = _keymove[i][kDirLeft] = _keymove[i][kDirUp] = 0;
_frame[i] = 0;
}
_oCoords = false;
_opts = 3;
_opt = 0;
_mode = kModeMenu;
_soundEnabled = false;
_olx = _oly = _orx = _ory = _obx = _oby = 0;
_compserve = 0;
_sstage = 0;
_tbx = 64 + _starter * 165;
_pbx = _tbx;
_bx = _tbx << 6;
_tby = 135;
_pby = 135;
_by = _tby << 6;
_server = 2 + _starter;
_hitter = 2;
_serve = _servevel = 1;
_rmode = false;
_shouldQuit = false;
_inited = true;
}
void EE::draw(int sn, int x1, int y1) {
int x = x1 * _scale;
int y = y1 * _scale;
_back.copyRectToSurfaceWithKey(_sp[sn].getPixels(), _sp[sn].pitch, x, y, _sp[sn].w, _sp[sn].h, _colorKey);
g_system->copyRectToOverlay(_back.getBasePtr(x, y), _back.pitch, _windowX + x, _windowY + y, _sp[sn].w, _sp[sn].h);
}
void EE::putshapes() {
int sprite;
cls(false);
if (_oCoords) {
int obx = _obx * _scale, oby = _oby * _scale;
int olx = _olx * _scale, oly = _oly * _scale;
int orx = _orx * _scale, ory = _ory * _scale;
g_system->copyRectToOverlay(_back.getBasePtr(obx, oby), _back.pitch, _windowX + obx, _windowY + oby, _sp[kSpB1].w, _sp[kSpB1].h);
g_system->copyRectToOverlay(_back.getBasePtr(olx, oly), _back.pitch, _windowX + olx, _windowY + oly, _sp[kSpL1].w, _sp[kSpL1].h);
g_system->copyRectToOverlay(_back.getBasePtr(orx, ory), _back.pitch, _windowX + orx, _windowY + ory, _sp[kSpR1].w, _sp[kSpR1].h);
}
sprite = kSpB1 + (_tbx / 16) % 4;
draw(sprite, _tbx, _tby);
_obx = _tbx;
_oby = _tby;
if (_y[0] < 173) {
sprite = kSpL3;
} else {
if ((_x[0] / 8) % 2)
sprite = kSpL1;
else
sprite = kSpL2;
}
draw(sprite, _x[0], _y[0]);
_olx = _x[0];
_oly = _y[0];
if (_y[1] < 173) {
sprite = kSpR3;
} else {
if ((_x[1] / 8) % 2)
sprite = kSpR1;
else
sprite = kSpR2;
}
draw(sprite, _x[1], _y[1]);
_orx = _x[1];
_ory = _y[1];
_oCoords = true;
const int frames[] = {
153, 106, 160, 200,
4, 13, 6, 200,
310, 13, 312, 200,
4, 13, 312, 14,
4, 199, 312, 200,
154, 107, 159, 199
};
uint32 color1 = _back.format.RGBToColor(5 * 16, 7 * 16, 8 * 16);
uint32 color2 = _back.format.RGBToColor(6 * 16, 8 * 16, 12 * 16);
const int *ptr = frames;
for (int i = 0; i < 6; i++, ptr += 4) {
Common::Rect r(ptr[0] * _scale, ptr[1] * _scale, ptr[2] * _scale, ptr[3] * _scale);
_back.fillRect(r, (i < 5 ? color1 : color2));
g_system->copyRectToOverlay(_back.getBasePtr(r.left, r.top), _back.pitch, _windowX + r.left, _windowY + r.top, r.width(), r.height());
}
for (int i = 0; i < 2; i++) {
int startx = i ? 264 : 32;
draw(kSpNum0 + _score[i] / 10, startx, 1);
startx += 8;
draw(kSpNum0 + _score[i] % 10, startx, 1);
startx += 8;
draw(_server == i ? kSpStar : kSpSpace, startx, 1);
}
for (int i = 0; i < 6; i++) {
int sx = i == 0 ? 92 : 80;
int sy = i == 0 ? 2 : 20;
int code = i == 0 ? kSpCode1 : i - 1 == _opt ? kSpCode1h + i : kSpCode1 + i;
draw(code, sx, sy + i * 10);
if (_mode != kModeMenu)
break;
}
}
void EE::doMenu(Common::Event &e) {
switch (e.kbd.keycode) {
case Common::KEYCODE_UP:
_opt--;
if (_opt < 0)
_opt = 4;
break;
case Common::KEYCODE_DOWN:
_opt = (_opt + 1) % 5;
break;
case Common::KEYCODE_RETURN:
if (_opt < 4) {
_opts = _opt;
_mode = kModePlay;
cls();
} else {
_shouldQuit = true;
}
break;
case Common::KEYCODE_ESCAPE:
_shouldQuit = true;
break;
default:
break;
}
}
void EE::game() {
if (_mode == kModePlay || _mode == kModeMenu) {
_rnd = (_rnd * 5 + 1) % 32767;
if (_opts & 1)
computer0();
if (_opts & 2)
computer1();
if (_rmode) {
if (_rcnt-- > 0 || _frameindex[0] != -1 || _frameindex[1] != -1) {
resetpt();
} else {
_rmode = false;
calcscore();
}
return;
}
getPos();
if (_serve) {
docollisions();
_air = true;
} else {
if (_air) {
docollisions();
_air = moveball();
}
}
if (!_air || _hits > 2) {
setwinner();
_rmode = true;
_rcnt = 20;
}
}
}
void EE::playSound(int sound) {
// FIXME
}
static const uint32 ball[21] = {
0x0000fd00, 0x00074ac0, 0x000afde0, 0x001703e8, 0x0038f7d8, 0x0056f73c,
0x00aef6fc, 0x00cf79fb, 0x01afbbf7, 0x01b7bdcd, 0x0077dd3a, 0x013beef3,
0x01bbef6a, 0x015df78b, 0x00defbea, 0x00ef7dd8, 0x00779eb4, 0x0029ef68,
0x001ef4a0, 0x000673c0, 0x0000ae00
};
static const uint32 head[38] = {
0xbb400000, 0x0000000f, 0xea650000, 0x000003af, 0xfaaa6000, 0x00000e7f,
0xfea5a400, 0x03ff2af0, 0x3eaa6900, 0x3ea969c0, 0x3ea99940, 0xeaaa69c8,
0x3a669540, 0xea995540, 0x5a555550, 0xa9aa999a, 0x65a65550, 0x2aa69a5a,
0x59555650, 0x02aaaa55, 0x6aa66550, 0x000aa950, 0x6a569950, 0x000a9500,
0xa9595580, 0x00015001, 0xa9a55540, 0x00000015, 0xa9a9a500, 0x0000015a,
0x59695800, 0x00001659, 0x65659000, 0x00000a96, 0xa65a8000, 0x000002aa,
0xa6500000, 0x0000002a
};
static const uint32 legs[42] = {
0xa0000000, 0x00000000, 0x80000000, 0x00000002, 0x80000000, 0x00000002, 0xa0000000,
0x00000000, 0x50000000, 0x00000000, 0xf0000000, 0x00000003, 0xfc000000, 0x000003ff,
0xa0000000, 0x00000002, 0x0a000000, 0x0000000a, 0x02400000, 0x00000028, 0x00700000,
0x00000028, 0x00fc0000, 0x00000014, 0x03c00000, 0x000000fc, 0x0f000000, 0x0000ffff,
0xa0000000, 0x00000000, 0x80000000, 0x00000002, 0xa8000000, 0x00000002, 0x29c00000,
0x00000000, 0x01f00000, 0x00000000, 0x0fc00000, 0x00000000, 0xfc000000, 0x00000000
};
static const int spcolors[10 * 3] = {
0, 0, 0, 8, 15, 8, 6, 12, 6, 12, 13, 12,
0, 0, 0, 15, 8, 8, 12, 6, 6, 13, 12, 12,
0, 0, 0, 12, 12, 12
};
const char *const codes =
"Dvhgkm#Ztrsm|ffrs(#$%&'#$%&O}pes&}{1$M{tiq$%&'#$M{tiq${y5(Fsrv||hv%&'#$"
"Hutxxxjx'~v2%N|udr%&'#Gtsw}wiw&}{1$Hutxxxjx'#$%&'(#$%W|qw$%&'(#$%&'";
void EE::setupGraphics() {
// Determine scale factor
float scaleX = g_system->getOverlayWidth() * 0.9 / 320;
float scaleY = g_system->getOverlayHeight() * 0.9 / 200;
_scale = MIN(scaleX, scaleY);
_windowW = 320 * _scale;
_windowH = 200 * _scale;
_windowX = (g_system->getOverlayWidth() > 320) ? (g_system->getOverlayWidth() - _windowW) / 2 : 0;
_windowY = (g_system->getOverlayHeight() > 200) ? (g_system->getOverlayHeight() - _windowH) / 2 : 0;
_format = g_system->getOverlayFormat();
_back.create(_windowW, _windowH, _format);
_colorBlack = _format.RGBToColor( 0 * 16, 0 * 16, 0 * 16);
_colorBlue = _format.RGBToColor( 5 * 16, 7 * 16, 8 * 16);
_colorOrange = _format.RGBToColor(15 * 16, 7 * 16, 8 * 16);
_colorKey = _colorBlack;
cls();
uint32 palette[12];
for (int i = 0; i < 10 * 3; i += 3)
palette[i / 3] = _back.format.RGBToColor(spcolors[i] * 16, spcolors[i + 1] * 16, spcolors[i + 2] * 16);
Graphics::Surface tmp;
int w = 25, h = 21;
tmp.create(w, h, g_system->getOverlayFormat());
for (int s = 0; s < 4; s++) {
int posy = 0, dy = 1;
if (s & 2) { posy = 20; dy = -1; }
for (int y = 0; y < h; y++, posy += dy) {
uint32 pixels = ball[y];
int posx = 0, dx = 1;
if (s & 1) { posx = 24; dx = -1; }
for (int x = 0; x < w; x++, posx += dx) {
int color = pixels & 1;
pixels >>= 1;
tmp.setPixel(posx, posy, palette[color + 8]);
}
}
Graphics::Surface *tmp2 = tmp.scale(w * _scale, h * _scale);
_sp[kSpB1 + s].copyFrom(*tmp2);
tmp2->free();
delete tmp2;
}
tmp.free();
w = 32;
h = 26;
tmp.create(w, h, g_system->getOverlayFormat());
for (int s = 0; s < 6; s++) {
for (int y = 0; y < h; y++) {
const uint32 *ptr = (y < 19) ? &head[y * 2] : &legs[(y - 19 + (s % 3) * 7) * 2];
uint32 pixels = *ptr++;
int posx = 0, dx = 1;
if (s > 2) { posx = 31; dx = -1; }
for (int x = 0; x < w; x++, posx += dx) {
int color = pixels & 3;
pixels >>= 2;
if (x == 15)
pixels = *ptr;
tmp.setPixel(posx, y, palette[color + 4 * (s / 3)]);
}
}
Graphics::Surface *tmp2 = tmp.scale(w * _scale, h * _scale);
_sp[kSpL1 + s].copyFrom(*tmp2);
tmp2->free();
delete tmp2;
}
tmp.free();
w = 23 * 8;
h = 10;
tmp.create(w, h, g_system->getOverlayFormat());
for (int hl = 0; hl < 2; hl++) {
for (int i = 0; i < 6; i++) {
tmp.fillRect(Common::Rect(0, 0, w, h), hl == 1 ? _colorBlue : _colorKey);
char buf[100];
int c;
for (c = 0; c < 23; c++)
buf[c] = codes[c + 23 * i] - 3 - c % 6;
buf[c] = 0;
int c1 = i == 0 ? _colorOrange : hl == 1 ? _colorKey : _colorBlue;
_font.drawString(&tmp, buf, 0, 1, w, c1, Graphics::kTextAlignLeft, 0, false);
Graphics::Surface *tmp2 = tmp.scale(w * _scale, h * _scale, true);
_sp[kSpCode1 + hl * 6 + i].copyFrom(*tmp2);
tmp2->free();
delete tmp2;
}
}
tmp.free();
w = 8;
h = 10;
tmp.create(w, h, g_system->getOverlayFormat());
for (int i = 0; i < 12; i++) {
tmp.fillRect(Common::Rect(0, 0, w, h), _colorKey);
char buf[2];
buf[0] = i == 10 ? '*' : i == 11 ? ' ' : '0' + i;
buf[1] = 0;
int c = i > 9 ? _colorBlue : _colorOrange;
_font.drawString(&tmp, buf, 0, 1, w, c, Graphics::kTextAlignLeft, 0, false);
Graphics::Surface *tmp2 = tmp.scale(w * _scale, h * _scale, true);
_sp[kSpNum0 + i].copyFrom(*tmp2);
tmp2->free();
delete tmp2;
}
tmp.free();
}
} // End of namespace GUI