1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "base/plugins.h"
24
25 #include "common/config-manager.h"
26 #include "common/events.h"
27 #include "common/file.h"
28 #include "common/fs.h"
29 #include "common/savefile.h"
30 #include "common/system.h"
31 #include "common/textconsole.h"
32
33 #include "engines/util.h"
34
35 #include "queen/queen.h"
36 #include "queen/bankman.h"
37 #include "queen/command.h"
38 #include "queen/cutaway.h"
39 #include "queen/debug.h"
40 #include "queen/display.h"
41 #include "queen/graphics.h"
42 #include "queen/grid.h"
43 #include "queen/input.h"
44 #include "queen/logic.h"
45 #include "queen/resource.h"
46 #include "queen/sound.h"
47 #include "queen/talk.h"
48 #include "queen/walk.h"
49
50 namespace Queen {
51
QueenEngine(OSystem * syst)52 QueenEngine::QueenEngine(OSystem *syst)
53 : Engine(syst), _bam(nullptr), _bankMan(nullptr), _command(nullptr), _debugger(nullptr),
54 _display(nullptr), _graphics(nullptr), _grid(nullptr), _input(nullptr), _logic(nullptr),
55 _sound(nullptr), _resource(nullptr), _walk(nullptr), _gameStarted(false),
56 randomizer("queen") {
57 }
58
~QueenEngine()59 QueenEngine::~QueenEngine() {
60 delete _bam;
61 delete _resource;
62 delete _bankMan;
63 delete _command;
64 delete _display;
65 delete _graphics;
66 delete _grid;
67 delete _input;
68 delete _logic;
69 delete _sound;
70 delete _walk;
71 //_debugger is deleted by Engine
72 }
73
registerDefaultSettings()74 void QueenEngine::registerDefaultSettings() {
75 ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED);
76 ConfMan.registerDefault("subtitles", true);
77 _subtitles = true;
78 }
79
checkOptionSettings()80 void QueenEngine::checkOptionSettings() {
81 // check talkspeed value
82 if (_talkSpeed < MIN_TEXT_SPEED) {
83 _talkSpeed = MIN_TEXT_SPEED;
84 } else if (_talkSpeed > MAX_TEXT_SPEED) {
85 _talkSpeed = MAX_TEXT_SPEED;
86 }
87
88 // demo and interview versions don't have speech at all
89 if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) {
90 _sound->speechToggle(false);
91 }
92
93 // ensure text is always on when voice is off
94 if (!_sound->speechOn()) {
95 _subtitles = true;
96 }
97 }
98
syncSoundSettings()99 void QueenEngine::syncSoundSettings() {
100 Engine::syncSoundSettings();
101
102 readOptionSettings();
103 }
104
readOptionSettings()105 void QueenEngine::readOptionSettings() {
106 bool mute = false;
107 if (ConfMan.hasKey("mute"))
108 mute = ConfMan.getBool("mute");
109
110 _sound->setVolume(ConfMan.getInt("music_volume"));
111 _sound->musicToggle(!(mute || ConfMan.getBool("music_mute")));
112 _sound->sfxToggle(!(mute || ConfMan.getBool("sfx_mute")));
113 _sound->speechToggle(!(mute || ConfMan.getBool("speech_mute")));
114 _talkSpeed = (ConfMan.getInt("talkspeed") * (MAX_TEXT_SPEED - MIN_TEXT_SPEED) + 255 / 2) / 255 + MIN_TEXT_SPEED;
115 _subtitles = ConfMan.getBool("subtitles");
116 checkOptionSettings();
117 }
118
writeOptionSettings()119 void QueenEngine::writeOptionSettings() {
120 ConfMan.setInt("music_volume", _sound->getVolume());
121 ConfMan.setBool("music_mute", !_sound->musicOn());
122 ConfMan.setBool("sfx_mute", !_sound->sfxOn());
123 ConfMan.setInt("talkspeed", ((_talkSpeed - MIN_TEXT_SPEED) * 255 + (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 2) / (MAX_TEXT_SPEED - MIN_TEXT_SPEED));
124 ConfMan.setBool("speech_mute", !_sound->speechOn());
125 ConfMan.setBool("subtitles", _subtitles);
126 ConfMan.flushToDisk();
127 }
128
update(bool checkPlayerInput)129 void QueenEngine::update(bool checkPlayerInput) {
130 _graphics->update(_logic->currentRoom());
131 _logic->update();
132
133 int frameDelay = (_lastUpdateTime + Input::DELAY_NORMAL - _system->getMillis());
134 if (frameDelay <= 0) {
135 frameDelay = 1;
136 }
137 _input->delay(frameDelay);
138 _lastUpdateTime = _system->getMillis();
139
140 if (!_resource->isInterview()) {
141 _display->palCustomScroll(_logic->currentRoom());
142 }
143 BobSlot *joe = _graphics->bob(0);
144 _display->update(joe->active, joe->x, joe->y);
145
146 _input->checkKeys();
147
148 if (canLoadOrSave()) {
149 if (_input->quickSave()) {
150 _input->quickSaveReset();
151 saveGameState(SLOT_QUICKSAVE, "Quicksave");
152 }
153 if (_input->quickLoad()) {
154 _input->quickLoadReset();
155 loadGameState(SLOT_QUICKSAVE);
156 }
157 }
158 if (!_input->cutawayRunning()) {
159 if (checkPlayerInput) {
160 _command->updatePlayer();
161 }
162 if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
163 _display->blankScreen();
164 }
165 }
166 _sound->updateMusic();
167 }
168
canLoadOrSave() const169 bool QueenEngine::canLoadOrSave() const {
170 return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview()) && _gameStarted;
171 }
172
canLoadGameStateCurrently()173 bool QueenEngine::canLoadGameStateCurrently() {
174 return canLoadOrSave();
175 }
176
canSaveGameStateCurrently()177 bool QueenEngine::canSaveGameStateCurrently() {
178 return canLoadOrSave();
179 }
180
saveGameState(int slot,const Common::String & desc,bool isAutosave)181 Common::Error QueenEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
182 debug(3, "Saving game to slot %d", slot);
183 char name[20];
184 Common::Error err = Common::kNoError;
185 makeGameStateName(slot, name);
186 Common::OutSaveFile *file = _saveFileMan->openForSaving(name);
187 if (file) {
188 // save data
189 byte *saveData = new byte[SAVESTATE_MAX_SIZE];
190 byte *p = saveData;
191 _bam->saveState(p);
192 _grid->saveState(p);
193 _logic->saveState(p);
194 _sound->saveState(p);
195 uint32 dataSize = p - saveData;
196 assert(dataSize < SAVESTATE_MAX_SIZE);
197
198 // write header
199 file->writeUint32BE('SCVM');
200 file->writeUint32BE(SAVESTATE_CUR_VER);
201 file->writeUint32BE(0);
202 file->writeUint32BE(dataSize);
203 char description[32];
204 Common::strlcpy(description, desc.c_str(), sizeof(description));
205 file->write(description, sizeof(description));
206
207 // write save data
208 file->write(saveData, dataSize);
209 file->finalize();
210
211 // check for errors
212 if (file->err()) {
213 warning("Can't write file '%s'. (Disk full?)", name);
214 err = Common::kWritingFailed;
215 }
216 delete[] saveData;
217 delete file;
218 } else {
219 warning("Can't create file '%s', game not saved", name);
220 err = Common::kCreatingFileFailed;
221 }
222
223 return err;
224 }
225
loadGameState(int slot)226 Common::Error QueenEngine::loadGameState(int slot) {
227 debug(3, "Loading game from slot %d", slot);
228 Common::Error err = Common::kNoError;
229 GameStateHeader header;
230 Common::InSaveFile *file = readGameStateHeader(slot, &header);
231 if (file && header.dataSize != 0) {
232 byte *saveData = new byte[header.dataSize];
233 byte *p = saveData;
234 if (file->read(saveData, header.dataSize) != header.dataSize) {
235 warning("Error reading savegame file");
236 err = Common::kReadingFailed;
237 } else {
238 _bam->loadState(header.version, p);
239 _grid->loadState(header.version, p);
240 _logic->loadState(header.version, p);
241 _sound->loadState(header.version, p);
242 if (header.dataSize != (uint32)(p - saveData)) {
243 warning("Corrupted savegame file");
244 err = Common::kReadingFailed; // FIXME
245 } else {
246 _logic->setupRestoredGame();
247 }
248 }
249 delete[] saveData;
250 delete file;
251 } else {
252 err = Common::kReadingFailed;
253 }
254
255 return err;
256 }
257
readGameStateHeader(int slot,GameStateHeader * gsh)258 Common::InSaveFile *QueenEngine::readGameStateHeader(int slot, GameStateHeader *gsh) {
259 char name[20];
260 makeGameStateName(slot, name);
261 Common::InSaveFile *file = _saveFileMan->openForLoading(name);
262 if (file && file->readUint32BE() == MKTAG('S','C','V','M')) {
263 gsh->version = file->readUint32BE();
264 gsh->flags = file->readUint32BE();
265 gsh->dataSize = file->readUint32BE();
266 file->read(gsh->description, sizeof(gsh->description));
267 } else {
268 memset(gsh, 0, sizeof(GameStateHeader));
269 }
270 return file;
271 }
272
getSaveStateName(int slot) const273 Common::String QueenEngine::getSaveStateName(int slot) const {
274 if (slot == SLOT_LISTPREFIX) {
275 return "queen.s??";
276 } else if (slot == SLOT_AUTOSAVE) {
277 slot = getAutosaveSlot();
278 }
279
280 assert(slot >= 0);
281 return Common::String::format("queen.s%02d", slot);
282 }
283
makeGameStateName(int slot,char * buf) const284 void QueenEngine::makeGameStateName(int slot, char *buf) const {
285 Common::String name = getSaveStateName(slot);
286 strcpy(buf, name.c_str());
287 }
288
getGameStateSlot(const char * filename) const289 int QueenEngine::getGameStateSlot(const char *filename) const {
290 int i = -1;
291 const char *slot = strrchr(filename, '.');
292 if (slot && (slot[1] == 's' || slot[1] == 'S')) {
293 i = atoi(slot + 2);
294 }
295 return i;
296 }
297
findGameStateDescriptions(char descriptions[100][32])298 void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) {
299 char prefix[20];
300 makeGameStateName(SLOT_LISTPREFIX, prefix);
301 Common::StringArray filenames = _saveFileMan->listSavefiles(prefix);
302 for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
303 int i = getGameStateSlot(it->c_str());
304 if (i >= 0 && i < SAVESTATE_MAX_NUM) {
305 GameStateHeader header;
306 Common::InSaveFile *f = readGameStateHeader(i, &header);
307 strcpy(descriptions[i], header.description);
308 delete f;
309 }
310 }
311 }
312
hasFeature(EngineFeature f) const313 bool Queen::QueenEngine::hasFeature(EngineFeature f) const {
314 return
315 (f == kSupportsReturnToLauncher) ||
316 (f == kSupportsLoadingDuringRuntime) ||
317 (f == kSupportsSavingDuringRuntime) ||
318 (f == kSupportsSubtitleOptions);
319 }
320
run()321 Common::Error QueenEngine::run() {
322 initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
323
324 _resource = new Resource();
325
326 _bam = new BamScene(this);
327 _bankMan = new BankManager(_resource);
328 _command = new Command(this);
329 _debugger = new Debugger(this);
330 setDebugger(_debugger);
331 _display = new Display(this, _system);
332 _graphics = new Graphics(this);
333 _grid = new Grid(this);
334 _input = new Input(_resource->getLanguage(), _system);
335
336 if (_resource->isDemo()) {
337 _logic = new LogicDemo(this);
338 } else if (_resource->isInterview()) {
339 _logic = new LogicInterview(this);
340 } else {
341 _logic = new LogicGame(this);
342 }
343
344 _sound = Sound::makeSoundInstance(_mixer, this, _resource->getCompression());
345
346 _walk = new Walk(this);
347 //_talkspeedScale = (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 255.0;
348
349 registerDefaultSettings();
350
351 // Setup mixer
352 syncSoundSettings();
353
354 _logic->start();
355 _gameStarted = true;
356 if (ConfMan.hasKey("save_slot") && canLoadOrSave()) {
357 loadGameState(ConfMan.getInt("save_slot"));
358 }
359 _lastUpdateTime = _system->getMillis();
360
361 while (!shouldQuit()) {
362 if (_logic->newRoom() > 0) {
363 _logic->update();
364 _logic->oldRoom(_logic->currentRoom());
365 _logic->currentRoom(_logic->newRoom());
366 _logic->changeRoom();
367 _display->fullscreen(false);
368 if (_logic->currentRoom() == _logic->newRoom()) {
369 _logic->newRoom(0);
370 }
371 } else if (_logic->joeWalk() == JWM_EXECUTE) {
372 _logic->joeWalk(JWM_NORMAL);
373 _command->executeCurrentAction();
374 } else {
375 _logic->joeWalk(JWM_NORMAL);
376 update(true);
377 }
378 }
379
380 return Common::kNoError;
381 }
382
383 } // End of namespace Queen
384