/* 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 "lastexpress/fight/fight.h" #include "lastexpress/game/logic.h" #include "lastexpress/game/beetle.h" #include "lastexpress/game/savegame.h" #include "lastexpress/menu/menu.h" #include "lastexpress/lastexpress.h" namespace LastExpress { LogicManager::LogicManager(LastExpressEngine *engine) { _engine = engine; } LogicManager::~LogicManager() { } void LogicManager::send(int sender, int receiver, int actionId, ConsCallParam param) { _engine->getMessageManager()->addMessage(receiver, actionId, sender, param); } void LogicManager::sendAll(int character, int action, ConsCallParam param) { for (int i = 1; i < 40; ++i) { if (i != character) _engine->getMessageManager()->addMessage(i, action, character, param); } } void LogicManager::fedEx(int sender, int receiver, int action, ConsCallParam param) { Message msg; if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) { msg.sender = sender; msg.receiver = receiver; msg.action = action; msg.param = param; _engine->getMessageManager()->forceMessage(&msg); } } void LogicManager::forceJump(int character, void (LogicManager::*functionPointer)(CONS_PARAMS)) { if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) { getCharacter(character).currentCall = 0; getCharacter(character).inventoryItem = 0; if (whoRunningDialog(character)) endDialog(character); endGraphics(character); releaseEverything(character); (this->*functionPointer)(0, 0, 0, 0); } } void LogicManager::autoMessage(int character, int action, ConsCallParam param) { int chosenIdx = 0; for (Message *i = _engine->getMessageManager()->_autoMessages; i->receiver; ++i) { chosenIdx++; if (chosenIdx >= 128) return; } _engine->getMessageManager()->_autoMessages[chosenIdx].receiver = character; _engine->getMessageManager()->_autoMessages[chosenIdx].action = action; _engine->getMessageManager()->_autoMessages[chosenIdx].param = param; } bool LogicManager::doAutoMessage(Message *msg) { for (int i = 0; i < 128 && _engine->getMessageManager()->_autoMessages[i].receiver; ++i) { if (msg->receiver == _engine->getMessageManager()->_autoMessages[i].receiver && msg->action == _engine->getMessageManager()->_autoMessages[i].action) { getCharacterParams(_engine->getMessageManager()->_autoMessages[i].receiver, 8)[_engine->getMessageManager()->_autoMessages[i].param.intParam] = 1; return true; } } return false; } void LogicManager::save(int character, int type, int event) { if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) _engine->getVCR()->writeSavePoint(type, character, event); } void LogicManager::endGame(int type, int value, int sceneIndex, bool showScene) { if (_engine->isDemo() && !_engine->_navigationEngineIsRunning) return; _engine->getSoundManager()->endAmbient(); _engine->getOtisManager()->wipeAllGSysInfo(); _engine->_navigationEngineIsRunning = false; _engine->getMessageManager()->clearMessageQueue(); _engine->_stopUpdatingCharacters = false; if (showScene) { fadeToWhite(); if (!sceneIndex) { while (!_engine->mouseHasRightClicked()) { Slot *i; for (i = _engine->getSoundManager()->_soundCache; i; i = i->getNext()) { if (i->hasTag(kSoundTagLink)) break; } if (!i && !whoRunningDialog(kCharacterClerk)) break; _engine->handleEvents(); _engine->getSoundManager()->soundThread(); } } if (sceneIndex) { if (!_engine->mouseHasRightClicked()) { bumpCathNode(sceneIndex); while (whoRunningDialog(kCharacterTableE)) { if (_engine->mouseHasRightClicked()) break; _engine->handleEvents(); _engine->getSoundManager()->soundThread(); } } } } _engine->getMenu()->doEgg(false, type, value); } void LogicManager::winGame() { if (_engine->isDemo() && _engine->_navigationEngineIsRunning) { _engine->getSoundManager()->endAmbient(); _engine->getOtisManager()->wipeAllGSysInfo(); _engine->getMessageManager()->clearMessageQueue(); _engine->_stopUpdatingCharacters = true; _engine->demoEnding(true); _engine->getMenu()->doEgg(false, 0, 0); } else { _engine->getSoundManager()->endAmbient(); playNIS(kEventFinalSequence); _engine->doCredits(); _engine->getOtisManager()->wipeAllGSysInfo(); _engine->getMessageManager()->clearMessageQueue(); _engine->_stopUpdatingCharacters = true; _engine->getMenu()->doEgg(false, 0, 0); } } void LogicManager::killGracePeriod() { _engine->_gracePeriodTimer = 0; } void LogicManager::fadeToBlack() { _engine->getGraphicsManager()->setMouseDrawable(false); _engine->getGraphicsManager()->clear(_engine->getGraphicsManager()->_frontBuffer, 0, 0, 640, 480); _engine->getGraphicsManager()->_renderBox1.x = 0; _engine->getGraphicsManager()->_renderBox1.y = 0; _engine->getGraphicsManager()->_renderBox1.width = 640; _engine->getGraphicsManager()->_renderBox1.height = 480; _engine->getGraphicsManager()->stepDissolve(&_engine->getGraphicsManager()->_renderBox1); } void LogicManager::fadeToWhite() { PixMap *backgroundBuffer; PixMap white; _engine->getGraphicsManager()->setMouseDrawable(false); white = 0x7FFF; _engine->getGraphicsManager()->modifyPalette(&white, 1); backgroundBuffer = _engine->getGraphicsManager()->_frontBuffer; for (int i = 480; i > 0; i--) { for (int j = 640; j > 0; j--) { *backgroundBuffer++ = white; } } _engine->getGraphicsManager()->_renderBox1.x = 0; _engine->getGraphicsManager()->_renderBox1.y = 0; _engine->getGraphicsManager()->_renderBox1.width = 640; _engine->getGraphicsManager()->_renderBox1.height = 480; _engine->getGraphicsManager()->stepDissolve(&_engine->getGraphicsManager()->_renderBox1); } void LogicManager::restoreIcons() { _engine->getGraphicsManager()->drawItemDim(_globals[kGlobalCathIcon], 0, 0, 1); _engine->getGraphicsManager()->drawItemDim(_engine->_currentGameFileColorId + 39, 608, 448, 1); if (_activeItem) { _engine->getGraphicsManager()->drawItem(_items[_activeItem].mnum, 44, 0); _engine->getGraphicsManager()->burstBox(44, 0, 32, 32); } _engine->getGraphicsManager()->burstBox(0, 0, 32, 32); _engine->getGraphicsManager()->burstBox(608, 448, 32, 32); } void LogicManager::dropItem(int item, int outLocation) { if (item < 32) { if (_items[item].floating != outLocation) { _items[item].floating = outLocation; if (nodeHasItem(item)) { if (!_doubleClickFlag) cleanNIS(); } } } } void LogicManager::takeItem(int item) { int cathDir; if (item < 32) { if (_items[item].floating) { _items[item].floating = 0; if (item == kItem3) { if (checkLoc(kCharacterCath, kCarRestaurant)) { cathDir = checkCathDir(); if (_closeUp) cathDir = _trainData[_nodeReturn].cathDir; if (cathDir == 56) { if (!_closeUp) { bumpCath(kCarRestaurant, 56, 255); return; } _nodeReturn = getBumpNode(kCarRestaurant, 56, 255); return; } } } else if (item == 5) { if (checkLoc(kCharacterCath, kCarRedSleeping) && !_doubleClickFlag) { cathDir = checkCathDir(); if (_closeUp) cathDir = _trainData[_nodeReturn].cathDir; if (cathDir >= 23 && cathDir <= 32) { if (!_closeUp) { bumpCath(kCarRedSleeping, cathDir, 255); return; } _nodeReturn = getBumpNode(kCarRedSleeping, cathDir, 255); return; } } } else if (item == 7 && checkLoc(kCharacterCath, kCarGreenSleeping) && !_doubleClickFlag) { cathDir = checkCathDir(); if (_closeUp) cathDir = _trainData[_nodeReturn].cathDir; if (cathDir == 1 || (cathDir >= 22 && cathDir <= 33)) { if (_closeUp) { _nodeReturn = getBumpNode(kCarGreenSleeping, cathDir, 255); return; } bumpCath(kCarGreenSleeping, cathDir, 255); } } } } } void LogicManager::giveCathItem(int item) { if (item < 32) { _items[item].floating = 0; _items[item].haveIt = 1; if (_items[item].mnum) { if (!_items[item].inPocket) { _activeItem = item; _engine->getGraphicsManager()->drawItem(_items[item].mnum, 44, 0); _engine->getGraphicsManager()->burstBox(44, 0, 32, 32); } } } } void LogicManager::takeCathItem(int item) { if (item < 32) { _items[item].floating = 0; _items[item].haveIt = 0; if (item == _activeItem) { _activeItem = 0; if (_engine->getGraphicsManager()->acquireSurface()) { _engine->getGraphicsManager()->clear(_engine->getGraphicsManager()->_screenSurface, 44, 0, 32, 32); _engine->getGraphicsManager()->unlockSurface(); } _engine->getGraphicsManager()->burstBox(44, 0, 32, 32); } } } bool LogicManager::cathHasItem(int item) { if (item && item < 32) return _items[item].haveIt == 1; else return false; } int LogicManager::findLargeItem() { Item *inventory = _items; for (int i = kItemMatchBox; i < 32; i++) { Item *item = &inventory[i]; if (item->haveIt && inventory[i - 1].haveIt && !item->inPocket) { return i; } } return 0; } int LogicManager::checkCathDir() { return _trainData[_activeNode].cathDir; } bool LogicManager::checkCathDir(int car, int position) { return getCharacter(kCharacterCath).characterPosition.car == car && _trainData[_activeNode].cathDir == position; } bool LogicManager::isFemale(int character) { return character == kCharacterAnna || character == kCharacterTatiana || character == kCharacterVesna || character == kCharacterKahina || character == kCharacterMadame || character == kCharacterRebecca || character == kCharacterSophie || character == kCharacterYasmin || character == kCharacterHadija || character == kCharacterAlouan; } bool LogicManager::isSingleFemale(int character) { return character == kCharacterTatiana || character == kCharacterRebecca || character == kCharacterSophie; } bool LogicManager::isNight() { int chapter = _globals[kGlobalChapter]; return chapter == 1 || chapter == 4 || (chapter == 5 && !_globals[kGlobalIsDayTime]); } bool LogicManager::whoOutside(int character) { return getCharacter(character).characterPosition.location == 2; } bool LogicManager::whoOnScreen(int character) { return getCharacter(character).frame1 && getCharacter(character).frame1->compType != 3; } bool LogicManager::checkLoc(int character, int car) { return getCharacter(character).characterPosition.car == car && getCharacter(character).characterPosition.location < 2; } bool LogicManager::emptyComp(int car, int position) { for (int i = 1; i < 40; i++) { if (inComp(i, car, position)) return false; } return true; } bool LogicManager::inComp(int character, int car, int position) { return getCharacter(character).characterPosition.car == car && getCharacter(character).characterPosition.position == position && getCharacter(character).characterPosition.location == 1; } bool LogicManager::inComp(int character) { uint16 car = getCharacter(character).characterPosition.car; return (car == kCarGreenSleeping || car == kCarRedSleeping) && getCharacter(character).characterPosition.location == 1; } bool LogicManager::cathOutHisWindow() { if (getCharacter(kCharacterCath).characterPosition.location == 2 && getCharacter(kCharacterCath).characterPosition.car == kCarGreenSleeping) { uint16 position = getCharacter(kCharacterCath).characterPosition.position; if (position == 8200 || position == 7500) return true; } return false; } bool LogicManager::cathOutRebeccaWindow() { if (getCharacter(kCharacterCath).characterPosition.location == 2 && getCharacter(kCharacterCath).characterPosition.car == kCarRedSleeping) { uint16 position = getCharacter(kCharacterCath).characterPosition.position; if (position == 4840 || position == 4070) return true; } return false; } bool LogicManager::inSuite(int character, int car, int position) { if (getCharacter(character).characterPosition.car != car || getCharacter(character).characterPosition.location != 1) return false; uint16 pos = getCharacter(character).characterPosition.position; if (position == 4455) { return pos == 4840 || pos == 4070 || pos == 4455; } if (position == 6130) { return pos == 6470 || pos == 5790 || pos == 6130; } if (position == 7850) { return pos == 8200 || pos == 7500 || pos == 7850; } return false; } bool LogicManager::inOffice(int character) { if (checkLoc(character, kCarBaggage)) { uint16 position = getCharacter(character).characterPosition.position; if (position >= 4500 && position <= 5500) return true; } return false; } bool LogicManager::inLowBaggage(int character) { return checkLoc(character, kCarBaggage) && getCharacter(character).characterPosition.position < 4500; } bool LogicManager::inSalon(int character) { if (checkLoc(character, kCarRestaurant)) { uint16 position = getCharacter(character).characterPosition.position; if (position >= 1540 && position <= 3650) return true; } return false; } bool LogicManager::inDiningRoom(int character) { if (checkLoc(character, kCarRestaurant)) { uint16 position = getCharacter(character).characterPosition.position; if (position >= 3650 && position <= 5800) return true; } return false; } bool LogicManager::inKitchen(int character) { return checkLoc(character, kCarRestaurant) && getCharacter(character).characterPosition.position > 5800; } bool LogicManager::inOuterSanctum(int character) { if (checkLoc(character, kCarKronos)) { uint16 position = getCharacter(character).characterPosition.position; if (position >= 5500 && position <= 7500) return true; } return false; } bool LogicManager::inInnerSanctum(int character) { if (checkLoc(character, kCarKronos)) { uint16 position = getCharacter(character).characterPosition.position; if (position >= 3500 && position <= 5500) return true; } return false; } bool LogicManager::onLowPlatform(int character) { return checkLoc(character, kCarGreenSleeping) && getCharacter(character).characterPosition.position < 850; } bool LogicManager::onKronPlatform(int character) { return checkLoc(character, kCarKronos) && getCharacter(character).characterPosition.position < 7900; } bool LogicManager::cathInCorridor(int car) { return checkLoc(kCharacterCath, car) && !getCharacter(kCharacterCath).characterPosition.location && !onLowPlatform(kCharacterCath); } bool LogicManager::cathFacingUp() { return _engine->getOtisManager()->fDirection(0); } bool LogicManager::rcClear() { for (int i = 1; i < 31; i++) { if (!getCharacter(i).characterPosition.location && (inSalon(i) || inDiningRoom(i))) { return false; } } return true; } int32 LogicManager::absPosition(int character1, int character2) { return ABS((int)(getCharacter(character1).characterPosition.position - getCharacter(character2).characterPosition.position)); } bool LogicManager::nearChar(int character1, int character2, int maxDist) { return getCharacter(character2).characterPosition.car == getCharacter(character1).characterPosition.car && absPosition(character1, character2) <= maxDist && (getCharacter(character1).characterPosition.location != 2 || getCharacter(character2).characterPosition.location != 2); } bool LogicManager::nearX(int character, int x, int maxDist) { return maxDist >= ABS((int)getCharacter(character).characterPosition.position - x); } bool LogicManager::whoFacingCath(int character) { if (getCharacter(character).direction == 1 && _engine->getOtisManager()->rDirection(0)) return true; return getCharacter(character).direction == 2 && _engine->getOtisManager()->fDirection(0); } bool LogicManager::whoWalking(int character) { int8 direction = getCharacter(character).direction; return direction == 1 || direction == 2; } int LogicManager::checkDoor(int door) { if (door < 128) { return _doors[door].status; } else { return 0; } } bool LogicManager::preventEnterComp(int door) { int car; int position; if (door && door <= 8) { car = kCarGreenSleeping; position = _doorPositions[door - 1]; if (inComp(kCharacterCath, car, position)) return false; } else if (door < 32 || door > 39) { if (door < 17 || door > 22) { if (door < 48 || door > 53) return false; car = kCarRedSleeping; position = _doorPositions[door - 48]; } else { car = kCarGreenSleeping; position = _doorPositions[door - 17]; } } else { car = kCarRedSleeping; position = _doorPositions[door - 32]; if (inComp(kCharacterCath, car, position)) return false; } for (int i = 1; i < 40; i++) { if (inComp(i, car, position) && i != kCharacterVassili) return true; } return false; } void LogicManager::setDoor(int door, int character, int status, int windowCursor, int handleCursor) { if (door < 128) { int oldStatus = _doors[door].status; _doors[door].who = character; _doors[door].status = status; if (windowCursor != 255 || handleCursor != 255) { if (windowCursor != 255) _doors[door].windowCursor = windowCursor; if (handleCursor != 255) _doors[door].handleCursor = handleCursor; mouseStatus(); } _engine->getSoundManager()->_scanAnySoundLoopingSection = true; if (oldStatus != status && (oldStatus == 2 || status == 2) && ((door && door <= 8) || (door >= 32 && door <= 39))) _engine->getOtisManager()->drawLooseSprites(); } } void LogicManager::setModel(int door, int8 model) { if (door < 128) _doors[door].model = model; } int LogicManager::getModel(int door) { if (door < 128) { return _doors[door].model; } else { return 0; } } void LogicManager::blockView(int character, int car, int position) { if (character == kCharacterMitchell) character = kCharacterCath; if (character <= 31) { _blockedViews[100 * car + position] |= 1 << character; if (checkCathDir(car, position) || (car == kCarRestaurant && position == 57 && checkCathDir(kCarRestaurant, 50))) { playChrExcuseMe(character, kCharacterCath, 0); smartBumpCath(); playDialog(kCharacterCath, "CAT1127A", -1, 0); } else { mouseStatus(); } } } void LogicManager::releaseView(int character, int car, int position) { if (character == kCharacterMitchell) character = kCharacterCath; if (character <= 31) { _blockedViews[100 * car + position] &= ~(1 << character); mouseStatus(); } } void LogicManager::releaseEverything(int character) { if (character == kCharacterMitchell) character = kCharacterCath; if (character <= 31) { for (int i = 0; i < 1000; i++) { _blockedViews[i] &= ~(1 << character); } for (int i = 0; i < 16; _softBlockedX[i - 1] &= ~(1 << character)) { _blockedX[i++] &= ~(1 << character); } mouseStatus(); } } void LogicManager::release2Views(int character, int car, int pos1, int pos2) { if (character == kCharacterMitchell) character = kCharacterCath; if (character <= 31) { _blockedViews[100 * car + pos1] &= ~(1 << character); _blockedViews[100 * car + pos2] &= ~(1 << character); mouseStatus(); } } void LogicManager::block2ViewsBump4(int character, int car, int pos1, int pos2, int pos3, int pos4) { if (character == kCharacterMitchell) character = kCharacterCath; if (character <= 31) { _blockedViews[100 * car + pos1] |= 1 << character; _blockedViews[100 * car + pos2] |= 1 << character; if ((checkCathDir(car, pos1) || checkCathDir(car, pos2) || checkCathDir(car, pos3) || checkCathDir(car, pos4))) { playChrExcuseMe(character, kCharacterCath, 0); smartBumpCath(); playDialog(kCharacterCath, "CAT1127A", -1, 0); } else { mouseStatus(); } } } void LogicManager::blockAtDoor(int character, int status) { if (character <= 31 && (status - 1) <= 38) { switch (status) { case 1: block2ViewsBump4(character, kCarGreenSleeping, 41, 51, 17, 38); _blockedX[0] |= 1 << character; break; case 2: block2ViewsBump4(character, kCarGreenSleeping, 42, 52, 15, 36); _blockedX[1] |= 1 << character; break; case 3: block2ViewsBump4(character, kCarGreenSleeping, 43, 53, 13, 34); _blockedX[2] |= 1 << character; break; case 4: block2ViewsBump4(character, kCarGreenSleeping, 44, 54, 11, 32); _blockedX[3] |= 1 << character; break; case 5: block2ViewsBump4(character, kCarGreenSleeping, 45, 55, 9, 30); _blockedX[4] |= 1 << character; break; case 6: block2ViewsBump4(character, kCarGreenSleeping, 46, 56, 7, 28); _blockedX[5] |= 1 << character; break; case 7: block2ViewsBump4(character, kCarGreenSleeping, 47, 57, 5, 26); _blockedX[6] |= 1 << character; break; case 8: block2ViewsBump4(character, kCarGreenSleeping, 48, 58, 3, 25); _blockedX[7] |= 1 << character; break; case 32: block2ViewsBump4(character, kCarRedSleeping, 41, 51, 17, 38); _blockedX[8] |= 1 << character; break; case 33: block2ViewsBump4(character, kCarRedSleeping, 42, 52, 15, 36); _blockedX[9] |= 1 << character; break; case 34: block2ViewsBump4(character, kCarRedSleeping, 43, 53, 13, 34); _blockedX[10] |= 1 << character; break; case 35: block2ViewsBump4(character, kCarRedSleeping, 44, 54, 11, 32); _blockedX[11] |= 1 << character; break; case 36: block2ViewsBump4(character, kCarRedSleeping, 45, 55, 9, 30); _blockedX[12] |= 1 << character; break; case 37: block2ViewsBump4(character, kCarRedSleeping, 46, 56, 7, 28); _blockedX[13] |= 1 << character; break; case 38: block2ViewsBump4(character, kCarRedSleeping, 47, 57, 5, 26); _blockedX[14] |= 1 << character; break; case 39: block2ViewsBump4(character, kCarRedSleeping, 48, 58, 3, 25); _blockedX[15] |= 1 << character; break; default: return; } } } void LogicManager::releaseAtDoor(int character, int status) { if (character <= 31 && (status - 1) <= 38) { switch (status) { case 1: release2Views(character, kCarGreenSleeping, 41, 51); _blockedX[0] &= ~(1 << character); break; case 2: release2Views(character, kCarGreenSleeping, 42, 52); _blockedX[1] &= ~(1 << character); break; case 3: release2Views(character, kCarGreenSleeping, 43, 53); _blockedX[2] &= ~(1 << character); break; case 4: release2Views(character, kCarGreenSleeping, 44, 54); _blockedX[3] &= ~(1 << character); break; case 5: release2Views(character, kCarGreenSleeping, 45, 55); _blockedX[4] &= ~(1 << character); break; case 6: release2Views(character, kCarGreenSleeping, 46, 56); _blockedX[5] &= ~(1 << character); break; case 7: release2Views(character, kCarGreenSleeping, 47, 57); _blockedX[6] &= ~(1 << character); break; case 8: release2Views(character, kCarGreenSleeping, 48, 58); _blockedX[7] &= ~(1 << character); break; case 32: release2Views(character, kCarRedSleeping, 41, 51); _blockedX[8] &= ~(1 << character); break; case 33: release2Views(character, kCarRedSleeping, 42, 52); _blockedX[9] &= ~(1 << character); break; case 34: release2Views(character, kCarRedSleeping, 43, 53); _blockedX[10] &= ~(1 << character); break; case 35: release2Views(character, kCarRedSleeping, 44, 54); _blockedX[11] &= ~(1 << character); break; case 36: release2Views(character, kCarRedSleeping, 45, 55); _blockedX[12] &= ~(1 << character); break; case 37: release2Views(character, kCarRedSleeping, 46, 56); _blockedX[13] &= ~(1 << character); break; case 38: release2Views(character, kCarRedSleeping, 47, 57); _blockedX[14] &= ~(1 << character); break; case 39: release2Views(character, kCarRedSleeping, 48, 58); _blockedX[15] &= ~(1 << character); break; default: return; } } } void LogicManager::softBlockAtDoor(int character, int status) { if (character <= 31 && (status - 1) <= 38) { switch (status) { case 1: block2ViewsBump4(character, kCarGreenSleeping, 41, 51, 17, 38); _softBlockedX[0] |= 1 << character; break; case 2: block2ViewsBump4(character, kCarGreenSleeping, 42, 52, 15, 36); _softBlockedX[1] |= 1 << character; break; case 3: block2ViewsBump4(character, kCarGreenSleeping, 43, 53, 13, 34); _softBlockedX[2] |= 1 << character; break; case 4: block2ViewsBump4(character, kCarGreenSleeping, 44, 54, 11, 32); _softBlockedX[3] |= 1 << character; break; case 5: block2ViewsBump4(character, kCarGreenSleeping, 45, 55, 9, 30); _softBlockedX[4] |= 1 << character; break; case 6: block2ViewsBump4(character, kCarGreenSleeping, 46, 56, 7, 28); _softBlockedX[5] |= 1 << character; break; case 7: block2ViewsBump4(character, kCarGreenSleeping, 47, 57, 5, 26); _softBlockedX[6] |= 1 << character; break; case 8: block2ViewsBump4(character, kCarGreenSleeping, 48, 58, 3, 25); _softBlockedX[7] |= 1 << character; break; case 32: block2ViewsBump4(character, kCarRedSleeping, 41, 51, 17, 38); _softBlockedX[8] |= 1 << character; break; case 33: block2ViewsBump4(character, kCarRedSleeping, 42, 52, 15, 36); _softBlockedX[9] |= 1 << character; break; case 34: block2ViewsBump4(character, kCarRedSleeping, 43, 53, 13, 34); _softBlockedX[10] |= 1 << character; break; case 35: block2ViewsBump4(character, kCarRedSleeping, 44, 54, 11, 32); _softBlockedX[11] |= 1 << character; break; case 36: block2ViewsBump4(character, kCarRedSleeping, 45, 55, 9, 30); _softBlockedX[12] |= 1 << character; break; case 37: block2ViewsBump4(character, kCarRedSleeping, 46, 56, 7, 28); _softBlockedX[13] |= 1 << character; break; case 38: block2ViewsBump4(character, kCarRedSleeping, 47, 57, 5, 26); _softBlockedX[14] |= 1 << character; break; case 39: block2ViewsBump4(character, kCarRedSleeping, 48, 58, 3, 25); _softBlockedX[15] |= 1 << character; break; default: return; } } } void LogicManager::softReleaseAtDoor(int character, int status) { if (character <= 31 && (status - 1) <= 38) { switch (status) { case 1: release2Views(character, kCarGreenSleeping, 41, 51); _softBlockedX[0] &= ~(1 << character); break; case 2: release2Views(character, kCarGreenSleeping, 42, 52); _softBlockedX[1] &= ~(1 << character); break; case 3: release2Views(character, kCarGreenSleeping, 43, 53); _softBlockedX[2] &= ~(1 << character); break; case 4: release2Views(character, kCarGreenSleeping, 44, 54); _softBlockedX[3] &= ~(1 << character); break; case 5: release2Views(character, kCarGreenSleeping, 45, 55); _softBlockedX[4] &= ~(1 << character); break; case 6: release2Views(character, kCarGreenSleeping, 46, 56); _softBlockedX[5] &= ~(1 << character); break; case 7: release2Views(character, kCarGreenSleeping, 47, 57); _softBlockedX[6] &= ~(1 << character); break; case 8: release2Views(character, kCarGreenSleeping, 48, 58); _softBlockedX[7] &= ~(1 << character); break; case 32: release2Views(character, kCarRedSleeping, 41, 51); _softBlockedX[8] &= ~(1 << character); break; case 33: release2Views(character, kCarRedSleeping, 42, 52); _softBlockedX[9] &= ~(1 << character); break; case 34: release2Views(character, kCarRedSleeping, 43, 53); _softBlockedX[10] &= ~(1 << character); break; case 35: release2Views(character, kCarRedSleeping, 44, 54); _softBlockedX[11] &= ~(1 << character); break; case 36: release2Views(character, kCarRedSleeping, 45, 55); _softBlockedX[12] &= ~(1 << character); break; case 37: release2Views(character, kCarRedSleeping, 46, 56); _softBlockedX[13] &= ~(1 << character); break; case 38: release2Views(character, kCarRedSleeping, 47, 57); _softBlockedX[14] &= ~(1 << character); break; case 39: release2Views(character, kCarRedSleeping, 48, 58); _softBlockedX[15] &= ~(1 << character); break; default: return; } } } int LogicManager::getBumpNode(int car, int position, int param) { int i; if (_numberOfScenes <= 1) return _activeNode; for (i = 1; i < _numberOfScenes; i++) { Node *curNode = &_trainData[i]; if (curNode->nodePosition.car != car || curNode->cathDir != position) continue; if ((param == 0xFF && curNode->parameter3 == 0) || curNode->parameter3 == param || curNode->property == kNodeHas3Items) break; } if (i >= _numberOfScenes) return _activeNode; if (_blockedViews[100 * _trainData[i].nodePosition.car + _trainData[i].cathDir]) { return getSmartBumpNode(i); } else { return i; } } void LogicManager::bumpCath(int car, int position, int param) { if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) { bumpCathNode(getBumpNode(car, position, param)); } } bool LogicManager::obstacleBetween(int character1, int character2) { int offset; uint32 char1Pos; uint32 char2Pos; int i, j; if (getCharacter(character1).characterPosition.car != getCharacter(character2).characterPosition.car || getCharacter(character1).characterPosition.car < kCarGreenSleeping || getCharacter(character1).characterPosition.car > kCarRedSleeping) { return false; } if (getCharacter(character1).characterPosition.car == 3) { offset = 0; } else { offset = 8; } if (getCharacter(character1).characterPosition.position >= getCharacter(character2).characterPosition.position) { char1Pos = getCharacter(character2).characterPosition.position; char2Pos = getCharacter(character1).characterPosition.position; } else { char1Pos = getCharacter(character1).characterPosition.position; char2Pos = getCharacter(character2).characterPosition.position; } for (i = 7; i > -1 && _doorPositions[i] < (int)char1Pos; i--); for (j = 0; j < 8 && _doorPositions[j] > (int)char2Pos; j++); if (i > -1 && j < 8) { while (j <= i) { if (_blockedX[j + offset] || _softBlockedX[j + offset]) return true; j++; } } // The original code went for "k <= 40" here, but that would trigger a bad memory access... for (int k = 1; k < 40; ++k) { if (k != character1 && k != character2 && whoWalking(k) && getCharacter(k).characterPosition.car == getCharacter(character1).characterPosition.car && getCharacter(k).characterPosition.position > char1Pos && getCharacter(k).characterPosition.position < char2Pos) { return true; } } return false; } bool LogicManager::bumpCathTowardsCond(int door, bool playSound, bool loadScene) { if (getCharacter(kCharacterCath).characterPosition.location || ((door < 2 || door > 8) && (door < 32 || door > 39))) { return false; } if (getCharacter(kCharacterCath).characterPosition.car == getCharacter(kCharacterPolice).characterPosition.car && !getCharacter(kCharacterPolice).characterPosition.location && !obstacleBetween(kCharacterCath, kCharacterPolice)) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (loadScene) bumpCathRDoor(door); return true; } if (getCharacter(kCharacterCath).characterPosition.car == kCarGreenSleeping && getCharacter(kCharacterCond1).characterPosition.car == kCarGreenSleeping && !getCharacter(kCharacterCond1).characterPosition.location && !getCharacterParams(kCharacterCond1, 8)[0]) { if (!obstacleBetween(kCharacterCath, kCharacterCond1)) { if (getCharacter(kCharacterCond1).characterPosition.position < 2740 && getCharacter(kCharacterCond1).characterPosition.position > 850 && (getCharacter(kCharacterCond2).characterPosition.car != kCarGreenSleeping || getCharacter(kCharacterCond2).characterPosition.position > 2740) && (getCharacter(kCharacterTrainM).characterPosition.car != kCarGreenSleeping || getCharacter(kCharacterTrainM).characterPosition.position > 2740)) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond1)) playCondYelling(kCharacterCond1, door); send(kCharacterCath, kCharacterCond1, 305159806, 0); if (loadScene) bumpCathRDoor(door); return true; } } if (!obstacleBetween(kCharacterCath, kCharacterCond1) && getCharacter(kCharacterCond1).direction == 1 && getCharacter(kCharacterCond1).characterPosition.position < getCharacter(kCharacterCath).characterPosition.position) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond1)) { if (rnd(2) == 0) { playDialog(kCharacterCond1, "CON1000A", -1, 0); } else { playDialog(kCharacterCond1, "CON1000", -1, 0); } } if (loadScene) bumpCathRDoor(door); return true; } if (!obstacleBetween(kCharacterCath, kCharacterCond1) && getCharacter(kCharacterCond1).direction == 2 && getCharacter(kCharacterCond1).characterPosition.position > getCharacter(kCharacterCath).characterPosition.position) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond1)) { if (rnd(2) == 0) { playDialog(kCharacterCond1, "CON1000A", -1, 0); } else { playDialog(kCharacterCond1, "CON1000", -1, 0); } } if (loadScene) bumpCathFDoor(door); return true; } } if (getCharacter(kCharacterCath).characterPosition.car != kCarRedSleeping || getCharacter(kCharacterCond2).characterPosition.car == kCarNone || getCharacter(kCharacterCond2).characterPosition.location || getCharacterParams(kCharacterCond2, 8)[0]) { return false; } if (!obstacleBetween(kCharacterCath, kCharacterCond2)) { if (getCharacter(kCharacterCond2).characterPosition.position < 2740 && getCharacter(kCharacterCond2).characterPosition.position > 850 && (getCharacter(kCharacterCond1).characterPosition.car != kCarRedSleeping || getCharacter(kCharacterCond1).characterPosition.position > 2740) && (getCharacter(kCharacterTrainM).characterPosition.car != kCarRedSleeping || getCharacter(kCharacterTrainM).characterPosition.position > 2740) && (getCharacter(kCharacterMadame).characterPosition.car != kCarRedSleeping || getCharacter(kCharacterMadame).characterPosition.position > 2740)) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond2)) playCondYelling(kCharacterCond2, door); send(kCharacterCath, kCharacterCond2, 305159806, 0); if (loadScene) bumpCathRDoor(door); return true; } } if (!obstacleBetween(kCharacterCath, kCharacterCond2) && getCharacter(kCharacterCond2).direction == 1 && getCharacter(kCharacterCath).characterPosition.position > getCharacter(kCharacterCond2).characterPosition.position) { if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond2)) { if (rnd(2) == 0) { playDialog(kCharacterCond2, "JAC1000A", -1, 0); } else { playDialog(kCharacterCond2, "JAC1000", -1, 0); } } if (loadScene) bumpCathRDoor(door); return true; } if (obstacleBetween(kCharacterCath, kCharacterCond2) || getCharacter(kCharacterCond2).direction != 2 || getCharacter(kCharacterCath).characterPosition.position >= getCharacter(kCharacterCond2).characterPosition.position) { return false; } if (playSound) { if (_doors[door].status == 1 || _doors[door].status == 3 || preventEnterComp(door)) { queueSFX(kCharacterCath, 13, 0); } else { queueSFX(kCharacterCath, 14, 0); queueSFX(kCharacterCath, 15, 3); } } if (!whoRunningDialog(kCharacterCond2)) { if (rnd(2) == 0) { playDialog(kCharacterCond2, "JAC1000A", -1, 0); } else { playDialog(kCharacterCond2, "JAC1000", -1, 0); } } if (loadScene) bumpCathFDoor(door); return true; } void LogicManager::bumpCathFx(int car, int position) { int i; for (i = 1; i <= 18; i++) { if (_engine->_fudgePosition[i] > position) break; } if (i <= 18) { if (i == 1) { bumpCath(car, 1, 255); } else if (i == 5) { bumpCath(car, 3, 255); } else { bumpCath(car, i - 1, 255); } } else { bumpCath(car, 18, 255); } } void LogicManager::bumpCathRx(int car, int position) { int i; for (i = 40; i >= 22; i--) { if (_engine->_fudgePosition[i] < position && i != 24) break; } if (i >= 22) { if (i == 40) { bumpCath(car, 40, 255); } else if (i == 23) { bumpCath(car, 25, 255); } else { bumpCath(car, i + 1, 255); } } else { bumpCath(car, 22, 255); } } void LogicManager::bumpCathFDoor(int door) { if ((door - 1) <= 38) { switch (door) { case 1: bumpCath(kCarGreenSleeping, 17, 255); break; case 2: bumpCath(kCarGreenSleeping, 15, 255); break; case 3: bumpCath(kCarGreenSleeping, 13, 255); break; case 4: bumpCath(kCarGreenSleeping, 11, 255); break; case 5: bumpCath(kCarGreenSleeping, 9, 255); break; case 6: bumpCath(kCarGreenSleeping, 7, 255); break; case 7: bumpCath(kCarGreenSleeping, 5, 255); break; case 8: bumpCath(kCarGreenSleeping, 3, 255); break; case 32: bumpCath(kCarRedSleeping, 17, 255); break; case 33: bumpCath(kCarRedSleeping, 15, 255); break; case 34: bumpCath(kCarRedSleeping, 13, 255); break; case 35: bumpCath(kCarRedSleeping, 11, 255); break; case 36: bumpCath(kCarRedSleeping, 9, 255); break; case 37: bumpCath(kCarRedSleeping, 7, 255); break; case 38: bumpCath(kCarRedSleeping, 5, 255); break; case 39: bumpCath(kCarRedSleeping, 3, 255); break; default: return; } } } void LogicManager::bumpCathRDoor(int door) { if ((door - 1) <= 38) { switch (door) { case 1: bumpCath(kCarGreenSleeping, 38, 255); break; case 2: bumpCath(kCarGreenSleeping, 36, 255); break; case 3: bumpCath(kCarGreenSleeping, 34, 255); break; case 4: bumpCath(kCarGreenSleeping, 32, 255); break; case 5: bumpCath(kCarGreenSleeping, 30, 255); break; case 6: bumpCath(kCarGreenSleeping, 28, 255); break; case 7: bumpCath(kCarGreenSleeping, 26, 255); break; case 8: bumpCath(kCarGreenSleeping, 25, 255); break; case 32: bumpCath(kCarRedSleeping, 38, 255); break; case 33: bumpCath(kCarRedSleeping, 36, 255); break; case 34: bumpCath(kCarRedSleeping, 34, 255); break; case 35: bumpCath(kCarRedSleeping, 32, 255); break; case 36: bumpCath(kCarRedSleeping, 30, 255); break; case 37: bumpCath(kCarRedSleeping, 28, 255); break; case 38: bumpCath(kCarRedSleeping, 26, 255); break; case 39: bumpCath(kCarRedSleeping, 25, 255); break; default: return; } } } void LogicManager::bumpCathTylerComp() { bumpCathNode(41); } int LogicManager::getSmartBumpNode(int node) { uint8 effDir; uint16 car = _trainData[node].nodePosition.car; if (car <= kCarKronos) return node; if (car <= kCarRedSleeping) { if (_engine->getOtisManager()->corrRender(0)) { if (!_engine->getOtisManager()->fDirection(0)) { effDir = _trainData[node].cathDir + 1; } else { effDir = _trainData[node].cathDir - 1; } if (effDir == 4) effDir = 3; if (effDir == 24) effDir = 25; if (_blockedViews[100 * car + effDir]) { return node; } else { return getBumpNode(car, effDir, 255); } } else { switch (_trainData[node].cathDir) { case 41: case 51: if (!_blockedViews[100 * car + 39]) return getBumpNode(car, 39, 255); // Fallthrough case 42: case 52: if (!_blockedViews[100 * car + 14]) return getBumpNode(car, 14, 255); // Fallthrough case 43: case 53: if (!_blockedViews[100 * car + 35]) return getBumpNode(car, 35, 255); // Fallthrough case 44: case 54: if (!_blockedViews[100 * car + 10]) return getBumpNode(car, 10, 255); // Fallthrough case 45: case 55: if (!_blockedViews[100 * car + 32]) return getBumpNode(car, 32, 255); // Fallthrough case 46: case 56: if (!_blockedViews[100 * car + 7]) return getBumpNode(car, 7, 255); // Fallthrough case 47: case 57: if (!_blockedViews[100 * car + 27]) return getBumpNode(car, 27, 255); // Fallthrough case 48: case 58: if (!_blockedViews[100 * car + 2]) return getBumpNode(car, 2, 255); // Fallthrough default: return node; } } } else { if (car != kCarRestaurant) return node; switch (_trainData[node].cathDir) { case 50: case 56: case 57: case 58: if (!_blockedViews[555]) return getBumpNode(kCarRestaurant, 55, 255); if (!_blockedViews[560]) return getBumpNode(kCarRestaurant, 60, 255); if (!_blockedViews[559]) return getBumpNode(kCarRestaurant, 59, 255); if (!_blockedViews[561]) return getBumpNode(kCarRestaurant, 61, 255); if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 52: case 53: case 54: if (!_blockedViews[551]) return getBumpNode(kCarRestaurant, 51, 255); if (!_blockedViews[555]) return getBumpNode(kCarRestaurant, 55, 255); if (!_blockedViews[560]) return getBumpNode(kCarRestaurant, 60, 255); if (!_blockedViews[559]) return getBumpNode(kCarRestaurant, 59, 255); if (!_blockedViews[561]) return getBumpNode(kCarRestaurant, 61, 255); if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 59: if (!_blockedViews[560]) return getBumpNode(kCarRestaurant, 60, 255); if (!_blockedViews[559]) return getBumpNode(kCarRestaurant, 59, 255); if (!_blockedViews[561]) return getBumpNode(kCarRestaurant, 61, 255); if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 60: if (!_blockedViews[559]) return getBumpNode(kCarRestaurant, 59, 255); if (!_blockedViews[561]) return getBumpNode(kCarRestaurant, 61, 255); if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 62: case 63: case 64: if (!_blockedViews[561]) return getBumpNode(kCarRestaurant, 61, 255); if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 66: case 67: case 68: if (!_blockedViews[565]) return getBumpNode(kCarRestaurant, 65, 255); if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; case 69: case 71: if (!_blockedViews[570]) return getBumpNode(kCarRestaurant, 70, 255); return node; default: return node; } } return node; } void LogicManager::smartBumpCath() { if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) { bumpCathNode(getSmartBumpNode(_activeNode)); } } void LogicManager::bumpCathCloseUp(int item) { if (item < 32) { if (_items[item].closeUp) { if (!_closeUp) { _closeUp = 1; _nodeReturn = _activeNode; } bumpCathNode(_items[item].closeUp); } } } int LogicManager::playFight(int fightId) { if (!_engine->isDemo() || _engine->_navigationEngineIsRunning) { return _engine->doFight(fightId); } else { return 1; } } void LogicManager::playNIS(int nisId) { if (_engine->isDemo() && !_engine->_navigationEngineIsRunning) return; char filename[16]; bool videoWithoutFade = false; if (nisId >= (int)kEventCorpseDropFloorOriginal || nisId == kEventCathWakingUp || nisId == kEventConcertCough || nisId == kEventConcertSit || nisId == kEventConcertLeaveWithBriefcase) { videoWithoutFade = true; } Common::sprintf_s(filename, "%s.NIS", _nisNames[nisId]); _engine->getSoundManager()->_scanAnySoundLoopingSection = true; restoreIcons(); displayWaitIcon(); if (!_engine->mouseHasRightClicked()) { if (_engine->_gracePeriodTimer && dialogRunning("TIMER")) { fadeDialog("TIMER"); _engine->_gracePeriodTimer = 105; } _engine->getGraphicsManager()->_renderBox1.x = 80; _engine->getGraphicsManager()->_renderBox1.width = 480; if (_engine->isGoldEdition()) { warning("NIS support for Gold Edition is currently missing. Skipping cutscene"); } else { if (videoWithoutFade) { _engine->getNISManager()->doNIS(filename, kNisFlagBaseFlag); } else { _engine->getNISManager()->doNIS(filename, kNisFlagBaseFlag | kNisFlagSoundFade); } } if (dialogRunning("TIMER")) endDialog("TIMER"); } _engine->getGraphicsManager()->_renderBox1.width = 480; _engine->getGraphicsManager()->_renderBox1.height = 480; _engine->getGraphicsManager()->_renderBox1.x = 80; _engine->getGraphicsManager()->_renderBox1.y = 0; _doneNIS[nisId] = 1; _realTime += _nisTicks[nisId]; _gameTime += _nisTicks[nisId] * _timeSpeed; } void LogicManager::cleanNIS() { if (_engine->isDemo() && !_engine->_navigationEngineIsRunning) return; if (_closeUp) { int largeItem = findLargeItem(); _closeUp = 0; if (largeItem && _activeItem != largeItem) { _activeItem = largeItem; _engine->getGraphicsManager()->drawItem(_items[largeItem].mnum, 44, 0); _engine->getGraphicsManager()->burstBox(44, 0, 32, 32); } if (_blockedViews[100 * _trainData[_nodeReturn].nodePosition.car + _trainData[_nodeReturn].cathDir]) { bumpCathNode(getSmartBumpNode(_nodeReturn)); } else { bumpCathNode(_nodeReturn); } } else { bumpCathNode(_activeNode); } } int LogicManager::getVolume(int character) { int chosenVolume; int relativePos; int positionVolumes[32]; if (!character) return 16; positionVolumes[0] = 16; positionVolumes[1] = 15; positionVolumes[2] = 14; positionVolumes[3] = 13; positionVolumes[4] = 12; positionVolumes[5] = 11; positionVolumes[6] = 11; positionVolumes[7] = 10; positionVolumes[8] = 10; positionVolumes[9] = 9; positionVolumes[10] = 9; positionVolumes[11] = 8; positionVolumes[12] = 8; positionVolumes[13] = 7; positionVolumes[14] = 7; positionVolumes[15] = 7; positionVolumes[16] = 6; positionVolumes[17] = 6; positionVolumes[18] = 6; positionVolumes[19] = 5; positionVolumes[20] = 5; positionVolumes[21] = 5; positionVolumes[22] = 5; positionVolumes[23] = 4; positionVolumes[24] = 4; positionVolumes[25] = 4; positionVolumes[26] = 4; positionVolumes[27] = 3; positionVolumes[28] = 3; positionVolumes[29] = 3; positionVolumes[30] = 3; positionVolumes[31] = 3; if (getCharacter(character).characterPosition.car != getCharacter(kCharacterCath).characterPosition.car) return 0; chosenVolume = 2; relativePos = ABS((int16)getCharacter(character).characterPosition.position - (int16)getCharacter(kCharacterCath).characterPosition.position) / 230; if (relativePos < 32) chosenVolume = positionVolumes[relativePos]; if (getCharacter(kCharacterCath).characterPosition.location != 2) { switch (getCharacter(kCharacterCath).characterPosition.car) { case kCarKronos: if (inOuterSanctum(character) != inOuterSanctum(kCharacterCath)) chosenVolume >>= 1; break; case kCarGreenSleeping: case kCarRedSleeping: if (onLowPlatform(kCharacterCath) && !onLowPlatform(character)) chosenVolume >>= 1; if (getCharacter(kCharacterCath).characterPosition.location == 1 && (getCharacter(character).characterPosition.location != 1 || !nearChar(kCharacterCath, character, 400))) { chosenVolume >>= 1; } break; case kCarRestaurant: if (inSalon(character) == inSalon(kCharacterCath)) { if (inDiningRoom(character) != inDiningRoom(kCharacterCath)) chosenVolume >>= 1; } else { chosenVolume >>= 2; } break; default: return chosenVolume; } return chosenVolume; } if (getCharacter(character).characterPosition.car != kCarKronos && !cathOutHisWindow() && !cathOutRebeccaWindow()) return 0; chosenVolume /= 6; return chosenVolume; } void LogicManager::queueSFX(int character, uint8 action, uint8 delay) { uint8 sndNum; int volume; int ambientLevel; byte sndArray[5]; char filename[16]; memset(sndArray, 0, sizeof(sndArray)); if (getCharacter(character).characterPosition.car == getCharacter(kCharacterCath).characterPosition.car) { if (inSalon(character) == inSalon(kCharacterCath)) { sndNum = action; volume = getVolume(character); switch (action) { case 36: ambientLevel = 16; if (volume <= 9) ambientLevel = volume + 7; _engine->getSoundManager()->raiseAmbient(ambientLevel, delay); break; case 37: _engine->getSoundManager()->levelAmbient(delay); break; case 150: sndArray[0] = 151; sndArray[1] = 152; sndArray[2] = 153; sndArray[3] = 154; sndArray[4] = 155; sndNum = sndArray[rnd(5)]; break; case 156: sndArray[0] = 157; sndArray[1] = 158; sndArray[2] = 159; sndArray[3] = 160; sndArray[4] = 161; sndNum = sndArray[rnd(5)]; break; case 162: sndArray[0] = 163; sndArray[1] = 164; sndArray[2] = 165; sndArray[3] = 166; sndArray[4] = 167; sndNum = sndArray[rnd(5)]; break; case 168: sndArray[0] = 169; sndArray[1] = 170; sndArray[2] = 171; sndArray[3] = 172; sndArray[4] = 173; sndNum = sndArray[rnd(5)]; break; case 174: sndArray[0] = 175; sndArray[1] = 176; sndArray[2] = 177; sndNum = sndArray[rnd(3)]; break; case 180: sndArray[0] = 181; sndArray[1] = 182; sndArray[2] = 183; sndArray[3] = 184; sndNum = sndArray[rnd(4)]; break; case 184: sndArray[0] = 185; sndArray[1] = 186; sndArray[2] = 187; sndNum = sndArray[rnd(3)]; break; case 188: sndArray[0] = 189; sndArray[1] = 190; sndArray[2] = 191; sndArray[3] = 192; sndArray[4] = 193; sndNum = sndArray[rnd(5)]; break; case 194: sndArray[0] = 195; sndArray[1] = 196; sndArray[2] = 197; sndNum = sndArray[rnd(3)]; break; case 198: sndArray[0] = 198; sndArray[1] = 200; sndArray[2] = 201; sndArray[3] = 202; sndArray[4] = 203; sndNum = sndArray[rnd(5)]; break; case 246: sndArray[0] = 0; memcpy(&sndArray[1], "hint", 4); sndNum = sndArray[rnd(5)]; break; case 247: sndArray[0] = 11; memcpy(&sndArray[1], "{|", 2); sndNum = sndArray[rnd(3)]; break; case 248: sndArray[0] = 0; memcpy(&sndArray[1], "glm", 3); sndNum = sndArray[rnd(4)]; break; case 249: sndArray[0] = 0; memcpy(&sndArray[1], "8pq", 3); sndNum = sndArray[rnd(4)]; break; case 250: sndArray[0] = 0; memcpy(&sndArray[1], "ksu", 3); sndNum = sndArray[rnd(4)]; break; case 251: sndArray[0] = 0; sndArray[1] = 11; memcpy(&sndArray[2], "8q", 2); sndNum = sndArray[rnd(4)]; break; case 252: sndArray[0] = 0; sndArray[1] = 6; memcpy(&sndArray[2], "my", 2); sndNum = sndArray[rnd(4)]; break; case 254: sndArray[0] = 0; memcpy(&sndArray[1], "hxy", 3); sndNum = sndArray[rnd(4)]; break; case 255: sndArray[0] = 0; memcpy(&sndArray[1], "js", 2); sndNum = sndArray[rnd(3)]; break; default: break; } if (sndNum) { Common::sprintf_s(filename, "LIB%03d.SND", sndNum); if (volume) _engine->getSoundManager()->playSoundFile(filename, volume, 0, delay); } } } } void LogicManager::playChrExcuseMe(int character, int receivingCharacter, int volume) { if (whoRunningDialog(character) && character != kCharacterCath && character != kCharacterClerk && (character != kCharacterMaster || receivingCharacter == kCharacterFrancois || receivingCharacter == kCharacterMax || character == kCharacterFrancois) && getCharacter(kCharacterFrancois).walkStepSize != 30) { return; } if ((whoRunningDialog(character) && character != kCharacterCath && character != kCharacterClerk && character != kCharacterMaster) || receivingCharacter == kCharacterFrancois || receivingCharacter == kCharacterMax || (character == kCharacterFrancois && getCharacter(kCharacterFrancois).walkStepSize != 30)) { return; } if (!volume) volume = getVolume(character); switch (character) { case kCharacterAnna: playDialog(0, "ANN1107A", volume, 0); return; case kCharacterAugust: switch (rnd(4)) { case 0: playDialog(0, "AUG1100A", volume, 0); break; case 1: playDialog(0, "AUG1100B", volume, 0); break; case 2: playDialog(0, "AUG1100C", volume, 0); break; case 3: default: playDialog(0, "AUG1100D", volume, 0); } return; case kCharacterCond1: if (isFemale(receivingCharacter)) { switch (rnd(2)) { case 0: playDialog(0, "CON1111A", volume, 0); break; case 1: default: playDialog(0, "CON1111", volume, 0); } return; } if (receivingCharacter == kCharacterCath && _globals[kGlobalJacket] == 2 && rnd(2) != 0) { if (isNight()) { if (_globals[kGlobalPhaseOfTheNight] != 2) { playDialog(0, "CON1110E", volume, 0); } else { playDialog(0, "CON1110F", volume, 0); } } else { playDialog(0, "CON1110D", volume, 0); } } else { switch (rnd(3)) { case 0: playDialog(0, "CON1110", volume, 0); break; case 1: playDialog(0, "CON1110A", volume, 0); break; case 2: default: playDialog(0, "CON1110C", volume, 0); } } return; case kCharacterCond2: if (isFemale(receivingCharacter)) { playDialog(0, "JAC1111D", volume, 0); } else if (!receivingCharacter && _globals[kGlobalJacket] == 2 && rnd(2)) { playDialog(0, "JAC1113B", volume, 0); } else { switch (rnd(4)) { case 0: playDialog(0, "JAC1111", volume, 0); break; case 1: playDialog(0, "JAC1111A", volume, 0); break; case 2: playDialog(0, "JAC1111B", volume, 0); break; case 3: default: playDialog(0, "JAC1111C", volume, 0); } } return; case kCharacterHeadWait: switch (rnd(2)) { case 0: playDialog(0, "HED1002", volume, 0); break; case 1: default: playDialog(0, "HED1002A", volume, 0); } return; case kCharacterWaiter1: switch (rnd(3)) { case 0: playDialog(0, "WAT1002", volume, 0); break; case 1: playDialog(0, "WAT1002A", volume, 0); break; case 2: default: playDialog(0, "WAT1002B", volume, 0); } return; case kCharacterWaiter2: switch (rnd(3)) { case 0: playDialog(0, "WAT1003", volume, 0); break; case 1: playDialog(0, "WAT1003A", volume, 0); break; case 2: default: playDialog(0, "WAT1003B", volume, 0); } return; case kCharacterTrainM: if (isFemale(receivingCharacter)) { switch (rnd(2)) { case 0: playDialog(0, "TRA1113A", volume, 0); break; case 1: default: playDialog(0, "TRA1113B", volume, 0); } } else { playDialog(0, "TRA1112", volume, 0); } return; case kCharacterTatiana: switch (rnd(2)) { case 0: playDialog(0, "TAT1102A", volume, 0); break; case 1: default: playDialog(0, "TAT1102B", volume, 0); } return; case kCharacterAlexei: switch (rnd(2)) { case 0: playDialog(0, "ALX1099C", volume, 0); break; case 1: default: playDialog(0, "ALX1099D", volume, 0); } return; case kCharacterAbbot: if (isFemale(receivingCharacter)) { playDialog(0, "ABB3002C", volume, 0); } else { switch (rnd(3)) { case 0: playDialog(0, "ABB3002", volume, 0); break; case 1: playDialog(0, "ABB3002A", volume, 0); break; case 2: default: playDialog(0, "ABB3002B", volume, 0); } } return; case kCharacterVesna: switch (rnd(3)) { case 0: playDialog(0, "VES1109A", volume, 0); break; case 1: playDialog(0, "VES1109B", volume, 0); break; case 2: default: playDialog(0, "VES1109C", volume, 0); } return; case kCharacterKahina: switch (rnd(2)) { case 0: playDialog(0, "KAH1001A", volume, 0); break; case 1: default: playDialog(0, "KAH1001", volume, 0); } return; case kCharacterFrancois: switch (rnd(4)) { case 0: playDialog(0, "FRA1001", volume, 0); break; case 1: playDialog(0, "FRA1001A", volume, 0); break; case 2: playDialog(0, "FRA1001B", volume, 0); break; case 3: default: playDialog(0, "FRA1001C", volume, 0); } return; case kCharacterMadame: switch (rnd(4)) { case 0: playDialog(0, "MME1103A", volume, 0); break; case 1: playDialog(0, "MME1103B", volume, 0); break; case 2: playDialog(0, "MME1103C", volume, 0); break; case 3: default: playDialog(0, "MME1103d", volume, 0); } return; case kCharacterMonsieur: playDialog(0, "MRB1104", volume, 0); if (volume > 2) _globals[kGlobalMetMonsieur] = 1; return; case kCharacterRebecca: switch (rnd(2)) { case 0: playDialog(0, "Reb1106A", volume, 0); break; case 1: default: playDialog(0, "REB1106", volume, 0); } return; case kCharacterSophie: if (isFemale(receivingCharacter)) { switch (rnd(3)) { case 0: playDialog(0, "SOP1105", volume, 0); break; case 1: playDialog(0, "SOP1105C", volume, 0); break; case 2: default: playDialog(0, "SOP1105D", volume, 0); } return; } else { switch (rnd(3)) { case 0: playDialog(0, "SOP1105", volume, 0); return; case 1: playDialog(0, "SOP1105A", volume, 0); break; case 2: default: playDialog(0, "SOP1105B", volume, 0); break; } } return; case kCharacterMahmud: playDialog(0, "MAH1101", volume, 0); return; case kCharacterYasmin: playDialog(0, "HAR1002", volume, 0); if (volume > 2) _globals[kGlobalMetYasmin] = 1; return; case kCharacterHadija: switch (rnd(2)) { case 0: playDialog(0, "HAR1001A", volume, 0); break; case 1: default: playDialog(0, "HAR1001", volume, 0); } if (volume > 2) _globals[kGlobalMetHadija] = 1; return; case kCharacterAlouan: playDialog(0, "HAR1004", volume, 0); return; default: return; } } void LogicManager::playCathExcuseMe() { switch (rnd(3)) { case 0: playDialog(kCharacterCath, "CAT1126B", -1, 0); break; case 1: playDialog(kCharacterCath, "CAT1126C", -1, 0); break; case 2: default: playDialog(kCharacterCath, "CAT1126D", -1, 0); } } const char *LogicManager::getCathJustChecking() { switch (rnd(4)) { case 0: return "CAT5001"; case 1: return "CAT5001A"; case 2: return "CAT5001B"; case 3: return "CAT5001C"; default: return "CAT5001"; } } const char *LogicManager::getCathSorryDialog() { switch (rnd(5)) { case 0: return "CAT1125"; case 1: return "CAT1125A"; case 2: return "CAT1125B"; case 3: return "CAT1125C"; case 4: return "CAT1125D"; default: return "CAT1125"; } } const char *LogicManager::getCathWCDialog() { switch (rnd(3)) { case 0: return "CAT1520"; case 1: return "CAT1521"; case 2: return "CAT1125"; default: return "CAT1520"; } } void LogicManager::playCondYelling(int character, int situation) { if (character == kCharacterCond1) { switch (situation) { case 2: if (_lastTickCondYellingComp2 + 450 >= _realTime) { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500C", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1500B", 16, 0); break; } break; case 1: default: playDialog(kCharacterCond1, "Con1502A", 16, 0); break; } } else { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1500", 16, 0); break; } break; case 1: default: playDialog(kCharacterCond1, "Con1502", 16, 0); break; } } _lastTickCondYellingComp2 = _realTime; break; case 3: if (_lastTickCondYellingComp3 + 450 >= _realTime) { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500C", 16, 0); break; case 1: playDialog(kCharacterCond1, "Con1500B", 16, 0); break; } break; case 1: playDialog(kCharacterCond1, "Con1501A", 16, 0); break; } } else { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500A", 16, 0); break; case 1: playDialog(kCharacterCond1, "Con1500", 16, 0); break; } break; case 1: playDialog(kCharacterCond1, "Con1501", 16, 0); break; } } _lastTickCondYellingComp3 = _realTime; break; case 4: if (_lastTickCondYellingComp4 + 450 >= _realTime) { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500C", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1500B", 16, 0); break; } break; case 1: default: playDialog(kCharacterCond1, "Con1503", 16, 0); break; } } else { switch (rnd(2)) { case 0: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1500", 16, 0); break; } break; case 1: default: playDialog(kCharacterCond1, "Con1503", 16, 0); break; } } _lastTickCondYellingComp4 = _realTime; break; case 5: case 6: case 7: case 8: _lastTickCondYellingCompHarem++; switch (_lastTickCondYellingCompHarem) { case 1: playDialog(kCharacterCond1, "Con1503C", 16, 0); break; case 2: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1503A", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1503E", 16, 0); break; } break; case 3: switch (rnd(2)) { case 0: playDialog(kCharacterCond1, "Con1503D", 16, 0); break; case 1: default: playDialog(kCharacterCond1, "Con1503B", 16, 0); break; } _lastTickCondYellingCompHarem = 0; break; default: break; } break; default: break; } return; } if (character == kCharacterCond2) { switch (situation) { case 32: if (_lastTickCondYellingCompA + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1508A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1508", 16, 0); break; } } _lastTickCondYellingCompA = _realTime; return; case 33: if (_lastTickCondYellingCompB + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else if (_globals[kGlobalConcertIsHappening] || (_gameTime > 2101500 && _gameTime < 2133000)) { playDialog(kCharacterCond2, "Jac1507A", 16, 0); } else { playDialog(kCharacterCond2, "Jac1507", 16, 0); } _lastTickCondYellingCompB = _realTime; return; case 34: if (_lastTickCondYellingCompC + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { if (_globals[kGlobalChapter] < 3) { playDialog(kCharacterCond2, "Jac1506", 16, 0); _lastTickCondYellingCompC = _realTime; return; } switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1506B", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1506A", 16, 0); break; } } _lastTickCondYellingCompC = _realTime; return; case 35: if (_lastTickCondYellingCompD + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { playDialog(kCharacterCond2, "Jac1505", 16, 0); } _lastTickCondYellingCompD = _realTime; return; case 36: if (_lastTickCondYellingCompE + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); _lastTickCondYellingCompE = _realTime; return; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); _lastTickCondYellingCompE = _realTime; return; } } if (_globals[kGlobalConcertIsHappening] || (_gameTime > 2115000 && _gameTime < 2133000)) { playDialog(kCharacterCond2, "Jac1504B", 16, 0); _lastTickCondYellingCompE = _realTime; return; } if (inComp(kCharacterRebecca, kCarRedSleeping, 4840)) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); _lastTickCondYellingCompE = _realTime; return; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); _lastTickCondYellingCompE = _realTime; return; } } switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1504A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1504", 16, 0); break; } _lastTickCondYellingCompE = _realTime; return; case 37: if (_lastTickCondYellingCompF + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { if (_globals[kGlobalConcertIsHappening] || (_gameTime > 2083500 && _gameTime < 2133000)) { playDialog(kCharacterCond2, "Jac1503B", 16, 0); _lastTickCondYellingCompF = _realTime; return; } if (rnd(2) == 0 || inComp(kCharacterAnna, kCarRedSleeping, 4070)) { playDialog(kCharacterCond2, "Jac1503A", 16, 0); } else { playDialog(kCharacterCond2, "Jac1503", 16, 0); } } _lastTickCondYellingCompF = _realTime; return; case 38: if (_lastTickCondYellingCompG + 450 >= _realTime) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { if (rnd(2) == 1 || !inComp(kCharacterMilos, kCarRedSleeping, 3050)) { playDialog(kCharacterCond2, "Jac1502", 16, 0); } else { playDialog(kCharacterCond2, "Jac1502A", 16, 0); } } _lastTickCondYellingCompG = _realTime; return; case 39: if (_lastTickCondYellingCompH + 450 >= _realTime || inComp(kCharacterIvo, kCarRedSleeping, 2740)) { switch (rnd(2)) { case 0: playDialog(kCharacterCond2, "Jac1500A", 16, 0); break; case 1: default: playDialog(kCharacterCond2, "Jac1500", 16, 0); break; } } else { playDialog(kCharacterCond2, "Jac1501", 16, 0); } _lastTickCondYellingCompH = _realTime; return; default: return; } } } void LogicManager::playDialog(int character, const char *filename, int volume, int delay) { int soundFlags; char tmp[16]; if (whoRunningDialog(character) && character && character < 32) endDialog(character); if (volume == -1) { soundFlags = _engine->getLogicManager()->getVolume(character); } else { soundFlags = volume; } if (strchr(filename, '.')) { Common::strcpy_s(tmp, filename); } else { Common::sprintf_s(tmp, "%s.SND", filename); } if (volume != -1) soundFlags |= kSoundFlagFixedVolume; if (!_engine->getSoundManager()->playSoundFile(tmp, soundFlags, character, delay)) { if (character) _engine->getLogicManager()->send(kCharacterCath, character, 2, 0); } } bool LogicManager::whoRunningDialog(int character) { return _engine->getSoundManager()->findSlotWho(character) != nullptr; } bool LogicManager::dialogRunning(const char *name) { char tmp[32]; if (strchr(name, '.')) { Common::strcpy_s(tmp, name); } else { Common::sprintf_s(tmp, "%s.SND", name); } return _engine->getSoundManager()->findSlotName(tmp) != nullptr; } bool LogicManager::cathRunningDialog(const char *name) { char tmp[16]; if (strchr(name, '.')) { Common::strcpy_s(tmp, name); } else { Common::sprintf_s(tmp, "%s.SND", name); } Slot *slot = _engine->getSoundManager()->findSlotName(tmp); return slot && slot->getAssociatedCharacter() == kCharacterCath; } void LogicManager::fadeDialog(uint32 character) { Slot *slot = _engine->getSoundManager()->findSlotWho(character); if (slot) { slot->setFade(0); slot->setAssociatedCharacter(0); } } void LogicManager::fadeDialog(const char *name) { char tmp[16]; if (strchr(name, '.')) { Common::strcpy_s(tmp, name); } else { Common::sprintf_s(tmp, "%s.SND", name); } Slot *slot = _engine->getSoundManager()->findSlotName(tmp); if (slot) { slot->setFade(0); slot->setAssociatedCharacter(0); } } void LogicManager::endDialog(uint32 character) { Slot *slot = _engine->getSoundManager()->findSlotWho(character); if (slot) { slot->addStatusFlag(kSoundFlagCloseRequested); slot->setAssociatedCharacter(0); slot->closeArchive(); } } void LogicManager::endDialog(const char *name) { char tmp[16]; if (strchr(name, '.')) { Common::strcpy_s(tmp, name); } else { Common::sprintf_s(tmp, "%s.SND", name); } Slot *slot = _engine->getSoundManager()->findSlotName(tmp); if (slot) { slot->addStatusFlag(kSoundFlagCloseRequested); slot->setAssociatedCharacter(0); slot->closeArchive(); } } void LogicManager::claimNISLink(int character) { Slot *slot = _engine->getSoundManager()->_soundCache; if (slot) { do { if (slot->hasTag(kSoundTagLink)) break; slot = slot->getNext(); } while (slot); if (slot) slot->setAssociatedCharacter(character); } } int32 LogicManager::getSoundTicks(int character) { Slot *slot = _engine->getSoundManager()->findSlotWho(character); if (slot) { return slot->getTime() >> 1; } else { return 0; } } int LogicManager::whoseBit(int character) { int result = 0; if (character && (character & 1) == 0) { do { character >>= 1; result++; } while ((character & 1) == 0); } return result; } bool LogicManager::cathWillSeeOtis(int position) { int param1; int param2; switch (position) { case 1500: param1 = 1; param2 = 23; break; case 2740: param1 = 3; param2 = 25; break; case 3050: param1 = 5; param2 = 26; break; case 4070: param1 = 7; param2 = 28; break; case 4840: param1 = 9; param2 = 30; break; case 5790: param1 = 11; param2 = 32; break; case 6470: param1 = 13; param2 = 34; break; case 7500: param1 = 15; param2 = 36; break; case 8200: param1 = 17; param2 = 38; break; default: return true; } return (_engine->getOtisManager()->fDirection(0) && _engine->_fudgePosition[param1] >= getCharacter(kCharacterCath).characterPosition.position) || (_engine->getOtisManager()->rDirection(0) && _engine->_fudgePosition[param2] <= getCharacter(kCharacterCath).characterPosition.position); } bool LogicManager::walk(int character, int car, int position) { int seqDirection; int excuseMeCharacter; uint32 characterPosition; bool canTeleportToTarget = false; bool moveCharacterByOffset = false; bool walkCounterUpdated = false; int offset = 0; uint32 cathPos = getCharacter(kCharacterCath).characterPosition.position; if (position == 2000 && cathFacingUp() && !checkCathDir(kCarGreenSleeping, 1) && !checkCathDir(kCarRedSleeping, 2)) position = 1500; if (getCharacter(character).direction != 1 && getCharacter(character).direction != 2) getCharacter(character).walkCounter = 0; if (getCharacter(character).walkCounter) { getCharacter(character).walkCounter--; if (getCharacter(character).walkCounter == 0x80) getCharacter(character).walkCounter = 0; if ((getCharacter(character).walkCounter & 0x7F) != 8) { getCharacter(character).waitedTicksUntilCycleRestart = 0; return false; } walkCounterUpdated = true; if ((getCharacter(character).walkCounter & 0x80) != 0) moveCharacterByOffset = true; } if (car == getCharacter(character).characterPosition.car) { if (ABS(getCharacter(character).characterPosition.position - position) >= 100) { if (position > 850 && position < 9270 && ABS(getCharacter(character).characterPosition.position - position) < 300) canTeleportToTarget = true; } else { canTeleportToTarget = true; } if (!canTeleportToTarget && ((_engine->getOtisManager()->fDirection(0) && getCharacter(character).direction == 1) || (_engine->getOtisManager()->rDirection(0) && getCharacter(character).direction == 2)) && !cathWillSeeOtis(position) && nearChar(character, kCharacterCath, 250)) { canTeleportToTarget = true; } if (canTeleportToTarget && whoOnScreen(character) && whoFacingCath(character) && !cathWillSeeOtis(position)) { canTeleportToTarget = false; if (getCharacter(character).direction == 1) position = getCharacter(kCharacterCath).characterPosition.position + 250; else position = getCharacter(kCharacterCath).characterPosition.position - 250; } if (canTeleportToTarget) { getCharacter(character).characterPosition.position = position; if (getCharacter(character).direction == 1 || getCharacter(character).direction == 2) getCharacter(character).direction = 0; getCharacter(character).attachedConductor = 0; return true; } } if (car <= getCharacter(character).characterPosition.car) { if (car >= getCharacter(character).characterPosition.car) { if (position <= getCharacter(character).characterPosition.position) seqDirection = 2; else seqDirection = 1; } else { seqDirection = 2; } } else { seqDirection = 1; } if (seqDirection == getCharacter(character).direction && !walkCounterUpdated) { if (!nearX(character, 1500, 750) || character == 20) { if (getCharacter(character).attachedConductor) { send(character, getCharacter(character).attachedConductor, 16, 0); getCharacter(character).attachedConductor = 0; } } else if (!getCharacter(character).attachedConductor) { if (getCharacter(character).direction != 1 || (position <= 2000 && car == getCharacter(character).characterPosition.car)) { if (getCharacter(character).direction == 2 && (position < 1500 || car != getCharacter(character).characterPosition.car) && getCharacter(character).characterPosition.position > 1500) { if (getCharacter(character).characterPosition.car == kCarGreenSleeping) { send(character, kCharacterCond1, 11, 0); getCharacter(character).attachedConductor = kCharacterCond1; } else if (getCharacter(character).characterPosition.car == kCarRedSleeping) { send(character, kCharacterCond2, 11, 0); getCharacter(character).attachedConductor = kCharacterCond2; } } } else if (getCharacter(character).characterPosition.position < 1500) { if (getCharacter(character).characterPosition.car == kCarGreenSleeping) { send(character, kCharacterCond1, 11, 1); getCharacter(character).attachedConductor = kCharacterCond1; } else if (getCharacter(character).characterPosition.car == kCarRedSleeping) { send(character, kCharacterCond2, 11, 1); getCharacter(character).attachedConductor = kCharacterCond2; } } } if (whoOnScreen(character)) { if (getCharacter(character).needsPosFudge) { if (getCharacter(character).characterPosition.car == kCarGreenSleeping) { offset = 0; } else if (getCharacter(character).characterPosition.car == kCarRedSleeping) { offset = 8; } for (int i = 0; i < 8; i++) { if ((_blockedX[offset + i] || _softBlockedX[offset + i]) && nearX(character, _doorPositions[i], 750) && cathWillSeeOtis(_doorPositions[i]) && ((getCharacter(character).direction == 1 && getCharacter(character).characterPosition.position < _doorPositions[i] && (car != getCharacter(character).characterPosition.car || position > _doorPositions[i])) || (getCharacter(character).direction == 2 && (getCharacter(character).characterPosition.position > _doorPositions[i]) && (car != getCharacter(character).characterPosition.car || position < _doorPositions[i])))) { if (_blockedX[offset + i]) { excuseMeCharacter = whoseBit(_blockedX[offset + i]); } else { excuseMeCharacter = whoseBit(_softBlockedX[offset + i]); } playChrExcuseMe(character, excuseMeCharacter, 0); getCharacter(character).walkCounter = 0x80 | 0x10; break; } } for (int j = 1; j < 40; j++) { if (_engine->getMessageManager()->getMessageHandle(j) && whoOnScreen(j) && j != character && nearChar(character, j, 750) && whoWalking(j) && (character != kCharacterRebecca || j != kCharacterSophie) && (character != kCharacterSophie || j != kCharacterRebecca) && (character != kCharacterIvo || j != kCharacterSalko) && (character != kCharacterSalko || j != kCharacterIvo) && (character != kCharacterMilos || j != kCharacterVesna) && (character != kCharacterVesna || j != kCharacterMilos)) { if (getCharacter(character).direction == getCharacter(j).direction) { if (ABS(getCharacter(character).characterPosition.position - getCharacter(kCharacterCath).characterPosition.position) > ABS(getCharacter(j).characterPosition.position - getCharacter(kCharacterCath).characterPosition.position)) { if (!whoFacingCath(character)) { if (seqDirection == 1) { if (getCharacter(character).characterPosition.position < 9500) getCharacter(character).characterPosition.position += 500; } else if (getCharacter(character).characterPosition.position > 500) { getCharacter(character).characterPosition.position -= 500; } _engine->getOtisManager()->startSeq(character, seqDirection, 1); return false; } getCharacter(character).waitedTicksUntilCycleRestart = 0; break; } } else if ((getCharacter(character).direction == 1 && getCharacter(character).characterPosition.position < getCharacter(j).characterPosition.position) || (getCharacter(character).direction == 2 && (getCharacter(character).characterPosition.position > getCharacter(j).characterPosition.position))) { getCharacter(character).waitedTicksUntilCycleRestart = 0; getCharacter(j).waitedTicksUntilCycleRestart = 0; getCharacter(character).walkCounter = 0x10; getCharacter(j).walkCounter = 0x10; playChrExcuseMe(character, j, 0); playChrExcuseMe(j, character, 0); if (j > character) getCharacter(j).walkCounter++; break; } } } } return false; } if (getCharacter(character).direction == 1) { if (getCharacter(character).characterPosition.position < 10000 - (uint16)getCharacter(character).walkStepSize) getCharacter(character).characterPosition.position += getCharacter(character).walkStepSize; } else if (getCharacter(character).characterPosition.position > (uint16)getCharacter(character).walkStepSize) { getCharacter(character).characterPosition.position -= getCharacter(character).walkStepSize; } characterPosition = getCharacter(character).characterPosition.position; if (characterPosition > 9270 && getCharacter(character).direction == 1) { if (getCharacter(kCharacterCath).characterPosition.car == getCharacter(character).characterPosition.car) { queueSFX(character, 36, 0); queueSFX(character, 37, 30); } getCharacter(character).characterPosition.car++; getCharacter(character).characterPosition.position = 851; if (getCharacter(character).characterPosition.car == kCarGreenSleeping && onLowPlatform(kCharacterCath)) { queueSFX(kCharacterCath, 14, 0); playChrExcuseMe(character, kCarGreenSleeping, 16); bumpCath(kCarGreenSleeping, 1, 255); playDialog(kCharacterCath, "CAT1127A" , -1, 0); queueSFX(kCharacterCath, 15, 0); } if (getCharacter(character).characterPosition.car > car || (getCharacter(character).characterPosition.car == car && getCharacter(character).characterPosition.position >= position)) { getCharacter(character).characterPosition.car = car; getCharacter(character).characterPosition.position = position; getCharacter(character).direction = 0; getCharacter(character).attachedConductor = 0; return true; } if (getCharacter(character).characterPosition.car == kCarGreenSleeping && onKronPlatform(0)) { queueSFX(kCharacterCath, 14, 0); playChrExcuseMe(character, 0, 16); bumpCath(kCarGreenSleeping, 62, 255); playDialog(kCharacterCath, "CAT1127A" , -1, 0); queueSFX(kCharacterCath, 15, 0); } if (getCharacter(kCharacterCath).characterPosition.car == getCharacter(character).characterPosition.car) { queueSFX(character, 36, 0); queueSFX(character, 37, 30); } } else if (characterPosition < 850 && getCharacter(character).direction == 2) { if (getCharacter(kCharacterCath).characterPosition.car == getCharacter(character).characterPosition.car) { queueSFX(character, 36, 0); queueSFX(character, 37, 30); } getCharacter(character).characterPosition.car--; getCharacter(character).characterPosition.position = 9269; if (getCharacter(character).characterPosition.car == kCarKronos && onLowPlatform(kCharacterCath)) { queueSFX(kCharacterCath, 14, 0); playChrExcuseMe(character, kCharacterCath, 16); bumpCath(kCarGreenSleeping, 1, 255); playDialog(kCharacterCath, "CAT1127A", -1, 0); queueSFX(kCharacterCath, 15, 0); } if (getCharacter(character).characterPosition.car < car || (getCharacter(character).characterPosition.car == car && getCharacter(character).characterPosition.position <= position)) { getCharacter(character).characterPosition.car = car; getCharacter(character).characterPosition.position = position; getCharacter(character).direction = 0; getCharacter(character).attachedConductor = 0; return true; } if (getCharacter(character).characterPosition.car == kCarKronos && onKronPlatform(kCharacterCath)) { queueSFX(kCharacterCath, 14, 0); playChrExcuseMe(character, 0, 16); bumpCath(kCarGreenSleeping, 62, 255); playDialog(kCharacterCath, "CAT1127A", -1, 0); queueSFX(kCharacterCath, 15, 0); } if (getCharacter(kCharacterCath).characterPosition.car == getCharacter(character).characterPosition.car) { queueSFX(character, 36, 0); queueSFX(character, 37, 30); } } if (getCharacter(kCharacterCath).characterPosition.car != getCharacter(character).characterPosition.car || getCharacter(kCharacterCath).characterPosition.location) return false; if (getCharacter(character).direction == 1) { if (cathPos <= characterPosition || cathPos - characterPosition < 500 || cathPos - characterPosition >= (unsigned int)(uint16)getCharacter(character).walkStepSize + 500) { return false; } if (_engine->getOtisManager()->fDirection(0) || _engine->getOtisManager()->doorView()) { send(kCharacterCath, character, 6, 0); if (_engine->getOtisManager()->doorView()) bumpCathFDoor(_trainData[_activeNode].parameter1); return false; } if (!_engine->getOtisManager()->rDirection(0)) return false; } else { if (cathPos >= characterPosition || characterPosition - cathPos < 500 || (int32)(characterPosition - cathPos) >= getCharacter(character).walkStepSize + 500) { return false; } if (!_engine->getOtisManager()->fDirection(0)) { if (_engine->getOtisManager()->rDirection(0) || _engine->getOtisManager()->doorView()) { send(kCharacterCath, character, 6, 0); if (_engine->getOtisManager()->doorView()) bumpCathRDoor(_trainData[_activeNode].parameter1); } return false; } } send(kCharacterCath, character, 5, 0); return false; } if (walkCounterUpdated) { if (seqDirection == 1) { if (moveCharacterByOffset) { if (getCharacter(character).characterPosition.position < 8800) getCharacter(character).characterPosition.position += 1200; } else if (getCharacter(character).characterPosition.position < 9250) { getCharacter(character).characterPosition.position += 750; } if (car == getCharacter(character).characterPosition.car && getCharacter(character).characterPosition.position >= position) { getCharacter(character).characterPosition.position = position; getCharacter(character).direction = 0; getCharacter(character).attachedConductor = 0; return true; } } else { if (moveCharacterByOffset) { if (getCharacter(character).characterPosition.position > 1200) getCharacter(character).characterPosition.position -= 1200; } else if (getCharacter(character).characterPosition.position > 750) { getCharacter(character).characterPosition.position -= 750; } if (car == getCharacter(character).characterPosition.car && getCharacter(character).characterPosition.position <= position) { getCharacter(character).characterPosition.position = position; getCharacter(character).direction = 0; getCharacter(character).attachedConductor = 0; return true; } } } _engine->getOtisManager()->startSeq(character, seqDirection, 1); return false; } void LogicManager::startCycOtis(int character, const char *filename) { Common::String filenameStr(filename); filenameStr.toUppercase(); Common::strcpy_s(getCharacter(character).sequenceNamePrefix, filenameStr.c_str()); Common::strcat_s(getCharacter(character).sequenceNamePrefix, "-"); getCharacter(character).elapsedFrames = 0; getCharacter(character).waitedTicksUntilCycleRestart = 0; getCharacter(character).currentFrameSeq1 = 0; _engine->getOtisManager()->startSeq(character, 3, true); } void LogicManager::startSeqOtis(int character, const char *filename) { Common::String filenameStr(filename); filenameStr.toUppercase(); Common::strcpy_s(getCharacter(character).sequenceNamePrefix, filenameStr.c_str()); Common::strcat_s(getCharacter(character).sequenceNamePrefix, "-"); getCharacter(character).elapsedFrames = 0; getCharacter(character).waitedTicksUntilCycleRestart = 0; getCharacter(character).currentFrameSeq1 = 0; _engine->getOtisManager()->startSeq(character, 4, true); } void LogicManager::advanceFrame(int character) { Seq *sequence; int16 *frameCountPtr; int16 elapsedFrames; bool found = false; if (getCharacter(character).direction == 5) { sequence = getCharacter(character).sequence2; frameCountPtr = &(getCharacter(character).currentFrameSeq2); } else { sequence = getCharacter(character).sequence1; frameCountPtr = &(getCharacter(character).currentFrameSeq1); } if (sequence) { int16 oldFrameCount = *frameCountPtr; if (sequence->numFrames > *frameCountPtr) { elapsedFrames = getCharacter(character).elapsedFrames; do { if ((sequence->numFrames - 1) <= *frameCountPtr) break; if ((sequence->sprites[*frameCountPtr].flags & 8) != 0) { found = true; } else { if (sequence->sprites[*frameCountPtr].soundAction == 35) found = true; getCharacter(character).elapsedFrames += sequence->sprites[*frameCountPtr].ticksToWaitUntilCycleRestart; (*frameCountPtr)++; } } while (!found); if (!found) { *frameCountPtr = oldFrameCount; getCharacter(character).elapsedFrames = elapsedFrames; } } } } void LogicManager::endGraphics(int character) { _engine->getSpriteManager()->destroySprite(&getCharacter(character).frame1, false); _engine->getSpriteManager()->destroySprite(&getCharacter(character).frame2, false); if (getCharacter(character).sequence2) { _engine->getMemoryManager()->freeMem(getCharacter(character).sequence2->rawSeqData); delete getCharacter(character).sequence2; getCharacter(character).sequence2 = nullptr; Common::strcpy_s(getCharacter(character).sequenceName2, ""); getCharacter(character).needsSecondaryPosFudge = 0; getCharacter(character).directionSwitch = 0; } if (getCharacter(character).sequence1) { _engine->getMemoryManager()->freeMem(getCharacter(character).sequence1->rawSeqData); delete getCharacter(character).sequence1; getCharacter(character).sequence1 = nullptr; Common::strcpy_s(getCharacter(character).sequenceName, ""); getCharacter(character).needsPosFudge = 0; getCharacter(character).currentFrameSeq1 = -1; } Common::strcpy_s(getCharacter(character).sequenceNamePrefix, ""); getCharacter(character).direction = 0; getCharacter(character).doProcessEntity = 1; } void LogicManager::makeAllJump(int chapter) { if (chapter) { for (int i = 0; i < 40; i++) { getCharacter(i).currentCall = 0; getCharacter(i).inventoryItem = 0; endGraphics(i); } for (int i = 0; i < 1000; i++) { _blockedViews[i] = 0; } for (int i = 0; i < 16; i++) { _blockedX[i] = 0; _softBlockedX[i] = 0; } // Originally the first edition of the game checked only for kSoundTagMenu; // I added kSoundTagLink since otherwise the music coda playing after // the very first cutscene is immediately killed by this function, which // is also happening in the original. But the latter gets the news of the // sound getting killed a bit later, so we manage to hear it anyway; not // here though... // // Incidentally this is what the demo and subsequent releases do. if (chapter == 1 || chapter == 3) { _engine->getSoundManager()->killAllExcept(kSoundTagMenu, kSoundTagLink, 0, 0, 0, 0, 0); } else { _engine->getSoundManager()->killAllExcept(kSoundTagMenu, 0, 0, 0, 0, 0, 0); } } if (_engine->isDemo()) { CONS_DemoAnna(chapter); CONS_DemoAugust(chapter); CONS_DemoCond2(chapter); CONS_DemoWaiter1(chapter); CONS_DemoTatiana(chapter); CONS_DemoAbbot(chapter); CONS_DemoVesna(chapter); CONS_DemoIvo(chapter); CONS_DemoRebecca(chapter); CONS_DemoFrancois(chapter); CONS_DemoMadame(chapter); CONS_DemoMonsieur(chapter); if (chapter < 2) CONS_DemoMaster(chapter); CONS_DemoTableA(chapter); CONS_DemoTableB(chapter); CONS_DemoTableC(chapter); CONS_DemoTableD(chapter); CONS_DemoTableE(chapter); CONS_DemoTableF(chapter); } else { CONS_Anna(chapter); CONS_August(chapter); CONS_Cond1(chapter); CONS_Cond2(chapter); CONS_HeadWait(chapter); CONS_Waiter1(chapter); CONS_Waiter2(chapter); CONS_Cook(chapter); CONS_TrainM(chapter); CONS_Tatiana(chapter); CONS_Vassili(chapter); CONS_Alexei(chapter); CONS_Abbot(chapter); CONS_Milos(chapter); CONS_Vesna(chapter); CONS_Ivo(chapter); CONS_Salko(chapter); CONS_Rebecca(chapter); CONS_Sophie(chapter); CONS_Francois(chapter); CONS_Madame(chapter); CONS_Monsieur(chapter); CONS_Kronos(chapter); CONS_Kahina(chapter); CONS_Mahmud(chapter); CONS_Yasmin(chapter); CONS_Hadija(chapter); CONS_Alouan(chapter); CONS_Police(chapter); CONS_Max(chapter); if (chapter < 2) CONS_Master(chapter); CONS_Clerk(chapter); CONS_TableA(chapter); CONS_TableB(chapter); CONS_TableC(chapter); CONS_TableD(chapter); CONS_TableE(chapter); CONS_TableF(chapter); CONS_Mitchell(chapter); } } void LogicManager::CONS_All(bool isFirstChapter, int character) { makeAllJump(isFirstChapter ? 1 : 0); _flagBumpCathNode = false; if (!isFirstChapter && character) { fedEx(kCharacterCath, character, 0, 0); } if (!_flagBumpCathNode) bumpCathNode(_activeNode); // Uncomment this to activate "debug walks" functions // CONS_Master_WalkTest(0, 0, 0, 0); } int32 LogicManager::getGameTime() { return _gameTime; } int32 LogicManager::getRealTime() { return _realTime; } int32 LogicManager::getTimeSpeed() { return _timeSpeed; } } // End of namespace LastExpress