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), _gameStarted(false), _debugger(0), randomizer("queen") {
54 }
55
~QueenEngine()56 QueenEngine::~QueenEngine() {
57 delete _bam;
58 delete _resource;
59 delete _bankMan;
60 delete _command;
61 delete _debugger;
62 delete _display;
63 delete _graphics;
64 delete _grid;
65 delete _input;
66 delete _logic;
67 delete _sound;
68 delete _walk;
69 }
70
registerDefaultSettings()71 void QueenEngine::registerDefaultSettings() {
72 ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED);
73 ConfMan.registerDefault("subtitles", true);
74 _subtitles = true;
75 }
76
checkOptionSettings()77 void QueenEngine::checkOptionSettings() {
78 // check talkspeed value
79 if (_talkSpeed < MIN_TEXT_SPEED) {
80 _talkSpeed = MIN_TEXT_SPEED;
81 } else if (_talkSpeed > MAX_TEXT_SPEED) {
82 _talkSpeed = MAX_TEXT_SPEED;
83 }
84
85 // demo and interview versions don't have speech at all
86 if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) {
87 _sound->speechToggle(false);
88 }
89
90 // ensure text is always on when voice is off
91 if (!_sound->speechOn()) {
92 _subtitles = true;
93 }
94 }
95
syncSoundSettings()96 void QueenEngine::syncSoundSettings() {
97 Engine::syncSoundSettings();
98
99 readOptionSettings();
100 }
101
readOptionSettings()102 void QueenEngine::readOptionSettings() {
103 bool mute = false;
104 if (ConfMan.hasKey("mute"))
105 mute = ConfMan.getBool("mute");
106
107 _sound->setVolume(ConfMan.getInt("music_volume"));
108 _sound->musicToggle(!(mute || ConfMan.getBool("music_mute")));
109 _sound->sfxToggle(!(mute || ConfMan.getBool("sfx_mute")));
110 _sound->speechToggle(!(mute || ConfMan.getBool("speech_mute")));
111 _talkSpeed = (ConfMan.getInt("talkspeed") * (MAX_TEXT_SPEED - MIN_TEXT_SPEED) + 255 / 2) / 255 + MIN_TEXT_SPEED;
112 _subtitles = ConfMan.getBool("subtitles");
113 checkOptionSettings();
114 }
115
writeOptionSettings()116 void QueenEngine::writeOptionSettings() {
117 ConfMan.setInt("music_volume", _sound->getVolume());
118 ConfMan.setBool("music_mute", !_sound->musicOn());
119 ConfMan.setBool("sfx_mute", !_sound->sfxOn());
120 ConfMan.setInt("talkspeed", ((_talkSpeed - MIN_TEXT_SPEED) * 255 + (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 2) / (MAX_TEXT_SPEED - MIN_TEXT_SPEED));
121 ConfMan.setBool("speech_mute", !_sound->speechOn());
122 ConfMan.setBool("subtitles", _subtitles);
123 ConfMan.flushToDisk();
124 }
125
update(bool checkPlayerInput)126 void QueenEngine::update(bool checkPlayerInput) {
127 _debugger->onFrame();
128
129 _graphics->update(_logic->currentRoom());
130 _logic->update();
131
132 int frameDelay = (_lastUpdateTime + Input::DELAY_NORMAL - _system->getMillis());
133 if (frameDelay <= 0) {
134 frameDelay = 1;
135 }
136 _input->delay(frameDelay);
137 _lastUpdateTime = _system->getMillis();
138
139 if (!_resource->isInterview()) {
140 _display->palCustomScroll(_logic->currentRoom());
141 }
142 BobSlot *joe = _graphics->bob(0);
143 _display->update(joe->active, joe->x, joe->y);
144
145 _input->checkKeys();
146 if (_input->debugger()) {
147 _input->debuggerReset();
148 _debugger->attach();
149 }
150 if (canLoadOrSave()) {
151 if (_input->quickSave()) {
152 _input->quickSaveReset();
153 saveGameState(SLOT_QUICKSAVE, "Quicksave");
154 }
155 if (_input->quickLoad()) {
156 _input->quickLoadReset();
157 loadGameState(SLOT_QUICKSAVE);
158 }
159 if (shouldPerformAutoSave(_lastSaveTime)) {
160 saveGameState(SLOT_AUTOSAVE, "Autosave");
161 _lastSaveTime = _system->getMillis();
162 }
163 }
164 if (!_input->cutawayRunning()) {
165 if (checkPlayerInput) {
166 _command->updatePlayer();
167 }
168 if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
169 _display->blankScreen();
170 }
171 }
172 _sound->updateMusic();
173 }
174
canLoadOrSave() const175 bool QueenEngine::canLoadOrSave() const {
176 return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview()) && _gameStarted;
177 }
178
canLoadGameStateCurrently()179 bool QueenEngine::canLoadGameStateCurrently() {
180 return canLoadOrSave();
181 }
182
canSaveGameStateCurrently()183 bool QueenEngine::canSaveGameStateCurrently() {
184 return canLoadOrSave();
185 }
186
saveGameState(int slot,const Common::String & desc)187 Common::Error QueenEngine::saveGameState(int slot, const Common::String &desc) {
188 debug(3, "Saving game to slot %d", slot);
189 char name[20];
190 Common::Error err = Common::kNoError;
191 makeGameStateName(slot, name);
192 Common::OutSaveFile *file = _saveFileMan->openForSaving(name);
193 if (file) {
194 // save data
195 byte *saveData = new byte[SAVESTATE_MAX_SIZE];
196 byte *p = saveData;
197 _bam->saveState(p);
198 _grid->saveState(p);
199 _logic->saveState(p);
200 _sound->saveState(p);
201 uint32 dataSize = p - saveData;
202 assert(dataSize < SAVESTATE_MAX_SIZE);
203
204 // write header
205 file->writeUint32BE('SCVM');
206 file->writeUint32BE(SAVESTATE_CUR_VER);
207 file->writeUint32BE(0);
208 file->writeUint32BE(dataSize);
209 char description[32];
210 Common::strlcpy(description, desc.c_str(), sizeof(description));
211 file->write(description, sizeof(description));
212
213 // write save data
214 file->write(saveData, dataSize);
215 file->finalize();
216
217 // check for errors
218 if (file->err()) {
219 warning("Can't write file '%s'. (Disk full?)", name);
220 err = Common::kWritingFailed;
221 }
222 delete[] saveData;
223 delete file;
224 } else {
225 warning("Can't create file '%s', game not saved", name);
226 err = Common::kCreatingFileFailed;
227 }
228
229 return err;
230 }
231
loadGameState(int slot)232 Common::Error QueenEngine::loadGameState(int slot) {
233 debug(3, "Loading game from slot %d", slot);
234 Common::Error err = Common::kNoError;
235 GameStateHeader header;
236 Common::InSaveFile *file = readGameStateHeader(slot, &header);
237 if (file && header.dataSize != 0) {
238 byte *saveData = new byte[header.dataSize];
239 byte *p = saveData;
240 if (file->read(saveData, header.dataSize) != header.dataSize) {
241 warning("Error reading savegame file");
242 err = Common::kReadingFailed;
243 } else {
244 _bam->loadState(header.version, p);
245 _grid->loadState(header.version, p);
246 _logic->loadState(header.version, p);
247 _sound->loadState(header.version, p);
248 if (header.dataSize != (uint32)(p - saveData)) {
249 warning("Corrupted savegame file");
250 err = Common::kReadingFailed; // FIXME
251 } else {
252 _logic->setupRestoredGame();
253 }
254 }
255 delete[] saveData;
256 delete file;
257 } else {
258 err = Common::kReadingFailed;
259 }
260
261 return err;
262 }
263
readGameStateHeader(int slot,GameStateHeader * gsh)264 Common::InSaveFile *QueenEngine::readGameStateHeader(int slot, GameStateHeader *gsh) {
265 char name[20];
266 makeGameStateName(slot, name);
267 Common::InSaveFile *file = _saveFileMan->openForLoading(name);
268 if (file && file->readUint32BE() == MKTAG('S','C','V','M')) {
269 gsh->version = file->readUint32BE();
270 gsh->flags = file->readUint32BE();
271 gsh->dataSize = file->readUint32BE();
272 file->read(gsh->description, sizeof(gsh->description));
273 } else {
274 memset(gsh, 0, sizeof(GameStateHeader));
275 }
276 return file;
277 }
278
makeGameStateName(int slot,char * buf) const279 void QueenEngine::makeGameStateName(int slot, char *buf) const {
280 if (slot == SLOT_LISTPREFIX) {
281 strcpy(buf, "queen.s??");
282 } else if (slot == SLOT_AUTOSAVE) {
283 strcpy(buf, "queen.asd");
284 } else {
285 assert(slot >= 0);
286 sprintf(buf, "queen.s%02d", slot);
287 }
288 }
289
getGameStateSlot(const char * filename) const290 int QueenEngine::getGameStateSlot(const char *filename) const {
291 int i = -1;
292 const char *slot = strrchr(filename, '.');
293 if (slot && (slot[1] == 's' || slot[1] == 'S')) {
294 i = atoi(slot + 2);
295 }
296 return i;
297 }
298
findGameStateDescriptions(char descriptions[100][32])299 void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) {
300 char prefix[20];
301 makeGameStateName(SLOT_LISTPREFIX, prefix);
302 Common::StringArray filenames = _saveFileMan->listSavefiles(prefix);
303 for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
304 int i = getGameStateSlot(it->c_str());
305 if (i >= 0 && i < SAVESTATE_MAX_NUM) {
306 GameStateHeader header;
307 Common::InSaveFile *f = readGameStateHeader(i, &header);
308 strcpy(descriptions[i], header.description);
309 delete f;
310 }
311 }
312 }
313
getDebugger()314 GUI::Debugger *QueenEngine::getDebugger() {
315 return _debugger;
316 }
317
hasFeature(EngineFeature f) const318 bool Queen::QueenEngine::hasFeature(EngineFeature f) const {
319 return
320 (f == kSupportsRTL) ||
321 (f == kSupportsLoadingDuringRuntime) ||
322 (f == kSupportsSavingDuringRuntime) ||
323 (f == kSupportsSubtitleOptions);
324 }
325
run()326 Common::Error QueenEngine::run() {
327 initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
328
329 _resource = new Resource();
330
331 _bam = new BamScene(this);
332 _bankMan = new BankManager(_resource);
333 _command = new Command(this);
334 _debugger = new Debugger(this);
335 _display = new Display(this, _system);
336 _graphics = new Graphics(this);
337 _grid = new Grid(this);
338 _input = new Input(_resource->getLanguage(), _system);
339
340 if (_resource->isDemo()) {
341 _logic = new LogicDemo(this);
342 } else if (_resource->isInterview()) {
343 _logic = new LogicInterview(this);
344 } else {
345 _logic = new LogicGame(this);
346 }
347
348 _sound = Sound::makeSoundInstance(_mixer, this, _resource->getCompression());
349
350 _walk = new Walk(this);
351 //_talkspeedScale = (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 255.0;
352
353 registerDefaultSettings();
354
355 // Setup mixer
356 syncSoundSettings();
357
358 _logic->start();
359 if (ConfMan.hasKey("save_slot") && canLoadOrSave()) {
360 loadGameState(ConfMan.getInt("save_slot"));
361 }
362 _lastSaveTime = _lastUpdateTime = _system->getMillis();
363
364 while (!shouldQuit()) {
365 if (_logic->newRoom() > 0) {
366 _logic->update();
367 _logic->oldRoom(_logic->currentRoom());
368 _logic->currentRoom(_logic->newRoom());
369 _logic->changeRoom();
370 _display->fullscreen(false);
371 // From this point onwards it is safe to use the load/save
372 // menu, so consider game to be 'started'
373 _gameStarted = true;
374 if (_logic->currentRoom() == _logic->newRoom()) {
375 _logic->newRoom(0);
376 }
377 } else if (_logic->joeWalk() == JWM_EXECUTE) {
378 _logic->joeWalk(JWM_NORMAL);
379 _command->executeCurrentAction();
380 } else {
381 _logic->joeWalk(JWM_NORMAL);
382 update(true);
383 }
384 }
385
386 return Common::kNoError;
387 }
388
389 } // End of namespace Queen
390