Files
scummvm-cursorfix/engines/titanic/pet_control/pet_conversations.cpp
2026-02-02 04:50:13 +01:00

591 lines
15 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "titanic/pet_control/pet_conversations.h"
#include "titanic/pet_control/pet_control.h"
#include "titanic/debugger.h"
#include "titanic/game_manager.h"
#include "titanic/titanic.h"
namespace Titanic {
CPetConversations::CPetConversations() : CPetSection(),
_logChanged(false), _field418(0), _npcNum(-1),
_rect1(22, 352, 598, 478) {
Rect logRect(85, 18, 513, 87);
logRect.translate(20, 350);
_log.setBounds(logRect);
_log.resize(50);
_log.setHasBorder(false);
_log.setColor(getColor(2));
_log.setup();
_log.addLine("Welcome to your PET v1.0a");
Rect inputRect(85, 95, 513, 135);
inputRect.translate(20, 350);
_textInput.setBounds(inputRect);
_textInput.setHasBorder(false);
_textInput.resize(2);
_textInput.setMaxCharsPerLine(74);
_textInput.setColor(getColor(0));
_textInput.setup();
_npcLevels[0] = _npcLevels[1] = _npcLevels[2] = 0;
}
bool CPetConversations::setup(CPetControl *petControl) {
if (petControl && setupControl(petControl))
return reset();
return false;
}
bool CPetConversations::reset() {
_dials[0].setup(MODE_UNSELECTED, "3PetDial1", _petControl);
_dials[1].setup(MODE_UNSELECTED, "3PetDial2", _petControl);
_dials[2].setup(MODE_UNSELECTED, "3PetDial3", _petControl);
_dialBackground.reset("PetDialBack", _petControl);
_scrollUp.reset("PetScrollUp", _petControl);
_scrollDown.reset("PetScrollDown", _petControl);
_doorBot.reset("PetCallDoorOut", _petControl, MODE_UNSELECTED);
_doorBot.reset("PetCallDoorIn", _petControl, MODE_SELECTED);
_bellBot.reset("PetCallBellOut", _petControl, MODE_UNSELECTED);
_bellBot.reset("PetCallBellIn", _petControl, MODE_SELECTED);
_indent.reset("PetSmallCharacterIndent", _petControl);
_splitter.reset("PetSplitter", _petControl);
_npcIcons[0].setup(MODE_UNSELECTED, "3PetSmlDoorbot", _petControl);
_npcIcons[1].setup(MODE_UNSELECTED, "3PetSmlDeskbot", _petControl);
_npcIcons[2].setup(MODE_UNSELECTED, "3PetSmlLiftbot", _petControl);
_npcIcons[3].setup(MODE_UNSELECTED, "3PetSmlParrot", _petControl);
_npcIcons[4].setup(MODE_UNSELECTED, "3PetSmlBarbot", _petControl);
_npcIcons[5].setup(MODE_UNSELECTED, "3PetSmlChatterbot", _petControl);
_npcIcons[6].setup(MODE_UNSELECTED, "3PetSmlBellbot", _petControl);
_npcIcons[7].setup(MODE_UNSELECTED, "3PetSmlMaitreD", _petControl);
_npcIcons[8].setup(MODE_UNSELECTED, "3PetSmlSuccubus", _petControl);
if (_petControl->getPassengerClass() == 1) {
uint col = getColor(0);
_textInput.setColor(col);
_textInput.setLineColor(0, col);
// Replace the log colors with new 1st class ones
uint colors1[5], colors2[5];
copyColors(2, colors1);
copyColors(1, colors2);
_log.remapColors(5, colors1, colors2);
_log.setColor(getColor(2));
}
// WORKAROUND: After loading, mark log as changed so the
// current NPC portrait to display gets recalculated
_logChanged = true;
return true;
}
void CPetConversations::draw(CScreenManager *screenManager) {
_dialBackground.draw(screenManager);
_splitter.draw(screenManager);
_dials[0].draw(screenManager);
_dials[1].draw(screenManager);
_dials[2].draw(screenManager);
_indent.draw(screenManager);
_doorBot.draw(screenManager);
_bellBot.draw(screenManager);
_scrollUp.draw(screenManager);
_scrollDown.draw(screenManager);
_log.draw(screenManager);
_textInput.draw(screenManager);
if (_logChanged) {
int endIndex = _log.displayEndIndex();
if (endIndex >= 0) {
int npcNum = _log.getNPCNum(1, endIndex);
if (npcNum > 0 && npcNum < 10)
_npcNum = npcNum - 1;
}
_logChanged = false;
}
if (_npcNum >= 0)
_npcIcons[_npcNum].draw(screenManager);
}
Rect CPetConversations::getBounds() const {
Rect rect = _dials[0].getBounds();
rect.combine(_dials[1].getBounds());
rect.combine(_dials[2].getBounds());
return rect;
}
bool CPetConversations::isValid(CPetControl *petControl) {
return setupControl(petControl);
}
bool CPetConversations::MouseButtonDownMsg(CMouseButtonDownMsg *msg) {
if (_scrollDown.MouseButtonDownMsg(msg->_mousePos)) {
scrollDown();
return true;
} else if (_scrollUp.MouseButtonDownMsg(msg->_mousePos)) {
scrollUp();
return true;
}
return
_doorBot.MouseButtonDownMsg(msg->_mousePos) ||
_bellBot.MouseButtonDownMsg(msg->_mousePos);
}
bool CPetConversations::MouseButtonUpMsg(CMouseButtonUpMsg *msg) {
if (_scrollUp.MouseButtonUpMsg(msg->_mousePos) ||
_scrollDown.MouseButtonUpMsg(msg->_mousePos))
return true;
if (_doorBot.MouseButtonUpMsg(msg->_mousePos)) {
switch (canSummonBot("DoorBot")) {
case SUMMON_CANT:
_log.addLine(g_vm->_strings[CANT_SUMMON_DOORBOT], getColor(1));
break;
case SUMMON_CAN:
summonBot("DoorBot");
return true;
default:
break;
}
// Scroll to the bottom of the log
scrollToBottom();
return true;
}
if (_bellBot.MouseButtonUpMsg(msg->_mousePos)) {
switch (canSummonBot("BellBot")) {
case SUMMON_CANT:
_log.addLine(g_vm->_strings[CANT_SUMMON_BELLBOT], getColor(1));
break;
case SUMMON_CAN:
summonBot("BellBot");
return true;
default:
break;
}
// Scroll to the bottom of the log
scrollToBottom();
return true;
}
return false;
}
bool CPetConversations::MouseDoubleClickMsg(CMouseDoubleClickMsg *msg) {
return _scrollDown.MouseDoubleClickMsg(msg->_mousePos)
|| _scrollUp.MouseDoubleClickMsg(msg->_mousePos);
}
bool CPetConversations::MouseWheelMsg(CMouseWheelMsg *msg) {
if (msg->_wheelUp)
scrollUp();
else
scrollDown();
return true;
}
bool CPetConversations::KeyCharMsg(CKeyCharMsg *msg) {
Common::KeyState keyState;
keyState.ascii = msg->_key;
return handleKey(keyState);
}
bool CPetConversations::ActionMsg(CActionMsg *msg) {
Common::CustomEventType action;
action = msg->_action;
switch (action) {
case kActionPETScrollPageUp:
scrollUpPage();
return true;
case kActionPETScrollPageDown:
scrollDownPage();
return true;
case kActionPETScrollTop:
scrollToTop();
return true;
case kActionPeTScrollBottom:
scrollToBottom();
return true;
default:
break;
}
return false;
}
void CPetConversations::displayMessage(const CString &msg) {
_log.addLine(msg, getColor(1));
scrollToBottom();
}
void CPetConversations::load(SimpleFile *file, int param) {
_textInput.load(file, param);
_log.load(file, param);
for (int idx = 0; idx < TOTAL_DIALS; ++idx)
_npcLevels[idx] = file->readNumber();
}
void CPetConversations::postLoad() {
reset();
}
void CPetConversations::save(SimpleFile *file, int indent) {
_textInput.save(file, indent);
_log.save(file, indent);
for (int idx = 0; idx < TOTAL_DIALS; ++idx)
file->writeNumberLine(_npcLevels[idx], indent);
}
void CPetConversations::enter(PetArea oldArea) {
resetDials();
if (_petControl && _petControl->_activeNPC)
// Start a timer for the NPC
startNPCTimer();
// Show the text cursor
_textInput.showCursor(-2);
}
void CPetConversations::leave() {
_textInput.hideCursor();
stopNPCTimer();
}
void CPetConversations::timerExpired(int val) {
if (val != 1) {
CPetSection::timerExpired(val);
} else {
CString name = _field418 ? _npcName : getActiveNPCName();
for (int idx = 0; idx < TOTAL_DIALS; ++idx) {
if (!_dials[idx].hasActiveMovie())
updateDial(idx, name);
}
}
}
void CPetConversations::displayNPCName(CGameObject *npc) {
const Strings &strings = g_vm->_strings;
if (npc) {
displayMessage(CString());
CString msg = strings[TALKING_TO];
CString name = npc->getName();
int id = 1;
if (name.containsIgnoreCase("Doorbot")) {
msg += strings[DOORBOT_NAME];
} else if (name.containsIgnoreCase("Deskbot")) {
id = 2;
msg += strings[DESKBOT_NAME];
} else if (name.containsIgnoreCase("LiftBot")) {
id = 3;
msg += strings[LIFTBOT_NAME];
} else if (name.containsIgnoreCase("Parrot")) {
id = 4;
msg += strings[PARROT_NAME];
} else if (name.containsIgnoreCase("BarBot")) {
id = 5;
msg += strings[BARBOT_NAME];
} else if (name.containsIgnoreCase("ChatterBot")) {
id = 6;
msg += strings[CHATTERBOT_NAME];
} else if (name.containsIgnoreCase("BellBot")) {
id = 7;
msg += strings[BELLBOT_NAME];
} else if (name.containsIgnoreCase("Maitre")) {
id = 8;
msg += strings[MAITRED_NAME];
} else if (name.containsIgnoreCase("Succubus") || name.containsIgnoreCase("Sub")) {
id = 9;
msg += strings[SUCCUBUS_NAME];
} else {
msg += strings[UNKNOWN_NAME];
}
_log.setNPC(1, id);
displayMessage(msg);
}
}
void CPetConversations::setNPC(const CString &name) {
_field418 = 0;
resetDials(name);
startNPCTimer();
}
void CPetConversations::resetNPC() {
stopNPCTimer();
resetDials("0");
}
void CPetConversations::showCursor() {
_textInput.showCursor(-2);
}
void CPetConversations::hideCursor() {
_textInput.hideCursor();
}
bool CPetConversations::setupControl(CPetControl *petControl) {
if (petControl) {
_petControl = petControl;
_dialBackground.setBounds(Rect(0, 0, 21, 130));
_dialBackground.translate(20, 350);
const Rect rect1(0, 0, 22, 36);
_dials[0].setBounds(rect1);
_dials[0].translate(20, 359);
_dials[1].setBounds(rect1);
_dials[1].translate(20, 397);
_dials[2].setBounds(rect1);
_dials[2].translate(20, 434);
const Rect rect2(0, 0, 11, 24);
_scrollUp.setBounds(rect2);
_scrollUp.translate(87, 374);
_scrollDown.setBounds(rect2);
_scrollDown.translate(87, 421);
const Rect rect3(0, 0, 39, 39);
_doorBot.setBounds(rect3);
_doorBot.translate(546, 372);
_bellBot.setBounds(rect3);
_bellBot.translate(546, 418);
_indent.setBounds(Rect(0, 0, 37, 70));
_indent.translate(46, 374);
_splitter.setBounds(Rect(0, 0, 435, 3));
_splitter.translate(102, 441);
const Rect rect4(0, 0, 33, 66);
for (int idx = 0; idx < 9; ++idx) {
_npcIcons[idx].setBounds(rect4);
_npcIcons[idx].translate(48, 376);
}
}
return true;
}
void CPetConversations::scrollUp() {
_log.scrollUp(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
void CPetConversations::scrollDown() {
_log.scrollDown(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
void CPetConversations::scrollUpPage() {
_log.scrollUpPage(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
void CPetConversations::scrollDownPage() {
_log.scrollDownPage(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
void CPetConversations::scrollToTop() {
_log.scrollToTop(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
void CPetConversations::scrollToBottom() {
_log.scrollToBottom(CScreenManager::_screenManagerPtr);
if (_petControl)
_petControl->makeDirty();
_logChanged = true;
}
int CPetConversations::canSummonBot(const CString &name) {
return _petControl ? _petControl->canSummonBot(name) : SUMMON_CANT;
}
void CPetConversations::summonBot(const CString &name) {
if (_petControl) {
if (_petControl->getPassengerClass() >= UNCHECKED) {
_petControl->displayMessage(AT_LEAST_3RD_CLASS_FOR_HELP);
} else {
_petControl->summonBot(name, 0);
}
}
}
void CPetConversations::startNPCTimer() {
_petControl->startPetTimer(1, 1000, 1000, this);
}
void CPetConversations::stopNPCTimer() {
_petControl->stopPetTimer(1);
}
TTnpcScript *CPetConversations::getNPCScript(const CString &name) const {
if (name.empty() || !_petControl)
return nullptr;
CGameManager *gameManager = _petControl->getGameManager();
if (!gameManager)
return nullptr;
CTrueTalkManager *trueTalk = gameManager->getTalkManager();
if (!trueTalk)
return nullptr;
return trueTalk->getTalker(name);
}
bool CPetConversations::handleKey(const Common::KeyState &keyState) {
if (keyState.ascii > 0 && keyState.ascii <= 127
&& keyState.ascii != Common::KEYCODE_TAB) {
if (_textInput.handleKey(keyState.ascii))
// Text line finished, so process line
textLineEntered(_textInput.getText());
return true;
}
return false;
}
void CPetConversations::textLineEntered(const CString &textLine) {
if (textLine.empty() || !_petControl)
return;
if (_petControl->_activeNPC) {
_log.addLine("- " + textLine, getColor(0));
CTextInputMsg inputMsg(textLine, "");
inputMsg.execute(_petControl->_activeNPC);
if (!inputMsg._response.empty())
_log.addLine(inputMsg._response);
} else {
_log.addLine(g_vm->_strings[NO_ONE_TO_TALK_TO], getColor(1));
}
// Clear input line and scroll log down to end to show response
_textInput.setup();
scrollToBottom();
}
void CPetConversations::setActiveNPC(const CString &name) {
_npcName = name;
_field418 = 1;
resetDials();
startNPCTimer();
}
void CPetConversations::updateDial(uint dialNum, const CString &npcName) {
TTnpcScript *script = getNPCScript(npcName);
uint newLevel = getDialLevel(dialNum, script);
npcDialChange(dialNum, _npcLevels[dialNum], newLevel);
_npcLevels[dialNum] = newLevel;
}
uint CPetConversations::getDialLevel(uint dialNum, TTnpcScript *script, bool flag) {
if (!script)
return 0;
else
return MAX(script->getDialLevel(dialNum, flag), 15);
}
void CPetConversations::npcDialChange(uint dialNum, uint oldLevel, uint newLevel) {
const uint ascending[2] = { 0, 21 };
const uint descending[2] = { 43, 22 };
assert(oldLevel <= 100 && newLevel <= 100);
if (newLevel != oldLevel) {
debugC(DEBUG_DETAILED, kDebugScripts, "Dial %d change from %d to %d",
dialNum, oldLevel, newLevel);
uint src = ascending[0], dest = ascending[1];
if (newLevel < oldLevel) {
src = descending[0];
dest = descending[1];
}
uint val1 = (oldLevel * dest) + (100 - oldLevel) * src;
uint startFrame = val1 / 100;
uint val2 = (newLevel * dest) + (100 - newLevel) * src;
uint endFrame = val2 / 100;
if (startFrame != endFrame)
_dials[dialNum].playMovie(startFrame, endFrame);
}
}
void CPetConversations::resetDials() {
resetDials(getActiveNPCName());
}
void CPetConversations::resetDials(const CString &name) {
TTnpcScript *script = getNPCScript(name);
for (int idx = 0; idx < TOTAL_DIALS; ++idx) {
uint oldLevel = _npcLevels[idx];
uint newLevel = getDialLevel(idx, script);
npcDialChange(idx, oldLevel, newLevel);
_npcLevels[idx] = newLevel;
}
}
void CPetConversations::resetDials0() {
stopNPCTimer();
resetDials("0");
}
void CPetConversations::addLine(const CString &line) {
_log.addLine(line);
scrollToBottom();
}
} // End of namespace Titanic