/* 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 . * */ #include "common/str.h" #include "common/system.h" #include "common/timer.h" #include "private/grammar.h" #include "private/private.h" #include "private/tokens.h" namespace Private { static void fChgMode(ArgArray args) { // assert types assert(args.size() == 2 || args.size() == 3); assert(args[0].type == NUM); if (args.size() == 2) debugC(1, kPrivateDebugScript, "ChgMode(%d, %s)", args[0].u.val, args[1].u.sym->name->c_str()); else if (args.size() == 3) debugC(1, kPrivateDebugScript, "ChgMode(%d, %s, %s)", args[0].u.val, args[1].u.sym->name->c_str(), args[2].u.sym->name->c_str()); else assert(0); g_private->_mode = args[0].u.val; g_private->_nextSetting = args[1].u.sym->name->c_str(); if (g_private->_mode == 0) { g_private->_origin = Common::Point(kOriginZero[0], kOriginZero[1]); } else if (g_private->_mode == 1) { g_private->_origin = Common::Point(kOriginOne[0], kOriginOne[1]); } else assert(0); if (args.size() == 3) { Symbol *location = g_private->maps.lookupLocation(args[2].u.sym->name); g_private->setLocationAsVisited(location); // set a game flag when visiting the police station. if (!g_private->isDemo()) { if (*(args[2].u.sym->name) == g_private->getPoliceStationLocation()) { Common::String beenDowntownName = g_private->getBeenDowntownVariable(); Symbol *beenDowntown = g_private->maps.lookupVariable(&beenDowntownName); setSymbol(beenDowntown, 1); } } } if (g_private->_mode == 0) { // This is the only place where this should be used if (g_private->_noStopSounds) { g_private->_noStopSounds = false; } else { g_private->stopSounds(); } } } static void fVSPicture(ArgArray args) { assert(args[0].type == STRING); debugC(1, kPrivateDebugScript, "VSPicture(%s)", args[0].u.str); g_private->_nextVS = args[0].u.str; } static void fDiaryLocList(ArgArray args) { int x1, y1, x2, y2; assert(args[0].type == NUM); assert(args[1].type == NUM); assert(args[2].type == NUM); assert(args[3].type == NUM); g_private->_currentDiaryPage = -1; debugC(1, kPrivateDebugScript, "DiaryLocList(%d, %d, %d, %d)", args[0].u.val, args[1].u.val, args[2].u.val, args[3].u.val); x2 = args[0].u.val; y2 = args[1].u.val; x1 = args[2].u.val; y1 = args[3].u.val; Common::Rect rect(x1, y1, x2, y2); g_private->loadLocations(rect); } static void fDiaryGoLoc(ArgArray args) { debugC(1, kPrivateDebugScript, "DiaryGoLoc(%d, ..)", args[0].u.val); ExitInfo e; e.rect = *args[1].u.rect; e.nextSetting = g_private->getDiaryMiddleSetting(); if (args[0].u.val) { e.cursor = "kTurnRight"; g_private->_diaryNextPageExit = e; } else { e.cursor = "kTurnLeft"; g_private->_diaryPrevPageExit = e; } g_private->_exits.push_front(e); } static void fDiaryPageTurn(ArgArray args) { debugC(1, kPrivateDebugScript, "DiaryPageTurn(%d, ..)", args[0].u.val); ExitInfo e; e.nextSetting = g_private->getDiaryMiddleSetting(); e.rect = *args[1].u.rect; if (args[0].u.val == 1) { e.cursor = "kTurnRight"; if (g_private->_currentDiaryPage >= (int)g_private->_diaryPages.size() - 1) { e.nextSetting = g_private->getDiaryLastPageSetting(); } g_private->_diaryNextPageExit = e; } else { e.cursor = "kTurnLeft"; if (g_private->_currentDiaryPage <= 0) { e.nextSetting = g_private->getDiaryTOCSetting(); } g_private->_diaryPrevPageExit = e; } g_private->_exits.push_front(e); } static void fDiaryPage(ArgArray args) { debugC(1, kPrivateDebugScript, "DiaryPage(%d, %d, %d, %d, ..)", args[0].u.rect->left, args[0].u.rect->top, args[0].u.rect->right, args[0].u.rect->bottom); g_private->loadMemories(*args[0].u.rect, args[1].u.val, args[2].u.val); } static void fDiaryInvList(ArgArray args) { debugC(1, kPrivateDebugScript, "DiaryInvList(%d, ..)", args[0].u.val); g_private->_currentDiaryPage = g_private->_diaryPages.size(); const Common::Rect *r1 = args[1].u.rect; const Common::Rect *r2 = args[2].u.rect; g_private->loadInventory(args[0].u.val, *r1, *r2); } static void fgoto(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "goto(%s)", args[0].u.str); g_private->_nextSetting = args[0].u.str; } static void fSyncSound(ArgArray args) { assert(args[0].type == STRING); assert(args[1].type == NAME); debugC(1, kPrivateDebugScript, "SyncSound(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str()); g_private->_nextSetting = args[1].u.sym->name->c_str(); Common::String s = args[0].u.str; if (s != "\"\"") { g_private->drawScreen(); g_private->stopSounds(); g_private->playForegroundSound(s); g_private->waitForSoundsToStop(); } } static void fQuit(ArgArray args) { debugC(1, kPrivateDebugScript, "Quit()"); g_private->quitGame(); } static void fLoadGame(ArgArray args) { assert(args[0].type == STRING); assert(args[2].type == NAME); debugC(1, kPrivateDebugScript, "LoadGame(%s, %s)", args[0].u.str, args[2].u.sym->name->c_str()); MaskInfo m; if (strcmp(args[0].u.str, "\"\"") == 0) // Not sure why the game tries to load an empty mask return; m.surf = g_private->loadMask(args[0].u.str, 0, 0, true); m.cursor = *args[2].u.sym->name; m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_loadGameMask = m; g_private->_masks.push_front(m); } static void fSaveGame(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "SaveGame(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str()); MaskInfo m; m.surf = g_private->loadMask(args[0].u.str, 0, 0, true); m.cursor = *args[1].u.sym->name; m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_saveGameMask = m; g_private->_masks.push_front(m); } static void fRestartGame(ArgArray args) { assert(args.size() == 0); g_private->restartGame(); } static void fPoliceBust(ArgArray args) { // assert types assert(args.size() == 1 || args.size() == 2); int mode = (args.size() == 2) ? args[1].u.val : 0; debugC(1, kPrivateDebugScript, "PoliceBust(%d, %d)", args[0].u.val, mode); if (mode == 3) { g_private->completePoliceBust(); return; } if (mode == 2) { g_private->wallSafeAlarm(); return; } if (mode == 1) { // Not implemented: a special mode for police busts // in Marlowe's office that was removed from the game. return; } if (args[0].u.val) { g_private->startPoliceBust(); } else { g_private->stopPoliceBust(); } } static void fBustMovie(ArgArray args) { // assert types assert(args.size() == 1); debugC(1, kPrivateDebugScript, "BustMovie(%s)", args[0].u.sym->name->c_str()); g_private->_nextMovie = g_private->_policeBustMovie; g_private->_nextSetting = args[0].u.sym->name->c_str(); Common::String memoryPath = g_private->_policeBustMovie; memoryPath.replace('/', '\\'); g_private->addMemory(memoryPath); } static void fDossierAdd(ArgArray args) { assert(args.size() == 2); Common::String s1 = args[0].u.str; Common::String s2 = args[1].u.str; if (s2 == "\"\"") { s2 = ""; } g_private->addDossier(s1, s2); } static void fDossierBitmap(ArgArray args) { assert(args.size() == 2); int x = args[0].u.val; int y = args[1].u.val; assert(x == 40 && y == 30); g_private->loadDossier(); } static void fDossierChgSheet(ArgArray args) { assert(args.size() == 4); debugC(1, kPrivateDebugScript, "DossierChgSheet(%s,%d,%d,%d)", args[0].u.str, args[1].u.val, args[2].u.val, args[3].u.val); Common::String s(args[0].u.str); MaskInfo m; // do nothing if suspect only has one sheet if (g_private->_dossiers[g_private->_dossierSuspect].page2.empty()) { return; } int p = args[1].u.val; int x = args[2].u.val; int y = args[3].u.val; m.surf = g_private->loadMask(s, x, y, true); m.cursor = g_private->getExitCursor(); m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; if (p == 0) g_private->_dossierPrevSheetMask = m; else if (p == 1) g_private->_dossierNextSheetMask = m; else error("Invalid sheet number in DossierChgSheet %d", p); g_private->_masks.push_front(m); } static void fDossierPrevSuspect(ArgArray args) { assert(args.size() == 3); Common::String s(args[0].u.str); MaskInfo m; if (g_private->_dossierSuspect == 0) { return; } int x = args[1].u.val; int y = args[2].u.val; m.surf = g_private->loadMask(s, x, y, true); m.cursor = g_private->getExitCursor(); m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_dossierPrevSuspectMask = m; g_private->_masks.push_front(m); } static void fDossierNextSuspect(ArgArray args) { assert(args.size() == 3); Common::String s(args[0].u.str); MaskInfo m; if ((g_private->_dossierSuspect + 1) >= g_private->_dossiers.size()) { return; } int x = args[1].u.val; int y = args[2].u.val; m.surf = g_private->loadMask(s, x, y, true); m.cursor = g_private->getExitCursor(); m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_dossierNextSuspectMask = m; g_private->_masks.push_front(m); } static void fNoStopSounds(ArgArray args) { assert(args.size() == 0); debugC(1, kPrivateDebugScript, "NoStopSounds()"); g_private->_noStopSounds = true; } static void fLoseInventory(ArgArray args) { assert(args.size() == 0); debugC(1, kPrivateDebugScript, "LoseInventory()"); g_private->removeRandomInventory(); } static void fInventory(ArgArray args) { // assert types Datum b1 = args[0]; Datum v1 = args[1]; Datum v2 = args[2]; Datum e = args[3]; Datum i = args[4]; Datum c = args[5]; Datum snd; if (args.size() >= 9) snd = args[8]; else { snd.type = STRING; snd.u.str = "\"\""; } assert(v1.type == STRING || v1.type == NAME); assert(b1.type == STRING); assert(e.type == NAME || e.type == NUM); assert(snd.type == STRING); assert(i.type == STRING); Common::String bmp(i.u.str); assert(g_private->isDemo() || bmp != "\"\""); if (v1.type == STRING) assert(strcmp(v1.u.str, "\"\"") == 0); debugC(1, kPrivateDebugScript, "Inventory(...)"); Common::String mask(b1.u.str); if (mask != "\"\"") { if (bmp != "\"\"" && g_private->inInventory(bmp)) { return; } MaskInfo m; m.surf = g_private->loadMask(mask, 0, 0, true); if (e.type == NUM) { assert(e.u.val == 0); m.nextSetting = ""; } else m.nextSetting = e.u.sym->name->c_str(); m.cursor = g_private->getInventoryCursor(); m.point = Common::Point(0, 0); if (v1.type == NAME) { m.flag1 = g_private->maps.lookupVariable(v1.u.sym->name); } else m.flag1 = nullptr; if (v2.type == NAME) { m.flag2 = g_private->maps.lookupVariable(v2.u.sym->name); } else m.flag2 = nullptr; m.inventoryItem = bmp; g_private->_masks.push_front(m); g_private->_toTake = true; Common::String sound(snd.u.str); if (sound == "\"\"") { sound = g_private->getTakeLeaveSound(); } g_private->playForegroundSound(g_private->_takeLeaveSound, sound); } else { Common::String flag; if (v1.type == NAME) { if (strcmp(c.u.str, "\"REMOVE\"") == 0) { g_private->removeInventory(bmp); } else { flag = *(v1.u.sym->name); g_private->addInventory(bmp, flag); } } else { g_private->addInventory(bmp, flag); } if (v2.type == NAME) { v2.u.sym = g_private->maps.lookupVariable(v2.u.sym->name); v2.u.sym->u.val = 1; } } } static void fSetFlag(ArgArray args) { assert(args.size() == 2); assert(args[0].type == NAME && args[1].type == NUM); debugC(1, kPrivateDebugScript, "SetFlag(%s, %d)", args[0].u.sym->name->c_str(), args[1].u.val); args[0].u.sym = g_private->maps.lookupVariable(args[0].u.sym->name); args[0].u.sym->u.val = args[1].u.val; } static void fExit(ArgArray args) { // assert types assert(args[2].type == RECT || args[2].type == NAME); debugC(1, kPrivateDebugScript, "Exit(%d %d %d)", args[0].type, args[1].type, args[2].type); //, args[0].u.str, args[1].u.sym->name->c_str(), "RECT"); ExitInfo e; if (args[0].type == NUM && args[0].u.val == 0) e.nextSetting = ""; else e.nextSetting = args[0].u.sym->name->c_str(); if (args[1].type == NUM && args[1].u.val == 0) e.cursor = ""; else e.cursor = *args[1].u.sym->name; if (args[2].type == NAME) { Symbol *rect = g_private->maps.lookupRect(args[2].u.sym->name); assert(rect->type == RECT); args[2].u.rect = rect->u.rect; } e.rect = *args[2].u.rect; g_private->_exits.push_front(e); } static void fSetModifiedFlag(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "SetModifiedFlag(%d)", args[0].u.val); g_private->_modified = args[0].u.val != 0; } static void fPaperShuffleSound(ArgArray args) { assert(args.size() == 0); debugC(1, kPrivateDebugScript, "PaperShuffleSound()"); g_private->playForegroundSound(g_private->getPaperShuffleSound()); } static void fSoundEffect(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "SoundEffect(%s)", args[0].u.str); Common::String s(args[0].u.str); if (s != "\"\"") { g_private->playForegroundSound(s); } else { g_private->stopSounds(); } } static void fSound(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "Sound(%s)", args[0].u.str); if (args.size() == 4) { bool b1 = args[1].u.val != 0; bool b2 = args[2].u.val != 0; int c = args[3].u.val; if (!b1 && !b2 && c == 1) { g_private->stopSounds(); } else if (!b1 && !b2 && c == 2) { g_private->stopForegroundSounds(); } else assert(0); } Common::String s(args[0].u.str); if (s != "\"\"") { g_private->playForegroundSound(s); } else { g_private->stopSounds(); } } static void fLoopedSound(ArgArray args) { // assert types assert(args.size() == 1); debugC(1, kPrivateDebugScript, "LoopedSound(%s)", args[0].u.str); Common::String s(args[0].u.str); if (s != "\"\"") { g_private->playBackgroundSound(s); } else { g_private->stopSounds(); } } static void fViewScreen(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "WARNING: ViewScreen not implemented!"); } static void fTransition(ArgArray args) { assert(args[0].type == STRING); assert(args[1].type == NAME); debugC(1, kPrivateDebugScript, "Transition(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str()); g_private->_nextMovie = args[0].u.str; g_private->_nextSetting = args[1].u.sym->name->c_str(); } static void fResume(ArgArray args) { assert(args[0].type == NUM); debugC(1, kPrivateDebugScript, "Resume(%d)", args[0].u.val); // this value is always 1 g_private->resumeGame(); } static void fMovie(ArgArray args) { // assert types assert(args[0].type == STRING); assert(args[1].type == NAME); debugC(1, kPrivateDebugScript, "Movie(%s, %s)", args[0].u.str, args[1].u.sym->name->c_str()); Common::String movie = args[0].u.str; Common::String nextSetting = *args[1].u.sym->name; if (!g_private->_playedMovies.contains(movie) && movie != "\"\"") { g_private->addMemory(movie); g_private->_nextMovie = movie; g_private->_playedMovies.setVal(movie, true); g_private->_nextSetting = nextSetting; } else if (movie == "\"\"") { g_private->_repeatedMovieExit = nextSetting; debugC(1, kPrivateDebugScript, "repeated movie exit is %s", nextSetting.c_str()); } else { debugC(1, kPrivateDebugScript, "movie %s already played", movie.c_str()); g_private->_nextSetting = g_private->_repeatedMovieExit; } } static void fCRect(ArgArray args) { // assert types debugC(1, kPrivateDebugScript, "CRect(%d, %d, %d, %d)", args[0].u.val, args[1].u.val, args[2].u.val, args[3].u.val); int x1, y1, x2, y2; x1 = args[0].u.val; y1 = args[1].u.val; x2 = args[2].u.val; y2 = args[3].u.val; Datum d = Datum(); Common::Rect *rect = new Common::Rect(x1, y1, x2, y2); d.type = RECT; d.u.rect = rect; Gen::push(d); g_private->_rects.push_back(rect); } static void fBitmap(ArgArray args) { assert(args.size() == 1 || args.size() == 3); int x = 0; int y = 0; const char *f = args[0].u.str; if (args.size() == 3) { x = args[1].u.val; y = args[2].u.val; } debugC(1, kPrivateDebugScript, "Bitmap(%s, %d, %d)", f, x, y); Common::String s(args[0].u.str); g_private->loadImage(s, x, y); } static void _fMask(ArgArray args, bool drawn) { assert(args.size() == 3 || args.size() == 5); int x = 0; int y = 0; const char *f = args[0].u.str; const char *e = args[1].u.sym->name->c_str(); Common::String *c = args[2].u.sym->name; if (args.size() == 5) { x = args[3].u.val; y = args[4].u.val; } debugC(1, kPrivateDebugScript, "Mask(%s, %s, %s, %d, %d)", f, e, c->c_str(), x, y); const Common::String s(f); MaskInfo m; m.surf = g_private->loadMask(s, x, y, drawn); m.nextSetting = e; m.cursor = *c; m.flag1 = nullptr; m.flag2 = nullptr; m.point = Common::Point(x, y); g_private->_masks.push_front(m); } static void fMask(ArgArray args) { _fMask(args, false); } static void fMaskDrawn(ArgArray args) { _fMask(args, true); } static void fAMRadioClip(ArgArray args) { assert(args.size() <= 4); debugC(1, kPrivateDebugScript, "AMRadioClip(%s,%d,...)", args[0].u.str, args[1].u.val); const char *name = args[0].u.str; if (strcmp(name, "\"\"") == 0) { int clipCount = args[1].u.val; g_private->initializeAMRadioChannels(clipCount); return; } int priority = args[1].u.val; // The third and fourth parameters are numbers followed by an optional '+' character. // Each number is a priority and the '+' indicates that it is to be treated as a range // instead of the default behavior of requiring an exact match. int disabledPriority1 = (args.size() >= 3) ? args[2].u.val : 0; bool exactPriorityMatch1 = (args.size() >= 3) ? (args[2].type != NUM_PLUS) : true; int disabledPriority2 = (args.size() >= 4) ? args[3].u.val : 0; bool exactPriorityMatch2 = (args.size() >= 4) ? (args[3].type != NUM_PLUS) : true; Common::String flagName = (args.size() >= 6) ? *(args[4].u.sym->name) : ""; int flagValue = (args.size() >= 6) ? args[5].u.val : 0; g_private->addRadioClip(g_private->_AMRadio, name, priority, disabledPriority1, exactPriorityMatch1, disabledPriority2, exactPriorityMatch2, flagName, flagValue); } static void fPoliceClip(ArgArray args) { assert(args.size() <= 4 || args.size() == 6); debugC(1, kPrivateDebugScript, "PoliceClip(%s,%d,...)", args[0].u.str, args[1].u.val); const char *name = args[0].u.str; if (strcmp(name, "\"\"") == 0) { g_private->initializePoliceRadioChannels(); return; } int priority = args[1].u.val; if (strcmp(name, "\"DISABLE_ONLY\"") == 0) { g_private->disableRadioClips(g_private->_policeRadio, priority); return; } // The third and fourth parameters are numbers followed by an optional '+' character. // Each number is a priority and the '+' indicates that it is to be treated as a range // instead of the default behavior of requiring an exact match. int disabledPriority1 = (args.size() >= 3) ? args[2].u.val : 0; bool exactPriorityMatch1 = (args.size() >= 3) ? (args[2].type != NUM_PLUS) : true; int disabledPriority2 = (args.size() >= 4) ? args[3].u.val : 0; bool exactPriorityMatch2 = (args.size() >= 4) ? (args[3].type != NUM_PLUS) : true; g_private->addRadioClip(g_private->_policeRadio, name, priority, disabledPriority1, exactPriorityMatch1, disabledPriority2, exactPriorityMatch2, "", 0); } static void fPhoneClip(ArgArray args) { if (args.size() == 2) { debugC(1, kPrivateDebugScript, "Unimplemented PhoneClip special case"); return; } assert(args.size() == 6); debugC(1, kPrivateDebugScript, "PhoneClip(%s,%d,%d,%d,%s,%d)", args[0].u.str, args[1].u.val, args[2].u.val, args[3].u.val, args[4].u.sym->name->c_str(), args[5].u.val); const char *name = args[0].u.str; bool once = (args[1].u.val != 0); int startIndex = args[2].u.val; int endIndex = args[3].u.val; Common::String *flagName = args[4].u.sym->name; int flagValue = args[5].u.val; assert(startIndex <= endIndex); g_private->addPhone(name, once, startIndex, endIndex, *flagName, flagValue); } static void fSoundArea(ArgArray args) { // assert types //char *n; Common::String n; if (args[1].type == NAME) n = *(args[1].u.sym->name); else if (args[1].type == STRING) { n = Common::String(args[1].u.str); Common::replace(n, "\"", ""); Common::replace(n, "\"", ""); } else error("Invalid input for SoundArea"); debugC(1, kPrivateDebugScript, "SoundArea(%s, %s, ..)", args[0].u.str, n.c_str()); Common::String s = args[0].u.str; MaskInfo m; if (n == "kAMRadio") { m.surf = g_private->loadMask(s, 0, 0, true); m.cursor = *args[2].u.sym->name; m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_AMRadioArea = m; g_private->_masks.push_front(m); } else if (n == "kPoliceRadio") { m.surf = g_private->loadMask(s, 0, 0, true); m.cursor = *args[2].u.sym->name; m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_policeRadioArea = m; g_private->_masks.push_front(m); } else if (n == "kPhone") { m.surf = g_private->loadMask(s, 0, 0, true); m.cursor = *args[2].u.sym->name; m.nextSetting = ""; m.flag1 = nullptr; m.flag2 = nullptr; g_private->_phoneArea = m; g_private->initializePhoneOnDesktop(); } else error("Invalid type for SoundArea"); } static void fSafeDigit(ArgArray args) { assert(args[0].type == NUM); assert(args[1].type == RECT); debugC(1, kPrivateDebugScript, "SafeDigit(%d, ..)", args[0].u.val); g_private->addSafeDigit(args[0].u.val, args[1].u.rect); } static void fAskSave(ArgArray args) { // This is not needed, since scummvm will take care of this debugC(1, kPrivateDebugScript, "WARNING: AskSave is partially implemented"); g_private->_nextSetting = *args[0].u.sym->name; } static void fTimer(ArgArray args) { assert(args.size() == 2 || args.size() == 3); if (args.size() == 3) debugC(1, kPrivateDebugScript, "Timer(%d, %s, %s)", args[0].u.val, args[1].u.sym->name->c_str(), args[2].u.sym->name->c_str()); else debugC(1, kPrivateDebugScript, "Timer(%d, %s)", args[0].u.val, args[1].u.str); int32 delay = args[0].u.val * 1000; // seconds => milliseconds if (delay > 0) { Common::String skipSetting; if (args.size() == 3) { skipSetting = *(args[2].u.sym->name); } g_private->setTimer(delay, *(args[1].u.sym->name), skipSetting); } else if (delay == 0) { g_private->_nextSetting = *(args[1].u.sym->name); } else { assert(0); } } const FuncTable funcTable[] = { // Control flow {fChgMode, "ChgMode"}, {fResume, "Resume"}, {fgoto, "goto"}, {fTimer, "Timer"}, // Variables {fSetFlag, "SetFlag"}, {fSetModifiedFlag, "SetModifiedFlag"}, // Sounds {fSound, "Sound"}, {fSoundEffect, "SoundEffect"}, {fLoopedSound, "LoopedSound"}, {fNoStopSounds, "NoStopSounds"}, {fSyncSound, "SyncSound"}, {fAMRadioClip, "AMRadioClip"}, {fPoliceClip, "PoliceClip"}, {fPhoneClip, "PhoneClip"}, {fSoundArea, "SoundArea"}, {fPaperShuffleSound, "PaperShuffleSound"}, // Images {fBitmap, "Bitmap"}, {fMask, "Mask"}, {fMaskDrawn, "MaskDrawn"}, {fVSPicture, "VSPicture"}, {fViewScreen, "ViewScreen"}, {fExit, "Exit"}, // Video {fTransition, "Transition"}, {fMovie, "Movie"}, // Diary {fDiaryLocList, "DiaryLocList"}, {fDiaryPageTurn, "DiaryPageTurn"}, {fDiaryPage, "DiaryPage"}, {fDiaryInvList, "DiaryInvList"}, {fDiaryGoLoc, "DiaryGoLoc"}, // Main menu {fQuit, "Quit"}, {fLoadGame, "LoadGame"}, {fSaveGame, "SaveGame"}, {fAskSave, "AskSave"}, {fRestartGame, "RestartGame"}, // Dossiers {fDossierAdd, "DossierAdd"}, {fDossierChgSheet, "DossierChgSheet"}, {fDossierBitmap, "DossierBitmap"}, {fDossierPrevSuspect, "DossierPrevSuspect"}, {fDossierNextSuspect, "DossierNextSuspect"}, // Inventory {fLoseInventory, "LoseInventory"}, {fInventory, "Inventory"}, // PoliceBust {fPoliceBust, "PoliceBust"}, {fBustMovie, "BustMovie"}, // Others {fSafeDigit, "SafeDigit"}, {fCRect, "CRect"}, {nullptr, nullptr}}; void call(const char *name, const ArgArray &args) { Common::String n(name); if (!g_private->_functions.contains(n)) { error("I don't know how to execute %s", name); } void (*func)(ArgArray) = (void (*)(ArgArray))g_private->_functions.getVal(n); func(args); } } // End of namespace Private