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"
|
2022-06-25 17:08:24 +02:00
|
|
|
#include "common/math.h"
|
2022-11-01 13:44:21 +01:00
|
|
|
#include "common/random.h"
|
2022-11-08 20:46:59 +01:00
|
|
|
#include "common/timer.h"
|
2022-08-15 17:23:22 +02:00
|
|
|
#include "graphics/cursorman.h"
|
2023-11-18 22:11:52 +01:00
|
|
|
#include "image/neo.h"
|
|
|
|
#include "image/scr.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-11-15 08:36:00 +01:00
|
|
|
#include "freescape/objects/sensor.h"
|
2021-04-15 20:23:38 -03:00
|
|
|
|
2021-04-12 21:29:57 -03:00
|
|
|
namespace Freescape {
|
|
|
|
|
2023-08-13 23:23:25 +02:00
|
|
|
FreescapeEngine *g_freescape;
|
|
|
|
|
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) {
|
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-12-29 09:46:48 -03:00
|
|
|
} else if (isCPC()) {
|
|
|
|
_renderMode = Common::kRenderCPC;
|
2022-12-23 15:53:54 -03:00
|
|
|
} else if (isSpectrum()) {
|
2022-12-23 18:55:00 -03:00
|
|
|
_renderMode = Common::kRenderZX;
|
2023-01-02 08:08:54 -03:00
|
|
|
} else if (isC64()) {
|
|
|
|
_renderMode = Common::kRenderC64;
|
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
|
|
|
|
2023-04-30 18:59:03 +02:00
|
|
|
_language = Common::parseLanguage(ConfMan.get("language"));
|
|
|
|
|
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-11-27 08:55:35 +01:00
|
|
|
if (!Common::parseBool(ConfMan.get("extended_timer"), _useExtendedTimer))
|
|
|
|
error("Failed to parse bool from extended_timer option");
|
|
|
|
|
2022-11-21 23:38:02 +01:00
|
|
|
if (!Common::parseBool(ConfMan.get("disable_demo_mode"), _disableDemoMode))
|
|
|
|
error("Failed to parse bool from disable_demo_mode option");
|
|
|
|
|
2022-12-02 12:03:37 +01:00
|
|
|
if (!Common::parseBool(ConfMan.get("disable_sensors"), _disableSensors))
|
|
|
|
error("Failed to parse bool from disable_sensors option");
|
|
|
|
|
2023-01-07 17:14:06 -03:00
|
|
|
if (!Common::parseBool(ConfMan.get("disable_falling"), _disableFalling))
|
|
|
|
error("Failed to parse bool from disable_falling option");
|
|
|
|
|
2023-08-12 15:41:22 +02:00
|
|
|
if (!Common::parseBool(ConfMan.get("invert_y"), _invertY))
|
|
|
|
error("Failed to parse bool from disable_falling option");
|
|
|
|
|
2024-02-09 10:53:42 +01:00
|
|
|
_gameStateControl = kFreescapeGameStateStart;
|
2022-10-26 13:18:59 +02:00
|
|
|
_startArea = 0;
|
|
|
|
_startEntrance = 0;
|
2024-02-10 21:21:02 +01:00
|
|
|
_endArea = 0;
|
|
|
|
_endEntrance = 0;
|
2022-06-25 17:08:24 +02:00
|
|
|
_currentArea = nullptr;
|
2023-08-24 07:56:30 +02:00
|
|
|
_gotoExecuted = false;
|
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);
|
2023-01-07 17:14:06 -03:00
|
|
|
_hasFallen = false;
|
2024-02-12 09:52:32 +01:00
|
|
|
_maxFallingDistance = 64;
|
2022-10-26 13:18:59 +02:00
|
|
|
_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-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;
|
2022-10-19 19:27:51 +02:00
|
|
|
_demoIndex = 0;
|
2022-10-22 22:10:52 +02:00
|
|
|
_currentDemoInputCode = 0;
|
|
|
|
_currentDemoInputRepetition = 0;
|
2022-11-15 18:28:50 +01:00
|
|
|
_currentDemoMousePosition = _crossairPosition;
|
2022-07-16 13:31:03 +02:00
|
|
|
_flyMode = false;
|
2022-09-10 13:21:12 +02:00
|
|
|
_noClipMode = false;
|
2023-07-27 09:55:43 +02:00
|
|
|
_playerWasCrushed = false;
|
2022-12-27 20:01:47 -03:00
|
|
|
_forceEndGame = false;
|
2023-01-24 09:08:42 +01:00
|
|
|
_syncSound = false;
|
2023-01-27 08:26:39 +01:00
|
|
|
_firstSound = false;
|
2022-08-14 11:12:41 +02:00
|
|
|
_playerHeightNumber = 1;
|
2022-10-24 19:33:48 +02:00
|
|
|
_angleRotationIndex = 0;
|
2022-11-12 18:07:20 +01:00
|
|
|
|
|
|
|
// TODO: this is not the same for every game
|
|
|
|
_playerStepIndex = 6;
|
|
|
|
_playerSteps.push_back(1);
|
|
|
|
_playerSteps.push_back(2);
|
|
|
|
_playerSteps.push_back(5);
|
|
|
|
_playerSteps.push_back(10);
|
|
|
|
_playerSteps.push_back(25);
|
|
|
|
_playerSteps.push_back(50);
|
|
|
|
_playerSteps.push_back(100);
|
|
|
|
|
2022-10-02 19:16:51 +02:00
|
|
|
_border = nullptr;
|
|
|
|
_title = nullptr;
|
2024-05-16 21:53:00 +02:00
|
|
|
_background = nullptr;
|
2022-10-02 20:18:23 +02:00
|
|
|
_titleTexture = nullptr;
|
2021-10-23 21:49:50 +02:00
|
|
|
_borderTexture = nullptr;
|
2024-05-16 21:53:00 +02:00
|
|
|
_skyTexture = 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;
|
|
|
|
|
|
|
|
_lastFrame = 0;
|
2022-12-21 14:06:00 -03:00
|
|
|
_nearClipPlane = 2;
|
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;
|
2023-08-27 10:11:14 +02:00
|
|
|
_stepUpDistance = 0;
|
2022-10-26 13:18:59 +02:00
|
|
|
_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;
|
2023-09-04 22:35:56 +02:00
|
|
|
_rawCGAPaletteByArea = nullptr;
|
2023-01-24 09:08:42 +01:00
|
|
|
_speaker = nullptr;
|
2022-11-30 20:40:59 +01:00
|
|
|
_savedScreen = nullptr;
|
2022-11-08 20:46:59 +01:00
|
|
|
|
|
|
|
_timerStarted = false;
|
2022-11-30 11:40:53 +01:00
|
|
|
_initialCountdown = 0;
|
2022-11-08 20:46:59 +01:00
|
|
|
_countdown = 0;
|
2022-11-15 08:36:00 +01:00
|
|
|
_ticks = 0;
|
2024-02-11 09:46:59 +01:00
|
|
|
_ticksFromEnd = 0;
|
2022-12-17 19:13:35 -03:00
|
|
|
_lastTick = -1;
|
2023-01-29 11:39:14 +01:00
|
|
|
_lastMinute = -1;
|
2022-11-08 21:49:11 +01:00
|
|
|
_frameLimiter = nullptr;
|
2022-12-23 08:56:36 -03:00
|
|
|
_vsyncEnabled = false;
|
2022-12-17 19:13:35 -03:00
|
|
|
|
|
|
|
_underFireFrames = 0;
|
|
|
|
_shootingFrames = 0;
|
2024-03-30 19:18:51 +01:00
|
|
|
_delayedShootObject = nullptr;
|
2024-03-22 11:57:46 +01:00
|
|
|
_avoidRenderingFrames = 0;
|
|
|
|
_endGamePlayerEndArea = false;
|
|
|
|
_endGameKeyPressed = false;
|
2023-07-29 12:11:30 +02:00
|
|
|
|
|
|
|
_maxShield = 63;
|
|
|
|
_maxEnergy = 63;
|
2023-09-25 10:28:58 +02:00
|
|
|
_gameStateBits = 0;
|
2024-01-04 11:11:56 +01:00
|
|
|
_eventManager = new EventManagerWrapper(g_system->getEventManager());
|
2023-08-13 23:23:25 +02:00
|
|
|
|
2024-01-06 08:33:45 +01:00
|
|
|
// Workaround to make the game playable on iOS: remove when there
|
|
|
|
// is a better way to hint the best controls
|
2024-01-15 20:54:55 +01:00
|
|
|
#ifdef IPHONE
|
2024-01-06 08:33:45 +01:00
|
|
|
const Common::String &gameDomain = ConfMan.getActiveDomainName();
|
|
|
|
ConfMan.setBool("gamepad_controller", true, gameDomain);
|
|
|
|
ConfMan.setBool("gamepad_controller_minimal_layout", true, gameDomain);
|
2024-01-15 20:54:55 +01:00
|
|
|
ConfMan.setInt("gamepad_controller_directional_input", 1 /* kDirectionalInputDpad */, gameDomain);
|
2024-01-07 01:33:40 +01:00
|
|
|
#endif
|
2024-01-06 08:33:45 +01:00
|
|
|
|
2023-08-13 23:23:25 +02:00
|
|
|
g_freescape = this;
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
FreescapeEngine::~FreescapeEngine() {
|
2022-11-08 21:21:22 +01:00
|
|
|
removeTimers();
|
2021-04-12 21:29:57 -03:00
|
|
|
delete _rnd;
|
2022-10-25 12:44:39 +02:00
|
|
|
|
|
|
|
if (_title && _title != _border) {
|
|
|
|
_title->free();
|
|
|
|
delete _title;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_border) {
|
|
|
|
_border->free();
|
|
|
|
delete _border;
|
|
|
|
}
|
|
|
|
|
2024-05-16 21:53:00 +02:00
|
|
|
if (_background) {
|
|
|
|
_background->free();
|
|
|
|
delete _background;
|
|
|
|
}
|
|
|
|
|
2023-01-19 20:47:16 +01:00
|
|
|
if (_gfx->_isAccelerated) {
|
|
|
|
delete _borderTexture;
|
|
|
|
delete _uiTexture;
|
|
|
|
delete _titleTexture;
|
2024-05-16 21:53:00 +02:00
|
|
|
delete _skyTexture;
|
2023-01-19 20:47:16 +01:00
|
|
|
}
|
2022-09-23 19:18:48 +02:00
|
|
|
|
2022-11-02 20:05:02 +01:00
|
|
|
for (auto &it : _areaMap) {
|
2022-11-12 09:18:41 +01:00
|
|
|
delete it._value;
|
2022-11-02 20:05:02 +01:00
|
|
|
}
|
|
|
|
|
2021-04-24 13:32:05 -03:00
|
|
|
delete _gfx;
|
2022-10-25 21:52:32 +02:00
|
|
|
delete _dataBundle;
|
2023-01-24 09:08:42 +01:00
|
|
|
delete _speaker;
|
2023-08-09 12:41:45 +02:00
|
|
|
|
|
|
|
for (auto &it : _indicators) {
|
|
|
|
it->free();
|
|
|
|
delete it;
|
|
|
|
}
|
2024-02-06 08:09:13 +01:00
|
|
|
|
|
|
|
for (auto &it : _soundsFx) {
|
2024-02-06 08:14:04 +01:00
|
|
|
if (it._value) {
|
|
|
|
free(it._value->data);
|
|
|
|
free(it._value);
|
|
|
|
}
|
2024-02-06 08:09:13 +01:00
|
|
|
}
|
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);
|
2022-12-21 17:06:20 -03:00
|
|
|
assert(_borderTexture);
|
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() {
|
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
2023-07-30 08:30:09 +02:00
|
|
|
if (_title) {
|
|
|
|
if (!_titleTexture) {
|
|
|
|
Graphics::Surface *title = _gfx->convertImageFormatIfNecessary(_title);
|
|
|
|
_titleTexture = _gfx->createTexture(title);
|
|
|
|
title->free();
|
|
|
|
delete title;
|
|
|
|
}
|
|
|
|
_gfx->drawTexturedRect2D(_fullscreenViewArea, _fullscreenViewArea, _titleTexture);
|
2022-12-28 09:34:20 -03:00
|
|
|
}
|
2022-10-02 20:18:23 +02:00
|
|
|
_gfx->setViewport(_viewArea);
|
|
|
|
}
|
|
|
|
|
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-11-12 11:33:44 +01:00
|
|
|
void FreescapeEngine::centerCrossair() {
|
|
|
|
_crossairPosition.x = _viewArea.left + _viewArea.width() / 2;
|
|
|
|
_crossairPosition.y = _viewArea.top + _viewArea.height() / 2;
|
2022-11-22 13:03:28 +01:00
|
|
|
_currentDemoMousePosition = _crossairPosition;
|
2022-11-12 11:33:44 +01:00
|
|
|
}
|
|
|
|
|
2022-12-17 19:13:35 -03:00
|
|
|
void FreescapeEngine::checkSensors() {
|
2022-12-02 12:03:37 +01:00
|
|
|
if (_disableSensors)
|
2022-12-17 19:13:35 -03:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (_lastTick == _ticks)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_lastTick = _ticks;
|
2022-11-15 08:36:00 +01:00
|
|
|
for (auto &it : _sensors) {
|
|
|
|
Sensor *sensor = (Sensor *)it;
|
2022-12-17 14:07:59 -03:00
|
|
|
bool playerDetected = sensor->playerDetected(_position, _currentArea);
|
2022-11-25 21:52:03 +01:00
|
|
|
if (playerDetected) {
|
2022-12-17 14:07:59 -03:00
|
|
|
if (_ticks % sensor->_firingInterval == 0) {
|
2022-12-17 19:13:35 -03:00
|
|
|
if (_underFireFrames <= 0)
|
2022-12-23 08:56:36 -03:00
|
|
|
_underFireFrames = 4;
|
2022-11-22 21:57:26 +01:00
|
|
|
takeDamageFromSensor();
|
|
|
|
}
|
2022-11-15 08:36:00 +01:00
|
|
|
}
|
2022-12-17 19:13:35 -03:00
|
|
|
sensor->shouldShoot(playerDetected);
|
2022-11-15 08:36:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-28 16:53:33 +02:00
|
|
|
void FreescapeEngine::drawSensorShoot(Sensor *sensor) {}
|
2022-11-22 21:57:26 +01:00
|
|
|
|
2022-11-25 18:47:15 +01:00
|
|
|
void FreescapeEngine::flashScreen(int backgroundColor) {
|
2022-11-25 20:25:05 +01:00
|
|
|
if (backgroundColor >= 16)
|
|
|
|
return;
|
2022-11-25 18:47:15 +01:00
|
|
|
_currentArea->remapColor(_currentArea->_usualBackgroundColor, backgroundColor);
|
|
|
|
_currentArea->remapColor(_currentArea->_skyColor, backgroundColor);
|
2022-11-25 13:05:23 +01:00
|
|
|
drawFrame();
|
|
|
|
_currentArea->unremapColor(_currentArea->_usualBackgroundColor);
|
|
|
|
_currentArea->unremapColor(_currentArea->_skyColor);
|
|
|
|
}
|
|
|
|
|
2022-11-25 18:47:15 +01:00
|
|
|
void FreescapeEngine::takeDamageFromSensor() {
|
|
|
|
_gameStateVars[k8bitVariableShield]--;
|
|
|
|
}
|
|
|
|
|
2024-01-13 13:01:01 +01:00
|
|
|
void FreescapeEngine::clearBackground() {
|
2023-11-26 18:35:19 +01:00
|
|
|
_gfx->clear(0, 0, 0, true);
|
2022-11-25 13:05:23 +01:00
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
2023-04-23 14:10:33 +02:00
|
|
|
_gfx->drawBackground(_currentArea->_usualBackgroundColor);
|
2022-11-25 13:05:23 +01:00
|
|
|
_gfx->setViewport(_viewArea);
|
2024-01-13 13:01:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::drawBackground() {
|
|
|
|
clearBackground();
|
2023-04-23 14:10:33 +02:00
|
|
|
_gfx->drawBackground(_currentArea->_skyColor);
|
2024-05-16 21:53:00 +02:00
|
|
|
|
|
|
|
if (isCastle()) {
|
|
|
|
assert(_background);
|
|
|
|
if (!_skyTexture)
|
|
|
|
_skyTexture = _gfx->createTexture(_background);
|
|
|
|
_gfx->drawSkybox(_skyTexture, _position);
|
|
|
|
}
|
2022-11-22 21:57:26 +01:00
|
|
|
}
|
|
|
|
|
2022-06-25 17:08:24 +02:00
|
|
|
void FreescapeEngine::drawFrame() {
|
2024-03-30 11:34:49 +01:00
|
|
|
int farClipPlane = _farClipPlane;
|
|
|
|
if (_currentArea->isOutside())
|
|
|
|
farClipPlane *= 100;
|
|
|
|
|
|
|
|
_gfx->updateProjectionMatrix(90.0, _nearClipPlane, farClipPlane);
|
2022-06-22 22:02:09 +02:00
|
|
|
_gfx->positionCamera(_position, _position + _cameraFront);
|
2022-12-17 19:13:35 -03:00
|
|
|
|
|
|
|
if (_underFireFrames > 0) {
|
2023-05-14 11:48:15 +02:00
|
|
|
int underFireColor = _currentArea->_underFireBackgroundColor;
|
|
|
|
|
|
|
|
if (isDriller() && (isDOS() || isAmiga() || isAtariST()))
|
|
|
|
underFireColor = 1;
|
2024-03-31 18:58:01 +02:00
|
|
|
else if (isDark() && (isDOS() || isAmiga() || isAtariST())) {
|
|
|
|
if (_renderMode == Common::kRenderCGA)
|
|
|
|
underFireColor = 3;
|
|
|
|
else
|
|
|
|
underFireColor = 4;
|
|
|
|
}
|
2023-05-14 11:48:15 +02:00
|
|
|
|
|
|
|
_currentArea->remapColor(_currentArea->_usualBackgroundColor, underFireColor);
|
|
|
|
_currentArea->remapColor(_currentArea->_skyColor, underFireColor);
|
2022-12-17 19:13:35 -03:00
|
|
|
}
|
|
|
|
|
2022-11-25 13:05:23 +01:00
|
|
|
drawBackground();
|
2024-05-12 20:26:08 +02:00
|
|
|
if (_avoidRenderingFrames == 0) { // Avoid rendering inside objects
|
2024-05-06 22:56:54 +02:00
|
|
|
_currentArea->draw(_gfx, _ticks / 10, _position, _cameraFront);
|
2024-05-12 20:26:08 +02:00
|
|
|
if (_currentArea->hasActiveGroups() && _ticks % 50 == 0) {
|
|
|
|
executeMovementConditions();
|
|
|
|
}
|
|
|
|
} else
|
2024-02-12 09:52:32 +01:00
|
|
|
_avoidRenderingFrames--;
|
2022-12-17 19:13:35 -03:00
|
|
|
|
|
|
|
if (_underFireFrames > 0) {
|
|
|
|
for (auto &it : _sensors) {
|
|
|
|
Sensor *sensor = (Sensor *)it;
|
|
|
|
if (sensor->isShooting())
|
|
|
|
drawSensorShoot(sensor);
|
|
|
|
}
|
|
|
|
_underFireFrames--;
|
2023-07-22 20:57:47 +02:00
|
|
|
if (_underFireFrames == 0) {
|
|
|
|
_currentArea->unremapColor(_currentArea->_usualBackgroundColor);
|
|
|
|
_currentArea->unremapColor(_currentArea->_skyColor);
|
|
|
|
}
|
2022-12-17 19:13:35 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (_shootingFrames > 0) {
|
|
|
|
_gfx->setViewport(_fullscreenViewArea);
|
2023-11-15 09:37:06 +01:00
|
|
|
if (isDriller() || isDark())
|
|
|
|
_gfx->renderPlayerShootRay(0, _crossairPosition, _viewArea);
|
|
|
|
else
|
|
|
|
_gfx->renderPlayerShootBall(0, _crossairPosition, _shootingFrames, _viewArea);
|
|
|
|
|
2022-12-17 19:13:35 -03:00
|
|
|
_gfx->setViewport(_viewArea);
|
|
|
|
_shootingFrames--;
|
|
|
|
}
|
|
|
|
|
2022-12-17 19:28:27 -03:00
|
|
|
drawBorder();
|
|
|
|
drawUI();
|
2022-06-22 22:02:09 +02:00
|
|
|
}
|
|
|
|
|
2022-08-21 00:44:43 +02:00
|
|
|
void FreescapeEngine::pressedKey(const int keycode) {}
|
|
|
|
|
2024-02-25 16:24:16 +01:00
|
|
|
void FreescapeEngine::releasedKey(const int keycode) {}
|
|
|
|
|
2023-01-20 09:15:47 +01:00
|
|
|
void FreescapeEngine::resetInput() {
|
|
|
|
_shootMode = false;
|
|
|
|
centerCrossair();
|
|
|
|
g_system->warpMouse(_crossairPosition.x, _crossairPosition.y);
|
2024-01-04 11:11:56 +01:00
|
|
|
_eventManager->purgeMouseEvents();
|
|
|
|
_eventManager->purgeKeyboardEvents();
|
2023-01-20 09:15:47 +01:00
|
|
|
rotate(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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-11-16 10:39:59 +01:00
|
|
|
|
|
|
|
if (_demoMode && !_demoEvents.empty()) {
|
2024-01-04 11:11:56 +01:00
|
|
|
_eventManager->purgeMouseEvents();
|
|
|
|
_eventManager->purgeKeyboardEvents();
|
|
|
|
_eventManager->pushEvent(_demoEvents.front());
|
2022-11-16 10:39:59 +01:00
|
|
|
_demoEvents.remove_at(0);
|
|
|
|
}
|
|
|
|
|
2024-01-04 11:11:56 +01:00
|
|
|
while (_eventManager->pollEvent(event)) {
|
2024-02-10 21:21:02 +01:00
|
|
|
if (_gameStateControl != kFreescapeGameStatePlaying) {
|
2023-07-20 11:14:40 +02:00
|
|
|
if (event.type == Common::EVENT_SCREEN_CHANGED)
|
|
|
|
; // Allow event
|
2024-02-10 21:21:02 +01:00
|
|
|
else if (_gameStateControl == kFreescapeGameStateEnd && event.type == Common::EVENT_KEYDOWN) {
|
|
|
|
_endGameKeyPressed = true;
|
|
|
|
continue;
|
|
|
|
} else if (event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE)
|
|
|
|
; // Allow event
|
2023-07-20 11:14:40 +02:00
|
|
|
else 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) {
|
2023-03-19 23:28:05 +01:00
|
|
|
case Common::EVENT_JOYBUTTON_DOWN:
|
2024-02-12 09:52:32 +01:00
|
|
|
if (_hasFallen || _playerWasCrushed)
|
2023-03-19 23:28:05 +01:00
|
|
|
break;
|
|
|
|
switch (event.joystick.button) {
|
|
|
|
case Common::JOYSTICK_BUTTON_B:
|
|
|
|
case Common::JOYSTICK_BUTTON_DPAD_UP:
|
|
|
|
move(kForwardMovement, _scaleVector.x(), deltaTime);
|
|
|
|
break;
|
|
|
|
case Common::JOYSTICK_BUTTON_DPAD_DOWN:
|
|
|
|
move(kBackwardMovement, _scaleVector.x(), deltaTime);
|
|
|
|
break;
|
|
|
|
case Common::JOYSTICK_BUTTON_DPAD_LEFT:
|
|
|
|
move(kLeftMovement, _scaleVector.y(), deltaTime);
|
|
|
|
break;
|
|
|
|
case Common::JOYSTICK_BUTTON_DPAD_RIGHT:
|
|
|
|
move(kRightMovement, _scaleVector.y(), deltaTime);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2022-10-22 22:10:52 +02:00
|
|
|
case Common::EVENT_KEYDOWN:
|
2023-01-07 17:14:06 -03:00
|
|
|
if (_hasFallen)
|
|
|
|
break;
|
2022-11-02 13:07:16 +01:00
|
|
|
switch (event.kbd.keycode) {
|
|
|
|
case Common::KEYCODE_o:
|
|
|
|
case Common::KEYCODE_UP:
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kForwardMovement, _scaleVector.x(), deltaTime);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_k:
|
|
|
|
case Common::KEYCODE_DOWN:
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kBackwardMovement, _scaleVector.x(), deltaTime);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_LEFT:
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kLeftMovement, _scaleVector.y(), deltaTime);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_RIGHT:
|
2022-10-23 18:53:07 +02:00
|
|
|
move(kRightMovement, _scaleVector.y(), deltaTime);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_KP5:
|
|
|
|
case Common::KEYCODE_KP0:
|
2023-08-03 21:52:49 +02:00
|
|
|
case Common::KEYCODE_0:
|
2022-08-09 12:39:00 +02:00
|
|
|
shoot();
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_p:
|
2022-10-20 23:44:07 +02:00
|
|
|
rotate(0, 5);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_l:
|
2022-10-20 23:44:07 +02:00
|
|
|
rotate(0, -5);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_u:
|
2022-10-24 10:00:44 +02:00
|
|
|
rotate(180, 0);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_n:
|
2022-09-10 13:21:12 +02:00
|
|
|
_noClipMode = !_noClipMode;
|
|
|
|
_flyMode = _noClipMode;
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_ESCAPE:
|
2022-12-23 08:56:36 -03:00
|
|
|
drawFrame();
|
2022-11-30 20:40:59 +01:00
|
|
|
_savedScreen = _gfx->getScreenshot();
|
2022-07-17 15:46:32 +02:00
|
|
|
openMainMenuDialog();
|
2023-07-11 13:40:52 +02:00
|
|
|
_gfx->computeScreenViewport();
|
2022-11-30 20:40:59 +01:00
|
|
|
_savedScreen->free();
|
|
|
|
delete _savedScreen;
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_SPACE:
|
2022-10-23 18:48:34 +02:00
|
|
|
_shootMode = !_shootMode;
|
2023-01-08 15:25:03 -03:00
|
|
|
centerCrossair();
|
2023-01-08 13:31:18 +00:00
|
|
|
if (!_shootMode) {
|
|
|
|
g_system->lockMouse(true);
|
|
|
|
} else {
|
|
|
|
g_system->lockMouse(false);
|
2023-01-08 15:25:03 -03:00
|
|
|
g_system->warpMouse(_crossairPosition.x, _crossairPosition.y);
|
2024-01-04 11:11:56 +01:00
|
|
|
_eventManager->purgeMouseEvents();
|
|
|
|
_eventManager->purgeKeyboardEvents();
|
2023-01-08 13:31:18 +00:00
|
|
|
}
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
2022-12-22 08:50:16 -03:00
|
|
|
case Common::KEYCODE_i:
|
|
|
|
drawInfoMenu();
|
|
|
|
break;
|
2022-11-02 13:07:16 +01:00
|
|
|
default:
|
2022-08-21 00:44:43 +02:00
|
|
|
pressedKey(event.kbd.keycode);
|
2022-11-02 13:07:16 +01:00
|
|
|
break;
|
|
|
|
}
|
2022-06-22 22:34:00 +02:00
|
|
|
break;
|
|
|
|
|
2024-02-25 16:24:16 +01:00
|
|
|
case Common::EVENT_KEYUP:
|
|
|
|
if (_hasFallen)
|
|
|
|
break;
|
|
|
|
|
|
|
|
releasedKey(event.kbd.keycode);
|
|
|
|
break;
|
|
|
|
|
2022-06-22 22:34:00 +02:00
|
|
|
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;
|
|
|
|
|
2022-11-06 08:50:19 +01:00
|
|
|
case Common::EVENT_SCREEN_CHANGED:
|
|
|
|
_gfx->computeScreenViewport();
|
2023-07-11 22:57:50 +02:00
|
|
|
_gfx->clear(0, 0, 0, true);
|
2022-11-06 08:50:19 +01:00
|
|
|
break;
|
|
|
|
|
2022-06-22 22:34:00 +02:00
|
|
|
case Common::EVENT_MOUSEMOVE:
|
2023-01-07 17:14:06 -03:00
|
|
|
if (_hasFallen)
|
|
|
|
break;
|
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) {
|
2023-08-05 14:46:52 +02:00
|
|
|
_crossairPosition.x = _screenW * mousePos.x / g_system->getWidth();
|
|
|
|
_crossairPosition.y = _screenH * mousePos.y / g_system->getHeight();
|
2022-10-23 18:48:34 +02:00
|
|
|
break;
|
2023-08-05 14:46:52 +02:00
|
|
|
} else {
|
|
|
|
// Mouse pointer is locked into the the middle of the screen
|
|
|
|
// since we only need the relative movements. This will not affect any touchscreen device
|
|
|
|
// so on-screen controls are still accesible
|
|
|
|
mousePos.x = g_system->getWidth() * ( _viewArea.left + _viewArea.width() / 2) / _screenW;
|
|
|
|
mousePos.y = g_system->getHeight() * (_viewArea.top + _viewArea.height() / 2) / _screenW;
|
2023-08-12 15:41:22 +02:00
|
|
|
if (_invertY)
|
|
|
|
event.relMouse.y = -event.relMouse.y;
|
|
|
|
|
2023-08-05 14:46:52 +02:00
|
|
|
g_system->warpMouse(mousePos.x, mousePos.y);
|
2024-01-04 11:11:56 +01:00
|
|
|
_eventManager->purgeMouseEvents();
|
2022-10-23 18:48:34 +02:00
|
|
|
}
|
|
|
|
|
2023-01-08 13:23:52 -03:00
|
|
|
rotate(event.relMouse.x * _mouseSensitivity, event.relMouse.y * _mouseSensitivity);
|
2022-06-22 22:34:00 +02:00
|
|
|
break;
|
2022-06-25 17:08:24 +02:00
|
|
|
|
|
|
|
case Common::EVENT_LBUTTONDOWN:
|
2023-01-07 17:14:06 -03:00
|
|
|
if (_hasFallen)
|
|
|
|
break;
|
2023-03-24 11:18:20 +01:00
|
|
|
mousePos = event.mouse;
|
|
|
|
{
|
|
|
|
bool touchedScreenControls = false;
|
|
|
|
|
2023-07-19 12:11:56 +02:00
|
|
|
Common::Point resolution(g_system->getWidth(), g_system->getHeight());
|
2023-06-04 15:48:04 +02:00
|
|
|
mousePos.x = _screenW * mousePos.x / resolution.x;
|
|
|
|
mousePos.y = _screenH * mousePos.y / resolution.y;
|
2023-03-24 11:18:20 +01:00
|
|
|
touchedScreenControls = onScreenControls(mousePos);
|
|
|
|
|
2023-08-05 14:46:52 +02:00
|
|
|
if (!touchedScreenControls) {
|
|
|
|
if (_viewArea.contains(_shootMode ? _crossairPosition : mousePos))
|
|
|
|
shoot();
|
|
|
|
}
|
|
|
|
|
2023-03-24 11:18:20 +01:00
|
|
|
}
|
2022-06-25 17:08:24 +02:00
|
|
|
break;
|
|
|
|
|
2023-05-03 08:00:10 +02:00
|
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
|
|
if (_hasFallen || !isCastle())
|
|
|
|
break;
|
|
|
|
activate();
|
|
|
|
break;
|
|
|
|
|
2022-06-22 22:34:00 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-24 11:18:20 +01:00
|
|
|
bool FreescapeEngine::onScreenControls(Common::Point mouse) {
|
|
|
|
return false;
|
2023-03-22 08:57:42 +01:00
|
|
|
}
|
|
|
|
|
2023-02-03 15:01:06 +01:00
|
|
|
void FreescapeEngine::executeMovementConditions() {
|
|
|
|
// Only execute "on collision" room/global conditions
|
2023-03-25 09:11:21 +01:00
|
|
|
executeLocalGlobalConditions(false, true, false);
|
2023-02-03 15:01:06 +01:00
|
|
|
}
|
|
|
|
|
2023-08-01 14:46:13 +02:00
|
|
|
void FreescapeEngine::updateTimeVariables() {}
|
2023-01-29 11:39:14 +01:00
|
|
|
|
2021-09-26 18:29:12 +02:00
|
|
|
Common::Error FreescapeEngine::run() {
|
2022-12-23 08:56:36 -03:00
|
|
|
_vsyncEnabled = g_system->getFeatureState(OSystem::kFeatureVSync);
|
2022-11-08 21:49:11 +01:00
|
|
|
_frameLimiter = new Graphics::FrameLimiter(g_system, ConfMan.getInt("engine_speed"));
|
2022-07-04 21:47:00 +02:00
|
|
|
// Initialize graphics
|
2022-12-13 10:59:24 -03:00
|
|
|
_screenW = g_system->getWidth();
|
|
|
|
_screenH = g_system->getHeight();
|
2022-11-06 19:51:46 +01:00
|
|
|
_gfx = createRenderer(_screenW, _screenH, _renderMode);
|
2023-01-22 14:45:13 +01:00
|
|
|
_speaker = new SizedPCSpeaker();
|
|
|
|
_speaker->setVolume(50);
|
2022-12-13 10:59:24 -03:00
|
|
|
_crossairPosition.x = _screenW / 2;
|
|
|
|
_crossairPosition.y = _screenH / 2;
|
|
|
|
|
2022-11-01 09:23:34 +01:00
|
|
|
// The following error code will force return to launcher
|
|
|
|
// but it will not force any other GUI message to be displayed
|
|
|
|
if (!_gfx)
|
|
|
|
return Common::kUserCanceled;
|
|
|
|
|
2021-09-26 18:29:12 +02:00
|
|
|
_gfx->init();
|
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();
|
|
|
|
|
2023-07-25 09:16:08 +02:00
|
|
|
g_system->showMouse(true);
|
|
|
|
g_system->lockMouse(false);
|
2022-11-06 00:32:47 +01:00
|
|
|
|
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");
|
2023-01-11 10:14:08 -03:00
|
|
|
if (saveSlot == -1)
|
|
|
|
titleScreen();
|
2022-12-21 18:02:59 -03:00
|
|
|
loadBorder(); // Border is load unmodified
|
2023-01-11 10:14:08 -03:00
|
|
|
if (saveSlot == -1)
|
|
|
|
borderScreen();
|
2022-12-21 18:02:59 -03:00
|
|
|
processBorder(); // Border is processed to use during the game
|
|
|
|
|
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-10-19 21:06:59 +02:00
|
|
|
// Draw first frame
|
|
|
|
|
2023-08-01 13:15:42 +02:00
|
|
|
g_system->showMouse(false);
|
2023-07-25 09:16:08 +02:00
|
|
|
g_system->lockMouse(true);
|
2023-01-20 09:15:47 +01:00
|
|
|
resetInput();
|
2023-07-11 13:40:52 +02:00
|
|
|
_gfx->computeScreenViewport();
|
2023-07-11 22:57:50 +02:00
|
|
|
_gfx->clear(0, 0, 0, true);
|
2022-10-19 21:06:59 +02:00
|
|
|
_gfx->flipBuffer();
|
|
|
|
g_system->updateScreen();
|
|
|
|
|
2022-11-30 11:40:53 +01:00
|
|
|
while (!shouldQuit()) {
|
2023-01-29 11:39:14 +01:00
|
|
|
updateTimeVariables();
|
2024-02-10 21:21:02 +01:00
|
|
|
if (_gameStateControl == kFreescapeGameStateRestart) {
|
2022-11-30 11:40:53 +01:00
|
|
|
initGameState();
|
|
|
|
gotoArea(_startArea, _startEntrance);
|
2024-02-10 21:21:02 +01:00
|
|
|
} else if (_gameStateControl == kFreescapeGameStateEnd)
|
|
|
|
endGame();
|
|
|
|
|
2022-12-23 08:56:36 -03:00
|
|
|
processInput();
|
|
|
|
if (_demoMode)
|
|
|
|
generateDemoInput();
|
2022-11-30 11:40:53 +01:00
|
|
|
|
2022-12-17 19:13:35 -03:00
|
|
|
checkSensors();
|
|
|
|
drawFrame();
|
2022-11-26 22:53:47 +01:00
|
|
|
|
2024-03-30 19:18:51 +01:00
|
|
|
if (_shootingFrames == 0) {
|
|
|
|
if (_delayedShootObject) {
|
|
|
|
executeObjectConditions(_delayedShootObject, true, false, false);
|
|
|
|
executeLocalGlobalConditions(true, false, false); // Only execute "on shot" room/global conditions
|
|
|
|
_delayedShootObject = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-02 12:50:03 +02:00
|
|
|
_gfx->flipBuffer();
|
2022-11-08 21:49:11 +01:00
|
|
|
_frameLimiter->delayBeforeSwap();
|
2021-10-10 17:51:06 +02:00
|
|
|
g_system->updateScreen();
|
2022-11-08 21:49:11 +01:00
|
|
|
_frameLimiter->startFrame();
|
2022-12-23 08:56:36 -03:00
|
|
|
if (_vsyncEnabled) // if vsync is enabled, the framelimiter will not work
|
|
|
|
g_system->delayMillis(15); // try to target ~60 FPS
|
2024-02-10 21:21:02 +01:00
|
|
|
|
|
|
|
checkIfGameEnded();
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2024-01-16 09:40:06 +01:00
|
|
|
_eventManager->clearExitEvents();
|
2021-04-12 21:29:57 -03:00
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
void FreescapeEngine::endGame() {
|
2024-03-30 19:18:51 +01:00
|
|
|
_shootingFrames = 0;
|
|
|
|
_delayedShootObject = nullptr;
|
2024-03-13 19:43:12 +01:00
|
|
|
if (_gameStateControl == kFreescapeGameStateEnd && !isPlayingSound() && !_endGamePlayerEndArea) {
|
|
|
|
_endGamePlayerEndArea = true;
|
|
|
|
gotoArea(_endArea, _endEntrance);
|
2024-02-10 21:21:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-21 18:02:59 -03:00
|
|
|
void FreescapeEngine::loadBorder() {
|
2023-03-07 16:09:41 +01:00
|
|
|
if (_border) {
|
|
|
|
Graphics::Surface *border = _gfx->convertImageFormatIfNecessary(_border);
|
|
|
|
_borderTexture = _gfx->createTexture(border);
|
|
|
|
border->free();
|
|
|
|
delete border;
|
|
|
|
}
|
2022-12-21 18:02:59 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::processBorder() {
|
2022-12-20 23:11:33 -03:00
|
|
|
if (_border) {
|
2022-12-21 18:02:59 -03:00
|
|
|
if (_borderTexture)
|
|
|
|
delete _borderTexture;
|
2023-03-07 16:09:41 +01:00
|
|
|
Graphics::Surface *border = _gfx->convertImageFormatIfNecessary(_border);
|
|
|
|
|
2022-12-20 23:11:33 -03:00
|
|
|
uint32 gray = _gfx->_texturePixelFormat.ARGBToColor(0x00, 0xA0, 0xA0, 0xA0);
|
2023-03-07 16:09:41 +01:00
|
|
|
border->fillRect(_viewArea, gray);
|
2022-12-21 17:06:20 -03:00
|
|
|
|
|
|
|
// Replace black pixel for transparent ones
|
2023-03-07 16:09:41 +01:00
|
|
|
uint32 black = border->format.ARGBToColor(0xFF, 0x00, 0x00, 0x00);
|
|
|
|
uint32 transparent = border->format.ARGBToColor(0x00, 0x00, 0x00, 0x00);
|
2022-12-21 17:06:20 -03:00
|
|
|
|
2023-03-07 16:09:41 +01:00
|
|
|
for (int i = 0; i < border->w; i++) {
|
|
|
|
for (int j = 0; j < border->h; j++) {
|
2023-04-25 08:38:15 +02:00
|
|
|
if (!isCastle() && border->getPixel(i, j) == black)
|
2023-03-07 16:09:41 +01:00
|
|
|
border->setPixel(i, j, transparent);
|
2022-12-21 17:06:20 -03:00
|
|
|
}
|
|
|
|
}
|
2023-03-07 16:09:41 +01:00
|
|
|
|
|
|
|
_borderTexture = _gfx->createTexture(border);
|
|
|
|
border->free();
|
|
|
|
delete border;
|
2022-12-20 23:11:33 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-26 08:51:47 +02:00
|
|
|
bool FreescapeEngine::checkIfGameEnded() {
|
2024-02-10 21:21:02 +01:00
|
|
|
if (_gameStateControl != kFreescapeGameStatePlaying)
|
|
|
|
return false;
|
|
|
|
|
2024-02-12 09:52:32 +01:00
|
|
|
if (_avoidRenderingFrames > 0)
|
|
|
|
return false;
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
if (_gameStateVars[k8bitVariableShield] == 0) {
|
2024-03-13 19:43:12 +01:00
|
|
|
if (isSpectrum())
|
|
|
|
playSound(14, true);
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
if (!_noShieldMessage.empty())
|
|
|
|
insertTemporaryMessage(_noShieldMessage, _countdown - 2);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
} else if (_gameStateVars[k8bitVariableEnergy] == 0) {
|
2024-03-13 19:43:12 +01:00
|
|
|
if (isSpectrum())
|
|
|
|
playSound(14, true);
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
if (!_noEnergyMessage.empty())
|
|
|
|
insertTemporaryMessage(_noEnergyMessage, _countdown - 2);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
} else if (_hasFallen) {
|
|
|
|
_hasFallen = false;
|
2024-03-13 19:43:12 +01:00
|
|
|
if (isSpectrum())
|
|
|
|
playSound(14, false);
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
if (!_fallenMessage.empty())
|
|
|
|
insertTemporaryMessage(_fallenMessage, _countdown - 4);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
} else if (_countdown <= 0) {
|
2024-03-13 19:43:12 +01:00
|
|
|
if (isSpectrum())
|
|
|
|
playSound(14, false);
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
if (!_timeoutMessage.empty())
|
|
|
|
insertTemporaryMessage(_timeoutMessage, _countdown - 4);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
} else if (_playerWasCrushed) {
|
2024-03-13 19:43:12 +01:00
|
|
|
if (isSpectrum())
|
|
|
|
playSound(25, true);
|
|
|
|
|
2024-02-10 21:21:02 +01:00
|
|
|
_playerWasCrushed = false;
|
|
|
|
if (!_crushedMessage.empty())
|
|
|
|
insertTemporaryMessage(_crushedMessage, _countdown - 4);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
} else if (_forceEndGame) {
|
2024-03-09 08:14:39 +01:00
|
|
|
if (isSpectrum())
|
2024-03-13 19:43:12 +01:00
|
|
|
playSound(14, true);
|
2024-02-10 21:21:02 +01:00
|
|
|
_forceEndGame = false;
|
|
|
|
if (!_forceEndGameMessage.empty())
|
|
|
|
insertTemporaryMessage(_forceEndGameMessage, _countdown - 4);
|
|
|
|
_gameStateControl = kFreescapeGameStateEnd;
|
|
|
|
}
|
2022-08-27 19:36:17 +02:00
|
|
|
return false; // TODO
|
2022-08-26 08:51:47 +02:00
|
|
|
}
|
|
|
|
|
2023-02-05 08:57:03 +01:00
|
|
|
void FreescapeEngine::setGameBit(int index) {
|
2023-09-15 07:52:10 +02:00
|
|
|
_gameStateBits |= (1 << (index - 1));
|
2023-02-05 08:57:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::clearGameBit(int index) {
|
2023-09-15 07:52:10 +02:00
|
|
|
_gameStateBits &= ~(1 << (index - 1));
|
2023-02-05 08:57:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::toggleGameBit(int index) {
|
2023-09-15 07:52:10 +02:00
|
|
|
_gameStateBits ^= (1 << (index - 1));
|
2023-02-05 08:57:03 +01:00
|
|
|
}
|
|
|
|
|
2023-09-15 10:03:50 +02:00
|
|
|
uint16 FreescapeEngine::getGameBit(int index) {
|
2023-09-15 07:52:10 +02:00
|
|
|
return (_gameStateBits >> (index - 1)) & 1;
|
|
|
|
}
|
2023-02-05 08:57:03 +01:00
|
|
|
|
2022-07-04 21:47:00 +02:00
|
|
|
void FreescapeEngine::initGameState() {
|
2024-02-09 10:53:42 +01:00
|
|
|
_gameStateControl = kFreescapeGameStatePlaying;
|
|
|
|
|
2022-07-17 20:18:17 +02:00
|
|
|
for (int i = 0; i < k8bitMaxVariable; i++) // TODO: check maximum variable
|
|
|
|
_gameStateVars[i] = 0;
|
|
|
|
|
2024-02-09 10:53:42 +01:00
|
|
|
for (auto &it : _areaMap)
|
|
|
|
it._value->resetArea();
|
|
|
|
|
2023-09-15 07:52:10 +02:00
|
|
|
_gameStateBits = 0;
|
2024-02-09 10:53:42 +01:00
|
|
|
|
|
|
|
_flyMode = false;
|
|
|
|
_noClipMode = false;
|
|
|
|
_playerWasCrushed = false;
|
|
|
|
_shootingFrames = 0;
|
2024-03-30 19:18:51 +01:00
|
|
|
_delayedShootObject = nullptr;
|
2024-02-09 10:53:42 +01:00
|
|
|
_underFireFrames = 0;
|
2024-02-12 09:52:32 +01:00
|
|
|
_avoidRenderingFrames = 0;
|
2024-02-09 10:53:42 +01:00
|
|
|
_yaw = 0;
|
|
|
|
_pitch = 0;
|
2024-02-10 21:21:02 +01:00
|
|
|
_endGameKeyPressed = false;
|
2024-03-13 19:43:12 +01:00
|
|
|
_endGamePlayerEndArea = false;
|
2024-02-09 10:53:42 +01:00
|
|
|
|
|
|
|
_demoIndex = 0;
|
|
|
|
_demoEvents.clear();
|
|
|
|
|
|
|
|
removeTimers();
|
|
|
|
startCountdown(_initialCountdown - 1);
|
|
|
|
int seconds, minutes, hours;
|
|
|
|
getTimeFromCountdown(seconds, minutes, hours);
|
|
|
|
_lastMinute = minutes;
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-11-06 08:44:39 +01:00
|
|
|
// The TinyGL renderer does not support arbitrary resolutions for now
|
|
|
|
bool softRenderer = determinateRenderType() == Graphics::kRendererTypeTinyGL;
|
2021-04-16 17:04:41 -03:00
|
|
|
return (f == kSupportsReturnToLauncher) ||
|
|
|
|
(f == kSupportsLoadingDuringRuntime) ||
|
2022-11-06 08:44:39 +01:00
|
|
|
(f == kSupportsSavingDuringRuntime) ||
|
|
|
|
(f == kSupportsArbitraryResolutions && !softRenderer);
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2022-12-03 09:11:25 +01:00
|
|
|
void FreescapeEngine::drawStringInSurface(const Common::String &str, int x, int y, uint32 fontColor, uint32 backColor, Graphics::Surface *surface, int offset) {
|
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();
|
2022-11-21 19:16:27 +01:00
|
|
|
|
2023-05-01 09:06:03 +02:00
|
|
|
int sizeX = 8;
|
|
|
|
int sizeY = isCastle() ? 8 : 6;
|
|
|
|
int sep = isCastle() ? 9 : 8;
|
2023-11-15 22:07:46 +01:00
|
|
|
int additional = isCastle() || isEclipse() ? 0 : 1;
|
2023-05-01 09:06:03 +02:00
|
|
|
|
2023-01-06 23:01:31 -03:00
|
|
|
if (isDOS() || isSpectrum() || isCPC() || isC64()) {
|
2022-11-21 19:16:27 +01:00
|
|
|
for (uint32 c = 0; c < ustr.size(); c++) {
|
|
|
|
assert(ustr[c] >= 32);
|
2024-04-07 12:10:14 +02:00
|
|
|
int position = sizeX * sizeY * (offset + ustr[c] - 32);
|
2023-05-01 09:06:03 +02:00
|
|
|
for (int j = 0; j < sizeY; j++) {
|
|
|
|
for (int i = 0; i < sizeX; i++) {
|
2024-04-07 12:10:14 +02:00
|
|
|
if (_font.get(position + additional + j * 8 + i))
|
2023-05-01 09:06:03 +02:00
|
|
|
surface->setPixel(x + 8 - i + sep * c, y + j, fontColor);
|
2022-11-21 19:16:27 +01:00
|
|
|
else
|
2023-05-01 09:06:03 +02:00
|
|
|
surface->setPixel(x + 8 - i + sep * c, y + j, backColor);
|
2022-11-21 19:16:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-21 20:06:52 +01:00
|
|
|
} else if (isAmiga() || isAtariST()) {
|
2024-04-07 12:10:14 +02:00
|
|
|
int multiplier1 = isDriller() ? 33 : 16;
|
|
|
|
int multiplier2 = isDriller() ? 32 : 16;
|
|
|
|
|
2022-11-21 19:16:27 +01:00
|
|
|
for (uint32 c = 0; c < ustr.size(); c++) {
|
|
|
|
assert(ustr[c] >= 32);
|
2024-04-07 12:10:14 +02:00
|
|
|
int position = 8 * (multiplier1*(offset + ustr[c] - 32) + 1);
|
2022-11-21 19:16:27 +01:00
|
|
|
for (int j = 0; j < 8; j++) {
|
|
|
|
for (int i = 0; i < 8; i++) {
|
2024-04-07 12:10:14 +02:00
|
|
|
if (_font.get(position + j * multiplier2 + i))
|
2022-11-21 19:16:27 +01:00
|
|
|
surface->setPixel(x + 8 - i + 8 * c, y + j, fontColor);
|
|
|
|
else
|
2024-01-29 19:32:00 +01:00
|
|
|
surface->setPixel(x + 8 - i + 8 * c, y + j, backColor);
|
2022-11-21 19:16:27 +01:00
|
|
|
}
|
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();
|
|
|
|
}
|
|
|
|
|
2023-09-15 07:52:10 +02:00
|
|
|
_gameStateBits = stream->readUint32LE();
|
2022-07-17 20:18:17 +02:00
|
|
|
|
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-12-06 13:39:54 +01:00
|
|
|
_noClipMode = false;
|
2022-11-13 18:56:00 +01:00
|
|
|
_playerHeightNumber = stream->readUint32LE();
|
2022-11-15 08:42:37 +01:00
|
|
|
_countdown = stream->readUint32LE();
|
|
|
|
_ticks = 0;
|
2022-07-17 15:46:32 +02:00
|
|
|
if (!_currentArea || _currentArea->getAreaID() != areaID)
|
|
|
|
gotoArea(areaID, -1); // Do not change position nor rotation
|
2022-11-13 09:58:52 +01:00
|
|
|
return loadGameStreamExtended(stream);
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-09-15 07:52:10 +02:00
|
|
|
stream->writeUint32LE(_gameStateBits);
|
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-11-13 18:56:00 +01:00
|
|
|
stream->writeUint32LE(_playerHeightNumber);
|
2022-11-15 08:42:37 +01:00
|
|
|
stream->writeUint32LE(_countdown);
|
2022-11-13 09:58:52 +01:00
|
|
|
return saveGameStreamExtended(stream, isAutosave);
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error FreescapeEngine::saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave) {
|
|
|
|
return Common::kNoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error FreescapeEngine::loadGameStreamExtended(Common::SeekableReadStream *stream) {
|
2022-07-17 15:46:32 +02:00
|
|
|
return Common::kNoError;
|
2021-04-12 21:29:57 -03:00
|
|
|
}
|
|
|
|
|
2022-11-09 12:11:28 +01:00
|
|
|
void FreescapeEngine::insertTemporaryMessage(const Common::String message, int deadline) {
|
|
|
|
_temporaryMessages.insert_at(0, message);
|
|
|
|
_temporaryMessageDeadlines.insert_at(0, deadline);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::getLatestMessages(Common::String &message, int &deadline) {
|
|
|
|
deadline = _countdown + 1;
|
|
|
|
message.clear();
|
|
|
|
while (!_temporaryMessages.empty() && deadline > _countdown) {
|
|
|
|
message = _temporaryMessages.back();
|
|
|
|
deadline = _temporaryMessageDeadlines.back();
|
|
|
|
_temporaryMessages.pop_back();
|
|
|
|
_temporaryMessageDeadlines.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-03 12:19:07 +01:00
|
|
|
void FreescapeEngine::clearTemporalMessages() {
|
|
|
|
_temporaryMessages.clear();
|
|
|
|
_temporaryMessageDeadlines.clear();
|
|
|
|
}
|
|
|
|
|
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);
|
2023-11-18 22:11:52 +01:00
|
|
|
Image::NeoDecoder decoder;
|
2022-10-02 19:16:51 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-03-07 08:55:43 +01:00
|
|
|
Graphics::ManagedSurface *FreescapeEngine::loadAndConvertNeoImage(Common::SeekableReadStream *stream, int offset, byte *palette) {
|
2022-10-02 22:46:59 +02:00
|
|
|
stream->seek(offset);
|
2023-11-18 22:11:52 +01:00
|
|
|
Image::NeoDecoder decoder(palette);
|
2022-10-02 22:46:59 +02:00
|
|
|
decoder.loadStream(*stream);
|
2023-03-07 08:55:43 +01:00
|
|
|
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
2022-10-02 19:16:51 +02:00
|
|
|
surface->copyFrom(*decoder.getSurface());
|
2024-03-20 22:49:38 -05:00
|
|
|
surface->convertToInPlace(_gfx->_currentPixelFormat, decoder.getPalette(), decoder.getPaletteColorCount());
|
2022-10-02 19:16:51 +02:00
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2023-03-07 08:55:43 +01:00
|
|
|
Graphics::ManagedSurface *FreescapeEngine::loadAndCenterScrImage(Common::SeekableReadStream *stream) {
|
2023-11-18 21:47:34 +01:00
|
|
|
Image::ScrDecoder decoder;
|
2023-02-22 20:51:30 +01:00
|
|
|
decoder.loadStream(*stream);
|
2023-03-07 08:55:43 +01:00
|
|
|
Graphics::ManagedSurface *surface = new Graphics::ManagedSurface();
|
2023-02-22 20:51:30 +01:00
|
|
|
const Graphics::Surface *decoded = decoder.getSurface();
|
|
|
|
surface->create(320, 200, decoded->format);
|
|
|
|
surface->copyRectToSurface(*decoded, (320 - decoded->w) / 2, (200 - decoded->h) / 2, Common::Rect(decoded->w, decoded->h));
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
2023-01-29 11:39:14 +01:00
|
|
|
void FreescapeEngine::getTimeFromCountdown(int &seconds, int &minutes, int &hours) {
|
|
|
|
int countdown = _countdown;
|
|
|
|
int h = countdown <= 0 ? 0 : countdown / 3600;
|
|
|
|
int m = countdown <= 0 ? 0 : (countdown - h * 3600) / 60;
|
|
|
|
int s = countdown <= 0 ? 0 : countdown - h * 3600 - m * 60;
|
|
|
|
|
|
|
|
seconds = s;
|
|
|
|
minutes = m;
|
|
|
|
hours = h;
|
|
|
|
}
|
|
|
|
|
2022-11-08 20:46:59 +01:00
|
|
|
static void countdownCallback(void *refCon) {
|
|
|
|
FreescapeEngine* self = (FreescapeEngine *)refCon;
|
2023-09-15 09:04:38 +02:00
|
|
|
if (self->isPaused())
|
|
|
|
return;
|
2022-11-15 08:36:00 +01:00
|
|
|
self->_ticks++;
|
|
|
|
if (self->_ticks % 50 == 0)
|
|
|
|
self->_countdown--;
|
2022-11-08 20:46:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FreescapeEngine::startCountdown(uint32 delay) {
|
|
|
|
_countdown = delay;
|
2022-11-30 11:40:53 +01:00
|
|
|
_ticks = 0;
|
2022-11-08 20:46:59 +01:00
|
|
|
_timerStarted = true;
|
2022-11-15 08:36:00 +01:00
|
|
|
uint32 oneTick = 1000000 / 50;
|
|
|
|
return g_system->getTimerManager()->installTimerProc(&countdownCallback, oneTick, (void *)this, "countdown");
|
2022-11-08 20:46:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FreescapeEngine::removeTimers() {
|
|
|
|
_timerStarted = false;
|
|
|
|
g_system->getTimerManager()->removeTimerProc(&countdownCallback);
|
|
|
|
}
|
|
|
|
|
2023-01-08 13:31:18 +00:00
|
|
|
void FreescapeEngine::pauseEngineIntern(bool pause) {
|
|
|
|
Engine::pauseEngineIntern(pause);
|
|
|
|
|
|
|
|
// TODO: Handle the viewport here
|
2023-08-09 12:14:56 +02:00
|
|
|
if (_frameLimiter)
|
|
|
|
_frameLimiter->pause(pause);
|
2023-01-08 13:31:18 +00:00
|
|
|
|
|
|
|
// Unlock the mouse so that the cursor is usable when the GMM opens
|
|
|
|
if (!_shootMode) {
|
|
|
|
_system->lockMouse(!pause);
|
|
|
|
}
|
|
|
|
}
|
2022-11-08 20:46:59 +01:00
|
|
|
|
2021-04-16 17:04:41 -03:00
|
|
|
} // namespace Freescape
|