2022-08-13 11:16:55 +02:00
|
|
|
/* 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/>.
|
|
|
|
*
|
|
|
|
*/
|
2021-04-12 21:29:57 -03:00
|
|
|
|
|
|
|
#include "common/config-manager.h"
|
|
|
|
#include "common/events.h"
|
|
|
|
#include "common/file.h"
|
2022-06-25 17:08:24 +02:00
|
|
|
#include "common/math.h"
|
2022-08-28 09:56:01 +02:00
|
|
|
#include "common/unzip.h"
|
2022-08-15 17:23:22 +02:00
|
|
|
#include "graphics/cursorman.h"
|
2021-04-12 21:29:57 -03:00
|
|
|
|
|
|
|
#include "freescape/freescape.h"
|
2022-07-04 21:47:00 +02:00
|
|
|
#include "freescape/language/8bitDetokeniser.h"
|
2022-10-02 19:16:51 +02:00
|
|
|
#include "freescape/neo.h"
|
2021-04-15 20:23:38 -03:00
|
|
|
|
2021-04-12 21:29:57 -03:00
|
|
|
namespace Freescape {
|
|
|
|
|
2021-04-24 15:04:18 -03:00
|
|
|
FreescapeEngine *g_freescape = NULL;
|
|
|
|
|
2022-09-23 21:35:55 +02:00
|
|
|
FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
|
2022-10-02 19:16:51 +02:00
|
|
|
: Engine(syst), _gameDescription(gd), _gfx(nullptr) {
|
2021-04-24 15:04:18 -03:00
|
|
|
g_freescape = this;
|
2022-08-15 17:23:22 +02:00
|
|
|
if (!ConfMan.hasKey("render_mode") || ConfMan.get("render_mode").empty())
|
2022-10-29 15:25:38 +02:00
|
|
|
_renderMode = Common::kRenderEGA;
|
2022-07-03 18:46:10 +02:00
|
|
|
else
|
2022-10-29 15:25:38 +02:00
|
|
|
_renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
|
2022-07-03 18:46:10 +02:00
|
|
|
|
2022-10-26 13:18:59 +02:00
|
|
|
_binaryBits = 0;
|
2022-10-01 20:12:35 +02:00
|
|
|
_screenW = 320;
|
|
|
|
_screenH = 200;
|
|
|
|
|
2022-09-24 23:35:14 +02:00
|
|
|
if (isAmiga()) {
|
2022-10-29 15:25:38 +02:00
|
|
|
_renderMode = Common::kRenderAmiga;
|
2022-10-01 20:12:35 +02:00
|
|
|
} else if (isAtariST()) {
|
2022-10-29 15:25:38 +02:00
|
|
|
_renderMode = Common::kRenderAtariST;
|
2022-09-24 23:35:14 +02:00
|
|
|
}
|
|
|
|
|
2022-10-31 12:35:29 +01:00
|
|
|
_variant = gd->flags;
|
2022-10-01 09:44:36 +02:00
|
|
|
|
2022-09-03 10:56:50 +02:00
|
|
|
if (!Common::parseBool(ConfMan.get("prerecorded_sounds"), _usePrerecordedSounds))
|
|
|
|
error("Failed to parse bool from prerecorded_sounds option");
|
|
|
|
|
2022-10-26 13:18:59 +02:00
|
|
|
_startArea = 0;
|
|
|
|
_startEntrance = 0;
|
2022-06-25 17:08:24 +02:00
|
|
|
_currentArea = nullptr;
|
2022-10-26 13:18:59 +02:00
|
|
|
_rotation = Math::Vector3d(0, 0, 0);
|
|
|
|
_position = Math::Vector3d(0, 0, 0);
|
|
|
|
_lastPosition = Math::Vector3d(0, 0, 0);
|
|
|
|
_velocity = Math::Vector3d(0, 0, 0);
|
|
|
|
_cameraFront = Math::Vector3d(0, 0, 0);
|
|
|
|
_cameraRight = Math::Vector3d(0, 0, 0);
|
|
|
|
_yaw = 0;
|
|
|
|
_pitch = 0;
|
|
|
|
_upVector = Math::Vector3d(0, 1, 0);
|
2022-07-11 21:35:35 +02:00
|
|
|
_movementSpeed = 1.5f;
|
2022-08-15 17:23:22 +02:00
|
|
|
_mouseSensitivity = 0.25f;
|
2022-10-05 09:07:29 +02:00
|
|
|
_demoMode = false;
|
2022-10-23 18:48:34 +02:00
|
|
|
_shootMode = false;
|
|
|
|
_crossairPosition.x = _screenW / 2;
|
|
|
|
_crossairPosition.y = _screenH / 2;
|
2022-10-19 19:27:51 +02:00
|
|
|
_demoIndex = 0;
|
2022-10-22 22:10:52 +02:00
|
|
|
_currentDemoInputCode = 0;
|
|
|
|
_currentDemoInputRepetition = 0;
|
2022-07-16 13:31:03 +02:00
|
|
|
_flyMode = false;
|
2022-09-10 13:21:12 +02:00
|
|
|
_noClipMode = false;
|
2022-08-14 11:12:41 +02:00
|
|
|
_playerHeightNumber = 1;
|
2022-10-24 19:33:48 +02:00
|
|
|
_angleRotationIndex = 0;
|
2022-10-02 19:16:51 +02:00
|
|
|
_border = nullptr;
|
|
|
|
_title = nullptr;
|
2022-10-02 20:18:23 +02:00
|
|
|
_titleTexture = nullptr;
|
2021-10-23 21:49:50 +02:00
|
|
|
_borderTexture = nullptr;
|
2022-09-23 19:18:48 +02:00
|
|
|
_uiTexture = nullptr;
|
2022-09-24 19:54:43 +02:00
|
|
|
_fontLoaded = false;
|
2022-10-26 13:18:59 +02:00
|
|
|
_dataBundle = nullptr;
|
|
|
|
|
|
|
|
_lastMousePos = Common::Point(0, 0);
|
|
|
|
_lastFrame = 0;
|
|
|
|
_nearClipPlane = 1;
|
2022-10-26 22:19:46 +02:00
|
|
|
_farClipPlane = 8192 + 1802; // Added some extra distance to avoid flickering
|
2022-10-26 13:18:59 +02:00
|
|
|
|
|
|
|
// These depends on the specific game
|
|
|
|
_playerHeight = 0;
|
|
|
|
_playerWidth = 0;
|
|
|
|
_playerDepth = 0;
|
|
|
|
_colorNumber = 0;
|
2022-09-23 19:18:48 +02:00
|
|
|
|
2022-08-19 15:27:42 +02:00
|
|
|
_fullscreenViewArea = Common::Rect(0, 0, _screenW, _screenH);
|
|
|
|
_viewArea = _fullscreenViewArea;
|
2021-04-12 21:29:57 -03:00
|
|
|
_rnd = new Common::RandomSource("freescape");
|
2022-10-26 13:18:59 +02:00
|
|
|
_gfx = nullptr;
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
FreescapeEngine::~FreescapeEngine() {
|
|
|
|
delete _rnd;
|
2022-10-25 12:44:39 +02:00
|
|
|
|
|
|
|
if (_title && _title != _border) {
|
|
|
|
_title->free();
|
|
|
|
delete _title;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_border) {
|
|
|
|
_border->free();
|
|
|
|
delete _border;
|
|
|
|
}
|
|
|
|
|
2022-09-23 19:18:48 +02:00
|
|
|
delete _borderTexture;
|
|
|
|
delete _uiTexture;
|
2022-10-25 12:44:39 +02:00
|
|
|
delete _titleTexture;
|
2022-09-23 19:18:48 +02:00
|
|
|
|
2022-10-29 09:21:28 +02:00
|
|
|
for (auto &it : _areaMap)
|
|
|
|
delete it._value;
|
2022-10-25 15:31:11 +02:00
|
|
|
|
2021-04-24 13:32:05 -03:00
|
|
|
delete _gfx;
|
2022-10-25 21:52:32 +02:00
|
|
|
delete _dataBundle;
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2021-04-16 21:08:56 -03:00
|
|
|
void FreescapeEngine::drawBorder() {
|
2022-07-04 20:43:02 +02:00
|
|
|
if (!_border)
|
2021-04-16 22:03:23 -03:00
|
|
|
return;
|
2021-04-24 13:32:05 -03:00
|
|
|
|
2022-08-19 15:27:42 +02:00
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
2021-10-23 21:49:50 +02:00
|
|
|
if (!_borderTexture)
|
|
|
|
_borderTexture = _gfx->createTexture(_border);
|
2022-08-19 15:27:42 +02:00
|
|
|
_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _borderTexture);
|
2022-07-22 23:41:09 +02:00
|
|
|
_gfx->setViewport(_viewArea);
|
2021-04-16 21:08:56 -03:00
|
|
|
}
|
|
|
|
|
2022-10-02 20:18:23 +02:00
|
|
|
void FreescapeEngine::drawTitle() {
|
|
|
|
if (!_title)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
|
|
|
if (!_titleTexture)
|
|
|
|
_titleTexture = _gfx->createTexture(_title);
|
|
|
|
_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _titleTexture);
|
|
|
|
_gfx->setViewport(_viewArea);
|
|
|
|
}
|
|
|
|
|
2021-09-26 18:29:12 +02:00
|
|
|
void FreescapeEngine::loadAssets() {
|
2022-10-26 21:21:55 +02:00
|
|
|
error("Function \"%s\" not implemented", __FUNCTION__);
|
2021-09-26 18:29:12 +02:00
|
|
|
}
|
|
|
|
|
2022-06-25 17:08:24 +02:00
|
|
|
// Taken from the Myst 3 codebase, it should be abstracted
|
|
|
|
Math::Vector3d FreescapeEngine::directionToVector(float pitch, float heading) {
|
|
|
|
Math::Vector3d v;
|
|
|
|
|
|
|
|
float radHeading = Common::deg2rad(heading);
|
|
|
|
float radPitch = Common::deg2rad(pitch);
|
|
|
|
|
|
|
|
v.setValue(0, cos(radPitch) * cos(radHeading));
|
|
|
|
v.setValue(1, sin(radPitch));
|
|
|
|
v.setValue(2, cos(radPitch) * sin(radHeading));
|
2022-09-05 12:47:16 +02:00
|
|
|
v.normalize();
|
2022-06-25 17:08:24 +02:00
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2022-08-19 15:27:42 +02:00
|
|
|
void FreescapeEngine::drawUI() {
|
2022-10-23 18:48:34 +02:00
|
|
|
_gfx->renderCrossair(0, _crossairPosition);
|
2022-08-20 13:59:08 +02:00
|
|
|
_gfx->setViewport(_viewArea);
|
2022-08-19 15:27:42 +02:00
|
|
|
}
|
|
|
|
|
2022-06-25 17:08:24 +02:00
|
|
|
void FreescapeEngine::drawFrame() {
|
2022-06-22 22:02:09 +02:00
|
|
|
_gfx->updateProjectionMatrix(60.0, _nearClipPlane, _farClipPlane);
|
|
|
|
_gfx->positionCamera(_position, _position + _cameraFront);
|
2022-06-25 17:08:24 +02:00
|
|
|
_currentArea->draw(_gfx);
|
2022-07-04 20:28:45 +02:00
|
|
|
drawBorder();
|
2022-08-19 15:27:42 +02:00
|
|
|
drawUI();
|
2022-06-22 22:02:09 +02:00
|
|
|
}
|
|
|
|
|
2022-08-21 00:44:43 +02:00
|
|
|
void FreescapeEngine::pressedKey(const int keycode) {}
|
|
|
|
|
2022-10-05 09:07:29 +02:00
|
|
|
void FreescapeEngine::generateInput() {
|
|
|
|
Common::Event event;
|
2022-10-19 19:27:51 +02:00
|
|
|
if (isDOS()) {
|
|
|
|
|
2022-10-22 22:10:52 +02:00
|
|
|
if (_currentDemoInputRepetition == 0) {
|
|
|
|
_currentDemoInputRepetition = 1;
|
|
|
|
_currentDemoInputCode = _demoData[_demoIndex++];
|
2022-10-31 09:02:52 +01:00
|
|
|
if (_currentDemoInputCode & 0x80) {
|
|
|
|
_currentDemoInputRepetition = (_currentDemoInputCode & 0x7F) + 1;
|
|
|
|
if (_currentDemoInputRepetition == 1)
|
|
|
|
_currentDemoInputRepetition = 255;
|
2022-10-22 22:10:52 +02:00
|
|
|
_currentDemoInputCode = _demoData[_demoIndex++];
|
|
|
|
}
|
2022-10-19 19:27:51 +02:00
|
|
|
}
|
|
|
|
|
2022-10-22 22:10:52 +02:00
|
|
|
if (_currentDemoInputCode >= 0x16 && _currentDemoInputCode <= 0x1a) {
|
|
|
|
// 0x16 -> left click / shoot
|
|
|
|
// 0x17 -> right?
|
|
|
|
// 0x18 -> left
|
|
|
|
// 0x19 -> down?
|
|
|
|
// 0x1a -> up
|
|
|
|
// TODO: mouse events
|
|
|
|
} else if (_currentDemoInputCode == 0x7f) {
|
2022-10-31 09:02:52 +01:00
|
|
|
// NOP
|
2022-10-22 22:10:52 +02:00
|
|
|
} else {
|
|
|
|
event = Common::Event();
|
|
|
|
event.type = Common::EVENT_KEYDOWN;
|
|
|
|
event.kbd.keycode = (Common::KeyCode)decodeDOSKey(_currentDemoInputCode);
|
|
|
|
event.customType = 0xde00;
|
2022-10-19 19:27:51 +02:00
|
|
|
g_system->getEventManager()->pushEvent(event);
|
2022-10-26 20:58:04 +02:00
|
|
|
debugC(1, kFreescapeDebugMove, "Pushing key: %x with repetition %d", event.kbd.keycode, _currentDemoInputRepetition);
|
2022-10-31 09:02:52 +01:00
|
|
|
g_system->delayMillis(100);
|
2022-10-19 19:27:51 +02:00
|
|
|
}
|
2022-10-22 22:10:52 +02:00
|
|
|
_currentDemoInputRepetition--;
|
2022-10-19 19:27:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-24 09:40:38 +02:00
|
|
|
int mouseX = _demoData[_demoIndex++] << 1;
|
2022-10-19 19:27:51 +02:00
|
|
|
int mouseY = _demoData[_demoIndex++];
|
2022-10-26 20:58:04 +02:00
|
|
|
debugC(1, kFreescapeDebugMove, "Mouse moved to: %d, %d", mouseX, mouseY);
|
2022-10-19 19:27:51 +02:00
|
|
|
|
2022-10-24 09:56:31 +02:00
|
|
|
event.type = Common::EVENT_MOUSEMOVE;
|
2022-10-19 19:27:51 +02:00
|
|
|
event.mouse = Common::Point(mouseX, mouseY);
|
2022-10-05 09:07:29 +02:00
|
|
|
event.customType = 0xde00;
|
2022-10-24 09:56:31 +02:00
|
|
|
g_system->getEventManager()->pushEvent(event);
|
2022-10-05 09:07:29 +02:00
|
|
|
|
2022-10-19 19:27:51 +02:00
|
|
|
byte nextKeyCode = _demoData[_demoIndex++];
|
|
|
|
|
2022-10-24 09:40:38 +02:00
|
|
|
if (nextKeyCode == 0x30) {
|
2022-10-24 09:56:31 +02:00
|
|
|
event.type = Common::EVENT_LBUTTONDOWN; // Keep same event fields
|
|
|
|
g_system->getEventManager()->pushEvent(event);
|
2022-10-19 19:27:51 +02:00
|
|
|
nextKeyCode = _demoData[_demoIndex++];
|
2022-10-24 09:40:38 +02:00
|
|
|
} else {
|
2022-10-26 20:58:04 +02:00
|
|
|
while (nextKeyCode != 0) {
|
2022-10-24 09:40:38 +02:00
|
|
|
event = Common::Event();
|
|
|
|
event.type = Common::EVENT_KEYDOWN;
|
|
|
|
event.kbd.keycode = (Common::KeyCode)decodeAmigaAtariKey(nextKeyCode);
|
2022-10-26 20:58:04 +02:00
|
|
|
debugC(1, kFreescapeDebugMove, "Pushing key: %x", event.kbd.keycode);
|
2022-10-24 09:40:38 +02:00
|
|
|
event.customType = 0xde00;
|
|
|
|
g_system->getEventManager()->pushEvent(event);
|
|
|
|
nextKeyCode = _demoData[_demoIndex++];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(!nextKeyCode);
|
2022-10-26 20:58:04 +02:00
|
|
|
g_system->delayMillis(50);
|
2022-10-05 09:07:29 +02:00
|
|
|
}
|
|
|
|
|
2022-06-22 22:34:00 +02:00
|
|
|
void FreescapeEngine::processInput() {
|
|
|
|
float currentFrame = g_system->getMillis();
|
2022-09-05 12:47:16 +02:00
|
|
|
float deltaTime = 20.0;
|
2022-06-22 22:34:00 +02:00
|
|
|
_lastFrame = currentFrame;
|
|
|
|
Common::Event event;
|
2022-10-24 09:56:31 +02:00
|
|
|
Common::Point mousePos;
|
2022-06-22 22:34:00 +02:00
|
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
2022-10-22 22:10:52 +02:00
|
|
|
if (_demoMode) {
|
|
|
|
if (event.customType != 0xde00)
|
2022-10-05 09:07:29 +02:00
|
|
|
continue;
|
2022-10-22 22:10:52 +02:00
|
|
|
}
|
2022-10-05 09:07:29 +02:00
|
|
|
|
2022-10-22 22:10:52 +02:00
|
|
|
switch (event.type) {
|
|
|
|
case Common::EVENT_KEYDOWN:
|
2022-08-09 12:39:00 +02:00
|
|
|
if (event.kbd.keycode == Common::KEYCODE_o || event.kbd.keycode == Common::KEYCODE_UP)
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kForwardMovement, _scaleVector.x(), deltaTime);
|
2022-08-09 12:39:00 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_k || event.kbd.keycode == Common::KEYCODE_DOWN)
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kBackwardMovement, _scaleVector.x(), deltaTime);
|
2022-10-20 23:44:07 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_LEFT)
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kLeftMovement, _scaleVector.y(), deltaTime);
|
2022-10-20 23:44:07 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_RIGHT)
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kRightMovement, _scaleVector.y(), deltaTime);
|
2022-08-09 12:39:00 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_KP5 || event.kbd.keycode == Common::KEYCODE_KP0)
|
|
|
|
shoot();
|
2022-10-20 23:44:07 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_p)
|
|
|
|
rotate(0, 5);
|
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_l)
|
|
|
|
rotate(0, -5);
|
2022-10-24 10:00:44 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_u)
|
|
|
|
rotate(180, 0);
|
2022-10-20 23:44:07 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_q)
|
2022-10-24 19:33:48 +02:00
|
|
|
rotate(-_angleRotations[_angleRotationIndex], 0);
|
2022-10-20 23:44:07 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_w)
|
2022-10-24 19:33:48 +02:00
|
|
|
rotate(_angleRotations[_angleRotationIndex], 0);
|
2022-08-14 11:12:41 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_r)
|
|
|
|
rise();
|
2022-07-03 09:27:24 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_f)
|
2022-08-14 11:12:41 +02:00
|
|
|
lower();
|
2022-09-10 13:21:12 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_n) {
|
|
|
|
_noClipMode = !_noClipMode;
|
|
|
|
_flyMode = _noClipMode;
|
|
|
|
} else if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
|
2022-07-17 15:46:32 +02:00
|
|
|
openMainMenuDialog();
|
2022-10-23 18:48:34 +02:00
|
|
|
else if (event.kbd.keycode == Common::KEYCODE_SPACE) {
|
|
|
|
_shootMode = !_shootMode;
|
|
|
|
if (!_shootMode) {
|
|
|
|
_crossairPosition.x = _screenW / 2;
|
|
|
|
_crossairPosition.y = _screenH / 2;
|
|
|
|
}
|
|
|
|
} else
|
2022-08-21 00:44:43 +02:00
|
|
|
pressedKey(event.kbd.keycode);
|
2022-06-22 22:34:00 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Common::EVENT_QUIT:
|
|
|
|
case Common::EVENT_RETURN_TO_LAUNCHER:
|
2022-06-24 09:17:25 +02:00
|
|
|
quitGame();
|
2022-06-22 22:34:00 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
case Common::EVENT_MOUSEMOVE:
|
2022-10-24 09:56:31 +02:00
|
|
|
mousePos = event.mouse;
|
|
|
|
|
|
|
|
if (_demoMode)
|
|
|
|
g_system->warpMouse(mousePos.x, mousePos.y);
|
|
|
|
|
2022-10-23 18:48:34 +02:00
|
|
|
if (_shootMode) {
|
|
|
|
_crossairPosition = mousePos;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-06-25 19:17:24 +02:00
|
|
|
if (mousePos.x <= 5 || mousePos.x >= _screenW - 5) {
|
2022-07-16 09:29:17 +02:00
|
|
|
g_system->warpMouse(_screenW / 2, mousePos.y);
|
2022-08-15 17:23:22 +02:00
|
|
|
|
2022-07-16 09:29:17 +02:00
|
|
|
_lastMousePos.x = _screenW / 2;
|
2022-06-22 22:34:00 +02:00
|
|
|
_lastMousePos.y = mousePos.y;
|
2022-08-15 17:23:22 +02:00
|
|
|
if (mousePos.x <= 5)
|
|
|
|
mousePos.x = _lastMousePos.x + 3;
|
|
|
|
else
|
|
|
|
mousePos.x = _lastMousePos.x - 3;
|
|
|
|
|
|
|
|
mousePos.y = _lastMousePos.y;
|
|
|
|
|
2022-06-25 19:17:24 +02:00
|
|
|
} else if (mousePos.y <= 5 || mousePos.y >= _screenH - 5) {
|
2022-07-16 09:29:17 +02:00
|
|
|
g_system->warpMouse(mousePos.x, _screenH / 2);
|
2022-06-25 19:17:24 +02:00
|
|
|
_lastMousePos.x = mousePos.x;
|
2022-07-16 09:29:17 +02:00
|
|
|
_lastMousePos.y = _screenH / 2;
|
2022-08-15 17:23:22 +02:00
|
|
|
if (mousePos.y <= 5)
|
|
|
|
mousePos.y = _lastMousePos.y + 3;
|
|
|
|
else
|
|
|
|
mousePos.y = _lastMousePos.y - 3;
|
|
|
|
|
|
|
|
mousePos.x = _lastMousePos.x;
|
2022-06-22 22:34:00 +02:00
|
|
|
}
|
2022-08-15 17:23:22 +02:00
|
|
|
rotate(_lastMousePos, mousePos);
|
|
|
|
_lastMousePos = mousePos;
|
2022-06-22 22:34:00 +02:00
|
|
|
break;
|
2022-06-25 17:08:24 +02:00
|
|
|
|
|
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
|
|
shoot();
|
|
|
|
break;
|
|
|
|
|
2022-06-22 22:34:00 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-26 18:29:12 +02:00
|
|
|
Common::Error FreescapeEngine::run() {
|
2022-07-04 21:47:00 +02:00
|
|
|
// Initialize graphics
|
2022-10-29 18:04:33 +02:00
|
|
|
_gfx = createRenderer(_system, _screenW, _screenH, _renderMode);
|
2021-09-26 18:29:12 +02:00
|
|
|
_gfx->init();
|
|
|
|
_gfx->clear();
|
2022-07-17 20:18:17 +02:00
|
|
|
|
|
|
|
// Load game data and init game state
|
2022-08-28 09:56:01 +02:00
|
|
|
loadDataBundle();
|
2021-09-26 18:29:12 +02:00
|
|
|
loadAssets();
|
2022-07-17 20:18:17 +02:00
|
|
|
initGameState();
|
2022-09-02 09:08:32 +02:00
|
|
|
loadColorPalette();
|
|
|
|
|
2021-04-12 21:29:57 -03:00
|
|
|
// Simple main event loop
|
2022-08-17 09:18:32 +02:00
|
|
|
int saveSlot = ConfMan.getInt("save_slot");
|
2022-07-04 20:28:45 +02:00
|
|
|
|
2022-10-02 20:18:23 +02:00
|
|
|
if (_title) {
|
2022-08-17 09:18:32 +02:00
|
|
|
if (saveSlot == -1) {
|
2022-10-02 20:18:23 +02:00
|
|
|
drawTitle();
|
2022-08-17 09:18:32 +02:00
|
|
|
_gfx->flipBuffer();
|
|
|
|
g_system->updateScreen();
|
2022-10-02 20:18:23 +02:00
|
|
|
g_system->delayMillis(3000);
|
2022-08-17 09:18:32 +02:00
|
|
|
}
|
2022-10-02 20:18:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_border) {
|
2022-07-04 20:43:02 +02:00
|
|
|
_borderTexture = nullptr;
|
2022-07-22 23:41:09 +02:00
|
|
|
_border->fillRect(_viewArea, 0xA0A0A0FF);
|
2022-07-04 20:43:02 +02:00
|
|
|
}
|
2022-07-17 15:46:32 +02:00
|
|
|
if (saveSlot >= 0) { // load the savegame
|
|
|
|
loadGameState(saveSlot);
|
|
|
|
} else
|
|
|
|
gotoArea(_startArea, _startEntrance);
|
|
|
|
|
2022-07-17 10:28:46 +02:00
|
|
|
debugC(1, kFreescapeDebugMove, "Starting area %d", _currentArea->getAreaID());
|
2022-08-15 17:23:22 +02:00
|
|
|
_system->lockMouse(true);
|
2022-08-26 08:51:47 +02:00
|
|
|
bool endGame = false;
|
2022-10-19 21:06:59 +02:00
|
|
|
// Draw first frame
|
|
|
|
|
|
|
|
rotate(_lastMousePos, _lastMousePos);
|
|
|
|
drawFrame();
|
|
|
|
_gfx->flipBuffer();
|
|
|
|
g_system->updateScreen();
|
|
|
|
|
2022-08-26 08:51:47 +02:00
|
|
|
while (!shouldQuit() && !endGame) {
|
2022-06-25 17:08:24 +02:00
|
|
|
drawFrame();
|
2022-10-05 09:07:29 +02:00
|
|
|
if (_demoMode)
|
|
|
|
generateInput();
|
|
|
|
|
2022-07-02 12:50:03 +02:00
|
|
|
processInput();
|
|
|
|
_gfx->flipBuffer();
|
2021-10-10 17:51:06 +02:00
|
|
|
g_system->updateScreen();
|
2021-04-12 21:29:57 -03:00
|
|
|
g_system->delayMillis(10);
|
2022-08-26 08:51:47 +02:00
|
|
|
endGame = checkIfGameEnded();
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
2022-08-26 08:51:47 +02:00
|
|
|
bool FreescapeEngine::checkIfGameEnded() {
|
2022-08-27 19:36:17 +02:00
|
|
|
return false; // TODO
|
2022-08-26 08:51:47 +02:00
|
|
|
}
|
|
|
|
|
2022-07-04 21:47:00 +02:00
|
|
|
void FreescapeEngine::initGameState() {
|
2022-07-17 20:18:17 +02:00
|
|
|
for (int i = 0; i < k8bitMaxVariable; i++) // TODO: check maximum variable
|
|
|
|
_gameStateVars[i] = 0;
|
|
|
|
|
2022-10-29 09:21:28 +02:00
|
|
|
for (auto &it : _areaMap)
|
|
|
|
_gameStateBits[it._key] = 0;
|
2022-07-04 21:47:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-20 23:44:07 +02:00
|
|
|
void FreescapeEngine::rotate(float xoffset, float yoffset) {
|
|
|
|
_yaw -= xoffset;
|
|
|
|
_pitch += yoffset;
|
|
|
|
|
|
|
|
// Make sure that when pitch is out of bounds, screen doesn't get flipped
|
|
|
|
if (_pitch > 360.0f)
|
|
|
|
_pitch -= 360.0f;
|
|
|
|
if (_pitch < 0.0f)
|
|
|
|
_pitch += 360.0f;
|
|
|
|
|
|
|
|
if (_yaw > 360.0f)
|
|
|
|
_yaw -= 360.0f;
|
|
|
|
if (_yaw < 0.0f)
|
|
|
|
_yaw += 360.0f;
|
|
|
|
|
|
|
|
updateCamera();
|
|
|
|
}
|
|
|
|
|
2021-09-27 23:23:49 +02:00
|
|
|
void FreescapeEngine::rotate(Common::Point lastMousePos, Common::Point mousePos) {
|
2022-10-19 21:06:59 +02:00
|
|
|
if (lastMousePos != Common::Point(0, 0)) {
|
|
|
|
float xoffset = mousePos.x - lastMousePos.x;
|
|
|
|
float yoffset = mousePos.y - lastMousePos.y;
|
|
|
|
|
|
|
|
xoffset *= _mouseSensitivity;
|
|
|
|
yoffset *= _mouseSensitivity;
|
|
|
|
|
|
|
|
_yaw -= xoffset;
|
|
|
|
_pitch += yoffset;
|
|
|
|
|
|
|
|
// Make sure that when pitch is out of bounds, screen doesn't get flipped
|
|
|
|
if (_pitch > 360.0f)
|
|
|
|
_pitch -= 360.0f;
|
|
|
|
if (_pitch < 0.0f)
|
|
|
|
_pitch += 360.0f;
|
|
|
|
|
|
|
|
if (_yaw > 360.0f)
|
|
|
|
_yaw -= 360.0f;
|
|
|
|
if (_yaw < 0.0f)
|
|
|
|
_yaw += 360.0f;
|
|
|
|
}
|
2022-10-20 23:44:07 +02:00
|
|
|
updateCamera();
|
|
|
|
}
|
2022-09-09 21:30:53 +02:00
|
|
|
|
2022-10-20 23:44:07 +02:00
|
|
|
void FreescapeEngine::updateCamera() {
|
2022-06-25 17:08:24 +02:00
|
|
|
_cameraFront = directionToVector(_pitch, _yaw);
|
2022-10-20 23:44:07 +02:00
|
|
|
// _right = _front x _up;
|
|
|
|
Math::Vector3d v = Math::Vector3d::crossProduct(_cameraFront, _upVector);
|
2021-09-27 23:23:49 +02:00
|
|
|
v.normalize();
|
2021-10-10 17:51:06 +02:00
|
|
|
_cameraRight = v;
|
2021-09-27 23:23:49 +02:00
|
|
|
}
|
|
|
|
|
2021-04-12 21:29:57 -03:00
|
|
|
bool FreescapeEngine::hasFeature(EngineFeature f) const {
|
2021-04-16 17:04:41 -03:00
|
|
|
return (f == kSupportsReturnToLauncher) ||
|
|
|
|
(f == kSupportsLoadingDuringRuntime) ||
|
|
|
|
(f == kSupportsSavingDuringRuntime);
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2022-08-27 19:53:39 +02:00
|
|
|
void FreescapeEngine::drawStringInSurface(const Common::String &str, int x, int y, uint32 fontColor, uint32 backColor, Graphics::Surface *surface) {
|
2022-09-24 19:54:43 +02:00
|
|
|
if (!_fontLoaded)
|
|
|
|
return;
|
2022-08-27 19:11:26 +02:00
|
|
|
Common::String ustr = str;
|
|
|
|
ustr.toUppercase();
|
|
|
|
for (uint32 c = 0; c < ustr.size(); c++) {
|
2022-08-27 11:29:52 +02:00
|
|
|
for (int j = 0; j < 6; j++) {
|
|
|
|
for (int i = 0; i < 8; i++) {
|
2022-10-26 21:57:23 +02:00
|
|
|
if (_font.get(48 * (ustr[c] - 32) + 1 + j * 8 + i))
|
|
|
|
surface->setPixel(x + 8 - i + 8 * c, y + j, fontColor);
|
2022-08-27 11:29:52 +02:00
|
|
|
else
|
2022-10-26 21:57:23 +02:00
|
|
|
surface->setPixel(x + 8 - i + 8 * c, y + j, backColor);
|
2022-08-27 11:29:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-12 21:29:57 -03:00
|
|
|
Common::Error FreescapeEngine::loadGameStream(Common::SeekableReadStream *stream) {
|
2022-07-17 15:46:32 +02:00
|
|
|
|
|
|
|
uint16 areaID = stream->readUint16LE();
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
_position.setValue(i, stream->readFloatLE());
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
_rotation.setValue(i, stream->readFloatLE());
|
|
|
|
|
|
|
|
_yaw = stream->readFloatLE();
|
|
|
|
_pitch = stream->readFloatLE();
|
|
|
|
|
2022-07-17 20:18:17 +02:00
|
|
|
// Level state
|
2022-10-30 10:36:05 +01:00
|
|
|
for (uint i = 0; i < _gameStateVars.size(); i++) {
|
2022-07-17 20:18:17 +02:00
|
|
|
uint16 key = stream->readUint16LE();
|
|
|
|
_gameStateVars[key] = stream->readUint32LE();
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:36:05 +01:00
|
|
|
for (uint i = 0; i < _gameStateBits.size(); i++) {
|
2022-07-17 20:18:17 +02:00
|
|
|
uint16 key = stream->readUint16LE();
|
|
|
|
_gameStateBits[key] = stream->readUint32LE();
|
|
|
|
}
|
|
|
|
|
2022-10-30 10:36:05 +01:00
|
|
|
for (uint i = 0; i < _areaMap.size(); i++) {
|
2022-07-18 10:37:38 +02:00
|
|
|
uint16 key = stream->readUint16LE();
|
|
|
|
assert(_areaMap.contains(key));
|
|
|
|
Area *area = _areaMap[key];
|
2022-08-25 18:55:05 +02:00
|
|
|
area->loadObjects(stream, _areaMap[255]);
|
2022-07-18 10:37:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-18 20:44:39 +02:00
|
|
|
_flyMode = stream->readByte();
|
2022-07-17 15:46:32 +02:00
|
|
|
if (!_currentArea || _currentArea->getAreaID() != areaID)
|
|
|
|
gotoArea(areaID, -1); // Do not change position nor rotation
|
2021-04-12 21:29:57 -03:00
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error FreescapeEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
|
2022-07-17 15:46:32 +02:00
|
|
|
if (isAutosave)
|
|
|
|
return Common::kNoError;
|
|
|
|
|
|
|
|
stream->writeUint16LE(_currentArea->getAreaID());
|
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
stream->writeFloatLE(_position.getValue(i));
|
2021-04-12 21:29:57 -03:00
|
|
|
|
2022-07-17 15:46:32 +02:00
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
stream->writeFloatLE(_rotation.getValue(i));
|
|
|
|
|
|
|
|
stream->writeFloatLE(_yaw);
|
|
|
|
stream->writeFloatLE(_pitch);
|
|
|
|
|
2022-07-17 20:18:17 +02:00
|
|
|
// Level state
|
2022-10-29 09:21:28 +02:00
|
|
|
for (auto &it : _gameStateVars) {
|
|
|
|
stream->writeUint16LE(it._key);
|
|
|
|
stream->writeUint32LE(it._value);
|
2022-07-17 20:18:17 +02:00
|
|
|
}
|
|
|
|
|
2022-10-29 09:21:28 +02:00
|
|
|
for (auto &it : _gameStateBits) {
|
|
|
|
stream->writeUint16LE(it._key);
|
|
|
|
stream->writeUint32LE(it._value);
|
2022-07-17 20:18:17 +02:00
|
|
|
}
|
|
|
|
|
2022-10-29 09:21:28 +02:00
|
|
|
for (auto &it : _areaMap) {
|
|
|
|
stream->writeUint16LE(it._key);
|
|
|
|
it._value->saveObjects(stream);
|
2022-07-18 10:37:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-18 20:44:39 +02:00
|
|
|
stream->writeByte(_flyMode);
|
2022-07-17 15:46:32 +02:00
|
|
|
return Common::kNoError;
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2022-08-28 09:56:01 +02:00
|
|
|
void FreescapeEngine::loadDataBundle() {
|
|
|
|
_dataBundle = Common::makeZipArchive(FREESCAPE_DATA_BUNDLE);
|
|
|
|
if (!_dataBundle) {
|
2022-10-26 21:57:23 +02:00
|
|
|
error("ENGINE: Couldn't load data bundle '%s'.", FREESCAPE_DATA_BUNDLE.c_str());
|
2022-08-28 09:56:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-02 22:46:59 +02:00
|
|
|
byte *FreescapeEngine::getPaletteFromNeoImage(Common::SeekableReadStream *stream, int offset) {
|
2022-10-02 19:16:51 +02:00
|
|
|
stream->seek(offset);
|
|
|
|
Image::NeoDecoder decoder;
|
|
|
|
decoder.loadStream(*stream);
|
2022-10-26 21:57:23 +02:00
|
|
|
byte *palette = (byte *)malloc(16 * 3 * sizeof(byte));
|
2022-10-02 22:46:59 +02:00
|
|
|
memcpy(palette, decoder.getPalette(), 16 * 3 * sizeof(byte));
|
|
|
|
return palette;
|
|
|
|
}
|
|
|
|
|
2022-10-26 21:57:23 +02:00
|
|
|
Graphics::Surface *FreescapeEngine::loadAndConvertNeoImage(Common::SeekableReadStream *stream, int offset, byte *palette) {
|
2022-10-02 22:46:59 +02:00
|
|
|
stream->seek(offset);
|
|
|
|
Image::NeoDecoder decoder(palette);
|
|
|
|
decoder.loadStream(*stream);
|
2022-10-02 19:16:51 +02:00
|
|
|
Graphics::Surface *surface = new Graphics::Surface();
|
|
|
|
surface->copyFrom(*decoder.getSurface());
|
|
|
|
surface->convertToInPlace(_gfx->_currentPixelFormat, decoder.getPalette());
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2021-04-16 17:04:41 -03:00
|
|
|
} // namespace Freescape
|